feat: add test execution history manager

This commit is contained in:
张翔
2026-03-13 11:30:07 +08:00
parent f272e8499d
commit a3e7114349
4 changed files with 128 additions and 0 deletions
+103
View File
@@ -0,0 +1,103 @@
import fs from 'fs';
import path from 'path';
interface TestExecutionRecord {
testId: string;
file: string;
title: string;
duration: number;
timestamp: number;
success: boolean;
flaky: boolean;
}
interface TestHistory {
records: TestExecutionRecord[];
lastUpdated: number;
}
const HISTORY_FILE = path.join(__dirname, '../../test-history.json');
export class TestHistoryManager {
private history: TestHistory;
constructor() {
this.loadHistory();
}
private loadHistory(): void {
if (fs.existsSync(HISTORY_FILE)) {
const data = fs.readFileSync(HISTORY_FILE, 'utf-8');
this.history = JSON.parse(data);
} else {
this.history = { records: [], lastUpdated: Date.now() };
}
}
private saveHistory(): void {
this.history.lastUpdated = Date.now();
fs.writeFileSync(HISTORY_FILE, JSON.stringify(this.history, null, 2));
}
recordExecution(testId: string, file: string, title: string, duration: number, success: boolean): void {
const record: TestExecutionRecord = {
testId,
file,
title,
duration,
timestamp: Date.now(),
success,
flaky: this.isFlaky(testId),
};
this.history.records.push(record);
if (this.history.records.length > 1000) {
this.history.records = this.history.records.slice(-1000);
}
this.saveHistory();
}
getAverageDuration(testId: string): number {
const testRecords = this.history.records.filter(r => r.testId === testId);
if (testRecords.length === 0) return 0;
const durations = testRecords.map(r => r.duration);
return durations.reduce((a, b) => a + b, 0) / durations.length;
}
isFlaky(testId: string): boolean {
const testRecords = this.history.records
.filter(r => r.testId === testId)
.slice(-10);
if (testRecords.length < 5) return false;
const failureCount = testRecords.filter(r => !r.success).length;
return failureCount >= 3;
}
getSlowTests(threshold: number = 2): TestExecutionRecord[] {
const avgDurations = new Map<string, number>();
this.history.records.forEach(record => {
const avg = avgDurations.get(record.testId) || 0;
const count = this.history.records.filter(r => r.testId === record.testId).length;
avgDurations.set(record.testId, avg + record.duration / count);
});
return Array.from(avgDurations.entries())
.filter(([_, avg]) => avg > threshold * 60000)
.map(([testId, avg]) => ({
testId,
file: this.history.records.find(r => r.testId === testId)!.file,
title: this.history.records.find(r => r.testId === testId)!.title,
duration: avg,
timestamp: Date.now(),
success: true,
flaky: false,
}))
.sort((a, b) => b.duration - a.duration);
}
}