import { execFileSync } from "node:child_process"; import { readFileSync } from "node:fs"; import path from "node:path"; import { describe, expect, it } from "vitest"; const projectRoot = process.cwd(); const pipelineRoot = path.join(projectRoot, "scripts", "runewaker-pipeline"); const scripts = [ "lib/recipe.mjs", "lib/wdb.mjs", "discover-instances.mjs", "inspect-wdb.mjs", "scaffold-instance.mjs", "refresh-population.mjs", "build-environment.mjs", "build-population.mjs", "extract-mechanics.mjs", "validate-instance.mjs", "cli.mjs", ]; describe("generic RuneWaker instance pipeline", () => { it("keeps every JavaScript pipeline entry point syntactically valid", () => { for (const script of scripts) { expect(() => execFileSync(process.execPath, ["--check", path.join(pipelineRoot, script)], { cwd: projectRoot, stdio: "pipe", })).not.toThrow(); } }); it("documents the offline boundary and all import stages", () => { const guide = readFileSync(path.join(pipelineRoot, "INSTANCE_IMPORT_GUIDE.md"), "utf8"); for (const stage of [ "discover", "scaffold", "environment", "refresh", "actors", "population", "mechanics", "validate", ]) { expect(guide).toContain(stage); } expect(guide).toContain("SQL Server is used only by the explicit `refresh` command"); expect(guide).toContain("Simulation state stays outside React scene components"); }); it("limits SQL restore access to the explicit refresh command", () => { const cli = readFileSync(path.join(pipelineRoot, "cli.mjs"), "utf8"); const refresh = readFileSync(path.join(pipelineRoot, "refresh-population.ps1"), "utf8"); const offlineScripts = [ "build-environment.mjs", "build-population.mjs", "extract-mechanics.mjs", ].map((name) => readFileSync(path.join(pipelineRoot, name), "utf8")).join("\n"); expect(cli).toContain('command === "refresh"'); expect(refresh).toContain("SET READ_ONLY WITH ROLLBACK IMMEDIATE"); expect(refresh).toContain("DROP DATABASE"); expect(refresh).toContain("[Parameter(Mandatory = $true)][int]$ZoneId"); expect(offlineScripts).not.toMatch(/SqlConnection|RESTORE DATABASE|ROM_Global\.bak/); }); it("records a reviewed, non-executable mechanics audit for Forsaken Abbey", () => { const report = JSON.parse(readFileSync(path.join( projectRoot, "src/assets/game/dungeons/forsaken-abbey/mechanics-audit.json", ), "utf8")); expect(report.status).toBe("review-required"); expect(report.luaSources).toHaveLength(3); expect(report.bossRows).toHaveLength(8); expect(report.templateCandidates).toHaveLength(31); expect(report.safety).toMatchObject({ executableMechanicsGenerated: false, runtimeSqlDependency: false, }); }); it("passes the packaged Forsaken Abbey validation gate", () => { const report = JSON.parse(readFileSync(path.join( projectRoot, "src/assets/game/dungeons/forsaken-abbey/instance-validation-report.json", ), "utf8")); expect(report.status).toBe("green"); expect(report.errors).toEqual([]); expect(report.checks.every((check: { passed: boolean }) => check.passed)).toBe(true); }); });