137 lines
4.7 KiB
C#
137 lines
4.7 KiB
C#
using Microsoft.Extensions.DependencyInjection;
|
|
using NeonSprawl.Server.Game.Rewards;
|
|
using NeonSprawl.Server.Tests;
|
|
using Xunit;
|
|
|
|
namespace NeonSprawl.Server.Tests.Game.Rewards;
|
|
|
|
public sealed class InMemoryRewardDeliveryStoreTests
|
|
{
|
|
private const string PlayerId = "dev-local-1";
|
|
private const string QuestId = "prototype_quest_operator_chain";
|
|
private static readonly DateTimeOffset DeliveredAt = new(2026, 6, 7, 12, 0, 0, TimeSpan.Zero);
|
|
|
|
[Fact]
|
|
public void TryRecord_ShouldBeIdempotent_AtStoreLayer()
|
|
{
|
|
// Arrange
|
|
var store = new InMemoryRewardDeliveryStore();
|
|
var deliveryEvent = CreatePrototypeEvent();
|
|
// Act
|
|
var first = store.TryRecord(deliveryEvent);
|
|
var second = store.TryRecord(deliveryEvent with { DeliveredAt = DeliveredAt.AddHours(1) });
|
|
var third = store.TryRecord(
|
|
deliveryEvent with
|
|
{
|
|
PlayerId = "DEV-LOCAL-1",
|
|
QuestId = "PROTOTYPE_QUEST_OPERATOR_CHAIN",
|
|
});
|
|
// Assert
|
|
Assert.True(first);
|
|
Assert.False(second);
|
|
Assert.False(third);
|
|
Assert.True(store.TryGet(PlayerId, QuestId, out var stored));
|
|
Assert.Equal(deliveryEvent.IdempotencyKey, stored.IdempotencyKey);
|
|
Assert.Equal(DeliveredAt, stored.DeliveredAt);
|
|
Assert.Single(stored.GrantedItems);
|
|
Assert.Single(stored.GrantedSkillXp);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryRecord_ShouldFailClosed_WhenPlayerIdIsEmpty()
|
|
{
|
|
// Arrange
|
|
var store = new InMemoryRewardDeliveryStore();
|
|
var invalidEvent = CreatePrototypeEvent() with { PlayerId = " " };
|
|
// Act
|
|
var recorded = store.TryRecord(invalidEvent);
|
|
// Assert
|
|
Assert.False(recorded);
|
|
Assert.False(store.TryGet(" ", QuestId, out _));
|
|
}
|
|
|
|
[Fact]
|
|
public void TryRecord_ShouldFailClosed_WhenQuestIdIsEmpty()
|
|
{
|
|
// Arrange
|
|
var store = new InMemoryRewardDeliveryStore();
|
|
var invalidEvent = CreatePrototypeEvent() with { QuestId = " " };
|
|
// Act
|
|
var recorded = store.TryRecord(invalidEvent);
|
|
// Assert
|
|
Assert.False(recorded);
|
|
Assert.False(store.TryGet(PlayerId, " ", out _));
|
|
}
|
|
|
|
[Fact]
|
|
public void TryGet_ShouldExposeReadableGrantSummary()
|
|
{
|
|
// Arrange
|
|
var store = new InMemoryRewardDeliveryStore();
|
|
var deliveryEvent = CreatePrototypeEvent();
|
|
// Act
|
|
var recorded = store.TryRecord(deliveryEvent);
|
|
var found = store.TryGet(PlayerId, QuestId, out var stored);
|
|
// Assert
|
|
Assert.True(recorded);
|
|
Assert.True(found);
|
|
Assert.Equal("survey_drone_kit", stored.GrantedItems[0].ItemId);
|
|
Assert.Equal(1, stored.GrantedItems[0].Quantity);
|
|
Assert.Equal("salvage", stored.GrantedSkillXp[0].SkillId);
|
|
Assert.Equal(50, stored.GrantedSkillXp[0].Amount);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Host_ShouldResolveRewardDeliveryStoreFromDi_WhenStartupSucceeds()
|
|
{
|
|
// Arrange
|
|
await using var factory = new InMemoryWebApplicationFactory();
|
|
using var client = factory.CreateClient();
|
|
_ = await client.GetAsync("/health");
|
|
var deliveryStore = factory.Services.GetRequiredService<IRewardDeliveryStore>();
|
|
// Act
|
|
var recorded = deliveryStore.TryRecord(CreatePrototypeEvent());
|
|
var found = deliveryStore.TryGet(PlayerId, QuestId, out var stored);
|
|
// Assert
|
|
Assert.IsType<InMemoryRewardDeliveryStore>(deliveryStore);
|
|
Assert.True(recorded);
|
|
Assert.True(found);
|
|
Assert.Equal(RewardDeliveryIds.MakeIdempotencyKey(PlayerId, QuestId), stored.IdempotencyKey);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryClear_ShouldRemoveDeliveryRow_WhenPresent()
|
|
{
|
|
// Arrange
|
|
var store = new InMemoryRewardDeliveryStore();
|
|
var deliveryEvent = CreatePrototypeEvent();
|
|
Assert.True(store.TryRecord(deliveryEvent));
|
|
// Act
|
|
var cleared = store.TryClear(PlayerId, QuestId);
|
|
// Assert
|
|
Assert.True(cleared);
|
|
Assert.False(store.TryGet(PlayerId, QuestId, out _));
|
|
}
|
|
|
|
[Fact]
|
|
public void TryClear_ShouldReturnTrue_WhenRowAlreadyAbsent()
|
|
{
|
|
// Arrange
|
|
var store = new InMemoryRewardDeliveryStore();
|
|
// Act
|
|
var cleared = store.TryClear(PlayerId, QuestId);
|
|
// Assert
|
|
Assert.True(cleared);
|
|
}
|
|
|
|
private static RewardDeliveryEvent CreatePrototypeEvent() =>
|
|
new(
|
|
PlayerId,
|
|
QuestId,
|
|
DeliveredAt,
|
|
[new RewardItemGrantApplied("survey_drone_kit", 1)],
|
|
[new RewardSkillXpGrantApplied("salvage", 50)],
|
|
[],
|
|
RewardDeliveryIds.MakeIdempotencyKey(PlayerId, QuestId));
|
|
}
|