neon-sprawl/server/NeonSprawl.Server.Tests/Game/Factions/FactionGateOperationsTests.cs

132 lines
5.4 KiB
C#

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using NeonSprawl.Server.Game.Factions;
using NeonSprawl.Server.Game.PositionState;
using NeonSprawl.Server.Game.Quests;
using NeonSprawl.Server.Tests;
using Xunit;
namespace NeonSprawl.Server.Tests.Game.Factions;
public sealed class FactionGateOperationsTests
{
private const string PlayerId = "dev-local-1";
private const string GridFactionId = PrototypeE7M3QuestFactionRules.GridContractGateFactionId;
private const string RustCollectiveFactionId = "prototype_faction_rust_collective";
private const int GridMinStanding = PrototypeE7M3QuestFactionRules.GridContractMinStanding;
[Fact]
public void TryEvaluate_ShouldPass_WhenRulesEmpty()
{
// Arrange
var standingStore = CreateStandingStore();
// Act
var outcome = FactionGateOperations.TryEvaluate(PlayerId, [], standingStore);
// Assert
Assert.True(outcome.Success);
Assert.Null(outcome.ReasonCode);
Assert.Null(outcome.FailingFactionId);
}
[Fact]
public void TryEvaluate_ShouldPass_WhenStandingAtMin()
{
// Arrange
var standingStore = CreateStandingStore();
Assert.True(standingStore.TryApplyStandingDelta(PlayerId, GridFactionId, GridMinStanding).Success);
var rules = new[] { new FactionGateRuleRow(GridFactionId, GridMinStanding) };
// Act
var outcome = FactionGateOperations.TryEvaluate(PlayerId, rules, standingStore);
// Assert
Assert.True(outcome.Success);
Assert.Null(outcome.ReasonCode);
}
[Fact]
public void TryEvaluate_ShouldDeny_WhenStandingBelowMin()
{
// Arrange
var standingStore = CreateStandingStore();
Assert.True(standingStore.TryApplyStandingDelta(PlayerId, GridFactionId, GridMinStanding - 1).Success);
var rules = new[] { new FactionGateRuleRow(GridFactionId, GridMinStanding) };
// Act
var outcome = FactionGateOperations.TryEvaluate(PlayerId, rules, standingStore);
// Assert
Assert.False(outcome.Success);
Assert.Equal(FactionGateEvaluateReasonCodes.GateBlocked, outcome.ReasonCode);
Assert.Equal(GridFactionId, outcome.FailingFactionId);
Assert.Equal(GridMinStanding, outcome.RequiredMinStanding);
Assert.Equal(GridMinStanding - 1, outcome.CurrentStanding);
}
[Fact]
public void TryEvaluate_ShouldDeny_WhenSecondRuleFailsInAndList()
{
// Arrange
var standingStore = CreateStandingStore();
Assert.True(standingStore.TryApplyStandingDelta(PlayerId, GridFactionId, GridMinStanding).Success);
var rules = new[]
{
new FactionGateRuleRow(GridFactionId, GridMinStanding),
new FactionGateRuleRow(RustCollectiveFactionId, 1),
};
// Act
var outcome = FactionGateOperations.TryEvaluate(PlayerId, rules, standingStore);
// Assert
Assert.False(outcome.Success);
Assert.Equal(FactionGateEvaluateReasonCodes.GateBlocked, outcome.ReasonCode);
Assert.Equal(RustCollectiveFactionId, outcome.FailingFactionId);
Assert.Equal(1, outcome.RequiredMinStanding);
Assert.Equal(0, outcome.CurrentStanding);
}
[Fact]
public void TryEvaluate_ShouldDenyUnknownFaction_WhenStoreDenies()
{
// Arrange
var standingStore = CreateStandingStore();
const string unknownFactionId = "prototype_faction_missing";
var rules = new[] { new FactionGateRuleRow(unknownFactionId, 0) };
// Act
var outcome = FactionGateOperations.TryEvaluate(PlayerId, rules, standingStore);
// Assert
Assert.False(outcome.Success);
Assert.Equal(FactionStandingReasonCodes.UnknownFaction, outcome.ReasonCode);
Assert.Equal(unknownFactionId, outcome.FailingFactionId);
}
[Fact]
public async Task Host_ShouldResolveStandingStoreForGateEval_WhenStartupSucceeds()
{
// Arrange — missing standing row reads as 0 (neutral), below grid contract minStanding 15.
await using var factory = new InMemoryWebApplicationFactory();
using var client = factory.CreateClient();
_ = await client.GetAsync("/health");
var standingStore = factory.Services.GetRequiredService<IFactionStandingStore>();
var rules = new[] { new FactionGateRuleRow(GridFactionId, GridMinStanding) };
// Act
var outcome = FactionGateOperations.TryEvaluate(PlayerId, rules, standingStore);
// Assert
Assert.False(outcome.Success);
Assert.Equal(FactionGateEvaluateReasonCodes.GateBlocked, outcome.ReasonCode);
}
private static InMemoryFactionStandingStore CreateStandingStore()
{
var registry = CreatePrototypeRegistry();
var options = Options.Create(new GamePositionOptions { DevPlayerId = PlayerId });
return new InMemoryFactionStandingStore(options, registry);
}
private static FactionDefinitionRegistry CreatePrototypeRegistry()
{
var byId = new Dictionary<string, FactionDefRow>(StringComparer.Ordinal)
{
[GridFactionId] = PrototypeE7M3FactionCatalogRules.ExpectedFactionFreeze[GridFactionId],
[RustCollectiveFactionId] = PrototypeE7M3FactionCatalogRules.ExpectedFactionFreeze[RustCollectiveFactionId],
};
var catalog = new FactionDefinitionCatalog("/tmp/catalog", byId, catalogJsonFileCount: 1);
return new FactionDefinitionRegistry(catalog);
}
}