122 lines
5.1 KiB
C#
122 lines
5.1 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 (pulse loop; fake clock avoids wall-clock cooldown flakes)
|
|
Assert.NotNull(Factory.FakeClock);
|
|
var clock = Factory.FakeClock;
|
|
clock.Advance(TimeSpan.FromSeconds(3) + TimeSpan.FromMilliseconds(50));
|
|
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,
|
|
});
|
|
AbilityCastResponse? defeatBody = null;
|
|
for (var i = 0; i < 12; i++)
|
|
{
|
|
var response = await firstClient.PostAsJsonAsync("/game/players/dev-local-1/ability-cast", cast);
|
|
response.EnsureSuccessStatusCode();
|
|
var body = await response.Content.ReadFromJsonAsync<AbilityCastResponse>();
|
|
Assert.NotNull(body);
|
|
Assert.True(body!.Accepted, body.ReasonCode);
|
|
Assert.NotNull(body.CombatResolution);
|
|
if (body.CombatResolution!.TargetDefeated)
|
|
{
|
|
defeatBody = body;
|
|
break;
|
|
}
|
|
|
|
clock.Advance(TimeSpan.FromSeconds(3) + TimeSpan.FromMilliseconds(50));
|
|
}
|
|
|
|
Assert.NotNull(defeatBody);
|
|
Assert.True(defeatBody!.CombatResolution!.TargetDefeated);
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|