using NUnit.Framework;
using ProjectM.Server;
using ProjectM.Simulation;
using Unity.Entities;
namespace ProjectM.Tests
{
///
/// Guards the ComponentSystemSorter "circular dependency" hazard that is INVISIBLE to the rest of the suite:
/// every other fixture registers a single system then sorts, so a cross-system [UpdateBefore/After] cycle can
/// never surface — it only throws at world creation in Play. This registers the REAL ordered server-sim set
/// (every system that participates in an UpdateBefore/After relation, or its constraint is silently ignored)
/// into one SimulationSystemGroup and sorts, reproducing the Play-time sort headlessly. Only SortSystems runs
/// (never Update), so a bare world with no entities/singletons suffices.
///
public class SystemOrderingCycleTests
{
[Test]
public void RunCycleCombatChain_Sorts_Without_A_Dependency_Cycle()
{
using var world = new World("OrderCycleGuard");
var group = world.GetOrCreateSystemManaged();
void Add() where T : unmanaged, ISystem
=> group.AddSystemToUpdateList(world.GetOrCreateSystem());
// RPC-receive systems + the surviving server spine. The 2026-08-07 audit purge removed
// ReadyToggle / RouteSelect / PortalInteract / MetaSpend / BoonApply / PrepPurchase / RunDirector /
// RoomField / RoomEnemyDirector / BoonOffer / BossAI / EnemyProjectile* along with the shell.
Add();
Add();
Add();
Add();
Add();
Add();
Add();
Assert.DoesNotThrow(() => group.SortSystems(),
"A [UpdateBefore/After] cycle in the run/cycle/combat chain throws here instead of only at Play world-creation.");
}
[Test]
public void PredictedCombatChain_Sorts_Without_A_Dependency_Cycle()
{
// A cycle in the predicted combat chain's [UpdateBefore/After] edges is INVISIBLE to per-system
// fixtures — it only throws at Play world creation. Co-register the chain and sort to reproduce that
// headlessly (SortSystems only, never Update). DashTrailDamageSystem and KillRewardSystem were
// removed from this roster with the 2026-08-07 boon purge.
using var world = new World("OrderCyclePredicted");
var group = world.GetOrCreateSystemManaged();
void Add() where T : unmanaged, ISystem
=> group.AddSystemToUpdateList(world.GetOrCreateSystem());
Add(); Add(); Add();
Add(); Add(); Add();
Add();
// 07-15 facing rework: PlayerAimSystem gained [UpdateAfter(MeleeComboSystem)] (plus the existing
// StatRecompute/PlayerDeathState UpdateBefore edges) - co-register the full facing neighborhood so a
// cycle in these edges throws here instead of only at Play world-creation.
Add(); Add(); Add(); Add();
Assert.DoesNotThrow(() => group.SortSystems(),
"A cycle in the Phase 1.7 predicted combat chain throws here instead of only at Play world-creation.");
}
}
}