Skip to main content
Glama
test-triage.ts8.86 kB
/** * Code Agent - Test Auto-Triage * Automatically categorize and prioritize test failures */ import { callLLM } from '../llm/index.js'; import { getModelsByLayer } from '../../config/models.js'; import { logger } from '../../logging/logger.js'; export interface TestFailure { testName: string; errorMessage: string; stackTrace?: string; file: string; line?: number; } export interface TriageResult { category: TestFailureCategory; severity: 'critical' | 'high' | 'medium' | 'low'; rootCause: string; suggestedFix: string; affectedArea: string; confidence: number; relatedFailures?: string[]; } export type TestFailureCategory = | 'assertion-failure' | 'runtime-error' | 'timeout' | 'setup-teardown' | 'dependency-issue' | 'race-condition' | 'environment' | 'flaky-test'; /** * Analyze and categorize test failure */ export async function triageTestFailure( failure: TestFailure, codeContext?: string, ): Promise<TriageResult> { logger.info('Triaging test failure', { testName: failure.testName, file: failure.file, }); const models = getModelsByLayer('L1'); if (models.length === 0) { throw new Error('No L1 models available for test triage'); } const prompt = `Analyze this test failure and provide diagnosis. TEST NAME: ${failure.testName} FILE: ${failure.file}${failure.line ? ` (line ${failure.line})` : ''} ERROR MESSAGE: ${failure.errorMessage} ${failure.stackTrace ? `STACK TRACE:\n${failure.stackTrace}\n` : ''} ${codeContext ? `CODE CONTEXT:\n\`\`\`\n${codeContext}\n\`\`\`\n` : ''} Analyze and provide: 1. Category of failure 2. Severity level (critical/high/medium/low) 3. Root cause analysis 4. Suggested fix with code examples 5. Affected area (component/module) 6. Confidence in diagnosis (0-100) Categories: assertion-failure, runtime-error, timeout, setup-teardown, dependency-issue, race-condition, environment, flaky-test Format as JSON: { "category": "assertion-failure", "severity": "high", "rootCause": "Expected value mismatch due to...", "suggestedFix": "Change line 42 to...", "affectedArea": "UserService.login()", "confidence": 85, "relatedFailures": ["test-2", "test-3"] }`; const response = await callLLM( { prompt, maxTokens: 1500, temperature: 0.2, }, models[0], ); const jsonMatch = response.content.match(/\{[\s\S]*\}/); if (!jsonMatch) { throw new Error('Failed to parse triage response'); } const result = JSON.parse(jsonMatch[0]); return { category: result.category || 'runtime-error', severity: result.severity || 'medium', rootCause: result.rootCause || 'Unknown', suggestedFix: result.suggestedFix || 'No fix suggested', affectedArea: result.affectedArea || 'Unknown', confidence: result.confidence || 0, relatedFailures: result.relatedFailures || [], }; } /** * Batch triage multiple test failures */ export async function triageBatchFailures( failures: TestFailure[], codeBase?: Map<string, string>, ): Promise<Map<string, TriageResult>> { const results = new Map<string, TriageResult>(); logger.info(`Triaging ${failures.length} test failures`); for (const failure of failures) { const codeContext = codeBase?.get(failure.file); const result = await triageTestFailure(failure, codeContext); results.set(failure.testName, result); } // Group related failures const grouped = groupRelatedFailures(results); logger.info(`Grouped into ${grouped.size} failure clusters`); return results; } /** * Group related test failures */ function groupRelatedFailures( results: Map<string, TriageResult>, ): Map<string, string[]> { const groups = new Map<string, string[]>(); for (const [testName, result] of results.entries()) { const key = `${result.category}-${result.affectedArea}`; if (!groups.has(key)) { groups.set(key, []); } const list = groups.get(key); if (list) { list.push(testName); } } return groups; } /** * Prioritize test failures by severity and impact */ export function prioritizeFailures( results: Map<string, TriageResult>, ): Array<{ testName: string; result: TriageResult; priority: number }> { const prioritized: Array<{ testName: string; result: TriageResult; priority: number }> = []; const severityScores = { critical: 100, high: 75, medium: 50, low: 25, }; for (const [testName, result] of results.entries()) { let priority = severityScores[result.severity]; // Boost priority for high-confidence diagnoses priority += result.confidence * 0.2; // Boost priority for failures affecting multiple tests if (result.relatedFailures && result.relatedFailures.length > 0) { priority += result.relatedFailures.length * 5; } prioritized.push({ testName, result, priority }); } // Sort by priority descending return prioritized.sort((a, b) => b.priority - a.priority); } /** * Generate fix suggestions for test failures */ export async function generateFixForFailure( failure: TestFailure, triageResult: TriageResult, sourceCode: string, ): Promise<{ originalCode: string; fixedCode: string; explanation: string; }> { const models = getModelsByLayer('L2'); if (models.length === 0) { throw new Error('No L2 models available'); } const prompt = `Generate a code fix for this test failure. TEST FAILURE: - Name: ${failure.testName} - Error: ${failure.errorMessage} TRIAGE ANALYSIS: - Category: ${triageResult.category} - Root Cause: ${triageResult.rootCause} - Suggested Fix: ${triageResult.suggestedFix} SOURCE CODE: \`\`\`typescript ${sourceCode} \`\`\` Provide: 1. Fixed code (complete file) 2. Explanation of changes 3. Verification steps Format as JSON: { "fixedCode": "complete fixed code...", "explanation": "Changed line X because...", "verificationSteps": ["Run test again", "Check edge cases"] }`; const response = await callLLM( { prompt, maxTokens: 2000, temperature: 0.3, }, models[0], ); const jsonMatch = response.content.match(/\{[\s\S]*\}/); if (!jsonMatch) { throw new Error('Failed to parse fix response'); } const result = JSON.parse(jsonMatch[0]); return { originalCode: sourceCode, fixedCode: result.fixedCode || sourceCode, explanation: result.explanation || 'No explanation provided', }; } /** * Auto-fix test failures (with user confirmation) */ export async function autoFixFailures( failures: TestFailure[], triageResults: Map<string, TriageResult>, codeBase: Map<string, string>, confidenceThreshold = 80, ): Promise< Array<{ testName: string; fixed: boolean; newCode?: string; reason: string; }> > { const results: Array<{ testName: string; fixed: boolean; newCode?: string; reason: string; }> = []; for (const failure of failures) { const triage = triageResults.get(failure.testName); if (!triage) { results.push({ testName: failure.testName, fixed: false, reason: 'No triage result available', }); continue; } // Only auto-fix high-confidence issues if (triage.confidence < confidenceThreshold) { results.push({ testName: failure.testName, fixed: false, reason: `Confidence too low: ${triage.confidence}%`, }); continue; } const sourceCode = codeBase.get(failure.file); if (!sourceCode) { results.push({ testName: failure.testName, fixed: false, reason: 'Source code not found', }); continue; } try { const fix = await generateFixForFailure(failure, triage, sourceCode); results.push({ testName: failure.testName, fixed: true, newCode: fix.fixedCode, reason: fix.explanation, }); } catch (error) { results.push({ testName: failure.testName, fixed: false, reason: error instanceof Error ? error.message : 'Unknown error', }); } } return results; }

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/babasida246/ai-mcp-gateway'

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