git mv packages/business -> packages/organization (@mosaic/organization). The old package stays as one-line re-export shims of each moved module, with src/business.mjs re-exporting src/organization-config.mjs and src/cli.mjs forwarding to the moved executable. BusinessError is OrganizationConfigError (one constructor, name kept BusinessError); OrganizationError stays distinct. The bus adapter gains organization-context.mjs (busOrganization), with business.mjs (busBusiness) as the same function. Import consumers, manifests, the lockfile, scripts/mosaic, stack-copy.sh COPY_PATHS (both roots), seat sandbox RUNNER_FILES and the copy closure walker (organization-imports.mjs, both roots required) move to the new path. v1 serialization, v1 writers and the stage A fixtures are unchanged; the stage A contract tests stay where they were and pass as before.
82 lines
4.3 KiB
JavaScript
82 lines
4.3 KiB
JavaScript
// Private fixture runtime only; no live host, unit, store or organization file.
|
|
import { spawn } from "node:child_process";
|
|
import { once } from "node:events";
|
|
import { chmodSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { fixture, BUSINESSES } from "./helpers.mjs";
|
|
import { systemFor } from "../../organization/tests/helpers.mjs";
|
|
import { reserveOrg, activateOrg } from '../../organization/src/org-registry.mjs';
|
|
|
|
export async function childResult(file, args, env, input) {
|
|
const child = spawn(file, args, { env, stdio: ["pipe", "pipe", "pipe"] });
|
|
let stdout = "", stderr = "";
|
|
child.stdout.on("data", b => stdout += b);
|
|
child.stderr.on("data", b => stderr += b);
|
|
child.stdin.on("error", () => {});
|
|
child.stdin.end(input);
|
|
const [status, signal] = await once(child, "close");
|
|
return { status, signal, stdout, stderr };
|
|
}
|
|
|
|
export async function provedRuntime(t) {
|
|
const root = mkdtempSync(join(tmpdir(), "channel-recovery-"));
|
|
let host;
|
|
t.after(async () => {
|
|
try {
|
|
if (host && host.exitCode === null && host.signalCode === null) {
|
|
const done = once(host, "close");
|
|
const timer = setTimeout(() => host.kill("SIGKILL"), 3000);
|
|
host.kill("SIGTERM");
|
|
try { await done; } finally { clearTimeout(timer); }
|
|
}
|
|
} finally { rmSync(root, { recursive: true, force: true }); }
|
|
});
|
|
const f = fixture(root, "demo", doc => ({ ...doc, plugins: { channel: [] } }));
|
|
mkdirSync(join(root, '.config'), { mode: 0o700 });
|
|
const configDir = join(root, '.config', 'mosaic-test');
|
|
renameSync(join(root, 'config'), configDir); chmodSync(configDir, 0o700);
|
|
writeFileSync(join(f.dataRoot, 'instance.json'), JSON.stringify({ instanceVersion: 1, name: 'test' }));
|
|
const ctx = { configDir, dataRoot: f.dataRoot, instance: 'test' };
|
|
const org = await reserveOrg(ctx, 'demo'); await activateOrg(ctx, 'demo');
|
|
const url = rel => new URL(rel, import.meta.url).href;
|
|
const humanURL = url("../../bus/src/human.mjs");
|
|
const script = join(root, "fixture-runtime.mjs");
|
|
// As in stack-copy.test.mjs, isolate the fixture's ancestry from the seat.
|
|
// The real PID, UID, argv path, start time, nonce and verifier still run.
|
|
writeFileSync(script, `
|
|
import { mock } from 'node:test';
|
|
const real = await import(${JSON.stringify(humanURL)});
|
|
mock.module(${JSON.stringify(humanURL)}, { namedExports: {
|
|
readProcess: real.readProcess,
|
|
verifyHuman: (proof, options) => real.verifyHuman(proof, { ...options,
|
|
readProcess: pid => ({ ...real.readProcess(pid), ppid: 1 }) }),
|
|
} });
|
|
const { startBroker } = await import(${JSON.stringify(url("../../bus/src/runtime.mjs"))});
|
|
const runtime = await startBroker({ dataRoot: ${JSON.stringify(org.root)},
|
|
businesses: ${JSON.stringify(BUSINESSES)},
|
|
organization: { dir: ${JSON.stringify(configDir)}, rolesDir: ${JSON.stringify(f.roles)}, system: ${JSON.stringify(systemFor(root))} } });
|
|
process.on('SIGTERM', async () => { await runtime.close(); process.exit(0); });
|
|
console.log('fixture ready');
|
|
`);
|
|
const env = { PATH: process.env.PATH, HOME: root, TMPDIR: process.env.TMPDIR,
|
|
MOSAIC_CONFIG: join(configDir, 'config.json'), MOSAIC_ROLES_DIR: f.roles };
|
|
host = spawn(process.execPath, ["--experimental-test-module-mocks", script], { env, stdio: ["ignore", "pipe", "pipe"] });
|
|
let output = "", errors = "";
|
|
host.stderr.on("data", b => errors += b);
|
|
await new Promise((resolve, reject) => {
|
|
host.stdout.on("data", b => { output += b; if (output.includes("fixture ready")) resolve(); });
|
|
host.once("exit", code => reject(new Error(`fixture failed ${code}: ${errors}`)));
|
|
host.once("error", reject);
|
|
});
|
|
const cli = fileURLToPath(new URL("../src/cli.mjs", import.meta.url));
|
|
const shim = fileURLToPath(new URL("../../bus/src/human-cli.mjs", import.meta.url));
|
|
return { root, f, env, shim, socket: join(org.root, "bus/broker.sock"),
|
|
cli: argv => childResult(process.execPath, [cli, ...argv, "--business", "demo"], env),
|
|
shimCall: (file, request) => childResult(process.execPath, [file, join(org.root, "bus/broker.sock")], env, JSON.stringify(request) + "\n"),
|
|
file: join(configDir, "businesses/demo.json"),
|
|
history: () => readFileSync(join(configDir, "businesses/.history/demo/changes.jsonl"), "utf8").trim().split("\n").map(JSON.parse),
|
|
};
|
|
}
|