neon-sprawl/server/NeonSprawl.Server.Tests/Game/Encounters/EncounterProgressOperations...

218 lines
9.9 KiB
C#

using NeonSprawl.Server.Game.Encounters;
namespace NeonSprawl.Server.Tests.Game.Encounters;
public sealed class EncounterProgressOperationsTests
{
private const string PlayerId = "dev-local-1";
private const string EncounterId = "prototype_combat_pocket";
private const string MeleeNpc = "prototype_npc_melee";
private const string RangedNpc = "prototype_npc_ranged";
private const string EliteNpc = "prototype_npc_elite";
[Fact]
public async Task TryActivateOnFirstEngagement_ShouldStartEncounter_WhenNpcIsRequired()
{
// Arrange
await using var factory = new InMemoryWebApplicationFactory();
var registry = factory.Services.GetRequiredService<IEncounterDefinitionRegistry>();
var progress = factory.Services.GetRequiredService<IEncounterProgressStore>();
var completion = factory.Services.GetRequiredService<IEncounterCompletionStore>();
// Act
var activated = EncounterProgressOperations.TryActivateOnFirstEngagement(
PlayerId,
MeleeNpc,
registry,
progress,
completion);
// Assert
Assert.True(activated);
Assert.True(progress.TryGetProgress(PlayerId, EncounterId, out var snapshot));
Assert.True(snapshot.Started);
Assert.Empty(snapshot.DefeatedNpcInstanceIds);
Assert.False(EncounterProgressOperations.IsAllRequiredTargetsDefeated(PlayerId, EncounterId, registry, progress));
}
[Fact]
public async Task TryMarkTargetDefeated_ShouldReturnFalse_WhenNotActivated()
{
// Arrange
await using var factory = new InMemoryWebApplicationFactory();
var registry = factory.Services.GetRequiredService<IEncounterDefinitionRegistry>();
var progress = factory.Services.GetRequiredService<IEncounterProgressStore>();
var completion = factory.Services.GetRequiredService<IEncounterCompletionStore>();
// Act
var marked = EncounterProgressOperations.TryMarkTargetDefeated(
PlayerId,
MeleeNpc,
registry,
progress,
completion);
// Assert
Assert.False(marked);
}
[Fact]
public async Task IsAllRequiredTargetsDefeated_ShouldBecomeTrue_AfterThirdDefeatInAnyOrder()
{
// Arrange
await using var factory = new InMemoryWebApplicationFactory();
var registry = factory.Services.GetRequiredService<IEncounterDefinitionRegistry>();
var progress = factory.Services.GetRequiredService<IEncounterProgressStore>();
var completion = factory.Services.GetRequiredService<IEncounterCompletionStore>();
_ = EncounterProgressOperations.TryActivateOnFirstEngagement(PlayerId, EliteNpc, registry, progress, completion);
// Act
_ = EncounterProgressOperations.TryMarkTargetDefeated(PlayerId, EliteNpc, registry, progress, completion);
_ = EncounterProgressOperations.TryMarkTargetDefeated(PlayerId, MeleeNpc, registry, progress, completion);
var afterTwo = EncounterProgressOperations.IsAllRequiredTargetsDefeated(PlayerId, EncounterId, registry, progress);
_ = EncounterProgressOperations.TryMarkTargetDefeated(PlayerId, RangedNpc, registry, progress, completion);
var afterThree = EncounterProgressOperations.IsAllRequiredTargetsDefeated(PlayerId, EncounterId, registry, progress);
// Assert
Assert.False(afterTwo);
Assert.True(afterThree);
}
[Fact]
public async Task TryMarkTargetDefeated_ShouldBeIdempotent_WhenNpcAlreadyDefeated()
{
// Arrange
await using var factory = new InMemoryWebApplicationFactory();
var registry = factory.Services.GetRequiredService<IEncounterDefinitionRegistry>();
var progress = factory.Services.GetRequiredService<IEncounterProgressStore>();
var completion = factory.Services.GetRequiredService<IEncounterCompletionStore>();
_ = EncounterProgressOperations.TryActivateOnFirstEngagement(PlayerId, MeleeNpc, registry, progress, completion);
_ = EncounterProgressOperations.TryMarkTargetDefeated(PlayerId, MeleeNpc, registry, progress, completion);
// Act
var secondMark = EncounterProgressOperations.TryMarkTargetDefeated(
PlayerId,
MeleeNpc,
registry,
progress,
completion);
// Assert
Assert.True(secondMark);
Assert.True(progress.TryGetProgress(PlayerId, EncounterId, out var snapshot));
Assert.Single(snapshot.DefeatedNpcInstanceIds);
}
[Fact]
public async Task TryMarkTargetDefeated_ShouldReturnFalse_WhenEncounterAlreadyCompleted()
{
// Arrange
await using var factory = new InMemoryWebApplicationFactory();
var registry = factory.Services.GetRequiredService<IEncounterDefinitionRegistry>();
var progress = factory.Services.GetRequiredService<IEncounterProgressStore>();
var completion = factory.Services.GetRequiredService<IEncounterCompletionStore>();
_ = EncounterProgressOperations.TryActivateOnFirstEngagement(PlayerId, MeleeNpc, registry, progress, completion);
_ = completion.TryMarkCompleted(PlayerId, EncounterId, DateTimeOffset.UtcNow);
// Act
var marked = EncounterProgressOperations.TryMarkTargetDefeated(
PlayerId,
MeleeNpc,
registry,
progress,
completion);
// Assert
Assert.False(marked);
}
[Fact]
public async Task TryActivateOnFirstEngagement_ShouldReturnFalse_WhenEncounterAlreadyCompleted()
{
// Arrange
await using var factory = new InMemoryWebApplicationFactory();
var registry = factory.Services.GetRequiredService<IEncounterDefinitionRegistry>();
var progress = factory.Services.GetRequiredService<IEncounterProgressStore>();
var completion = factory.Services.GetRequiredService<IEncounterCompletionStore>();
_ = completion.TryMarkCompleted(PlayerId, EncounterId, DateTimeOffset.UtcNow);
// Act
var activated = EncounterProgressOperations.TryActivateOnFirstEngagement(
PlayerId,
MeleeNpc,
registry,
progress,
completion);
// Assert
Assert.False(activated);
Assert.False(progress.TryGetProgress(PlayerId, EncounterId, out _));
}
[Fact]
public async Task TryActivateOnFirstEngagement_ShouldReturnFalse_WhenNpcNotInAnyEncounter()
{
// Arrange
await using var factory = new InMemoryWebApplicationFactory();
var registry = factory.Services.GetRequiredService<IEncounterDefinitionRegistry>();
var progress = factory.Services.GetRequiredService<IEncounterProgressStore>();
var completion = factory.Services.GetRequiredService<IEncounterCompletionStore>();
// Act
var activated = EncounterProgressOperations.TryActivateOnFirstEngagement(
PlayerId,
"prototype_npc_unknown",
registry,
progress,
completion);
// Assert
Assert.False(activated);
}
[Fact]
public async Task IsAllRequiredTargetsDefeated_ShouldReturnTrue_WhenEncounterIdHasMixedCase()
{
// Arrange
await using var factory = new InMemoryWebApplicationFactory();
var registry = factory.Services.GetRequiredService<IEncounterDefinitionRegistry>();
var progress = factory.Services.GetRequiredService<IEncounterProgressStore>();
var completion = factory.Services.GetRequiredService<IEncounterCompletionStore>();
_ = EncounterProgressOperations.TryActivateOnFirstEngagement(PlayerId, MeleeNpc, registry, progress, completion);
_ = EncounterProgressOperations.TryMarkTargetDefeated(PlayerId, MeleeNpc, registry, progress, completion);
_ = EncounterProgressOperations.TryMarkTargetDefeated(PlayerId, RangedNpc, registry, progress, completion);
_ = EncounterProgressOperations.TryMarkTargetDefeated(PlayerId, EliteNpc, registry, progress, completion);
// Act
var allDefeated = EncounterProgressOperations.IsAllRequiredTargetsDefeated(
PlayerId,
" Prototype_Combat_Pocket ",
registry,
progress);
// Assert
Assert.True(allDefeated);
}
[Fact]
public async Task IsAllRequiredTargetsDefeated_ShouldReturnTrue_WhenDefeatedSetIsSupersetOfRequired()
{
// Arrange
await using var factory = new InMemoryWebApplicationFactory();
var registry = factory.Services.GetRequiredService<IEncounterDefinitionRegistry>();
var progress = factory.Services.GetRequiredService<IEncounterProgressStore>();
_ = progress.TryActivate(PlayerId, EncounterId);
_ = progress.TryAddDefeatedTarget(PlayerId, EncounterId, MeleeNpc);
_ = progress.TryAddDefeatedTarget(PlayerId, EncounterId, RangedNpc);
_ = progress.TryAddDefeatedTarget(PlayerId, EncounterId, EliteNpc);
_ = progress.TryAddDefeatedTarget(PlayerId, EncounterId, "extra_non_required_npc");
// Act
var allDefeated = EncounterProgressOperations.IsAllRequiredTargetsDefeated(
PlayerId,
EncounterId,
registry,
progress);
// Assert
Assert.True(allDefeated);
}
[Fact]
public async Task TryMarkCompleted_ShouldReturnFalseOnSecondAttempt_WhenUsingCompletionStoreDirectly()
{
// Arrange
await using var factory = new InMemoryWebApplicationFactory();
var completion = factory.Services.GetRequiredService<IEncounterCompletionStore>();
var completedAt = new DateTimeOffset(2026, 5, 31, 12, 0, 0, TimeSpan.Zero);
// Act
var first = completion.TryMarkCompleted(PlayerId, EncounterId, completedAt);
var second = completion.TryMarkCompleted(PlayerId, EncounterId, completedAt);
// Assert
Assert.True(first);
Assert.False(second);
}
}