neon-sprawl/server/NeonSprawl.Server.Tests/Game/Gathering/ResourceNodeInstancePersist...

105 lines
4.4 KiB
C#

using Microsoft.Extensions.DependencyInjection;
using NeonSprawl.Server.Game.Gathering;
using NeonSprawl.Server.Tests.Game.PositionState;
using Npgsql;
using Xunit;
namespace NeonSprawl.Server.Tests.Game.Gathering;
[Collection("Postgres integration")]
public sealed class ResourceNodeInstancePersistenceIntegrationTests(PostgresIntegrationHarness harness)
{
private PostgresWebApplicationFactory Factory => harness.Factory;
private const string AlphaId = "prototype_resource_node_alpha";
[RequirePostgresFact]
public async Task TryCommitSuccessfulGatherDecrementAcrossNewFactory_ShouldPersistRemainingGathers()
{
// Arrange
await ResetInstanceTableAsync();
ResourceNodeInstanceMutationOutcome firstOutcome;
// Act — write through first host
using (var firstScope = Factory.Services.CreateScope())
{
var registry = firstScope.ServiceProvider.GetRequiredService<IResourceNodeDefinitionRegistry>();
var store = firstScope.ServiceProvider.GetRequiredService<IResourceNodeInstanceStore>();
firstOutcome = ResourceNodeInstanceOperations.TryCommitSuccessfulGatherDecrement(AlphaId, registry, store);
}
// Act — read back through a fresh host
await using var secondFactory = new PostgresWebApplicationFactory();
using var secondScope = secondFactory.Services.CreateScope();
var readStore = secondScope.ServiceProvider.GetRequiredService<IResourceNodeInstanceStore>();
var readBack = readStore.TryGetRemainingGathers(AlphaId, out var snapshot);
// Assert
Assert.Equal(ResourceNodeInstanceMutationKind.Applied, firstOutcome.Kind);
Assert.Equal(9, firstOutcome.Snapshot!.Value.RemainingGathers);
Assert.True(readBack);
Assert.Equal(9, snapshot.RemainingGathers);
}
[RequirePostgresFact]
public async Task TryCommitSuccessfulGatherDecrement_WhenDepleted_ShouldPersistZeroRow()
{
// Arrange
await ResetInstanceTableAsync();
using (var seedScope = Factory.Services.CreateScope())
{
var registry = seedScope.ServiceProvider.GetRequiredService<IResourceNodeDefinitionRegistry>();
var store = seedScope.ServiceProvider.GetRequiredService<IResourceNodeInstanceStore>();
for (var i = 0; i < 10; i++)
{
_ = ResourceNodeInstanceOperations.TryCommitSuccessfulGatherDecrement(AlphaId, registry, store);
}
}
// Act
ResourceNodeInstanceMutationOutcome denyOutcome;
await using var secondFactory = new PostgresWebApplicationFactory();
using (var scope = secondFactory.Services.CreateScope())
{
var registry = scope.ServiceProvider.GetRequiredService<IResourceNodeDefinitionRegistry>();
var store = scope.ServiceProvider.GetRequiredService<IResourceNodeInstanceStore>();
denyOutcome = ResourceNodeInstanceOperations.TryCommitSuccessfulGatherDecrement(AlphaId, registry, store);
store.TryGetRemainingGathers(AlphaId, out var snapshot);
// Assert
Assert.Equal(ResourceNodeInstanceMutationKind.Denied, denyOutcome.Kind);
Assert.Equal(ResourceNodeInstanceReasonCodes.NodeDepleted, denyOutcome.ReasonCode);
Assert.Equal(0, snapshot.RemainingGathers);
}
}
private async Task ResetInstanceTableAsync()
{
var cs = Environment.GetEnvironmentVariable("ConnectionStrings__NeonSprawl");
if (string.IsNullOrWhiteSpace(cs))
{
throw new InvalidOperationException("ConnectionStrings__NeonSprawl is not set.");
}
_ = Factory.Services;
var ddlPath = Path.Combine(AppContext.BaseDirectory, "db", "migrations", "V006__resource_node_instance.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 resource_node_instance;", conn))
{
await truncate.ExecuteNonQueryAsync();
}
}
}