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.
54 lines
2.9 KiB
JavaScript
54 lines
2.9 KiB
JavaScript
// Row 285 amendment 1 (#1790, mos-1580, sage-4059): a chief references gitea
|
|
// exactly when this business declares a library. The Case 1 source stays unchanged.
|
|
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { BusinessError, validateBusinessDocument } from '../src/index.mjs';
|
|
import { mappingEntry } from '../../library/tests/helpers.mjs';
|
|
|
|
const ROLES = fileURLToPath(new URL('../../../roles/', import.meta.url));
|
|
const CASE1 = new URL('../../../docs/guides/case1-business.mosaic-stack.json', import.meta.url);
|
|
const GITEA = { gitea: { store: true, rotateBy: '2099-01-01' } };
|
|
|
|
function fixture(t) {
|
|
const root = mkdtempSync(join(tmpdir(), 'chief-library-'));
|
|
t.after(() => rmSync(root, { recursive: true, force: true }));
|
|
const doc = JSON.parse(readFileSync(CASE1, 'utf8'));
|
|
const validate = () => validateBusinessDocument(doc, join(root, 'businesses/mosaic-stack.json'), { rolesDir: ROLES });
|
|
return { doc, validate };
|
|
}
|
|
|
|
// Removing the library condition makes this successful Case 1 load fail.
|
|
test('row 285 amendment 1: no library loads with empty chief credentials', t => {
|
|
const { doc, validate } = fixture(t);
|
|
doc.roles.chief.credentials = {};
|
|
const business = validate();
|
|
assert.deepEqual(business.roles.chief.credentials, {});
|
|
assert.deepEqual(business.roles.assistant.credentials, {});
|
|
});
|
|
|
|
// Dropping the required-reference refusal makes the missing-reference assertion fail.
|
|
test('row 285 amendment 1: a library without chief gitea refuses by name', t => {
|
|
const { doc, validate } = fixture(t);
|
|
doc.projects.books = mappingEntry();
|
|
assert.throws(validate, error => error instanceof BusinessError && error.exitCode === 2 &&
|
|
/chief-library-gitea-required: .*roles\.chief\.credentials/.test(error.message));
|
|
doc.roles.chief.credentials = structuredClone(GITEA);
|
|
assert.deepEqual(Object.keys(validate().roles.chief.credentials), ['gitea']);
|
|
});
|
|
|
|
// Letting the chief keep unused gitea, or weakening exact-match globally, breaks this refusal.
|
|
test('row 285 amendment 1: no library with chief gitea refuses the extra reference', t => {
|
|
const { doc, validate } = fixture(t);
|
|
doc.roles.chief.credentials = structuredClone(GITEA);
|
|
assert.throws(validate, error => error instanceof BusinessError && error.exitCode === 2 &&
|
|
/roles\.chief\.credentials must reference exactly the services its role definition needs \(none; got gitea\)/.test(error.message));
|
|
delete doc.roles.chief.credentials;
|
|
doc.roles.assistant.credentials = structuredClone(GITEA);
|
|
assert.throws(validate, error => error instanceof BusinessError && error.exitCode === 2 &&
|
|
/roles\.assistant\.credentials must reference exactly the services its role definition needs \(none; got gitea\)/.test(error.message));
|
|
});
|