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
@@ -429,5 +429,64 @@ namespace ProjectM.Tests
}
}
// ---- any-attack harvest (MC-4 melee mines too) ----
static int LedgerCount(EntityManager em, Entity ledger, ushort itemId)
{
var buf = em.GetBuffer<StorageEntry>(ledger);
for (int i = 0; i < buf.Length; i++) if (buf[i].ItemId == itemId) return buf[i].Count;
return 0;
}
[Test]
public void Cleave_Harvests_A_Base_Node_To_The_Shared_Ledger()
{
var (world, group) = MakeWorld("MeleeHarvestBase", 100, server: true);
using (world)
{
var em = world.EntityManager;
var ledger = em.CreateEntity(typeof(ResourceLedger));
em.AddBuffer<StorageEntry>(ledger);
var p = MakePlayer(em, new float2(0, 1)); // at origin, facing +Z
var node = em.CreateEntity();
em.AddComponentData(node, LocalTransform.FromPosition(new float3(0, 0, 2))); // in the light cone
em.AddComponentData(node, new ResourceNode { ResourceId = ResourceId.Ore, Remaining = 30, HarvestPerHit = 5f });
em.AddComponentData(node, new RegionTag { Region = RegionId.Base });
Press(em, p);
group.Update();
Assert.AreEqual(5, LedgerCount(em, ledger, ResourceId.Ore), "a base-node melee hit credits the shared ledger (the build pool).");
Assert.AreEqual(25, em.GetComponentData<ResourceNode>(node).Remaining, "the node is depleted by HarvestPerHit (written back for the chip VFX).");
}
}
[Test]
public void Cleave_Harvests_An_Expedition_Node_To_Personal_Inventory_Not_The_Ledger()
{
var (world, group) = MakeWorld("MeleeHarvestExp", 100, server: true);
using (world)
{
var em = world.EntityManager;
var ledger = em.CreateEntity(typeof(ResourceLedger));
em.AddBuffer<StorageEntry>(ledger);
var p = MakePlayer(em, new float2(0, 1)); // GhostOwner NetworkId 7
em.AddComponent<PlayerTag>(p);
em.AddBuffer<InventorySlot>(p);
var node = em.CreateEntity();
em.AddComponentData(node, LocalTransform.FromPosition(new float3(0, 0, 2)));
em.AddComponentData(node, new ResourceNode { ResourceId = ResourceId.Aether, Remaining = 30, HarvestPerHit = 5f });
em.AddComponentData(node, new RegionTag { Region = RegionId.Expedition });
Press(em, p);
group.Update();
var inv = em.GetBuffer<InventorySlot>(p);
Assert.AreEqual(5, InventoryMath.CountOf(inv, ResourceId.Aether), "an expedition-node melee hit lands in the swinging player's PERSONAL inventory.");
Assert.AreEqual(0, LedgerCount(em, ledger, ResourceId.Aether), "an expedition harvest does NOT credit the shared base ledger (DR-026 personal haul).");
Assert.AreEqual(25, em.GetComponentData<ResourceNode>(node).Remaining, "the node is still depleted.");
}
}
}
}