227 lines
8.8 KiB
C#
227 lines
8.8 KiB
C#
using Microsoft.Extensions.DependencyInjection;
|
|
using NeonSprawl.Server.Game.Encounters;
|
|
using NeonSprawl.Server.Tests;
|
|
using Xunit;
|
|
|
|
namespace NeonSprawl.Server.Tests.Game.Encounters;
|
|
|
|
public class EncounterDefinitionRegistryTests
|
|
{
|
|
private const string PrototypeCombatPocket = "prototype_combat_pocket";
|
|
|
|
private static EncounterDefinitionRegistry CreateRegistryFromRows(IReadOnlyDictionary<string, EncounterDefRow> byId)
|
|
{
|
|
var catalog = new EncounterDefinitionCatalog("/tmp/encounters", byId, catalogJsonFileCount: 1);
|
|
return new EncounterDefinitionRegistry(catalog);
|
|
}
|
|
|
|
private static EncounterDefRow CreatePrototypeCombatPocketRow() =>
|
|
new(
|
|
PrototypeCombatPocket,
|
|
"Prototype Combat Pocket",
|
|
"defeat_all_targets",
|
|
["prototype_npc_melee", "prototype_npc_ranged", "prototype_npc_elite"],
|
|
"prototype_combat_pocket_clear");
|
|
|
|
[Fact]
|
|
public void TryGetDefinition_ShouldReturnTrueAndExpectedMetadata_WhenIdExists()
|
|
{
|
|
// Arrange
|
|
var rows = new Dictionary<string, EncounterDefRow>(StringComparer.Ordinal)
|
|
{
|
|
[PrototypeCombatPocket] = CreatePrototypeCombatPocketRow(),
|
|
};
|
|
var registry = CreateRegistryFromRows(rows);
|
|
// Act
|
|
var found = registry.TryGetDefinition(PrototypeCombatPocket, out var def);
|
|
// Assert
|
|
Assert.True(found);
|
|
Assert.NotNull(def);
|
|
Assert.Equal("Prototype Combat Pocket", def!.DisplayName);
|
|
Assert.Equal("defeat_all_targets", def.CompletionCriteriaKind);
|
|
Assert.Equal("prototype_combat_pocket_clear", def.RewardTableId);
|
|
Assert.Equal(3, def.RequiredNpcInstanceIds.Count);
|
|
Assert.Contains("prototype_npc_melee", def.RequiredNpcInstanceIds);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryGetDefinition_ShouldReturnFalse_WhenEncounterIdIsNull()
|
|
{
|
|
// Arrange
|
|
var rows = new Dictionary<string, EncounterDefRow>(StringComparer.Ordinal)
|
|
{
|
|
[PrototypeCombatPocket] = CreatePrototypeCombatPocketRow(),
|
|
};
|
|
var registry = CreateRegistryFromRows(rows);
|
|
// Act
|
|
var found = registry.TryGetDefinition(null, out var def);
|
|
// Assert
|
|
Assert.False(found);
|
|
Assert.Null(def);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryGetDefinition_ShouldReturnFalse_WhenIdUnknown()
|
|
{
|
|
// Arrange
|
|
var rows = new Dictionary<string, EncounterDefRow>(StringComparer.Ordinal)
|
|
{
|
|
[PrototypeCombatPocket] = CreatePrototypeCombatPocketRow(),
|
|
};
|
|
var registry = CreateRegistryFromRows(rows);
|
|
// Act
|
|
var found = registry.TryGetDefinition("not_a_real_encounter", out var def);
|
|
// Assert
|
|
Assert.False(found);
|
|
Assert.Null(def);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryNormalizeKnown_ShouldReturnTrueAndLowercaseId_WhenInputHasWhitespaceAndMixedCase()
|
|
{
|
|
// Arrange
|
|
var rows = new Dictionary<string, EncounterDefRow>(StringComparer.Ordinal)
|
|
{
|
|
[PrototypeCombatPocket] = CreatePrototypeCombatPocketRow(),
|
|
};
|
|
var registry = CreateRegistryFromRows(rows);
|
|
// Act
|
|
var found = registry.TryNormalizeKnown(" Prototype_Combat_Pocket ", out var normalized);
|
|
// Assert
|
|
Assert.True(found);
|
|
Assert.Equal(PrototypeCombatPocket, normalized);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryNormalizeKnown_ShouldReturnFalse_WhenInputIsNull()
|
|
{
|
|
// Arrange
|
|
var rows = new Dictionary<string, EncounterDefRow>(StringComparer.Ordinal)
|
|
{
|
|
[PrototypeCombatPocket] = CreatePrototypeCombatPocketRow(),
|
|
};
|
|
var registry = CreateRegistryFromRows(rows);
|
|
// Act
|
|
var found = registry.TryNormalizeKnown(null, out var normalized);
|
|
// Assert
|
|
Assert.False(found);
|
|
Assert.Equal(string.Empty, normalized);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryNormalizeKnown_ShouldReturnFalse_WhenInputIsWhitespaceOnly()
|
|
{
|
|
// Arrange
|
|
var rows = new Dictionary<string, EncounterDefRow>(StringComparer.Ordinal)
|
|
{
|
|
[PrototypeCombatPocket] = CreatePrototypeCombatPocketRow(),
|
|
};
|
|
var registry = CreateRegistryFromRows(rows);
|
|
// Act
|
|
var found = registry.TryNormalizeKnown(" ", out var normalized);
|
|
// Assert
|
|
Assert.False(found);
|
|
Assert.Equal(string.Empty, normalized);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryNormalizeKnown_ShouldReturnFalse_WhenIdNotInCatalog()
|
|
{
|
|
// Arrange
|
|
var rows = new Dictionary<string, EncounterDefRow>(StringComparer.Ordinal)
|
|
{
|
|
[PrototypeCombatPocket] = CreatePrototypeCombatPocketRow(),
|
|
};
|
|
var registry = CreateRegistryFromRows(rows);
|
|
// Act
|
|
var found = registry.TryNormalizeKnown("prototype_unknown", out var normalized);
|
|
// Assert
|
|
Assert.False(found);
|
|
Assert.Equal("prototype_unknown", normalized);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetDefinitionsInIdOrder_ShouldListAllRowsOrderedById_WhenMultipleEncounters()
|
|
{
|
|
// Arrange
|
|
var rows = new Dictionary<string, EncounterDefRow>(StringComparer.Ordinal)
|
|
{
|
|
["z_encounter"] = new EncounterDefRow(
|
|
"z_encounter",
|
|
"Z",
|
|
"defeat_all_targets",
|
|
["prototype_npc_melee"],
|
|
"prototype_combat_pocket_clear"),
|
|
[PrototypeCombatPocket] = CreatePrototypeCombatPocketRow(),
|
|
["a_encounter"] = new EncounterDefRow(
|
|
"a_encounter",
|
|
"A",
|
|
"defeat_all_targets",
|
|
["prototype_npc_melee"],
|
|
"prototype_combat_pocket_clear"),
|
|
};
|
|
var registry = CreateRegistryFromRows(rows);
|
|
// Act
|
|
var list = registry.GetDefinitionsInIdOrder();
|
|
// Assert
|
|
Assert.Equal(3, list.Count);
|
|
Assert.Equal("a_encounter", list[0].Id);
|
|
Assert.Equal(PrototypeCombatPocket, list[1].Id);
|
|
Assert.Equal("z_encounter", list[2].Id);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Host_ShouldResolveRegistriesFromDi_WhenStartupSucceeds()
|
|
{
|
|
// Arrange
|
|
await using var factory = new InMemoryWebApplicationFactory();
|
|
using var client = factory.CreateClient();
|
|
_ = await client.GetAsync("/health");
|
|
// Act
|
|
var encounterRegistry = factory.Services.GetRequiredService<IEncounterDefinitionRegistry>();
|
|
var rewardRegistry = factory.Services.GetRequiredService<IRewardTableDefinitionRegistry>();
|
|
var encounterFound = encounterRegistry.TryGetDefinition(PrototypeCombatPocket, out var encounter);
|
|
var unknownEncounter = encounterRegistry.TryGetDefinition("not_a_real_encounter", out var missingEncounter);
|
|
var normalizedEncounter = encounterRegistry.TryNormalizeKnown(" Prototype_Combat_Pocket ", out var encounterId);
|
|
var rewardFound = rewardRegistry.TryGetDefinition("prototype_combat_pocket_clear", out var rewardTable);
|
|
var unknownReward = rewardRegistry.TryNormalizeKnown("prototype_unknown", out var unknownRewardId);
|
|
var encounterList = encounterRegistry.GetDefinitionsInIdOrder();
|
|
// Assert
|
|
Assert.True(encounterFound);
|
|
Assert.NotNull(encounter);
|
|
Assert.Equal("prototype_combat_pocket_clear", encounter!.RewardTableId);
|
|
Assert.False(unknownEncounter);
|
|
Assert.Null(missingEncounter);
|
|
Assert.True(normalizedEncounter);
|
|
Assert.Equal(PrototypeCombatPocket, encounterId);
|
|
Assert.True(rewardFound);
|
|
Assert.NotNull(rewardTable);
|
|
Assert.Equal(10, rewardTable!.FixedGrants.First(g => g.ItemId == "scrap_metal_bulk").Quantity);
|
|
Assert.False(unknownReward);
|
|
Assert.Equal("prototype_unknown", unknownRewardId);
|
|
Assert.Single(encounterList);
|
|
Assert.Equal(PrototypeCombatPocket, encounterList[0].Id);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Host_ShouldResolveEncounterProgressStoresFromDi_WhenStartupSucceeds()
|
|
{
|
|
// Arrange
|
|
await using var factory = new InMemoryWebApplicationFactory();
|
|
using var client = factory.CreateClient();
|
|
_ = await client.GetAsync("/health");
|
|
var progressStore = factory.Services.GetRequiredService<IEncounterProgressStore>();
|
|
var completionStore = factory.Services.GetRequiredService<IEncounterCompletionStore>();
|
|
// Act
|
|
var activated = progressStore.TryActivate("dev-local-1", PrototypeCombatPocket);
|
|
var completed = completionStore.TryMarkCompleted(
|
|
"dev-local-1",
|
|
PrototypeCombatPocket,
|
|
new DateTimeOffset(2026, 5, 31, 12, 0, 0, TimeSpan.Zero));
|
|
// Assert
|
|
Assert.True(activated);
|
|
Assert.True(completed);
|
|
Assert.True(completionStore.IsCompleted("dev-local-1", PrototypeCombatPocket));
|
|
}
|
|
}
|