using System.Linq; using System.Net; using System.Net.Http.Json; using NeonSprawl.Server.Game.Interaction; using NeonSprawl.Server.Game.PositionState; using NeonSprawl.Server.Game.Skills; using NeonSprawl.Server.Game.World; using Xunit; namespace NeonSprawl.Server.Tests.Game.Interaction; /// NEO-41: successful resource_node interact grants salvage XP via shared NEO-38 grant path. public sealed class InteractionResourceNodeGatherXpTests { [Fact] public async Task PostInteract_PrototypeTerminal_ShouldNotIncreaseSalvageXp() { // Arrange await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); var move = new MoveCommandRequest { SchemaVersion = MoveCommandRequest.CurrentSchemaVersion, Target = new PositionVector { X = 0.5, Y = 0.9, Z = 0.5 }, }; await client.PostAsJsonAsync("/game/players/dev-local-1/move", move); // Act var interact = await client.PostAsJsonAsync( "/game/players/dev-local-1/interact", new InteractionRequest { SchemaVersion = InteractionRequest.CurrentSchemaVersion, InteractableId = PrototypeInteractableRegistry.PrototypeTerminalId, }); var snapshot = await client.GetAsync("/game/players/dev-local-1/skill-progression"); // Assert Assert.Equal(HttpStatusCode.OK, interact.StatusCode); Assert.Equal(HttpStatusCode.OK, snapshot.StatusCode); var body = await snapshot.Content.ReadFromJsonAsync(); Assert.NotNull(body); var salvage = body!.Skills!.Single(static s => s.Id == "salvage"); Assert.Equal(0, salvage.Xp); } [Fact] public async Task PostInteract_ResourceNode_WhenInRange_ShouldGrantSalvageActivityXp_ViaProgressionSnapshot() { // Arrange — node at (12, -6), radius 3; stand at (10, -6) → horizontal distance 2 await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); var move = new MoveCommandRequest { SchemaVersion = MoveCommandRequest.CurrentSchemaVersion, Target = new PositionVector { X = 10, Y = 0.9, Z = -6 }, }; await client.PostAsJsonAsync("/game/players/dev-local-1/move", move); // Act var interact = await client.PostAsJsonAsync( "/game/players/dev-local-1/interact", new InteractionRequest { SchemaVersion = InteractionRequest.CurrentSchemaVersion, InteractableId = PrototypeInteractableRegistry.PrototypeResourceNodeAlphaId, }); var snapshot = await client.GetAsync("/game/players/dev-local-1/skill-progression"); // Assert Assert.Equal(HttpStatusCode.OK, interact.StatusCode); var envelope = await interact.Content.ReadFromJsonAsync(); Assert.NotNull(envelope); Assert.True(envelope!.Allowed); Assert.Equal(HttpStatusCode.OK, snapshot.StatusCode); var body = await snapshot.Content.ReadFromJsonAsync(); Assert.NotNull(body); var salvage = body!.Skills!.Single(static s => s.Id == "salvage"); Assert.Equal(GatherSkillXpConstants.ActivityXpPerResourceNodeGather, salvage.Xp); } }