Tests: base mining field, expedition teardown, schedule siege, melee harvest

8 new EditMode tests (302 total, all green): BaseFieldSpawnSystem (target count + Base/Ore + cadence + top-up), ExpeditionFieldSystem teardown preserves the base field, ThreatDirector schedule arming, and melee harvest routing (base->ledger, expedition->personal inventory) which guards the cross-region leak the post-impl review caught. See DR-031.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-11 15:00:03 -07:00
parent e1ed08a803
commit 096c62862f
6 changed files with 312 additions and 0 deletions
@@ -151,5 +151,52 @@ namespace ProjectM.Tests
"The siege clock resets after collapse.");
}
}
[Test]
public void Schedule_First_Pass_Seeds_NextTick_Without_Firing()
{
var (world, group) = MakeWorld("ThreatScheduleSeed", serverTick: 200);
using (world)
{
var em = world.EntityManager;
var config = DefaultConfig();
config.PostExpeditionEnabled = 0;
config.ScheduleEnabled = 1;
config.ScheduleIntervalTicks = 100;
var dir = MakeDirector(em, CyclePhase.Calm, new ThreatState { NextScheduledTick = 0 }, config);
group.Update();
var ts = em.GetComponentData<ThreatState>(dir);
Assert.AreEqual(300u, ts.NextScheduledTick, "The first pass seeds the next scheduled tick one interval out (200 + 100).");
Assert.AreEqual(0, ts.PendingSiegeSize, "The first pass only seeds — it does not arm a siege immediately.");
}
}
[Test]
public void Schedule_Arms_Siege_On_Cadence_Without_An_Expedition()
{
var (world, group) = MakeWorld("ThreatScheduleFire", serverTick: 400);
using (world)
{
var em = world.EntityManager;
var config = DefaultConfig();
config.PostExpeditionEnabled = 0; // isolate the schedule source
config.ScheduleEnabled = 1;
config.ScheduleIntervalTicks = 100;
config.ScheduleSizePerWave = 0;
config.SizeBase = 5;
config.PostExpeditionDelayTicks = 10;
// NextScheduledTick 300 <= now 400 => the scheduled siege is due.
var dir = MakeDirector(em, CyclePhase.Calm, new ThreatState { NextScheduledTick = 300 }, config);
group.Update();
var ts = em.GetComponentData<ThreatState>(dir);
Assert.AreEqual(5, ts.PendingSiegeSize, "A due scheduled tick arms a SizeBase siege with NO expedition trip.");
Assert.AreEqual(410u, ts.ArmTick, "The scheduled siege telegraphs at now + delay (400 + 10).");
Assert.AreEqual(500u, ts.NextScheduledTick, "The next scheduled siege is one interval out (400 + 100).");
}
}
}
}