Files
stack/packages/bus/tests/library-lookups.test.mjs

275 lines
17 KiB
JavaScript

// Row 224: exercise broker authorization, real C2-05a queries and host served snapshots.
// All bodies and remotes are made up. External sync is replaced only at its trusted host seam.
import test from 'node:test';
import assert from 'node:assert/strict';
import { mkdirSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { Store } from '../src/store.mjs';
import { Broker } from '../src/broker.mjs';
import { createLibraryLookup, startBroker } from '../src/runtime.mjs';
import { serve } from '../src/server.mjs';
import { Client } from '../src/client.mjs';
import * as library from '../../library/src/index.mjs';
import { scratchRepo, note } from '../../library/tests/query-fixture.mjs';
import { mappingEntry, git } from '../../library/tests/helpers.mjs';
const NOW = Date.parse('2026-10-22T16:00:00.000Z');
const VERBS = ['library.find', 'library.get', 'library.search', 'library.manuscript', 'library.universe'];
const GET = { universe: 'harbor', path: 'Book One/Characters/Tomas.md' };
const ARGS = [
{ universe: 'harbor', query: 'Tomas' }, GET,
{ universe: 'harbor', text: 'ferry' }, { universe: 'harbor', book: 'Book One' }, { universe: 'harbor' },
];
const TEXTS = {
'Book One/Characters/Tomas.md': note('character', 'Tomas', 'He runs the ferry. [[Elsewhere]]'),
'Book One/Chapters/01.md': '# A ferry\nOne line.\n',
'Book One/Playground/What If.md': note('character', 'Maybe Tomas', 'Unwritten ferry.'),
'Second World/Characters/Elsewhere.md': note('character', 'Elsewhere', 'Other ferry.'),
};
async function world(t, { projects, adapter, claimed = true, now = () => NOW, files = TEXTS } = {}) {
const f = scratchRepo(t, files), dataRoot = join(f.root, 'data');
mkdirSync(dataRoot);
const business = { id: 'acme', human: 'jason', vars: { 'bus.tester': true },
projects: projects ?? { books: mappingEntry() }, arbiters: { technical: 'chief', delivery: 'chief' },
roles: {chief: { definition: 'chief-of-staff', authority: { withinRole: ['decision.resolve.technical', 'decision.resolve.delivery'], crossRole: [] } }, coder: { definition: 'coder', authority: { withinRole: [], crossRole: [] } } } };
const context = { business, project: 'books', dataRoot };
let mirror = null, state = null, fetches = 0;
if (business.projects.books?.kind === 'library') {
mirror = await library.ensureMirror(context);
git(f.repo, '-c', 'protocol.file.allow=always', 'push', '-q', mirror, 'HEAD:refs/heads/served');
state = { version: 1, branch: 'main', served: f.commit, lastFetch: new Date(NOW).toISOString(),
lastError: null, diverged: null, problems: [] };
writeFileSync(join(mirror, library.SYNC_STATE_FILE), JSON.stringify(state), { mode: 0o600 });
}
const setState = patch => {
state = { ...state, ...patch };
writeFileSync(join(mirror, library.SYNC_STATE_FILE), JSON.stringify(state), { mode: 0o600 });
};
const lookup = createLibraryLookup({ dataRoot, now, sync: async () => {
fetches++;
setState({ lastFetch: new Date(now()).toISOString() });
return { state, code: null };
} });
const store = new Store(dataRoot);
t.after(() => store.close());
const broker = new Broker({ store, businesses: { acme: business },
library: adapter ?? { lookup, LibraryError: library.LibraryError } });
const agent = broker.bindLaunch({ business: 'acme', role: 'coder', run: 'r224', harness: 'pi' });
if (claimed) broker.request(agent, { verb: 'role.claim' });
const human = broker.bindHuman({ business: 'acme', human: 'jason', via: 'cli', outsideAgent: true });
const reader = broker.bindReader({ business: 'acme' }), tester = broker.bindTester({ business: 'acme' });
const call = (verb, args = {}, cap = agent) => Promise.resolve().then(() => broker.request(cap, { verb, args }));
return { ...f, dataRoot, business, context, mirror, store, broker, agent, human, reader, tester,
call, lookup, setState, fetches: () => fetches };
}
test('all five agent verbs read the configured served commit through real queries', async t => {
const f = await world(t);
const [find, get, search, manuscript, universe] = await Promise.all(VERBS.map((verb, i) => f.call(verb, ARGS[i])));
for (const result of [find, get, search, manuscript, universe]) assert.equal(result.commit, f.commit);
assert.deepEqual(find.results.map(x => x.path), ['Book One/Characters/Tomas.md']);
assert.match(JSON.stringify(get.lines), /He runs the ferry/);
assert.deepEqual(get.links_out, []); // The only target is in another universe.
assert.ok(search.results.length > 0);
assert.ok(search.results.every(x => !x.path.startsWith('Second World/') && x.scope === 'canon'));
assert.equal(manuscript.path, 'Book One/Chapters/01.md');
assert.deepEqual(universe.books, ['Book One']);
assert.equal(f.fetches(), 0);
const playground = await f.call('library.find', { universe: 'harbor', query: '', scope: 'playground' });
assert.deepEqual(playground.results.map(x => x.path), ['Book One/Playground/What If.md']);
assert.equal(await f.call('library.get', { universe: 'harbor', path: 'Second World/Characters/Elsewhere.md' }), null);
});
test('each verb refuses missing universe and unknown keys before host work', async t => {
let calls = 0;
const f = await world(t, { adapter: { lookup: () => { calls++; } } });
for (const [i, verb] of VERBS.entries()) {
const { universe, ...missing } = ARGS[i];
await assert.rejects(f.call(verb, missing), { code: 'invalid-request' });
for (const extra of [{ repo: '/elsewhere' }, { commit: 'b'.repeat(40) }, { business: 'other' }, { accept: 'b'.repeat(40) }])
await assert.rejects(f.call(verb, { ...ARGS[i], ...extra }), { code: 'invalid-request' });
await assert.rejects(f.call(verb, { ...ARGS[i], universe: '' }), { code: 'invalid-request' });
}
assert.equal(calls, 0);
});
test('no library, missing host adapter and undeclared project refuse library-not-configured', async t => {
const noLibrary = await world(t, { projects: {} });
const noAdapter = await world(t, { adapter: {} });
const f = await world(t);
for (const [i, verb] of VERBS.entries()) {
await assert.rejects(noLibrary.call(verb, ARGS[i]), { code: 'library-not-configured' });
await assert.rejects(noAdapter.call(verb, ARGS[i]), { code: 'library-not-configured' });
await assert.rejects(f.call(verb, { ...ARGS[i], project: 'other' }), { code: 'library-not-configured' });
}
f.business.projects.second = mappingEntry();
f.broker.reloadBusiness('acme', f.business);
await assert.rejects(f.call('library.get', GET), { code: 'invalid-request' });
assert.equal((await f.call('library.get', { ...GET, project: 'books' })).commit, f.commit);
});
test('reads need the current holder on each call; human/tester/reader are not agents', async t => {
const f = await world(t, { claimed: false });
for (const [i, verb] of VERBS.entries()) {
await assert.rejects(f.call(verb, ARGS[i]), { code: 'not-holder' });
await assert.rejects(f.call(verb, ARGS[i], f.human), { code: 'agent-required' });
await assert.rejects(f.call(verb, ARGS[i], f.tester), { code: 'tester-refused' });
await assert.rejects(f.call(verb, ARGS[i], f.reader), { code: 'read-only' });
}
f.broker.request(f.agent, { verb: 'role.claim' });
assert.equal((await f.call('library.get', GET)).commit, f.commit);
f.broker.request(f.agent, { verb: 'role.release' });
await assert.rejects(f.call('library.get', GET), { code: 'not-holder' });
});
test('releasing a claim during an async lookup fences the response', { timeout: 30_000 }, async t => {
let release, entered;
const started = new Promise(resolve => { entered = resolve; });
const f = await world(t, { adapter: { lookup: async () => {
entered(); await new Promise(resolve => { release = resolve; }); return { lines: [[1, 'not returned']] };
} } });
const pending = f.call('library.get', GET);
await started;
f.broker.request(f.agent, { verb: 'role.release' });
release();
await assert.rejects(pending, { code: 'not-holder' });
});
test('a business library removed during an async read cannot leak its result', { timeout: 30_000 }, async t => {
let release, entered;
const started = new Promise(resolve => { entered = resolve; });
const f = await world(t, { adapter: { lookup: async () => {
entered(); await new Promise(resolve => { release = resolve; }); return { lines: [[1, 'not returned']] };
} } });
const pending = f.call('library.get', GET);
await started;
delete f.business.projects.books;
f.broker.reloadBusiness('acme', f.business);
release();
await assert.rejects(pending, { code: 'library-not-configured' });
});
test('freshness boundary, unknown last fetch and failed fetch still use immutable served state', async t => {
const f = await world(t);
f.setState({ lastFetch: new Date(NOW - 60_000).toISOString() });
await f.call('library.get', GET);
assert.equal(f.fetches(), 0);
f.setState({ lastFetch: new Date(NOW - 60_001).toISOString() });
await f.call('library.get', GET);
assert.equal(f.fetches(), 1);
f.setState({ lastFetch: null });
await f.call('library.universe', { universe: 'harbor' });
assert.equal(f.fetches(), 2);
let attempted = 0, clock = NOW;
const failing = createLibraryLookup({ dataRoot: f.dataRoot, now: () => clock, sync: async () => {
attempted++;
f.setState({ lastError: { code: 'library-fetch-failed', at: new Date(clock).toISOString() } });
return { code: 'library-fetch-failed' };
} });
f.setState({ lastFetch: null });
const result = await failing(f.context, { method: 'get', args: GET, authorize() {} });
assert.equal(attempted, 1);
assert.equal(result.commit, f.commit);
for (const elapsed of [1, 59_999, 60_000]) {
clock = NOW + elapsed;
const retry = await failing(f.context, { method: 'get', args: GET, authorize() {} });
assert.equal(retry.commit, f.commit, 'serve the old pin during the failure cooldown');
assert.equal(attempted, 1, 'recent lastError prevents another sync even though lastFetch is null');
}
clock = NOW + 60_001;
assert.equal((await failing(f.context, { method: 'get', args: GET, authorize() {} })).commit, f.commit);
assert.equal(attempted, 2, 'retry only after the failed-attempt interval expires');
});
test('an unreadable sync state fails closed; a busy sync keeps serving the existing pin', async t => {
const f = await world(t);
const lookup = createLibraryLookup({ dataRoot: f.dataRoot, now: () => NOW, sync: async () => {
throw new library.LibraryError('library-sync-busy', 'another fetch');
} });
f.setState({ lastFetch: null });
assert.equal((await lookup(f.context, { method: 'get', args: GET, authorize() {} })).commit, f.commit);
writeFileSync(join(f.mirror, library.SYNC_STATE_FILE), '{bad json', { mode: 0o600 });
await assert.rejects(f.call('library.get', GET), { code: 'library-state-invalid' });
});
test('a stale lookup sees the commit published by its sync, not the prior served commit', async t => {
const f = await world(t);
writeFileSync(join(f.repo, GET.path), note('character', 'Tomas', 'Changed ferry.'));
git(f.repo, 'add', '-A'); git(f.repo, 'commit', '-qm', 'second fixture');
const next = git(f.repo, 'rev-parse', 'HEAD');
const lookup = createLibraryLookup({ dataRoot: f.dataRoot, now: () => NOW, sync: async () => {
git(f.repo, '-c', 'protocol.file.allow=always', 'push', '-q', f.mirror, 'HEAD:refs/heads/served');
f.setState({ served: next, lastFetch: new Date(NOW).toISOString() });
return { code: null };
} });
f.setState({ lastFetch: null });
const result = await lookup(f.context, { method: 'get', args: GET, authorize() {} });
assert.equal(result.commit, next);
assert.match(JSON.stringify(result.lines), /Changed ferry/);
});
test('real socket awaits lookup results and rechecks rejected query arguments', { timeout: 30_000 }, async t => {
const f = await world(t);
const path = join(f.root, 'lookup.sock');
const server = await serve({ broker: f.broker, path }); t.after(() => server.close());
const client = new Client({ path, cap: f.agent });
const result = await client.call('library.get', GET);
assert.equal(result.commit, f.commit);
await assert.rejects(client.call('library.find', { universe: 'harbor', query: '', scope: 'anything' }), /query-argument-invalid/);
});
test('runtime actually injects the lookup adapter, without exposing human library.file', { timeout: 30_000 }, async t => {
const f = scratchRepo(t, TEXTS), dataRoot = join(f.root, 'runtime'); mkdirSync(dataRoot);
const business = { id: 'acme', human: 'jason', plugins: { harness: ['pi'] }, projects: { books: mappingEntry() },
arbiters: { technical: 'chief', delivery: 'chief' },
roles: {chief: { definition: 'chief-of-staff', authority: { withinRole: ['decision.resolve.technical', 'decision.resolve.delivery'], crossRole: [] } }, coder: { definition: 'coder', authority: { withinRole: [], crossRole: [] } } } };
const runtime = await startBroker({ dataRoot, businesses: { acme: business } }); t.after(() => runtime.close());
const mirror = library.mirrorPath({ business, project: 'books', dataRoot });
git(f.repo, '-c', 'protocol.file.allow=always', 'push', '-q', mirror, 'HEAD:refs/heads/served');
writeFileSync(join(mirror, library.SYNC_STATE_FILE), JSON.stringify({ version: 1, branch: 'main', served: f.commit,
lastFetch: new Date().toISOString(), lastError: null, diverged: null, problems: [] }), { mode: 0o600 });
const cap = runtime.broker.bindLaunch({ business: 'acme', role: 'coder', run: 'r224-runtime', harness: 'pi' });
runtime.broker.request(cap, { verb: 'role.claim' });
assert.equal((await runtime.broker.request(cap, { verb: 'library.get', args: GET })).commit, f.commit);
await assert.rejects(runtime.broker.request(cap, { verb: 'library.file', args: { project: 'books', path: GET.path } }),
{ code: 'human-required' });
});
test('C2-05c broker forwards supporting scope and returns the served blob without adding write verbs', async t => {
const projects = { books: mappingEntry() };
projects.books.universes.harbor.supporting = ['Book One/Support/'];
const path = 'Book One/Support/Helper.md';
const f = await world(t, { projects, files: { ...TEXTS, [path]: note('summary', 'Helper', 'Supporting fixture.') } });
assert.deepEqual((await f.call('library.find', { universe: 'harbor', query: '', scope: 'supporting' })).results.map(x => x.path), [path]);
assert.deepEqual((await f.call('library.search', { universe: 'harbor', text: 'Supporting fixture', scope: 'supporting' })).results.map(x => x.path), [path]);
assert.equal(await f.call('library.get', { universe: 'harbor', path }), null);
const result = await f.call('library.get', { universe: 'harbor', path, scope: 'supporting' });
assert.equal(result.scope, 'supporting');
assert.equal(result.commit, f.commit);
assert.equal(result.blob, git(f.mirror, 'rev-parse', `${f.commit}:${path}`));
assert.equal(await f.call('library.get', { universe: 'second', path, scope: 'supporting' }), null);
await assert.rejects(f.call('library.manuscript', { universe: 'harbor', book: 'Book One', scope: 'supporting' }), { code: 'query-argument-invalid' });
});
test('C2-15 broker: library.find, library.search and library.universe in a hybrid see its own files and source canon, minus the type-form exclude', async t => {
const projects = { books: mappingEntry() };
projects.books.universes.cross = { kind: 'hybrid', name: 'Cross', sources: ['harbor', 'second'], books: ['Crossover'],
constraints: [{ exclude: 'second/Characters' }], notes: 'Fixture crossover.' };
const f = await world(t, { projects, files: { ...TEXTS, 'Crossover/Chapters/01.md': '# Both\n[[Tomas]] and [[Elsewhere]].\n' } });
const found = await f.call('library.find', { universe: 'cross', query: '' });
assert.deepEqual(found.results.map(x => [x.path, x.universe]), [
['Book One/Chapters/01.md', 'harbor'], ['Book One/Characters/Tomas.md', 'harbor'], ['Crossover/Chapters/01.md', 'cross']]);
assert.deepEqual((await f.call('library.find', { universe: 'cross', query: '', scope: 'playground' })).results, []);
assert.ok(!(await f.call('library.find', { universe: 'harbor', query: '' })).results.some(x => x.path.startsWith('Crossover/')));
// Search hits name their source universe too: a source's canon and the hybrid's own file, and nothing excluded.
const pairs = reply => reply.results.map(x => [x.path, x.universe]);
assert.deepEqual(pairs(await f.call('library.search', { universe: 'cross', text: 'Elsewhere' })), [
['Book One/Characters/Tomas.md', 'harbor'], ['Crossover/Chapters/01.md', 'cross']]);
assert.deepEqual(pairs(await f.call('library.search', { universe: 'cross', text: 'ferry' })), [
['Book One/Chapters/01.md', 'harbor'], ['Book One/Characters/Tomas.md', 'harbor']]);
const universe = await f.call('library.universe', { universe: 'cross' });
assert.deepEqual([universe.kind, universe.sources, universe.constraints, universe.notes],
['hybrid', ['harbor', 'second'], [{ exclude: 'second/Characters' }], 'Fixture crossover.']);
});