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)
176 lines
6.3 KiB
TypeScript
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');
|
|
});
|
|
});
|