Add HSchool.Content project for JSONC definitions, catalog, and map validation. Update solution structure to include new content and tests projects. Enhance school management to support mod packs and map instances, ensuring proper loading and validation. Revise documentation to reflect these changes and update tests for new functionality.
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
namespace HSchool.Content.Tests;
|
||||
|
||||
public class MapValidationTests
|
||||
{
|
||||
private readonly CatalogLoader _loader = new();
|
||||
|
||||
[Fact]
|
||||
public void ConnectedMap_WithAnEmptyRoom_IsValid()
|
||||
{
|
||||
var (catalog, map) = MiniSchool(fillOffice: false);
|
||||
MapValidator.Validate(map, catalog);
|
||||
Assert.Empty(map.Rooms.Single(room => room.Id == "office").Slots);
|
||||
Assert.Equal(["Principal"], catalog.PositionsFor(DefKind.Room, "Office"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AbstractDef_CannotBePlacedOnTheMap()
|
||||
{
|
||||
var catalog = _loader.Load(
|
||||
[CatalogLoader.CorePackId],
|
||||
MiniDefs(abstractYard: true));
|
||||
var map = MiniMap();
|
||||
|
||||
var ex = Assert.Throws<MapValidationException>(() => MapValidator.Validate(map, catalog));
|
||||
Assert.Contains("Abstract", ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UnknownDef_IsRejected()
|
||||
{
|
||||
var catalog = _loader.Load([CatalogLoader.CorePackId], MiniDefs());
|
||||
var map = MiniMap(officeDef: "MissingOffice");
|
||||
|
||||
var ex = Assert.Throws<MapValidationException>(() => MapValidator.Validate(map, catalog));
|
||||
Assert.Contains("Unknown", ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EdgeToNowhere_IsRejected()
|
||||
{
|
||||
var (catalog, map) = MiniSchool(extraLink: new MapLink { A = "office", B = "ghost" });
|
||||
|
||||
var ex = Assert.Throws<MapValidationException>(() => MapValidator.Validate(map, catalog));
|
||||
Assert.Contains("unknown node", ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsolatedNode_IsRejected()
|
||||
{
|
||||
var catalog = _loader.Load([CatalogLoader.CorePackId], MiniDefs());
|
||||
var map = MiniMap(includeOfficeLink: false);
|
||||
|
||||
var ex = Assert.Throws<MapValidationException>(() => MapValidator.Validate(map, catalog));
|
||||
Assert.Contains("isolated", ex.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DisconnectedGraph_IsRejected()
|
||||
{
|
||||
var catalog = _loader.Load([CatalogLoader.CorePackId], MiniDefs());
|
||||
var map = new MapLayout
|
||||
{
|
||||
Territory = new TerritoryNode { Id = "yard", Def = "Yard" },
|
||||
Buildings = [new BuildingNode { Id = "main", Def = "Main" }],
|
||||
Floors = [new FloorNode { Id = "floor-1", Def = "Floor", Building = "main" }],
|
||||
Rooms =
|
||||
[
|
||||
new RoomNode { Id = "office", Def = "Office", Building = "main", Floor = "floor-1" },
|
||||
new RoomNode { Id = "a", Def = "Office", Building = "main", Floor = "floor-1" },
|
||||
new RoomNode { Id = "b", Def = "Office", Building = "main", Floor = "floor-1" },
|
||||
],
|
||||
Links =
|
||||
[
|
||||
new MapLink { A = "yard", B = "office" },
|
||||
new MapLink { A = "a", B = "b" },
|
||||
],
|
||||
};
|
||||
|
||||
var ex = Assert.Throws<MapValidationException>(() => MapValidator.Validate(map, catalog));
|
||||
Assert.Contains("not connected", ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OneRoomWithoutAYard_IsRejected()
|
||||
{
|
||||
var catalog = _loader.Load([CatalogLoader.CorePackId], MiniDefs());
|
||||
var map = new MapLayout
|
||||
{
|
||||
Buildings = [new BuildingNode { Id = "main", Def = "Main" }],
|
||||
Floors = [new FloorNode { Id = "floor-1", Def = "Floor", Building = "main" }],
|
||||
Rooms = [new RoomNode { Id = "office", Def = "Office", Building = "main", Floor = "floor-1" }],
|
||||
Links = [new MapLink { A = "office", B = "office" }],
|
||||
};
|
||||
|
||||
var ex = Assert.Throws<MapValidationException>(() => MapValidator.Validate(map, catalog));
|
||||
Assert.Contains("no territory", ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MapWithNoRooms_IsRejected()
|
||||
{
|
||||
var catalog = _loader.Load([CatalogLoader.CorePackId], MiniDefs());
|
||||
var map = new MapLayout
|
||||
{
|
||||
Territory = new TerritoryNode { Id = "yard", Def = "Yard" },
|
||||
};
|
||||
|
||||
var ex = Assert.Throws<MapValidationException>(() => MapValidator.Validate(map, catalog));
|
||||
Assert.Contains("at least one room", ex.Message);
|
||||
}
|
||||
|
||||
private (DefCatalog Catalog, MapLayout Map) MiniSchool(bool fillOffice = true, MapLink? extraLink = null)
|
||||
{
|
||||
var catalog = _loader.Load([CatalogLoader.CorePackId], MiniDefs());
|
||||
var map = MiniMap(fillOffice: fillOffice, extraLink: extraLink);
|
||||
return (catalog, map);
|
||||
}
|
||||
|
||||
private static List<ContentDocument> MiniDefs(bool abstractYard = false) =>
|
||||
[
|
||||
PackDocuments.Def(CatalogLoader.CorePackId, "actions", "sit", """{ "defName": "Sit" }"""),
|
||||
PackDocuments.Def(CatalogLoader.CorePackId, "things", "chair", """{ "defName": "Chair", "actions": ["Sit"] }"""),
|
||||
PackDocuments.Def(CatalogLoader.CorePackId, "positions", "principal", """{ "defName": "Principal" }"""),
|
||||
PackDocuments.Def(
|
||||
CatalogLoader.CorePackId,
|
||||
"territories",
|
||||
"yard",
|
||||
abstractYard ? """{ "defName": "Yard", "abstract": true }""" : """{ "defName": "Yard" }"""),
|
||||
PackDocuments.Def(CatalogLoader.CorePackId, "buildings", "main", """{ "defName": "Main" }"""),
|
||||
PackDocuments.Def(CatalogLoader.CorePackId, "floors", "floor", """{ "defName": "Floor" }"""),
|
||||
PackDocuments.Def(
|
||||
CatalogLoader.CorePackId,
|
||||
"rooms",
|
||||
"office",
|
||||
"""{ "defName": "Office", "slots": [{ "key": "seat", "thing": "Chair" }], "positions": ["Principal"] }"""),
|
||||
];
|
||||
|
||||
private static MapLayout MiniMap(
|
||||
bool fillOffice = false,
|
||||
bool includeOfficeLink = true,
|
||||
string officeDef = "Office",
|
||||
MapLink? extraLink = null)
|
||||
{
|
||||
var links = new List<MapLink>();
|
||||
if (includeOfficeLink)
|
||||
{
|
||||
links.Add(new MapLink { A = "yard", B = "office" });
|
||||
}
|
||||
|
||||
if (extraLink is not null)
|
||||
{
|
||||
links.Add(extraLink);
|
||||
}
|
||||
|
||||
return new MapLayout
|
||||
{
|
||||
Territory = new TerritoryNode { Id = "yard", Def = "Yard" },
|
||||
Buildings = [new BuildingNode { Id = "main", Def = "Main" }],
|
||||
Floors = [new FloorNode { Id = "floor-1", Def = "Floor", Building = "main" }],
|
||||
Rooms =
|
||||
[
|
||||
new RoomNode
|
||||
{
|
||||
Id = "office",
|
||||
Def = officeDef,
|
||||
Building = "main",
|
||||
Floor = "floor-1",
|
||||
Slots = fillOffice ? [new SlotFill { Key = "seat", Thing = "Chair" }] : [],
|
||||
},
|
||||
],
|
||||
Links = links,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user