/** * AgentOrchestrator Tests * Tests for the agent orchestration system */ import { AgentOrchestrator, getOrchestrator } from '../../../lib/agents/AgentOrchestrator'; // Mock all agents jest.mock('../../../lib/agents/PlantLineageAgent', () => ({ PlantLineageAgent: jest.fn().mockImplementation(() => ({ config: { id: 'plant-lineage-agent', name: 'Plant Lineage Agent', priority: 'high' }, status: 'idle', start: jest.fn().mockResolvedValue(undefined), stop: jest.fn().mockResolvedValue(undefined), getMetrics: jest.fn().mockReturnValue({ agentId: 'plant-lineage-agent', tasksCompleted: 5, tasksFailed: 0, averageExecutionMs: 100, errors: [], }), getAlerts: jest.fn().mockReturnValue([]), })), getPlantLineageAgent: jest.fn(), })); jest.mock('../../../lib/agents/TransportTrackerAgent', () => ({ TransportTrackerAgent: jest.fn().mockImplementation(() => ({ config: { id: 'transport-tracker-agent', name: 'Transport Tracker Agent', priority: 'high' }, status: 'idle', start: jest.fn().mockResolvedValue(undefined), stop: jest.fn().mockResolvedValue(undefined), getMetrics: jest.fn().mockReturnValue({ agentId: 'transport-tracker-agent', tasksCompleted: 3, tasksFailed: 1, averageExecutionMs: 150, errors: [], }), getAlerts: jest.fn().mockReturnValue([]), })), getTransportTrackerAgent: jest.fn(), })); jest.mock('../../../lib/agents/DemandForecastAgent', () => ({ DemandForecastAgent: jest.fn().mockImplementation(() => ({ config: { id: 'demand-forecast-agent', name: 'Demand Forecast Agent', priority: 'high' }, status: 'idle', start: jest.fn().mockResolvedValue(undefined), stop: jest.fn().mockResolvedValue(undefined), getMetrics: jest.fn().mockReturnValue({ agentId: 'demand-forecast-agent', tasksCompleted: 2, tasksFailed: 0, averageExecutionMs: 200, errors: [], }), getAlerts: jest.fn().mockReturnValue([]), })), getDemandForecastAgent: jest.fn(), })); jest.mock('../../../lib/agents/VerticalFarmAgent', () => ({ VerticalFarmAgent: jest.fn().mockImplementation(() => ({ config: { id: 'vertical-farm-agent', name: 'Vertical Farm Agent', priority: 'critical' }, status: 'idle', start: jest.fn().mockResolvedValue(undefined), stop: jest.fn().mockResolvedValue(undefined), getMetrics: jest.fn().mockReturnValue({ agentId: 'vertical-farm-agent', tasksCompleted: 10, tasksFailed: 0, averageExecutionMs: 50, errors: [], }), getAlerts: jest.fn().mockReturnValue([]), })), getVerticalFarmAgent: jest.fn(), })); jest.mock('../../../lib/agents/EnvironmentAnalysisAgent', () => ({ EnvironmentAnalysisAgent: jest.fn().mockImplementation(() => ({ config: { id: 'environment-analysis-agent', name: 'Environment Analysis Agent', priority: 'medium' }, status: 'idle', start: jest.fn().mockResolvedValue(undefined), stop: jest.fn().mockResolvedValue(undefined), getMetrics: jest.fn().mockReturnValue({ agentId: 'environment-analysis-agent', tasksCompleted: 1, tasksFailed: 0, averageExecutionMs: 300, errors: [], }), getAlerts: jest.fn().mockReturnValue([]), })), getEnvironmentAnalysisAgent: jest.fn(), })); jest.mock('../../../lib/agents/MarketMatchingAgent', () => ({ MarketMatchingAgent: jest.fn().mockImplementation(() => ({ config: { id: 'market-matching-agent', name: 'Market Matching Agent', priority: 'high' }, status: 'idle', start: jest.fn().mockResolvedValue(undefined), stop: jest.fn().mockResolvedValue(undefined), getMetrics: jest.fn().mockReturnValue({ agentId: 'market-matching-agent', tasksCompleted: 4, tasksFailed: 0, averageExecutionMs: 120, errors: [], }), getAlerts: jest.fn().mockReturnValue([]), })), getMarketMatchingAgent: jest.fn(), })); jest.mock('../../../lib/agents/SustainabilityAgent', () => ({ SustainabilityAgent: jest.fn().mockImplementation(() => ({ config: { id: 'sustainability-agent', name: 'Sustainability Agent', priority: 'medium' }, status: 'idle', start: jest.fn().mockResolvedValue(undefined), stop: jest.fn().mockResolvedValue(undefined), getMetrics: jest.fn().mockReturnValue({ agentId: 'sustainability-agent', tasksCompleted: 1, tasksFailed: 0, averageExecutionMs: 400, errors: [], }), getAlerts: jest.fn().mockReturnValue([]), })), getSustainabilityAgent: jest.fn(), })); jest.mock('../../../lib/agents/NetworkDiscoveryAgent', () => ({ NetworkDiscoveryAgent: jest.fn().mockImplementation(() => ({ config: { id: 'network-discovery-agent', name: 'Network Discovery Agent', priority: 'medium' }, status: 'idle', start: jest.fn().mockResolvedValue(undefined), stop: jest.fn().mockResolvedValue(undefined), getMetrics: jest.fn().mockReturnValue({ agentId: 'network-discovery-agent', tasksCompleted: 1, tasksFailed: 0, averageExecutionMs: 500, errors: [], }), getAlerts: jest.fn().mockReturnValue([]), })), getNetworkDiscoveryAgent: jest.fn(), })); jest.mock('../../../lib/agents/QualityAssuranceAgent', () => ({ QualityAssuranceAgent: jest.fn().mockImplementation(() => ({ config: { id: 'quality-assurance-agent', name: 'Quality Assurance Agent', priority: 'critical' }, status: 'idle', start: jest.fn().mockResolvedValue(undefined), stop: jest.fn().mockResolvedValue(undefined), getMetrics: jest.fn().mockReturnValue({ agentId: 'quality-assurance-agent', tasksCompleted: 8, tasksFailed: 0, averageExecutionMs: 80, errors: [], }), getAlerts: jest.fn().mockReturnValue([]), })), getQualityAssuranceAgent: jest.fn(), })); jest.mock('../../../lib/agents/GrowerAdvisoryAgent', () => ({ GrowerAdvisoryAgent: jest.fn().mockImplementation(() => ({ config: { id: 'grower-advisory-agent', name: 'Grower Advisory Agent', priority: 'high' }, status: 'idle', start: jest.fn().mockResolvedValue(undefined), stop: jest.fn().mockResolvedValue(undefined), getMetrics: jest.fn().mockReturnValue({ agentId: 'grower-advisory-agent', tasksCompleted: 6, tasksFailed: 1, averageExecutionMs: 180, errors: [], }), getAlerts: jest.fn().mockReturnValue([]), })), getGrowerAdvisoryAgent: jest.fn(), })); describe('AgentOrchestrator', () => { let orchestrator: AgentOrchestrator; beforeEach(() => { // Reset singleton (globalThis as any).__orchestratorInstance = undefined; orchestrator = new AgentOrchestrator(); }); afterEach(async () => { const status = orchestrator.getStatus(); if (status.isRunning) { await orchestrator.stopAll(); } }); describe('Initialization', () => { it('should initialize with all 10 agents', () => { const status = orchestrator.getStatus(); expect(status.totalAgents).toBe(10); }); it('should not be running initially', () => { const status = orchestrator.getStatus(); expect(status.isRunning).toBe(false); }); it('should have no running agents initially', () => { const status = orchestrator.getStatus(); expect(status.runningAgents).toBe(0); }); }); describe('Starting Agents', () => { it('should start all agents', async () => { await orchestrator.startAll(); const status = orchestrator.getStatus(); expect(status.isRunning).toBe(true); }); it('should start individual agent', async () => { await orchestrator.startAgent('plant-lineage-agent'); const health = orchestrator.getAgentHealth('plant-lineage-agent'); expect(health).toBeDefined(); }); }); describe('Stopping Agents', () => { it('should stop all agents', async () => { await orchestrator.startAll(); await orchestrator.stopAll(); const status = orchestrator.getStatus(); expect(status.isRunning).toBe(false); }); it('should stop individual agent', async () => { await orchestrator.startAgent('plant-lineage-agent'); await orchestrator.stopAgent('plant-lineage-agent'); // Agent should be stopped }); }); describe('Agent Health', () => { it('should return health for existing agent', async () => { await orchestrator.startAll(); const health = orchestrator.getAgentHealth('plant-lineage-agent'); expect(health).toHaveProperty('agentId'); expect(health?.agentId).toBe('plant-lineage-agent'); }); it('should return undefined for non-existent agent', () => { const health = orchestrator.getAgentHealth('non-existent-agent'); expect(health).toBeUndefined(); }); }); describe('Status', () => { it('should return correct status structure', () => { const status = orchestrator.getStatus(); expect(status).toHaveProperty('isRunning'); expect(status).toHaveProperty('totalAgents'); expect(status).toHaveProperty('runningAgents'); expect(status).toHaveProperty('healthyAgents'); }); it('should track uptime when running', async () => { await orchestrator.startAll(); await new Promise((resolve) => setTimeout(resolve, 50)); const status = orchestrator.getStatus(); expect(status.uptime).toBeGreaterThan(0); }); }); describe('Dashboard', () => { it('should return dashboard data', async () => { await orchestrator.startAll(); const dashboard = orchestrator.getDashboard(); expect(dashboard).toHaveProperty('status'); expect(dashboard).toHaveProperty('agents'); expect(dashboard).toHaveProperty('recentAlerts'); }); it('should include all agents in dashboard', async () => { await orchestrator.startAll(); const dashboard = orchestrator.getDashboard(); expect(dashboard.agents.length).toBe(10); }); }); describe('Alerts', () => { it('should return empty alerts initially', () => { const alerts = orchestrator.getAlerts(); expect(alerts).toEqual([]); }); it('should filter alerts by severity', () => { const criticalAlerts = orchestrator.getAlerts('critical'); expect(Array.isArray(criticalAlerts)).toBe(true); }); }); describe('Singleton', () => { it('should return same instance from getOrchestrator', () => { const orch1 = getOrchestrator(); const orch2 = getOrchestrator(); expect(orch1).toBe(orch2); }); }); });