veza/scripts/flaky-detection.mjs

136 lines
3.9 KiB
JavaScript
Raw Permalink Normal View History

chore(cleanup): remove orphan code + archive disabled workflows + .playwright-mcp Triple cleanup, landed together because they share the same cleanup branch intent and touch non-overlapping trees. 1. 38× tracked .playwright-mcp/*.yml stage-deleted MCP session recordings that had been inadvertently committed. .gitignore already covers .playwright-mcp/ (post-audit J2 block added in d12b901de). Working tree copies removed separately. 2. 19× disabled CI workflows moved to docs/archive/workflows/ Legacy .yml.disabled files in .github/workflows/ were 1676 LOC of dead config (backend-ci, cd, staging-validation, accessibility, chromatic, visual-regression, storybook-audit, contract-testing, zap-dast, container-scan, semgrep, sast, mutation-testing, rust-mutation, load-test-nightly, flaky-report, openapi-lint, commitlint, performance). Preserved in docs/archive/workflows/ for historical reference; `.github/workflows/` now only lists the 5 actually-running pipelines. 3. Orphan code removed (0 consumers confirmed via grep) - veza-backend-api/internal/repository/user_repository.go In-memory UserRepository mock, never imported anywhere. - proto/chat/chat.proto Chat server Rust deleted 2026-02-22 (commit 279a10d31); proto file was orphan spec. Chat lives 100% in Go backend now. - veza-common/src/types/chat.rs (Conversation, Message, MessageType, Attachment, Reaction) - veza-common/src/types/websocket.rs (WebSocketMessage, PresenceStatus, CallType — depended on chat::MessageType) - veza-common/src/types/mod.rs updated: removed `pub mod chat;`, `pub mod websocket;`, and their re-exports. Only `veza_common::logging` is consumed by veza-stream-server (verified with `grep -r "veza_common::"`). `cargo check` on veza-common passes post-removal. Refs: AUDIT_REPORT.md §8.2 "Code mort / orphelin" + §9.1. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 18:33:40 +00:00
#!/usr/bin/env node
/**
* Flaky Test Detection Script
*
* Analyzes Playwright JSON results to detect flaky tests (tests that passed on retry).
* Usage: node scripts/flaky-detection.mjs [results-dir]
*
* Output: Markdown report to stdout, suitable for piping to a file or GitHub comment.
*/
import { readFileSync, readdirSync, existsSync } from 'fs';
import { join } from 'path';
const resultsDir = process.argv[2] || 'tests/e2e/test-results';
const resultsFile = process.argv[3] || 'tests/e2e/test-results/results.json';
function analyzeResults(filePath) {
if (!existsSync(filePath)) {
console.error(`Results file not found: ${filePath}`);
return null;
}
const raw = JSON.parse(readFileSync(filePath, 'utf8'));
const suites = raw.suites || [];
const flaky = [];
const failed = [];
const slow = [];
function walkSpecs(specs, suitePath = '') {
for (const spec of specs) {
const fullTitle = suitePath ? `${suitePath} > ${spec.title}` : spec.title;
for (const test of spec.tests || []) {
const results = test.results || [];
// Flaky: passed eventually but had retries
if (test.status === 'expected' && results.length > 1) {
flaky.push({
title: fullTitle,
retries: results.length - 1,
file: spec.file || 'unknown',
});
}
// Failed
if (test.status === 'unexpected') {
failed.push({
title: fullTitle,
file: spec.file || 'unknown',
error: results[results.length - 1]?.error?.message?.slice(0, 200) || 'unknown',
});
}
// Slow (> 30s)
const duration = results.reduce((sum, r) => sum + (r.duration || 0), 0);
if (duration > 30000) {
slow.push({
title: fullTitle,
duration: Math.round(duration / 1000),
file: spec.file || 'unknown',
});
}
}
}
}
function walkSuites(suites, path = '') {
for (const suite of suites) {
const suitePath = path ? `${path} > ${suite.title}` : suite.title;
walkSpecs(suite.specs || [], suitePath);
walkSuites(suite.suites || [], suitePath);
}
}
walkSuites(suites);
return { flaky, failed, slow };
}
function generateReport(analysis) {
if (!analysis) return '# Flaky Test Report\n\nNo results file found.\n';
const { flaky, failed, slow } = analysis;
const lines = ['# Flaky Test Report', ''];
lines.push(`Generated: ${new Date().toISOString()}`, '');
// Flaky tests
lines.push(`## Flaky Tests (${flaky.length})`, '');
if (flaky.length === 0) {
lines.push('No flaky tests detected.', '');
} else {
lines.push('| Test | Retries | File |');
lines.push('|------|---------|------|');
for (const t of flaky.sort((a, b) => b.retries - a.retries)) {
lines.push(`| ${t.title} | ${t.retries} | \`${t.file}\` |`);
}
lines.push('');
}
// Failed tests
lines.push(`## Failed Tests (${failed.length})`, '');
if (failed.length === 0) {
lines.push('No failed tests.', '');
} else {
lines.push('| Test | Error | File |');
lines.push('|------|-------|------|');
for (const t of failed) {
const safeError = t.error.replace(/\|/g, '\\|').replace(/\n/g, ' ');
lines.push(`| ${t.title} | ${safeError} | \`${t.file}\` |`);
}
lines.push('');
}
// Slow tests
lines.push(`## Slow Tests (> 30s) (${slow.length})`, '');
if (slow.length === 0) {
lines.push('No slow tests.', '');
} else {
lines.push('| Test | Duration | File |');
lines.push('|------|----------|------|');
for (const t of slow.sort((a, b) => b.duration - a.duration)) {
lines.push(`| ${t.title} | ${t.duration}s | \`${t.file}\` |`);
}
lines.push('');
}
return lines.join('\n');
}
const analysis = analyzeResults(resultsFile);
console.log(generateReport(analysis));
if (analysis && analysis.flaky.length > 0) {
process.exit(0); // Flaky tests are warnings, not failures
}