@measey/mycoder-agent
Version:
Agent module for mycoder - an AI-powered software development assistant
134 lines • 5.51 kB
JavaScript
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { shellStartTool } from './shellStart';
import { ShellStatus, ShellTracker } from './ShellTracker';
// Track the process 'on' handlers
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
let processOnHandlers = {};
// Create a mock process
const mockProcess = {
on: vi.fn((event, handler) => {
processOnHandlers[event] = handler;
return mockProcess;
}),
stdout: {
on: vi.fn().mockReturnThis(),
},
stderr: {
on: vi.fn().mockReturnThis(),
},
stdin: {
write: vi.fn(),
writable: true,
},
};
// Mock child_process.spawn
vi.mock('child_process', () => ({
spawn: vi.fn(() => mockProcess),
}));
// Mock uuid
vi.mock('uuid', () => ({
v4: vi.fn(() => 'mock-uuid'),
}));
describe('shellStartTool sync execution', () => {
const mockLogger = {
log: vi.fn(),
debug: vi.fn(),
error: vi.fn(),
warn: vi.fn(),
info: vi.fn(),
};
const shellTracker = new ShellTracker('test-agent');
// Create a mock ToolContext with all required properties
const mockToolContext = {
logger: mockLogger,
workingDirectory: '/test',
headless: false,
userSession: false,
tokenTracker: { trackTokens: vi.fn() },
githubMode: false,
provider: 'anthropic',
maxTokens: 4000,
temperature: 0,
agentTracker: { registerAgent: vi.fn() },
shellTracker: shellTracker,
browserTracker: { registerSession: vi.fn() },
};
beforeEach(() => {
vi.clearAllMocks();
shellTracker['shells'] = new Map();
shellTracker.processStates.clear();
processOnHandlers = {};
});
afterEach(() => {
vi.resetAllMocks();
});
it('should mark a quickly completed process as COMPLETED in sync mode', async () => {
// Start executing the command but don't await it yet
const resultPromise = shellStartTool.execute({
command: 'echo "test"',
description: 'Testing sync completion',
timeout: 5000, // Use a longer timeout to ensure we're testing sync mode
}, mockToolContext);
// Verify the shell was registered as running
expect(shellTracker.getShells(ShellStatus.RUNNING).length).toBe(1);
// Simulate the process completing successfully
processOnHandlers['exit']?.(0, null);
// Now await the result
const result = await resultPromise;
// Verify we got a sync response
expect(result.mode).toBe('sync');
// Verify the shell status was updated to COMPLETED
const completedShells = shellTracker.getShells(ShellStatus.COMPLETED);
expect(completedShells.length).toBe(1);
expect(completedShells?.[0]?.shellId).toBe('mock-uuid');
// Verify no shells are left in RUNNING state
expect(shellTracker.getShells(ShellStatus.RUNNING).length).toBe(0);
});
it('should mark a process that exits with non-zero code as ERROR in sync mode', async () => {
// Start executing the command but don't await it yet
const resultPromise = shellStartTool.execute({
command: 'some-failing-command',
description: 'Testing sync error handling',
timeout: 5000,
}, mockToolContext);
// Verify the shell was registered as running
expect(shellTracker.getShells(ShellStatus.RUNNING).length).toBe(1);
// Simulate the process failing with a non-zero exit code
processOnHandlers['exit']?.(1, null);
// Now await the result
const result = await resultPromise;
// Verify we got a sync response with error
expect(result.mode).toBe('sync');
expect(result['exitCode']).toBe(1);
// Verify the shell status was updated to ERROR
const errorShells = shellTracker.getShells(ShellStatus.ERROR);
expect(errorShells.length).toBe(1);
expect(errorShells?.[0]?.shellId).toBe('mock-uuid');
// Verify no shells are left in RUNNING state
expect(shellTracker.getShells(ShellStatus.RUNNING).length).toBe(0);
});
it('should mark a process with an error event as ERROR in sync mode', async () => {
// Start executing the command but don't await it yet
const resultPromise = shellStartTool.execute({
command: 'command-that-errors',
description: 'Testing sync error event handling',
timeout: 5000,
}, mockToolContext);
// Verify the shell was registered as running
expect(shellTracker.getShells(ShellStatus.RUNNING).length).toBe(1);
// Simulate an error event
processOnHandlers['error']?.(new Error('Test error'));
// Now await the result
const result = await resultPromise;
// Verify we got a sync response with error info
expect(result.mode).toBe('async'); // Error events always use async mode
expect(result.error).toBe('Test error');
// Verify the shell status was updated to ERROR
const errorShells = shellTracker.getShells(ShellStatus.ERROR);
expect(errorShells.length).toBe(1);
expect(errorShells?.[0]?.shellId).toBe('mock-uuid');
// Verify no shells are left in RUNNING state
expect(shellTracker.getShells(ShellStatus.RUNNING).length).toBe(0);
});
});
//# sourceMappingURL=shellSync.test.js.map