192 lines
7.9 KiB
C#
192 lines
7.9 KiB
C#
using System.Text;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using NeonSprawl.Server.Game.Factions;
|
|
using NeonSprawl.Server.Tests;
|
|
using Xunit;
|
|
|
|
namespace NeonSprawl.Server.Tests.Game.Factions;
|
|
|
|
public class FactionDefinitionCatalogLoaderTests
|
|
{
|
|
private const string ValidPrototypeCatalogJson =
|
|
"""
|
|
{
|
|
"schemaVersion": 1,
|
|
"factions": [
|
|
{
|
|
"id": "prototype_faction_grid_operators",
|
|
"displayName": "Grid Operators",
|
|
"minStanding": -100,
|
|
"maxStanding": 100,
|
|
"neutralStanding": 0
|
|
},
|
|
{
|
|
"id": "prototype_faction_rust_collective",
|
|
"displayName": "Rust Collective",
|
|
"minStanding": -100,
|
|
"maxStanding": 100,
|
|
"neutralStanding": 0
|
|
}
|
|
]
|
|
}
|
|
""";
|
|
|
|
private static (string Root, string FactionsDir, string SchemaPath) CreateTempContentLayout()
|
|
{
|
|
var root = Directory.CreateTempSubdirectory("neon-sprawl-factioncat-");
|
|
var factionsDir = Path.Combine(root.FullName, "content", "factions");
|
|
var schemaDir = Path.Combine(root.FullName, "content", "schemas");
|
|
Directory.CreateDirectory(factionsDir);
|
|
Directory.CreateDirectory(schemaDir);
|
|
var schemaPath = Path.Combine(schemaDir, "faction-def.schema.json");
|
|
File.Copy(FactionCatalogTestPaths.DiscoverRepoFactionDefSchemaPath(), schemaPath, overwrite: true);
|
|
return (root.FullName, factionsDir, schemaPath);
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_ShouldSucceed_WhenCatalogMatchesRepoPrototypeFile()
|
|
{
|
|
// Arrange
|
|
var factionsDir = FactionCatalogTestPaths.DiscoverRepoFactionsDirectory();
|
|
var schemaPath = FactionCatalogTestPaths.DiscoverRepoFactionDefSchemaPath();
|
|
// Act
|
|
var catalog = FactionDefinitionCatalogLoader.Load(factionsDir, schemaPath, NullLogger.Instance);
|
|
// Assert
|
|
Assert.Equal(PrototypeE7M3FactionCatalogRules.ExpectedFactionIds.Count, catalog.DistinctFactionCount);
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_ShouldSucceed_WhenCatalogMatchesPrototypeContract()
|
|
{
|
|
// Arrange
|
|
var (_, factionsDir, schemaPath) = CreateTempContentLayout();
|
|
File.WriteAllText(Path.Combine(factionsDir, "prototype_factions.json"), ValidPrototypeCatalogJson, Encoding.UTF8);
|
|
// Act
|
|
var catalog = FactionDefinitionCatalogLoader.Load(factionsDir, schemaPath, NullLogger.Instance);
|
|
// Assert
|
|
Assert.Equal(2, catalog.DistinctFactionCount);
|
|
Assert.Equal(1, catalog.CatalogJsonFileCount);
|
|
Assert.True(catalog.ById.TryGetValue("prototype_faction_grid_operators", out var grid));
|
|
Assert.NotNull(grid);
|
|
Assert.Equal("Grid Operators", grid!.DisplayName);
|
|
Assert.Equal(0, grid.NeutralStanding);
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_ShouldThrow_WhenFactionsIsNotArray()
|
|
{
|
|
// Arrange
|
|
var (_, factionsDir, schemaPath) = CreateTempContentLayout();
|
|
File.WriteAllText(Path.Combine(factionsDir, "bad_factions.json"), """{"schemaVersion": 1, "factions": "nope"}""", Encoding.UTF8);
|
|
// Act
|
|
var ex = Record.Exception(() => FactionDefinitionCatalogLoader.Load(factionsDir, schemaPath, NullLogger.Instance));
|
|
// Assert
|
|
var ioe = Assert.IsType<InvalidOperationException>(ex);
|
|
Assert.Contains("bad_factions.json", ioe.Message, StringComparison.Ordinal);
|
|
Assert.Contains("expected top-level 'factions' array", ioe.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_ShouldThrow_WhenDuplicateIdAcrossFiles()
|
|
{
|
|
// Arrange
|
|
var (_, factionsDir, schemaPath) = CreateTempContentLayout();
|
|
var singleFaction = """
|
|
{
|
|
"schemaVersion": 1,
|
|
"factions": [
|
|
{
|
|
"id": "prototype_faction_grid_operators",
|
|
"displayName": "Grid Operators",
|
|
"minStanding": -100,
|
|
"maxStanding": 100,
|
|
"neutralStanding": 0
|
|
}
|
|
]
|
|
}
|
|
""";
|
|
File.WriteAllText(Path.Combine(factionsDir, "a_factions.json"), singleFaction, Encoding.UTF8);
|
|
File.WriteAllText(Path.Combine(factionsDir, "b_factions.json"), singleFaction, Encoding.UTF8);
|
|
// Act
|
|
var ex = Record.Exception(() => FactionDefinitionCatalogLoader.Load(factionsDir, schemaPath, NullLogger.Instance));
|
|
// Assert
|
|
var ioe = Assert.IsType<InvalidOperationException>(ex);
|
|
Assert.Contains("duplicate faction id", ioe.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_ShouldThrow_WhenRosterGateFails()
|
|
{
|
|
// Arrange
|
|
var (_, factionsDir, schemaPath) = CreateTempContentLayout();
|
|
var oneFaction = """
|
|
{
|
|
"schemaVersion": 1,
|
|
"factions": [
|
|
{
|
|
"id": "prototype_faction_grid_operators",
|
|
"displayName": "Grid Operators",
|
|
"minStanding": -100,
|
|
"maxStanding": 100,
|
|
"neutralStanding": 0
|
|
}
|
|
]
|
|
}
|
|
""";
|
|
File.WriteAllText(Path.Combine(factionsDir, "prototype_factions.json"), oneFaction, Encoding.UTF8);
|
|
// Act
|
|
var ex = Record.Exception(() => FactionDefinitionCatalogLoader.Load(factionsDir, schemaPath, NullLogger.Instance));
|
|
// Assert
|
|
var ioe = Assert.IsType<InvalidOperationException>(ex);
|
|
Assert.Contains("prototype E7M3 expects exactly faction ids", ioe.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_ShouldThrow_WhenFreezeTableDiverges()
|
|
{
|
|
// Arrange
|
|
var (_, factionsDir, schemaPath) = CreateTempContentLayout();
|
|
var badDisplay = ValidPrototypeCatalogJson.Replace("Grid Operators", "Wrong Name", StringComparison.Ordinal);
|
|
File.WriteAllText(Path.Combine(factionsDir, "prototype_factions.json"), badDisplay, Encoding.UTF8);
|
|
// Act
|
|
var ex = Record.Exception(() => FactionDefinitionCatalogLoader.Load(factionsDir, schemaPath, NullLogger.Instance));
|
|
// Assert
|
|
var ioe = Assert.IsType<InvalidOperationException>(ex);
|
|
Assert.Contains("must match E7M3 freeze table", ioe.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryGetStandingBandGateError_ShouldReturnError_WhenOrderingFails()
|
|
{
|
|
// Arrange
|
|
var rows = new Dictionary<string, FactionDefRow>(StringComparer.Ordinal)
|
|
{
|
|
["prototype_faction_grid_operators"] = new(
|
|
"prototype_faction_grid_operators",
|
|
"Grid Operators",
|
|
10,
|
|
100,
|
|
0),
|
|
};
|
|
// Act
|
|
var error = PrototypeE7M3FactionCatalogRules.TryGetStandingBandGateError(rows);
|
|
// Assert
|
|
Assert.NotNull(error);
|
|
Assert.Contains("requires minStanding <= neutralStanding <= maxStanding", error, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_ShouldThrow_WhenSchemaFileMissing()
|
|
{
|
|
// Arrange
|
|
var (_, factionsDir, schemaPath) = CreateTempContentLayout();
|
|
File.Delete(schemaPath);
|
|
// Act
|
|
var ex = Record.Exception(() => FactionDefinitionCatalogLoader.Load(factionsDir, schemaPath, NullLogger.Instance));
|
|
// Assert
|
|
var ioe = Assert.IsType<InvalidOperationException>(ex);
|
|
Assert.Contains("missing schema file", ioe.Message, StringComparison.Ordinal);
|
|
}
|
|
}
|