feat(admin): 添加用户管理相关文件
添加用户管理视图、API和状态管理文件
This commit is contained in:
@@ -0,0 +1,290 @@
|
||||
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<string, TDDFix> = new Map();
|
||||
|
||||
constructor(config?: Partial<TDDConfig>) {
|
||||
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<TestResult> {
|
||||
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<ErrorAnalyzer['getErrorStatistics']>, 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<string, ErrorAnalysis>,
|
||||
failedTests: TestCase[]
|
||||
): Promise<TDDFix[]> {
|
||||
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<TDDFix[]> {
|
||||
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<boolean> {
|
||||
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<boolean> {
|
||||
logger.debug(`应用配置修复: ${fix.description}`);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async applyTestFix(fix: TDDFix): Promise<boolean> {
|
||||
logger.debug(`应用测试修复: ${fix.description}`);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async applyCodeFix(fix: TDDFix): Promise<boolean> {
|
||||
logger.debug(`应用代码修复: ${fix.description}`);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async applyDataFix(fix: TDDFix): Promise<boolean> {
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user