// Row 225 (C2-10b, design 7.4): the chat's library status read, a write notice's Revert and the // shared reply renderer served to the page. The revert is C2-02's `library.revert`; until that verb // exists the bus refuses it and the route reports the code. import test from 'node:test'; import assert from 'node:assert/strict'; import { request } from 'node:http'; import { readFileSync } from 'node:fs'; import { startServer, userRoute } from '../src/serve.mjs'; import { BusReadError } from '../src/bus.mjs'; import { consoleAccess } from '../src/access.mjs'; import { createSsoSession } from './helpers/sso-session.mjs'; const preview = { host: 'console.example', header: 'x-user', user: 'jason' }; const C1 = 'a'.repeat(40), C2 = 'b'.repeat(40); const payload = { project: 'books', commit: C1 }; const STATUS = { project: 'books', version: 1, branch: 'main', served: C2, lastFetch: '2026-10-23T18:00:00.000Z', lastError: null, diverged: null, problems: [] }; async function setup(t, { enabled = true, revert, status, lines = [] } = {}) { const calls = { revert: [], status: [] }; const auth = await createSsoSession(t, { host: 'console.example' }); const server = await startServer({ port: 0, sso: auth.sso, log: l => lines.push(l), send: enabled ? async () => ({}) : null, libraryStatus: status === null ? null : async args => { calls.status.push(args); return status ? status(args) : STATUS; }, libraryRevert: enabled ? async args => { calls.revert.push(args); return revert ? revert(args) : { project: args.project, path: 'Notes/Eyes.md', reverted: args.commit, commit: C2 }; } : null, }); t.after(() => new Promise(r => server.close(r))); const session = await auth.signIn(server.address().port); return { calls, lines, port: server.address().port, headers: auth.writeHeaders(session) }; } function call(s, { method = 'POST', path = '/api/library/revert', hs = s.headers, data = payload } = {}) { return new Promise((resolve, reject) => { const req = request({ hostname: '127.0.0.1', port: s.port, path, method, headers: hs }, r => { let text = ''; r.on('data', c => text += c); r.on('end', () => resolve({ status: r.statusCode, type: r.headers['content-type'], text, body: text.startsWith('{') ? JSON.parse(text) : null })); }); req.on('error', reject); req.end(data === null ? undefined : typeof data === 'string' ? data : JSON.stringify(data)); }); } const get = (s, path, hs = s.headers) => call(s, { method: 'GET', path, hs, data: null }); const refusal = code => { throw new BusReadError(code); }; test('a signed-in same-origin revert reaches exactly one library.revert and returns the new commit', async t => { const s = await setup(t), r = await call(s); assert.equal(r.status, 200, r.text); assert.deepEqual(s.calls.revert, [payload]); assert.deepEqual({ ...r.body, at: undefined }, { project: 'books', path: 'Notes/Eyes.md', reverted: C1, commit: C2, at: undefined }); assert.ok(s.lines.some(l => l.includes('library-revert') && l.includes(`commit=${C2}`) && l.includes('project="books"'))); }); test('a revert needs send, the named Host, the user, the exact Origin and same-origin Fetch Metadata', async t => { const off = await setup(t, { enabled: false }); assert.deepEqual((await call(off)).body, { error: 'read-only', sent: false }); const s = await setup(t); for (const [key, value, status] of [['host', 'wrong.example', 403], ['cookie', '__Host-mosaic_session=invalid', 401], ['origin', 'https://else.example', 403], ['sec-fetch-site', 'cross-site', 403], ['origin', undefined, 403], ['sec-fetch-site', undefined, 403], ['cookie', undefined, 401]]) { const hs = { ...s.headers }; if (value === undefined) delete hs[key]; else hs[key] = value; const r = await call(s, { hs }); assert.equal(r.status, status, key); assert.notEqual(r.body?.sent, true); } assert.equal((await call(s, { method: 'GET', data: null })).status, 405); assert.deepEqual(s.calls.revert, []); }); test('a revert takes a project and a 40-hex commit, nothing else, before the bus is asked', async t => { const s = await setup(t); for (const data of ['not json', 'null', '[]', '5', { project: 'books' }, { commit: C1 }, { ...payload, path: 'a.md' }, { project: 'books', commit: 'abc' }, { project: 'books', commit: C1.toUpperCase() }, { project: '../x', commit: C1 }, { project: 7, commit: C1 }]) { const r = await call(s, { data }); assert.equal(r.status, 400, JSON.stringify(data)); assert.equal(r.body.error, 'invalid-request'); assert.equal(r.body.sent, false); } assert.deepEqual(s.calls.revert, []); }); test('a bus refusal is reported as not reverted with its code; before C2-02 that is the verb refusal', async t => { const codes = { 'unknown-verb': 502, 'library-revert-not-agent': 409, 'stale-base': 409, 'human-required': 403, 'project-not-configured': 503, 'invalid-request': 400 }; for (const [code, status] of Object.entries(codes)) { const s = await setup(t, { revert: () => refusal(code) }), r = await call(s); assert.equal(r.status, status, code); assert.equal(r.body.error, code); assert.equal(r.body.sent, false); assert.match(r.body.message, new RegExp(`not reverted: the bus refused it \\(${code}\\)`)); assert.equal(s.calls.revert.length, 1, 'never retried'); } }); test('a revert whose answer is lost or unusable is reported unknown and is never retried', async t => { const answers = [() => refusal('outcome-unknown'), () => { throw new Error('socket closed'); }, () => ({ project: 'books', path: 'a.md', reverted: C1 }), () => ({ project: 'books', path: 'a.md', reverted: C2, commit: C2 }), () => ({ project: 'books', reverted: C1, commit: C2 }), () => null]; for (const revert of answers) { const s = await setup(t, { revert }), r = await call(s); assert.equal(r.status, 503, revert.toString()); assert.equal(r.body.error, 'outcome-unknown'); assert.equal(r.body.sent, 'unknown'); assert.equal(s.calls.revert.length, 1); } }); test('the status read takes one project query and answers the bus result for that project', async t => { const s = await setup(t), r = await get(s, '/api/library/status?project=books'); assert.equal(r.status, 200, r.text); assert.deepEqual(s.calls.status, [{ project: 'books' }]); assert.deepEqual({ ...r.body, at: undefined }, { ...STATUS, at: undefined }); for (const path of ['/api/library/status', '/api/library/status?project=books&x=1', '/api/library/status?project=books&project=other', '/api/library/status?project=../x']) assert.equal((await get(s, path)).status, 400, path); assert.equal((await call(s, { path: '/api/library/status?project=books' })).status, 403, 'the preview write fence precedes GET-only routes'); assert.equal(s.calls.status.length, 1); }); test('a status answer for another project or of the wrong shape is refused; a refusal keeps its status', async t => { for (const answer of [{ ...STATUS, project: 'other' }, null, [], 'books']) { const s = await setup(t, { status: () => answer }), r = await get(s, '/api/library/status?project=books'); assert.equal(r.status, 502, JSON.stringify(answer)); assert.equal(r.body.error, 'invalid-response'); } for (const [code, status] of Object.entries({ 'human-required': 403, 'project-not-configured': 503, 'project-not-declared': 400, 'library-mapping-invalid': 502 })) { const s = await setup(t, { status: () => refusal(code) }), r = await get(s, '/api/library/status?project=books'); assert.equal(r.status, status, code); assert.equal(r.body.error, code); } const none = await setup(t, { status: null }); assert.equal((await get(none, '/api/library/status?project=books')).body.error, 'not-configured'); }); test('the status read needs no send; a read-only Console still has no revert', async t => { const s = await setup(t, { enabled: false }); assert.equal((await get(s, '/api/library/status?project=books')).status, 200); assert.equal((await call(s)).status, 403); }); test('library routes are the owner\'s: a granted user is refused them, and the tester never reverts', async t => { for (const [path, method] of [['/api/library/status', 'GET'], ['/api/library/revert', 'POST']]) assert.equal(userRoute(path, method), false, path); await assert.rejects(startServer({ port: 0, tester: true, testerSocket: '/nonexistent', libraryRevert: async () => ({}) }), /tester listener permits only/); }); test('the page gets the one library renderer, byte for byte, as a JavaScript module', async t => { const s = await setup(t), r = await get(s, '/render.mjs'); assert.equal(r.status, 200); assert.match(r.type, /^text\/javascript/); assert.equal(r.text, readFileSync(new URL('../../library/src/render.mjs', import.meta.url), 'utf8')); }); // A valid commit from an unrelated project is an unusable receipt, never evidence of this revert. test('a revert receipt for another project is unknown and is never retried', async t => { const s = await setup(t, { revert: args => ({ project: 'other', path: 'Notes/Eyes.md', reverted: args.commit, commit: C2 }) }); const r = await call(s); assert.equal(r.status, 503); assert.equal(r.body.error, 'outcome-unknown'); assert.equal(r.body.sent, 'unknown'); assert.deepEqual(s.calls.revert, [payload]); }); // Catch a route being added ahead of the granted-user owner fence: neither transport may be used. test('a granted-user HTTP session cannot reach owner library callbacks', async t => { let ownerCalls = 0, userCalls = 0; const access = { accessVersion: 1, revision: 1, users: [{ id: 'alice', home: 'local', subjects: [{ provider: 'writer', sub: 'alice-sub' }] }], grants: [{ id: 'g-1', user: 'alice', business: 'demo', projects: ['*'], instances: ['*'], level: 'collaborator' }] }; const users = consoleAccess({ load: () => access, business: () => 'demo' }); const auth = await createSsoSession(t, { host: 'console.example', users }); const server = await startServer({ port: 0, sso: auth.sso, send: async () => ({}), libraryStatus: async () => { ownerCalls++; return STATUS; }, libraryRevert: async () => { ownerCalls++; return { project: 'books', path: 'a.md', reverted: C1, commit: C2 }; }, userOptions: () => { userCalls++; return {}; }, log: () => {} }); t.after(() => new Promise(r => server.close(r))); const session = await auth.signIn(server.address().port, { sub: 'alice-sub' }); const s = { port: server.address().port, headers: auth.writeHeaders(session) }; for (const options of [{ method: 'GET', path: '/api/library/status?project=books', data: null }, {}]) { const r = await call(s, options); assert.equal(r.status, 403); assert.equal(r.body.error, 'owner-only'); } assert.equal(ownerCalls, 0); assert.equal(userCalls, 0); }); // A supplied callback is insufficient authority: --send must independently be enabled. test('a configured revert callback remains unreachable when send is off', async t => { let calls = 0; const server = await startServer({ port: 0, preview, libraryRevert: async () => { calls++; return { project: 'books', path: 'a.md', reverted: C1, commit: C2 }; } }); t.after(() => new Promise(r => server.close(r))); const r = await call({ port: server.address().port, headers: { host: preview.host, 'x-user': preview.user, origin: 'https://console.example', 'sec-fetch-site': 'same-origin', 'content-type': 'application/json' } }); assert.equal(r.status, 403); assert.equal(r.body.error, 'read-only'); assert.equal(r.body.sent, false); assert.equal(calls, 0); });