86 lines
4.1 KiB
JavaScript
86 lines
4.1 KiB
JavaScript
// Row 164: the chat switch and fixed recovery message stay local to chat HTTP.
|
|
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { request } from "node:http";
|
|
import { createChat } from "../src/chat.mjs";
|
|
import { startServer } from "../src/serve.mjs";
|
|
import { BusReadError } from "../src/bus.mjs";
|
|
import { close } from "./fixture.mjs";
|
|
import { createSsoSession } from "./helpers/sso-session.mjs";
|
|
const P = "10000000-0000-4000-8000-000000000001";
|
|
const T = "20000000-0000-4000-8000-000000000002";
|
|
function http(port, headers, path, body) {
|
|
const raw = body === undefined ? undefined : JSON.stringify(body);
|
|
return new Promise((resolve, reject) => {
|
|
const req = request({ host: '127.0.0.1', port, path, method: raw === undefined ? 'GET' : 'POST',
|
|
headers: { ...headers, ...(raw === undefined ? {} : { 'content-length': Buffer.byteLength(raw) }) },
|
|
}, res => {
|
|
let text = '';
|
|
res.on('data', chunk => text += chunk);
|
|
res.on('end', () => resolve({ status: res.statusCode, data: JSON.parse(text) }));
|
|
});
|
|
req.on('error', reject); req.end(raw);
|
|
});
|
|
}
|
|
|
|
async function setup(t, selected) {
|
|
const calls = [], logs = [];
|
|
const auth = await createSsoSession(t, { host: 'chat.example.com' });
|
|
const chat = createChat({ call: async (verb, args) => {
|
|
calls.push([verb, args]);
|
|
if (verb === "plugins") return { business: "demo", revision: "a".repeat(64), plugins: selected === undefined ? {} : { channel: selected } };
|
|
return [];
|
|
} });
|
|
const server = await startServer({ port: 0, chat, sso: auth.sso, send: async () => ({}), log: line => logs.push(line) });
|
|
t.after(() => close(server));
|
|
const session = await auth.signIn(server.address().port);
|
|
const headers = auth.writeHeaders(session);
|
|
return { calls, logs, async request(path, body) {
|
|
return http(server.address().port, headers, path, body);
|
|
} };
|
|
}
|
|
|
|
test("WebUI channel off returns 503 and named recovery for every chat route without a chat operation", async t => {
|
|
for (const selected of [[], undefined, ["cli"]]) {
|
|
const s = await setup(t, selected);
|
|
for (const [path, body] of [
|
|
["/api/chat/projects"], ["/api/chat/threads"], [`/api/chat/threads/${T}/messages`],
|
|
["/api/chat/projects", { name: "work" }], ["/api/chat/threads", { project: P, name: "chat" }],
|
|
[`/api/chat/threads/${T}/messages`, { body: "private body" }], [`/api/chat/threads/${T}/open`, {}],
|
|
]) {
|
|
s.calls.length = 0;
|
|
const res = await s.request(path, body);
|
|
assert.equal(res.status, 503);
|
|
assert.equal(res.data.error, "channel-disabled");
|
|
assert.match(res.data.message, /mosaic plugins.*mosaic plugins set/s);
|
|
assert.match(res.data.message, /plugins\.channel/);
|
|
assert.match(res.data.message, /cli.*webui/);
|
|
if (body !== undefined) assert.equal(res.data.sent, false);
|
|
assert.deepEqual(s.calls, [["plugins", {}]]);
|
|
assert.doesNotMatch(JSON.stringify(res.data), /private body/);
|
|
}
|
|
}
|
|
});
|
|
|
|
test("WebUI only selection keeps web chat enabled while retaining the old read envelope", async t => {
|
|
const s = await setup(t, ["webui"]);
|
|
const res = await s.request("/api/chat/projects");
|
|
assert.equal(res.status, 200);
|
|
assert.deepEqual(res.data.rows, []);
|
|
assert.deepEqual(s.calls, [["plugins", {}], ["project.list", {}]]);
|
|
});
|
|
|
|
test("WebUI channel-disabled recovery text is fixed and never echoes transport text", async t => {
|
|
const chat = createChat({ call: async () => { throw new BusReadError("channel-disabled", "SENTINEL-private-input"); } });
|
|
const auth = await createSsoSession(t, { host: 'chat.example.com' });
|
|
const server = await startServer({ port: 0, chat, sso: auth.sso, send: async () => ({}), log: () => {} });
|
|
t.after(() => close(server));
|
|
const session = await auth.signIn(server.address().port);
|
|
const res = await http(server.address().port, auth.writeHeaders(session), '/api/chat/projects');
|
|
const result = res.data;
|
|
assert.equal(res.status, 503);
|
|
assert.equal(result.error, "channel-disabled");
|
|
assert.match(result.message, /mosaic plugins.*mosaic plugins set/s);
|
|
assert.doesNotMatch(JSON.stringify(result), /SENTINEL/);
|
|
});
|