103 new tests across 7 files: - setup/status: emitStatus output structure - setup/packages: loadPackageList (real files), loadAllPackageLists - setup/tailscale: maybeEnableTailscaleInJail enabled/disabled/pkg-fail paths - setup/bastille-helpers: bastille(), jailRoot, jailExists, resolveJailName - setup/hosts: syncLocalHosts with jail discovery and skip logic - src/upstream/classify: classifyUpstreamFiles, classificationHint, summarizeCommit - src/upstream/git: getUpstreamConfig, isGitRepo, listRemotes, readEnvFlag/writeEnvFlag, countCommitsBetween, listCommitHashes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --- Build: FAIL | Tests: pass — Tests 1493 passed (1493)
134 lines
4.9 KiB
TypeScript
134 lines
4.9 KiB
TypeScript
/**
|
|
* setup/tailscale.test.ts — maybeEnableTailscaleInJail unit tests.
|
|
*
|
|
* Run with: npx vitest run setup/tailscale.test.ts
|
|
*/
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Mocks
|
|
// ---------------------------------------------------------------------------
|
|
|
|
vi.mock('../src/env.js', () => ({
|
|
readEnvFile: vi.fn().mockReturnValue({}),
|
|
}));
|
|
|
|
vi.mock('../src/logger.js', () => ({
|
|
logger: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() },
|
|
}));
|
|
|
|
import { maybeEnableTailscaleInJail } from './tailscale.js';
|
|
import { readEnvFile } from '../src/env.js';
|
|
|
|
const mockReadEnvFile = vi.mocked(readEnvFile);
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Helpers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
function makeRunner(results: Record<string, { ok: boolean; output: string }> = {}) {
|
|
return vi.fn().mockImplementation((args: string[]) => {
|
|
const key = args[0];
|
|
return results[key] ?? { ok: true, output: '' };
|
|
});
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// maybeEnableTailscaleInJail
|
|
// ---------------------------------------------------------------------------
|
|
|
|
describe('maybeEnableTailscaleInJail — disabled', () => {
|
|
beforeEach(() => {
|
|
mockReadEnvFile.mockReset().mockReturnValue({ FEATURE_TAILSCALE: 'NO', TAILSCALE_AUTHKEY: '' });
|
|
delete process.env.FEATURE_TAILSCALE;
|
|
delete process.env.TAILSCALE_AUTHKEY;
|
|
});
|
|
|
|
it('does nothing when FEATURE_TAILSCALE is off', () => {
|
|
const runner = makeRunner();
|
|
maybeEnableTailscaleInJail(runner, 'test-jail', 'test-jail');
|
|
expect(runner).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('does nothing when FEATURE_TAILSCALE is not set', () => {
|
|
mockReadEnvFile.mockReturnValue({});
|
|
const runner = makeRunner();
|
|
maybeEnableTailscaleInJail(runner, 'test-jail', 'test-jail');
|
|
expect(runner).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('maybeEnableTailscaleInJail — enabled but no authkey', () => {
|
|
beforeEach(() => {
|
|
mockReadEnvFile.mockReset().mockReturnValue({ FEATURE_TAILSCALE: 'YES', TAILSCALE_AUTHKEY: '' });
|
|
delete process.env.TAILSCALE_AUTHKEY;
|
|
});
|
|
|
|
it('does nothing when auth key is missing', () => {
|
|
const runner = makeRunner();
|
|
maybeEnableTailscaleInJail(runner, 'test-jail', 'test-jail');
|
|
expect(runner).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('maybeEnableTailscaleInJail — enabled with authkey', () => {
|
|
beforeEach(() => {
|
|
mockReadEnvFile.mockReset().mockReturnValue({
|
|
FEATURE_TAILSCALE: 'YES',
|
|
TAILSCALE_AUTHKEY: 'tskey-abc123',
|
|
});
|
|
delete process.env.FEATURE_TAILSCALE;
|
|
delete process.env.TAILSCALE_AUTHKEY;
|
|
});
|
|
|
|
it('calls pkg install tailscale', () => {
|
|
const runner = makeRunner();
|
|
maybeEnableTailscaleInJail(runner, 'my-jail', 'my-jail');
|
|
const pkgCall = runner.mock.calls.find((c: string[][]) => c[0].includes('tailscale') && c[0][0] === 'pkg');
|
|
expect(pkgCall).toBeDefined();
|
|
});
|
|
|
|
it('calls tailscale up with the auth key', () => {
|
|
const runner = makeRunner();
|
|
maybeEnableTailscaleInJail(runner, 'my-jail', 'my-jail');
|
|
const upCall = runner.mock.calls.find((c: string[][]) =>
|
|
c[0].includes('tailscale') && c[0].includes('up'),
|
|
);
|
|
expect(upCall).toBeDefined();
|
|
expect(upCall![0]).toContain('tskey-abc123');
|
|
});
|
|
|
|
it('passes normalized hostname to tailscale up', () => {
|
|
const runner = makeRunner();
|
|
maybeEnableTailscaleInJail(runner, 'my-jail', 'My_Jail Host!');
|
|
const upCall = runner.mock.calls.find((c: string[][]) =>
|
|
c[0].includes('up'),
|
|
);
|
|
// normalizeHostname: lowercase, strip non [a-z0-9-]
|
|
expect(upCall![0]).toContain('myjailhost');
|
|
});
|
|
|
|
it('stops after pkg install fails (no tailscale up call)', () => {
|
|
const runner = makeRunner({ pkg: { ok: false, output: 'install failed' } });
|
|
maybeEnableTailscaleInJail(runner, 'my-jail', 'my-jail');
|
|
const upCall = runner.mock.calls.find((c: string[][]) => c[0].includes('up'));
|
|
expect(upCall).toBeUndefined();
|
|
});
|
|
|
|
it('uses process.env.TAILSCALE_AUTHKEY when set', () => {
|
|
process.env.TAILSCALE_AUTHKEY = 'env-override-key';
|
|
mockReadEnvFile.mockReturnValue({ FEATURE_TAILSCALE: 'YES', TAILSCALE_AUTHKEY: 'file-key' });
|
|
const runner = makeRunner();
|
|
maybeEnableTailscaleInJail(runner, 'my-jail', 'my-jail');
|
|
const upCall = runner.mock.calls.find((c: string[][]) => c[0].includes('up'));
|
|
expect(upCall![0]).toContain('env-override-key');
|
|
delete process.env.TAILSCALE_AUTHKEY;
|
|
});
|
|
|
|
it('falls back to jail name as hostname when hostnameHint is empty', () => {
|
|
const runner = makeRunner();
|
|
maybeEnableTailscaleInJail(runner, 'my-jail', '');
|
|
const upCall = runner.mock.calls.find((c: string[][]) => c[0].includes('up'));
|
|
expect(upCall![0]).toContain('my-jail');
|
|
});
|
|
});
|