PRD 1.20 REQ-TASK-1, REQ-PLUG-1, REQ-WS-8. Each organization keeps its tasks in its own bus through the new task-tracker plug-in kind. The default module is the built-in store; Vikunja is the optional module and a Vikunja organization keeps today's path. - bus schema 3u to 3v (migration 3u-3v.sql): store_tasks, append-only, one row per write, each citing its events row; seven guard triggers. - broker: the eight role verbs on the store with the slice 1 field table checked twice (module, then a before/after field diff); the person's tasks.create/edit/archive/unarchive/delete as human.input events; the tasks read returns store rows in board order (group, position), filtered and paged like tracker rows. - runtime: each business gets its module at start (organization file plugin, else vikunja with a tracker entry, else store); a store business with a Vikunja tracker refuses the boot (tracker-config). - plugins: task-tracker kind with store and vikunja modules. - cli: the launcher's tracker flag covers store organizations; trail prints store writes. - pins: schema.test counts, the bus migration tests, stack-release #811 and T46 (store_tasks on the always list; COUNTED unchanged), budget-supervisor accepts 3v. Rebased onto SYS1 by the lead: SYS1 took 3t-3u, so this step is 3u-3v.
72 lines
5.0 KiB
JavaScript
72 lines
5.0 KiB
JavaScript
// REQ-PLUG-1: the kind registry and the organization file's `plugins` shape (row 131).
|
|
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { KINDS, NAME, PluginError, checkPlugins } from "../src/kinds.mjs";
|
|
|
|
const code = expected => e => e instanceof PluginError && e.code === expected;
|
|
|
|
test("REQ-PLUG-1 REQ-LIMIT-4 the registry: usage-source at interface version 1, limit-check at 2 (row 125), each a list", () => {
|
|
assert.deepEqual(Object.keys(KINDS), ["usage-source", "limit-check", "search-source", "channel", "harness", "usage", "model-provider", "system-type", "theme", "task-tracker"]);
|
|
assert.deepEqual({ ...KINDS["task-tracker"], exports: [...KINDS["task-tracker"].exports] }, { kind: "task-tracker", interfaceVersion: 1, exports: ["adapter"], switch: "one" });
|
|
assert.ok(Object.isFrozen(KINDS["task-tracker"]) && Object.isFrozen(KINDS["task-tracker"].exports));
|
|
// Stream S4 (PLAN C5): a theme is data. interfaceVersion 1, one export, a list switch.
|
|
assert.deepEqual({ ...KINDS.theme, exports: [...KINDS.theme.exports] }, { kind: "theme", interfaceVersion: 1, exports: ["theme"], switch: "list" });
|
|
assert.ok(Object.isFrozen(KINDS.theme) && Object.isFrozen(KINDS.theme.exports));
|
|
assert.deepEqual({ ...KINDS["usage-source"], exports: [...KINDS["usage-source"].exports] },
|
|
{ kind: "usage-source", interfaceVersion: 1, exports: ["readTurn"], switch: "list" });
|
|
assert.deepEqual({ ...KINDS["limit-check"], exports: [...KINDS["limit-check"].exports] },
|
|
{ kind: "limit-check", interfaceVersion: 2, exports: ["mayLaunch", "mustStop", "report"], switch: "list" });
|
|
assert.deepEqual({ ...KINDS["search-source"], exports: [...KINDS["search-source"].exports] },
|
|
{ kind: "search-source", interfaceVersion: 1, exports: ["search"], switch: "one" });
|
|
// Row 65 (REQ-PLUG-2, Q156): the harness kind, a list, at interface version 1.
|
|
assert.deepEqual({ ...KINDS.harness, exports: [...KINDS.harness.exports] }, {
|
|
kind: "harness", interfaceVersion: 1, switch: "list",
|
|
exports: ["adapter", "levels", "preflight", "version", "countsAccount", "mounts", "builtinWeb", "builtins",
|
|
"typedName", "reliesOn", "gateTimeout", "bundle", "env", "turnEnd", "outputPolicy", "transcript", "compactions"],
|
|
});
|
|
assert.deepEqual({ ...KINDS["model-provider"], exports: [...KINDS["model-provider"].exports] }, { kind: "model-provider", interfaceVersion: 1, exports: ["catalogue", "preflight", "route"], switch: "list" });
|
|
assert.ok(Object.isFrozen(KINDS.harness) && Object.isFrozen(KINDS.harness.exports));
|
|
assert.ok(Object.isFrozen(KINDS) && Object.isFrozen(KINDS["usage-source"]) && Object.isFrozen(KINDS["limit-check"].exports));
|
|
assert.equal(String(NAME), "/^[a-z][a-z0-9-]*$/");
|
|
});
|
|
|
|
test("REQ-PLUG-1 checkPlugins accepts one key per kind, each a list of names, empty included", () => {
|
|
for (const value of [{}, { "usage-source": [] }, { "usage-source": ["a", "b-2"], "limit-check": ["cap"] }]) {
|
|
assert.equal(checkPlugins(value, "plugins"), value);
|
|
}
|
|
});
|
|
|
|
test("REQ-PLUG-1 checkPlugins refuses bad shapes, unknown kinds, bad names and duplicates", () => {
|
|
for (const value of [null, [], "x", 3]) assert.throws(() => checkPlugins(value, "plugins"), code("bad-setting"));
|
|
assert.throws(() => checkPlugins({ "made-up": [] }, "plugins"), code("unknown-kind"));
|
|
assert.throws(() => checkPlugins({ toString: [] }, "plugins"), code("unknown-kind"));
|
|
assert.throws(() => checkPlugins({ "usage-source": "meter" }, "plugins"), code("bad-setting"));
|
|
for (const name of ["Meter", "1m", "m_x", "../x", "a/b", ".x", "", 7, null]) {
|
|
assert.throws(() => checkPlugins({ "usage-source": [name] }, "plugins"), code("bad-name"), String(name));
|
|
}
|
|
assert.throws(() => checkPlugins({ "limit-check": ["cap", "cap"] }, "plugins"), code("bad-setting"));
|
|
});
|
|
|
|
test("REQ-PLUG-1 a kind that switches on one module takes one name, not a list", () => {
|
|
const kinds = { single: { kind: "single", interfaceVersion: 1, exports: ["run"], switch: "one" } };
|
|
assert.ok(checkPlugins({ single: "only" }, "plugins", { kinds }));
|
|
assert.throws(() => checkPlugins({ single: ["only"] }, "plugins", { kinds }), code("bad-setting"));
|
|
assert.throws(() => checkPlugins({ single: "Only" }, "plugins", { kinds }), code("bad-name"));
|
|
});
|
|
|
|
test("REQ-PLUG-1 checkPlugins reports through the caller's fail callback", () => {
|
|
const seen = [];
|
|
const fail = (c, message) => { seen.push(c); throw new Error(message); };
|
|
assert.throws(() => checkPlugins({ "made-up": [] }, "x plugins", { fail }), /x plugins names unknown kind/);
|
|
assert.deepEqual(seen, ["unknown-kind"]);
|
|
});
|
|
|
|
|
|
test("P5 usage telemetry is optional, single module, separate from enforcing sources", () => {
|
|
assert.deepEqual({ ...KINDS.usage, exports: [...KINDS.usage.exports] },
|
|
{ kind: "usage", interfaceVersion: 1, exports: ["read"], switch: "one" });
|
|
assert.deepEqual(checkPlugins({}, "plugins"), {});
|
|
assert.deepEqual(checkPlugins({ usage: "cliproxy-queue" }, "plugins"), { usage: "cliproxy-queue" });
|
|
assert.throws(() => checkPlugins({ usage: ["cliproxy-queue"] }, "plugins"), code("bad-setting"));
|
|
});
|