using System.Net; using System.Net.Http.Json; using System.Text; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using NeonSprawl.Server.Game.PositionState; using Xunit; namespace NeonSprawl.Server.Tests.Game.PositionState; public sealed class MoveStreamApiTests { [Fact] public async Task PostMoveStream_ShouldApplyChainAndIncrementSequence_WhenTwoSmallSteps() { await using var factory = new InMemoryWebApplicationFactory().WithWebHostBuilder(b => { b.ConfigureTestServices(services => { services.PostConfigure(o => { o.MovementValidation.HorizontalStepEnabled = true; o.MovementValidation.MaxHorizontalStep = 1.0; }); }); }); var client = factory.CreateClient(); var before = await client.GetFromJsonAsync("/game/players/dev-local-1/position"); Assert.NotNull(before); var seq0 = before!.Sequence; var body = new MoveStreamRequest { SchemaVersion = MoveStreamRequest.CurrentSchemaVersion, Targets = [ new PositionVector { X = -4.9, Y = 0.9, Z = -5.0 }, new PositionVector { X = -4.8, Y = 0.9, Z = -5.0 }, ], }; var postResponse = await client.PostAsJsonAsync("/game/players/dev-local-1/move-stream", body); var postBody = await postResponse.Content.ReadFromJsonAsync(); Assert.Equal(HttpStatusCode.OK, postResponse.StatusCode); Assert.NotNull(postBody); Assert.Equal(seq0 + 2, postBody!.Sequence); Assert.Equal(-4.8, postBody.Position.X); Assert.Equal(0.9, postBody.Position.Y); Assert.Equal(-5.0, postBody.Position.Z); } [Fact] public async Task PostMoveStream_ShouldReturnNotFound_WhenPlayerIsUnknown() { await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); var body = new MoveStreamRequest { SchemaVersion = MoveStreamRequest.CurrentSchemaVersion, Targets = [new PositionVector { X = 0, Y = 0.9, Z = 0 }], }; var response = await client.PostAsJsonAsync("/game/players/unknown-player/move-stream", body); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } [Fact] public async Task PostMoveStream_ShouldReturn400_WhenTargetsEmpty() { await using var factory = new InMemoryWebApplicationFactory(); var client = factory.CreateClient(); var content = new StringContent( "{\"schemaVersion\":1,\"targets\":[]}", Encoding.UTF8, "application/json"); var response = await client.PostAsync("/game/players/dev-local-1/move-stream", content); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); } [Fact] public async Task PostMoveStream_ShouldReturn400WithReason_WhenSecondStepExceedsHorizontalLimit() { await using var factory = new InMemoryWebApplicationFactory().WithWebHostBuilder(b => { b.ConfigureTestServices(services => { services.PostConfigure(o => { o.MovementValidation.HorizontalStepEnabled = true; o.MovementValidation.MaxHorizontalStep = 0.2; }); }); }); var client = factory.CreateClient(); var body = new MoveStreamRequest { SchemaVersion = MoveStreamRequest.CurrentSchemaVersion, Targets = [ new PositionVector { X = -4.95, Y = 0.9, Z = -5.0 }, new PositionVector { X = -4.0, Y = 0.9, Z = -5.0 }, ], }; var response = await client.PostAsJsonAsync("/game/players/dev-local-1/move-stream", body); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var rej = await response.Content.ReadFromJsonAsync(); Assert.NotNull(rej); Assert.Equal(MoveCommandReasonCodes.HorizontalStepExceeded, rej!.ReasonCode); var after = await client.GetFromJsonAsync("/game/players/dev-local-1/position"); Assert.NotNull(after); Assert.Equal(-5.0, after!.Position.X); } }