const test = require("node:test"); const assert = require("node:assert/strict"); const data = require("../assets/scripts/pages/princess-lima-data.js"); const state = require("../assets/scripts/pages/princess-lima-state.js"); test("fresh save uses the Princess Lima schema and safe village start", () => { const fresh = state.fresh(" Rowan ", "pine"); assert.equal(fresh.version, 1); assert.equal(state.STORAGE_KEY, "zxh_princess_lima_rpg_v1"); assert.equal(fresh.player.name, "Rowan"); assert.equal(fresh.player.appearance, "pine"); assert.equal(fresh.region, "village"); assert.deepEqual({ x: fresh.position.x, y: fresh.position.y }, data.MAPS.village.spawns.start); assert.equal(fresh.settings.soundEnabled, false); assert.equal(fresh.introSeen, false); assert.equal(fresh.settings.subtitles, true); assert.equal(fresh.settings.narrationEnabled, true); }); test("old v1 saves continue without unexpectedly replaying the new introduction", () => { const candidate = state.fresh("Legacy", "azure"); delete candidate.introSeen; delete candidate.settings.voice; delete candidate.settings.subtitles; delete candidate.settings.narrationEnabled; const restored = state.normalize(candidate); assert.equal(restored.introSeen, true); assert.equal(restored.settings.voice, 0.85); assert.equal(restored.settings.subtitles, true); assert.equal(restored.settings.narrationEnabled, true); }); test("names and appearances validate", () => { assert.equal(state.validName(" A Traveller "), "A Traveller"); assert.equal(state.validName(""), null); assert.equal(state.validName("x".repeat(21)), null); assert.equal(state.normalize({ ...state.fresh("A", "azure"), player: { name: "A", appearance: "bad" } }), null); }); test("valid saves restore bounded state and derived equipment", () => { const candidate = state.fresh("Mira", "azure"); candidate.health = 999; candidate.inventory = [ { id: "tempered_sword", quantity: 1 }, { id: "healing_tonic", quantity: 99 }, { id: "bad", quantity: 4 } ]; candidate.solvedPuzzles = ["forest_stones", "forest_stones", "bad"]; candidate.defeatedBosses = ["captain", "captain", "bad"]; const restored = state.normalize(candidate); assert.equal(restored.health, restored.maxHealth); assert.equal(restored.attack, 2); assert.equal(restored.inventory.find((item) => item.id === "healing_tonic").quantity, 9); assert.deepEqual(restored.solvedPuzzles, ["forest_stones"]); assert.deepEqual(restored.defeatedBosses, ["captain"]); }); test("invalid and unsupported saves recover without throwing", () => { assert.equal(state.parse("{"), null); assert.equal(state.parse(JSON.stringify({ version: 99 })), null); assert.equal(state.parse(JSON.stringify({ version: 1 })), null); }); test("quest, route, puzzle, boss and settings restoration filters malformed data", () => { const candidate = state.fresh("Mira", "ember"); candidate.quests.aftermath = { status: "active", count: 999, rewarded: true }; candidate.quests.village_defence = { status: "nonsense", count: -10 }; candidate.unlockedRoutes = ["guide_found", "guide_found", "fake"]; candidate.settings = { master: 4, music: -2, effects: "bad", textSpeed: "instant", highContrast: true }; const restored = state.normalize(candidate); assert.equal(restored.quests.aftermath.count, 99); assert.equal(restored.quests.village_defence.status, "locked"); assert.deepEqual(restored.unlockedRoutes, ["guide_found"]); assert.equal(restored.settings.master, 1); assert.equal(restored.settings.music, 0); assert.equal(restored.settings.effects, 0.7); assert.equal(restored.settings.textSpeed, "instant"); });