23f7624596
ADR-166 MCP Bridge Security Lock / Static-source security lock (push) Failing after 0s
ADR-166 MCP Bridge Security Lock / Compose default binds loopback + Mongo has auth (push) Failing after 2s
CodeQL Advanced / Analyze (rust) (push) Failing after 0s
ADR-166 MCP Bridge Security Lock / plugin-agent-federation bindHost default (push) Failing after 1s
ADR-166 MCP Bridge Security Lock / Runtime behavior — 401 + terminal gate + fail-closed (push) Failing after 4s
business-pods-smoke / smoke (push) Failing after 1s
all-plugins-smoke / smoke-all (push) Failing after 2s
CI/CD Pipeline / Security & Code Quality (push) Failing after 1s
CI/CD Pipeline / Test Suite (ubuntu-latest) (push) Failing after 1s
CI/CD Pipeline / Build & Package (macos-latest) (push) Has been skipped
CI/CD Pipeline / Build & Package (ubuntu-latest) (push) Has been skipped
CI/CD Pipeline / Build & Package (windows-latest) (push) Has been skipped
CI/CD Pipeline / Documentation & Examples (push) Failing after 1s
Clone Tracker (14-day rolling) / Snapshot clones for ruflo ecosystem (push) Failing after 1s
CodeQL Advanced / Analyze (actions) (push) Failing after 1s
CodeQL Advanced / Analyze (javascript-typescript) (push) Failing after 1s
federation-peer-rust / stable-noop (push) Failing after 1s
metaharness-ci / score (push) Failing after 1s
metaharness-ci / router-compat (push) Failing after 0s
metaharness-ci / similarity-tests (push) Failing after 0s
no-agentbbs-smoke / smoke-without-agentbbs (push) Failing after 1s
V3 CI/CD Pipeline / Build V3 (windows-latest) (push) Has been skipped
codex-integration-audit / Codex integration audit (push) Failing after 1s
helpers-manifest-guard / guard (push) Failing after 1s
🔗 Cross-Agent Integration Tests / 🤝 Agent Coordination Tests (push) Has been skipped
🔗 Cross-Agent Integration Tests / 🧠 Memory Sharing Integration (push) Has been skipped
🔗 Cross-Agent Integration Tests / 🛡️ Fault Tolerance Tests (push) Has been skipped
🔗 Cross-Agent Integration Tests / ⚡ Performance Integration Tests (push) Has been skipped
metaharness-ci / mcp-scan (push) Failing after 1s
metaharness-ci / eject-dryrun (push) Failing after 1s
metaharness-ci / metaharness-real-data (push) Failing after 0s
no-cli-optdep-bloat-2561 / guard (push) Failing after 1s
no-metaharness-smoke / smoke-without-metaharness (push) Failing after 1s
no-phantom-agentic-flow-subpath / guard (push) Failing after 1s
🔄 Automated Rollback Manager / 🚨 Failure Detection (push) Failing after 1s
V3 CI/CD Pipeline / Plugin hooks smoke / ubuntu-latest / Node 22 (push) Failing after 1s
V3 CI/CD Pipeline / ruflo-graph-intelligence build + test smoke (#2044, ADR-123) (push) Failing after 1s
CVE Audit Gate / Audit root (critical-blocking) (push) Failing after 2s
cost-tracker-smoke / smoke (push) Failing after 3s
oia-audit-weekly / audit (push) Failing after 2s
ruflo-agent-smoke / ruflo-agent structural smoke (push) Failing after 1s
📊 Status Badges Update / 📊 Update Status Badges (push) Failing after 1s
V3 CI/CD Pipeline / Static regression guards (#2267 YAML + (push) Failing after 1s
V3 CI/CD Pipeline / Test V3 Packages (push) Failing after 0s
V3 CI/CD Pipeline / agent_execute provider routing smoke (#2042) (push) Failing after 0s
CVE Audit Gate / Audit v3 (critical-blocking) (push) Failing after 1s
federation-peer-rust / stable-native (push) Failing after 2s
🔗 Cross-Agent Integration Tests / 🚀 Integration Test Setup (push) Failing after 2s
neural-trader-smoke / runtime-smoke (push) Failing after 1s
V3 CI/CD Pipeline / Build V3 (macos-latest) (push) Has been skipped
V3 CI/CD Pipeline / Build V3 (ubuntu-latest) (push) Has been skipped
V3 CI/CD Pipeline / Type Check V3 (push) Failing after 1s
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / ubuntu-latest / Node 24 (push) Failing after 1s
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / ubuntu-latest / Node 22 (push) Failing after 2s
V3 CI/CD Pipeline / browser rvf create flag smoke (#2015) (push) Failing after 0s
V3 CI/CD Pipeline / Dependency review (#2046) (push) Has been skipped
V3 CI/CD Pipeline / Supply-chain audit (#2046) (push) Failing after 0s
V3 CI/CD Pipeline / witness marker drift smoke (#2021) (push) Failing after 1s
V3 CI/CD Pipeline / neural-trader portfolio CG smoke (#2068, ADR-126 Phase 3) (push) Failing after 1s
V3 CI/CD Pipeline / neural-trader backtest signing smoke (#2068, ADR-126 Phase 4) (push) Failing after 1s
V3 CI/CD Pipeline / kg-extract type-import classification smoke (#2049) (push) Failing after 0s
V3 CI/CD Pipeline / witness verify precondition smoke (#1880) (push) Failing after 2s
V3 CI/CD Pipeline / neural-trader pipeline risk-gate smoke (#2068, ADR-126 Phase 5) (push) Failing after 0s
V3 CI/CD Pipeline / neural-trader feature attribution smoke (#2068, ADR-126 Phase 6) (push) Failing after 0s
V3 CI/CD Pipeline / plugin-registry signature verification smoke (#1922, CWE-347) (push) Failing after 4s
V3 CI/CD Pipeline / memory stats legacy-DB smoke (#2120) (push) Failing after 4s
V3 CI/CD Pipeline / github deprecated actions smoke (#2089, ADR-127 Phase 3) (push) Failing after 1s
V3 CI/CD Pipeline / graph query + pathfinder smoke (ADR-130 P2+P5) (push) Has been skipped
V3 CI/CD Pipeline / graph trajectory hooks smoke (ADR-130 P3) (push) Has been skipped
V3 CI/CD Pipeline / graph plugin adapter smoke (ADR-130 P4) (push) Has been skipped
V3 CI/CD Pipeline / graph benchmark (ADR-130 P6) (push) Has been skipped
V3 CI/CD Pipeline / statusline generator delegation smoke (#2195) (push) Failing after 1s
V3 CI/CD Pipeline / wizard init regression guard (#2206 (push) Failing after 1s
V3 CI/CD Pipeline / memory no-stray-db smoke (ADR-125 P7) (push) Failing after 1s
V3 CI/CD Pipeline / github-safe injection smoke (#2089, ADR-127 Phase 1) (push) Failing after 1s
V3 CI/CD Pipeline / github actions pin smoke (#2089, ADR-127 Phase 1) (push) Failing after 1s
V3 CI/CD Pipeline / github attribution opt-in smoke (#2089, ADR-127 Phase 4) (push) Failing after 1s
V3 CI/CD Pipeline / pre-bash hook safety smoke (#2017) (push) Failing after 1s
V3 CI/CD Pipeline / Memory import smoke / ubuntu-latest (push) Failing after 0s
V3 CI/CD Pipeline / MCP protocol smoke / ubuntu-latest (push) Failing after 2s
V3 CI/CD Pipeline / ruvllm WASM auto-init smoke (#2086) (push) Failing after 4s
V3 CI/CD Pipeline / MCP paired-tool round-trip smoke (#1889) (push) Failing after 1s
V3 CI/CD Pipeline / Plugin package install-safety (#1902/#1903/#1904) (push) Failing after 1s
V3 CI/CD Pipeline / Tool description discoverability (ADR-112) (push) Failing after 3s
V3 CI/CD Pipeline / CLI npx-install smoke (#1147 / (22) (push) Failing after 1s
V3 CI/CD Pipeline / CLI npx-install smoke (#1147 / (24) (push) Failing after 1s
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / ubuntu-latest (push) Failing after 2s
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / ubuntu-latest (push) Failing after 1s
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / ubuntu-latest (push) Failing after 1s
V3 CI/CD Pipeline / Vector-index dimension audit (#1947) (push) Failing after 0s
V3 CI/CD Pipeline / Hook-command install safety (#1921) (push) Failing after 1s
V3 CI/CD Pipeline / ToolOutputGuardrail smoke (ADR-131, (push) Failing after 1s
V3 CI/CD Pipeline / init-bundle invariants smoke (#2095, ADR-128 Phase 5) (push) Failing after 1s
V3 CI/CD Pipeline / wasm provider bridge smoke (ADR-129 P1) (push) Failing after 2s
V3 CI/CD Pipeline / wasm gallery CRUD smoke (ADR-129 P3) (push) Failing after 1s
V3 CI/CD Pipeline / wasm plugin bridge smoke (ADR-129 P4) (push) Failing after 0s
V3 CI/CD Pipeline / wasm compose smoke (ADR-129 P2) (push) Failing after 4s
V3 CI/CD Pipeline / graph schema smoke (ADR-130 P1) (push) Failing after 0s
Validate Marketplace / validate (push) Failing after 1s
🔍 Verification Pipeline / 🚀 Setup Verification (push) Failing after 1s
🔍 Verification Pipeline / 🛡️ Security Verification (push) Has been skipped
🔍 Verification Pipeline / 📝 Code Quality (push) Has been skipped
🔍 Verification Pipeline / 🧪 Test Verification (${{ matrix.os }}, Node ${{ matrix.node }}) (push) Has been skipped
🔍 Verification Pipeline / 🏗️ Build Verification (push) Has been skipped
🔍 Verification Pipeline / 📚 Documentation Verification (push) Has been skipped
CVE Audit Gate / High-severity report (warn only) (push) Has been cancelled
🔄 Automated Rollback Manager / 🔄 Execute Rollback (push) Has been cancelled
🔄 Automated Rollback Manager / ✅ Post-Rollback Verification (push) Has been cancelled
🔄 Automated Rollback Manager / 📊 Rollback Monitoring (push) Has been cancelled
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / windows-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / windows-latest (push) Has been cancelled
🔄 Automated Rollback Manager / ⏳ Manual Rollback Approval (push) Has been cancelled
V3 CI/CD Pipeline / MCP protocol smoke / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Memory import smoke / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / windows-latest (push) Has been cancelled
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Witness verify (signed manifest) / macos-latest (push) Has been cancelled
V3 CI/CD Pipeline / Witness verify (signed manifest) / ubuntu-latest (push) Has been cancelled
V3 CI/CD Pipeline / Witness verify (signed manifest) / windows-latest (push) Has been cancelled
V3 CI/CD Pipeline / Publish to npm (alpha) (push) Has been cancelled
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / macos-latest / Node 22 (push) Has been cancelled
V3 CI/CD Pipeline / Plugin hooks smoke / macos-latest / Node 22 (push) Has been cancelled
CI/CD Pipeline / Deploy & Release (push) Has been cancelled
CI/CD Pipeline / CI Status (push) Has been cancelled
🔗 Cross-Agent Integration Tests / 📊 Integration Test Report (push) Has been cancelled
🔄 Automated Rollback Manager / 🔍 Pre-Rollback Validation (push) Has been cancelled
🔍 Verification Pipeline / ⚡ Performance Verification (push) Has been cancelled
🔍 Verification Pipeline / 📊 Verification Report (push) Has been cancelled
630 lines
19 KiB
TypeScript
630 lines
19 KiB
TypeScript
/**
|
|
* Coverage Analysis Tool Tests
|
|
*
|
|
* Tests for the aqe/analyze-coverage MCP tool that provides
|
|
* O(log n) gap detection using Johnson-Lindenstrauss algorithm.
|
|
*/
|
|
|
|
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
|
|
// ============================================================================
|
|
// Mock Types
|
|
// ============================================================================
|
|
|
|
interface AnalyzeCoverageInput {
|
|
coverageReport: string;
|
|
targetPath?: string;
|
|
algorithm?: 'johnson-lindenstrauss' | 'naive';
|
|
options?: {
|
|
minCoverage?: number;
|
|
maxGaps?: number;
|
|
includeUncovered?: boolean;
|
|
groupByFile?: boolean;
|
|
};
|
|
}
|
|
|
|
interface CoverageGap {
|
|
file: string;
|
|
startLine: number;
|
|
endLine: number;
|
|
type: 'statement' | 'branch' | 'function';
|
|
priority: 'low' | 'medium' | 'high' | 'critical';
|
|
complexity: number;
|
|
suggestion?: string;
|
|
}
|
|
|
|
interface CoverageMetrics {
|
|
statements: { covered: number; total: number; percentage: number };
|
|
branches: { covered: number; total: number; percentage: number };
|
|
functions: { covered: number; total: number; percentage: number };
|
|
lines: { covered: number; total: number; percentage: number };
|
|
}
|
|
|
|
interface AnalyzeCoverageOutput {
|
|
success: boolean;
|
|
gaps: CoverageGap[];
|
|
metrics: CoverageMetrics;
|
|
summary: {
|
|
totalGaps: number;
|
|
criticalGaps: number;
|
|
estimatedEffort: string;
|
|
recommendation: string;
|
|
};
|
|
performance: {
|
|
algorithm: string;
|
|
timeMs: number;
|
|
complexity: string;
|
|
};
|
|
errors?: string[];
|
|
}
|
|
|
|
// ============================================================================
|
|
// Mock Implementation
|
|
// ============================================================================
|
|
|
|
class MockAnalyzeCoverageTool {
|
|
private defaultMinCoverage = 80;
|
|
private defaultMaxGaps = 50;
|
|
|
|
async execute(input: AnalyzeCoverageInput): Promise<AnalyzeCoverageOutput> {
|
|
const startTime = performance.now();
|
|
|
|
// Validate input
|
|
const errors = this.validateInput(input);
|
|
if (errors.length > 0) {
|
|
return this.createErrorResponse(errors);
|
|
}
|
|
|
|
// Parse coverage report (mock)
|
|
const metrics = this.parseCoverageReport(input.coverageReport);
|
|
|
|
// Detect gaps using specified algorithm
|
|
const algorithm = input.algorithm ?? 'johnson-lindenstrauss';
|
|
const gaps = this.detectGaps(metrics, algorithm, input.options);
|
|
|
|
// Calculate priority and suggestions
|
|
const prioritizedGaps = this.prioritizeGaps(gaps);
|
|
|
|
const endTime = performance.now();
|
|
|
|
return {
|
|
success: true,
|
|
gaps: prioritizedGaps.slice(0, input.options?.maxGaps ?? this.defaultMaxGaps),
|
|
metrics,
|
|
summary: this.generateSummary(prioritizedGaps, metrics),
|
|
performance: {
|
|
algorithm,
|
|
timeMs: Math.round(endTime - startTime),
|
|
complexity: algorithm === 'johnson-lindenstrauss' ? 'O(log n)' : 'O(n)',
|
|
},
|
|
};
|
|
}
|
|
|
|
private validateInput(input: AnalyzeCoverageInput): string[] {
|
|
const errors: string[] = [];
|
|
|
|
if (!input.coverageReport) {
|
|
errors.push('coverageReport is required');
|
|
}
|
|
|
|
if (input.coverageReport && !this.isValidCoverageFormat(input.coverageReport)) {
|
|
errors.push('Invalid coverage report format');
|
|
}
|
|
|
|
if (input.options?.minCoverage !== undefined) {
|
|
if (input.options.minCoverage < 0 || input.options.minCoverage > 100) {
|
|
errors.push('minCoverage must be between 0 and 100');
|
|
}
|
|
}
|
|
|
|
if (input.options?.maxGaps !== undefined && input.options.maxGaps < 1) {
|
|
errors.push('maxGaps must be at least 1');
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
private isValidCoverageFormat(report: string): boolean {
|
|
// Mock validation - in real impl would check for lcov, cobertura, etc.
|
|
return report.includes('coverage') || report.endsWith('.info') || report.endsWith('.json');
|
|
}
|
|
|
|
private parseCoverageReport(report: string): CoverageMetrics {
|
|
// Mock coverage parsing - returns realistic mock data
|
|
return {
|
|
statements: { covered: 450, total: 600, percentage: 75 },
|
|
branches: { covered: 120, total: 200, percentage: 60 },
|
|
functions: { covered: 85, total: 100, percentage: 85 },
|
|
lines: { covered: 430, total: 580, percentage: 74.1 },
|
|
};
|
|
}
|
|
|
|
private detectGaps(
|
|
metrics: CoverageMetrics,
|
|
algorithm: string,
|
|
options?: AnalyzeCoverageInput['options']
|
|
): CoverageGap[] {
|
|
const gaps: CoverageGap[] = [];
|
|
|
|
// Mock gap detection
|
|
// Statement gaps
|
|
const uncoveredStatements = metrics.statements.total - metrics.statements.covered;
|
|
for (let i = 0; i < Math.min(uncoveredStatements / 10, 20); i++) {
|
|
gaps.push({
|
|
file: `src/module${i % 5}.ts`,
|
|
startLine: 10 + i * 15,
|
|
endLine: 12 + i * 15,
|
|
type: 'statement',
|
|
priority: 'medium',
|
|
complexity: Math.floor(Math.random() * 10) + 1,
|
|
});
|
|
}
|
|
|
|
// Branch gaps (higher priority)
|
|
const uncoveredBranches = metrics.branches.total - metrics.branches.covered;
|
|
for (let i = 0; i < Math.min(uncoveredBranches / 10, 15); i++) {
|
|
gaps.push({
|
|
file: `src/conditionals${i % 3}.ts`,
|
|
startLine: 20 + i * 10,
|
|
endLine: 25 + i * 10,
|
|
type: 'branch',
|
|
priority: 'high',
|
|
complexity: Math.floor(Math.random() * 15) + 5,
|
|
});
|
|
}
|
|
|
|
// Function gaps
|
|
const uncoveredFunctions = metrics.functions.total - metrics.functions.covered;
|
|
for (let i = 0; i < uncoveredFunctions; i++) {
|
|
gaps.push({
|
|
file: `src/functions${i % 4}.ts`,
|
|
startLine: 50 + i * 20,
|
|
endLine: 65 + i * 20,
|
|
type: 'function',
|
|
priority: 'low',
|
|
complexity: Math.floor(Math.random() * 20) + 10,
|
|
});
|
|
}
|
|
|
|
return gaps;
|
|
}
|
|
|
|
private prioritizeGaps(gaps: CoverageGap[]): CoverageGap[] {
|
|
// Sort by priority and add suggestions
|
|
const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
|
|
return gaps
|
|
.map((gap) => ({
|
|
...gap,
|
|
// Upgrade priority for high complexity gaps
|
|
priority:
|
|
gap.complexity > 15 && gap.priority !== 'critical'
|
|
? ('high' as const)
|
|
: gap.priority,
|
|
suggestion: this.generateSuggestion(gap),
|
|
}))
|
|
.sort((a, b) => {
|
|
const priorityDiff = priorityOrder[a.priority] - priorityOrder[b.priority];
|
|
if (priorityDiff !== 0) return priorityDiff;
|
|
return b.complexity - a.complexity;
|
|
});
|
|
}
|
|
|
|
private generateSuggestion(gap: CoverageGap): string {
|
|
switch (gap.type) {
|
|
case 'branch':
|
|
return 'Add test cases for both true and false conditions';
|
|
case 'function':
|
|
return 'Add unit tests for this function with various inputs';
|
|
case 'statement':
|
|
return 'Ensure this code path is executed in tests';
|
|
default:
|
|
return 'Add test coverage for this area';
|
|
}
|
|
}
|
|
|
|
private generateSummary(
|
|
gaps: CoverageGap[],
|
|
metrics: CoverageMetrics
|
|
): AnalyzeCoverageOutput['summary'] {
|
|
const criticalGaps = gaps.filter((g) => g.priority === 'critical').length;
|
|
const highGaps = gaps.filter((g) => g.priority === 'high').length;
|
|
|
|
const totalComplexity = gaps.reduce((sum, g) => sum + g.complexity, 0);
|
|
const estimatedHours = Math.ceil(totalComplexity / 10);
|
|
|
|
let recommendation = 'Coverage is acceptable';
|
|
if (metrics.lines.percentage < 50) {
|
|
recommendation = 'Critical: Significant test coverage needed';
|
|
} else if (metrics.lines.percentage < 70) {
|
|
recommendation = 'Warning: Focus on high-priority gaps first';
|
|
} else if (metrics.lines.percentage < 80) {
|
|
recommendation = 'Good progress, continue adding tests for branch coverage';
|
|
}
|
|
|
|
return {
|
|
totalGaps: gaps.length,
|
|
criticalGaps: criticalGaps + highGaps,
|
|
estimatedEffort: `${estimatedHours} hours`,
|
|
recommendation,
|
|
};
|
|
}
|
|
|
|
private createErrorResponse(errors: string[]): AnalyzeCoverageOutput {
|
|
return {
|
|
success: false,
|
|
gaps: [],
|
|
metrics: {
|
|
statements: { covered: 0, total: 0, percentage: 0 },
|
|
branches: { covered: 0, total: 0, percentage: 0 },
|
|
functions: { covered: 0, total: 0, percentage: 0 },
|
|
lines: { covered: 0, total: 0, percentage: 0 },
|
|
},
|
|
summary: {
|
|
totalGaps: 0,
|
|
criticalGaps: 0,
|
|
estimatedEffort: '0 hours',
|
|
recommendation: 'Fix validation errors first',
|
|
},
|
|
performance: {
|
|
algorithm: 'none',
|
|
timeMs: 0,
|
|
complexity: 'N/A',
|
|
},
|
|
errors,
|
|
};
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Tests
|
|
// ============================================================================
|
|
|
|
describe('AnalyzeCoverageTool', () => {
|
|
let tool: MockAnalyzeCoverageTool;
|
|
|
|
beforeEach(() => {
|
|
tool = new MockAnalyzeCoverageTool();
|
|
});
|
|
|
|
describe('input validation', () => {
|
|
it('should require coverageReport', async () => {
|
|
const result = await tool.execute({ coverageReport: '' });
|
|
|
|
expect(result.success).toBe(false);
|
|
expect(result.errors).toContain('coverageReport is required');
|
|
});
|
|
|
|
it('should validate coverage report format', async () => {
|
|
const result = await tool.execute({ coverageReport: 'invalid-format' });
|
|
|
|
expect(result.success).toBe(false);
|
|
expect(result.errors).toContain('Invalid coverage report format');
|
|
});
|
|
|
|
it('should accept lcov format', async () => {
|
|
const result = await tool.execute({ coverageReport: './coverage/lcov.info' });
|
|
|
|
expect(result.success).toBe(true);
|
|
});
|
|
|
|
it('should accept json format', async () => {
|
|
const result = await tool.execute({ coverageReport: './coverage/coverage.json' });
|
|
|
|
expect(result.success).toBe(true);
|
|
});
|
|
|
|
it('should validate minCoverage range', async () => {
|
|
const negativeResult = await tool.execute({
|
|
coverageReport: './coverage.info',
|
|
options: { minCoverage: -10 },
|
|
});
|
|
|
|
const overResult = await tool.execute({
|
|
coverageReport: './coverage.info',
|
|
options: { minCoverage: 110 },
|
|
});
|
|
|
|
expect(negativeResult.success).toBe(false);
|
|
expect(overResult.success).toBe(false);
|
|
});
|
|
|
|
it('should validate maxGaps minimum', async () => {
|
|
const result = await tool.execute({
|
|
coverageReport: './coverage.info',
|
|
options: { maxGaps: 0 },
|
|
});
|
|
|
|
expect(result.success).toBe(false);
|
|
expect(result.errors.some(e => e.includes('maxGaps'))).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('gap detection', () => {
|
|
it('should detect coverage gaps', async () => {
|
|
const result = await tool.execute({ coverageReport: './coverage.info' });
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.gaps.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('should include gap file path', async () => {
|
|
const result = await tool.execute({ coverageReport: './coverage.info' });
|
|
|
|
for (const gap of result.gaps) {
|
|
expect(gap.file).toBeTruthy();
|
|
expect(gap.file).toMatch(/\.ts$/);
|
|
}
|
|
});
|
|
|
|
it('should include gap line numbers', async () => {
|
|
const result = await tool.execute({ coverageReport: './coverage.info' });
|
|
|
|
for (const gap of result.gaps) {
|
|
expect(gap.startLine).toBeGreaterThan(0);
|
|
expect(gap.endLine).toBeGreaterThanOrEqual(gap.startLine);
|
|
}
|
|
});
|
|
|
|
it('should categorize gaps by type', async () => {
|
|
const result = await tool.execute({ coverageReport: './coverage.info' });
|
|
|
|
const types = new Set(result.gaps.map((g) => g.type));
|
|
expect(types.has('statement')).toBe(true);
|
|
expect(types.has('branch')).toBe(true);
|
|
expect(types.has('function')).toBe(true);
|
|
});
|
|
|
|
it('should assign priority to gaps', async () => {
|
|
const result = await tool.execute({ coverageReport: './coverage.info' });
|
|
|
|
for (const gap of result.gaps) {
|
|
expect(['low', 'medium', 'high', 'critical']).toContain(gap.priority);
|
|
}
|
|
});
|
|
|
|
it('should include complexity score', async () => {
|
|
const result = await tool.execute({ coverageReport: './coverage.info' });
|
|
|
|
for (const gap of result.gaps) {
|
|
expect(gap.complexity).toBeGreaterThan(0);
|
|
}
|
|
});
|
|
|
|
it('should include suggestions', async () => {
|
|
const result = await tool.execute({ coverageReport: './coverage.info' });
|
|
|
|
for (const gap of result.gaps) {
|
|
expect(gap.suggestion).toBeTruthy();
|
|
}
|
|
});
|
|
|
|
it('should respect maxGaps limit', async () => {
|
|
const result = await tool.execute({
|
|
coverageReport: './coverage.info',
|
|
options: { maxGaps: 5 },
|
|
});
|
|
|
|
expect(result.gaps.length).toBeLessThanOrEqual(5);
|
|
});
|
|
});
|
|
|
|
describe('algorithm selection', () => {
|
|
it('should use johnson-lindenstrauss by default', async () => {
|
|
const result = await tool.execute({ coverageReport: './coverage.info' });
|
|
|
|
expect(result.performance.algorithm).toBe('johnson-lindenstrauss');
|
|
expect(result.performance.complexity).toBe('O(log n)');
|
|
});
|
|
|
|
it('should support naive algorithm', async () => {
|
|
const result = await tool.execute({
|
|
coverageReport: './coverage.info',
|
|
algorithm: 'naive',
|
|
});
|
|
|
|
expect(result.performance.algorithm).toBe('naive');
|
|
expect(result.performance.complexity).toBe('O(n)');
|
|
});
|
|
|
|
it('should report execution time', async () => {
|
|
const result = await tool.execute({ coverageReport: './coverage.info' });
|
|
|
|
expect(result.performance.timeMs).toBeGreaterThanOrEqual(0);
|
|
});
|
|
});
|
|
|
|
describe('metrics reporting', () => {
|
|
it('should include statement coverage', async () => {
|
|
const result = await tool.execute({ coverageReport: './coverage.info' });
|
|
|
|
expect(result.metrics.statements.covered).toBeGreaterThan(0);
|
|
expect(result.metrics.statements.total).toBeGreaterThan(0);
|
|
expect(result.metrics.statements.percentage).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('should include branch coverage', async () => {
|
|
const result = await tool.execute({ coverageReport: './coverage.info' });
|
|
|
|
expect(result.metrics.branches.covered).toBeDefined();
|
|
expect(result.metrics.branches.total).toBeDefined();
|
|
expect(result.metrics.branches.percentage).toBeDefined();
|
|
});
|
|
|
|
it('should include function coverage', async () => {
|
|
const result = await tool.execute({ coverageReport: './coverage.info' });
|
|
|
|
expect(result.metrics.functions.covered).toBeGreaterThan(0);
|
|
expect(result.metrics.functions.total).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('should include line coverage', async () => {
|
|
const result = await tool.execute({ coverageReport: './coverage.info' });
|
|
|
|
expect(result.metrics.lines.covered).toBeGreaterThan(0);
|
|
expect(result.metrics.lines.total).toBeGreaterThan(0);
|
|
expect(result.metrics.lines.percentage).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
describe('summary generation', () => {
|
|
it('should include total gap count', async () => {
|
|
const result = await tool.execute({ coverageReport: './coverage.info' });
|
|
|
|
expect(result.summary.totalGaps).toBe(result.gaps.length);
|
|
});
|
|
|
|
it('should count critical gaps', async () => {
|
|
const result = await tool.execute({ coverageReport: './coverage.info' });
|
|
|
|
const criticalAndHigh = result.gaps.filter(
|
|
(g) => g.priority === 'critical' || g.priority === 'high'
|
|
).length;
|
|
expect(result.summary.criticalGaps).toBe(criticalAndHigh);
|
|
});
|
|
|
|
it('should estimate effort', async () => {
|
|
const result = await tool.execute({ coverageReport: './coverage.info' });
|
|
|
|
expect(result.summary.estimatedEffort).toMatch(/\d+ hours/);
|
|
});
|
|
|
|
it('should provide recommendation', async () => {
|
|
const result = await tool.execute({ coverageReport: './coverage.info' });
|
|
|
|
expect(result.summary.recommendation).toBeTruthy();
|
|
});
|
|
});
|
|
|
|
describe('gap prioritization', () => {
|
|
it('should sort gaps by priority', async () => {
|
|
const result = await tool.execute({ coverageReport: './coverage.info' });
|
|
|
|
const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
let lastPriority = -1;
|
|
|
|
for (const gap of result.gaps) {
|
|
const currentPriority = priorityOrder[gap.priority];
|
|
expect(currentPriority).toBeGreaterThanOrEqual(lastPriority);
|
|
if (currentPriority > lastPriority) {
|
|
lastPriority = currentPriority;
|
|
}
|
|
}
|
|
});
|
|
|
|
it('should upgrade priority for high complexity gaps', async () => {
|
|
const result = await tool.execute({ coverageReport: './coverage.info' });
|
|
|
|
// High complexity gaps should not be 'low' priority
|
|
const highComplexityLowPriority = result.gaps.filter(
|
|
(g) => g.complexity > 15 && g.priority === 'low'
|
|
);
|
|
expect(highComplexityLowPriority.length).toBe(0);
|
|
});
|
|
|
|
it('should provide type-specific suggestions', async () => {
|
|
const result = await tool.execute({ coverageReport: './coverage.info' });
|
|
|
|
const branchGap = result.gaps.find((g) => g.type === 'branch');
|
|
const functionGap = result.gaps.find((g) => g.type === 'function');
|
|
|
|
if (branchGap) {
|
|
expect(branchGap.suggestion).toContain('condition');
|
|
}
|
|
if (functionGap) {
|
|
expect(functionGap.suggestion).toContain('unit test');
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('AnalyzeCoverageTool Performance', () => {
|
|
let tool: MockAnalyzeCoverageTool;
|
|
|
|
beforeEach(() => {
|
|
tool = new MockAnalyzeCoverageTool();
|
|
});
|
|
|
|
it('should complete analysis in reasonable time', async () => {
|
|
const startTime = performance.now();
|
|
|
|
const result = await tool.execute({ coverageReport: './coverage.info' });
|
|
|
|
const duration = performance.now() - startTime;
|
|
|
|
expect(result.success).toBe(true);
|
|
// Should complete in under 100ms for mock data
|
|
expect(duration).toBeLessThan(100);
|
|
});
|
|
|
|
it('should report O(log n) complexity for J-L algorithm', async () => {
|
|
const result = await tool.execute({
|
|
coverageReport: './coverage.info',
|
|
algorithm: 'johnson-lindenstrauss',
|
|
});
|
|
|
|
expect(result.performance.complexity).toBe('O(log n)');
|
|
});
|
|
|
|
it('should report O(n) complexity for naive algorithm', async () => {
|
|
const result = await tool.execute({
|
|
coverageReport: './coverage.info',
|
|
algorithm: 'naive',
|
|
});
|
|
|
|
expect(result.performance.complexity).toBe('O(n)');
|
|
});
|
|
});
|
|
|
|
describe('AnalyzeCoverageTool Edge Cases', () => {
|
|
let tool: MockAnalyzeCoverageTool;
|
|
|
|
beforeEach(() => {
|
|
tool = new MockAnalyzeCoverageTool();
|
|
});
|
|
|
|
it('should handle path with targetPath filter', async () => {
|
|
const result = await tool.execute({
|
|
coverageReport: './coverage.info',
|
|
targetPath: './src/auth',
|
|
});
|
|
|
|
expect(result.success).toBe(true);
|
|
});
|
|
|
|
it('should handle includeUncovered option', async () => {
|
|
const result = await tool.execute({
|
|
coverageReport: './coverage.info',
|
|
options: { includeUncovered: true },
|
|
});
|
|
|
|
expect(result.success).toBe(true);
|
|
});
|
|
|
|
it('should handle groupByFile option', async () => {
|
|
const result = await tool.execute({
|
|
coverageReport: './coverage.info',
|
|
options: { groupByFile: true },
|
|
});
|
|
|
|
expect(result.success).toBe(true);
|
|
});
|
|
|
|
it('should handle all options combined', async () => {
|
|
const result = await tool.execute({
|
|
coverageReport: './coverage.info',
|
|
targetPath: './src',
|
|
algorithm: 'johnson-lindenstrauss',
|
|
options: {
|
|
minCoverage: 80,
|
|
maxGaps: 25,
|
|
includeUncovered: true,
|
|
groupByFile: true,
|
|
},
|
|
});
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.gaps.length).toBeLessThanOrEqual(25);
|
|
});
|
|
});
|