/** * BaseAgent Tests * Tests for the abstract base agent class */ import { BaseAgent } from '../../../lib/agents/BaseAgent'; import { AgentConfig, AgentTask, AgentStatus } from '../../../lib/agents/types'; // Concrete implementation for testing class TestAgent extends BaseAgent { public runOnceResult: AgentTask | null = null; public runOnceError: Error | null = null; public runOnceCallCount = 0; constructor(config?: Partial) { super({ id: 'test-agent', name: 'Test Agent', description: 'Agent for testing', enabled: true, intervalMs: 100, priority: 'medium', maxRetries: 3, timeoutMs: 5000, ...config, }); } async runOnce(): Promise { this.runOnceCallCount++; if (this.runOnceError) { throw this.runOnceError; } return this.runOnceResult; } // Expose protected methods for testing public testCreateAlert( severity: 'info' | 'warning' | 'error' | 'critical', title: string, message: string ) { return this.createAlert(severity, title, message); } public testHandleError(error: Error) { return this.handleError(error); } public testAddTask(type: string, payload: Record) { return this.addTask(type, payload); } public testCreateTaskResult( type: string, status: 'completed' | 'failed', result?: any ) { return this.createTaskResult(type, status, result); } } describe('BaseAgent', () => { let agent: TestAgent; beforeEach(() => { agent = new TestAgent(); jest.clearAllMocks(); }); afterEach(async () => { if (agent.status === 'running') { await agent.stop(); } }); describe('Initialization', () => { it('should initialize with correct config', () => { expect(agent.config.id).toBe('test-agent'); expect(agent.config.name).toBe('Test Agent'); expect(agent.config.enabled).toBe(true); }); it('should initialize with idle status', () => { expect(agent.status).toBe('idle'); }); it('should initialize with empty metrics', () => { const metrics = agent.getMetrics(); expect(metrics.tasksCompleted).toBe(0); expect(metrics.tasksFailed).toBe(0); expect(metrics.averageExecutionMs).toBe(0); expect(metrics.errors).toEqual([]); }); it('should initialize with empty alerts', () => { expect(agent.getAlerts()).toEqual([]); }); }); describe('Lifecycle', () => { it('should start and update status to running', async () => { agent.runOnceResult = agent.testCreateTaskResult('test', 'completed'); await agent.start(); expect(agent.status).toBe('running'); }); it('should not start twice', async () => { agent.runOnceResult = agent.testCreateTaskResult('test', 'completed'); await agent.start(); const firstCallCount = agent.runOnceCallCount; await agent.start(); // Second call expect(agent.runOnceCallCount).toBe(firstCallCount); }); it('should stop and update status to idle', async () => { agent.runOnceResult = agent.testCreateTaskResult('test', 'completed'); await agent.start(); await agent.stop(); expect(agent.status).toBe('idle'); }); it('should pause and resume', async () => { agent.runOnceResult = agent.testCreateTaskResult('test', 'completed'); await agent.start(); agent.pause(); expect(agent.status).toBe('paused'); agent.resume(); expect(agent.status).toBe('running'); }); it('should not pause if not running', () => { agent.pause(); expect(agent.status).toBe('idle'); }); it('should not resume if not paused', () => { agent.resume(); expect(agent.status).toBe('idle'); }); }); describe('Task Execution', () => { it('should run task on start', async () => { agent.runOnceResult = agent.testCreateTaskResult('test', 'completed'); await agent.start(); expect(agent.runOnceCallCount).toBe(1); await agent.stop(); }); it('should increment tasksCompleted on success', async () => { agent.runOnceResult = agent.testCreateTaskResult('test', 'completed', { data: 'test' }); await agent.start(); await agent.stop(); expect(agent.getMetrics().tasksCompleted).toBe(1); }); it('should increment tasksFailed on failure', async () => { agent.runOnceResult = agent.testCreateTaskResult('test', 'failed', null); await agent.start(); await agent.stop(); expect(agent.getMetrics().tasksFailed).toBe(1); }); it('should update lastRunAt after execution', async () => { agent.runOnceResult = agent.testCreateTaskResult('test', 'completed'); await agent.start(); await agent.stop(); expect(agent.getMetrics().lastRunAt).not.toBeNull(); }); }); describe('Error Handling', () => { it('should handle thrown errors', async () => { agent.runOnceError = new Error('Test error'); await agent.start(); await agent.stop(); const metrics = agent.getMetrics(); expect(metrics.errors.length).toBe(1); expect(metrics.errors[0].message).toBe('Test error'); }); it('should record error timestamp', async () => { agent.testHandleError(new Error('Test error')); const metrics = agent.getMetrics(); expect(metrics.lastErrorAt).not.toBeNull(); }); it('should limit errors to 50', () => { for (let i = 0; i < 60; i++) { agent.testHandleError(new Error(`Error ${i}`)); } expect(agent.getMetrics().errors.length).toBe(50); }); }); describe('Alerts', () => { it('should create alerts with correct structure', () => { const alert = agent.testCreateAlert('warning', 'Test Alert', 'Test message'); expect(alert.id).toBeDefined(); expect(alert.severity).toBe('warning'); expect(alert.title).toBe('Test Alert'); expect(alert.message).toBe('Test message'); expect(alert.acknowledged).toBe(false); }); it('should add alerts to the list', () => { agent.testCreateAlert('info', 'Alert 1', 'Message 1'); agent.testCreateAlert('warning', 'Alert 2', 'Message 2'); expect(agent.getAlerts().length).toBe(2); }); it('should limit alerts to 100', () => { for (let i = 0; i < 110; i++) { agent.testCreateAlert('info', `Alert ${i}`, 'Message'); } expect(agent.getAlerts().length).toBe(100); }); }); describe('Metrics', () => { it('should calculate uptime when running', async () => { agent.runOnceResult = agent.testCreateTaskResult('test', 'completed'); await agent.start(); await new Promise((resolve) => setTimeout(resolve, 50)); const metrics = agent.getMetrics(); expect(metrics.uptime).toBeGreaterThan(0); await agent.stop(); }); it('should calculate average execution time', async () => { agent.runOnceResult = agent.testCreateTaskResult('test', 'completed'); await agent.start(); await agent.stop(); const metrics = agent.getMetrics(); expect(metrics.averageExecutionMs).toBeGreaterThanOrEqual(0); }); }); describe('Task Queue', () => { it('should add tasks with unique IDs', () => { const task1 = agent.testAddTask('type1', { key: 'value1' }); const task2 = agent.testAddTask('type2', { key: 'value2' }); expect(task1.id).not.toBe(task2.id); }); it('should set correct task properties', () => { const task = agent.testAddTask('test-type', { data: 'value' }); expect(task.type).toBe('test-type'); expect(task.payload).toEqual({ data: 'value' }); expect(task.status).toBe('pending'); expect(task.agentId).toBe('test-agent'); }); }); describe('Task Results', () => { it('should create completed task result', () => { const result = agent.testCreateTaskResult('scan', 'completed', { count: 10 }); expect(result.status).toBe('completed'); expect(result.result).toEqual({ count: 10 }); }); it('should create failed task result', () => { const result = agent.testCreateTaskResult('scan', 'failed', null); expect(result.status).toBe('failed'); }); }); });