From 71af7a594296b3a20003b342c1667da4a60e45c9 Mon Sep 17 00:00:00 2001 From: VinPropane Date: Sat, 25 Apr 2026 22:57:05 -0400 Subject: [PATCH] NEO-29: normalize test formatting across server and client suites Audit all test files and bring them in line with formatting/style expectations by adding explicit AAA markers across server test methods and fixing GDScript test lint/format issues. --- client/test/hotbar_loadout_client_test.gd | 18 ++++- .../Game/Interaction/HorizontalReachTests.cs | 23 +++++- .../Interaction/InteractablesWorldApiTests.cs | 5 +- .../Game/Interaction/InteractionApiTests.cs | 60 ++++++++++++--- .../Game/PositionState/MoveCommandApiTests.cs | 15 +++- .../MoveCommandValidationTests.cs | 45 ++++++++--- .../Game/PositionState/MoveStreamApiTests.cs | 20 ++++- .../Targeting/PlayerTargetStateReaderTests.cs | 21 +++++- .../Game/Targeting/TargetingApiTests.cs | 74 +++++++++++++++---- 9 files changed, 229 insertions(+), 52 deletions(-) diff --git a/client/test/hotbar_loadout_client_test.gd b/client/test/hotbar_loadout_client_test.gd index 91a894c..3b657ec 100644 --- a/client/test/hotbar_loadout_client_test.gd +++ b/client/test/hotbar_loadout_client_test.gd @@ -52,7 +52,13 @@ class HoldFirstThenAutoTransport: ) -> Error: request_count += 1 last_body = request_data - var body_bytes: PackedByteArray = '{"schemaVersion":1,"updated":true,"loadout":{"schemaVersion":1,"playerId":"dev-local-1","slotCount":8,"slots":[]}}'.to_utf8_buffer() + var body_bytes: PackedByteArray = ( + '{"schemaVersion":1,"updated":true,"loadout":' + + ( + '{"schemaVersion":1,"playerId":"dev-local-1","slotCount":8,"slots":[]}}' + . to_utf8_buffer() + ) + ) if _first: _first = false call_deferred("_emit", body_bytes) @@ -109,7 +115,10 @@ func test_bind_slot_posts_expected_payload() -> void: transport.enqueue( HTTPRequest.RESULT_SUCCESS, 200, - '{"schemaVersion":1,"updated":true,"loadout":{"schemaVersion":1,"playerId":"dev-local-1","slotCount":8,"slots":[]}}' + ( + '{"schemaVersion":1,"updated":true,"loadout":' + + '{"schemaVersion":1,"playerId":"dev-local-1","slotCount":8,"slots":[]}}' + ) ) var c := _make_client(transport) c.call("request_bind_slot", 3, "prototype_burst") @@ -130,7 +139,10 @@ func test_denied_update_emits_reason_signal() -> void: transport.enqueue( HTTPRequest.RESULT_SUCCESS, 200, - '{"schemaVersion":1,"updated":false,"reasonCode":"unknown_ability","loadout":{"schemaVersion":1,"playerId":"dev-local-1","slotCount":8,"slots":[]}}' + ( + '{"schemaVersion":1,"updated":false,"reasonCode":"unknown_ability","loadout":' + + '{"schemaVersion":1,"playerId":"dev-local-1","slotCount":8,"slots":[]}}' + ) ) var c := _make_client(transport) monitor_signals(c) diff --git a/server/NeonSprawl.Server.Tests/Game/Interaction/HorizontalReachTests.cs b/server/NeonSprawl.Server.Tests/Game/Interaction/HorizontalReachTests.cs index e1d6ef1..3d87274 100644 --- a/server/NeonSprawl.Server.Tests/Game/Interaction/HorizontalReachTests.cs +++ b/server/NeonSprawl.Server.Tests/Game/Interaction/HorizontalReachTests.cs @@ -9,35 +9,50 @@ public class HorizontalReachTests public void HorizontalDistance_IgnoresY() { // Y is not a parameter; identical X/Z → zero horizontal separation regardless of implied height difference. + // Act + // Arrange var d = HorizontalReach.HorizontalDistance(1, 2, 1, 2); + // Assert Assert.Equal(0, d); } [Fact] public void IsWithinHorizontalRadius_AtExactBoundary_ReturnsTrue() - { + { // Assert + // Act + // Arrange + Assert.True(HorizontalReach.IsWithinHorizontalRadius(0, 0, 3, 0, 3)); } [Fact] public void IsWithinHorizontalRadius_JustInside_ReturnsTrue() - { + { // Act + // Arrange + const double radius = 3.0; const double eps = 1e-10; + // Assert Assert.True(HorizontalReach.IsWithinHorizontalRadius(0, 0, radius - eps, 0, radius)); } [Fact] public void IsWithinHorizontalRadius_JustOutside_ReturnsFalse() - { + { // Act + // Arrange + const double radius = 3.0; const double eps = 1e-7; + // Assert Assert.False(HorizontalReach.IsWithinHorizontalRadius(0, 0, radius + eps, 0, radius)); } [Fact] public void IsWithinHorizontalRadius_NegativeRadius_ThrowsArgumentOutOfRange() - { + { // Assert + // Act + // Arrange + Assert.Throws(() => HorizontalReach.IsWithinHorizontalRadius(0, 0, 0, 0, -1.0)); } diff --git a/server/NeonSprawl.Server.Tests/Game/Interaction/InteractablesWorldApiTests.cs b/server/NeonSprawl.Server.Tests/Game/Interaction/InteractablesWorldApiTests.cs index 3193f52..5431be4 100644 --- a/server/NeonSprawl.Server.Tests/Game/Interaction/InteractablesWorldApiTests.cs +++ b/server/NeonSprawl.Server.Tests/Game/Interaction/InteractablesWorldApiTests.cs @@ -10,11 +10,14 @@ public class InteractablesWorldApiTests { [Fact] public async Task GetInteractables_ShouldReturnOrderedDescriptors_WithSchemaV1() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); + // Act var response = await client.GetAsync("/game/world/interactables"); + // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); var body = await response.Content.ReadFromJsonAsync(); diff --git a/server/NeonSprawl.Server.Tests/Game/Interaction/InteractionApiTests.cs b/server/NeonSprawl.Server.Tests/Game/Interaction/InteractionApiTests.cs index 70f4dd8..ea956db 100644 --- a/server/NeonSprawl.Server.Tests/Game/Interaction/InteractionApiTests.cs +++ b/server/NeonSprawl.Server.Tests/Game/Interaction/InteractionApiTests.cs @@ -12,7 +12,9 @@ public class InteractionApiTests { [Fact] public async Task PostInteract_ShouldAllow_WhenExactlyAtHorizontalRadius() - { + { // Act + // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); var move = new MoveCommandRequest @@ -20,6 +22,7 @@ public class InteractionApiTests SchemaVersion = MoveCommandRequest.CurrentSchemaVersion, Target = new PositionVector { X = 3, Y = 0, Z = 0 }, }; + // Assert Assert.Equal(HttpStatusCode.OK, (await client.PostAsJsonAsync("/game/players/dev-local-1/move", move)).StatusCode); var response = await client.PostAsJsonAsync( @@ -38,7 +41,8 @@ public class InteractionApiTests [Fact] public async Task PostInteract_ShouldAllow_WhenPlayerInHorizontalRange() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); var move = new MoveCommandRequest @@ -46,7 +50,9 @@ public class InteractionApiTests SchemaVersion = MoveCommandRequest.CurrentSchemaVersion, Target = new PositionVector { X = 0, Y = 0.9, Z = 0 }, }; + // Act var moveResp = await client.PostAsJsonAsync("/game/players/dev-local-1/move", move); + // Assert Assert.Equal(HttpStatusCode.OK, moveResp.StatusCode); var req = new InteractionRequest @@ -69,7 +75,8 @@ public class InteractionApiTests [Fact] public async Task PostInteract_ShouldDenyOutOfRange_WhenPlayerSeededFarFromTerminal() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); var req = new InteractionRequest @@ -77,8 +84,10 @@ public class InteractionApiTests SchemaVersion = InteractionRequest.CurrentSchemaVersion, InteractableId = PrototypeInteractableRegistry.PrototypeTerminalId, }; + // Act var response = await client.PostAsJsonAsync("/game/players/dev-local-1/interact", req); + // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); var body = await response.Content.ReadFromJsonAsync(); @@ -89,7 +98,8 @@ public class InteractionApiTests [Fact] public async Task PostInteract_ShouldDenyUnknownInteractable_WhenIdNotInRegistry() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); var req = new InteractionRequest @@ -97,8 +107,10 @@ public class InteractionApiTests SchemaVersion = InteractionRequest.CurrentSchemaVersion, InteractableId = "no_such_thing", }; + // Act var response = await client.PostAsJsonAsync("/game/players/dev-local-1/interact", req); + // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); var body = await response.Content.ReadFromJsonAsync(); @@ -109,7 +121,9 @@ public class InteractionApiTests [Fact] public async Task PostInteract_ShouldBeCaseInsensitive_ForInteractableId() - { + { // Act + // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); var move = new MoveCommandRequest @@ -117,6 +131,7 @@ public class InteractionApiTests SchemaVersion = MoveCommandRequest.CurrentSchemaVersion, Target = new PositionVector { X = 0, Y = 0, Z = 0 }, }; + // Assert Assert.Equal(HttpStatusCode.OK, (await client.PostAsJsonAsync("/game/players/dev-local-1/move", move)).StatusCode); var req = new InteractionRequest @@ -135,7 +150,8 @@ public class InteractionApiTests [Fact] public async Task PostInteract_ShouldReturnBadRequest_WhenSchemaVersionWrong() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); var req = new InteractionRequest @@ -143,30 +159,36 @@ public class InteractionApiTests SchemaVersion = 999, InteractableId = PrototypeInteractableRegistry.PrototypeTerminalId, }; + // Act var response = await client.PostAsJsonAsync("/game/players/dev-local-1/interact", req); + // Assert Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); } [Fact] public async Task PostInteract_ShouldReturnBadRequest_WhenInteractableIdMissing() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); var content = new StringContent( $"{{\"schemaVersion\":{InteractionRequest.CurrentSchemaVersion}}}", Encoding.UTF8, "application/json"); + // Act var response = await client.PostAsync("/game/players/dev-local-1/interact", content); + // Assert Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); } [Fact] public async Task PostInteract_ShouldReturnBadRequest_WhenInteractableIdWhitespaceOnly() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); var req = new InteractionRequest @@ -174,15 +196,18 @@ public class InteractionApiTests SchemaVersion = InteractionRequest.CurrentSchemaVersion, InteractableId = " \t ", }; + // Act var response = await client.PostAsJsonAsync("/game/players/dev-local-1/interact", req); + // Assert Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); } [Fact] public async Task PostInteract_ShouldReturnNotFound_WhenPlayerUnknown() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); var req = new InteractionRequest @@ -190,17 +215,21 @@ public class InteractionApiTests SchemaVersion = InteractionRequest.CurrentSchemaVersion, InteractableId = PrototypeInteractableRegistry.PrototypeTerminalId, }; + // Act var response = await client.PostAsJsonAsync("/game/players/nobody-here/interact", req); + // Assert Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } [Fact] public async Task PostInteract_ShouldReflectNewPosition_AfterMove() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); + // Act var far = await client.PostAsJsonAsync( "/game/players/dev-local-1/interact", @@ -210,6 +239,7 @@ public class InteractionApiTests InteractableId = PrototypeInteractableRegistry.PrototypeTerminalId, }); var farBody = await far.Content.ReadFromJsonAsync(); + // Assert Assert.False(farBody!.Allowed); Assert.Equal(InteractionApi.ReasonOutOfRange, farBody.ReasonCode); @@ -233,7 +263,9 @@ public class InteractionApiTests [Fact] public async Task PostInteract_ShouldAllowResourceNode_WhenPlayerInRangeOfResourceAnchor() - { + { // Act + // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); var move = new MoveCommandRequest @@ -241,6 +273,7 @@ public class InteractionApiTests SchemaVersion = MoveCommandRequest.CurrentSchemaVersion, Target = new PositionVector { X = 12, Y = 0.9, Z = -6 }, }; + // Assert Assert.Equal(HttpStatusCode.OK, (await client.PostAsJsonAsync("/game/players/dev-local-1/move", move)).StatusCode); var response = await client.PostAsJsonAsync( @@ -260,9 +293,11 @@ public class InteractionApiTests [Fact] public async Task PostInteract_ShouldDenyOutOfRange_ForResourceNode_WhenPlayerFar() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); + // Act var response = await client.PostAsJsonAsync( "/game/players/dev-local-1/interact", new InteractionRequest @@ -270,6 +305,7 @@ public class InteractionApiTests SchemaVersion = InteractionRequest.CurrentSchemaVersion, InteractableId = PrototypeInteractableRegistry.PrototypeResourceNodeAlphaId, }); + // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); var body = await response.Content.ReadFromJsonAsync(); diff --git a/server/NeonSprawl.Server.Tests/Game/PositionState/MoveCommandApiTests.cs b/server/NeonSprawl.Server.Tests/Game/PositionState/MoveCommandApiTests.cs index 643c1fa..e393286 100644 --- a/server/NeonSprawl.Server.Tests/Game/PositionState/MoveCommandApiTests.cs +++ b/server/NeonSprawl.Server.Tests/Game/PositionState/MoveCommandApiTests.cs @@ -110,7 +110,8 @@ public class MoveCommandApiTests [Fact] public async Task PostMove_ShouldReturn400WithReason_WhenHorizontalStepTooLarge() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory().WithWebHostBuilder(b => { b.ConfigureTestServices(services => @@ -128,8 +129,10 @@ public class MoveCommandApiTests SchemaVersion = MoveCommandRequest.CurrentSchemaVersion, Target = new PositionVector { X = 0, Y = 0.9, Z = 0 }, }; + // Act var response = await client.PostAsJsonAsync("/game/players/dev-local-1/move", cmd); + // Assert Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var body = await response.Content.ReadFromJsonAsync(); @@ -140,7 +143,8 @@ public class MoveCommandApiTests [Fact] public async Task PostMove_ShouldReturn400WithReason_WhenVerticalStepTooLarge() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory().WithWebHostBuilder(b => { b.ConfigureTestServices(services => @@ -154,8 +158,10 @@ public class MoveCommandApiTests SchemaVersion = MoveCommandRequest.CurrentSchemaVersion, Target = new PositionVector { X = -5, Y = 2.0, Z = -5 }, }; + // Act var response = await client.PostAsJsonAsync("/game/players/dev-local-1/move", cmd); + // Assert Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var body = await response.Content.ReadFromJsonAsync(); @@ -165,12 +171,15 @@ public class MoveCommandApiTests [Fact] public async Task PostMove_ShouldReturn400WithoutReasonBody_WhenSchemaInvalid() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); + // Act var response = await client.PostAsync( "/game/players/dev-local-1/move", new StringContent("{\"schemaVersion\":999,\"target\":{\"x\":0,\"y\":0,\"z\":0}}", Encoding.UTF8, "application/json")); + // Assert Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var text = await response.Content.ReadAsStringAsync(); diff --git a/server/NeonSprawl.Server.Tests/Game/PositionState/MoveCommandValidationTests.cs b/server/NeonSprawl.Server.Tests/Game/PositionState/MoveCommandValidationTests.cs index 4d45c09..dfb3016 100644 --- a/server/NeonSprawl.Server.Tests/Game/PositionState/MoveCommandValidationTests.cs +++ b/server/NeonSprawl.Server.Tests/Game/PositionState/MoveCommandValidationTests.cs @@ -23,79 +23,106 @@ public class MoveCommandValidationTests [Fact] public void TryValidate_ShouldAllow_WhenHorizontalExactlyAtMax() - { + { // Act + // Arrange + var from = new PositionSnapshot(0, 0, 0, 0); var to = new PositionVector { X = 5, Y = 0, Z = 0 }; + // Assert Assert.True(MoveCommandValidation.TryValidate(from, to, Rules(5, 10), out var code)); Assert.Equal("", code); } [Fact] public void TryValidate_ShouldRejectHorizontal_WhenJustOutsideMax() - { + { // Act + // Arrange + var from = new PositionSnapshot(0, 0, 0, 0); var to = new PositionVector { X = 5 + 1e-6, Y = 0, Z = 0 }; + // Assert Assert.False(MoveCommandValidation.TryValidate(from, to, Rules(5, 10), out var code)); Assert.Equal(MoveCommandReasonCodes.HorizontalStepExceeded, code); } [Fact] public void TryValidate_ShouldAllow_WhenVerticalExactlyAtMax() - { + { // Act + // Arrange + var from = new PositionSnapshot(0, 1, 0, 0); var to = new PositionVector { X = 0, Y = 2.5, Z = 0 }; + // Assert Assert.True(MoveCommandValidation.TryValidate(from, to, Rules(10, 1.5), out _)); } [Fact] public void TryValidate_ShouldRejectVertical_WhenJustOutsideMax() - { + { // Act + // Arrange + var from = new PositionSnapshot(0, 1, 0, 0); var to = new PositionVector { X = 0, Y = 2.5 + 1e-9, Z = 0 }; + // Assert Assert.False(MoveCommandValidation.TryValidate(from, to, Rules(10, 1.5), out var code)); Assert.Equal(MoveCommandReasonCodes.VerticalStepExceeded, code); } [Fact] public void TryValidate_ShouldPreferHorizontalReason_WhenBothWouldFail() - { + { // Act + // Arrange + var from = new PositionSnapshot(0, 0, 0, 0); var to = new PositionVector { X = 100, Y = 100, Z = 0 }; + // Assert Assert.False(MoveCommandValidation.TryValidate(from, to, Rules(1, 1), out var code)); Assert.Equal(MoveCommandReasonCodes.HorizontalStepExceeded, code); } [Fact] public void TryValidate_ShouldAllow_WhenHorizontalDisabled() - { + { // Act + // Arrange + var from = new PositionSnapshot(0, 0, 0, 0); var to = new PositionVector { X = 1000, Y = 0, Z = 1000 }; + // Assert Assert.True(MoveCommandValidation.TryValidate(from, to, Rules(1, 10, horizontalEnabled: false), out var code)); Assert.Equal("", code); } [Fact] public void TryValidate_ShouldIgnoreBounds_WhenDistrictDisabled() - { + { // Act + // Arrange + var from = new PositionSnapshot(0, 0, 0, 0); var to = new PositionVector { X = -50, Y = 0, Z = 0 }; + // Assert Assert.True(MoveCommandValidation.TryValidate(from, to, Rules(100, 100, bounds: false), out _)); } [Fact] public void TryValidate_ShouldRejectOutOfBounds_WhenDistrictEnabled() - { + { // Act + // Arrange + var from = new PositionSnapshot(5, 5, 5, 0); var to = new PositionVector { X = 5, Y = 5, Z = 11 }; + // Assert Assert.False(MoveCommandValidation.TryValidate(from, to, Rules(20, 20, bounds: true), out var code)); Assert.Equal(MoveCommandReasonCodes.OutOfBounds, code); } [Fact] public void TryValidate_ShouldAllowOnBoundsSurface_WhenDistrictEnabled() - { + { // Act + // Arrange + var from = new PositionSnapshot(5, 5, 5, 0); var to = new PositionVector { X = 10, Y = 10, Z = 10 }; + // Assert Assert.True(MoveCommandValidation.TryValidate(from, to, Rules(20, 20, bounds: true), out _)); } } diff --git a/server/NeonSprawl.Server.Tests/Game/PositionState/MoveStreamApiTests.cs b/server/NeonSprawl.Server.Tests/Game/PositionState/MoveStreamApiTests.cs index 186c88a..38cf073 100644 --- a/server/NeonSprawl.Server.Tests/Game/PositionState/MoveStreamApiTests.cs +++ b/server/NeonSprawl.Server.Tests/Game/PositionState/MoveStreamApiTests.cs @@ -12,7 +12,8 @@ public sealed class MoveStreamApiTests { [Fact] public async Task PostMoveStream_ShouldApplyChainAndIncrementSequence_WhenTwoSmallSteps() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory().WithWebHostBuilder(b => { b.ConfigureTestServices(services => @@ -25,7 +26,9 @@ public sealed class MoveStreamApiTests }); }); var client = factory.CreateClient(); + // Act var before = await client.GetFromJsonAsync("/game/players/dev-local-1/position"); + // Assert Assert.NotNull(before); var seq0 = before!.Sequence; @@ -52,7 +55,8 @@ public sealed class MoveStreamApiTests [Fact] public async Task PostMoveStream_ShouldReturnNotFound_WhenPlayerIsUnknown() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); var body = new MoveStreamRequest @@ -60,30 +64,36 @@ public sealed class MoveStreamApiTests SchemaVersion = MoveStreamRequest.CurrentSchemaVersion, Targets = [new PositionVector { X = 0, Y = 0.9, Z = 0 }], }; + // Act var response = await client.PostAsJsonAsync("/game/players/unknown-player/move-stream", body); + // Assert Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } [Fact] public async Task PostMoveStream_ShouldReturn400_WhenTargetsEmpty() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); var content = new StringContent( "{\"schemaVersion\":1,\"targets\":[]}", Encoding.UTF8, "application/json"); + // Act var response = await client.PostAsync("/game/players/dev-local-1/move-stream", content); + // Assert Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); } [Fact] public async Task PostMoveStream_ShouldReturn400WithReason_WhenSecondStepExceedsHorizontalLimit() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory().WithWebHostBuilder(b => { b.ConfigureTestServices(services => @@ -105,8 +115,10 @@ public sealed class MoveStreamApiTests new PositionVector { X = -4.0, Y = 0.9, Z = -5.0 }, ], }; + // Act var response = await client.PostAsJsonAsync("/game/players/dev-local-1/move-stream", body); + // Assert Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var rej = await response.Content.ReadFromJsonAsync(); diff --git a/server/NeonSprawl.Server.Tests/Game/Targeting/PlayerTargetStateReaderTests.cs b/server/NeonSprawl.Server.Tests/Game/Targeting/PlayerTargetStateReaderTests.cs index f7c8cd7..bdd600f 100644 --- a/server/NeonSprawl.Server.Tests/Game/Targeting/PlayerTargetStateReaderTests.cs +++ b/server/NeonSprawl.Server.Tests/Game/Targeting/PlayerTargetStateReaderTests.cs @@ -8,15 +8,21 @@ public class PlayerTargetStateReaderTests { [Fact] public void ComputeValidity_ShouldReturnInvalidTarget_WhenLockIdNotInRegistry() - { + { // Act + // Arrange + var snap = new PositionSnapshot(0, 0, 0, 0); + // Assert Assert.Equal(TargetValidity.InvalidTarget, PlayerTargetStateReader.ComputeValidity("not_in_registry", in snap)); } [Fact] public void ComputeValidity_ShouldReturnNone_WhenLockNull() - { + { // Act + // Arrange + var snap = new PositionSnapshot(0, 0, 0, 0); + // Assert Assert.Equal(TargetValidity.None, PlayerTargetStateReader.ComputeValidity(null, in snap)); } @@ -24,7 +30,10 @@ public class PlayerTargetStateReaderTests public void ComputeValidity_AtExactLockRadius_ReturnsOk() { // Alpha anchor XZ (-3, -3), lockRadius 6 → horizontal distance 6 from (3, -3) is on boundary (inclusive). + // Act + // Arrange var snap = new PositionSnapshot(3, 0, -3, 0); + // Assert Assert.Equal( TargetValidity.Ok, PlayerTargetStateReader.ComputeValidity(PrototypeTargetRegistry.PrototypeTargetAlphaId, in snap)); @@ -32,9 +41,12 @@ public class PlayerTargetStateReaderTests [Fact] public void ComputeValidity_JustOutsideLockRadius_ReturnsOutOfRange() - { + { // Act + // Arrange + const double eps = 1e-6; var snap = new PositionSnapshot(3 + eps, 0, -3, 0); + // Assert Assert.Equal( TargetValidity.OutOfRange, PlayerTargetStateReader.ComputeValidity(PrototypeTargetRegistry.PrototypeTargetAlphaId, in snap)); @@ -45,7 +57,10 @@ public class PlayerTargetStateReaderTests { // NEO-24 post-merge: alpha (-3,-3) r=6 and beta (3,3) r=6 overlap at origin so Tab can // flip without locomotion. Origin is ~4.24 m from each anchor (< 6 m). + // Act + // Arrange var snap = new PositionSnapshot(0, 0, 0, 0); + // Assert Assert.Equal( TargetValidity.Ok, PlayerTargetStateReader.ComputeValidity(PrototypeTargetRegistry.PrototypeTargetAlphaId, in snap)); diff --git a/server/NeonSprawl.Server.Tests/Game/Targeting/TargetingApiTests.cs b/server/NeonSprawl.Server.Tests/Game/Targeting/TargetingApiTests.cs index 3d28883..6ea1460 100644 --- a/server/NeonSprawl.Server.Tests/Game/Targeting/TargetingApiTests.cs +++ b/server/NeonSprawl.Server.Tests/Game/Targeting/TargetingApiTests.cs @@ -12,11 +12,14 @@ public class TargetingApiTests { [Fact] public async Task GetTarget_ShouldReturnNone_WhenNoLock() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); + // Act var response = await client.GetAsync("/game/players/dev-local-1/target"); + // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); var body = await response.Content.ReadFromJsonAsync(); Assert.NotNull(body); @@ -29,9 +32,11 @@ public class TargetingApiTests [Fact] public async Task PostSelect_ShouldApplyLock_WhenInRange() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); + // Act var response = await client.PostAsJsonAsync( "/game/players/dev-local-1/target/select", @@ -40,6 +45,7 @@ public class TargetingApiTests SchemaVersion = TargetSelectRequest.CurrentSchemaVersion, TargetId = PrototypeTargetRegistry.PrototypeTargetAlphaId, }); + // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); var body = await response.Content.ReadFromJsonAsync(); @@ -53,9 +59,11 @@ public class TargetingApiTests [Fact] public async Task PostSelect_ShouldDenyUnknownTarget_WithReasonCode() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); + // Act var response = await client.PostAsJsonAsync( "/game/players/dev-local-1/target/select", @@ -64,6 +72,7 @@ public class TargetingApiTests SchemaVersion = TargetSelectRequest.CurrentSchemaVersion, TargetId = "not_a_real_target", }); + // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); var body = await response.Content.ReadFromJsonAsync(); @@ -76,9 +85,11 @@ public class TargetingApiTests [Fact] public async Task PostSelect_ShouldDenyOutOfRange_WhenBetaFarFromSpawn() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); + // Act var response = await client.PostAsJsonAsync( "/game/players/dev-local-1/target/select", @@ -87,6 +98,7 @@ public class TargetingApiTests SchemaVersion = TargetSelectRequest.CurrentSchemaVersion, TargetId = PrototypeTargetRegistry.PrototypeTargetBetaId, }); + // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); var body = await response.Content.ReadFromJsonAsync(); @@ -102,8 +114,10 @@ public class TargetingApiTests // NEO-24 follow-up #5: without the hint, spawn's stored position (-5, -5) denies beta (r=6, // anchor (3, 3)) — ~11.3 m out. With an advisory `positionHint` pinning the capsule inside // beta's ring, the server must accept. This is exactly the race-free path the client uses. + // Arrange await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); + // Act var response = await client.PostAsJsonAsync( "/game/players/dev-local-1/target/select", @@ -113,6 +127,7 @@ public class TargetingApiTests TargetId = PrototypeTargetRegistry.PrototypeTargetBetaId, PositionHint = new PositionVector { X = 3.0, Y = 0.5, Z = 5.0 }, }); + // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); var body = await response.Content.ReadFromJsonAsync(); @@ -127,8 +142,10 @@ public class TargetingApiTests { // Hint that places the capsule outside beta's ring must deny — hint is advisory only; // it does not bypass the radius check. + // Arrange await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); + // Act var response = await client.PostAsJsonAsync( "/game/players/dev-local-1/target/select", @@ -138,6 +155,7 @@ public class TargetingApiTests TargetId = PrototypeTargetRegistry.PrototypeTargetBetaId, PositionHint = new PositionVector { X = 50.0, Y = 0.0, Z = 50.0 }, }); + // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); var body = await response.Content.ReadFromJsonAsync(); @@ -151,10 +169,13 @@ public class TargetingApiTests { // Hint must be purely advisory for the range check. Confirm the stored `PositionState` // is untouched by a select-with-hint by reading it back through `GET /position`. + // Arrange await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); + // Act var before = await client.GetFromJsonAsync("/game/players/dev-local-1/position"); + // Assert Assert.NotNull(before); Assert.Equal( @@ -178,9 +199,12 @@ public class TargetingApiTests [Fact] public async Task PostSelect_ShouldClearLock_WhenTargetIdOmitted() - { + { // Act + // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); + // Assert Assert.Equal( HttpStatusCode.OK, @@ -209,9 +233,12 @@ public class TargetingApiTests [Fact] public async Task GetTarget_ShouldShowOutOfRange_AfterMoveWithoutSelect() - { + { // Act + // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); + // Assert Assert.Equal( HttpStatusCode.OK, @@ -240,19 +267,24 @@ public class TargetingApiTests [Fact] public async Task GetTarget_ShouldReturnNotFound_WhenPlayerUnknown() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); + // Act var response = await client.GetAsync("/game/players/missing-player/target"); + // Assert Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } [Fact] public async Task PostSelect_ShouldReturnNotFound_WhenPlayerUnknown() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); + // Act var response = await client.PostAsJsonAsync( "/game/players/missing-player/target/select", @@ -261,15 +293,18 @@ public class TargetingApiTests SchemaVersion = TargetSelectRequest.CurrentSchemaVersion, TargetId = PrototypeTargetRegistry.PrototypeTargetAlphaId, }); + // Assert Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } [Fact] public async Task PostSelect_ShouldReturnBadRequest_WhenSchemaVersionWrong() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); + // Act var response = await client.PostAsJsonAsync( "/game/players/dev-local-1/target/select", @@ -278,15 +313,18 @@ public class TargetingApiTests SchemaVersion = 999, TargetId = PrototypeTargetRegistry.PrototypeTargetAlphaId, }); + // Assert Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); } [Fact] public async Task PostSelect_ShouldReturnBadRequest_WhenTargetIdWhitespaceOnly() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); + // Act var response = await client.PostAsJsonAsync( "/game/players/dev-local-1/target/select", @@ -295,15 +333,18 @@ public class TargetingApiTests SchemaVersion = TargetSelectRequest.CurrentSchemaVersion, TargetId = " \t ", }); + // Assert Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); } [Fact] public async Task PostSelect_ShouldBeCaseInsensitive_ForTargetId() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); + // Act var response = await client.PostAsJsonAsync( "/game/players/dev-local-1/target/select", @@ -312,6 +353,7 @@ public class TargetingApiTests SchemaVersion = TargetSelectRequest.CurrentSchemaVersion, TargetId = " PROTOTYPE_TARGET_ALPHA ", }); + // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); var body = await response.Content.ReadFromJsonAsync(); @@ -322,7 +364,8 @@ public class TargetingApiTests [Fact] public async Task PostSelect_ShouldStayIdempotent_WhenSameTargetReselect() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); @@ -331,8 +374,10 @@ public class TargetingApiTests SchemaVersion = TargetSelectRequest.CurrentSchemaVersion, TargetId = PrototypeTargetRegistry.PrototypeTargetAlphaId, }; + // Act var first = await client.PostAsJsonAsync("/game/players/dev-local-1/target/select", req); var second = await client.PostAsJsonAsync("/game/players/dev-local-1/target/select", req); + // Assert Assert.Equal(HttpStatusCode.OK, first.StatusCode); Assert.Equal(HttpStatusCode.OK, second.StatusCode); var b1 = await first.Content.ReadFromJsonAsync(); @@ -342,9 +387,11 @@ public class TargetingApiTests [Fact] public async Task PostSelect_ShouldDenyOutOfRange_WhenReselectingSameLockWhileOutOfRange() - { + { // Arrange + await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); + // Act await client.PostAsJsonAsync( "/game/players/dev-local-1/target/select", @@ -368,6 +415,7 @@ public class TargetingApiTests SchemaVersion = TargetSelectRequest.CurrentSchemaVersion, TargetId = PrototypeTargetRegistry.PrototypeTargetAlphaId, }); + // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); var body = await response.Content.ReadFromJsonAsync();