import { expect, tap } from '@git.zone/tstest/tapbundle'; import * as tspm from '../ts/index.js'; import * as path from 'path'; import * as fs from 'fs/promises'; import { TspmIpcClient } from '../ts/client/tspm.ipcclient.js'; import * as os from 'os'; // Test IPC client functionality tap.test('TspmIpcClient creation', async () => { const client = new TspmIpcClient(); expect(client).toBeInstanceOf(TspmIpcClient); }); tap.test('IPC client socket path', async () => { const client = new TspmIpcClient(); const socketPath = (client as any).socketPath; expect(socketPath).toInclude('.tspm'); expect(socketPath).toInclude('tspm.sock'); }); tap.test('IPC client daemon PID file path', async () => { const client = new TspmIpcClient(); const daemonPidFile = (client as any).daemonPidFile; expect(daemonPidFile).toInclude('.tspm'); expect(daemonPidFile).toInclude('daemon.pid'); }); tap.test('IPC client connection state', async () => { const client = new TspmIpcClient(); const isConnected = (client as any).isConnected; expect(isConnected).toEqual(false); // Should be false initially }); tap.test('IPC client daemon running check - no daemon', async () => { const client = new TspmIpcClient(); const tspmDir = path.join(os.homedir(), '.tspm'); const pidFile = path.join(tspmDir, 'daemon.pid'); // Ensure no PID file exists for this test await fs.unlink(pidFile).catch(() => {}); const isRunning = await (client as any).isDaemonRunning(); expect(isRunning).toEqual(false); }); tap.test('IPC client daemon running check - stale PID', async () => { const client = new TspmIpcClient(); const tspmDir = path.join(os.homedir(), '.tspm'); const pidFile = path.join(tspmDir, 'daemon.pid'); // Create directory if it doesn't exist await fs.mkdir(tspmDir, { recursive: true }); // Write a fake PID that doesn't exist await fs.writeFile(pidFile, '99999999'); const isRunning = await (client as any).isDaemonRunning(); expect(isRunning).toEqual(false); // Clean up - the stale PID should be removed const fileExists = await fs .access(pidFile) .then(() => true) .catch(() => false); expect(fileExists).toEqual(false); }); tap.test('IPC client daemon running check - current process', async () => { const client = new TspmIpcClient(); const tspmDir = path.join(os.homedir(), '.tspm'); const pidFile = path.join(tspmDir, 'daemon.pid'); const socketFile = path.join(tspmDir, 'tspm.sock'); // Create directory if it doesn't exist await fs.mkdir(tspmDir, { recursive: true }); // Write current process PID (simulating daemon is this process) await fs.writeFile(pidFile, process.pid.toString()); // Create a fake socket file await fs.writeFile(socketFile, ''); const isRunning = await (client as any).isDaemonRunning(); expect(isRunning).toEqual(true); // Clean up await fs.unlink(pidFile).catch(() => {}); await fs.unlink(socketFile).catch(() => {}); }); tap.test('IPC client singleton instance', async () => { // Import the singleton const { tspmIpcClient } = await import('../ts/client/tspm.ipcclient.js'); expect(tspmIpcClient).toBeInstanceOf(TspmIpcClient); // Test that it's the same instance const { tspmIpcClient: secondImport } = await import( '../ts/client/tspm.ipcclient.js' ); expect(tspmIpcClient).toEqual(secondImport); }); tap.test('IPC client request method type safety', async () => { const client = new TspmIpcClient(); // Test that request method exists expect(client.request).toBeInstanceOf(Function); expect(client.connect).toBeInstanceOf(Function); expect(client.disconnect).toBeInstanceOf(Function); expect(client.stopDaemon).toBeInstanceOf(Function); expect(client.getDaemonStatus).toBeInstanceOf(Function); }); tap.test('IPC client error message formatting', async () => { const errorMessage = 'Could not connect to TSPM daemon. Please try running "tspm daemon start" manually.'; expect(errorMessage).toInclude('tspm daemon start'); }); tap.test('IPC client reconnection logic', async () => { const client = new TspmIpcClient(); // Test reconnection error conditions const econnrefusedError = new Error('ECONNREFUSED'); expect(econnrefusedError.message).toInclude('ECONNREFUSED'); const enoentError = new Error('ENOENT'); expect(enoentError.message).toInclude('ENOENT'); }); tap.test('IPC client daemon start timeout', async () => { const maxWaitTime = 10000; // 10 seconds const checkInterval = 500; // 500ms const maxChecks = maxWaitTime / checkInterval; expect(maxChecks).toEqual(20); }); tap.test('IPC client daemon stop timeout', async () => { const maxWaitTime = 15000; // 15 seconds const checkInterval = 500; // 500ms const maxChecks = maxWaitTime / checkInterval; expect(maxChecks).toEqual(30); }); export default tap.start();