Files
buzz/beehive/test/codex-home.test.ts
2026-09-12 12:15:27 -04:00

69 lines
4.8 KiB
TypeScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { mkdtempSync, mkdirSync, realpathSync, rmSync, existsSync, readFileSync, writeFileSync, symlinkSync, statSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { codexHomePaths, prepareCodexHome } from '../src/codex-home.ts';
import { runtimeBindings } from '../src/settings-runtime.ts';
import { newKey, publicKey } from '../src/protocol.ts';
import type { Setup } from '../src/host.ts';
import type { Settings } from '../src/settings.ts';
function fixture(t: test.TestContext) {
const root = realpathSync(mkdtempSync(join(tmpdir(), 'beehive-codex-home-')));
t.after(() => rmSync(root, { recursive: true, force: true }));
return root;
}
const settings: Settings = { version: 1, revision: 1, agents: [], providers: [{ id: 'provider', name: 'Fixture', type: 'openai', endpoint: 'https://api.openai.com/v1', key: { service: 'beehive', account: 'provider:00000000-0000-0000-0000-000000000000' } }], runtimes: [{ id: 'runtime', name: 'Fixture', harness: 'codex', cli: '/fixture/codex', executable: '/fixture/codex-acp', providerId: 'provider', model: 'custom-model' }] };
test('base-slot Codex projection is pure, public-slot bound, stable, and preserves legacy paths', t => {
const root = fixture(t), agent = publicKey(newKey());
const base: Setup = { mode: 'fixture', host: publicKey(newKey()), ownerPublic: publicKey(newKey()), runner: '/fixture/runner', args: [], workspace: root, serviceHome: root, configDirectory: root };
// Explicit public slot identity works without an agent key read (v3/key-removed catalogs).
const first = runtimeBindings(base, settings, agent)['runtime:runtime']!;
assert.notEqual(first.serviceHome, root); assert.notEqual(first.configDirectory, first.serviceHome);
assert.equal(existsSync(first.serviceHome!), false);
assert.deepEqual(runtimeBindings(base, settings, agent)['runtime:runtime'], first);
const other = runtimeBindings(base, settings, publicKey(newKey()))['runtime:runtime']!;
assert.notEqual(other.serviceHome, first.serviceHome);
const next = runtimeBindings(base, { ...settings, runtimes: [{ ...settings.runtimes[0]!, id: 'next' }] }, agent)['runtime:next']!;
assert.notEqual(next.serviceHome, first.serviceHome);
const old = runtimeBindings({ ...base, serviceHome: join(root, 'legacy-home'), configDirectory: join(root, 'legacy-config') }, settings, agent)['runtime:runtime']!;
assert.equal(old.serviceHome, join(root, 'legacy-home')); assert.equal(old.configDirectory, join(root, 'legacy-config'));
assert.equal(old.codex!.managedHome, undefined);
assert.throws(() => runtimeBindings(base, settings), /public slot authority/);
});
test('managed directories retain state across preparation and never import unrelated HOME config', t => {
const parent = fixture(t), value = { parent, identity: 'a'.repeat(64) }, paths = codexHomePaths(value);
const unrelated = join(parent, 'config.toml'); writeFileSync(unrelated, 'do-not-read-or-retarget', { mode: 0o600 });
prepareCodexHome(value, paths.home, paths.config);
for (const path of [paths.root, paths.home, paths.config]) assert.equal(statSync(path).mode & 0o777, 0o700);
assert.equal(existsSync(join(paths.config, 'config.toml')), false);
writeFileSync(join(paths.config, 'retained-session'), 'resume-state', { mode: 0o600 });
prepareCodexHome(value, paths.home, paths.config);
assert.equal(readFileSync(join(paths.config, 'retained-session'), 'utf8'), 'resume-state');
assert.equal(readFileSync(unrelated, 'utf8'), 'do-not-read-or-retarget');
assert.throws(() => prepareCodexHome(value, parent, paths.config), /binding mismatch/);
});
test('unowned, symlinked or partial managed homes fail closed without adoption or deletion', t => {
const parent = fixture(t);
for (const [index, kind] of ['missing-marker', 'wrong-marker', 'symlink-root', 'symlink-config'].entries()) {
const value = { parent, identity: String(index).repeat(64) }, paths = codexHomePaths(value);
const unrelated = join(parent, `unrelated-${index}`); mkdirSync(unrelated, { mode: 0o700 });
writeFileSync(join(unrelated, 'sentinel'), 'unchanged');
if (kind === 'symlink-root') symlinkSync(unrelated, paths.root);
else if (kind === 'symlink-config') {
prepareCodexHome(value, paths.home, paths.config);
rmSync(paths.config, { recursive: true }); symlinkSync(unrelated, paths.config);
} else {
mkdirSync(paths.root, { mode: 0o700 });
if (kind === 'wrong-marker') writeFileSync(join(paths.root, 'beehive-owner.json'), '{}', { mode: 0o600 });
}
assert.throws(() => prepareCodexHome(value, paths.home, paths.config));
assert.equal(readFileSync(join(unrelated, 'sentinel'), 'utf8'), 'unchanged');
if (kind !== 'symlink-config') assert.equal(existsSync(paths.home), false);
}
});