git mv packages/business -> packages/organization (@mosaic/organization). The old package stays as one-line re-export shims of each moved module, with src/business.mjs re-exporting src/organization-config.mjs and src/cli.mjs forwarding to the moved executable. BusinessError is OrganizationConfigError (one constructor, name kept BusinessError); OrganizationError stays distinct. The bus adapter gains organization-context.mjs (busOrganization), with business.mjs (busBusiness) as the same function. Import consumers, manifests, the lockfile, scripts/mosaic, stack-copy.sh COPY_PATHS (both roots), seat sandbox RUNNER_FILES and the copy closure walker (organization-imports.mjs, both roots required) move to the new path. v1 serialization, v1 writers and the stage A fixtures are unchanged; the stage A contract tests stay where they were and pass as before.
71 lines
4.2 KiB
JavaScript
71 lines
4.2 KiB
JavaScript
// Row 285 (#1790), mos-1585/1586: only creative-to-creative changes may retain
|
|
// the same declared service set. No reference is created, moved or minted by a change.
|
|
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { readFileSync, rmSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
import { instanceSettings } from '../src/instance-settings.mjs';
|
|
import { businessDoc, rolesCopy, systemFor, tmp, writeJson } from './helpers.mjs';
|
|
|
|
function fixture(t) {
|
|
const root = tmp();
|
|
t.after(() => rmSync(root, { recursive: true, force: true }));
|
|
const rolesDir = rolesCopy(root), dir = join(root, 'config');
|
|
const companion = JSON.parse(readFileSync(join(rolesDir, 'companion.json'), 'utf8'));
|
|
for (const [name, authority, credentials] of [
|
|
['creative-host', companion.authority, [...companion.credentials, { service: 'host' }]],
|
|
['plain-gitea', { withinRole: ['message.send'], crossRole: [] }, companion.credentials],
|
|
['creative-vikunja', companion.authority, [{ service: 'vikunja', scopes: { tasks: ['read_one'] } }]],
|
|
]) writeJson(join(rolesDir, `${name}.json`), { ...companion, name, authority, credentials });
|
|
const doc = businessDoc(root);
|
|
doc.roles.bean = { definition: 'companion', credentials: { gitea: { ...doc.roles.coder.credentials.gitea } } };
|
|
const file = writeJson(join(dir, 'businesses/acme.json'), doc);
|
|
const settings = instanceSettings({ dir, rolesDir, system: systemFor(root) });
|
|
const change = definition => settings.set('acme', { revision: settings.read('acme').revision, instance: 'bean', op: 'change', definition }, 'jason');
|
|
return { doc, file, settings, change };
|
|
}
|
|
|
|
// Keeping the old blanket credential fence breaks this supported change.
|
|
test('row 285 P: creative equal-service changes preserve every existing credential reference', t => {
|
|
const f = fixture(t), before = JSON.parse(readFileSync(f.file, 'utf8')).roles.bean.credentials;
|
|
f.change('ideas');
|
|
const after = JSON.parse(readFileSync(f.file, 'utf8')).roles.bean;
|
|
assert.equal(after.definition, 'ideas');
|
|
assert.deepEqual(after.credentials, before);
|
|
assert.deepEqual(Object.keys(after.credentials), ['gitea']);
|
|
assert.equal(after.credentials.gitea.store, undefined);
|
|
});
|
|
|
|
// Host has no file reference, so ordinary business validation alone cannot guard this set change.
|
|
test('row 285 P: creative declared-service mismatch refuses even when host has no reference', t => {
|
|
const f = fixture(t), before = readFileSync(f.file);
|
|
assert.throws(() => f.change('creative-host'), e => e.code === 'organization-credentials-out-of-scope');
|
|
assert.deepEqual(readFileSync(f.file), before);
|
|
});
|
|
|
|
// Letting equal sets bypass the non-creative base fence breaks this refusal.
|
|
test('row 285 P: an equal-service non-creative target keeps the base credential refusal', t => {
|
|
const f = fixture(t), before = readFileSync(f.file);
|
|
assert.throws(() => f.change('plain-gitea'), e => e.code === 'organization-credentials-out-of-scope');
|
|
assert.deepEqual(readFileSync(f.file), before);
|
|
});
|
|
|
|
// Running the credential fence ahead of validation changes the base BusinessError.
|
|
test('row 285 P: business validation precedes the creative credential fence', t => {
|
|
const f = fixture(t), before = readFileSync(f.file);
|
|
assert.throws(() => f.change('security-reviewer'), e => e.name === 'BusinessError');
|
|
assert.deepEqual(readFileSync(f.file), before);
|
|
});
|
|
|
|
// Removing creative addability, or admitting host/Vikunja there, breaks these literal entries.
|
|
test('row 285 P: listing derives creative from authority and accepts only existing gitea minting', t => {
|
|
const f = fixture(t), definitions = f.settings.read('acme').definitions;
|
|
assert.deepEqual(definitions.companion, { services: ['gitea'], accepted: true });
|
|
assert.deepEqual(definitions.ideas, { services: ['gitea'], accepted: true });
|
|
assert.deepEqual(definitions['creative-host'], { services: ['gitea', 'host'], accepted: false });
|
|
assert.deepEqual(definitions['creative-vikunja'], { services: ['vikunja'], accepted: false });
|
|
assert.deepEqual(definitions['plain-gitea'], { services: ['gitea'], accepted: false });
|
|
assert.deepEqual(definitions.coder, { services: ['gitea', 'vikunja'], accepted: false });
|
|
assert.equal(definitions['chief-of-staff'].accepted, false);
|
|
});
|