Files
stack/packages/organization/tests/last-values.test.mjs

82 lines
5.7 KiB
JavaScript

import { test } from 'node:test';
import assert from 'node:assert/strict';
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, readFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
const secret = 'sk-test-NOT-A-SECRET-1234';
function scratch(t) { const root = mkdtempSync(join(tmpdir(), 'r4-')); t.after(() => rmSync(root, { recursive: true, force: true })); return root; }
function safe(label, Type, props = {}, omitted = secret) { return error => {
assert.ok(error instanceof Type, error.stack);
for (const [key, value] of Object.entries(props)) assert.equal(error[key], value);
assert.ok(error.message.includes(label), error.message);
assert.doesNotMatch(error.message + error.stack, /sk-test|NOT-A-SECRET|Unexpected token/);
assert.ok(!error.message.includes(omitted), error.message);
assert.equal(error.cause, undefined);
return true;
}; }
import { checkAccess } from '../src/access.mjs';
import { checkAppearance } from '../src/appearance.mjs';
import { loadProject } from '../src/project.mjs';
import { validateRoleDocument } from '../src/role.mjs';
import { covers } from '../src/vocabulary.mjs';
import { BusinessError } from '../src/errors.mjs';
const context = { providers: ['test'], peers: [], businesses: { acme: { human: 'jason', instances: ['coder'] } }, ssoSubjects: [] };
const access = () => ({ accessVersion: 1, revision: 1, users: [{ id: 'bean', home: 'local', subjects: [{ provider: 'test', sub: 'user' }] }], grants: [{ id: 'grant', user: 'bean', business: 'acme', projects: ['*'], instances: ['coder'], level: 'viewer' }] });
for (const [field, edit] of [
['users[0].home', d => { d.users[0].home = secret; }],
['users[0].subjects[0].provider', d => { d.users[0].subjects[0].provider = secret; }],
...['user', 'business', 'level'].map(k => [`grants[0].${k}`, d => { d.grants[0][k] = secret; }]),
['grants[0].projects[0]', d => { d.grants[0].projects = [secret]; }],
['accessVersion', d => { d.accessVersion = secret; }],
]) test(`access ${field} omits the refused value`, () => {
const d = access(); edit(d); assert.throws(() => checkAccess(d, context), safe(field, BusinessError, { exitCode: 2 }));
});
for (const k of ['theme', 'mode']) test(`appearance ${k} omits its value`, () => {
assert.throws(() => checkAppearance({ [k]: secret }, 'appearance'), safe(k, BusinessError, { exitCode: 2 }));
});
test('project root omits its configured value', () => assert.throws(() => loadProject(secret), safe('project root', BusinessError, { exitCode: 2 })));
test('covers names the level field, not its value', () => assert.throws(() => covers({}, {}, new Date(), { level: secret }), safe('level', TypeError)));
const role = () => ({ roleVersion: 2, name: 'r', title: 'R', contract: 'r.md', tools: ['read'], network: 'none', authority: { withinRole: [], crossRole: [] }, credentials: [] });
for (const [field, edit] of [
['skill', d => { d.skills = [secret + '/']; }],
['contract', d => { d.contract = secret; }],
['tool', d => { d.tools = [secret]; }],
['authority.withinRole', d => { d.authority.withinRole = [secret]; }],
['hireable', d => { d.hireable = [secret.toLowerCase()]; }],
['gitea scope', d => { d.credentials = [{ service: 'gitea', scopes: [secret] }]; }],
['vikunja route group', d => { d.credentials = [{ service: 'vikunja', scopes: { [secret]: ['read'] } }]; }],
['vikunja verb', d => { d.credentials = [{ service: 'vikunja', scopes: { tasks: [secret] } }]; }],
['name', d => { d.name = secret.toLowerCase(); }],
]) test(`role ${field} omits the configured value`, t => {
const root = scratch(t); writeFileSync(join(root, 'r.md'), '# R\n\n## Authority\n');
const d = role(); edit(d);
assert.throws(() => validateRoleDocument(d, join(root, 'r.json')), safe(field, BusinessError, { exitCode: 2 }, secret.toLowerCase()));
});
// This branch is reachable only with already-validated service names; a sentinel service
// is refused earlier. A known service is already displayed in the role's rails.
test('creative service refusal names the validated service list', t => {
const root = scratch(t); writeFileSync(join(root, 'companion.md'), '# R\n\n## Authority\n');
const d = { ...role(), name: 'companion', contract: 'companion.md', messaging: { sendTo: ['human'], receiveFrom: ['human'] }, credentials: [{ service: 'vikunja', scopes: { tasks: ['read_one'] } }] };
assert.throws(() => validateRoleDocument(d, join(root, 'companion.json')), named('credential', 'vikunja'));
});
test('role duplicate skill omits the accepted list value', t => {
const root = scratch(t); writeFileSync(join(root, 'r.md'), '# R\n\n## Authority\n');
const d = { ...role(), skills: [secret, secret] };
assert.throws(() => validateRoleDocument(d, join(root, 'r.json')), safe('role skills', BusinessError, { exitCode: 2 }));
});
for (const at of ['user', 'grant', 'scope', 'human']) test(`access ${at} duplicate/reserved refusal names accepted ids`, () => {
const value = secret.toLowerCase(), d = access(), ctx = structuredClone(context);
if (at === 'user') { d.users[0].id = value; d.users.push({ ...structuredClone(d.users[0]), subjects: [{ provider: 'test', sub: 'second' }] }); }
if (at === 'grant') { d.grants[0].id = value; d.grants.push(structuredClone(d.grants[0])); }
if (at === 'scope') { ctx.businesses.acme.instances = [value]; d.grants[0].instances = [value, value]; }
if (at === 'human') { ctx.businesses.acme.human = value; d.users[0].id = value; }
assert.throws(() => checkAccess(d, ctx), named(at === 'scope' ? 'instances' : '.id', value));
});
function named(label, value) { return error => {
assert.ok(error instanceof BusinessError); assert.equal(error.exitCode, 2);
assert.ok(error.message.includes(label), error.message);
assert.ok(error.message.includes(value), error.message);
return true;
}; }