UNPKG

@measey/mycoder-agent

Version:

Agent module for mycoder - an AI-powered software development assistant

182 lines 7.9 kB
import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { sleep } from '../../utils/sleep.js'; import { getMockToolContext } from '../getTools.test.js'; import { shellMessageTool, NodeSignals } from './shellMessage.js'; import { shellStartTool } from './shellStart.js'; const toolContext = getMockToolContext(); // Helper function to get shellId from shellStart result const getShellId = (result) => { if (result.mode === 'async') { return result.shellId; } throw new Error('Expected async mode result'); }; describe('shellMessageTool', () => { let testInstanceId = ''; beforeEach(() => { toolContext.shellTracker.processStates.clear(); }); afterEach(() => { for (const processState of toolContext.shellTracker.processStates.values()) { processState.process.kill(); } toolContext.shellTracker.processStates.clear(); }); it('should interact with a running process', async () => { // Start a test process - force async mode with timeout const startResult = await shellStartTool.execute({ command: 'cat', // cat will echo back input description: 'Test interactive process', timeout: 50, // Force async mode for interactive process }, toolContext); testInstanceId = getShellId(startResult); // Send input and get response const result = await shellMessageTool.execute({ shellId: testInstanceId, stdin: 'hello world', description: 'Test interaction', }, toolContext); // With 'cat', the input should be echoed back exactly expect(result.stdout).toBe('hello world'); expect(result.stderr).toBe(''); expect(result.completed).toBe(false); // Verify the instance ID is valid expect(toolContext.shellTracker.processStates.has(testInstanceId)).toBe(true); }); it('should handle nonexistent process', async () => { const result = await shellMessageTool.execute({ shellId: 'nonexistent-id', description: 'Test invalid process', }, toolContext); expect(result.error).toBeDefined(); expect(result.completed).toBe(false); }); it('should handle process completion', async () => { // Start a quick process - force async mode const startResult = await shellStartTool.execute({ command: 'echo "test" && sleep 0.1', description: 'Test completion', timeout: 0, // Force async mode }, toolContext); const shellId = getShellId(startResult); // Wait a moment for process to complete await sleep(150); const result = await shellMessageTool.execute({ shellId, description: 'Check completion', }, toolContext); expect(result.completed).toBe(true); // Process should still be in processStates even after completion expect(toolContext.shellTracker.processStates.has(shellId)).toBe(true); }); it('should handle SIGTERM signal correctly', async () => { // Start a long-running process const startResult = await shellStartTool.execute({ command: 'sleep 10', description: 'Test SIGTERM handling', timeout: 0, // Force async mode }, toolContext); const shellId = getShellId(startResult); const result = await shellMessageTool.execute({ shellId, signal: NodeSignals.SIGTERM, description: 'Send SIGTERM', }, toolContext); expect(result.signaled).toBe(true); await sleep(50); const result2 = await shellMessageTool.execute({ shellId, description: 'Check on status', }, toolContext); expect(result2.completed).toBe(true); expect(result2.error).toBeUndefined(); }); it('should handle signals on terminated process gracefully', async () => { // Start a process const startResult = await shellStartTool.execute({ command: 'sleep 1', description: 'Test signal handling on terminated process', timeout: 0, // Force async mode }, toolContext); const shellId = getShellId(startResult); // Try to send signal to completed process const result = await shellMessageTool.execute({ shellId, signal: NodeSignals.SIGTERM, description: 'Send signal to terminated process', }, toolContext); expect(result.signaled).toBe(true); expect(result.completed).toBe(true); }); it('should verify signaled flag after process termination', async () => { // Start a process const startResult = await shellStartTool.execute({ command: 'sleep 5', description: 'Test signal flag verification', timeout: 0, // Force async mode }, toolContext); const shellId = getShellId(startResult); // Send SIGTERM await shellMessageTool.execute({ shellId, signal: NodeSignals.SIGTERM, description: 'Send SIGTERM', }, toolContext); await sleep(50); // Check process state after signal const checkResult = await shellMessageTool.execute({ shellId, description: 'Check signal state', }, toolContext); expect(checkResult.signaled).toBe(true); expect(checkResult.completed).toBe(true); expect(toolContext.shellTracker.processStates.has(shellId)).toBe(true); }); it('should respect showStdIn and showStdout parameters', async () => { // Start a process with default visibility settings const startResult = await shellStartTool.execute({ command: 'cat', description: 'Test with stdin/stdout visibility', timeout: 50, // Force async mode }, toolContext); const shellId = getShellId(startResult); // Verify process state has default visibility settings const processState = toolContext.shellTracker.processStates.get(shellId); expect(processState?.showStdIn).toBe(false); expect(processState?.showStdout).toBe(false); // Send input with explicit visibility settings await shellMessageTool.execute({ shellId, stdin: 'test input', description: 'Test with explicit visibility settings', showStdIn: true, showStdout: true, }, toolContext); // Verify process state still exists expect(toolContext.shellTracker.processStates.has(shellId)).toBe(true); }); it('should inherit visibility settings from process state', async () => { // Start a process with explicit visibility settings const startResult = await shellStartTool.execute({ command: 'cat', description: 'Test with inherited visibility settings', timeout: 50, // Force async mode showStdIn: true, showStdout: true, }, toolContext); const shellId = getShellId(startResult); // Verify process state has the specified visibility settings const processState = toolContext.shellTracker.processStates.get(shellId); expect(processState?.showStdIn).toBe(true); expect(processState?.showStdout).toBe(true); // Send input without specifying visibility settings await shellMessageTool.execute({ shellId, stdin: 'test input', description: 'Test with inherited visibility settings', }, toolContext); // Verify process state still exists expect(toolContext.shellTracker.processStates.has(shellId)).toBe(true); }); }); //# sourceMappingURL=shellMessage.test.js.map