godcrm/backend/services/__tests__/CodeToolsService.test.js
GOD CRM Release f89e074dd1
Some checks failed
CI / Lint / Typecheck / Test / Build (push) Has been cancelled
CI / PostgreSQL Integration Tests (push) Has been cancelled
GOD CRM — public scrubbed snapshot
Governed substrate for autonomous agents: scoped identity (passports),
audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
2026-08-10 04:01:45 +03:00

278 lines
7.9 KiB
JavaScript

/**
* AI Agent Code Tools Tests
* ADR-032: Code Execution Engine — Judge0 + Agent Loop
*
* BEHAVIOR: AI Agent can execute code using tools
*
* Tests for:
* - run_code tool
* - validate_code tool
* - run_code_loop tool
*/
import { describe, it, expect, vi, beforeEach } from 'vitest';
// Mock fetch globally
global.fetch = vi.fn();
// Import after mocking
import {
runCodeTool,
runCodeLoopTool,
validateCodeTool,
CODE_TOOLS
} from '../CodeToolsService.js';
describe('AI Agent Code Tools - ADR-032', () => {
beforeEach(() => {
vi.clearAllMocks();
process.env.BACKEND_URL = 'http://localhost:5001';
});
describe('Tool Definitions', () => {
it('should export CODE_TOOLS array with all code tools', () => {
expect(Array.isArray(CODE_TOOLS)).toBe(true);
expect(CODE_TOOLS.length).toBeGreaterThanOrEqual(3);
const toolNames = CODE_TOOLS.map(t => t.function.name);
expect(toolNames).toContain('run_code');
expect(toolNames).toContain('validate_code');
expect(toolNames).toContain('run_code_loop');
});
it('run_code tool should have correct parameters', () => {
const tool = CODE_TOOLS.find(t => t.function.name === 'run_code');
expect(tool.type).toBe('function');
expect(tool.function.parameters.properties).toHaveProperty('source_code');
expect(tool.function.parameters.properties).toHaveProperty('language');
expect(tool.function.parameters.required).toContain('source_code');
});
it('validate_code tool should have correct parameters', () => {
const tool = CODE_TOOLS.find(t => t.function.name === 'validate_code');
expect(tool.type).toBe('function');
expect(tool.function.parameters.properties).toHaveProperty('source_code');
expect(tool.function.parameters.properties).toHaveProperty('language');
expect(tool.function.parameters.required).toContain('source_code');
});
it('run_code_loop tool should have callback parameters', () => {
const tool = CODE_TOOLS.find(t => t.function.name === 'run_code_loop');
expect(tool.type).toBe('function');
expect(tool.function.parameters.properties).toHaveProperty('source_code');
expect(tool.function.parameters.properties).toHaveProperty('next_prompt');
});
});
describe('runCodeTool.execute()', () => {
it('should execute Python code and return result', async () => {
// Arrange
global.fetch.mockResolvedValueOnce({
json: () => Promise.resolve({
success: true,
stdout: 'Hello, World!\n',
stderr: '',
status: 'Accepted',
time: '0.01',
memory: 3200
})
});
// Act
const result = await runCodeTool.execute({
source_code: 'print("Hello, World!")',
language: 'python'
});
// Assert
expect(result.success).toBe(true);
expect(result.stdout).toBe('Hello, World!\n');
expect(fetch).toHaveBeenCalledWith(
'http://localhost:5001/api/v3/code/execute',
expect.objectContaining({
method: 'POST'
})
);
// Verify body contains the code
const callArgs = fetch.mock.calls[0][1];
const body = JSON.parse(callArgs.body);
expect(body.source_code).toBe('print("Hello, World!")');
expect(body.language).toBe('python');
});
it('should pass stdin to code execution', async () => {
// Arrange
global.fetch.mockResolvedValueOnce({
json: () => Promise.resolve({
success: true,
stdout: 'Alice\n',
stderr: '',
status: 'Accepted'
})
});
// Act
await runCodeTool.execute({
source_code: 'print(input())',
language: 'python',
stdin: 'Alice'
});
// Assert
expect(fetch).toHaveBeenCalledWith(
expect.any(String),
expect.objectContaining({
body: expect.stringContaining('"stdin":"Alice"')
})
);
});
it('should return error for failed execution', async () => {
// Arrange
global.fetch.mockResolvedValueOnce({
json: () => Promise.resolve({
success: false,
stdout: '',
stderr: 'ZeroDivisionError: division by zero',
status: 'Runtime Error'
})
});
// Act
const result = await runCodeTool.execute({
source_code: 'print(1/0)',
language: 'python'
});
// Assert
expect(result.success).toBe(false);
expect(result.stderr).toContain('ZeroDivisionError');
});
it('should support multiple languages', async () => {
const languages = ['javascript', 'go', 'rust', 'ruby'];
for (const language of languages) {
global.fetch.mockResolvedValueOnce({
json: () => Promise.resolve({ success: true, stdout: 'ok' })
});
await runCodeTool.execute({
source_code: 'console.log("test")',
language
});
expect(fetch).toHaveBeenLastCalledWith(
expect.any(String),
expect.objectContaining({
body: expect.stringContaining(`"language":"${language}"`)
})
);
}
});
});
describe('validateCodeTool.execute()', () => {
it('should validate correct Python syntax', async () => {
// Arrange
global.fetch.mockResolvedValueOnce({
json: () => Promise.resolve({
valid: true,
errors: []
})
});
// Act
const result = await validateCodeTool.execute({
source_code: 'def hello(): return "world"',
language: 'python'
});
// Assert
expect(result.valid).toBe(true);
expect(result.errors).toEqual([]);
});
it('should detect invalid syntax', async () => {
// Arrange
global.fetch.mockResolvedValueOnce({
json: () => Promise.resolve({
valid: false,
errors: [{ message: 'SyntaxError: invalid syntax' }]
})
});
// Act
const result = await validateCodeTool.execute({
source_code: 'def hello( return',
language: 'python'
});
// Assert
expect(result.valid).toBe(false);
expect(result.errors.length).toBeGreaterThan(0);
});
});
describe('runCodeLoopTool.execute()', () => {
it('should submit code for async execution with context', async () => {
// Arrange
global.fetch.mockResolvedValueOnce({
json: () => Promise.resolve({
status: 'executing',
token: 'loop-token-123',
message: 'Code is executing'
})
});
const context = {
conversationId: 'conv-abc',
agentId: 1
};
// Act
const result = await runCodeLoopTool.execute({
source_code: 'print(sum(range(100)))',
language: 'python',
next_prompt: 'Analyze the result'
}, context);
// Assert
expect(result.status).toBe('executing');
expect(fetch).toHaveBeenCalledWith(
'http://localhost:5001/api/v3/code/execute-loop',
expect.objectContaining({
method: 'POST',
body: expect.stringContaining('conv-abc')
})
);
});
it('should include next_prompt in request', async () => {
// Arrange
global.fetch.mockResolvedValueOnce({
json: () => Promise.resolve({ status: 'executing', token: 'test' })
});
// Act
await runCodeLoopTool.execute({
source_code: 'print(1)',
language: 'python',
next_prompt: 'Continue analysis'
}, { conversationId: 'c1', agentId: 1 });
// Assert
expect(fetch).toHaveBeenCalledWith(
expect.any(String),
expect.objectContaining({
body: expect.stringContaining('Continue analysis')
})
);
});
});
});