neon-sprawl/server/NeonSprawl.Server.Tests/Game/Mastery/PerkStatePersistenceIntegra...

72 lines
3.1 KiB
C#

using NeonSprawl.Server.Game.Mastery;
using NeonSprawl.Server.Game.PositionState;
using NeonSprawl.Server.Tests.Game.PositionState;
namespace NeonSprawl.Server.Tests.Game.Mastery;
[Collection("Postgres integration")]
public sealed class PerkStatePersistenceIntegrationTests(PostgresIntegrationHarness harness)
{
private PostgresWebApplicationFactory Factory => harness.Factory;
[RequirePostgresFact]
public async Task BranchPickAndUnlock_ShouldPersistAcrossNewFactory()
{
// Arrange
await ResetPerkTablesAsync();
const string playerId = "dev-local-1";
using var scope = Factory.Services.CreateScope();
var xpStore = scope.ServiceProvider.GetRequiredService<IPlayerSkillProgressionStore>();
var engine = scope.ServiceProvider.GetRequiredService<PerkUnlockEngine>();
Assert.True(xpStore.TryApplyXpDelta(playerId, "salvage", 450, out _, out _));
Assert.True(engine.TrySelectBranch(playerId, "salvage", 1, "scrap_efficiency").Success);
_ = engine.ReevaluateAfterLevelUp(playerId, "salvage", newLevel: 4);
// Act
PerkStateSnapshot snapshot;
await using var secondFactory = new PostgresWebApplicationFactory();
using var secondScope = secondFactory.Services.CreateScope();
var perkStore = secondScope.ServiceProvider.GetRequiredService<IPlayerPerkStateStore>();
snapshot = perkStore.GetSnapshot(playerId);
// Assert
Assert.Equal("scrap_efficiency", snapshot.BranchPicksBySkillId["salvage"][1]);
Assert.Contains("salvage_scrap_efficiency_1", snapshot.UnlockedPerkIds);
}
private async Task ResetPerkTablesAsync()
{
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 perkDdlPath = Path.Combine(AppContext.BaseDirectory, "db", "migrations", "V004__player_perk_state.sql");
if (!File.Exists(positionDdlPath) || !File.Exists(perkDdlPath))
{
throw new FileNotFoundException("Test DDL for perk persistence not found.");
}
var positionDdl = await File.ReadAllTextAsync(positionDdlPath);
var perkDdl = await File.ReadAllTextAsync(perkDdlPath);
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 applyPerk = new NpgsqlCommand(perkDdl, conn);
await applyPerk.ExecuteNonQueryAsync();
}
}