// 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"); });