124 lines
4.2 KiB
TypeScript
124 lines
4.2 KiB
TypeScript
import { describe, it, expect, vi, beforeAll, beforeEach } from 'vitest';
|
|
import { execFile } from 'child_process';
|
|
|
|
// Mock child_process before importing module
|
|
vi.mock('child_process', () => ({
|
|
execFile: vi.fn(),
|
|
}));
|
|
|
|
const mockExecFile = vi.mocked(execFile);
|
|
|
|
describe('tailscale', () => {
|
|
// Import after mocking
|
|
let isTailscaleAvailable: typeof import('./tailscale.js').isTailscaleAvailable;
|
|
let startTailscaleServe: typeof import('./tailscale.js').startTailscaleServe;
|
|
let stopTailscaleServe: typeof import('./tailscale.js').stopTailscaleServe;
|
|
|
|
beforeAll(async () => {
|
|
const mod = await import('./tailscale.js');
|
|
isTailscaleAvailable = mod.isTailscaleAvailable;
|
|
startTailscaleServe = mod.startTailscaleServe;
|
|
stopTailscaleServe = mod.stopTailscaleServe;
|
|
});
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
describe('isTailscaleAvailable', () => {
|
|
it('returns available when tailscale CLI works', async () => {
|
|
mockExecFile
|
|
.mockImplementationOnce((_cmd, _args, _opts, callback: any) => {
|
|
callback(null, '1.62.0', '');
|
|
return {} as any;
|
|
})
|
|
.mockImplementationOnce((_cmd, _args, _opts, callback: any) => {
|
|
callback(null, '{}', '');
|
|
return {} as any;
|
|
});
|
|
|
|
const result = await isTailscaleAvailable();
|
|
expect(result.available).toBe(true);
|
|
expect(result.version).toBe('1.62.0');
|
|
});
|
|
|
|
it('returns unavailable when tailscale CLI fails', async () => {
|
|
mockExecFile.mockImplementationOnce((_cmd, _args, _opts, callback: any) => {
|
|
callback(new Error('command not found'), '', 'command not found');
|
|
return {} as any;
|
|
});
|
|
|
|
const result = await isTailscaleAvailable();
|
|
expect(result.available).toBe(false);
|
|
expect(result.error).toBeDefined();
|
|
});
|
|
});
|
|
|
|
describe('startTailscaleServe', () => {
|
|
it('calls tailscale serve with correct args', async () => {
|
|
mockExecFile
|
|
// serve command
|
|
.mockImplementationOnce((_cmd, _args, _opts, callback: any) => {
|
|
callback(null, '', '');
|
|
return {} as any;
|
|
})
|
|
// status for hostname
|
|
.mockImplementationOnce((_cmd, _args, _opts, callback: any) => {
|
|
callback(null, JSON.stringify({ Self: { DNSName: 'myhost.tailnet.ts.net.' } }), '');
|
|
return {} as any;
|
|
});
|
|
|
|
const url = await startTailscaleServe({ localPort: 18800 });
|
|
expect(url).toBe('https://myhost.tailnet.ts.net');
|
|
|
|
const serveCall = mockExecFile.mock.calls[0];
|
|
expect(serveCall[0]).toBe('tailscale');
|
|
expect(serveCall[1]).toEqual(['serve', '--bg', '--https=443', 'http://127.0.0.1:18800']);
|
|
});
|
|
|
|
it('uses custom serve port', async () => {
|
|
mockExecFile
|
|
.mockImplementationOnce((_cmd, _args, _opts, callback: any) => {
|
|
callback(null, '', '');
|
|
return {} as any;
|
|
})
|
|
.mockImplementationOnce((_cmd, _args, _opts, callback: any) => {
|
|
callback(null, JSON.stringify({ Self: { DNSName: 'myhost.tailnet.ts.net.' } }), '');
|
|
return {} as any;
|
|
});
|
|
|
|
const url = await startTailscaleServe({ localPort: 18800, servePort: 8443 });
|
|
expect(url).toBe('https://myhost.tailnet.ts.net:8443');
|
|
|
|
const serveCall = mockExecFile.mock.calls[0];
|
|
expect(serveCall[1]).toEqual(['serve', '--bg', '--https=8443', 'http://127.0.0.1:18800']);
|
|
});
|
|
});
|
|
|
|
describe('stopTailscaleServe', () => {
|
|
it('calls tailscale serve off', async () => {
|
|
mockExecFile.mockImplementationOnce((_cmd, _args, _opts, callback: any) => {
|
|
callback(null, '', '');
|
|
return {} as any;
|
|
});
|
|
|
|
await stopTailscaleServe({ localPort: 18800 });
|
|
|
|
expect(mockExecFile).toHaveBeenCalledOnce();
|
|
const call = mockExecFile.mock.calls[0];
|
|
expect(call[0]).toBe('tailscale');
|
|
expect(call[1]).toEqual(['serve', '--https=443', 'off']);
|
|
});
|
|
|
|
it('does not throw on failure', async () => {
|
|
mockExecFile.mockImplementationOnce((_cmd, _args, _opts, callback: any) => {
|
|
callback(new Error('failed'), '', 'failed');
|
|
return {} as any;
|
|
});
|
|
|
|
// Should not throw
|
|
await stopTailscaleServe({ localPort: 18800 });
|
|
});
|
|
});
|
|
});
|