Files
stack/packages/cli/tests/optional-tracker.test.mjs
T

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 "../../business/src/index.mjs";
import { businessDoc, launchDoc, REPO_ROLES, systemConfig, tmp, writeJson } from "../../business/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");
});