mirror of
https://github.com/proffesor-for-testing/agentic-qe.git
synced 2026-09-19 08:45:47 +08:00
c3b342a03a
## Summary Successfully fixed all initialization issues and completed Phase 2 integration: - Fixed agent template discovery and copying (18 agents now copy correctly) - Added comprehensive logging throughout init process - Fixed undefined config property serialization bug - All databases initialize correctly (memory.db, patterns.db) - Phase 2 features (learning, patterns, improvement) fully functional ## Bug Fixes 1. **Agent Copy Bug**: Changed from directory copy to individual file copy - Fixed: "Copied 0 agent definitions" → Now copies all 18 agents - Issue: fs.copy with filter wasn't working, changed to per-file copy 2. **Missing Agent**: Created qe-quality-analyzer.md - Was the only missing agent out of 17 required QE agents - Added complete agent definition with AQE hooks integration 3. **Incomplete Fallback**: Updated fallback from 6 to all 17 agents - Ensures all agents are created even if templates not found - Maintains feature parity in all scenarios 4. **Serialization Bug**: Added sanitizeConfig() helper - Fixed: "Cannot read properties of undefined (reading 'replace')" - Issue: jsonfile couldn't serialize config with undefined properties - Solution: Recursive sanitization removes undefined values before write 5. **Config Path Bug**: Fixed target vs source file list confusion - Was passing source files to createMissingAgents instead of target - Now correctly identifies which agents are missing in target ## Improvements - Added detailed path discovery logging with ✓/✗ checkmarks - Show agent template counts and copy progress - Log each config file write with file paths - Added stack traces to all error handlers (removed verbose-only check) - Created sanitizeConfig for robust JSON serialization ## Testing Verified in test project `/tmp/aqe-test-final`: ✅ All 17 QE agents created ✅ memory.db created (221KB, 12 tables) ✅ patterns.db created (155KB, 4 tables + FTS) ✅ All Phase 2 configs written successfully ✅ Complete initialization with no errors ## Phase 2 Features Confirmed Working - Learning System: Q-learning (lr=0.1, γ=0.95, 20% target) - Pattern Bank: 85% confidence, extraction enabled - Improvement Loop: 1hr cycles, A/B testing, manual approval ## Files Changed - .claude/agents/qe-quality-analyzer.md (NEW) - src/cli/commands/init.ts (MAJOR FIXES) - copyAgentTemplates(): Individual file copy, better logging - createBasicAgents(): All 17 agents - createMissingAgents(): Fixed file list bug - writeFleetConfig(): Added sanitization - sanitizeConfig(): NEW helper for undefined removal - src/learning/* (NEW - 9 files) - src/reasoning/* (NEW - 8 files) - tests/* (NEW - 30+ test files) - docs/* (NEW - 60+ documentation files) ## Breaking Changes None - all changes are internal improvements ## Migration No migration needed - initialization now works correctly 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
314 lines
10 KiB
TypeScript
314 lines
10 KiB
TypeScript
/**
|
||
* Performance Benchmarks for Flaky Test Detection
|
||
* Validates < 10 second processing time for 1000+ test results
|
||
*/
|
||
|
||
import { FlakyTestDetector } from '../../src/learning/FlakyTestDetector';
|
||
import type { TestResult } from '../../src/learning';
|
||
|
||
interface BenchmarkResult {
|
||
name: string;
|
||
testResultCount: number;
|
||
duration: number;
|
||
flakyDetected: number;
|
||
throughput: number; // results/second
|
||
memoryUsage: {
|
||
before: number;
|
||
after: number;
|
||
delta: number;
|
||
};
|
||
}
|
||
|
||
export class FlakyDetectionBenchmark {
|
||
private detector: FlakyTestDetector;
|
||
|
||
constructor() {
|
||
this.detector = new FlakyTestDetector({
|
||
minRuns: 5,
|
||
passRateThreshold: 0.8,
|
||
confidenceThreshold: 0.7,
|
||
useMLModel: true
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Run all benchmarks
|
||
*/
|
||
async runAll(): Promise<BenchmarkResult[]> {
|
||
const results: BenchmarkResult[] = [];
|
||
|
||
console.log('🚀 Starting Flaky Detection Benchmarks\n');
|
||
|
||
// Small dataset (100 results)
|
||
results.push(await this.runBenchmark('Small Dataset (100 results)', 100, 10));
|
||
|
||
// Medium dataset (1,000 results)
|
||
results.push(await this.runBenchmark('Medium Dataset (1,000 results)', 1000, 100));
|
||
|
||
// Large dataset (10,000 results)
|
||
results.push(await this.runBenchmark('Large Dataset (10,000 results)', 10000, 1000));
|
||
|
||
// Extra large dataset (100,000 results)
|
||
results.push(await this.runBenchmark('Extra Large Dataset (100,000 results)', 100000, 5000));
|
||
|
||
console.log('\n📊 Benchmark Summary:');
|
||
this.printSummary(results);
|
||
|
||
return results;
|
||
}
|
||
|
||
/**
|
||
* Run single benchmark
|
||
*/
|
||
private async runBenchmark(
|
||
name: string,
|
||
totalResults: number,
|
||
testsCount: number
|
||
): Promise<BenchmarkResult> {
|
||
console.log(`\n⏱️ Running: ${name}`);
|
||
|
||
// Generate test data
|
||
const history = this.generateLargeDataset(totalResults, testsCount);
|
||
|
||
// Measure memory before
|
||
const memBefore = process.memoryUsage().heapUsed / 1024 / 1024;
|
||
|
||
// Run detection and measure time
|
||
const startTime = Date.now();
|
||
const flakyTests = await this.detector.detectFlakyTests(history);
|
||
const duration = Date.now() - startTime;
|
||
|
||
// Measure memory after
|
||
const memAfter = process.memoryUsage().heapUsed / 1024 / 1024;
|
||
|
||
const result: BenchmarkResult = {
|
||
name,
|
||
testResultCount: totalResults,
|
||
duration,
|
||
flakyDetected: flakyTests.length,
|
||
throughput: totalResults / (duration / 1000),
|
||
memoryUsage: {
|
||
before: memBefore,
|
||
after: memAfter,
|
||
delta: memAfter - memBefore
|
||
}
|
||
};
|
||
|
||
console.log(` ✅ Completed in ${duration}ms`);
|
||
console.log(` 📊 Throughput: ${Math.round(result.throughput)} results/sec`);
|
||
console.log(` 🔍 Flaky tests detected: ${flakyTests.length}`);
|
||
console.log(` 💾 Memory delta: ${result.memoryUsage.delta.toFixed(2)} MB`);
|
||
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* Benchmark accuracy on known dataset
|
||
*/
|
||
async benchmarkAccuracy(): Promise<{
|
||
accuracy: number;
|
||
precision: number;
|
||
recall: number;
|
||
f1Score: number;
|
||
falsePositiveRate: number;
|
||
}> {
|
||
console.log('\n🎯 Benchmarking Accuracy...');
|
||
|
||
// Generate labeled dataset
|
||
const { trainingData, labels, testData, testLabels } = this.generateLabeledDataset();
|
||
|
||
// Train model
|
||
console.log(' Training model...');
|
||
await this.detector.trainModel(trainingData, labels);
|
||
|
||
// Test on hold-out set
|
||
console.log(' Testing on hold-out set...');
|
||
const allTestResults: TestResult[] = [];
|
||
for (const results of testData.values()) {
|
||
allTestResults.push(...results);
|
||
}
|
||
|
||
const detected = await this.detector.detectFlakyTests(allTestResults);
|
||
const detectedNames = new Set(detected.map(d => d.name));
|
||
|
||
// Calculate metrics
|
||
let tp = 0, tn = 0, fp = 0, fn = 0;
|
||
|
||
for (const [testName, isFlaky] of testLabels) {
|
||
const wasDetected = detectedNames.has(testName);
|
||
|
||
if (wasDetected && isFlaky) tp++;
|
||
else if (!wasDetected && !isFlaky) tn++;
|
||
else if (wasDetected && !isFlaky) fp++;
|
||
else fn++;
|
||
}
|
||
|
||
const accuracy = (tp + tn) / testLabels.size;
|
||
const precision = tp / Math.max(tp + fp, 1);
|
||
const recall = tp / Math.max(tp + fn, 1);
|
||
const f1Score = 2 * (precision * recall) / Math.max(precision + recall, 0.001);
|
||
const falsePositiveRate = fp / Math.max(fp + tn, 1);
|
||
|
||
const metrics = {
|
||
accuracy,
|
||
precision,
|
||
recall,
|
||
f1Score,
|
||
falsePositiveRate
|
||
};
|
||
|
||
console.log(` Accuracy: ${(accuracy * 100).toFixed(2)}%`);
|
||
console.log(` Precision: ${(precision * 100).toFixed(2)}%`);
|
||
console.log(` Recall: ${(recall * 100).toFixed(2)}%`);
|
||
console.log(` F1 Score: ${(f1Score * 100).toFixed(2)}%`);
|
||
console.log(` False Positive Rate: ${(falsePositiveRate * 100).toFixed(2)}%`);
|
||
|
||
return metrics;
|
||
}
|
||
|
||
/**
|
||
* Print benchmark summary
|
||
*/
|
||
private printSummary(results: BenchmarkResult[]): void {
|
||
console.log('\n┌─────────────────────────────────────────────────────────────┐');
|
||
console.log('│ BENCHMARK RESULTS │');
|
||
console.log('├─────────────────────────────────────────────────────────────┤');
|
||
|
||
results.forEach(r => {
|
||
console.log(`│ ${r.name.padEnd(40)} │`);
|
||
console.log(`│ Results: ${r.testResultCount.toLocaleString().padEnd(10)} Duration: ${r.duration}ms ${' '.repeat(14)}│`);
|
||
console.log(`│ Throughput: ${Math.round(r.throughput).toLocaleString()} results/sec ${' '.repeat(18)}│`);
|
||
console.log(`│ Memory: ${r.memoryUsage.delta.toFixed(2)}MB ${' '.repeat(28)}│`);
|
||
console.log('├─────────────────────────────────────────────────────────────┤');
|
||
});
|
||
|
||
// Performance validation
|
||
const largeDatasetResult = results.find(r => r.testResultCount >= 1000);
|
||
if (largeDatasetResult && largeDatasetResult.duration < 10000) {
|
||
console.log('│ ✅ PERFORMANCE TARGET MET: < 10s for 1000+ results │');
|
||
} else {
|
||
console.log('│ ⚠️ PERFORMANCE TARGET MISSED: > 10s for 1000+ results │');
|
||
}
|
||
|
||
console.log('└─────────────────────────────────────────────────────────────┘\n');
|
||
}
|
||
|
||
// Helper methods
|
||
|
||
private generateLargeDataset(totalResults: number, testsCount: number): TestResult[] {
|
||
const results: TestResult[] = [];
|
||
const resultsPerTest = Math.floor(totalResults / testsCount);
|
||
const baseTime = Date.now();
|
||
|
||
for (let i = 0; i < testsCount; i++) {
|
||
const testName = `test${i}`;
|
||
const isFlaky = Math.random() < 0.3; // 30% flaky
|
||
|
||
for (let j = 0; j < resultsPerTest; j++) {
|
||
const passed = isFlaky
|
||
? Math.random() < (0.4 + Math.random() * 0.3) // Flaky: 40-70% pass rate
|
||
: Math.random() < 0.98; // Stable: 98% pass rate
|
||
|
||
results.push({
|
||
name: testName,
|
||
status: passed ? 'passed' : 'failed',
|
||
duration: isFlaky
|
||
? 100 + Math.random() * 500 // High variance
|
||
: 100 + Math.random() * 20, // Low variance
|
||
timestamp: baseTime + (i * resultsPerTest + j) * 1000,
|
||
error: passed ? undefined : 'Test failure'
|
||
});
|
||
}
|
||
}
|
||
|
||
return results;
|
||
}
|
||
|
||
private generateLabeledDataset(): {
|
||
trainingData: Map<string, TestResult[]>;
|
||
labels: Map<string, boolean>;
|
||
testData: Map<string, TestResult[]>;
|
||
testLabels: Map<string, boolean>;
|
||
} {
|
||
const trainingData = new Map<string, TestResult[]>();
|
||
const labels = new Map<string, boolean>();
|
||
const testData = new Map<string, TestResult[]>();
|
||
const testLabels = new Map<string, boolean>();
|
||
|
||
// Generate training set (80 tests)
|
||
for (let i = 0; i < 80; i++) {
|
||
const testName = `train${i}`;
|
||
const isFlaky = i < 40;
|
||
const results = isFlaky
|
||
? this.generateFlakyResults(testName, 20, 0.4 + Math.random() * 0.3)
|
||
: this.generateStableResults(testName, 20);
|
||
|
||
trainingData.set(testName, results);
|
||
labels.set(testName, isFlaky);
|
||
}
|
||
|
||
// Generate test set (20 tests)
|
||
for (let i = 0; i < 20; i++) {
|
||
const testName = `test${i}`;
|
||
const isFlaky = i < 10;
|
||
const results = isFlaky
|
||
? this.generateFlakyResults(testName, 20, 0.4 + Math.random() * 0.3)
|
||
: this.generateStableResults(testName, 20);
|
||
|
||
testData.set(testName, results);
|
||
testLabels.set(testName, isFlaky);
|
||
}
|
||
|
||
return { trainingData, labels, testData, testLabels };
|
||
}
|
||
|
||
private generateFlakyResults(testName: string, count: number, passRate: number): TestResult[] {
|
||
const results: TestResult[] = [];
|
||
const baseTime = Date.now();
|
||
|
||
for (let i = 0; i < count; i++) {
|
||
const passed = Math.random() < passRate;
|
||
results.push({
|
||
name: testName,
|
||
status: passed ? 'passed' : 'failed',
|
||
duration: 100 + Math.random() * 500,
|
||
timestamp: baseTime + i * 60000,
|
||
error: passed ? undefined : 'Flaky failure'
|
||
});
|
||
}
|
||
|
||
return results;
|
||
}
|
||
|
||
private generateStableResults(testName: string, count: number): TestResult[] {
|
||
const results: TestResult[] = [];
|
||
const baseTime = Date.now();
|
||
|
||
for (let i = 0; i < count; i++) {
|
||
results.push({
|
||
name: testName,
|
||
status: 'passed',
|
||
duration: 100 + Math.random() * 10,
|
||
timestamp: baseTime + i * 60000
|
||
});
|
||
}
|
||
|
||
return results;
|
||
}
|
||
}
|
||
|
||
// Run benchmarks if executed directly
|
||
if (require.main === module) {
|
||
(async () => {
|
||
const benchmark = new FlakyDetectionBenchmark();
|
||
|
||
// Run performance benchmarks
|
||
await benchmark.runAll();
|
||
|
||
// Run accuracy benchmark
|
||
await benchmark.benchmarkAccuracy();
|
||
|
||
console.log('\n✅ All benchmarks completed!\n');
|
||
})();
|
||
}
|