using NeonSprawl.Server.Game.Encounters; using Xunit; namespace NeonSprawl.Server.Tests.Game.Encounters; public sealed class InMemoryEncounterCompleteEventStoreTests { private const string PlayerId = "dev-local-1"; private const string EncounterId = "prototype_combat_pocket"; private static readonly DateTimeOffset CompletedAt = new(2026, 5, 31, 20, 0, 0, TimeSpan.Zero); [Fact] public void TryRecord_ShouldBeIdempotent_AtStoreLayer() { // Arrange var store = new InMemoryEncounterCompleteEventStore(); var completeEvent = CreatePrototypeEvent(); // Act var first = store.TryRecord(completeEvent); var second = store.TryRecord(completeEvent with { CompletedAt = CompletedAt.AddHours(1) }); var found = store.TryGet(PlayerId, EncounterId, out var stored); // Assert Assert.True(first); Assert.False(second); Assert.True(found); Assert.Equal(completeEvent.IdempotencyKey, stored.IdempotencyKey); Assert.Equal(CompletedAt, stored.CompletedAt); Assert.Equal(2, stored.GrantedItems.Count); } [Fact] public void TryRecord_ShouldFailClosed_WhenPlayerOrEncounterIdIsEmpty() { // Arrange var store = new InMemoryEncounterCompleteEventStore(); var invalidEvent = CreatePrototypeEvent() with { PlayerId = " " }; // Act var recorded = store.TryRecord(invalidEvent); var found = store.TryGet(" ", EncounterId, out _); // Assert Assert.False(recorded); Assert.False(found); } private static EncounterCompleteEvent CreatePrototypeEvent() => new( PlayerId, EncounterId, "prototype_combat_pocket_clear", [ new EncounterGrantApplied("scrap_metal_bulk", 10), new EncounterGrantApplied("contract_handoff_token", 1), ], CompletedAt, $"{PlayerId}:{EncounterId}"); }