552 lines
22 KiB
C#
552 lines
22 KiB
C#
using System.Net;
|
|
using System.Text;
|
|
using System.Text.Json.Nodes;
|
|
using Microsoft.AspNetCore.Mvc.Testing;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using NeonSprawl.Server.Game.Gathering;
|
|
using NeonSprawl.Server.Game.Items;
|
|
using NeonSprawl.Server.Tests;
|
|
using Xunit;
|
|
|
|
namespace NeonSprawl.Server.Tests.Game.Gathering;
|
|
|
|
public class ResourceNodeCatalogLoaderTests
|
|
{
|
|
private static readonly HashSet<string> ValidPrototypeItemIds =
|
|
[
|
|
"scrap_metal_bulk",
|
|
"refined_plate_stock",
|
|
"field_stim_mk0",
|
|
"survey_drone_kit",
|
|
"contract_handoff_token",
|
|
"prototype_armor_shell",
|
|
];
|
|
|
|
private const string ValidPrototypeNodeCatalogJson =
|
|
"""
|
|
{
|
|
"schemaVersion": 1,
|
|
"nodes": [
|
|
{
|
|
"nodeDefId": "prototype_resource_node_alpha",
|
|
"displayName": "Prototype Salvage Heap",
|
|
"gatherLens": "consumer_salvage",
|
|
"maxGathers": 10
|
|
},
|
|
{
|
|
"nodeDefId": "prototype_subsurface_vein_beta",
|
|
"displayName": "Prototype Subsurface Vein",
|
|
"gatherLens": "subsurface",
|
|
"maxGathers": 10
|
|
},
|
|
{
|
|
"nodeDefId": "prototype_bio_mat_gamma",
|
|
"displayName": "Prototype Bio Mat",
|
|
"gatherLens": "bio",
|
|
"maxGathers": 10
|
|
},
|
|
{
|
|
"nodeDefId": "prototype_urban_bulk_delta",
|
|
"displayName": "Prototype Urban Bulk",
|
|
"gatherLens": "urban_bulk",
|
|
"maxGathers": 10
|
|
}
|
|
]
|
|
}
|
|
""";
|
|
|
|
private const string ValidPrototypeYieldCatalogJson =
|
|
"""
|
|
{
|
|
"schemaVersion": 1,
|
|
"yields": [
|
|
{
|
|
"nodeDefId": "prototype_resource_node_alpha",
|
|
"itemId": "scrap_metal_bulk",
|
|
"quantity": 1
|
|
},
|
|
{
|
|
"nodeDefId": "prototype_subsurface_vein_beta",
|
|
"itemId": "scrap_metal_bulk",
|
|
"quantity": 2
|
|
},
|
|
{
|
|
"nodeDefId": "prototype_bio_mat_gamma",
|
|
"itemId": "scrap_metal_bulk",
|
|
"quantity": 3
|
|
},
|
|
{
|
|
"nodeDefId": "prototype_urban_bulk_delta",
|
|
"itemId": "scrap_metal_bulk",
|
|
"quantity": 5
|
|
}
|
|
]
|
|
}
|
|
""";
|
|
|
|
private static (string Root, string ResourceNodesDir, string NodeSchemaPath, string YieldSchemaPath) CreateTempContentLayout()
|
|
{
|
|
var root = Directory.CreateTempSubdirectory("neon-sprawl-resnodecat-");
|
|
var resourceNodesDir = Path.Combine(root.FullName, "content", "resource-nodes");
|
|
var schemaDir = Path.Combine(root.FullName, "content", "schemas");
|
|
Directory.CreateDirectory(resourceNodesDir);
|
|
Directory.CreateDirectory(schemaDir);
|
|
var nodeSchemaPath = Path.Combine(schemaDir, "resource-node-def.schema.json");
|
|
var yieldSchemaPath = Path.Combine(schemaDir, "resource-yield-row.schema.json");
|
|
File.Copy(ResourceNodeCatalogTestPaths.DiscoverRepoResourceNodeDefSchemaPath(), nodeSchemaPath, overwrite: true);
|
|
File.Copy(ResourceNodeCatalogTestPaths.DiscoverRepoResourceYieldRowSchemaPath(), yieldSchemaPath, overwrite: true);
|
|
return (root.FullName, resourceNodesDir, nodeSchemaPath, yieldSchemaPath);
|
|
}
|
|
|
|
private static void WriteNodeCatalog(string resourceNodesDir, string catalogJson) =>
|
|
File.WriteAllText(Path.Combine(resourceNodesDir, "prototype_resource_nodes.json"), catalogJson, Encoding.UTF8);
|
|
|
|
private static void WriteYieldCatalog(string resourceNodesDir, string catalogJson) =>
|
|
File.WriteAllText(Path.Combine(resourceNodesDir, "prototype_resource_yields.json"), catalogJson, Encoding.UTF8);
|
|
|
|
private static void WriteValidCatalogs(string resourceNodesDir)
|
|
{
|
|
WriteNodeCatalog(resourceNodesDir, ValidPrototypeNodeCatalogJson);
|
|
WriteYieldCatalog(resourceNodesDir, ValidPrototypeYieldCatalogJson);
|
|
}
|
|
|
|
private static JsonObject GetNodeRow(JsonObject catalogRoot, string nodeDefId)
|
|
{
|
|
var nodes = catalogRoot["nodes"] as JsonArray
|
|
?? throw new InvalidOperationException("expected nodes array");
|
|
foreach (var node in nodes)
|
|
{
|
|
if (node is JsonObject row && row["nodeDefId"]?.GetValue<string>() == nodeDefId)
|
|
return row;
|
|
}
|
|
|
|
throw new InvalidOperationException($"nodeDefId not found: {nodeDefId}");
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_ShouldSucceed_WhenCatalogMatchesPrototypeContract()
|
|
{
|
|
// Arrange
|
|
var (_, resourceNodesDir, nodeSchemaPath, yieldSchemaPath) = CreateTempContentLayout();
|
|
WriteValidCatalogs(resourceNodesDir);
|
|
// Act
|
|
var catalog = ResourceNodeCatalogLoader.Load(
|
|
resourceNodesDir,
|
|
nodeSchemaPath,
|
|
yieldSchemaPath,
|
|
ValidPrototypeItemIds,
|
|
NullLogger.Instance);
|
|
// Assert
|
|
Assert.Equal(4, catalog.DistinctNodeCount);
|
|
Assert.Equal(4, catalog.DistinctYieldCount);
|
|
Assert.Equal(1, catalog.NodeCatalogJsonFileCount);
|
|
Assert.Equal(1, catalog.YieldCatalogJsonFileCount);
|
|
Assert.True(catalog.TryGetNode("prototype_resource_node_alpha", out var node));
|
|
Assert.NotNull(node);
|
|
Assert.Equal("consumer_salvage", node!.GatherLens);
|
|
Assert.Equal(10, node.MaxGathers);
|
|
Assert.True(catalog.TryGetYield("prototype_resource_node_alpha", out var yield));
|
|
Assert.NotNull(yield);
|
|
Assert.Equal("scrap_metal_bulk", yield!.ItemId);
|
|
Assert.Equal(1, yield.Quantity);
|
|
Assert.True(catalog.TryGetYield("prototype_urban_bulk_delta", out var deltaYield));
|
|
Assert.Equal(5, deltaYield!.Quantity);
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_ShouldThrow_WhenNodesIsNotArray()
|
|
{
|
|
// Arrange
|
|
var (_, resourceNodesDir, nodeSchemaPath, yieldSchemaPath) = CreateTempContentLayout();
|
|
WriteNodeCatalog(resourceNodesDir, """{"schemaVersion": 1, "nodes": "nope"}""");
|
|
WriteYieldCatalog(resourceNodesDir, ValidPrototypeYieldCatalogJson);
|
|
// Act
|
|
var ex = Record.Exception(() => ResourceNodeCatalogLoader.Load(
|
|
resourceNodesDir,
|
|
nodeSchemaPath,
|
|
yieldSchemaPath,
|
|
ValidPrototypeItemIds,
|
|
NullLogger.Instance));
|
|
// Assert
|
|
var ioe = Assert.IsType<InvalidOperationException>(ex);
|
|
Assert.Contains("expected top-level 'nodes' array", ioe.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_ShouldThrow_WhenYieldsIsNotArray()
|
|
{
|
|
// Arrange
|
|
var (_, resourceNodesDir, nodeSchemaPath, yieldSchemaPath) = CreateTempContentLayout();
|
|
WriteNodeCatalog(resourceNodesDir, ValidPrototypeNodeCatalogJson);
|
|
WriteYieldCatalog(resourceNodesDir, """{"schemaVersion": 1, "yields": "nope"}""");
|
|
// Act
|
|
var ex = Record.Exception(() => ResourceNodeCatalogLoader.Load(
|
|
resourceNodesDir,
|
|
nodeSchemaPath,
|
|
yieldSchemaPath,
|
|
ValidPrototypeItemIds,
|
|
NullLogger.Instance));
|
|
// Assert
|
|
var ioe = Assert.IsType<InvalidOperationException>(ex);
|
|
Assert.Contains("expected top-level 'yields' array", ioe.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_ShouldThrow_WhenSchemaVersionIsNotOne()
|
|
{
|
|
// Arrange
|
|
var (_, resourceNodesDir, nodeSchemaPath, yieldSchemaPath) = CreateTempContentLayout();
|
|
WriteNodeCatalog(resourceNodesDir, """{"schemaVersion": 2, "nodes": []}""");
|
|
WriteYieldCatalog(resourceNodesDir, ValidPrototypeYieldCatalogJson);
|
|
// Act
|
|
var ex = Record.Exception(() => ResourceNodeCatalogLoader.Load(
|
|
resourceNodesDir,
|
|
nodeSchemaPath,
|
|
yieldSchemaPath,
|
|
ValidPrototypeItemIds,
|
|
NullLogger.Instance));
|
|
// Assert
|
|
var ioe = Assert.IsType<InvalidOperationException>(ex);
|
|
Assert.Contains("expected schemaVersion 1", ioe.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_ShouldThrow_WhenNodeRowViolatesSchema()
|
|
{
|
|
// Arrange
|
|
var (_, resourceNodesDir, nodeSchemaPath, yieldSchemaPath) = CreateTempContentLayout();
|
|
const string bad = """
|
|
{
|
|
"schemaVersion": 1,
|
|
"nodes": [
|
|
{
|
|
"nodeDefId": "prototype_resource_node_alpha",
|
|
"displayName": "",
|
|
"gatherLens": "consumer_salvage",
|
|
"maxGathers": 10
|
|
}
|
|
]
|
|
}
|
|
""";
|
|
WriteNodeCatalog(resourceNodesDir, bad);
|
|
WriteYieldCatalog(resourceNodesDir, ValidPrototypeYieldCatalogJson);
|
|
// Act
|
|
var ex = Record.Exception(() => ResourceNodeCatalogLoader.Load(
|
|
resourceNodesDir,
|
|
nodeSchemaPath,
|
|
yieldSchemaPath,
|
|
ValidPrototypeItemIds,
|
|
NullLogger.Instance));
|
|
// Assert
|
|
var ioe = Assert.IsType<InvalidOperationException>(ex);
|
|
Assert.Contains("Resource node catalog validation failed", ioe.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_ShouldThrow_WhenDuplicateNodeDefIdAcrossFiles()
|
|
{
|
|
// Arrange
|
|
var (_, resourceNodesDir, nodeSchemaPath, yieldSchemaPath) = CreateTempContentLayout();
|
|
const string singleNode = """
|
|
{
|
|
"schemaVersion": 1,
|
|
"nodes": [
|
|
{
|
|
"nodeDefId": "prototype_resource_node_alpha",
|
|
"displayName": "A",
|
|
"gatherLens": "consumer_salvage",
|
|
"maxGathers": 10
|
|
}
|
|
]
|
|
}
|
|
""";
|
|
File.WriteAllText(Path.Combine(resourceNodesDir, "a_resource_nodes.json"), singleNode, Encoding.UTF8);
|
|
File.WriteAllText(Path.Combine(resourceNodesDir, "b_resource_nodes.json"), singleNode, Encoding.UTF8);
|
|
WriteYieldCatalog(resourceNodesDir, ValidPrototypeYieldCatalogJson);
|
|
// Act
|
|
var ex = Record.Exception(() => ResourceNodeCatalogLoader.Load(
|
|
resourceNodesDir,
|
|
nodeSchemaPath,
|
|
yieldSchemaPath,
|
|
ValidPrototypeItemIds,
|
|
NullLogger.Instance));
|
|
// Assert
|
|
var ioe = Assert.IsType<InvalidOperationException>(ex);
|
|
Assert.Contains("duplicate nodeDefId", ioe.Message, StringComparison.Ordinal);
|
|
Assert.Contains("prototype_resource_node_alpha", ioe.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_ShouldThrow_WhenSlice2NodeIdsIncomplete()
|
|
{
|
|
// Arrange
|
|
var (_, resourceNodesDir, nodeSchemaPath, yieldSchemaPath) = CreateTempContentLayout();
|
|
const string oneOnly = """
|
|
{
|
|
"schemaVersion": 1,
|
|
"nodes": [
|
|
{
|
|
"nodeDefId": "prototype_resource_node_alpha",
|
|
"displayName": "A",
|
|
"gatherLens": "consumer_salvage",
|
|
"maxGathers": 10
|
|
}
|
|
]
|
|
}
|
|
""";
|
|
WriteNodeCatalog(resourceNodesDir, oneOnly);
|
|
WriteYieldCatalog(resourceNodesDir, ValidPrototypeYieldCatalogJson);
|
|
// Act
|
|
var ex = Record.Exception(() => ResourceNodeCatalogLoader.Load(
|
|
resourceNodesDir,
|
|
nodeSchemaPath,
|
|
yieldSchemaPath,
|
|
ValidPrototypeItemIds,
|
|
NullLogger.Instance));
|
|
// Assert
|
|
var ioe = Assert.IsType<InvalidOperationException>(ex);
|
|
Assert.Contains("prototype Slice 2 expects exactly nodeDefIds", ioe.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_ShouldThrow_WhenSlice2GatherLensesDoNotMatch()
|
|
{
|
|
// Arrange
|
|
var (_, resourceNodesDir, nodeSchemaPath, yieldSchemaPath) = CreateTempContentLayout();
|
|
var root = JsonNode.Parse(ValidPrototypeNodeCatalogJson) as JsonObject
|
|
?? throw new InvalidOperationException("expected object root");
|
|
GetNodeRow(root, "prototype_subsurface_vein_beta")["gatherLens"] = "consumer_salvage";
|
|
WriteNodeCatalog(resourceNodesDir, root.ToJsonString());
|
|
WriteYieldCatalog(resourceNodesDir, ValidPrototypeYieldCatalogJson);
|
|
// Act
|
|
var ex = Record.Exception(() => ResourceNodeCatalogLoader.Load(
|
|
resourceNodesDir,
|
|
nodeSchemaPath,
|
|
yieldSchemaPath,
|
|
ValidPrototypeItemIds,
|
|
NullLogger.Instance));
|
|
// Assert
|
|
var ioe = Assert.IsType<InvalidOperationException>(ex);
|
|
Assert.Contains("requires exactly one row per gatherLens", ioe.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_ShouldThrow_WhenYieldNodeDefIdNotInNodes()
|
|
{
|
|
// Arrange
|
|
var (_, resourceNodesDir, nodeSchemaPath, yieldSchemaPath) = CreateTempContentLayout();
|
|
WriteValidCatalogs(resourceNodesDir);
|
|
var yieldRoot = JsonNode.Parse(ValidPrototypeYieldCatalogJson) as JsonObject
|
|
?? throw new InvalidOperationException("expected object root");
|
|
var yields = yieldRoot["yields"] as JsonArray ?? throw new InvalidOperationException("expected yields");
|
|
yields.Add(JsonNode.Parse("""{"nodeDefId": "unknown_node_xyz", "itemId": "scrap_metal_bulk", "quantity": 1}"""));
|
|
WriteYieldCatalog(resourceNodesDir, yieldRoot.ToJsonString());
|
|
// Act
|
|
var ex = Record.Exception(() => ResourceNodeCatalogLoader.Load(
|
|
resourceNodesDir,
|
|
nodeSchemaPath,
|
|
yieldSchemaPath,
|
|
ValidPrototypeItemIds,
|
|
NullLogger.Instance));
|
|
// Assert
|
|
var ioe = Assert.IsType<InvalidOperationException>(ex);
|
|
Assert.Contains("unknown_node_xyz", ioe.Message, StringComparison.Ordinal);
|
|
Assert.Contains("not defined in resource node catalogs", ioe.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_ShouldThrow_WhenYieldItemIdUnknown()
|
|
{
|
|
// Arrange
|
|
var (_, resourceNodesDir, nodeSchemaPath, yieldSchemaPath) = CreateTempContentLayout();
|
|
WriteValidCatalogs(resourceNodesDir);
|
|
var yieldRoot = JsonNode.Parse(ValidPrototypeYieldCatalogJson) as JsonObject
|
|
?? throw new InvalidOperationException("expected object root");
|
|
var yields = yieldRoot["yields"] as JsonArray ?? throw new InvalidOperationException("expected yields");
|
|
yields[0]!["itemId"] = "not_a_real_item";
|
|
WriteYieldCatalog(resourceNodesDir, yieldRoot.ToJsonString());
|
|
// Act
|
|
var ex = Record.Exception(() => ResourceNodeCatalogLoader.Load(
|
|
resourceNodesDir,
|
|
nodeSchemaPath,
|
|
yieldSchemaPath,
|
|
ValidPrototypeItemIds,
|
|
NullLogger.Instance));
|
|
// Assert
|
|
var ioe = Assert.IsType<InvalidOperationException>(ex);
|
|
Assert.Contains("not_a_real_item", ioe.Message, StringComparison.Ordinal);
|
|
Assert.Contains("not in item catalogs", ioe.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_ShouldThrow_WhenDuplicateYieldNodeDefId()
|
|
{
|
|
// Arrange
|
|
var (_, resourceNodesDir, nodeSchemaPath, yieldSchemaPath) = CreateTempContentLayout();
|
|
WriteValidCatalogs(resourceNodesDir);
|
|
const string dupYield = """
|
|
{
|
|
"schemaVersion": 1,
|
|
"yields": [
|
|
{
|
|
"nodeDefId": "prototype_resource_node_alpha",
|
|
"itemId": "scrap_metal_bulk",
|
|
"quantity": 1
|
|
}
|
|
]
|
|
}
|
|
""";
|
|
File.WriteAllText(Path.Combine(resourceNodesDir, "a_resource_yields.json"), dupYield, Encoding.UTF8);
|
|
File.WriteAllText(Path.Combine(resourceNodesDir, "b_resource_yields.json"), dupYield, Encoding.UTF8);
|
|
// Act
|
|
var ex = Record.Exception(() => ResourceNodeCatalogLoader.Load(
|
|
resourceNodesDir,
|
|
nodeSchemaPath,
|
|
yieldSchemaPath,
|
|
ValidPrototypeItemIds,
|
|
NullLogger.Instance));
|
|
// Assert
|
|
var ioe = Assert.IsType<InvalidOperationException>(ex);
|
|
Assert.Contains("duplicate yield nodeDefId", ioe.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_ShouldThrow_WhenSlice2YieldRowsIncomplete()
|
|
{
|
|
// Arrange
|
|
var (_, resourceNodesDir, nodeSchemaPath, yieldSchemaPath) = CreateTempContentLayout();
|
|
WriteValidCatalogs(resourceNodesDir);
|
|
const string oneYield = """
|
|
{
|
|
"schemaVersion": 1,
|
|
"yields": [
|
|
{
|
|
"nodeDefId": "prototype_resource_node_alpha",
|
|
"itemId": "scrap_metal_bulk",
|
|
"quantity": 1
|
|
}
|
|
]
|
|
}
|
|
""";
|
|
WriteYieldCatalog(resourceNodesDir, oneYield);
|
|
// Act
|
|
var ex = Record.Exception(() => ResourceNodeCatalogLoader.Load(
|
|
resourceNodesDir,
|
|
nodeSchemaPath,
|
|
yieldSchemaPath,
|
|
ValidPrototypeItemIds,
|
|
NullLogger.Instance));
|
|
// Assert
|
|
var ioe = Assert.IsType<InvalidOperationException>(ex);
|
|
Assert.Contains("expects exactly one yield row per nodeDefId", ioe.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_ShouldThrow_WhenSlice2YieldItemIdNotAllowed()
|
|
{
|
|
// Arrange
|
|
var (_, resourceNodesDir, nodeSchemaPath, yieldSchemaPath) = CreateTempContentLayout();
|
|
WriteValidCatalogs(resourceNodesDir);
|
|
var yieldRoot = JsonNode.Parse(ValidPrototypeYieldCatalogJson) as JsonObject
|
|
?? throw new InvalidOperationException("expected object root");
|
|
var yields = yieldRoot["yields"] as JsonArray ?? throw new InvalidOperationException("expected yields");
|
|
yields[0]!["itemId"] = "refined_plate_stock";
|
|
WriteYieldCatalog(resourceNodesDir, yieldRoot.ToJsonString());
|
|
// Act
|
|
var ex = Record.Exception(() => ResourceNodeCatalogLoader.Load(
|
|
resourceNodesDir,
|
|
nodeSchemaPath,
|
|
yieldSchemaPath,
|
|
ValidPrototypeItemIds,
|
|
NullLogger.Instance));
|
|
// Assert
|
|
var ioe = Assert.IsType<InvalidOperationException>(ex);
|
|
Assert.Contains("expects yield itemIds", ioe.Message, StringComparison.Ordinal);
|
|
Assert.Contains("scrap_metal_bulk", ioe.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_ShouldThrow_WhenResourceNodesDirectoryMissing()
|
|
{
|
|
// Arrange
|
|
var missingDir = Path.Combine(Path.GetTempPath(), "neon-sprawl-no-resnodes-" + Guid.NewGuid().ToString("n"));
|
|
var nodeSchemaPath = ResourceNodeCatalogTestPaths.DiscoverRepoResourceNodeDefSchemaPath();
|
|
var yieldSchemaPath = ResourceNodeCatalogTestPaths.DiscoverRepoResourceYieldRowSchemaPath();
|
|
// Act
|
|
var ex = Record.Exception(() => ResourceNodeCatalogLoader.Load(
|
|
missingDir,
|
|
nodeSchemaPath,
|
|
yieldSchemaPath,
|
|
ValidPrototypeItemIds,
|
|
NullLogger.Instance));
|
|
// Assert
|
|
var ioe = Assert.IsType<InvalidOperationException>(ex);
|
|
Assert.Contains("missing directory", ioe.Message, StringComparison.Ordinal);
|
|
Assert.Contains(missingDir, ioe.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_ShouldThrow_WhenNodeSchemaFileMissing()
|
|
{
|
|
// Arrange
|
|
var (_, resourceNodesDir, _, yieldSchemaPath) = CreateTempContentLayout();
|
|
WriteValidCatalogs(resourceNodesDir);
|
|
var missingSchema = Path.Combine(resourceNodesDir, "missing-node.schema.json");
|
|
// Act
|
|
var ex = Record.Exception(() => ResourceNodeCatalogLoader.Load(
|
|
resourceNodesDir,
|
|
missingSchema,
|
|
yieldSchemaPath,
|
|
ValidPrototypeItemIds,
|
|
NullLogger.Instance));
|
|
// Assert
|
|
var ioe = Assert.IsType<InvalidOperationException>(ex);
|
|
Assert.Contains("missing schema file", ioe.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Host_ShouldResolveCatalogFromDi_WhenStartupSucceeds()
|
|
{
|
|
// Arrange
|
|
await using var factory = new InMemoryWebApplicationFactory();
|
|
using var client = factory.CreateClient();
|
|
// Act
|
|
var response = await client.GetAsync("/health");
|
|
// Assert
|
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
var catalog = factory.Services.GetRequiredService<ResourceNodeCatalog>();
|
|
Assert.Equal(4, catalog.DistinctNodeCount);
|
|
Assert.True(catalog.TryGetNode("prototype_bio_mat_gamma", out var node));
|
|
Assert.Equal("bio", node!.GatherLens);
|
|
Assert.True(catalog.TryGetYield("prototype_bio_mat_gamma", out var yield));
|
|
Assert.Equal(3, yield!.Quantity);
|
|
}
|
|
|
|
[Fact]
|
|
public void Host_ShouldFailStartup_WhenCatalogDirectoryInvalid()
|
|
{
|
|
// Arrange
|
|
var badDir = Path.Combine(Path.GetTempPath(), "neon-sprawl-empty-resnodes-" + Guid.NewGuid().ToString("n"));
|
|
Directory.CreateDirectory(badDir);
|
|
try
|
|
{
|
|
// Act
|
|
var ex = Record.Exception(() =>
|
|
{
|
|
using var factory = new WebApplicationFactory<Program>().WithWebHostBuilder(b =>
|
|
b.UseSetting("Content:ResourceNodesDirectory", badDir));
|
|
factory.CreateClient();
|
|
});
|
|
// Assert
|
|
Assert.NotNull(ex);
|
|
Assert.Contains("Resource node catalog validation failed", ex.ToString(), StringComparison.Ordinal);
|
|
}
|
|
finally
|
|
{
|
|
if (Directory.Exists(badDir))
|
|
Directory.Delete(badDir);
|
|
}
|
|
}
|
|
}
|