62 lines
3.1 KiB
JavaScript
62 lines
3.1 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { mkdtempSync, rmSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import { Store } from '../src/store.mjs';
|
|
import { Broker, BusError } from '../src/broker.mjs';
|
|
|
|
function fixture(t, project, secretCheck = () => {}) {
|
|
const root = mkdtempSync(join(tmpdir(), 'project-await-'));
|
|
const store = new Store(root);
|
|
t.after(() => { store.close(); rmSync(root, { recursive: true, force: true }); });
|
|
const business = { id: 'demo', human: 'jason', 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: ['record.write'], crossRole: [] } },
|
|
} };
|
|
const broker = new Broker({ store, businesses: { demo: business }, project, secretCheck });
|
|
const cap = broker.bindLaunch({ business: 'demo', role: 'coder', run: 'run', harness: 'pi', address: 'coder' });
|
|
const call = (verb, args = {}) => broker.request(cap, { verb, args });
|
|
call('role.claim');
|
|
return { broker, cap, call, store };
|
|
}
|
|
|
|
test('D1: committed write with a failed receipt refuses outcome-unknown without action.allowed', async t => {
|
|
let f, committed = false;
|
|
f = fixture(t, { async writeRecord() {
|
|
committed = true;
|
|
f.call('role.release');
|
|
return { path: 'docs/result.md', commit: 'a'.repeat(40) };
|
|
} });
|
|
let returned;
|
|
await assert.rejects(async () => {
|
|
returned = await f.call('record.write', { project: 'stack', path: 'docs/result.md', record: '# Result' });
|
|
}, { code: 'outcome-unknown' });
|
|
assert.equal(committed, true);
|
|
assert.equal(returned, undefined);
|
|
assert.deepEqual(f.store.all("SELECT body FROM events WHERE kind='action.allowed' AND json_extract(body,'$.action')='record.write'"), []);
|
|
});
|
|
|
|
test('D2: spec.read refuses without content if its role is released during the read', async t => {
|
|
let f;
|
|
f = fixture(t, { async readSpec() {
|
|
f.call('role.release');
|
|
return { content: 'CONTENT-MUST-NOT-RETURN' };
|
|
} });
|
|
let returned;
|
|
await assert.rejects(async () => { returned = await f.call('spec.read', { project: 'stack' }); }, { code: 'not-holder' });
|
|
assert.equal(returned, undefined);
|
|
assert.ok(!JSON.stringify(f.store.all('SELECT body FROM events')).includes('CONTENT-MUST-NOT-RETURN'));
|
|
});
|
|
|
|
for (const field of ['content', 'subject']) test(`D3: spec.read secret check refuses flagged ${field} without content`, async t => {
|
|
const marker = 'FLAGGED-SPEC-VALUE';
|
|
const result = { content: field === 'content' ? marker : '# Safe', log: [{ subject: field === 'subject' ? marker : 'Safe' }] };
|
|
const f = fixture(t, { async readSpec() { return result; } }, value => {
|
|
if (JSON.stringify(value).includes(marker)) throw new BusError('secret-refused');
|
|
});
|
|
let returned;
|
|
await assert.rejects(async () => { returned = await f.call('spec.read', { project: 'stack' }); }, { code: 'secret-refused' });
|
|
assert.equal(returned, undefined);
|
|
assert.ok(!JSON.stringify(f.store.all('SELECT body FROM events')).includes(marker));
|
|
});
|