PR #1916 rule: a refusal names the field or file, never a config value or a host path. readJsonFile's three refusals and parseJsonFileText now name the file by its `what` label only. Callers whose label alone is ambiguous add a name that already passed a strict pattern (business id, role name, org id, turn id, project key) or a fixed path under the data root. Exit codes, refusal codes and check order are unchanged. organization: util, access, project, config-command, library-seats, org-service deleteOrg receipt, agent-levels regex; T1 files role.mjs and organization-config.mjs change only the readJsonFile label. cli: access lock, accounts owner, host state/lock, notifier journal, notify-config, run-evidence, dev-fixture, git-socket, org-bus, org-console. discord: turn record label names the turn id. Sentinel tests (sk-test-NOT-A-SECRET-1234 as a path component) cover each changed refusal and fail on BASE; old path pins updated.
67 lines
3.1 KiB
JavaScript
67 lines
3.1 KiB
JavaScript
// Row 325 (#1821): factory validation must load the repository's actual project document.
|
|
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { spawnSync } from "node:child_process";
|
|
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { REPO, REPO_ROLES, systemConfig, tmp, writeJson } from "./helpers.mjs";
|
|
|
|
const CASE1 = join(REPO, "docs/guides/case1-business.mosaic-stack.json");
|
|
const PROJECT = join(REPO, ".mosaic/project.json");
|
|
const document = () => JSON.parse(readFileSync(CASE1, "utf8"));
|
|
|
|
function fixture(t, { oldKey = false } = {}) {
|
|
const root = tmp();
|
|
t.after(() => rmSync(root, { recursive: true, force: true }));
|
|
const doc = document();
|
|
assert.equal(Object.keys(doc.projects).length, 1);
|
|
const [key] = Object.keys(doc.projects);
|
|
const projectRoot = join(root, "checkout");
|
|
const projectFile = join(projectRoot, ".mosaic/project.json");
|
|
const bytes = readFileSync(PROJECT);
|
|
mkdirSync(join(projectRoot, ".mosaic"), { recursive: true });
|
|
writeFileSync(projectFile, bytes);
|
|
assert.deepEqual(readFileSync(projectFile), bytes, "fixture must use the real project's unmodified bytes");
|
|
const entry = { ...doc.projects[key], root: projectRoot };
|
|
doc.projects = { [oldKey ? "mosaic-stack" : key]: entry };
|
|
const config = systemConfig(root);
|
|
writeJson(join(root, "config/businesses/mosaic-stack.json"), doc);
|
|
return {
|
|
project: JSON.parse(bytes), projectFile,
|
|
validate: () => spawnSync(process.execPath, [join(REPO, "packages/organization/src/cli.mjs"), "validate", "mosaic-stack"], {
|
|
env: { ...process.env, HOME: root, MOSAIC_CONFIG: config, MOSAIC_ROLES_DIR: REPO_ROLES },
|
|
encoding: "utf8", timeout: 30000,
|
|
}),
|
|
};
|
|
}
|
|
|
|
test("row 325: the Case 1 project's sole key matches the repository project id", () => {
|
|
const doc = document();
|
|
const project = JSON.parse(readFileSync(PROJECT, "utf8"));
|
|
assert.equal(project.id, "stack");
|
|
assert.deepEqual(Object.keys(doc.projects), [project.id]);
|
|
assert.equal(doc.id, "mosaic-stack");
|
|
assert.equal(doc.projects[project.id].root, "/srv/mosaic/src/mosaic-stack");
|
|
});
|
|
|
|
test("row 325: real business validate reports the copied repository project valid", (t) => {
|
|
const x = fixture(t);
|
|
const r = x.validate();
|
|
assert.equal(r.status, 0, r.stdout + r.stderr);
|
|
const result = JSON.parse(r.stdout);
|
|
assert.equal(result.business, "mosaic-stack");
|
|
assert.deepEqual(result.projects, { [x.project.id]: "valid" });
|
|
assert.doesNotMatch(r.stderr, /no project file|project variables/);
|
|
});
|
|
|
|
test("row 325: the old mosaic-stack project key refuses against those same project bytes", (t) => {
|
|
const x = fixture(t, { oldKey: true });
|
|
const r = x.validate();
|
|
assert.equal(r.status, 2, r.stdout + r.stderr);
|
|
assert.equal(r.stdout, "");
|
|
assert.match(r.stderr, /project file of project mosaic-stack has id that does not match the declared project key/);
|
|
// The refusal names the declared key, never the project root (PR #1916).
|
|
assert.ok(!r.stderr.includes(x.projectFile), r.stderr);
|
|
assert.doesNotMatch(r.stderr, /no project file/);
|
|
});
|