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.
73 lines
3.8 KiB
JavaScript
73 lines
3.8 KiB
JavaScript
// Q247 / row 324 (#1820): no tracker adapter, credential registration or Console sync row.
|
|
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { mkdirSync, readFileSync, rmSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { bootConfig } from "../src/config.mjs";
|
|
import { startBroker } from "../../bus/src/runtime.mjs";
|
|
import { Client } from "../../bus/src/client.mjs";
|
|
import { loadBusiness } from "../../organization/src/index.mjs";
|
|
import { businessDoc, launchDoc, REPO_ROLES, systemConfig, tmp, writeJson } from "../../organization/tests/helpers.mjs";
|
|
import { businessView } from "../../webui/src/reads.mjs";
|
|
|
|
function fixture(t, { present = false } = {}) {
|
|
const root = tmp();
|
|
t.after(() => rmSync(root, { recursive: true, force: true }));
|
|
const doc = businessDoc(root);
|
|
// Credential-free roles, so an empty runtime credential list proves no phantom @sync entry.
|
|
doc.roles = {
|
|
chief: { definition: "chief-of-staff", vars: { harness: "claude-code", model: "claude-opus-5-5", thinking: "high" } },
|
|
assistant: { definition: "assistant", vars: { harness: "claude-code", model: "claude-opus-5-5", thinking: "high" } },
|
|
};
|
|
doc.arbiters = { delivery: "chief", technical: "chief" };
|
|
doc.launch = launchDoc("chief", ["assistant"]);
|
|
doc.plugins = { harness: ["claude-code"] };
|
|
// Deliberately retain fixture tracker.baseUrl; missing business tracker must still mean no sync.
|
|
if (!present) delete doc.tracker;
|
|
// No credential files remain, so boot cannot accidentally rely on the old fixture's sync token.
|
|
rmSync(join(root, "secrets"), { recursive: true });
|
|
writeJson(join(root, "project/.mosaic/project.json"), { projectVersion: 1, id: "stack", vars: { "tracker.project": 3 } });
|
|
const config = systemConfig(root);
|
|
const system = JSON.parse(readFileSync(config, "utf8"));
|
|
mkdirSync(system.dataRoot, { mode: 0o700 });
|
|
writeJson(join(root, "config/businesses/acme.json"), doc);
|
|
const env = { HOME: root, MOSAIC_CONFIG: config, MOSAIC_ROLES_DIR: REPO_ROLES };
|
|
return { root, doc, system, env, b: loadBusiness("acme", { dir: join(root, "config"), rolesDir: REPO_ROLES }) };
|
|
}
|
|
|
|
test("Q247: bootConfig omits tracker setup even with tracker variables left in the business", (t) => {
|
|
const x = fixture(t);
|
|
assert.ok(x.doc.vars["tracker.baseUrl"]);
|
|
const boot = bootConfig({ system: x.system, businessId: "acme", env: x.env });
|
|
assert.equal(boot.businesses.acme.tracker, null);
|
|
assert.equal(Object.hasOwn(boot, "trackers"), false);
|
|
});
|
|
|
|
test("Q247: trackerless business boots and registers no @sync credential", async (t) => {
|
|
const x = fixture(t);
|
|
const boot = bootConfig({ system: x.system, businessId: "acme", env: x.env });
|
|
const runtime = await startBroker(boot);
|
|
t.after(() => runtime.close());
|
|
assert.deepEqual(runtime.credentials.status(), []);
|
|
await assert.rejects(runtime.credentials.useFile("acme/@sync", "vikunja", () => assert.fail("no sync token may be used")),
|
|
(e) => e.code === "credential-unknown");
|
|
const reader = new Client({ path: runtime.path, cap: runtime.readers[0].cap });
|
|
assert.deepEqual(await reader.call("credentials"), []);
|
|
});
|
|
|
|
test("Q247: Console business read works with tracker absent and shows no sync credential", (t) => {
|
|
const x = fixture(t);
|
|
const view = businessView(x.b, { system: x.system });
|
|
assert.equal(view.id, "acme");
|
|
assert.deepEqual(view.credentials, []);
|
|
assert.deepEqual(view.instances.map((entry) => entry.instance), ["chief", "assistant"]);
|
|
});
|
|
|
|
|
|
test("Q247: a present tracker with a missing token still refuses bus boot", async (t) => {
|
|
const x = fixture(t, { present: true });
|
|
const boot = bootConfig({ system: x.system, businessId: "acme", env: x.env });
|
|
assert.equal(boot.businesses.acme.tracker.sync.bot, x.doc.tracker.sync.bot);
|
|
await assert.rejects(startBroker(boot), (e) => e.code === "credential-file");
|
|
});
|