neon-sprawl/server/NeonSprawl.Server.Tests/Game/Gigs/GigProgressionGrantPersiste...

108 lines
4.4 KiB
C#

using NeonSprawl.Server.Game.AbilityInput;
using NeonSprawl.Server.Game.Gigs;
using NeonSprawl.Server.Game.PositionState;
using NeonSprawl.Server.Game.Targeting;
using NeonSprawl.Server.Tests.Game.PositionState;
namespace NeonSprawl.Server.Tests.Game.Gigs;
[Collection("Postgres integration")]
public sealed class GigProgressionGrantPersistenceIntegrationTests(PostgresIntegrationHarness harness)
{
private PostgresWebApplicationFactory Factory => harness.Factory;
[RequirePostgresFact]
public async Task DefeatGrantThenGetAcrossNewFactory_ShouldPersistGigXp()
{
// Arrange
await ResetGigProgressionTableAsync();
var cast = new AbilityCastRequest
{
SchemaVersion = AbilityCastRequest.CurrentSchemaVersion,
SlotIndex = 0,
AbilityId = PrototypeAbilityRegistry.PrototypePulse,
TargetId = PrototypeNpcRegistry.PrototypeNpcMeleeId,
};
// Act — defeat alpha through first host (four pulses)
using (var firstClient = Factory.CreateClient())
{
await firstClient.PostAsJsonAsync(
"/game/players/dev-local-1/hotbar-loadout",
new HotbarLoadoutUpdateRequest
{
SchemaVersion = HotbarLoadoutUpdateRequest.CurrentSchemaVersion,
Slots = [new HotbarSlotBindingJson { SlotIndex = 0, AbilityId = PrototypeAbilityRegistry.PrototypePulse }],
});
await firstClient.PostAsJsonAsync(
"/game/players/dev-local-1/target/select",
new TargetSelectRequest
{
SchemaVersion = TargetSelectRequest.CurrentSchemaVersion,
TargetId = PrototypeNpcRegistry.PrototypeNpcMeleeId,
});
for (var i = 0; i < 4; i++)
{
var response = await firstClient.PostAsJsonAsync("/game/players/dev-local-1/ability-cast", cast);
response.EnsureSuccessStatusCode();
if (i < 3)
{
await Task.Delay(TimeSpan.FromSeconds(3.2));
}
}
}
await using var secondFactory = new PostgresWebApplicationFactory();
using var secondClient = secondFactory.CreateClient();
var snapshot = await secondClient.GetFromJsonAsync<GigProgressionSnapshotResponse>(
"/game/players/dev-local-1/gig-progression");
// Assert
Assert.NotNull(snapshot);
var breach = Assert.Single(snapshot!.Gigs!);
Assert.Equal(GigProgressionConstants.CombatDefeatXpAmount, breach.Xp);
}
private async Task ResetGigProgressionTableAsync()
{
var cs = Environment.GetEnvironmentVariable("ConnectionStrings__NeonSprawl");
if (string.IsNullOrWhiteSpace(cs))
{
throw new InvalidOperationException("ConnectionStrings__NeonSprawl is not set.");
}
_ = Factory.Services;
var options = Factory.Services.GetRequiredService<IOptions<GamePositionOptions>>().Value;
var positionDdlPath = Path.Combine(AppContext.BaseDirectory, "db", "migrations", "V001__player_position.sql");
var gigDdlPath = Path.Combine(AppContext.BaseDirectory, "db", "migrations", "V007__player_gig_progression.sql");
if (!File.Exists(positionDdlPath))
{
throw new FileNotFoundException($"Test DDL not found at '{positionDdlPath}'.", positionDdlPath);
}
if (!File.Exists(gigDdlPath))
{
throw new FileNotFoundException($"Test DDL not found at '{gigDdlPath}'.", gigDdlPath);
}
var positionDdl = await File.ReadAllTextAsync(positionDdlPath);
var gigDdl = await File.ReadAllTextAsync(gigDdlPath);
await using var conn = new NpgsqlConnection(cs);
await conn.OpenAsync();
await using var applyPosition = new NpgsqlCommand(positionDdl, conn);
await applyPosition.ExecuteNonQueryAsync();
await using var truncate = new NpgsqlCommand("TRUNCATE player_position CASCADE;", conn);
await truncate.ExecuteNonQueryAsync();
PostgresPositionBootstrap.SeedDevPlayer(conn, options);
await using var applyGig = new NpgsqlCommand(gigDdl, conn);
await applyGig.ExecuteNonQueryAsync();
await using var truncateGig = new NpgsqlCommand("TRUNCATE player_gig_progression;", conn);
await truncateGig.ExecuteNonQueryAsync();
}
}