forked from phenom/healer-man
90 lines
3.2 KiB
TypeScript
90 lines
3.2 KiB
TypeScript
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);
|
|
});
|
|
});
|