Files
stack/packages/organization/tests/chief-library-credentials.test.mjs
jason.woltje a8d9eecf65 organization: move packages/business to packages/organization (rename r2 stage B1)
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.
2026-11-09 11:47:48 -06:00

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));
});