The business file accepts a kind "library" project entry, and each consumer that assumed a project root skips it. An organization write whose projects differ from the file's is refused (M9). The bare mirror is created at the projectRepo path with no remote. library.file and library.status are human only and refuse an agent session with human-required. Candidate herb/row215-on197dde3445abona36523713, Filbert APPROVE rev 2924.
124 lines
9.0 KiB
JavaScript
124 lines
9.0 KiB
JavaScript
// C2-04 M4 and real async transport, with made-up local library data.
|
|
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import { Store } from '../src/store.mjs';
|
|
import { Broker } from '../src/broker.mjs';
|
|
import { serve } from '../src/server.mjs';
|
|
import { Client } from '../src/client.mjs';
|
|
import { startBroker } from '../src/runtime.mjs';
|
|
import { mappingEntry, repo, git, BASE, MARKER } from '../../library/tests/helpers.mjs';
|
|
import * as library from '../../library/src/index.mjs';
|
|
|
|
function world(t, adapter = {}) {
|
|
const root = mkdtempSync(join(tmpdir(), 'bus-library-')), store = new Store(root);
|
|
t.after(() => { store.close(); rmSync(root, { recursive: true, force: true }); });
|
|
const business = { id: 'acme', human: 'jason', vars: { 'bus.tester': true },
|
|
projects: { books: mappingEntry() }, arbiters: { technical: 'coder', delivery: 'coder' }, roles: { coder: { authority: { withinRole: [], crossRole: [] } } } };
|
|
const broker = new Broker({ store, businesses: { acme: business }, library: adapter });
|
|
const human = broker.bindHuman({ business: 'acme', human: 'jason', via: 'cli', outsideAgent: true });
|
|
const agent = broker.bindLaunch({ business: 'acme', role: 'coder', run: 'library-agent', harness: 'pi' });
|
|
const reader = broker.bindReader({ business: 'acme' }), tester = broker.bindTester({ business: 'acme' });
|
|
const call = (cap, verb, args = {}) => Promise.resolve().then(() => broker.request(cap, { verb, args }));
|
|
return { root, store, broker, business, human, agent, reader, tester, call };
|
|
}
|
|
|
|
test('C2-04 M4: both verbs reject every agent before arguments or adapter access', async t => {
|
|
let touched = 0;
|
|
const f = world(t, { readFile: () => { touched++; }, status: () => { touched++; } });
|
|
for (const verb of ['library.file', 'library.status']) {
|
|
await assert.rejects(f.call(f.agent, verb, { unknown: true }), { code: 'human-required' });
|
|
await assert.rejects(f.call(f.tester, verb, { unknown: true }), { code: 'human-required' });
|
|
await assert.rejects(f.call(f.reader, verb, { unknown: true }), { code: 'read-only' });
|
|
}
|
|
assert.equal(touched, 0);
|
|
const refusals = f.store.all("SELECT body FROM events WHERE kind='action.refused'").map(row => JSON.parse(row.body));
|
|
assert.equal(refusals.length, 6);
|
|
assert.equal(JSON.stringify(refusals).includes('unknown'), false);
|
|
const absent = world(t, null);
|
|
for (const verb of ['library.file', 'library.status'])
|
|
await assert.rejects(absent.call(absent.agent, verb, {}), { code: 'human-required' });
|
|
});
|
|
|
|
test('C2-04 bus: human reads validate keys and return adapter results without path leakage', async t => {
|
|
const seen = [];
|
|
const f = world(t, { readFile: async args => { seen.push(args); return { project: 'books', path: 'Book One/A.md', commit: 'a'.repeat(40), content: '# Made up\n' }; },
|
|
status: async args => { seen.push(args); return { project: 'books', served: null, problems: [] }; }, LibraryError: library.LibraryError });
|
|
assert.equal((await f.call(f.human, 'library.file', { project: 'books', path: 'Book One/A.md' })).content, '# Made up\n');
|
|
assert.equal((await f.call(f.human, 'library.status', { project: 'books' })).served, null);
|
|
assert.equal(seen[0].business.id, 'acme'); assert.equal(seen[1].project, 'books');
|
|
for (const [verb, args] of [['library.file', { project: 'books' }], ['library.file', { project: 'books', path: 'Book One/A.md', commit: 'a'.repeat(40) }], ['library.status', { project: 'books', extra: 1 }]])
|
|
await assert.rejects(f.call(f.human, verb, args), { code: 'invalid-request' });
|
|
});
|
|
|
|
test('C2-04 socket: each async library verb resolves its result rather than serializing {}', async t => {
|
|
const f = world(t, { readFile: async () => ({ project: 'books', path: 'Book One/A.md', commit: 'a'.repeat(40), content: '# Fixture\n' }),
|
|
status: async () => ({ project: 'books', served: null, problems: [] }), LibraryError: library.LibraryError });
|
|
const path = join(f.root, 'bus.sock');
|
|
const server = await serve({ broker: f.broker, path, authenticateHuman: () => f.broker.bindHuman({ business: 'acme', human: 'jason', via: 'cli', outsideAgent: true }) });
|
|
t.after(() => server.close());
|
|
const human = new Client({ path, human: { fixture: true } });
|
|
assert.deepEqual(await human.call('library.status', { project: 'books' }), { project: 'books', served: null, problems: [] });
|
|
assert.equal((await human.call('library.file', { project: 'books', path: 'Book One/A.md' })).content, '# Fixture\n');
|
|
});
|
|
|
|
test('C2-04 runtime: protectedRoots skips library but retains real code-root credential protection', async t => {
|
|
const f = repo(t), dataRoot = join(f.root, 'data'), token = join(f.root, 'fixture-token');
|
|
mkdirSync(dataRoot, { mode: 0o700 }); writeFileSync(token, 'made-up-fixture-token-123456', { mode: 0o600 });
|
|
const business = { id: 'acme', human: 'jason', projects: { books: mappingEntry(), stack: { root: f.repo } },
|
|
arbiters: { technical: 'coder', delivery: 'coder' }, roles: { coder: { authority: { withinRole: [], crossRole: [] }, credentials: { gitea: { file: token, rotateBy: '2099-01-01' } } } } };
|
|
const runtime = await startBroker({ dataRoot, businesses: { acme: business } });
|
|
await runtime.close();
|
|
const mirror = library.mirrorPath({ business, project: 'books', dataRoot });
|
|
assert.equal(git(mirror, 'rev-parse', '--is-bare-repository'), 'true');
|
|
const bad = join(f.repo, 'credential'); writeFileSync(bad, 'made-up-fixture-token-123456', { mode: 0o600 });
|
|
business.roles.coder.credentials.gitea.file = bad;
|
|
await assert.rejects(startBroker({ dataRoot, businesses: { acme: business } }));
|
|
});
|
|
|
|
test('C2-04 runtime: unsafe existing mirror refuses boot and never publishes a socket', async t => {
|
|
const f = repo(t), dataRoot = join(f.root, 'data');
|
|
mkdirSync(dataRoot, { mode: 0o700 });
|
|
const business = { id: 'acme', human: 'jason', projects: { books: mappingEntry() }, arbiters: { technical: 'coder', delivery: 'coder' }, roles: { coder: { authority: { withinRole: [], crossRole: [] } } } };
|
|
const mirror = join(dataRoot, 'projects/acme/books'); mkdirSync(mirror, { recursive: true });
|
|
writeFileSync(join(mirror, 'keep'), 'made-up note');
|
|
await assert.rejects(startBroker({ dataRoot, businesses: { acme: business } }), { code: 'library-git' });
|
|
assert.equal(existsSync(join(dataRoot, 'bus.sock')), false);
|
|
assert.equal(readFileSync(join(mirror, 'keep'), 'utf8'), 'made-up note');
|
|
});
|
|
|
|
test('C2-04 runtime: real library adapters reach the socket and lock waits leave other bus reads responsive', { timeout: 15000 }, async t => {
|
|
const f = repo(t), dataRoot = join(f.root, 'data');
|
|
mkdirSync(dataRoot, { mode: 0o700 });
|
|
const business = { id: 'acme', human: 'jason', projects: { books: mappingEntry() }, arbiters: { technical: 'coder', delivery: 'coder' }, roles: { coder: { authority: { withinRole: [], crossRole: [] } } } };
|
|
const runtime = await startBroker({ dataRoot, businesses: { acme: business } });
|
|
t.after(() => runtime.close());
|
|
const cap = runtime.broker.bindHuman({ business: 'acme', human: 'jason', via: 'cli', outsideAgent: true });
|
|
const path = join(f.root, 'human-fixture.sock');
|
|
const server = await serve({ broker: runtime.broker, path, authenticateHuman: () => runtime.broker.bindHuman({ business: 'acme', human: 'jason', via: 'cli', outsideAgent: true }) }); t.after(() => server.close());
|
|
const human = new Client({ path, human: { fixture: true } });
|
|
assert.equal((await human.call('library.status', { project: 'books' })).served, null);
|
|
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: null, lastError: null, diverged: null, problems: [] }), { mode: 0o600 });
|
|
const file = await human.call('library.file', { project: 'books', path: 'Book One/Characters/Tomas.md' });
|
|
assert.equal(file.content, BASE['Book One/Characters/Tomas.md']);
|
|
assert.equal(file.commit, f.commit);
|
|
let release, done = false;
|
|
const hold = library.withLibraryLock(mirror, () => new Promise(r => { release = r; }));
|
|
const deadline = Date.now() + 5000;
|
|
while (!release && Date.now() < deadline) await new Promise(r => setTimeout(r, 5));
|
|
assert.equal(typeof release, 'function');
|
|
t.after(async () => { release(); await hold; });
|
|
const pending = human.call('library.status', { project: 'books' }).then(result => { done = true; return result; });
|
|
const ordinary = await Promise.race([human.call('agents'), new Promise((_, reject) => {
|
|
const timer = setTimeout(() => reject(Error('ordinary bus read blocked by lock acquisition')), 1000); timer.unref();
|
|
})]);
|
|
assert.ok(ordinary && typeof ordinary === 'object'); assert.equal(done, false);
|
|
release(); await hold;
|
|
assert.equal((await pending).served, f.commit);
|
|
});
|