Files
stack/scripts/case2/console.mjs
T

44 lines
2.8 KiB
JavaScript

// C2-14 S10: actual Console HTTP chat endpoints and browser DOM over the same acceptance bus.
// The unrelated Board is a local 503 fixture. No chat response, citation check or notice is stubbed.
import { createServer } from 'node:http';
import { startServer } from '../../packages/webui/src/serve.mjs';
import { consoleBus } from '../../packages/webui/src/console-bus.mjs';
import { browser } from '../../packages/webui/tests/browser.mjs';
const shutdown = server => new Promise((resolve, reject) => {
server.close(error => error ? reject(error) : resolve());
server.closeAllConnections?.();
});
const expect = (value, label) => { if (!value) throw new Error(label); };
export async function consoleAcceptance({ humanCall, thread, reply, writeNotice, writeCommit, cleanups }) {
const board = createServer((req, res) => { res.writeHead(503); res.end('Acceptance Board is not configured.'); });
await new Promise(resolve => board.listen(0, '127.0.0.1', resolve));
cleanups.push(() => shutdown(board));
const options = consoleBus(humanCall);
const server = await startServer({ port: 0, board: `http://127.0.0.1:${board.address().port}`, ...options, log: () => {} });
cleanups.push(() => shutdown(server));
const origin = `http://127.0.0.1:${server.address().port}`;
const response = await fetch(`${origin}/api/chat/threads/${thread}/messages`);
expect(response.ok, 'Console message endpoint refused');
const body = await response.json();
expect(Array.isArray(body.rows), 'Console message shape');
const checked = body.rows.find(m => m.id === reply.id), notice = body.rows.find(m => m.id === writeNotice.id);
expect(checked?.body === reply.body && checked.check?.citations?.[0]?.scope === 'canon', 'Console dropped stored reply/check');
expect(notice?.body === writeNotice.body, 'Console dropped broker notice');
const view = await browser({ machine: false });
cleanups.push(() => view.close());
await view.navigate(`${origin}/#/chat/threads/${thread}`);
const rendered = await view.evaluate(`(async () => {
for (let i = 0; i < 150; i++) {
const bodies = [...document.querySelectorAll('.chat-log .chat-body')].map(e => e.textContent);
const notices = [...document.querySelectorAll('.chat-notice > span:first-child')].map(e => e.textContent);
const canon = [...document.querySelectorAll('.chat-cites')].some(e => e.textContent.includes('canon'));
if (bodies.some(t => t === 'Grey eyes.') && canon && notices.some(t => t === ${JSON.stringify(`bean wrote ${JSON.parse(writeNotice.body).path} @${writeCommit.slice(0, 7)}`)}))
return { reply: true, canon, notice: true };
await new Promise(resolve => setTimeout(resolve, 40));
}
return { reply: false, canon: false, notice: false };
})()`);
expect(rendered.reply && rendered.canon && rendered.notice, 'Console did not render actual checked reply and write notice');
}