Files
stack/packages/webui/tests/org-bus.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

48 lines
3.3 KiB
JavaScript

import { test } from 'node:test';
import assert from 'node:assert/strict';
import * as bus from '../src/organizations.mjs';
test('organization adapter rechecks authority and verifies roots before selecting paths', async () => {
const checks = [], ctx = { configDir: '/cfg', dataRoot: '/data', instance: 'test' };
const adapter = bus.organizationConsole({ ctx,
listOrgs: async () => [{ id: 'alpha', state: 'active' }, { id: 'beta', state: 'active' }, { id: 'reserved', state: 'reserved' }],
showOrg: async (context, id) => { checks.push([id, context.principal.sub]); if (id === 'beta') throw Object.assign(Error('denied'), { code: 'org-forbidden' }); return { doc: { appearance: { theme: 'harbor' } } }; },
resolveOrg: async (_, id) => ({ id, root: `/verified/${id}`, state: 'active' }),
orgPaths: root => ({ socket: `${root}/bus/broker.sock` }),
busStatus: async (_, id) => ({ healthy: id === 'alpha' }),
options: ({ id, paths }) => ({ id, socket: paths.socket }),
});
const session = { provider: 'local', iss: 'local', sub: 'one' };
assert.deepEqual(await adapter.list(session), [{ id: 'alpha', label: 'alpha', theme: 'harbor', busHealthy: true }]);
assert.deepEqual(await adapter.resolve('alpha', session), { id: 'alpha', socket: '/verified/alpha/bus/broker.sock', busHealthy: true });
await assert.rejects(adapter.resolve('beta', session));
assert.deepEqual(checks, [['alpha', 'one'], ['beta', 'one'], ['alpha', 'one'], ['beta', 'one']]);
});
test('adapter uses the registry and owner mapping for two scratch organizations', async t => {
const { mkdtempSync, mkdirSync, rmSync } = await import('node:fs');
const { tmpdir } = await import('node:os');
const { join } = await import('node:path');
const { createOrg, showOrg, templateRender } = await import('../../organization/src/org-service.mjs');
const { listOrgs, resolveOrg } = await import('../../organization/src/org-registry.mjs');
const root = mkdtempSync(join(tmpdir(), 'org-console-'));
t.after(() => rmSync(root, { recursive: true, force: true }));
const ctx = { configDir: join(root, 'config'), dataRoot: join(root, 'data'), instance: 'test' };
mkdirSync(ctx.configDir, { mode: 0o700 }); mkdirSync(ctx.dataRoot, { mode: 0o700 });
const principal = { provider: 'local-dev', iss: 'local-dev', sub: 'local-dev-owner' };
for (const id of ['alpha', 'beta']) await createOrg({ ...ctx, principal }, id, await templateRender(id));
const adapter = bus.organizationConsole({ ctx, listOrgs, showOrg, resolveOrg,
orgPaths: orgRoot => ({ socket: join(orgRoot, 'bus', 'broker.sock') }),
busStatus: async (_, id) => ({ healthy: id === 'alpha' }),
options: ({ id, paths }) => ({ id, socket: paths.socket }),
});
assert.deepEqual((await adapter.list(principal)).map(o => [o.id, o.busHealthy]), [['alpha', true], ['beta', false]]);
const a = await adapter.resolve('alpha', principal), b = await adapter.resolve('beta', principal);
assert.notEqual(a.socket, b.socket);
assert.equal(a.socket, join(ctx.dataRoot, 'org-runtime', 'alpha', 'bus', 'broker.sock'));
const unauthorized = { ...principal, sub: 'someone-else' };
assert.deepEqual(await adapter.list(unauthorized), []);
await assert.rejects(adapter.resolve('alpha', unauthorized));
await assert.rejects(adapter.resolve('missing', principal));
});