using NeonSprawl.Server.Game.AbilityInput; using NeonSprawl.Server.Tests.Game.PositionState; namespace NeonSprawl.Server.Tests.Game.AbilityInput; [Collection("Postgres integration")] public sealed class HotbarLoadoutPersistenceIntegrationTests(PostgresIntegrationHarness harness) { private PostgresWebApplicationFactory Factory => harness.Factory; [RequirePostgresFact] public async Task PostThenGetAcrossNewFactory_ShouldPersistHotbarBindings() { // Arrange await ResetHotbarTableAsync(); var update = new HotbarLoadoutUpdateRequest { SchemaVersion = HotbarLoadoutUpdateRequest.CurrentSchemaVersion, Slots = [ new HotbarSlotBindingJson { SlotIndex = 1, AbilityId = PrototypeAbilityRegistry.PrototypePulse, }, new HotbarSlotBindingJson { SlotIndex = 4, AbilityId = PrototypeAbilityRegistry.PrototypeGuard, }, ], }; HttpStatusCode postStatus; // Act using var firstClient = Factory.CreateClient(); var post = await firstClient.PostAsJsonAsync( "/game/players/dev-local-1/hotbar-loadout", update); postStatus = post.StatusCode; // Assert await using var secondFactory = new PostgresWebApplicationFactory(); using var secondClient = secondFactory.CreateClient(); var loadout = await secondClient.GetFromJsonAsync( "/game/players/dev-local-1/hotbar-loadout"); Assert.Equal(HttpStatusCode.OK, postStatus); Assert.NotNull(loadout); Assert.Equal(PrototypeAbilityRegistry.PrototypePulse, loadout!.Slots[1].AbilityId); Assert.Equal(PrototypeAbilityRegistry.PrototypeGuard, loadout.Slots[4].AbilityId); } private static async Task ResetHotbarTableAsync() { var cs = Environment.GetEnvironmentVariable("ConnectionStrings__NeonSprawl"); if (string.IsNullOrWhiteSpace(cs)) { throw new InvalidOperationException("ConnectionStrings__NeonSprawl is not set."); } var ddlPath = Path.Combine(AppContext.BaseDirectory, "db", "migrations", "V002__player_hotbar_loadout.sql"); if (!File.Exists(ddlPath)) { throw new FileNotFoundException($"Test DDL not found at '{ddlPath}'.", ddlPath); } var ddl = await File.ReadAllTextAsync(ddlPath); await using var conn = new NpgsqlConnection(cs); await conn.OpenAsync(); await using var apply = new NpgsqlCommand(ddl, conn); await apply.ExecuteNonQueryAsync(); await using var truncate = new NpgsqlCommand("TRUNCATE player_hotbar_loadout;", conn); await truncate.ExecuteNonQueryAsync(); } }