// Row 164: channel selection stays independent of human proof and administration. import test from "node:test"; import assert from "node:assert/strict"; import { mkdirSync, renameSync, symlinkSync, unlinkSync } from "node:fs"; import { join } from "node:path"; import { createLoader } from "../src/loader.mjs"; import { KINDS } from "../src/kinds.mjs"; import { tree } from "./helpers.mjs"; const code = expected => e => e.code === expected; const fixtureModule = tag => `export const interfaceVersion = 1; export async function dispatch(request, {call}) { return call(request.verb, {...request.args, fixture:${JSON.stringify(tag)}}); } `; const settings = channel => ({ business: "demo", revision: "a".repeat(64), plugins: channel === undefined ? {} : { channel } }); const helper = () => import("../src/channel.mjs"); test("channel registry requires dispatch at interface 1 and switches a list", () => { assert.deepEqual(KINDS.channel, { kind: "channel", interfaceVersion: 1, exports: ["dispatch"], switch: "list" }); assert.ok(Object.isFrozen(KINDS.channel)); assert.ok(Object.isFrozen(KINDS.channel.exports)); }); test("channel shipped modules load and preserve every supported operation and bus refusal", async () => { const loader = createLoader({ log: () => {} }); assert.deepEqual((await loader.load({ channel: ["cli", "webui"] })).refused, []); assert.deepEqual(loader.loaded().channel, ["cli", "webui"]); const modules = loader.modules("channel"); for (const [index, verbs] of [ [0, ["project.list", "project.create", "thread.list", "thread.create", "thread.messages", "thread.send", "thread.open", "session.list", "session.create", "session.clear", "session.cancel", "session.operation", "message.receive", "message.read"]], [1, ["project.list", "project.create", "thread.list", "thread.create", "thread.messages", "thread.send", "thread.open", "session.list", "session.create", "session.clear", "session.cancel", "session.operation"]], ]) { for (const verb of verbs) { const seen = []; const result = await modules[index].dispatch({ verb, args: { thread: "thread-one", body: "literal", class: "REQUEST" } }, { call: async (v, args) => { seen.push([v, args]); return { receipt: "preserved" }; }, }); assert.deepEqual(result, { receipt: "preserved" }); assert.deepEqual(seen, [[verb, { thread: "thread-one", body: "literal", class: "REQUEST" }]]); } const refusal = Object.assign(new Error("thread-not-owned"), { code: "thread-not-owned" }); await assert.rejects(modules[index].dispatch({ verb: "thread.list", args: {} }, { call: async () => { throw refusal; } }), e => e === refusal); let calls = 0; await assert.rejects(modules[index].dispatch({ verb: "plugins.set", args: {} }, { call: async () => calls++ }), code("channel-operation")); assert.equal(calls, 0); } await assert.rejects(modules[1].dispatch({ verb: "message.receive", args: {} }, { call: async () => assert.fail("not a web chat operation") }), code("channel-operation")); }); test("channel snapshot is a frozen matched pair and a captured array survives reload", async t => { const s = tree(t); s.write("channel", "one", fixtureModule("one")); s.write("channel", "two", fixtureModule("two")); const loader = await s.loader(); await loader.load({ channel: ["two", "one"] }); assert.equal(typeof loader.snapshot, "function"); const captured = loader.snapshot("channel"); assert.deepEqual(captured.map(x => x.name), ["two", "one"]); assert.deepEqual(captured.map(x => x.name), loader.loaded().channel); assert.deepEqual(captured.map(x => x.module), loader.modules("channel")); assert.ok(Object.isFrozen(captured)); assert.ok(captured.every(Object.isFrozen)); assert.throws(() => captured.push(captured[0]), TypeError); await loader.load({ channel: ["one"] }); assert.deepEqual(loader.snapshot("channel").map(x => x.name), ["one"]); assert.deepEqual(captured.map(x => x.name), ["two", "one"]); assert.deepEqual(await captured[0].module.dispatch({ verb: "thread.list", args: {} }, { call: async (verb, args) => args }), { fixture: "two" }); assert.throws(() => loader.snapshot("no-such-kind"), code("unknown-kind")); }); test("channel guards refuse bad names, missing paths, exports and versions while retaining last good", async t => { const s = tree(t); s.write("channel", "good", fixtureModule("good")); mkdirSync(join(s.pkg, "channel", "empty")); mkdirSync(join(s.pkg, "channel", "dir-index", "index.mjs"), { recursive: true }); s.write("channel", "missing-export", "export const interfaceVersion = 1;\n"); s.write("channel", "missing-version", "export function dispatch() {}\n"); s.write("channel", "bad-type", "export const interfaceVersion = 1; export const dispatch = 1;\n"); s.write("channel", "wrong-version", "export const interfaceVersion = 2; export function dispatch() {}\n"); const loader = await s.loader(); assert.deepEqual((await loader.load({ channel: ["good"] })).refused, []); for (const [names, expected] of [ [["../good"], "bad-name"], [["absent"], "missing"], [["empty"], "missing"], [["dir-index"], "missing"], [["missing-export"], "missing-export"], [["missing-version"], "missing-export"], [["bad-type"], "missing-export"], [["wrong-version"], "interface-version"], ]) { const result = await loader.load({ channel: names }); assert.equal(result.refused.length, 1); assert.equal(result.refused[0].code, expected); assert.deepEqual(result.loaded.channel, ["good"]); assert.deepEqual(result.refused[0].kept, ["good"]); } }); test("channel guards refuse symlinked module, index and kind directories", async t => { const s = tree(t); const good = s.write("channel", "good", fixtureModule("good")); const loader = await s.loader(); await loader.load({ channel: ["good"] }); symlinkSync(good, join(s.pkg, "channel", "linked")); const index = s.write("channel", "linked-index", fixtureModule("unused")); unlinkSync(join(index, "index.mjs")); symlinkSync(join(good, "index.mjs"), join(index, "index.mjs")); for (const name of ["linked", "linked-index"]) { const result = await loader.load({ channel: [name] }); assert.equal(result.refused[0].code, "symlink"); const refusedPath = `/channel/${name}${name === "linked-index" ? "/index.mjs" : ""} is a symlink`; assert.ok(result.refused[0].message.endsWith(refusedPath)); assert.deepEqual(result.loaded.channel, ["good"]); } renameSync(join(s.pkg, "channel"), join(s.pkg, "channel-real")); symlinkSync(join(s.pkg, "channel-real"), join(s.pkg, "channel")); assert.equal((await loader.load({ channel: ["good"] })).refused[0].code, "symlink"); }); test("channel caller re-reads settings and keeps off switches independent by module name", async () => { const { createChannelCaller } = await helper(); let selected = ["webui"]; const calls = [], logged = []; const call = async (verb, args) => { calls.push([verb, args]); return verb === "plugins" ? settings(selected) : { ok: true }; }; const cli = createChannelCaller({ name: "cli", call, log: entry => logged.push(entry) }); const webui = createChannelCaller({ name: "webui", call, log: entry => logged.push(entry) }); await assert.rejects(cli("thread.list", {}), e => e.code === "channel-disabled" && /mosaic plugins.*mosaic plugins set/s.test(e.message)); assert.deepEqual(calls.splice(0), [["plugins", {}]]); assert.deepEqual(await webui("thread.list", {}), { ok: true }); assert.deepEqual(calls.splice(0), [["plugins", {}], ["thread.list", {}]]); selected = ["cli"]; assert.deepEqual(await cli("project.list", {}), { ok: true }); calls.length = 0; await assert.rejects(webui("thread.send", { body: "private" }), code("channel-disabled")); assert.deepEqual(calls, [["plugins", {}]]); for (selected of [[], undefined]) { calls.length = 0; await assert.rejects(cli("message.receive", {}), code("channel-disabled")); assert.deepEqual(calls, [["plugins", {}]]); } assert.deepEqual(logged, []); }); test("channel caller malformed settings refuse without a chat operation and no unrelated kind loads", async () => { const { createChannelCaller } = await helper(); let answer = null; let ops = 0; const logged = []; const call = async verb => verb === "plugins" ? answer : ++ops; const caller = createChannelCaller({ name: "cli", call, log: e => logged.push(e) }); for (answer of [null, [], {}, { plugins: null }, { plugins: [] }]) { await assert.rejects(caller("project.list", {}), code("channel-config")); assert.equal(ops, 0); } answer = { ...settings(["cli"]), plugins: { channel: ["cli"], "usage-source": ["missing-unrelated"] } }; assert.equal(await caller("project.list", {}), 1); assert.deepEqual(logged, []); }); test("channel caller uses last good after loader refusal and fails closed on first load", async () => { const { createChannelCaller } = await helper(); let selected = ["cli"]; const logged = []; const call = async verb => verb === "plugins" ? settings(selected) : "kept"; const caller = createChannelCaller({ name: "cli", call, log: e => logged.push(e) }); assert.equal(await caller("project.list", {}), "kept"); selected = ["missing-module"]; assert.equal(await caller("project.list", {}), "kept"); assert.equal(logged[0].code, "missing"); assert.deepEqual(logged[0].kept, ["cli"]); const fresh = createChannelCaller({ name: "cli", call, log: () => {} }); await assert.rejects(fresh("project.list", {}), code("channel-disabled")); }); test("channel dispatch holds no selection lock while a slow send is awaiting", async () => { const { createChannelCaller } = await helper(); let release, started; const entered = new Promise(r => started = r); const held = new Promise(r => release = r); const caller = createChannelCaller({ name: "cli", log: () => {}, call: async verb => { if (verb === "plugins") return settings(["cli"]); if (verb === "thread.send") { started(); await held; return "sent"; } return "listed"; } }); const send = caller("thread.send", {}); await entered; let timer; try { assert.equal(await Promise.race([caller("project.list", {}), new Promise((_, reject) => timer = setTimeout(() => reject(new Error("dispatch blocked list")), 1000))]), "listed"); } finally { clearTimeout(timer); release(); } assert.equal(await send, "sent"); }); test("channel last realpath check refuses an index swapped after component checks", async t => { const s = tree(t); const good = s.write("channel", "good", fixtureModule("good")); const swapped = s.write("channel", "swapped", fixtureModule("old")); let swap = false; const loader = await s.loader({ barrier: (point, { name }) => { if (swap && point === "checked" && name === "swapped") { unlinkSync(join(swapped, "index.mjs")); symlinkSync(join(good, "index.mjs"), join(swapped, "index.mjs")); } } }); await loader.load({ channel: ["good"] }); swap = true; const result = await loader.load({ channel: ["swapped"] }); assert.equal(result.refused[0].code, "symlink"); assert.deepEqual(result.loaded.channel, ["good"]); }); test("channel caller refuses invalid core construction", async () => { const { createChannelCaller } = await helper(); for (const name of ["../cli", "Cli", null]) assert.throws(() => createChannelCaller({ name, call: async () => ({}) }), code("bad-name")); assert.throws(() => createChannelCaller({ name: "cli", call: null }), TypeError); }); test("channel caller invokes the selected loaded module instead of bypassing dispatch", async t => { const s = tree(t); s.write("channel", "cli", fixtureModule("cli-fixture")); s.write("channel", "webui", fixtureModule("webui-fixture")); const { createChannelCaller } = await import(new URL("./channel.mjs", s.loaderUrl)); const caller = createChannelCaller({ name: "webui", log: () => {}, call: async (verb, args) => verb === "plugins" ? settings(["cli", "webui"]) : args }); assert.deepEqual(await caller("thread.list", { project: "one" }), { project: "one", fixture: "webui-fixture" }); }); test("channel caller keeps the selected module operation filter in front of the core transport", async () => { const { createChannelCaller } = await helper(); let writes = 0; const caller = createChannelCaller({ name: "cli", log: () => {}, call: async verb => verb === "plugins" ? settings(["cli"]) : ++writes }); await assert.rejects(caller("plugins.set", {}), code("channel-operation")); assert.equal(writes, 0); });