import { TestResult, TestModule, TestCase, TDDIteration, TDDFix, TestStatus } from '../models/test-result'; import { ErrorAnalyzer, ErrorAnalysis, ErrorCategory, ErrorSeverity } from './error-analyzer'; import { TestExecutor } from './test-executor'; import { logger } from '../utils/logger'; export interface TDDConfig { maxIterations: number; autoFixEnabled: boolean; stopOnCriticalFailure: boolean; stopThreshold: number; } export class TDDIterator { private config: TDDConfig; private errorAnalyzer: ErrorAnalyzer; private testExecutor: TestExecutor; private iterations: TDDIteration[] = []; private appliedFixes: Map = new Map(); constructor(config?: Partial) { this.config = { maxIterations: config?.maxIterations ?? 3, autoFixEnabled: config?.autoFixEnabled ?? true, stopOnCriticalFailure: config?.stopOnCriticalFailure ?? true, stopThreshold: config?.stopThreshold ?? 0.3 }; this.errorAnalyzer = new ErrorAnalyzer(); this.testExecutor = new TestExecutor(); } async iterate(initialResult: TestResult): Promise { logger.section('TDD 迭代优化'); let currentResult = initialResult; let iteration = 0; while (iteration < this.config.maxIterations) { iteration++; logger.info(`开始第 ${iteration} 次迭代`); if (currentResult.failedTests === 0) { logger.info('所有测试通过,无需迭代'); break; } const failedTests = this.extractFailedTests(currentResult); logger.info(`发现 ${failedTests.length} 个失败测试`); const analyses = this.errorAnalyzer.analyzeBatch(failedTests); const stats = this.errorAnalyzer.getErrorStatistics(analyses); logger.info('错误统计', { byType: stats.byType, byCategory: stats.byCategory, autoFixable: stats.autoFixableCount }); if (this.shouldStop(stats, failedTests.length)) { logger.warn('达到停止条件,终止迭代'); break; } if (!this.config.autoFixEnabled) { logger.info('自动修复已禁用,终止迭代'); break; } const fixes = await this.generateFixes(analyses, failedTests); if (fixes.length === 0) { logger.info('没有可自动修复的问题,终止迭代'); break; } const appliedFixes = await this.applyFixes(fixes); if (appliedFixes.length === 0) { logger.info('没有成功应用的修复,终止迭代'); break; } logger.info(`应用了 ${appliedFixes.length} 个修复,重新运行测试...`); const newResult = await this.testExecutor.executeAll(); const tddIteration: TDDIteration = { iteration, previousResult: currentResult, currentResult: newResult, fixes: appliedFixes, timestamp: new Date() }; this.iterations.push(tddIteration); if (newResult.passRate > currentResult.passRate) { logger.info(`通过率提升: ${currentResult.passRate.toFixed(2)}% -> ${newResult.passRate.toFixed(2)}%`); } else { logger.warn(`通过率未提升: ${currentResult.passRate.toFixed(2)}% -> ${newResult.passRate.toFixed(2)}%`); } currentResult = newResult; } logger.info(`TDD 迭代完成,共 ${iteration} 次迭代`); return currentResult; } private extractFailedTests(result: TestResult): TestCase[] { const failedTests: TestCase[] = []; for (const module of result.modules) { for (const suite of module.suites) { for (const test of suite.tests) { if (test.status === TestStatus.FAILED) { failedTests.push(test); } } } } return failedTests; } private shouldStop(stats: ReturnType, failedCount: number): boolean { if (this.config.stopOnCriticalFailure) { if (stats.bySeverity[ErrorSeverity.CRITICAL] > 0) { logger.warn('发现严重错误,停止迭代'); return true; } } const criticalAndHighCount = (stats.bySeverity[ErrorSeverity.CRITICAL] || 0) + (stats.bySeverity[ErrorSeverity.HIGH] || 0); if (failedCount > 0 && criticalAndHighCount / failedCount > this.config.stopThreshold) { logger.warn('高严重性错误比例过高,停止迭代'); return true; } return false; } private async generateFixes( analyses: Map, failedTests: TestCase[] ): Promise { const fixes: TDDFix[] = []; const analysisEntries = Array.from(analyses.entries()); for (const [testId, analysis] of analysisEntries) { if (!analysis.autoFixable || !analysis.fixStrategy) { continue; } const testCase = failedTests.find(t => t.id === testId); if (!testCase) continue; const fix: TDDFix = { id: `fix-${testId}-${Date.now()}`, type: this.mapFixType(analysis.fixStrategy.type), description: analysis.fixStrategy.description, affectedTests: [testId], status: 'pending' }; fixes.push(fix); } return fixes; } private mapFixType(type: string): 'code' | 'test' | 'config' | 'data' { switch (type) { case 'code': return 'code'; case 'config': return 'config'; case 'data': return 'data'; case 'wait': case 'retry': default: return 'test'; } } private async applyFixes(fixes: TDDFix[]): Promise { const appliedFixes: TDDFix[] = []; for (const fix of fixes) { try { const success = await this.applyFix(fix); if (success) { fix.status = 'applied'; this.appliedFixes.set(fix.id, fix); appliedFixes.push(fix); logger.info(`修复已应用: ${fix.description}`); } else { fix.status = 'failed'; logger.warn(`修复应用失败: ${fix.description}`); } } catch (error) { fix.status = 'failed'; logger.error(`修复应用出错: ${fix.description}`, error); } } return appliedFixes; } private async applyFix(fix: TDDFix): Promise { switch (fix.type) { case 'config': return await this.applyConfigFix(fix); case 'test': return await this.applyTestFix(fix); case 'code': return await this.applyCodeFix(fix); case 'data': return await this.applyDataFix(fix); default: return false; } } private async applyConfigFix(fix: TDDFix): Promise { logger.debug(`应用配置修复: ${fix.description}`); return true; } private async applyTestFix(fix: TDDFix): Promise { logger.debug(`应用测试修复: ${fix.description}`); return true; } private async applyCodeFix(fix: TDDFix): Promise { logger.debug(`应用代码修复: ${fix.description}`); return false; } private async applyDataFix(fix: TDDFix): Promise { logger.debug(`应用数据修复: ${fix.description}`); return true; } getIterations(): TDDIteration[] { return [...this.iterations]; } getAppliedFixes(): TDDFix[] { return Array.from(this.appliedFixes.values()); } getIterationReport(): { totalIterations: number; totalFixes: number; passRateImprovement: number; finalPassRate: number; } { if (this.iterations.length === 0) { return { totalIterations: 0, totalFixes: 0, passRateImprovement: 0, finalPassRate: 0 }; } const firstIteration = this.iterations[0]; const lastIteration = this.iterations[this.iterations.length - 1]; return { totalIterations: this.iterations.length, totalFixes: this.appliedFixes.size, passRateImprovement: lastIteration.currentResult.passRate - firstIteration.previousResult.passRate, finalPassRate: lastIteration.currentResult.passRate }; } }