using System.Net; using System.Net.Http.Json; using NeonSprawl.Server.Game.AbilityInput; using NeonSprawl.Server.Tests.Game.PositionState; using Npgsql; using Xunit; 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() { await ResetHotbarTableAsync(); using (var firstClient = Factory.CreateClient()) { var post = await firstClient.PostAsJsonAsync( "/game/players/dev-local-1/hotbar-loadout", new HotbarLoadoutUpdateRequest { SchemaVersion = HotbarLoadoutUpdateRequest.CurrentSchemaVersion, Slots = [ new HotbarSlotBindingJson { SlotIndex = 1, AbilityId = PrototypeAbilityRegistry.PrototypePulse, }, new HotbarSlotBindingJson { SlotIndex = 4, AbilityId = PrototypeAbilityRegistry.PrototypeGuard, }, ], }); Assert.Equal(HttpStatusCode.OK, post.StatusCode); } await using var secondFactory = new PostgresWebApplicationFactory(); using var secondClient = secondFactory.CreateClient(); var loadout = await secondClient.GetFromJsonAsync( "/game/players/dev-local-1/hotbar-loadout"); 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(); } } }