Skip to main content
Glama
test-outcome-auditor.ts11.8 kB
import { AuditTrailService } from './audit-trail-service.js'; import { AuditEventType, AuditSeverity } from './types.js'; import { createLogger } from '../logger/index.js'; export interface TestExecution { testId: string; testName: string; testSuite: string; agentId?: string; environment: string; startTime: number; endTime?: number; status: 'running' | 'passed' | 'failed' | 'skipped' | 'timeout'; duration?: number; correlationId?: string; } export interface TestDecision { testId: string; decisionType: 'retry' | 'skip' | 'continue' | 'abort' | 'modify'; reasoning: string; agentId?: string; confidence?: number; alternatives?: string[]; selectedAction: string; timestamp: number; correlationId?: string; } export interface TestOutcome { testId: string; outcome: 'success' | 'failure' | 'partial' | 'inconclusive'; summary: string; details: any; recommendations?: string[]; nextSteps?: string[]; timestamp: number; correlationId?: string; } export interface TestMetrics { testId: string; metrics: { executionTime: number; memoryUsage?: number; cpuUsage?: number; networkCalls?: number; databaseQueries?: number; errors?: number; warnings?: number; }; timestamp: number; } export class TestOutcomeAuditor { private auditService: AuditTrailService; private logger = createLogger('test-outcome-auditor'); private activeTests: Map<string, TestExecution> = new Map(); constructor(auditService?: AuditTrailService) { this.auditService = auditService || AuditTrailService.getInstance(); } /** * Start tracking a test execution */ public startTest(testExecution: TestExecution): string { const correlationId = testExecution.correlationId || this.auditService.generateCorrelationId(); testExecution.correlationId = correlationId; this.activeTests.set(testExecution.testId, testExecution); const message = `Test started: ${testExecution.testName} (${testExecution.testSuite})`; const eventId = this.auditService.logEvent( AuditEventType.TEST_STARTED, message, AuditSeverity.LOW, { correlationId, testId: testExecution.testId, agentId: testExecution.agentId, source: 'test-outcome-auditor' }, { testName: testExecution.testName, testSuite: testExecution.testSuite, environment: testExecution.environment, startTime: testExecution.startTime } ); this.logger.info(`Test execution started: ${testExecution.testName}`, { eventId, correlationId, testId: testExecution.testId }); return testExecution.testId; } /** * Complete a test execution */ public completeTest( testId: string, status: 'passed' | 'failed' | 'skipped' | 'timeout', summary?: string, details?: any ): string { const testExecution = this.activeTests.get(testId); if (!testExecution) { throw new Error(`Test ${testId} not found in active tests`); } const endTime = Date.now(); const duration = endTime - testExecution.startTime; testExecution.endTime = endTime; testExecution.status = status; testExecution.duration = duration; this.activeTests.delete(testId); const message = `Test completed: ${testExecution.testName} - ${status}`; const severity = this.mapTestStatusToSeverity(status); const eventId = this.auditService.logEvent( AuditEventType.TEST_COMPLETED, message, severity, { correlationId: testExecution.correlationId, testId: testExecution.testId, agentId: testExecution.agentId, source: 'test-outcome-auditor' }, { testName: testExecution.testName, testSuite: testExecution.testSuite, status, duration, summary, details, startTime: testExecution.startTime, endTime: endTime } ); this.logger.info(`Test execution completed: ${testExecution.testName} - ${status}`, { eventId, correlationId: testExecution.correlationId, testId: testExecution.testId, duration }); return eventId; } /** * Log a test decision */ public logTestDecision(decision: TestDecision): string { const message = `Test decision made: ${decision.testId} - ${decision.decisionType}`; const severity = this.mapDecisionTypeToSeverity(decision.decisionType); const eventId = this.auditService.logEvent( AuditEventType.TEST_DECISION, message, severity, { correlationId: decision.correlationId, testId: decision.testId, agentId: decision.agentId, source: 'test-outcome-auditor' }, { decisionType: decision.decisionType, reasoning: decision.reasoning, confidence: decision.confidence, alternatives: decision.alternatives, selectedAction: decision.selectedAction, timestamp: decision.timestamp } ); this.logger.info(`Test decision logged: ${decision.testId} - ${decision.decisionType}`, { eventId, correlationId: decision.correlationId, confidence: decision.confidence }); return eventId; } /** * Log test outcome */ public logTestOutcome(outcome: TestOutcome): string { const message = `Test outcome: ${outcome.testId} - ${outcome.outcome}`; const severity = this.mapOutcomeToSeverity(outcome.outcome); const eventId = this.auditService.logEvent( AuditEventType.TEST_OUTCOME, message, severity, { correlationId: outcome.correlationId, testId: outcome.testId, source: 'test-outcome-auditor' }, { outcome: outcome.outcome, summary: outcome.summary, details: outcome.details, recommendations: outcome.recommendations, nextSteps: outcome.nextSteps, timestamp: outcome.timestamp } ); this.logger.info(`Test outcome logged: ${outcome.testId} - ${outcome.outcome}`, { eventId, correlationId: outcome.correlationId }); return eventId; } /** * Log test metrics */ public logTestMetrics(metrics: TestMetrics): string { const message = `Test metrics: ${metrics.testId} - execution time: ${metrics.metrics.executionTime}ms`; const eventId = this.auditService.logEvent( AuditEventType.SYSTEM_EVENT, message, AuditSeverity.LOW, { testId: metrics.testId, source: 'test-outcome-auditor' }, { metrics: metrics.metrics, timestamp: metrics.timestamp } ); this.logger.debug(`Test metrics logged: ${metrics.testId}`, { eventId, executionTime: metrics.metrics.executionTime }); return eventId; } /** * Log test failure with detailed error information */ public logTestFailure( testId: string, error: Error, context?: any, correlationId?: string ): string { const message = `Test failure: ${testId} - ${error.message}`; const eventId = this.auditService.logEvent( AuditEventType.ERROR_EVENT, message, AuditSeverity.HIGH, { correlationId, testId, source: 'test-outcome-auditor' }, { error: { name: error.name, message: error.message, stack: error.stack }, context } ); this.logger.error(`Test failure logged: ${testId}`, { eventId, correlationId, error: error.message }); return eventId; } /** * Get all test events for a specific test */ public getTestEvents(testId: string): any[] { const allEvents = this.auditService.getAllEvents(); return allEvents.filter(event => event.context.testId === testId); } /** * Get all test events for a correlation ID */ public getTestEventsByCorrelation(correlationId: string): any[] { return this.auditService.getEventsByCorrelationId(correlationId); } /** * Get active tests */ public getActiveTests(): TestExecution[] { return Array.from(this.activeTests.values()); } /** * Get test execution summary */ public getTestSummary(testId: string): any { const events = this.getTestEvents(testId); const startEvent = events.find(e => e.type === AuditEventType.TEST_STARTED); const completeEvent = events.find(e => e.type === AuditEventType.TEST_COMPLETED); const outcomeEvent = events.find(e => e.type === AuditEventType.TEST_OUTCOME); return { testId, startEvent, completeEvent, outcomeEvent, totalEvents: events.length }; } /** * Get all test outcomes (completed tests) */ public getTestOutcomes(): any[] { const allEvents = this.auditService.getAllEvents(); const completedTests = new Map<string, any>(); // Find all test completion events allEvents.forEach(event => { if (event.type === AuditEventType.TEST_COMPLETED) { const testId = event.context.testId; if (!testId) return; // Skip if testId is undefined const testData = event.data; // Get all events for this test const testEvents = this.getTestEvents(testId); const decisions = testEvents.filter(e => e.type === AuditEventType.TEST_DECISION); completedTests.set(testId, { testId, testName: testData.testName, testSuite: testData.testSuite, status: testData.status, startTime: testData.startTime, endTime: testData.endTime, duration: testData.duration, summary: testData.summary, decisions: decisions.map(d => d.data), correlationId: event.context.correlationId }); } }); return Array.from(completedTests.values()); } /** * Export test outcomes with filtering */ public async exportTestOutcomes(filters?: { testSuite?: string; status?: string; testId?: string; startTime?: number; endTime?: number; }): Promise<any[]> { const allOutcomes = this.getTestOutcomes(); return allOutcomes.filter(outcome => { if (filters?.testSuite && outcome.testSuite !== filters.testSuite) { return false; } if (filters?.status && outcome.status !== filters.status) { return false; } if (filters?.testId && outcome.testId !== filters.testId) { return false; } if (filters?.startTime && outcome.startTime < filters.startTime) { return false; } if (filters?.endTime && outcome.endTime > filters.endTime) { return false; } return true; }); } private mapTestStatusToSeverity(status: string): AuditSeverity { switch (status) { case 'failed': case 'timeout': return AuditSeverity.HIGH; case 'skipped': return AuditSeverity.MEDIUM; case 'passed': default: return AuditSeverity.LOW; } } private mapDecisionTypeToSeverity(decisionType: string): AuditSeverity { switch (decisionType) { case 'abort': return AuditSeverity.HIGH; case 'retry': case 'modify': return AuditSeverity.MEDIUM; case 'skip': case 'continue': default: return AuditSeverity.LOW; } } private mapOutcomeToSeverity(outcome: string): AuditSeverity { switch (outcome) { case 'failure': return AuditSeverity.HIGH; case 'partial': case 'inconclusive': return AuditSeverity.MEDIUM; case 'success': default: return AuditSeverity.LOW; } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/evilpixi/pixi-midnight-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server