fix(daemon): Fix smartipc integration and add daemon/ipc integration tests
This commit is contained in:
266
test/test.integration.ts
Normal file
266
test/test.integration.ts
Normal file
@@ -0,0 +1,266 @@
|
||||
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 * as os from 'os';
|
||||
import { spawn } from 'child_process';
|
||||
import { tspmIpcClient } from '../ts/classes.ipcclient.js';
|
||||
|
||||
// Helper to ensure daemon is stopped before tests
|
||||
async function ensureDaemonStopped() {
|
||||
try {
|
||||
await tspmIpcClient.stopDaemon(false);
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
} catch (error) {
|
||||
// Ignore errors if daemon is not running
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to clean up test files
|
||||
async function cleanupTestFiles() {
|
||||
const tspmDir = path.join(os.homedir(), '.tspm');
|
||||
const pidFile = path.join(tspmDir, 'daemon.pid');
|
||||
const socketFile = path.join(tspmDir, 'tspm.sock');
|
||||
|
||||
await fs.unlink(pidFile).catch(() => {});
|
||||
await fs.unlink(socketFile).catch(() => {});
|
||||
}
|
||||
|
||||
// Integration tests for daemon-client communication
|
||||
tap.test('Full daemon lifecycle test', async (tools) => {
|
||||
const done = tools.defer();
|
||||
|
||||
// Ensure clean state
|
||||
await ensureDaemonStopped();
|
||||
await cleanupTestFiles();
|
||||
|
||||
// Test 1: Check daemon is not running
|
||||
let status = await tspmIpcClient.getDaemonStatus();
|
||||
expect(status).toEqual(null);
|
||||
|
||||
// Test 2: Start daemon
|
||||
console.log('Starting daemon...');
|
||||
await tspmIpcClient.connect();
|
||||
|
||||
// Give daemon time to fully initialize
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
// Test 3: Check daemon is running
|
||||
status = await tspmIpcClient.getDaemonStatus();
|
||||
expect(status).toBeDefined();
|
||||
expect(status?.status).toEqual('running');
|
||||
expect(status?.pid).toBeGreaterThan(0);
|
||||
expect(status?.processCount).toBeGreaterThanOrEqual(0);
|
||||
|
||||
// Test 4: Stop daemon
|
||||
console.log('Stopping daemon...');
|
||||
await tspmIpcClient.stopDaemon(true);
|
||||
|
||||
// Give daemon time to shutdown
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
// Test 5: Check daemon is stopped
|
||||
status = await tspmIpcClient.getDaemonStatus();
|
||||
expect(status).toEqual(null);
|
||||
|
||||
done.resolve();
|
||||
});
|
||||
|
||||
tap.test('Process management through daemon', async (tools) => {
|
||||
const done = tools.defer();
|
||||
|
||||
// Ensure daemon is running
|
||||
await tspmIpcClient.connect();
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// Test 1: List processes (should be empty initially)
|
||||
let listResponse = await tspmIpcClient.request('list', {});
|
||||
expect(listResponse.processes).toBeArray();
|
||||
expect(listResponse.processes.length).toEqual(0);
|
||||
|
||||
// Test 2: Start a test process
|
||||
const testConfig: tspm.IProcessConfig = {
|
||||
id: 'test-echo',
|
||||
name: 'Test Echo Process',
|
||||
command: 'echo "Test process"',
|
||||
projectDir: process.cwd(),
|
||||
memoryLimitBytes: 50 * 1024 * 1024,
|
||||
autorestart: false,
|
||||
};
|
||||
|
||||
const startResponse = await tspmIpcClient.request('start', { config: testConfig });
|
||||
expect(startResponse.processId).toEqual('test-echo');
|
||||
expect(startResponse.status).toBeDefined();
|
||||
|
||||
// Test 3: List processes (should have one process)
|
||||
listResponse = await tspmIpcClient.request('list', {});
|
||||
expect(listResponse.processes.length).toBeGreaterThanOrEqual(1);
|
||||
|
||||
const process = listResponse.processes.find(p => p.id === 'test-echo');
|
||||
expect(process).toBeDefined();
|
||||
expect(process?.id).toEqual('test-echo');
|
||||
|
||||
// Test 4: Describe the process
|
||||
const describeResponse = await tspmIpcClient.request('describe', { id: 'test-echo' });
|
||||
expect(describeResponse.processInfo).toBeDefined();
|
||||
expect(describeResponse.config).toBeDefined();
|
||||
expect(describeResponse.config.id).toEqual('test-echo');
|
||||
|
||||
// Test 5: Stop the process
|
||||
const stopResponse = await tspmIpcClient.request('stop', { id: 'test-echo' });
|
||||
expect(stopResponse.success).toEqual(true);
|
||||
expect(stopResponse.message).toInclude('stopped successfully');
|
||||
|
||||
// Test 6: Delete the process
|
||||
const deleteResponse = await tspmIpcClient.request('delete', { id: 'test-echo' });
|
||||
expect(deleteResponse.success).toEqual(true);
|
||||
|
||||
// Test 7: Verify process is gone
|
||||
listResponse = await tspmIpcClient.request('list', {});
|
||||
const deletedProcess = listResponse.processes.find(p => p.id === 'test-echo');
|
||||
expect(deletedProcess).toBeUndefined();
|
||||
|
||||
// Cleanup: stop daemon
|
||||
await tspmIpcClient.stopDaemon(true);
|
||||
|
||||
done.resolve();
|
||||
});
|
||||
|
||||
tap.test('Batch operations through daemon', async (tools) => {
|
||||
const done = tools.defer();
|
||||
|
||||
// Ensure daemon is running
|
||||
await tspmIpcClient.connect();
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// Add multiple test processes
|
||||
const testConfigs: tspm.IProcessConfig[] = [
|
||||
{
|
||||
id: 'batch-test-1',
|
||||
name: 'Batch Test 1',
|
||||
command: 'echo "Process 1"',
|
||||
projectDir: process.cwd(),
|
||||
memoryLimitBytes: 50 * 1024 * 1024,
|
||||
autorestart: false,
|
||||
},
|
||||
{
|
||||
id: 'batch-test-2',
|
||||
name: 'Batch Test 2',
|
||||
command: 'echo "Process 2"',
|
||||
projectDir: process.cwd(),
|
||||
memoryLimitBytes: 50 * 1024 * 1024,
|
||||
autorestart: false,
|
||||
},
|
||||
];
|
||||
|
||||
// Start processes
|
||||
for (const config of testConfigs) {
|
||||
await tspmIpcClient.request('start', { config });
|
||||
}
|
||||
|
||||
// Test 1: Stop all processes
|
||||
const stopAllResponse = await tspmIpcClient.request('stopAll', {});
|
||||
expect(stopAllResponse.stopped).toBeArray();
|
||||
expect(stopAllResponse.stopped.length).toBeGreaterThanOrEqual(2);
|
||||
|
||||
// Test 2: Start all processes
|
||||
const startAllResponse = await tspmIpcClient.request('startAll', {});
|
||||
expect(startAllResponse.started).toBeArray();
|
||||
|
||||
// Test 3: Restart all processes
|
||||
const restartAllResponse = await tspmIpcClient.request('restartAll', {});
|
||||
expect(restartAllResponse.restarted).toBeArray();
|
||||
|
||||
// Cleanup: delete all test processes
|
||||
for (const config of testConfigs) {
|
||||
await tspmIpcClient.request('delete', { id: config.id }).catch(() => {});
|
||||
}
|
||||
|
||||
// Stop daemon
|
||||
await tspmIpcClient.stopDaemon(true);
|
||||
|
||||
done.resolve();
|
||||
});
|
||||
|
||||
tap.test('Daemon error handling', async (tools) => {
|
||||
const done = tools.defer();
|
||||
|
||||
// Ensure daemon is running
|
||||
await tspmIpcClient.connect();
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// Test 1: Try to stop non-existent process
|
||||
try {
|
||||
await tspmIpcClient.request('stop', { id: 'non-existent-process' });
|
||||
expect(false).toEqual(true); // Should not reach here
|
||||
} catch (error) {
|
||||
expect(error.message).toInclude('Failed to stop process');
|
||||
}
|
||||
|
||||
// Test 2: Try to describe non-existent process
|
||||
try {
|
||||
await tspmIpcClient.request('describe', { id: 'non-existent-process' });
|
||||
expect(false).toEqual(true); // Should not reach here
|
||||
} catch (error) {
|
||||
expect(error.message).toInclude('not found');
|
||||
}
|
||||
|
||||
// Test 3: Try to restart non-existent process
|
||||
try {
|
||||
await tspmIpcClient.request('restart', { id: 'non-existent-process' });
|
||||
expect(false).toEqual(true); // Should not reach here
|
||||
} catch (error) {
|
||||
expect(error.message).toInclude('Failed to restart process');
|
||||
}
|
||||
|
||||
// Stop daemon
|
||||
await tspmIpcClient.stopDaemon(true);
|
||||
|
||||
done.resolve();
|
||||
});
|
||||
|
||||
tap.test('Daemon heartbeat functionality', async (tools) => {
|
||||
const done = tools.defer();
|
||||
|
||||
// Ensure daemon is running
|
||||
await tspmIpcClient.connect();
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// Test heartbeat
|
||||
const heartbeatResponse = await tspmIpcClient.request('heartbeat', {});
|
||||
expect(heartbeatResponse.timestamp).toBeGreaterThan(0);
|
||||
expect(heartbeatResponse.status).toEqual('healthy');
|
||||
|
||||
// Stop daemon
|
||||
await tspmIpcClient.stopDaemon(true);
|
||||
|
||||
done.resolve();
|
||||
});
|
||||
|
||||
tap.test('Daemon memory and CPU reporting', async (tools) => {
|
||||
const done = tools.defer();
|
||||
|
||||
// Ensure daemon is running
|
||||
await tspmIpcClient.connect();
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// Get daemon status
|
||||
const status = await tspmIpcClient.getDaemonStatus();
|
||||
expect(status).toBeDefined();
|
||||
expect(status?.memoryUsage).toBeGreaterThan(0);
|
||||
expect(status?.cpuUsage).toBeGreaterThanOrEqual(0);
|
||||
expect(status?.uptime).toBeGreaterThan(0);
|
||||
|
||||
// Stop daemon
|
||||
await tspmIpcClient.stopDaemon(true);
|
||||
|
||||
done.resolve();
|
||||
});
|
||||
|
||||
// Cleanup after all tests
|
||||
tap.test('Final cleanup', async () => {
|
||||
await ensureDaemonStopped();
|
||||
await cleanupTestFiles();
|
||||
});
|
||||
|
||||
export default tap.start();
|
Reference in New Issue
Block a user