clawdie-ai/setup/bastille-helpers.test.ts
Operator & Codex f1dc7ea6df Drop stale jail and agent migration paths (Codex)
Remove completed controlplane agent-id migration, simplify jail-name resolution to current canonical names, and drop SUDO_UID ownership fallback from service setup.

---
Build: pass | Tests: pass — 2370 passed (704 files)
2026-05-10 21:30:17 +02:00

176 lines
6.3 KiB
TypeScript

/**
* setup/bastille-helpers.test.ts — jailRoot, jailExists, resolveJailName tests.
*
* bastille() is tested via mocked spawnSync. provisionJail is excluded
* (full provisioning flow, not a unit test target).
*
* Run with: npx vitest run setup/bastille-helpers.test.ts
*/
import { describe, it, expect, vi, beforeEach } from 'vitest';
// ---------------------------------------------------------------------------
// Mocks
// ---------------------------------------------------------------------------
const spawnSyncMock = vi.hoisted(() => vi.fn());
vi.mock('child_process', async () => {
const actual = await vi.importActual<typeof import('child_process')>('child_process');
return { ...actual, spawnSync: spawnSyncMock, execSync: vi.fn() };
});
vi.mock('../src/logger.js', () => ({
logger: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() },
}));
vi.mock('./packages.js', () => ({
loadPackageList: vi.fn().mockReturnValue([]),
mountPkgCacheInJail: vi.fn(),
}));
vi.mock('./tailscale.js', () => ({
maybeEnableTailscaleInJail: vi.fn(),
}));
vi.mock('../src/jail-schema.js', () => ({
loadJailRegistry: vi.fn().mockReturnValue({ bridge: 'warden0', jails: {} }),
resolveJailIp: vi.fn().mockReturnValue('10.0.0.10'),
}));
import { bastille, jailRoot, jailExists, resolveJailName } from './bastille-helpers.js';
// ---------------------------------------------------------------------------
// Sample bastille list output
// ---------------------------------------------------------------------------
const BASTILLE_LIST_RUNNING = [
' JID Name Boot Prio State Type IP Address Published Ports Release',
' 1 db-worker on 99 Up thick 10.0.0.211 - 15.0-RELEASE',
' 2 git-worker on 99 Up thick 10.0.0.212 - 15.0-RELEASE',
].join('\n');
const BASTILLE_LIST_EMPTY = 'JID Name Boot Prio State Type IP Published Ports Release';
function makeSpawnResult(stdout: string, status = 0) {
return { stdout, stderr: '', status, pid: 1, output: [null, stdout, ''], signal: null };
}
// ---------------------------------------------------------------------------
// bastille()
// ---------------------------------------------------------------------------
describe('bastille()', () => {
beforeEach(() => spawnSyncMock.mockReset());
it('returns ok=true when status is 0', () => {
spawnSyncMock.mockReturnValue(makeSpawnResult('', 0));
const result = bastille('list');
expect(result.ok).toBe(true);
});
it('returns ok=false when status is non-zero', () => {
spawnSyncMock.mockReturnValue(makeSpawnResult('', 1));
const result = bastille('list');
expect(result.ok).toBe(false);
});
it('returns trimmed combined stdout+stderr as output', () => {
spawnSyncMock.mockReturnValue(makeSpawnResult(' hello world ', 0));
const result = bastille('list');
expect(result.output).toBe('hello world');
});
it('passes all args to spawnSync', () => {
spawnSyncMock.mockReturnValue(makeSpawnResult('', 0));
bastille('create', '-T', 'my-jail', '15.0-RELEASE');
expect(spawnSyncMock).toHaveBeenCalledWith(
'bastille',
['create', '-T', 'my-jail', '15.0-RELEASE'],
expect.any(Object),
);
});
});
// ---------------------------------------------------------------------------
// jailRoot()
// ---------------------------------------------------------------------------
describe('jailRoot()', () => {
it('returns the expected bastille jail root path', () => {
expect(jailRoot('my-jail')).toBe('/usr/local/bastille/jails/my-jail/root');
});
it('handles jail names with hyphens', () => {
expect(jailRoot('db-worker')).toBe('/usr/local/bastille/jails/db-worker/root');
});
});
// ---------------------------------------------------------------------------
// jailExists()
// ---------------------------------------------------------------------------
describe('jailExists()', () => {
beforeEach(() => spawnSyncMock.mockReset());
it('returns true when jail is in the list', () => {
spawnSyncMock.mockReturnValue(makeSpawnResult(BASTILLE_LIST_RUNNING, 0));
expect(jailExists('db-worker')).toBe(true);
});
it('returns false when jail is not in the list', () => {
spawnSyncMock.mockReturnValue(makeSpawnResult(BASTILLE_LIST_RUNNING, 0));
expect(jailExists('nonexistent')).toBe(false);
});
it('returns false for empty bastille list output', () => {
spawnSyncMock.mockReturnValue(makeSpawnResult(BASTILLE_LIST_EMPTY, 0));
expect(jailExists('db-worker')).toBe(false);
});
it('returns false for completely empty output', () => {
spawnSyncMock.mockReturnValue(makeSpawnResult('', 0));
expect(jailExists('db-worker')).toBe(false);
});
it('does not match partial name (db-worker vs db)', () => {
spawnSyncMock.mockReturnValue(makeSpawnResult(BASTILLE_LIST_RUNNING, 0));
expect(jailExists('db')).toBe(false);
});
});
// ---------------------------------------------------------------------------
// resolveJailName()
// ---------------------------------------------------------------------------
describe('resolveJailName()', () => {
beforeEach(() => spawnSyncMock.mockReset());
it('returns envOverride immediately without calling bastille', () => {
const result = resolveJailName({
role: 'db',
envOverride: 'custom-db-jail',
});
expect(result).toBe('custom-db-jail');
expect(spawnSyncMock).not.toHaveBeenCalled();
});
it('returns the role name when it exists', () => {
const list = BASTILLE_LIST_RUNNING + '\n 3 db on 99 Up thick 10.0.0.100 - 15.0-RELEASE';
spawnSyncMock.mockReturnValue(makeSpawnResult(list, 0));
const result = resolveJailName({ role: 'db' });
expect(result).toBe('db');
});
it('returns the role name when nothing is found', () => {
spawnSyncMock.mockReturnValue(makeSpawnResult(BASTILLE_LIST_RUNNING, 0));
const result = resolveJailName({ role: 'newrole' });
expect(result).toBe('newrole');
});
it('uses explicit candidate names when supplied', () => {
const list = BASTILLE_LIST_RUNNING + '\n 3 cms on 99 Up thick 10.0.0.100 - 15.0-RELEASE';
spawnSyncMock.mockReturnValue(makeSpawnResult(list, 0));
const result = resolveJailName({ role: 'web', names: ['web', 'cms'] });
expect(result).toBe('cms');
});
});