using System.Net; using System.Text; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.Abstractions; using NeonSprawl.Server.Game.Skills; using NeonSprawl.Server.Tests; using Xunit; namespace NeonSprawl.Server.Tests.Game.Skills; public class SkillDefinitionRegistryTests { private static SkillDefinitionRegistry CreateRegistryFromRows(IReadOnlyDictionary byId) { var catalog = new SkillDefinitionCatalog("/tmp/catalog", byId, catalogJsonFileCount: 1); return new SkillDefinitionRegistry(catalog); } [Fact] public void TryGetDefinition_ShouldReturnTrueAndExpectedXpKinds_WhenIdExists() { // Arrange var rows = new Dictionary(StringComparer.Ordinal) { ["intrusion"] = new SkillDefRow( "intrusion", "tech", "Intrusion", new[] { "activity", "mission_reward", "book_or_item" }), }; var registry = CreateRegistryFromRows(rows); // Act var found = registry.TryGetDefinition("intrusion", out var def); // Assert Assert.True(found); Assert.NotNull(def); Assert.Equal("tech", def.Category); Assert.Equal("Intrusion", def.DisplayName); Assert.Contains("activity", def.AllowedXpSourceKinds, StringComparer.Ordinal); Assert.Contains("book_or_item", def.AllowedXpSourceKinds, StringComparer.Ordinal); } [Fact] public void TryGetDefinition_ShouldReturnFalse_WhenIdUnknown() { // Arrange var rows = new Dictionary(StringComparer.Ordinal) { ["salvage"] = new SkillDefRow("salvage", "gather", "Salvage", new[] { "activity" }), }; var registry = CreateRegistryFromRows(rows); // Act var found = registry.TryGetDefinition("not_a_real_skill", out var def); // Assert Assert.False(found); Assert.Null(def); } [Fact] public void GetDefinitionsInIdOrder_ShouldListAllRowsOrderedById_WhenMultipleSkills() { // Arrange var rows = new Dictionary(StringComparer.Ordinal) { ["salvage"] = new SkillDefRow("salvage", "gather", "Salvage", new[] { "activity", "mission_reward" }), ["refine"] = new SkillDefRow("refine", "process", "Refine", new[] { "activity" }), ["intrusion"] = new SkillDefRow("intrusion", "tech", "Intrusion", new[] { "trainer" }), }; var registry = CreateRegistryFromRows(rows); // Act var list = registry.GetDefinitionsInIdOrder(); // Assert Assert.Equal(3, list.Count); Assert.Equal("intrusion", list[0].Id); Assert.Equal("refine", list[1].Id); Assert.Equal("salvage", list[2].Id); } [Fact] public void TryGetDefinition_ShouldMatchLoaderCatalog_WhenUsingPrototypeFixture() { // Arrange var root = Directory.CreateTempSubdirectory("neon-sprawl-registry-loader-"); try { var skillsDir = Path.Combine(root.FullName, "content", "skills"); var schemaDir = Path.Combine(root.FullName, "content", "schemas"); Directory.CreateDirectory(skillsDir); Directory.CreateDirectory(schemaDir); var schemaPath = Path.Combine(schemaDir, "skill-def.schema.json"); File.Copy(SkillCatalogTestPaths.DiscoverRepoSkillDefSchemaPath(), schemaPath, overwrite: true); var json = """ { "skills": [ { "id": "salvage", "category": "gather", "displayName": "Salvage", "allowedXpSourceKinds": ["activity", "mission_reward"] }, { "id": "refine", "category": "process", "displayName": "Refine", "allowedXpSourceKinds": ["activity", "mission_reward", "trainer"] }, { "id": "intrusion", "category": "tech", "displayName": "Intrusion", "allowedXpSourceKinds": ["activity", "mission_reward", "book_or_item"] } ] } """; File.WriteAllText(Path.Combine(skillsDir, "prototype.json"), json, Encoding.UTF8); var loaded = SkillDefinitionCatalogLoader.Load(skillsDir, schemaPath, NullLogger.Instance); var registry = new SkillDefinitionRegistry(loaded); // Act var ok = registry.TryGetDefinition("refine", out var refine); // Assert Assert.True(ok); Assert.NotNull(refine); Assert.Equal("process", refine.Category); Assert.Contains("trainer", refine.AllowedXpSourceKinds, StringComparer.Ordinal); } finally { try { Directory.Delete(root.FullName, recursive: true); } catch { // best-effort cleanup for temp dir } } } [Fact] public async Task Host_ShouldResolveRegistryFromDi_WhenStartupSucceeds() { // Arrange await using var factory = new InMemoryWebApplicationFactory(); using var client = factory.CreateClient(); // Act var response = await client.GetAsync("/health"); var registry = factory.Services.GetRequiredService(); var found = registry.TryGetDefinition("salvage", out var salvage); var status = response.StatusCode; // Assert Assert.Equal(HttpStatusCode.OK, status); Assert.True(found); Assert.NotNull(salvage); Assert.Contains("activity", salvage.AllowedXpSourceKinds, StringComparer.Ordinal); } }