neon-sprawl/server/NeonSprawl.Server.Tests/Game/Factions/InMemoryReputationDeltaStor...

143 lines
4.9 KiB
C#

using NeonSprawl.Server.Game.Factions;
using Xunit;
namespace NeonSprawl.Server.Tests.Game.Factions;
public sealed class InMemoryReputationDeltaStoreTests
{
private const string PlayerId = "dev-local-1";
private const string GridFactionId = "prototype_faction_grid_operators";
private static readonly DateTimeOffset RecordedAt = new(2026, 6, 15, 12, 0, 0, TimeSpan.Zero);
[Fact]
public void TryAppend_ShouldReturnTrueAndQueryRow_WhenFirstAppend()
{
// Arrange
var store = CreateStore();
var row = CreateRow("delta-001", 15, 15);
// Act
var appended = store.TryAppend(row);
var rows = store.GetDeltasForPlayer(PlayerId);
// Assert
Assert.True(appended);
var read = Assert.Single(rows);
Assert.Equal(row.Id, read.Id);
Assert.Equal(15, read.DeltaAmount);
Assert.Equal(15, read.ResultingStanding);
}
[Fact]
public void TryAppend_ShouldReturnFalse_WhenDuplicateId()
{
// Arrange
var store = CreateStore();
var row = CreateRow("delta-dup", 10, 10);
Assert.True(store.TryAppend(row));
// Act
var duplicate = store.TryAppend(row with { DeltaAmount = 99, ResultingStanding = 99 });
// Assert
Assert.False(duplicate);
var read = Assert.Single(store.GetDeltasForPlayer(PlayerId));
Assert.Equal(10, read.DeltaAmount);
}
[Fact]
public void TryAppend_ShouldReturnTrueTwice_WhenDistinctIds()
{
// Arrange
var store = CreateStore();
// Act
var first = store.TryAppend(CreateRow("delta-a", 15, 15));
var second = store.TryAppend(CreateRow("delta-b", 10, 25));
// Assert
Assert.True(first);
Assert.True(second);
Assert.Equal(2, store.GetDeltasForPlayer(PlayerId).Count);
}
[Fact]
public void TryAppend_ShouldReturnFalse_WhenRowInvalid()
{
// Arrange
var store = CreateStore();
var emptyId = CreateRow("", 5, 5);
var emptyPlayer = CreateRow("delta-x", 5, 5) with { PlayerId = " " };
var emptyFaction = CreateRow("delta-y", 5, 5) with { FactionId = "" };
// Act
var noId = store.TryAppend(emptyId);
var noPlayer = store.TryAppend(emptyPlayer);
var noFaction = store.TryAppend(emptyFaction);
// Assert
Assert.False(noId);
Assert.False(noPlayer);
Assert.False(noFaction);
Assert.Empty(store.GetDeltasForPlayer(PlayerId));
}
[Fact]
public void GetDeltasForPlayer_ShouldOrderByRecordedAtThenId()
{
// Arrange
var store = CreateStore();
Assert.True(store.TryAppend(CreateRow("delta-z", 1, 1) with { RecordedAt = RecordedAt.AddMinutes(2) }));
Assert.True(store.TryAppend(CreateRow("delta-a", 2, 3) with { RecordedAt = RecordedAt }));
Assert.True(store.TryAppend(CreateRow("delta-m", 3, 6) with { RecordedAt = RecordedAt }));
// Act
var rows = store.GetDeltasForPlayer(PlayerId);
// Assert
Assert.Equal(["delta-a", "delta-m", "delta-z"], [.. rows.Select(static r => r.Id)]);
}
[Fact]
public void GetDeltasForPlayer_ShouldRespectLimit_WhenProvided()
{
// Arrange
var store = CreateStore();
Assert.True(store.TryAppend(CreateRow("delta-1", 1, 1)));
Assert.True(store.TryAppend(CreateRow("delta-2", 1, 2)));
// Act
var rows = store.GetDeltasForPlayer(PlayerId, limit: 1);
// Assert
Assert.Single(rows);
Assert.Equal("delta-1", rows[0].Id);
}
[Fact]
public void TryClearQuestCompletionAudit_ShouldRemoveMatchingRows_WhenPresent()
{
// Arrange
var store = CreateStore();
Assert.True(store.TryAppend(CreateRow("delta-forward", 15, 15)));
Assert.True(store.TryAppend(CreateRow("delta-rollback", -15, 0) with
{
Id = "delta-forward:rollback",
}));
Assert.True(store.TryAppend(CreateRow("delta-other-quest", 10, 10) with
{
Id = "other-quest-delta",
SourceId = "prototype_quest_gather_intro",
}));
// Act
var cleared = store.TryClearQuestCompletionAudit(PlayerId, "prototype_quest_operator_chain");
// Assert
Assert.True(cleared);
Assert.DoesNotContain(
store.GetDeltasForPlayer(PlayerId),
row => row.SourceId == "prototype_quest_operator_chain");
Assert.Single(store.GetDeltasForPlayer(PlayerId));
}
private static InMemoryReputationDeltaStore CreateStore() => new();
private static ReputationDeltaRow CreateRow(string id, int delta, int resulting) =>
new(
id,
PlayerId,
GridFactionId,
delta,
resulting,
ReputationDeltaSourceKinds.QuestCompletion,
"prototype_quest_operator_chain",
RecordedAt);
}