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.
88 lines
4.9 KiB
JavaScript
88 lines
4.9 KiB
JavaScript
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { join } from "node:path";
|
|
import { bootConfig, loadSystem } from "../src/config.mjs";
|
|
import { writeJson } from "../../organization/tests/helpers.mjs";
|
|
import { fixture, tmp } from "./helpers.mjs";
|
|
|
|
test("bootConfig builds the broker's boot message for one business, with no trackers key when no project names one", (t) => {
|
|
const f = fixture(tmp(t));
|
|
const system = loadSystem({ env: f.env });
|
|
const warnings = [];
|
|
const boot = bootConfig({ system, businessId: "acme", env: f.env, warn: (w) => warnings.push(w) });
|
|
assert.equal(boot.dataRoot, f.dataRoot);
|
|
assert.deepEqual(Object.keys(boot.businesses), ["acme"]);
|
|
assert.deepEqual(boot.launches, []);
|
|
assert.deepEqual(boot.readers, ["acme"]);
|
|
assert.equal("trackers" in boot, false);
|
|
assert.match(warnings[0], /no project file sets tracker\.project/);
|
|
});
|
|
|
|
test("trackers come from the tracker.* variables of the one project that names a tracker project", (t) => {
|
|
const root = tmp(t);
|
|
const f = fixture(root);
|
|
writeJson(join(root, "project", ".mosaic", "project.json"), { projectVersion: 1, id: "stack", vars: { "tracker.project": 12 } });
|
|
const boot = bootConfig({ system: loadSystem({ env: f.env }), businessId: "acme", env: f.env });
|
|
assert.deepEqual(boot.trackers, { acme: { baseUrl: "http://127.0.0.1:3456", project: 12, pollSeconds: 60, reconcileMinutes: 60, descendants: false } });
|
|
// Decision 87: the project's tracker.descendants reaches the adapter's boot entry.
|
|
writeJson(join(root, "project", ".mosaic", "project.json"), { projectVersion: 1, id: "stack", vars: { "tracker.project": 12, "tracker.descendants": true } });
|
|
const tree = bootConfig({ system: loadSystem({ env: f.env }), businessId: "acme", env: f.env });
|
|
assert.equal(tree.trackers.acme.descendants, true);
|
|
});
|
|
|
|
test("with two projects, the one that sets tracker.project supplies the tracker and the other is no conflict", (t) => {
|
|
const root = tmp(t);
|
|
const f = fixture(root, "acme", (doc) => {
|
|
doc.projects.web = { root: join(root, "web") };
|
|
return doc;
|
|
});
|
|
writeJson(join(root, "project", ".mosaic", "project.json"), { projectVersion: 1, id: "stack", vars: { "tracker.project": 12 } });
|
|
writeJson(join(root, "web", ".mosaic", "project.json"), { projectVersion: 1, id: "web", vars: {} });
|
|
const boot = bootConfig({ system: loadSystem({ env: f.env }), businessId: "acme", env: f.env });
|
|
assert.deepEqual(boot.trackers, { acme: { baseUrl: "http://127.0.0.1:3456", project: 12, pollSeconds: 60, reconcileMinutes: 60, descendants: false } });
|
|
});
|
|
|
|
test("two projects that each name a tracker project refuse, since the boot shape holds one", (t) => {
|
|
const root = tmp(t);
|
|
const f = fixture(root, "acme", (doc) => {
|
|
doc.projects.web = { root: join(root, "web") };
|
|
return doc;
|
|
});
|
|
writeJson(join(root, "project", ".mosaic", "project.json"), { projectVersion: 1, id: "stack", vars: { "tracker.project": 12 } });
|
|
writeJson(join(root, "web", ".mosaic", "project.json"), { projectVersion: 1, id: "web", vars: { "tracker.project": 13 } });
|
|
assert.throws(() => bootConfig({ system: loadSystem({ env: f.env }), businessId: "acme", env: f.env }), (e) => e.exitCode === 3 && /stack, web each set tracker\.project/.test(e.message));
|
|
});
|
|
|
|
test("a business without tracker.baseUrl gets no trackers entry", (t) => {
|
|
const f = fixture(tmp(t), "acme", (doc) => {
|
|
delete doc.vars["tracker.baseUrl"];
|
|
return doc;
|
|
});
|
|
const warnings = [];
|
|
const boot = bootConfig({ system: loadSystem({ env: f.env }), businessId: "acme", env: f.env, warn: (w) => warnings.push(w) });
|
|
assert.equal("trackers" in boot, false);
|
|
assert.deepEqual(warnings, []);
|
|
});
|
|
|
|
test("an unknown business and a broken system config refuse with exit 3", (t) => {
|
|
const f = fixture(tmp(t));
|
|
assert.throws(() => bootConfig({ system: loadSystem({ env: f.env }), businessId: "nope", env: f.env }), (e) => e.exitCode === 3);
|
|
assert.throws(() => loadSystem({ env: { ...f.env, MOSAIC_CONFIG: join(f.dataRoot, "missing.json") } }), (e) => e.exitCode === 3);
|
|
});
|
|
|
|
test("Row 252 (Webby N1): every console.sso subject reaches the boot as a console owner, and no sso means no consoleOwners key", (t) => {
|
|
const f = fixture(tmp(t));
|
|
const system = loadSystem({ env: f.env });
|
|
assert.equal("consoleOwners" in bootConfig({ system, businessId: "acme", env: f.env }), false);
|
|
const sso = { providers: [
|
|
{ id: "authentik", issuer: "https://auth.example/app/", subjects: [{ sub: "jason-sub" }, { sub: "second-sub" }] },
|
|
{ id: "other", issuer: "https://idp.example", subjects: [{ sub: "jason-sub" }] },
|
|
] };
|
|
const boot = bootConfig({ system: { ...system, console: { sso } }, businessId: "acme", env: f.env });
|
|
assert.deepEqual(boot.consoleOwners, [
|
|
{ provider: "authentik", iss: "https://auth.example/app/", sub: "jason-sub" },
|
|
{ provider: "authentik", iss: "https://auth.example/app/", sub: "second-sub" },
|
|
{ provider: "other", iss: "https://idp.example", sub: "jason-sub" },
|
|
]);
|
|
});
|