using System; using System.IO; using NUnit.Framework; namespace Ironspore.Tests { public sealed class ProfileStoreTests { string folder; string path; ProfileStore store; [SetUp] public void Setup() { folder = Path.Combine(Path.GetTempPath(), "IronsporeLedgerTests", Guid.NewGuid().ToString("N")); path = Path.Combine(folder, "profile.json"); store = new ProfileStore(path); Assert.IsTrue(store.Begin(1, new[] { 0, 0, 0, 0 }, 180, 7)); } [TearDown] public void Cleanup() { string allowed = Path.GetFullPath(Path.Combine(Path.GetTempPath(), "IronsporeLedgerTests")) + Path.DirectorySeparatorChar; if (Path.GetFullPath(folder).StartsWith(allowed, StringComparison.OrdinalIgnoreCase) && Directory.Exists(folder)) Directory.Delete(folder, true); } [Test] public void PodBanksOnceAndSurvivesDeathAndReload() { Assert.IsTrue(store.AddCargo("blueprint_vector", 1, "crateA")); Assert.IsTrue(store.AddCargo("alloy", 4, "crateB")); string uid = store.Data.run.cargo[0].uid; Assert.IsTrue(store.SendCargo(new[] { uid })); Assert.IsFalse(store.SendCargo(new[] { uid })); Assert.IsTrue(store.Finish(true)); var loaded = new ProfileStore(path); Assert.AreEqual(1, loaded.Data.blueprints.Count); Assert.AreEqual(0, loaded.Data.alloy); Assert.IsFalse(loaded.Data.run.active); Assert.IsTrue(loaded.Data.run.podUsed); } [Test] public void PodRejectsOvercapacityAndDuplicateSelectionsWithoutConsuming() { for (int i = 0; i < 5; i++) Assert.IsTrue(store.AddCargo("blueprint_" + i, 1, "crate" + i)); var bag = store.Data.run.cargo; Assert.IsFalse(store.SendCargo(new[] { bag[0].uid, bag[1].uid, bag[2].uid, bag[3].uid, bag[4].uid })); Assert.IsFalse(store.SendCargo(new[] { bag[0].uid, bag[0].uid })); Assert.IsFalse(store.Data.run.podUsed); Assert.AreEqual(5, store.Data.run.cargo.Count); Assert.AreEqual(0, store.Data.blueprints.Count); Assert.IsTrue(store.SendCargo(new[] { bag[0].uid, bag[1].uid, bag[2].uid, bag[3].uid })); } [Test] public void SearchedSourceCannotDuplicateLootAfterReload() { Assert.IsTrue(store.AddCargo("alloy", 5, "crateA")); store = new ProfileStore(path); Assert.IsFalse(store.AddCargo("alloy", 5, "crateA")); Assert.AreEqual(5, store.Data.run.cargo[0].count); Assert.IsTrue(store.Finish(false)); Assert.IsFalse(store.Finish(false)); Assert.AreEqual(5, store.Data.alloy); Assert.AreEqual(1, store.Data.extractions); } [Test] public void CargoThenWholeMechReturnAreIndependentReceipts() { store.AddCargo("alloy", 4); store.AddCargo("cells", 3); Assert.IsTrue(store.SendCargo(new[] { store.Data.run.cargo[0].uid })); Assert.IsTrue(store.CompleteRoute()); Assert.IsFalse(store.CompleteRoute()); Assert.IsTrue(store.Finish(false)); Assert.AreEqual(4, store.Data.alloy); Assert.AreEqual(3, store.Data.cells); Assert.AreEqual(1, store.Data.completed); Assert.AreEqual(1, store.Data.extractions); } [Test] public void SafeCaseChecksShapeAndPersistsRotatedWeapon() { Assert.IsFalse(store.Uninstall(0, Hold.SafeCase, 0, 0, false)); Assert.IsFalse(store.Uninstall(0, Hold.SafeCase, 0, 0, true)); Assert.IsTrue(store.AddCargo("cells", 1)); Assert.IsTrue(store.MoveItem(store.Data.run.cargo[0].uid, Hold.Cargo, Hold.SafeCase, 0, 1)); string weapon = store.Data.run.installed.Find(i => i.itemId == "guided").uid; Assert.IsFalse(store.Uninstall(1, Hold.SafeCase, 0, 0, false)); Assert.IsTrue(store.Uninstall(1, Hold.SafeCase, 0, 0, true)); store = new ProfileStore(path); var saved = store.Data.run.safeCase.Find(i => i.uid == weapon); Assert.IsTrue(saved.rotated); Assert.AreEqual(0, saved.x); Assert.AreEqual(0, saved.y); Assert.AreEqual(-1, store.Data.run.equipment[1]); Assert.IsTrue(store.Finish(false)); Assert.IsTrue(store.Begin(1, new[] { 0, 0, 0, 0 }, 180, 8)); Assert.IsTrue(store.Data.run.installed.Exists(i => i.uid == weapon)); Assert.IsFalse(store.Data.warehouse.Exists(i => i.uid == weapon)); } [Test] public void DeathLocksInventoryAndRetriesOnlyTheProtectedReturn() { string run = store.Data.run.id; Assert.IsTrue(store.AddCargo("alloy", 4, "a", "pulse")); string pulse = store.Data.run.cargo.Find(i => i.itemId == "pulse").uid; Assert.IsTrue(store.MoveItem(pulse, Hold.Cargo, Hold.SafeCase)); Assert.IsTrue(store.SendCargo(new[] { store.Data.run.cargo[0].uid })); Assert.IsTrue(store.AddCargo("coil", 1, "b")); Assert.IsTrue(store.MarkDestroyed()); store = new ProfileStore(path); Assert.IsFalse(store.MoveItem(pulse, Hold.SafeCase, Hold.Cargo)); Assert.IsFalse(store.Finish(false)); Directory.CreateDirectory(path + ".tmp"); Assert.IsFalse(store.Finish(true)); Assert.IsTrue(store.Data.run.destructionPending); Assert.AreEqual(0, store.Data.warehouse.Count); Directory.Delete(path + ".tmp"); store = new ProfileStore(path); Assert.IsTrue(store.Finish(true)); Assert.IsFalse(store.Finish(true)); Assert.AreEqual(4, store.Data.alloy); Assert.AreEqual(1, store.Data.warehouse.Count); Assert.AreEqual(pulse, store.Data.warehouse[0].uid); CollectionAssert.AreEquivalent(new[] { run + ":secure_eject", run + ":cargo", run + ":return" }, store.Data.receipts); } [Test] public void SplitMergeSwapAndAtomicPlacementPreserveOwnership() { Assert.IsTrue(store.AddCargo("cells", 10)); string cells = store.Data.run.cargo[0].uid; Assert.IsTrue(store.MoveItem(cells, Hold.Cargo, Hold.SafeCase, 1, 1, false, 4)); string split = store.Data.run.safeCase[0].uid; Assert.AreNotEqual(cells, split); Assert.IsFalse(store.MergeMaterial(cells, Hold.Cargo, split, Hold.SafeCase, 7)); Assert.IsTrue(store.MergeMaterial(cells, Hold.Cargo, split, Hold.SafeCase, 2)); Assert.IsTrue(store.AddCargo("rapid", 1)); string rapid = store.Data.run.cargo.Find(i => i.itemId == "rapid").uid; Assert.IsFalse(store.SwapItems(rapid, Hold.Cargo, split, Hold.SafeCase, true)); Assert.IsTrue(store.SwapItems(cells, Hold.Cargo, split, Hold.SafeCase, false)); Assert.IsFalse(store.MoveItem(rapid, Hold.Cargo, Hold.SafeCase)); Assert.IsTrue(store.Finish(true)); Assert.AreEqual(4, store.Data.cells); } [Test] public void FailedRefitOrWriteKeepsOldSlotAndLootSource() { Assert.IsTrue(store.AddCargo("alloy", 1, "crate", "coil")); string uid = store.Data.run.cargo.Find(i => i.itemId == "coil").uid; Assert.IsFalse(store.InstallItem(uid, Hold.Cargo, parts => false)); Assert.AreEqual(0, store.Data.run.equipment[0]); Directory.CreateDirectory(path + ".tmp"); Assert.IsFalse(store.Uninstall(1, Hold.SafeCase, 0, 0, false)); Assert.IsFalse(store.AddCargo("alloy", 1, "retry")); Assert.IsFalse(store.Data.run.searched.Contains("retry")); Directory.Delete(path + ".tmp"); Assert.IsTrue(store.InstallItem(uid, Hold.Cargo, parts => true)); Assert.AreEqual(1, store.Data.run.equipment[0]); Assert.IsTrue(store.Data.run.cargo.Exists(i => i.itemId == "rapid")); Assert.IsTrue(store.Data.run.installed.Exists(i => i.uid == uid)); } [Test] public void BorrowedStarterDoesNotReplaceLostStock() { Assert.IsTrue(store.Finish(true)); Assert.IsFalse(store.Begin(1, new[] { 1, 0, 0, 0 }, 180, 8)); Assert.IsTrue(store.Begin(1, new[] { 0, 0, 0, 0 }, 180, 8)); Assert.IsTrue(store.Data.run.installed.TrueForAll(i => i.borrowed)); Assert.IsTrue(store.Uninstall(1, Hold.SafeCase, 0, 0, false)); Assert.IsTrue(store.Finish(false)); Assert.IsEmpty(store.Data.warehouse); } [Test] public void ManufacturingRequiresResearchAndMaterialsAndCreatesDistinctInstances() { Assert.IsTrue(store.Finish(false)); const string blueprint = "blueprint_coil_capacitor"; Assert.IsTrue(store.Commit(s => { s.alloy = 8; s.cells = 4; return true; })); Assert.IsFalse(store.Manufacture(blueprint)); Assert.IsTrue(store.Commit(s => { s.unlocked.Add(blueprint); return true; })); Assert.IsTrue(store.Manufacture(blueprint)); Assert.IsTrue(store.Manufacture(blueprint)); Assert.IsFalse(store.Manufacture(blueprint)); var copies = store.Data.warehouse.FindAll(i => i.itemId == "coil"); Assert.AreNotEqual(copies[0].uid, copies[1].uid); Assert.IsTrue(store.Begin(1, new[] { 1, 0, 0, 0 }, 180, 8, selected: new[] { copies[1].uid, null, null, null })); Assert.IsTrue(store.Data.run.installed.Exists(i => i.uid == copies[1].uid)); Assert.IsTrue(store.Finish(true)); Assert.IsTrue(store.Data.warehouse.Exists(i => i.uid == copies[0].uid)); Assert.AreEqual(0, store.Data.alloy); Assert.AreEqual(0, store.Data.cells); } [Test] public void WriteFailureLeavesOriginalMemoryState() { string blocker = Path.Combine(folder, "blocked"); File.WriteAllText(blocker, "not a directory"); var unavailable = new ProfileStore(Path.Combine(blocker, "save.json")); Assert.IsFalse(unavailable.Begin(0, new[] { 0, 0, 0, 0 }, 100, 1)); Assert.IsFalse(unavailable.Data.run.active); Assert.IsNotEmpty(unavailable.Error); } [Test] public void CorruptPrimaryRecoversPreviousCompleteSnapshot() { store.AddCargo("cells", 2); store.Commit(s => true); File.WriteAllText(path, "{broken json"); var recovered = new ProfileStore(path); Assert.IsTrue(recovered.Data.run.active); Assert.AreEqual(2, recovered.Data.run.cargo[0].count); } } }