53 lines
4.1 KiB
JavaScript
53 lines
4.1 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { mkdtempSync, rmSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
import { tmpdir } from 'node:os';
|
|
import { Store } from '../src/store.mjs';
|
|
import { Broker } from '../src/broker.mjs';
|
|
|
|
test('only the owner Console reads and writes', t => {
|
|
const root = mkdtempSync(join(tmpdir(), 'project-remote-bus-')), store = new Store(root), calls = [];
|
|
t.after(() => { store.close(); rmSync(root, { recursive: true, force: true }); });
|
|
const business = { id: 'demo', human: 'jason', vars: { 'bus.tester': true }, arbiters: { technical: 'cto', delivery: 'pm' },
|
|
roles: { pm: { definition: 'pm', authority: { withinRole: [], crossRole: [] } }, cto: { definition: 'cto', authority: { withinRole: [], crossRole: [] } } },
|
|
projects: { stack: { root } } };
|
|
const broker = new Broker({ store, businesses: { demo: business }, organization: (...args) => { calls.push(args); return { project: 'stack', revision: 'a'.repeat(64), remote: null, firstSaveAllowed: true }; } });
|
|
const owner = broker.bindConsole({ business: 'demo', human: 'jason', subject: { provider: 'oidc', iss: 'https://id.test', sub: 'owner' } });
|
|
const cli = broker.bindHuman({ business: 'demo', human: 'jason', via: 'cli', outsideAgent: true });
|
|
const reader = broker.bindReader({ business: 'demo' }), tester = broker.bindTester({ business: 'demo' });
|
|
const get = { verb: 'project.remote.get.console', args: { project: 'stack' } };
|
|
assert.equal(broker.request(owner, get).project, 'stack');
|
|
for (const cap of [cli, reader, tester]) assert.throws(() => broker.request(cap, get), /human-required|read-only|tester-refused/);
|
|
assert.throws(() => broker.request(owner, { ...get, args: { project: 'stack', business: 'other' } }), /invalid-request/);
|
|
assert.equal(broker.request(owner, { verb: 'project.remote.set.console', args: { project: 'stack', revision: 'a'.repeat(64), remote: 'https://git.example/a/b' } }).project, 'stack');
|
|
assert.equal(calls.length, 2);
|
|
assert.throws(() => broker.bindConsole({ business: 'demo', human: 'other', subject: { provider: 'oidc', iss: 'https://id.test', sub: 'owner' } }), /human-required/);
|
|
});
|
|
|
|
test('socket refuses tester, viewer, collaborator, agent and CLI human for both remote verbs', async t => {
|
|
const { serve } = await import('../src/server.mjs');
|
|
const { Client } = await import('../src/client.mjs');
|
|
const root = mkdtempSync(join(tmpdir(), 'remote-socket-')), store = new Store(root);
|
|
const access = { accessVersion: 1, revision: 1, users: [], grants: [
|
|
{ id: 'g-1', user: 'viewer', business: 'demo', projects: ['*'], instances: ['pm'], level: 'viewer' },
|
|
{ id: 'g-2', user: 'collab', business: 'demo', projects: ['*'], instances: ['pm'], level: 'collaborator' },
|
|
] };
|
|
const business = { id: 'demo', human: 'jason', vars: { 'bus.tester': true }, arbiters: { technical: 'pm', delivery: 'pm' },
|
|
roles: { pm: { definition: 'pm', authority: { withinRole: [], crossRole: [] } } }, projects: { stack: { root } } };
|
|
const broker = new Broker({ store, businesses: { demo: business }, access: () => access, organization: () => { throw Error('unexpected writer'); } });
|
|
const caps = {
|
|
human: broker.bindHuman({ business: 'demo', human: 'jason', via: 'cli', outsideAgent: true }),
|
|
tester: broker.bindTester({ business: 'demo' }),
|
|
viewer: broker.bindUser({ business: 'demo', user: 'viewer' }),
|
|
collaborator: broker.bindUser({ business: 'demo', user: 'collab' }),
|
|
agent: broker.bindLaunch({ business: 'demo', role: 'pm', run: 'remote-test', harness: 'pi' }),
|
|
};
|
|
const path = join(root, 'bus.sock'), server = await serve({ broker, path });
|
|
t.after(async () => { await server.close(); store.close(); rmSync(root, { recursive: true, force: true }); });
|
|
for (const [who, cap] of Object.entries(caps)) for (const [verb, args] of [
|
|
['project.remote.get.console', { project: 'stack' }],
|
|
['project.remote.set.console', { project: 'stack', revision: 'a'.repeat(64), remote: 'https://git.example/a/b' }],
|
|
]) await assert.rejects(new Client({ path, cap }).call(verb, args), e => ['human-required', 'read-only', 'tester-refused', 'access-refused'].includes(e.code), `${who} ${verb}`);
|
|
});
|