77 lines
3.2 KiB
C#
77 lines
3.2 KiB
C#
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Options;
|
|
using NeonSprawl.Server.Game.Mastery;
|
|
using NeonSprawl.Server.Game.PositionState;
|
|
using NeonSprawl.Server.Game.Skills;
|
|
using NeonSprawl.Server.Tests.Game.PositionState;
|
|
using Npgsql;
|
|
using Xunit;
|
|
|
|
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();
|
|
}
|
|
}
|