164 lines
6.0 KiB
C#
164 lines
6.0 KiB
C#
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Options;
|
|
using NeonSprawl.Server.Game.Factions;
|
|
using NeonSprawl.Server.Game.PositionState;
|
|
using Xunit;
|
|
|
|
namespace NeonSprawl.Server.Tests.Game.Factions;
|
|
|
|
public sealed class InMemoryFactionStandingStoreTests
|
|
{
|
|
private const string PlayerId = "dev-local-1";
|
|
private const string UnknownPlayerId = "unknown-player-xyz";
|
|
private const string GridFactionId = "prototype_faction_grid_operators";
|
|
private const string RustFactionId = "prototype_faction_rust_collective";
|
|
|
|
[Fact]
|
|
public void TryGetStanding_ShouldReturnZero_WhenRowMissing()
|
|
{
|
|
// Arrange
|
|
var store = CreateStore();
|
|
// Act
|
|
var outcome = store.TryGetStanding(PlayerId, GridFactionId);
|
|
// Assert
|
|
Assert.True(outcome.Success);
|
|
Assert.Null(outcome.ReasonCode);
|
|
Assert.Equal(0, outcome.Standing);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryApplyStandingDelta_ShouldReturnFifteen_WhenFirstApplyFromZero()
|
|
{
|
|
// Arrange
|
|
var store = CreateStore();
|
|
// Act
|
|
var outcome = store.TryApplyStandingDelta(PlayerId, GridFactionId, 15);
|
|
// Assert
|
|
Assert.True(outcome.Success);
|
|
Assert.Equal(0, outcome.PreviousStanding);
|
|
Assert.Equal(15, outcome.NewStanding);
|
|
var readBack = store.TryGetStanding(PlayerId, GridFactionId);
|
|
Assert.True(readBack.Success);
|
|
Assert.Equal(15, readBack.Standing);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryApplyStandingDelta_ShouldClampToMax_WhenDeltaExceedsBand()
|
|
{
|
|
// Arrange
|
|
var store = CreateStore();
|
|
Assert.True(store.TryApplyStandingDelta(PlayerId, GridFactionId, 95).Success);
|
|
// Act
|
|
var outcome = store.TryApplyStandingDelta(PlayerId, GridFactionId, 50);
|
|
// Assert
|
|
Assert.True(outcome.Success);
|
|
Assert.Equal(95, outcome.PreviousStanding);
|
|
Assert.Equal(100, outcome.NewStanding);
|
|
Assert.Equal(100, store.TryGetStanding(PlayerId, GridFactionId).Standing);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryApplyStandingDelta_ShouldClampToMin_WhenDeltaBelowBand()
|
|
{
|
|
// Arrange
|
|
var store = CreateStore();
|
|
Assert.True(store.TryApplyStandingDelta(PlayerId, GridFactionId, -95).Success);
|
|
// Act
|
|
var outcome = store.TryApplyStandingDelta(PlayerId, GridFactionId, -50);
|
|
// Assert
|
|
Assert.True(outcome.Success);
|
|
Assert.Equal(-95, outcome.PreviousStanding);
|
|
Assert.Equal(-100, outcome.NewStanding);
|
|
Assert.Equal(-100, store.TryGetStanding(PlayerId, GridFactionId).Standing);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryGetStanding_ShouldDenyUnknownFaction_WithStructuredReason()
|
|
{
|
|
// Arrange
|
|
var store = CreateStore();
|
|
// Act
|
|
var read = store.TryGetStanding(PlayerId, "not_a_real_faction");
|
|
var write = store.TryApplyStandingDelta(PlayerId, "not_a_real_faction", 5);
|
|
// Assert
|
|
Assert.False(read.Success);
|
|
Assert.Equal(FactionStandingReasonCodes.UnknownFaction, read.ReasonCode);
|
|
Assert.Equal(0, read.Standing);
|
|
Assert.False(write.Success);
|
|
Assert.Equal(FactionStandingReasonCodes.UnknownFaction, write.ReasonCode);
|
|
Assert.Equal(0, store.TryGetStanding(PlayerId, GridFactionId).Standing);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryApplyStandingDelta_ShouldDenyNonWritablePlayer()
|
|
{
|
|
// Arrange
|
|
var store = CreateStore();
|
|
// Act
|
|
var outcome = store.TryApplyStandingDelta(UnknownPlayerId, GridFactionId, 10);
|
|
// Assert
|
|
Assert.False(outcome.Success);
|
|
Assert.Equal(FactionStandingReasonCodes.PlayerNotWritable, outcome.ReasonCode);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryGetStanding_ShouldDenyNonWritablePlayer()
|
|
{
|
|
// Arrange
|
|
var store = CreateStore();
|
|
// Act
|
|
var outcome = store.TryGetStanding(UnknownPlayerId, GridFactionId);
|
|
// Assert
|
|
Assert.False(outcome.Success);
|
|
Assert.Equal(FactionStandingReasonCodes.PlayerNotWritable, outcome.ReasonCode);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryApplyStandingDelta_ShouldTrackSeparateFactionsPerPlayer()
|
|
{
|
|
// Arrange
|
|
var store = CreateStore();
|
|
// Act
|
|
Assert.True(store.TryApplyStandingDelta(PlayerId, GridFactionId, 15).Success);
|
|
Assert.True(store.TryApplyStandingDelta(PlayerId, RustFactionId, 10).Success);
|
|
// Assert
|
|
Assert.Equal(15, store.TryGetStanding(PlayerId, GridFactionId).Standing);
|
|
Assert.Equal(10, store.TryGetStanding(PlayerId, RustFactionId).Standing);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Host_ShouldResolveFactionStandingStoresFromDi_WhenStartupSucceeds()
|
|
{
|
|
// Arrange
|
|
await using var factory = new InMemoryWebApplicationFactory();
|
|
using var client = factory.CreateClient();
|
|
_ = await client.GetAsync("/health");
|
|
// Act
|
|
var standingStore = factory.Services.GetRequiredService<IFactionStandingStore>();
|
|
var auditStore = factory.Services.GetRequiredService<IReputationDeltaStore>();
|
|
var read = standingStore.TryGetStanding(PlayerId, GridFactionId);
|
|
// Assert
|
|
Assert.NotNull(auditStore);
|
|
Assert.True(read.Success);
|
|
Assert.Equal(0, read.Standing);
|
|
}
|
|
|
|
private static InMemoryFactionStandingStore CreateStore()
|
|
{
|
|
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],
|
|
[RustFactionId] = PrototypeE7M3FactionCatalogRules.ExpectedFactionFreeze[RustFactionId],
|
|
};
|
|
var catalog = new FactionDefinitionCatalog("/tmp/catalog", byId, catalogJsonFileCount: 1);
|
|
return new FactionDefinitionRegistry(catalog);
|
|
}
|
|
}
|