using Chessistics.Engine.Commands; using Chessistics.Engine.Events; using Chessistics.Engine.Model; using Chessistics.Tests.Helpers; using Xunit; namespace Chessistics.Tests.Simulation; /// /// Regression tests for SimPhase transitions. /// Bug: StepSimulationCommand always set phase to Paused, even during auto-play (Running). /// public class PhaseTests { private static SimHelper CreateSimpleRunnable() { var campaign = new CampaignDef { Name = "Phase Test", InitialWidth = 4, InitialHeight = 1, Missions = [ new MissionDef { Id = 1, Name = "M1", TerrainPatch = new TerrainPatch { NewWidth = 4, NewHeight = 1, Cells = [ new PatchCell { Col = 0, Row = 0, Type = CellType.Production, Production = new ProductionDef(new Coords(0, 0), "P", CargoType.Wood, 4) }, new PatchCell { Col = 3, Row = 0, Type = CellType.Demand, Demand = new DemandDef(new Coords(3, 0), "D", CargoType.Wood, 99) } ] }, UnlockedPieces = [PieceKind.Rook], UnlockedLevels = [new PieceUpgrade(PieceKind.Rook, 1)], Stock = [new PieceStock(PieceKind.Rook, 2)] } ] }; var sim = SimHelper.FromCampaign(campaign); sim.Sim.ProcessCommand(new LoadCampaignCommand()); sim.Place(PieceKind.Rook, (1, 0), (2, 0)); return sim; } [Fact] public void ManualStep_FromPaused_RemainsInPaused() { var sim = CreateSimpleRunnable(); Assert.Equal(SimPhase.Paused, sim.Snapshot.Phase); sim.Step(); Assert.Equal(SimPhase.Paused, sim.Snapshot.Phase); } [Fact] public void AutoPlayStep_FromRunning_StaysRunning() { var sim = CreateSimpleRunnable(); // Resume → Running, then step (simulates auto-play timer) sim.Resume(); Assert.Equal(SimPhase.Running, sim.Snapshot.Phase); sim.Step(); // Phase should stay Running — this was the bug (used to revert to Paused) Assert.Equal(SimPhase.Running, sim.Snapshot.Phase); } [Fact] public void AutoPlayStep_MultipleSteps_StaysRunning() { var sim = CreateSimpleRunnable(); sim.Resume(); // Multiple consecutive steps from Running should all stay Running for (int i = 0; i < 5; i++) { sim.Step(); Assert.Equal(SimPhase.Running, sim.Snapshot.Phase); } } [Fact] public void AutoPlayStep_CollisionCausesPause() { var campaign = new CampaignDef { Name = "Collision Phase Test", InitialWidth = 4, InitialHeight = 2, Missions = [ new MissionDef { Id = 1, Name = "M1", TerrainPatch = new TerrainPatch { NewWidth = 4, NewHeight = 2, Cells = [ new PatchCell { Col = 0, Row = 0, Type = CellType.Production, Production = new ProductionDef(new Coords(0, 0), "P", CargoType.Wood, 4) }, new PatchCell { Col = 3, Row = 0, Type = CellType.Demand, Demand = new DemandDef(new Coords(3, 0), "D", CargoType.Wood, 99) } ] }, UnlockedPieces = [PieceKind.Rook], UnlockedLevels = [new PieceUpgrade(PieceKind.Rook, 1)], Stock = [new PieceStock(PieceKind.Rook, 3)] } ] }; var sim = SimHelper.FromCampaign(campaign); sim.Sim.ProcessCommand(new LoadCampaignCommand()); // Two rooks with same end cell → collision on first step sim.Place(PieceKind.Rook, (0, 1), (1, 1)); sim.Place(PieceKind.Rook, (2, 1), (1, 1)); sim.Resume(); Assert.Equal(SimPhase.Running, sim.Snapshot.Phase); var events = sim.Step(); // Collision should auto-pause even from Running Assert.Contains(events, e => e is PieceReturnedToStockEvent); Assert.Equal(SimPhase.Paused, sim.Snapshot.Phase); } [Fact] public void AutoPlayStep_LastMissionComplete_SetsMissionComplete() { var campaign = new CampaignDef { Name = "Last Mission Phase Test", InitialWidth = 3, InitialHeight = 1, Missions = [ new MissionDef { Id = 1, Name = "M1", TerrainPatch = new TerrainPatch { NewWidth = 3, NewHeight = 1, Cells = [ new PatchCell { Col = 0, Row = 0, Type = CellType.Production, Production = new ProductionDef(new Coords(0, 0), "P", CargoType.Wood, 4) }, new PatchCell { Col = 2, Row = 0, Type = CellType.Demand, Demand = new DemandDef(new Coords(2, 0), "D", CargoType.Wood, 1) } ] }, UnlockedPieces = [PieceKind.Rook], UnlockedLevels = [new PieceUpgrade(PieceKind.Rook, 1)], Stock = [new PieceStock(PieceKind.Rook, 1)] } ] }; var sim = SimHelper.FromCampaign(campaign); sim.Sim.ProcessCommand(new LoadCampaignCommand()); sim.Place(PieceKind.Rook, (1, 0), (2, 0)); // Running auto-play → last mission completes → MissionComplete phase sim.Resume(); Assert.Equal(SimPhase.Running, sim.Snapshot.Phase); // Step until mission complete for (int i = 0; i < 20; i++) { sim.Step(); if (sim.Snapshot.Phase == SimPhase.MissionComplete) break; } Assert.Equal(SimPhase.MissionComplete, sim.Snapshot.Phase); } [Fact] public void AutoPlayStep_NonLastMissionComplete_AutoAdvances_StaysRunning() { var campaign = new CampaignDef { Name = "Auto-Advance Phase Test", InitialWidth = 3, InitialHeight = 1, Missions = [ new MissionDef { Id = 1, Name = "M1", TerrainPatch = new TerrainPatch { NewWidth = 3, NewHeight = 1, Cells = [ new PatchCell { Col = 0, Row = 0, Type = CellType.Production, Production = new ProductionDef(new Coords(0, 0), "P", CargoType.Wood, 4) }, new PatchCell { Col = 2, Row = 0, Type = CellType.Demand, Demand = new DemandDef(new Coords(2, 0), "D", CargoType.Wood, 1) } ] }, UnlockedPieces = [PieceKind.Rook], UnlockedLevels = [new PieceUpgrade(PieceKind.Rook, 1)], Stock = [new PieceStock(PieceKind.Rook, 2)] }, new MissionDef { Id = 2, Name = "M2", TerrainPatch = new TerrainPatch { NewWidth = 5, NewHeight = 1, Cells = [ new PatchCell { Col = 4, Row = 0, Type = CellType.Demand, Demand = new DemandDef(new Coords(4, 0), "D2", CargoType.Wood, 99) } ] }, UnlockedPieces = [], UnlockedLevels = [], Stock = [new PieceStock(PieceKind.Rook, 1)] } ] }; var sim = SimHelper.FromCampaign(campaign); sim.Sim.ProcessCommand(new LoadCampaignCommand()); sim.Place(PieceKind.Rook, (1, 0), (2, 0)); sim.Resume(); // Step until mission 1 completes and auto-advances bool advanced = false; for (int i = 0; i < 20; i++) { var events = sim.Step(); if (events.Any(e => e is MissionStartedEvent ms && ms.MissionIndex == 1)) { advanced = true; // Phase should still be Running after auto-advance Assert.Equal(SimPhase.Running, sim.Snapshot.Phase); break; } } Assert.True(advanced, "Mission 1 should have auto-advanced to Mission 2"); Assert.Equal(1, sim.Snapshot.Campaign!.CurrentMissionIndex); } }