Files
stack/packages/organization/tests/resolve-rights.test.mjs

87 lines
5.4 KiB
JavaScript

// RR (#1873): the file writer refuses arbiters without their domain's right.
import test from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync, rmSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { organizationWriter } from '../src/organization.mjs';
import { loadRole, validateBusinessDocument } from '../src/index.mjs';
import { businessDoc, REPO_ROLES, rolesCopy, tmp, writeJson } from './helpers.mjs';
for (const [domain, instance, definition] of [['technical', 'cto', 'cto'], ['delivery', 'pm', 'pm']]) {
test(`RR: naming ${instance} without decision.resolve.${domain} refuses when written`, t => {
const root = tmp(); t.after(() => rmSync(root, { recursive: true, force: true }));
const rolesDir = rolesCopy(root), file = join(root, 'businesses/acme.json');
writeJson(file, businessDoc(root));
const writer = organizationWriter({ dir: root, rolesDir });
const { document, revision } = writer.read('acme'), before = readFileSync(file, 'utf8');
const roleFile = join(rolesDir, `${definition}.json`), role = JSON.parse(readFileSync(roleFile, 'utf8'));
role.authority.withinRole = role.authority.withinRole.filter(a => a !== `decision.resolve.${domain}`);
writeJson(roleFile, role);
assert.throws(() => writer.write('acme', document, { revision, actor: 'jason' }), error => {
assert.match(error.message, new RegExp(`arbiters\\.${domain}.*${instance}.*role definition ${definition}.*decision\\.resolve\\.${domain}`));
return true;
});
assert.equal(readFileSync(file, 'utf8'), before);
});
}
test('RR: a coder named as arbiter refuses when written', t => {
const root = tmp(); t.after(() => rmSync(root, { recursive: true, force: true }));
const file = join(root, 'businesses/acme.json'); writeJson(file, businessDoc(root));
const writer = organizationWriter({ dir: root, rolesDir: REPO_ROLES });
const { document, revision } = writer.read('acme'), before = readFileSync(file, 'utf8');
document.arbiters.delivery = 'coder';
assert.throws(() => writer.write('acme', document, { revision, actor: 'jason' }), /arbiters\.delivery.*coder.*role definition coder.*decision\.resolve\.delivery/);
assert.equal(readFileSync(file, 'utf8'), before);
});
test('RR: a cross-role resolve grant cannot qualify an arbiter', t => {
const root = tmp(); t.after(() => rmSync(root, { recursive: true, force: true }));
const rolesDir = rolesCopy(root), roleFile = join(rolesDir, 'cto.json');
const role = JSON.parse(readFileSync(roleFile, 'utf8'));
role.authority.withinRole = role.authority.withinRole.filter(a => a !== 'decision.resolve.technical');
role.authority.crossRole.push('decision.resolve.technical'); writeJson(roleFile, role);
assert.throws(() => validateBusinessDocument(businessDoc(root), join(root, 'businesses/acme.json'), { rolesDir }), /cto.*decision\.resolve\.technical/);
});
test('RR: a version 2 contract without its Authority section refuses', t => {
const root = tmp(); t.after(() => rmSync(root, { recursive: true, force: true }));
const rolesDir = rolesCopy(root);
writeFileSync(join(rolesDir, 'coder.md'), '# Role contract: coder\n\n## Duties\nRead only.\n');
assert.throws(() => loadRole(rolesDir, 'coder'), /coder.*## Authority/);
});
for (const [domain, instance] of [['technical', 'cto'], ['delivery', 'pm']]) {
for (const layer of ['agent', 'business', 'intersection']) {
test(`RR: ${layer} limits removing ${instance}'s ${domain} resolve right refuse when written`, t => {
const root = tmp(); t.after(() => rmSync(root, { recursive: true, force: true }));
const file = join(root, 'businesses/acme.json'); writeJson(file, businessDoc(root));
const writer = organizationWriter({ dir: root, rolesDir: REPO_ROLES });
const { document, revision } = writer.read('acme'), before = readFileSync(file, 'utf8');
const right = `decision.resolve.${domain}`;
const rights = ['message.send', 'decision.resolve.technical', 'decision.resolve.delivery'];
if (layer === 'agent') document.roles[instance].vars['limits.authority'] = ['message.send'];
else document.vars['limits.authority'] = rights.filter(value => value !== right);
if (layer === 'intersection') document.roles[instance].vars['limits.authority'] = rights;
assert.throws(() => writer.write('acme', document, { revision, actor: 'jason' }), error => {
assert.match(error.message, new RegExp(`arbiters\\.${domain}.*${instance}.*role definition ${instance}.*decision\\.resolve\\.${domain}`));
return true;
});
assert.equal(readFileSync(file, 'utf8'), before);
});
}
}
test('RR: narrowed arbiters retaining their domain rights can be written', t => {
const root = tmp(); t.after(() => rmSync(root, { recursive: true, force: true }));
const file = join(root, 'businesses/acme.json'); writeJson(file, businessDoc(root));
const writer = organizationWriter({ dir: root, rolesDir: REPO_ROLES });
const { document, revision } = writer.read('acme');
document.vars['limits.authority'] = ['message.send', 'decision.resolve.technical', 'decision.resolve.delivery'];
for (const [domain, instance] of [['technical', 'cto'], ['delivery', 'pm']]) {
document.roles[instance].vars['limits.authority'] = ['message.send', `decision.resolve.${domain}`];
}
writer.write('acme', document, { revision, actor: 'jason' });
assert.deepEqual(writer.read('acme').document.arbiters, document.arbiters);
});