Files
stack/packages/bus/tests/citation-availability.test.mjs

57 lines
3.6 KiB
JavaScript

// E4/sage-3668: real flock contention must not turn a valid cite into an invalid-cite refusal.
import test from 'node:test';
import assert from 'node:assert/strict';
import { mkdirSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { ensureMirror, mirrorPath, SYNC_STATE_FILE, withLibraryLock } from '../../library/src/mirror.mjs';
import { guardReply } from '../../library/src/guard.mjs';
import { git } from '../../library/tests/helpers.mjs';
import { Client } from '../src/client.mjs';
import { serve } from '../src/server.mjs';
import { citationWorld, citedBody, fixtureCite } from './citation-helpers.mjs';
test('C2-06 held library lock gives the agent citation-unavailable and its retry reason', { timeout: 25000 }, async t => {
const f = citationWorld(t), dataRoot = join(f.root, 'data'); mkdirSync(dataRoot, { mode: 0o700 });
const context = { business: f.business, project: 'books', dataRoot };
await ensureMirror(context); const mirror = mirrorPath(context);
git(f.repo, '-c', 'protocol.file.allow=always', 'push', '-q', mirror, 'HEAD:refs/heads/served');
writeFileSync(join(mirror, SYNC_STATE_FILE), JSON.stringify({ version: 1, branch: 'main', served: f.commit,
lastFetch: null, lastError: null, diverged: null, problems: [] }), { mode: 0o600 });
f.adapter.guard = (ctx, body, opts) => guardReply({ ...ctx, dataRoot }, body, opts);
f.call(f.agent, 'role.claim');
const socket = join(f.root, 'availability.sock'), server = await serve({ broker: f.broker, path: socket });
t.after(() => server.close()); const client = new Client({ path: socket, cap: f.agent, timeout: 15000 });
const args = { to: 'human', body: citedBody([fixtureCite(f.commit)]) };
await client.call('message.send', args); // Establish that the cite and real mirror are valid.
const before = f.store.get('SELECT count(*) n FROM messages').n;
await withLibraryLock(mirror, async () => {
await assert.rejects(client.call('message.send', args), e => {
assert.equal(e.code, 'citation-unavailable');
assert.equal(e.reason, 'The library is busy or unavailable. Retry the answer when the library is available.');
return true;
});
assert.equal(f.store.get('SELECT count(*) n FROM messages').n, before);
// A fixed fallback carries no library cite and must not need the contested lock.
await client.call('message.send', { to: 'human', body: '(no answer passed the citation check)' });
});
await client.call('message.send', args); // No lock mutation or lost valid cite after release.
});
test('C2-06 malformed sync state is availability failure, never a fabricated invalid cite', async t => {
const f = citationWorld(t), dataRoot = join(f.root, 'data'); mkdirSync(dataRoot, { mode: 0o700 });
const context = { business: f.business, project: 'books', dataRoot };
await ensureMirror(context); const mirror = mirrorPath(context);
writeFileSync(join(mirror, SYNC_STATE_FILE), '{broken', { mode: 0o600 });
await assert.rejects(guardReply(context, citedBody([fixtureCite(f.commit)]), {
limits: f.limits(), definition: 'coder', pages: [],
}), { code: 'citation-unavailable' });
});
test('C2-06 no mirror or freshly initialized empty mirror still means no served citation', async t => {
const f = citationWorld(t), dataRoot = join(f.root, 'data'); mkdirSync(dataRoot, { mode: 0o700 });
const context = { business: f.business, project: 'books', dataRoot };
const check = () => guardReply(context, citedBody([fixtureCite(f.commit)]), { limits: f.limits(), definition: 'coder', pages: [] });
await assert.rejects(check(), { code: 'citation-invalid' });
await ensureMirror(context); await assert.rejects(check(), { code: 'citation-invalid' });
});