Chessistics/chessistics-tests/Loading/CampaignValidationTests.cs
Samuel Bouchet 2d1aea0a7a Snapshot campaign system progress before automation harness
Bundles in-flight work on the campaign/missions system (CampaignDef,
MissionDef, TerrainPatch, TransformerDef, MissionChecker, CampaignLoader,
FlavorBanner, transformer rules), plan files, and matching tests. Baseline
commit so the upcoming automation testing harness lands on a clean tree.
2026-04-16 21:22:49 +02:00

135 lines
4.8 KiB
C#

using Chessistics.Engine.Commands;
using Chessistics.Engine.Loading;
using Chessistics.Engine.Model;
using Chessistics.Engine.Simulation;
using Xunit;
namespace Chessistics.Tests.Loading;
/// <summary>
/// Validates campaign_01.json structural integrity:
/// no cell overlaps, unique building names, walls only on new cells.
/// </summary>
public class CampaignValidationTests
{
private static CampaignDef LoadCampaign()
=> CampaignLoader.LoadFromFile("../../../../Data/campaigns/campaign_01.json");
[Fact]
public void Campaign01_NoBuildingOverlaps()
{
var campaign = LoadCampaign();
var sim = new GameSim(campaign);
sim.ProcessCommand(new LoadCampaignCommand());
// Step through missions by manually applying patches and checking for conflicts
var state = BoardState.FromCampaign(campaign);
// Track all active buildings after each mission
for (int i = 0; i < campaign.Missions.Count; i++)
{
var mission = campaign.Missions[i];
state.ApplyTerrainPatch(mission.TerrainPatch, i);
// After each mission, verify no cell has multiple building types
foreach (var pos in state.Productions.Keys)
{
Assert.False(state.Demands.ContainsKey(pos),
$"Mission {i + 1}: Production and Demand overlap at {pos}");
Assert.False(state.Transformers.ContainsKey(pos),
$"Mission {i + 1}: Production and Transformer overlap at {pos}");
Assert.NotEqual(CellType.Wall, state.GetCell(pos));
}
foreach (var pos in state.Demands.Keys)
{
Assert.False(state.Transformers.ContainsKey(pos),
$"Mission {i + 1}: Demand and Transformer overlap at {pos}");
Assert.NotEqual(CellType.Wall, state.GetCell(pos));
}
foreach (var pos in state.Transformers.Keys)
{
Assert.NotEqual(CellType.Wall, state.GetCell(pos));
}
}
}
[Fact]
public void Campaign01_UniqueBuildingNames()
{
var campaign = LoadCampaign();
var state = BoardState.FromCampaign(campaign);
for (int i = 0; i < campaign.Missions.Count; i++)
{
state.ApplyTerrainPatch(campaign.Missions[i].TerrainPatch, i);
}
// Collect all building names
var names = new List<string>();
names.AddRange(state.Productions.Values.Select(p => p.Name));
names.AddRange(state.Demands.Values.Select(d => d.Name));
names.AddRange(state.Transformers.Values.Select(t => t.Name));
var duplicates = names.GroupBy(n => n).Where(g => g.Count() > 1).Select(g => g.Key).ToList();
Assert.Empty(duplicates);
}
[Fact]
public void Campaign01_WallsOnlyOnNewOrEmptyCells()
{
var campaign = LoadCampaign();
// Track which cells have buildings before each mission
var buildingCells = new HashSet<Coords>();
int prevWidth = campaign.InitialWidth;
int prevHeight = campaign.InitialHeight;
for (int i = 0; i < campaign.Missions.Count; i++)
{
var mission = campaign.Missions[i];
// Check walls in this mission's patch
foreach (var cell in mission.TerrainPatch.Cells)
{
if (cell.Type == CellType.Wall)
{
// Wall should be on a cell that was either:
// - Outside the previous board dimensions (new cell)
// - Not a building cell
bool isNewCell = cell.Col >= prevWidth || cell.Row >= prevHeight;
bool isBuilding = buildingCells.Contains(new Coords(cell.Col, cell.Row));
Assert.True(isNewCell || !isBuilding,
$"Mission {i + 1}: Wall at ({cell.Col},{cell.Row}) overwrites an existing building");
}
}
// Update building tracking
foreach (var cell in mission.TerrainPatch.Cells)
{
var coords = new Coords(cell.Col, cell.Row);
if (cell.Type is CellType.Production or CellType.Demand or CellType.Transformer)
buildingCells.Add(coords);
else
buildingCells.Remove(coords);
}
prevWidth = mission.TerrainPatch.NewWidth;
prevHeight = mission.TerrainPatch.NewHeight;
}
}
[Fact]
public void Campaign01_AllMissionsHaveFlavor()
{
var campaign = LoadCampaign();
for (int i = 0; i < campaign.Missions.Count; i++)
{
Assert.False(string.IsNullOrWhiteSpace(campaign.Missions[i].Flavor),
$"Mission {i + 1} ({campaign.Missions[i].Name}) has no flavor text");
}
}
}