import { test } from 'node:test'; import assert from 'node:assert/strict'; import { mkdirSync, mkdtempSync, symlinkSync, writeFileSync } from 'node:fs'; import { join } from 'node:path'; import { tmpdir } from 'node:os'; import { spawn } from 'node:child_process'; import { createHash } from 'node:crypto'; import { reserveOrg, activateOrg, resolveOrg, orgRoot, withRegistryWriter } from '../src/org-registry.mjs'; function fixture() { const root = mkdtempSync(join(tmpdir(), 'org-')); const configDir = join(root, 'config'); const dataRoot = join(root, 'data'); mkdirSync(configDir, { mode: 0o700 }); mkdirSync(dataRoot, { mode: 0o700 }); return { configDir, dataRoot, instance: 'test' }; } test('reserves and verifies an organization marker', async () => { const c = fixture(); await reserveOrg(c, 'alpha'); await activateOrg(c, 'alpha'); assert.equal((await resolveOrg(c, 'alpha')).root, orgRoot(c.dataRoot, 'alpha')); writeFileSync(join(orgRoot(c.dataRoot, 'alpha'), '.mosaic-org.json'), '{"instance":"other","id":"alpha"}'); await assert.rejects(resolveOrg(c, 'alpha'), { code: 'org-mismatch' }); }); test('activation records the digest of an existing organization document', async () => { const c = fixture(), dir = join(c.configDir, 'businesses'), raw = '{"id":"alpha"}\n'; mkdirSync(dir, { mode: 0o700 }); writeFileSync(join(dir, 'alpha.json'), raw); await reserveOrg(c, 'alpha'); const active = await activateOrg(c, 'alpha'); assert.equal(active.digest, createHash('sha256').update(raw).digest('hex')); }); test('activation refuses a symlinked organization document', async () => { const c = fixture(), dir = join(c.configDir, 'businesses'); mkdirSync(dir, { mode: 0o700 }); writeFileSync(join(dir, 'other.json'), '{}'); symlinkSync(join(dir, 'other.json'), join(dir, 'alpha.json')); await reserveOrg(c, 'alpha'); await assert.rejects(activateOrg(c, 'alpha')); assert.equal((await resolveOrg(c, 'alpha')).state, 'reserved'); }); test('the first registry reservation waits for a legacy writer already holding the lock', async () => { const c = fixture(); let release; const hold = new Promise(resolve => { release = resolve; }); const writer = withRegistryWriter(c.configDir, () => hold); let reserved = false; const reservation = reserveOrg(c, 'alpha').then(value => { reserved = true; return value; }); try { await new Promise(resolve => setTimeout(resolve, 80)); assert.equal(reserved, false); } finally { release(); await writer; await reservation; } }); test('refuses existing and symlink roots', async () => { const c = fixture(); mkdirSync(join(c.dataRoot, 'org-runtime'), { mode: 0o700 }); mkdirSync(orgRoot(c.dataRoot, 'alpha')); await assert.rejects(reserveOrg(c, 'alpha')); symlinkSync(orgRoot(c.dataRoot, 'alpha'), orgRoot(c.dataRoot, 'beta')); await assert.rejects(reserveOrg(c, 'beta')); }); test('two processes cannot reserve the same id', async () => { const c = fixture(); const url = new URL('../src/org-registry.mjs', import.meta.url).href; const code = `import {reserveOrg} from ${JSON.stringify(url)}; await reserveOrg(${JSON.stringify(c)}, 'alpha');`; const run = () => new Promise(resolve => { const p = spawn(process.execPath, ['--input-type=module', '-e', code], { stdio: 'ignore' }); p.on('close', resolve); }); const results = await Promise.all([run(), run()]); assert.deepEqual(results.sort(), [0, 1]); }); test('rejects a symlink in the data root path', async () => { const c = fixture(); symlinkSync(c.dataRoot, c.dataRoot + '-link'); c.dataRoot += '-link'; await assert.rejects(reserveOrg(c, 'alpha'), { code: 'org-mismatch' }); }); test('rejects an overlapping registry root', async () => { const c = fixture(); writeFileSync(join(c.configDir, 'org-runtime.json'), JSON.stringify({version: 1, instance: c.instance, orgs: { other: { id: 'other', root: join(c.dataRoot, 'org-runtime'), state: 'reserved', created: new Date().toISOString(), revision: 1 } } }), { mode: 0o600 }); await assert.rejects(reserveOrg(c, 'alpha'), { code: 'org-overlap' }); }); test('refuses loose ownership modes and a missing data root', async () => { const c = fixture(); const { chmodSync, rmSync } = await import('node:fs'); chmodSync(c.dataRoot, 0o755); await assert.rejects(reserveOrg(c, 'alpha'), { code: 'org-mismatch' }); chmodSync(c.dataRoot, 0o700); rmSync(c.dataRoot, { recursive: true }); await assert.rejects(reserveOrg(c, 'alpha')); });