From 661a51084e68f0ff405cb94a0061a72b1be3778a Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 18 Feb 2026 20:17:01 +0000 Subject: [PATCH] feat(test-audit): Add daily test integrity and coverage audit workflow - Adds `scripts/run-test-audit.sh` to automate unit test execution with coverage. - Adds `scripts/analyze-flakiness.mjs` to detect test flakiness. - Adds `scripts/static-test-analysis.mjs` to find suspicious test patterns. - Adds `scripts/coverage-gap-analysis.mjs` to identify critical coverage gaps. - Adds `scripts/generate-audit-report.mjs` to produce markdown reports. - Generates initial audit report `test-audit-report-2025-02-18.md` and `issues/ISSUE_test_audit_findings.md`. - Documents findings in `decisions/DECISIONS_2025-02-18.md`. Co-authored-by: PR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> --- context/CONTEXT_2025-02-18.md | 20 + decisions/DECISIONS_2025-02-18.md | 11 + issues/ISSUE_test_audit_findings.md | 22 + scripts/analyze-flakiness.mjs | 84 ++ scripts/coverage-gap-analysis.mjs | 84 ++ scripts/generate-audit-report.mjs | 126 +++ scripts/run-test-audit.sh | 44 + scripts/static-test-analysis.mjs | 78 ++ test-audit-report-2025-02-18.md | 266 ++++- test-audit-report-2026-02-18.md | 252 +++++ test-audit/coverage-gaps.json | 43 +- test-audit/flakiness-matrix.json | 1423 ++++++++++++++++++++++++++- test-audit/suspicious-tests.json | 239 +++-- todo/TODO_2025-02-18.md | 9 + 14 files changed, 2576 insertions(+), 125 deletions(-) create mode 100644 context/CONTEXT_2025-02-18.md create mode 100644 decisions/DECISIONS_2025-02-18.md create mode 100644 issues/ISSUE_test_audit_findings.md create mode 100644 scripts/analyze-flakiness.mjs create mode 100644 scripts/coverage-gap-analysis.mjs create mode 100644 scripts/generate-audit-report.mjs create mode 100755 scripts/run-test-audit.sh create mode 100644 scripts/static-test-analysis.mjs create mode 100644 test-audit-report-2026-02-18.md create mode 100644 todo/TODO_2025-02-18.md diff --git a/context/CONTEXT_2025-02-18.md b/context/CONTEXT_2025-02-18.md new file mode 100644 index 00000000..72dc41ac --- /dev/null +++ b/context/CONTEXT_2025-02-18.md @@ -0,0 +1,20 @@ +# Test Audit Context - 2025-02-18 + +## Mission +To audit the test suite for behavior fidelity, determinism, and cheat vectors. The goal is to build a daily test-audit workflow that identifies flaky tests, suspicious patterns, and coverage gaps in critical modules. + +## Scope +- Unit tests (`tests/` excluding `visual/` and `e2e/`). +- Critical modules: + - `js/services/authService.js` + - `js/relayManager.js` + - `js/nostr/dmDecryptWorker.js` + - `js/nostr/watchHistory.js` + - `js/userBlocks.js` + +## Definition of Done +- `test-audit-report-YYYY-MM-DD.md` generated. +- `test-audit/flakiness-matrix.json` generated. +- `test-audit/suspicious-tests.json` generated. +- `test-audit/coverage-gaps.json` generated. +- Prioritized remediation tasks identified. diff --git a/decisions/DECISIONS_2025-02-18.md b/decisions/DECISIONS_2025-02-18.md new file mode 100644 index 00000000..ae1dec90 --- /dev/null +++ b/decisions/DECISIONS_2025-02-18.md @@ -0,0 +1,11 @@ +# Decisions - 2025-02-18 + +## Test Audit Implementation +- **Tooling**: Used `c8` for coverage and custom Node.js scripts for flakiness/static analysis. +- **Timeout**: Experienced timeouts with full suite coverage run; mitigation was to use the partial results which were sufficient for initial audit. +- **Coverage**: Mapped coverage to critical files using `coverage-summary.json`. +- **Artifacts**: Stored all artifacts in `test-audit/` for historical tracking. + +## Findings +- Confirmed that critical modules (`userBlocks`, `relayManager`) have low test coverage. +- Identified widespread use of `sleep` and `fetch` in unit tests, indicating a need for better test isolation and determinism helpers. diff --git a/issues/ISSUE_test_audit_findings.md b/issues/ISSUE_test_audit_findings.md new file mode 100644 index 00000000..3a3c67c7 --- /dev/null +++ b/issues/ISSUE_test_audit_findings.md @@ -0,0 +1,22 @@ +# Test Audit Findings - 2025-02-18 + +## Overview +The daily test audit has identified significant coverage gaps and suspicious patterns in the test suite. + +## Critical Coverage Gaps (< 70%) +1. **js/userBlocks.js** (25.77% statements) - Critical for moderation. +2. **js/relayManager.js** (56.75% statements) - Core networking logic. +3. **js/nostr/watchHistory.js** (58.14% statements) - User data integrity. +4. **js/services/authService.js** (Branches: 62.71%) - Auth flow reliability. +5. **js/nostr/dmDecryptWorker.js** (Branches: 31.66%) - Privacy/Security. + +## Suspicious Patterns +- **91 files** contain suspicious patterns. +- **Network Dependence**: High usage of `fetch` / `WebSocket` without apparent mocking in unit tests (e.g., `tests/nostr/client.test.mjs`, `tests/services/attachmentService.test.mjs`). +- **Time Dependence**: Frequent use of `sleep`/`setTimeout` (e.g., `tests/login-modal-controller.test.mjs`). +- **Console Usage**: Tests logging to console (e.g., `tests/nostr/watchHistory.test.js` has 19 occurrences). + +## Recommended Actions +1. **Immediate**: Create a "fixit" sprint to address coverage in `js/userBlocks.js` and `js/relayManager.js`. +2. **Refactor**: Replace `sleep()` calls with `waitFor()` or mock timers in `tests/login-modal-controller.test.mjs`. +3. **Infrastructure**: Introduce `msw` or `nock` for network mocking to remove `fetch` dependencies in unit tests. diff --git a/scripts/analyze-flakiness.mjs b/scripts/analyze-flakiness.mjs new file mode 100644 index 00000000..eaf193b5 --- /dev/null +++ b/scripts/analyze-flakiness.mjs @@ -0,0 +1,84 @@ +import fs from 'node:fs'; +import path from 'node:path'; + +const logDir = 'test-audit'; +const matrixFile = path.join(logDir, 'flakiness-matrix.json'); + +function parseLog(content) { + const results = {}; + const lines = content.split('\n'); + let currentFile = null; + + for (const line of lines) { + // "→ Running tests/..." + const runningMatch = line.match(/→ Running (.+)/); + if (runningMatch) { + currentFile = runningMatch[1]; + if (!results[currentFile]) { + results[currentFile] = 'PASS'; // Assume pass until failure found + } + continue; + } + + // "✖ tests/... failed with exit code ..." + const failMatch = line.match(/✖ (.+) failed with exit code/); + if (failMatch) { + results[failMatch[1]] = 'FAIL'; + continue; + } + + // TAP failures + if (line.trim().startsWith('not ok')) { + // Ideally we'd map this to the specific test case, but for now map to the file + if (currentFile) { + results[currentFile] = 'FAIL'; + } + } + } + return results; +} + +function main() { + const matrix = {}; + const runFiles = fs.readdirSync(logDir).filter(f => f.startsWith('run-') && f.endsWith('.log')); + + if (runFiles.length === 0) { + console.log('No run logs found.'); + return; + } + + console.log(`Analyzing ${runFiles.length} runs...`); + + for (const file of runFiles) { + const content = fs.readFileSync(path.join(logDir, file), 'utf8'); + const runResults = parseLog(content); + + for (const [testFile, status] of Object.entries(runResults)) { + if (!matrix[testFile]) { + matrix[testFile] = []; + } + matrix[testFile].push(status); + } + } + + // Identify flaky tests + const flakyTests = {}; + for (const [testFile, statuses] of Object.entries(matrix)) { + const hasPass = statuses.includes('PASS'); + const hasFail = statuses.includes('FAIL'); + if (hasPass && hasFail) { + flakyTests[testFile] = { statuses, verdict: 'FLAKY' }; + } else if (hasFail) { + flakyTests[testFile] = { statuses, verdict: 'FAIL' }; + } else { + // Consistent PASS, ignore for report to save space, or include? + // Let's include everything for completeness + flakyTests[testFile] = { statuses, verdict: 'PASS' }; + } + } + + fs.writeFileSync(matrixFile, JSON.stringify(flakyTests, null, 2)); + console.log(`Wrote flakiness matrix to ${matrixFile}`); +} + +main(); diff --git a/scripts/coverage-gap-analysis.mjs b/scripts/coverage-gap-analysis.mjs new file mode 100644 index 00000000..51ea6a3c --- /dev/null +++ b/scripts/coverage-gap-analysis.mjs @@ -0,0 +1,84 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const rootDir = path.dirname(path.dirname(fileURLToPath(import.meta.url))); +let coverageFile = path.join(rootDir, 'test-audit', 'coverage', 'coverage', 'coverage-summary.json'); +const outputFile = path.join(rootDir, 'test-audit', 'coverage-gaps.json'); + +const criticalFiles = [ + 'js/services/authService.js', + 'js/relayManager.js', + 'js/nostr/dmDecryptWorker.js', + 'js/nostr/watchHistory.js', + 'js/userBlocks.js' +]; + +async function main() { + if (!fs.existsSync(coverageFile)) { + const altPath = path.join(rootDir, 'test-audit', 'coverage', 'coverage-summary.json'); + if (fs.existsSync(altPath)) { + coverageFile = altPath; + } else { + // One more fallback for potential flatter structure + const flatPath = path.join(rootDir, 'test-audit', 'coverage-summary.json'); + if (fs.existsSync(flatPath)) { + coverageFile = flatPath; + } else { + console.error(`Coverage file not found at ${coverageFile} or ${altPath}`); + process.exit(1); + } + } + } + + console.log(`Reading coverage from: ${coverageFile}`); + const coverage = JSON.parse(fs.readFileSync(coverageFile, 'utf8')); + const gaps = {}; + + // Create a normalized map of coverage keys (absolute paths) to relative paths + const normalizedCoverage = {}; + for (const key of Object.keys(coverage)) { + let relativeKey = path.relative(rootDir, key); + normalizedCoverage[relativeKey] = coverage[key]; + } + + for (const file of criticalFiles) { + const fileCoverage = normalizedCoverage[file]; + + if (!fileCoverage) { + gaps[file] = { + lines: 0, + statements: 0, + functions: 0, + branches: 0, + status: 'MISSING' + }; + continue; + } + + const { lines, statements, functions, branches } = fileCoverage; + // Check if any metric is < 70% + if (lines.pct < 70 || statements.pct < 70 || functions.pct < 70 || branches.pct < 70) { + gaps[file] = { + lines: lines.pct, + statements: statements.pct, + functions: functions.pct, + branches: branches.pct, + status: 'LOW_COVERAGE' + }; + } else { + gaps[file] = { + lines: lines.pct, + statements: statements.pct, + functions: functions.pct, + branches: branches.pct, + status: 'OK' + }; + } + } + + fs.writeFileSync(outputFile, JSON.stringify(gaps, null, 2)); + console.log(`Wrote coverage gaps to ${outputFile}`); +} + +main(); diff --git a/scripts/generate-audit-report.mjs b/scripts/generate-audit-report.mjs new file mode 100644 index 00000000..3d06b79e --- /dev/null +++ b/scripts/generate-audit-report.mjs @@ -0,0 +1,126 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { execSync } from 'node:child_process'; + +const rootDir = path.dirname(path.dirname(fileURLToPath(import.meta.url))); +const flakinessFile = path.join(rootDir, 'test-audit', 'flakiness-matrix.json'); +const suspiciousFile = path.join(rootDir, 'test-audit', 'suspicious-tests.json'); +const coverageFile = path.join(rootDir, 'test-audit', 'coverage-gaps.json'); +const outputFile = path.join(rootDir, 'test-audit-report-' + new Date().toISOString().split('T')[0] + '.md'); + +function readJson(file) { + if (fs.existsSync(file)) { + return JSON.parse(fs.readFileSync(file, 'utf8')); + } + return null; +} + +function main() { + const flakiness = readJson(flakinessFile); + const suspicious = readJson(suspiciousFile); + const coverage = readJson(coverageFile); + + const nodeVersion = process.version; + let npmVersion = 'unknown'; + try { + npmVersion = execSync('npm --version').toString().trim(); + } catch (e) {} + + let report = `# Test Audit Report - ${new Date().toISOString().split('T')[0]}\n\n`; + + report += `## Run Metadata\n`; + report += `- **Date**: ${new Date().toISOString()}\n`; + report += `- **Node Version**: ${nodeVersion}\n`; + report += `- **NPM Version**: ${npmVersion}\n`; + report += `- **Test Command**: \`npm run test:unit\` (with c8 coverage)\n\n`; + + // Summary + report += `## Summary\n`; + report += `- **Flaky Tests**: ${flakiness ? Object.keys(flakiness).filter(k => flakiness[k].verdict === 'FLAKY').length : 'N/A'}\n`; + report += `- **Suspicious Files**: ${suspicious ? Object.keys(suspicious).length : 'N/A'}\n`; + report += `- **Critical Coverage Gaps**: ${coverage ? Object.keys(coverage).filter(k => coverage[k].status === 'LOW_COVERAGE').length : 'N/A'}\n\n`; + + // Flakiness + report += `## Flakiness Detection\n`; + if (flakiness) { + const flakyTests = Object.keys(flakiness).filter(k => flakiness[k].verdict === 'FLAKY'); + if (flakyTests.length > 0) { + report += `Found ${flakyTests.length} flaky tests:\n`; + for (const test of flakyTests) { + report += `- **${test}**: ${flakiness[test].statuses.join(', ')}\n`; + } + } else { + report += `No flaky tests detected (based on run matrix).\n`; + } + } else { + report += `Flakiness matrix not found.\n`; + } + report += `\n`; + + // Suspicious Tests + report += `## Suspicious Patterns\n`; + if (suspicious) { + const files = Object.keys(suspicious); + if (files.length > 0) { + report += `Found suspicious patterns in ${files.length} files:\n`; + for (const file of files) { + report += `### ${file}\n`; + for (const issue of suspicious[file]) { + report += `- ${issue}\n`; + } + } + } else { + report += `No suspicious patterns found.\n`; + } + } else { + report += `Suspicious tests file not found.\n`; + } + report += `\n`; + + // Coverage Gaps + report += `## Critical Coverage Gaps\n`; + if (coverage) { + const gaps = Object.keys(coverage).filter(k => coverage[k].status !== 'OK'); + if (gaps.length > 0) { + report += `Critical modules with low coverage (< 70%):\n`; + for (const file of gaps) { + const stats = coverage[file]; + if (stats.status === 'MISSING') { + report += `- **${file}**: No coverage data found.\n`; + } else { + report += `- **${file}**: Lines: ${stats.lines}%, Statements: ${stats.statements}%, Functions: ${stats.functions}%, Branches: ${stats.branches}%\n`; + } + } + } else { + report += `All critical modules have acceptable coverage.\n`; + } + } else { + report += `Coverage gaps file not found.\n`; + } + report += `\n`; + + // Remediation + report += `## Prioritized Remediation\n`; + report += `### High Priority (P0)\n`; + if (coverage) { + const gaps = Object.keys(coverage).filter(k => coverage[k].status !== 'OK'); + for (const file of gaps) { + report += `- [ ] Increase coverage for \`${file}\` (currently < 70%).\n`; + } + } + if (flakiness) { + const flakyTests = Object.keys(flakiness).filter(k => flakiness[k].verdict === 'FLAKY'); + for (const test of flakyTests) { + report += `- [ ] Fix flakiness in \`${test}\`.\n`; + } + } + + report += `\n### Medium Priority (P1)\n`; + report += `- [ ] Review suspicious tests (time dependence, network usage) and replace with deterministic patterns.\n`; + + fs.writeFileSync(outputFile, report); + console.log(`Generated report: ${outputFile}`); +} + +main(); diff --git a/scripts/run-test-audit.sh b/scripts/run-test-audit.sh new file mode 100755 index 00000000..59aeeb39 --- /dev/null +++ b/scripts/run-test-audit.sh @@ -0,0 +1,44 @@ +#!/bin/bash +set -e + +# Setup +mkdir -p test-audit/coverage + +echo "Step 1: Running unit tests with coverage..." +# We use || true because tests might fail, but we still want coverage +# Using npx c8 to ensure coverage is captured +npx c8 --reporter=lcov --reporter=text --reporter=json-summary npm run test:unit > test-audit/coverage-run.log 2>&1 || true + +# Copy artifacts +# Check if coverage directory exists before copying +if [ -d "coverage" ]; then + cp -r coverage/* test-audit/coverage/ +else + echo "Warning: coverage directory not found. Coverage step might have failed completely." +fi + +echo "Step 2: Detecting flakiness..." +# Save the first run log +cp test-audit/coverage-run.log test-audit/run-1.log + +# Run 2 +echo "Pass 2..." +npm run test:unit > test-audit/run-2.log 2>&1 || true + +# Run 3 +echo "Pass 3..." +npm run test:unit > test-audit/run-3.log 2>&1 || true + +echo "Step 3: Analyzing flakiness..." +node scripts/analyze-flakiness.mjs + +echo "Step 4: Static Analysis..." +node scripts/static-test-analysis.mjs + +echo "Step 5: Coverage Gap Analysis..." +node scripts/coverage-gap-analysis.mjs + +echo "Step 6: Generating Report..." +node scripts/generate-audit-report.mjs + +echo "Done. Report generated." diff --git a/scripts/static-test-analysis.mjs b/scripts/static-test-analysis.mjs new file mode 100644 index 00000000..6d773b8f --- /dev/null +++ b/scripts/static-test-analysis.mjs @@ -0,0 +1,78 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const rootDir = path.dirname(path.dirname(fileURLToPath(import.meta.url))); +const testsDir = path.join(rootDir, 'tests'); +const outputFile = path.join(rootDir, 'test-audit', 'suspicious-tests.json'); + +const checks = { + zeroAssertions: { + pattern: /(expect\(|assert\.|t\.is|chai\.expect|should\.)/g, + inverse: true, + message: 'No assertions found' + }, + focusedOrSkipped: { + pattern: /\.(only|skip)\(/g, + message: 'Focused or skipped test' + }, + timeDependence: { + pattern: /(setTimeout|setInterval|sleep|delay)\(/g, + message: 'Time dependence (sleep/timeout)' + }, + networkDependence: { + pattern: /(fetch|axios\.|new WebSocket|WebSocket\()/g, + message: 'Network dependence (fetch/WebSocket)' + }, + consoleUsage: { + pattern: /console\.(log|warn|error)/g, + message: 'Console usage' + } +}; + +async function scanFile(filePath) { + const content = fs.readFileSync(filePath, 'utf8'); + const fileIssues = []; + + for (const [key, check] of Object.entries(checks)) { + const matches = content.match(check.pattern); + if (check.inverse) { + if (!matches) { + fileIssues.push(check.message); + } + } else { + if (matches) { + fileIssues.push(`${check.message} (${matches.length} occurrences)`); + } + } + } + return fileIssues; +} + +async function walk(dir) { + let results = {}; + const list = fs.readdirSync(dir); + for (const file of list) { + if (file === 'node_modules' || file === 'visual' || file === 'e2e') continue; + const filePath = path.join(dir, file); + const stat = fs.statSync(filePath); + if (stat && stat.isDirectory()) { + Object.assign(results, await walk(filePath)); + } else if (file.endsWith('.test.mjs') || file.endsWith('.test.js')) { + const issues = await scanFile(filePath); + if (issues.length > 0) { + results[path.relative(rootDir, filePath)] = issues; + } + } + } + return results; +} + +async function main() { + console.log('Scanning tests for suspicious patterns...'); + const suspiciousTests = await walk(testsDir); + fs.writeFileSync(outputFile, JSON.stringify(suspiciousTests, null, 2)); + console.log(`Wrote suspicious tests to ${outputFile}`); +} + +main(); diff --git a/test-audit-report-2025-02-18.md b/test-audit-report-2025-02-18.md index 4b88555e..0acb229e 100644 --- a/test-audit-report-2025-02-18.md +++ b/test-audit-report-2025-02-18.md @@ -1,34 +1,246 @@ -# Test Audit Report (2025-02-18) +# Test Audit Report - 2025-02-18 ## Summary -The test suite is functional but slow (~6m+ if run fully with retry). Overall line coverage is ~42%, with critical gaps in `authService.js` (10%) and `playbackCoordinator.js` (9%). Static analysis reveals widespread usage of `setTimeout` in tests, which contributes to potential flakiness and slowness. +- **Flaky Tests**: 0 +- **Suspicious Files**: 91 +- **Critical Coverage Gaps**: 5 -## Test Run -- **Environment**: Node.js v22 (experimental-test-coverage enabled) -- **Result**: 361 tests passed, 0 failed. -- **Flakiness**: Unable to fully assess due to timeout (suite > 400s), but timeouts in test code indicate risk. +## Flakiness Detection +No flaky tests detected (based on 2 runs). -## Coverage Gaps (Critical Files) -| File | Coverage (Line) | Criticality | -| :--- | :--- | :--- | -| `js/services/authService.js` | 10.16% | **P0** (Login/Security) | -| `js/app/playbackCoordinator.js` | 9.70% | **P0** (Core Feature) | -| `js/userBlocks.js` | 18.92% | **P1** (Moderation) | -| `js/relayManager.js` | 49.94% | **P1** (Network) | -| `js/nostr/watchHistory.js` | 46.24% | **P1** (User Data) | -| `js/nostr/dmDecryptWorkerClient.js`| 44.09% | **P1** (Privacy) | +## Suspicious Patterns +Found suspicious patterns in 91 files: +### tests/admin-list-store.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +- Console usage (4 occurrences) +### tests/app/feedCoordinator.test.mjs +- Network dependence (fetch/WebSocket) (1 occurrences) +### tests/app-batch-fetch-profiles.test.mjs +- Network dependence (fetch/WebSocket) (2 occurrences) +- Console usage (3 occurrences) +### tests/auth-service.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +### tests/comment-thread-service.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +- Network dependence (fetch/WebSocket) (78 occurrences) +- Console usage (1 occurrences) +### tests/compliance/nip07_compliance.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/discussion-count-service.test.mjs +- Network dependence (fetch/WebSocket) (2 occurrences) +- Console usage (1 occurrences) +### tests/dm-normalization.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +### tests/edit-modal-submit-state.test.mjs +- Network dependence (fetch/WebSocket) (3 occurrences) +### tests/event-schemas.test.mjs +- Console usage (3 occurrences) +### tests/feed-engine.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +- Console usage (1 occurrences) +### tests/hashtag-preferences.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +- Network dependence (fetch/WebSocket) (8 occurrences) +### tests/login-modal-controller.test.mjs +- Time dependence (sleep/timeout) (6 occurrences) +### tests/minimal-webtorrent.test.mjs +- No assertions found +### tests/modal-accessibility.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +- Network dependence (fetch/WebSocket) (4 occurrences) +### tests/moderation/submit-report.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/moderation/video-card.test.mjs +- Time dependence (sleep/timeout) (6 occurrences) +### tests/nip07-concurrency.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +### tests/nostr/client.test.mjs +- Time dependence (sleep/timeout) (3 occurrences) +- Network dependence (fetch/WebSocket) (13 occurrences) +### tests/nostr/comment-events.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/nostr/countDiagnostics.test.mjs +- Console usage (4 occurrences) +### tests/nostr/dm-direct-message-flow.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +### tests/nostr/dmDecryptWorkerClient.test.mjs +- Console usage (4 occurrences) +### tests/nostr/dmSignalEvents.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/nostr/integration-remote-flow.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/nostr/nip04WorkerClient.test.mjs +- Console usage (2 occurrences) +### tests/nostr/nip07Permissions.test.js +- Time dependence (sleep/timeout) (1 occurrences) +### tests/nostr/nip71.test.js +- Network dependence (fetch/WebSocket) (10 occurrences) +### tests/nostr/sessionActor.test.mjs +- Focused or skipped test (1 occurrences) +### tests/nostr/sign-request-queue.test.mjs +- Time dependence (sleep/timeout) (3 occurrences) +### tests/nostr/toolkit.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/nostr/watchHistory.test.js +- Time dependence (sleep/timeout) (1 occurrences) +- Network dependence (fetch/WebSocket) (5 occurrences) +### tests/nostr-boost-actions.test.mjs +- Network dependence (fetch/WebSocket) (2 occurrences) +- Console usage (1 occurrences) +### tests/nostr-count-fallback.test.mjs +- Console usage (4 occurrences) +### tests/nostr-delete-flow.test.mjs +- Console usage (1 occurrences) +### tests/nostr-login-permissions.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/nostr-nip46-queue.test.mjs +- Time dependence (sleep/timeout) (3 occurrences) +### tests/nostr-private-key-signer.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/nostr-publish-rejection.test.mjs +- Console usage (1 occurrences) +### tests/nostr-rebroadcast-guard.test.mjs +- Network dependence (fetch/WebSocket) (1 occurrences) +- Console usage (1 occurrences) +### tests/nostr-send-direct-message.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +### tests/nostr-service-access-control.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/nostr-signer-race.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/nostr-view-event-bindings.test.mjs +- Console usage (1 occurrences) +### tests/nostr-view-events.test.mjs +- Console usage (1 occurrences) +### tests/nwc-client.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +### tests/performance/resolvePostedAt.test.mjs +- Network dependence (fetch/WebSocket) (2 occurrences) +- Console usage (2 occurrences) +### tests/profile-cache.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +### tests/profile-modal-controller.test.mjs +- Time dependence (sleep/timeout) (3 occurrences) +- Network dependence (fetch/WebSocket) (3 occurrences) +### tests/race/nostr-client-init-race.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/revert-modal-controller.test.mjs +- Network dependence (fetch/WebSocket) (2 occurrences) +### tests/services/attachmentService.test.mjs +- Network dependence (fetch/WebSocket) (12 occurrences) +### tests/services/discussionCountService.test.mjs +- Network dependence (fetch/WebSocket) (1 occurrences) +### tests/services/exploreDataService.test.mjs +- Console usage (4 occurrences) +### tests/services/link-preview-service.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +- Network dependence (fetch/WebSocket) (7 occurrences) +- Console usage (1 occurrences) +### tests/services/nostr-service.test.mjs +- Network dependence (fetch/WebSocket) (3 occurrences) +### tests/services/playbackService_forcedSource.test.mjs +- Time dependence (sleep/timeout) (3 occurrences) +### tests/services/profileMetadataService.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +- Network dependence (fetch/WebSocket) (13 occurrences) +### tests/services/relay-health-service.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +- Network dependence (fetch/WebSocket) (1 occurrences) +### tests/services/trustBootstrap.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +### tests/sign-request-queue.test.mjs +- Time dependence (sleep/timeout) (3 occurrences) +### tests/state/cache.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/subscriptions-feed.test.mjs +- Network dependence (fetch/WebSocket) (1 occurrences) +- Console usage (1 occurrences) +### tests/subscriptions-manager.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +- Network dependence (fetch/WebSocket) (3 occurrences) +### tests/torrent/service-worker-fallback-message.test.mjs +- Network dependence (fetch/WebSocket) (1 occurrences) +### tests/torrent/toast-service.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/ui/components/debug_hashtag_strip_helper.test.mjs +- No assertions found +- Console usage (3 occurrences) +### tests/ui/creatorProfileController.test.mjs +- Network dependence (fetch/WebSocket) (12 occurrences) +### tests/ui/profile-modal-moderation-settings.test.mjs +- Network dependence (fetch/WebSocket) (2 occurrences) +### tests/ui/profileModalController-addProfile.test.mjs +- Network dependence (fetch/WebSocket) (2 occurrences) +- Console usage (2 occurrences) +### tests/ui/uploadModal-integration.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +- Network dependence (fetch/WebSocket) (7 occurrences) +### tests/ui/uploadModal-reset.test.mjs +- Network dependence (fetch/WebSocket) (1 occurrences) +- Console usage (1 occurrences) +### tests/ui/url-health-controller.test.mjs +- Time dependence (sleep/timeout) (5 occurrences) +- Network dependence (fetch/WebSocket) (3 occurrences) +### tests/unit/client-count-resilience.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/unit/editModalController.test.mjs +- Network dependence (fetch/WebSocket) (2 occurrences) +### tests/unit/notificationController.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/unit/services/r2Service.storage-config.test.mjs +- Network dependence (fetch/WebSocket) (3 occurrences) +### tests/unit/ui/videoModalController.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +### tests/user-blocks.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +### tests/utils/domUtils.test.mjs +- Console usage (2 occurrences) +### tests/video-card-source-visibility.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +### tests/video-modal-accessibility.test.mjs +- Network dependence (fetch/WebSocket) (5 occurrences) +- Console usage (4 occurrences) +### tests/video-modal-zap.test.mjs +- Network dependence (fetch/WebSocket) (4 occurrences) +### tests/view-counter.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +- Network dependence (fetch/WebSocket) (6 occurrences) +- Console usage (4 occurrences) +### tests/watch-history-feed.test.mjs +- Console usage (1 occurrences) +### tests/watch-history.test.mjs +- Time dependence (sleep/timeout) (4 occurrences) +- Network dependence (fetch/WebSocket) (14 occurrences) +- Console usage (19 occurrences) +### tests/watchHistory/watch-history-telemetry.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/webtorrent-handlers.test.mjs +- Time dependence (sleep/timeout) (3 occurrences) +### tests/webtorrent-regression.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +- Console usage (1 occurrences) +### tests/zap-shared-state.test.mjs +- Console usage (1 occurrences) +### tests/zap-split.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +- Network dependence (fetch/WebSocket) (7 occurrences) +- Console usage (1 occurrences) -## Findings & Smells -1. **Low Coverage in Auth**: `authService.js` is barely tested. It handles login, key management, and profile hydration. -2. **Timeouts in Tests**: Found 19 instances of `setTimeout` in tests. `tests/nostr/dm-direct-message-flow.test.mjs` uses `setTimeout(r, intervalMs)` and `setTimeout(resolve, 100)` to wait for events, which is fragile. -3. **Console Logs**: 20+ instances of `console.log` in test files. -4. **Slow Tests**: The suite takes too long to run repeatedly for flakiness checks. +## Critical Coverage Gaps +Critical modules with low coverage (< 70%): +- **js/services/authService.js**: Lines: 77.34%, Statements: 77.34%, Functions: 61.9%, Branches: 62.71% +- **js/relayManager.js**: Lines: 56.75%, Statements: 56.75%, Functions: 73.33%, Branches: 51.93% +- **js/nostr/dmDecryptWorker.js**: Lines: 71.8%, Statements: 71.8%, Functions: 100%, Branches: 31.66% +- **js/nostr/watchHistory.js**: Lines: 58.14%, Statements: 58.14%, Functions: 69.64%, Branches: 40.68% +- **js/userBlocks.js**: Lines: 25.77%, Statements: 25.77%, Functions: 33.33%, Branches: 29.2% -## Recommendations -1. **Fix P0 Coverage**: Add unit tests for `authService.js` (login flows, error handling). -2. **Refactor Timeouts**: Replace `setTimeout` with `waitFor` helpers or event listeners in `dm-direct-message-flow.test.mjs`. -3. **Cleanup**: Remove `console.log` from tests. +## Prioritized Remediation +### High Priority (P0) +- [ ] Increase coverage for `js/services/authService.js` (currently < 70%). +- [ ] Increase coverage for `js/relayManager.js` (currently < 70%). +- [ ] Increase coverage for `js/nostr/dmDecryptWorker.js` (currently < 70%). +- [ ] Increase coverage for `js/nostr/watchHistory.js` (currently < 70%). +- [ ] Increase coverage for `js/userBlocks.js` (currently < 70%). -## Remediation Plan -- **Action**: Create a PR to add tests for `js/services/authService.js`. -- **Target**: Increase coverage for `authService` to > 30% by testing the public `login` and `logout` methods. +### Medium Priority (P1) +- [ ] Review suspicious tests (time dependence, network usage) and replace with deterministic patterns. diff --git a/test-audit-report-2026-02-18.md b/test-audit-report-2026-02-18.md new file mode 100644 index 00000000..324c3361 --- /dev/null +++ b/test-audit-report-2026-02-18.md @@ -0,0 +1,252 @@ +# Test Audit Report - 2026-02-18 + +## Run Metadata +- **Date**: 2026-02-18T20:13:50.582Z +- **Node Version**: v22.22.0 +- **NPM Version**: 11.7.0 +- **Test Command**: `npm run test:unit` (with c8 coverage) + +## Summary +- **Flaky Tests**: 0 +- **Suspicious Files**: 91 +- **Critical Coverage Gaps**: 5 + +## Flakiness Detection +No flaky tests detected (based on run matrix). + +## Suspicious Patterns +Found suspicious patterns in 91 files: +### tests/admin-list-store.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +- Console usage (4 occurrences) +### tests/app/feedCoordinator.test.mjs +- Network dependence (fetch/WebSocket) (1 occurrences) +### tests/app-batch-fetch-profiles.test.mjs +- Network dependence (fetch/WebSocket) (2 occurrences) +- Console usage (3 occurrences) +### tests/auth-service.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +### tests/comment-thread-service.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +- Network dependence (fetch/WebSocket) (78 occurrences) +- Console usage (1 occurrences) +### tests/compliance/nip07_compliance.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/discussion-count-service.test.mjs +- Network dependence (fetch/WebSocket) (2 occurrences) +- Console usage (1 occurrences) +### tests/dm-normalization.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +### tests/edit-modal-submit-state.test.mjs +- Network dependence (fetch/WebSocket) (3 occurrences) +### tests/event-schemas.test.mjs +- Console usage (3 occurrences) +### tests/feed-engine.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +- Console usage (1 occurrences) +### tests/hashtag-preferences.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +- Network dependence (fetch/WebSocket) (8 occurrences) +### tests/login-modal-controller.test.mjs +- Time dependence (sleep/timeout) (6 occurrences) +### tests/minimal-webtorrent.test.mjs +- No assertions found +### tests/modal-accessibility.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +- Network dependence (fetch/WebSocket) (4 occurrences) +### tests/moderation/submit-report.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/moderation/video-card.test.mjs +- Time dependence (sleep/timeout) (6 occurrences) +### tests/nip07-concurrency.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +### tests/nostr/client.test.mjs +- Time dependence (sleep/timeout) (3 occurrences) +- Network dependence (fetch/WebSocket) (13 occurrences) +### tests/nostr/comment-events.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/nostr/countDiagnostics.test.mjs +- Console usage (4 occurrences) +### tests/nostr/dm-direct-message-flow.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +### tests/nostr/dmDecryptWorkerClient.test.mjs +- Console usage (4 occurrences) +### tests/nostr/dmSignalEvents.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/nostr/integration-remote-flow.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/nostr/nip04WorkerClient.test.mjs +- Console usage (2 occurrences) +### tests/nostr/nip07Permissions.test.js +- Time dependence (sleep/timeout) (1 occurrences) +### tests/nostr/nip71.test.js +- Network dependence (fetch/WebSocket) (10 occurrences) +### tests/nostr/sessionActor.test.mjs +- Focused or skipped test (1 occurrences) +### tests/nostr/sign-request-queue.test.mjs +- Time dependence (sleep/timeout) (3 occurrences) +### tests/nostr/toolkit.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/nostr/watchHistory.test.js +- Time dependence (sleep/timeout) (1 occurrences) +- Network dependence (fetch/WebSocket) (5 occurrences) +### tests/nostr-boost-actions.test.mjs +- Network dependence (fetch/WebSocket) (2 occurrences) +- Console usage (1 occurrences) +### tests/nostr-count-fallback.test.mjs +- Console usage (4 occurrences) +### tests/nostr-delete-flow.test.mjs +- Console usage (1 occurrences) +### tests/nostr-login-permissions.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/nostr-nip46-queue.test.mjs +- Time dependence (sleep/timeout) (3 occurrences) +### tests/nostr-private-key-signer.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/nostr-publish-rejection.test.mjs +- Console usage (1 occurrences) +### tests/nostr-rebroadcast-guard.test.mjs +- Network dependence (fetch/WebSocket) (1 occurrences) +- Console usage (1 occurrences) +### tests/nostr-send-direct-message.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +### tests/nostr-service-access-control.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/nostr-signer-race.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/nostr-view-event-bindings.test.mjs +- Console usage (1 occurrences) +### tests/nostr-view-events.test.mjs +- Console usage (1 occurrences) +### tests/nwc-client.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +### tests/performance/resolvePostedAt.test.mjs +- Network dependence (fetch/WebSocket) (2 occurrences) +- Console usage (2 occurrences) +### tests/profile-cache.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +### tests/profile-modal-controller.test.mjs +- Time dependence (sleep/timeout) (3 occurrences) +- Network dependence (fetch/WebSocket) (3 occurrences) +### tests/race/nostr-client-init-race.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/revert-modal-controller.test.mjs +- Network dependence (fetch/WebSocket) (2 occurrences) +### tests/services/attachmentService.test.mjs +- Network dependence (fetch/WebSocket) (12 occurrences) +### tests/services/discussionCountService.test.mjs +- Network dependence (fetch/WebSocket) (1 occurrences) +### tests/services/exploreDataService.test.mjs +- Console usage (4 occurrences) +### tests/services/link-preview-service.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +- Network dependence (fetch/WebSocket) (7 occurrences) +- Console usage (1 occurrences) +### tests/services/nostr-service.test.mjs +- Network dependence (fetch/WebSocket) (3 occurrences) +### tests/services/playbackService_forcedSource.test.mjs +- Time dependence (sleep/timeout) (3 occurrences) +### tests/services/profileMetadataService.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +- Network dependence (fetch/WebSocket) (13 occurrences) +### tests/services/relay-health-service.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +- Network dependence (fetch/WebSocket) (1 occurrences) +### tests/services/trustBootstrap.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +### tests/sign-request-queue.test.mjs +- Time dependence (sleep/timeout) (3 occurrences) +### tests/state/cache.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/subscriptions-feed.test.mjs +- Network dependence (fetch/WebSocket) (1 occurrences) +- Console usage (1 occurrences) +### tests/subscriptions-manager.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +- Network dependence (fetch/WebSocket) (3 occurrences) +### tests/torrent/service-worker-fallback-message.test.mjs +- Network dependence (fetch/WebSocket) (1 occurrences) +### tests/torrent/toast-service.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/ui/components/debug_hashtag_strip_helper.test.mjs +- No assertions found +- Console usage (3 occurrences) +### tests/ui/creatorProfileController.test.mjs +- Network dependence (fetch/WebSocket) (12 occurrences) +### tests/ui/profile-modal-moderation-settings.test.mjs +- Network dependence (fetch/WebSocket) (2 occurrences) +### tests/ui/profileModalController-addProfile.test.mjs +- Network dependence (fetch/WebSocket) (2 occurrences) +- Console usage (2 occurrences) +### tests/ui/uploadModal-integration.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +- Network dependence (fetch/WebSocket) (7 occurrences) +### tests/ui/uploadModal-reset.test.mjs +- Network dependence (fetch/WebSocket) (1 occurrences) +- Console usage (1 occurrences) +### tests/ui/url-health-controller.test.mjs +- Time dependence (sleep/timeout) (5 occurrences) +- Network dependence (fetch/WebSocket) (3 occurrences) +### tests/unit/client-count-resilience.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/unit/editModalController.test.mjs +- Network dependence (fetch/WebSocket) (2 occurrences) +### tests/unit/notificationController.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/unit/services/r2Service.storage-config.test.mjs +- Network dependence (fetch/WebSocket) (3 occurrences) +### tests/unit/ui/videoModalController.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +### tests/user-blocks.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +### tests/utils/domUtils.test.mjs +- Console usage (2 occurrences) +### tests/video-card-source-visibility.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +### tests/video-modal-accessibility.test.mjs +- Network dependence (fetch/WebSocket) (5 occurrences) +- Console usage (4 occurrences) +### tests/video-modal-zap.test.mjs +- Network dependence (fetch/WebSocket) (4 occurrences) +### tests/view-counter.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +- Network dependence (fetch/WebSocket) (6 occurrences) +- Console usage (4 occurrences) +### tests/watch-history-feed.test.mjs +- Console usage (1 occurrences) +### tests/watch-history.test.mjs +- Time dependence (sleep/timeout) (4 occurrences) +- Network dependence (fetch/WebSocket) (14 occurrences) +- Console usage (19 occurrences) +### tests/watchHistory/watch-history-telemetry.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +### tests/webtorrent-handlers.test.mjs +- Time dependence (sleep/timeout) (3 occurrences) +### tests/webtorrent-regression.test.mjs +- Time dependence (sleep/timeout) (2 occurrences) +- Console usage (1 occurrences) +### tests/zap-shared-state.test.mjs +- Console usage (1 occurrences) +### tests/zap-split.test.mjs +- Time dependence (sleep/timeout) (1 occurrences) +- Network dependence (fetch/WebSocket) (7 occurrences) +- Console usage (1 occurrences) + +## Critical Coverage Gaps +Critical modules with low coverage (< 70%): +- **js/services/authService.js**: Lines: 77.34%, Statements: 77.34%, Functions: 61.9%, Branches: 62.71% +- **js/relayManager.js**: Lines: 56.75%, Statements: 56.75%, Functions: 73.33%, Branches: 51.93% +- **js/nostr/dmDecryptWorker.js**: Lines: 71.8%, Statements: 71.8%, Functions: 100%, Branches: 31.66% +- **js/nostr/watchHistory.js**: Lines: 58.14%, Statements: 58.14%, Functions: 69.64%, Branches: 40.68% +- **js/userBlocks.js**: Lines: 25.77%, Statements: 25.77%, Functions: 33.33%, Branches: 29.2% + +## Prioritized Remediation +### High Priority (P0) +- [ ] Increase coverage for `js/services/authService.js` (currently < 70%). +- [ ] Increase coverage for `js/relayManager.js` (currently < 70%). +- [ ] Increase coverage for `js/nostr/dmDecryptWorker.js` (currently < 70%). +- [ ] Increase coverage for `js/nostr/watchHistory.js` (currently < 70%). +- [ ] Increase coverage for `js/userBlocks.js` (currently < 70%). + +### Medium Priority (P1) +- [ ] Review suspicious tests (time dependence, network usage) and replace with deterministic patterns. diff --git a/test-audit/coverage-gaps.json b/test-audit/coverage-gaps.json index adb2f169..58d331c4 100644 --- a/test-audit/coverage-gaps.json +++ b/test-audit/coverage-gaps.json @@ -1,10 +1,37 @@ { - "js/services/authService.js": 77.34, - "js/relayManager.js": 49.93, - "js/nostr/dmDecryptWorker.js": 71.8, - "js/nostr/dmDecryptWorkerClient.js": 96.85, - "js/nostr/watchHistory.js": 58.14, - "js/userBlocks.js": 25.86, - "js/ui/ambientBackground.js": 33.02, - "torrent/app.js": 0 + "js/services/authService.js": { + "lines": 77.34, + "statements": 77.34, + "functions": 61.9, + "branches": 62.71, + "status": "LOW_COVERAGE" + }, + "js/relayManager.js": { + "lines": 56.75, + "statements": 56.75, + "functions": 73.33, + "branches": 51.93, + "status": "LOW_COVERAGE" + }, + "js/nostr/dmDecryptWorker.js": { + "lines": 71.8, + "statements": 71.8, + "functions": 100, + "branches": 31.66, + "status": "LOW_COVERAGE" + }, + "js/nostr/watchHistory.js": { + "lines": 58.14, + "statements": 58.14, + "functions": 69.64, + "branches": 40.68, + "status": "LOW_COVERAGE" + }, + "js/userBlocks.js": { + "lines": 25.77, + "statements": 25.77, + "functions": 33.33, + "branches": 29.2, + "status": "LOW_COVERAGE" + } } \ No newline at end of file diff --git a/test-audit/flakiness-matrix.json b/test-audit/flakiness-matrix.json index 533325fa..b9363321 100644 --- a/test-audit/flakiness-matrix.json +++ b/test-audit/flakiness-matrix.json @@ -1,4 +1,1423 @@ { - "error": "Flakiness check timed out after 400s", - "runs": [] + "tests/app-batch-fetch-profiles.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/app-state.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/app/channel-profile-moderation.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/app/feedCoordinator.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/app/hash-change-handler.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/app/hydrate-sidebar-navigation.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/app/is-user-logged-in.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/app/moderation-overrides.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/app/moderation-settings-refresh.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/app/similar-content.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/auth-service.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/auth/signingAdapter.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/authService.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/comment-event-builder.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/comment-thread-service.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/compliance/nip04_44_compliance.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/compliance/nip07_compliance.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/compliance/nip65_compliance.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/compliance/video_note_compliance.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/design-system-metrics.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/designSystem/dynamicStyles.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/dm-block-filter.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/dm-db.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/dm-decryptor.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/dm-normalization.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/docs/verify-upload-claims.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/edit-modal-submit-state.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/event-schemas.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/feed-engine/explore-diversity.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/feed-engine/explore-scorer.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/feed-engine/kids-scorer.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/feed-engine/tag-preference-filter.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/grid-health.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/hashtag-preferences.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/login-modal-controller.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/magnet-utils.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/media-loader.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/minimal-channel-profile.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/minimal-webtorrent.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/modal-accessibility.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/moderation-service.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/moderation-stage.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/moderation/hide-thresholds.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/moderation/submit-report.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/moderation/trust-seeds.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/moderation/trusted-mute-lists.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/moderation/trusted-report-count.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/moderation/video-card.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/more-menu-controller.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nip07-concurrency.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nip71-base-event-tags.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nip71-builder.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nip71-form-manager.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr-login-permissions.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr-nip46-queue.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr-private-key-signer.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr-send-direct-message.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr-service-access-control.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr-session-actor.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr-signer-race.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr-specs/kind30078.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr-specs/nip04-nip44.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr-specs/nip65_compliance.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/adapters.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/cachePolicies.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/client.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/comment-events.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/commentTargetNormalizer.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/countDiagnostics.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/decryptionSchemeCache.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/defaultClient.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/dm-direct-message-flow.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/dmDecryptDiagnostics.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/dmDecryptorPreference.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/dmDecryptWorkerClient.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/dmSignalEvents.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/edit-video-dtag.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/eventsCacheStore.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/eventsMap.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/integration-remote-flow.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/maxListenerDiagnostics.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/nip04WorkerClient.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/nip07Adapter-race-condition.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/nip07Permissions.test.js": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/nip46Client.test.js": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/nip46Connector.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/nip71.test.js": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/nostrClientFacade.test.js": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/nostrClientRegistry.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/publishHelpers.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/reaction-events.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/revert-video-dtag.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/session-actor.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/sessionActor.test.js": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/sessionActor.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/sign-request-queue.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/signers.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/syncMetadataStore.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/toolkit.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/videoEventBuffer.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/videoPayloadBuilder.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/watchHistory.test.js": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nostr/watchHistoryBindings.test.js": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/nwc-settings-service.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/performance/resolvePostedAt.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/position-floating-panel.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/profile-cache.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/profile-modal-controller.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/race/nostr-client-init-race.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/reaction-event-builder.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/revert-modal-controller.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/safe-decode.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/services/attachmentService.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/services/authUtils.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/services/discussionCountService.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/services/exploreDataService.optimization.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/services/exploreDataService.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/services/link-preview-service.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/services/moderation-action-controller.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/services/nostr-service.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/services/playbackService_forcedSource.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/services/playbackService_order.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/services/playbackService.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/services/profileMetadataService.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/services/r2Service.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/services/relay-health-service.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/services/s3Service.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/services/s3UploadService.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/services/trustBootstrap.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/share-event-builder.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/shareNostrController.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/sign-request-queue.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/state/appState.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/state/cache-saved-profiles.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/state/cache.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/state/profile-cache.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/state/profile-settings-store.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/storage-service.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/subscriptions-manager.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/torrent/service-worker-fallback-message.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/torrent/style-helpers.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/torrent/toast-service.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/ui/app-chrome-controller.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/ui/components/debug_hashtag_strip_helper.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/ui/components/hashtagStripHelper.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/ui/components/videoMenuRenderers.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/ui/components/VideoModalSimilarHelpers.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/ui/creatorProfileController.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/ui/dm/zapHelpers.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/ui/engagement-controller.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/ui/hashtag-strip-helper-bounce.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/ui/popoverEngine.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/ui/profile-modal-moderation-settings.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/ui/profileModalController-addProfile.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/ui/reactionController.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/ui/similar-content-card.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/ui/similarContentController.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/ui/storageService.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/ui/tag-pill-list.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/ui/tag-preference-menu.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/ui/tagPreferenceMenuController.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/ui/torrentStatusController.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/ui/uploadModal-integration.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/ui/uploadModal-reset.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/ui/url-health-controller.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/ui/video-list-view-popular-tags.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/ui/video-list-view-sorting.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/unit/app_guard_logic.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/unit/client-count-resilience.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/unit/comment-avatar.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/unit/editModalController.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/unit/embed_player_modal.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/unit/embed-accent.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/unit/hashChangeHandler.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/unit/notificationController.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/unit/searchFilters.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/unit/security-config.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/unit/services/moderationDecorator.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/unit/services/r2Service.storage-config.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/unit/ui/EmbedVideoModal.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/unit/ui/videoModalController.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/utils/cardSourceVisibility.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/utils/domUtils.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/utils/hex.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/utils/linkPreviewSettings.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/utils/lruCache.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/utils/profileMedia.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/utils/video-deduper.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/utils/video-tags.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/utils/videoPointer.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/utils/videoTimestamps.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/validate-config.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/video-card-source-visibility.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/video-modal-accessibility.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/video-modal-comments.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/video-modal-controller.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/video-modal-controllers.test.mjs": { + "statuses": [ + "PASS", + "PASS" + ], + "verdict": "PASS" + }, + "tests/video-modal-moderation.test.mjs": { + "statuses": [ + "PASS" + ], + "verdict": "PASS" + }, + "tests/video-modal-tags.test.mjs": { + "statuses": [ + "PASS" + ], + "verdict": "PASS" + }, + "tests/video-note-payload.test.mjs": { + "statuses": [ + "PASS" + ], + "verdict": "PASS" + }, + "tests/video-schema-and-conversion.test.mjs": { + "statuses": [ + "PASS" + ], + "verdict": "PASS" + }, + "tests/video-settings-menu-controller.test.mjs": { + "statuses": [ + "PASS" + ], + "verdict": "PASS" + }, + "tests/webtorrent-handlers.test.mjs": { + "statuses": [ + "PASS" + ], + "verdict": "PASS" + }, + "tests/webtorrent-regression.test.mjs": { + "statuses": [ + "PASS" + ], + "verdict": "PASS" + } } \ No newline at end of file diff --git a/test-audit/suspicious-tests.json b/test-audit/suspicious-tests.json index 4f79b8fb..7daf5c24 100644 --- a/test-audit/suspicious-tests.json +++ b/test-audit/suspicious-tests.json @@ -1,245 +1,308 @@ { "tests/admin-list-store.test.mjs": [ - "Uses setTimeout/sleep (brittle)", - "Console usage inside test" + "Time dependence (sleep/timeout) (2 occurrences)", + "Console usage (4 occurrences)" + ], + "tests/app/feedCoordinator.test.mjs": [ + "Network dependence (fetch/WebSocket) (1 occurrences)" ], "tests/app-batch-fetch-profiles.test.mjs": [ - "Console usage inside test" + "Network dependence (fetch/WebSocket) (2 occurrences)", + "Console usage (3 occurrences)" ], "tests/auth-service.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (2 occurrences)" ], "tests/comment-thread-service.test.mjs": [ - "Uses setTimeout/sleep (brittle)", - "Console usage inside test" + "Time dependence (sleep/timeout) (1 occurrences)", + "Network dependence (fetch/WebSocket) (78 occurrences)", + "Console usage (1 occurrences)" ], "tests/compliance/nip07_compliance.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (1 occurrences)" ], "tests/discussion-count-service.test.mjs": [ - "Console usage inside test" + "Network dependence (fetch/WebSocket) (2 occurrences)", + "Console usage (1 occurrences)" ], "tests/dm-normalization.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (2 occurrences)" + ], + "tests/edit-modal-submit-state.test.mjs": [ + "Network dependence (fetch/WebSocket) (3 occurrences)" ], "tests/event-schemas.test.mjs": [ - "Console usage inside test" + "Console usage (3 occurrences)" ], "tests/feed-engine.test.mjs": [ - "Uses setTimeout/sleep (brittle)", - "Console usage inside test" + "Time dependence (sleep/timeout) (1 occurrences)", + "Console usage (1 occurrences)" ], "tests/hashtag-preferences.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (2 occurrences)", + "Network dependence (fetch/WebSocket) (8 occurrences)" ], - "tests/minimal-channel-profile.test.mjs": [ - "No obvious assertions found" + "tests/login-modal-controller.test.mjs": [ + "Time dependence (sleep/timeout) (6 occurrences)" ], "tests/minimal-webtorrent.test.mjs": [ - "No obvious assertions found" + "No assertions found" ], "tests/modal-accessibility.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (1 occurrences)", + "Network dependence (fetch/WebSocket) (4 occurrences)" ], "tests/moderation/submit-report.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (1 occurrences)" ], "tests/moderation/video-card.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (6 occurrences)" ], "tests/nip07-concurrency.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (2 occurrences)" ], "tests/nostr/client.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (3 occurrences)", + "Network dependence (fetch/WebSocket) (13 occurrences)" ], "tests/nostr/comment-events.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (1 occurrences)" ], "tests/nostr/countDiagnostics.test.mjs": [ - "Console usage inside test" + "Console usage (4 occurrences)" ], "tests/nostr/dm-direct-message-flow.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (2 occurrences)" ], "tests/nostr/dmDecryptWorkerClient.test.mjs": [ - "Console usage inside test" + "Console usage (4 occurrences)" ], "tests/nostr/dmSignalEvents.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (1 occurrences)" ], "tests/nostr/integration-remote-flow.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (1 occurrences)" ], "tests/nostr/nip04WorkerClient.test.mjs": [ - "Console usage inside test" + "Console usage (2 occurrences)" ], "tests/nostr/nip07Permissions.test.js": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (1 occurrences)" + ], + "tests/nostr/nip71.test.js": [ + "Network dependence (fetch/WebSocket) (10 occurrences)" ], "tests/nostr/sessionActor.test.mjs": [ - "Test is focused (.only) or skipped (.skip)" + "Focused or skipped test (1 occurrences)" ], "tests/nostr/sign-request-queue.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (3 occurrences)" ], "tests/nostr/toolkit.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (1 occurrences)" ], "tests/nostr/watchHistory.test.js": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (1 occurrences)", + "Network dependence (fetch/WebSocket) (5 occurrences)" ], "tests/nostr-boost-actions.test.mjs": [ - "Console usage inside test" + "Network dependence (fetch/WebSocket) (2 occurrences)", + "Console usage (1 occurrences)" ], "tests/nostr-count-fallback.test.mjs": [ - "Console usage inside test" + "Console usage (4 occurrences)" ], "tests/nostr-delete-flow.test.mjs": [ - "Console usage inside test" + "Console usage (1 occurrences)" ], "tests/nostr-login-permissions.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (1 occurrences)" ], "tests/nostr-nip46-queue.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (3 occurrences)" ], "tests/nostr-private-key-signer.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (1 occurrences)" ], "tests/nostr-publish-rejection.test.mjs": [ - "Console usage inside test" + "Console usage (1 occurrences)" ], "tests/nostr-rebroadcast-guard.test.mjs": [ - "Console usage inside test" + "Network dependence (fetch/WebSocket) (1 occurrences)", + "Console usage (1 occurrences)" ], "tests/nostr-send-direct-message.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (2 occurrences)" ], "tests/nostr-service-access-control.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (1 occurrences)" ], "tests/nostr-signer-race.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (1 occurrences)" ], "tests/nostr-view-event-bindings.test.mjs": [ - "Console usage inside test" + "Console usage (1 occurrences)" ], "tests/nostr-view-events.test.mjs": [ - "Console usage inside test" + "Console usage (1 occurrences)" ], "tests/nwc-client.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (2 occurrences)" ], "tests/performance/resolvePostedAt.test.mjs": [ - "Console usage inside test" + "Network dependence (fetch/WebSocket) (2 occurrences)", + "Console usage (2 occurrences)" ], "tests/profile-cache.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (2 occurrences)" ], "tests/profile-modal-controller.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (3 occurrences)", + "Network dependence (fetch/WebSocket) (3 occurrences)" ], "tests/race/nostr-client-init-race.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (1 occurrences)" + ], + "tests/revert-modal-controller.test.mjs": [ + "Network dependence (fetch/WebSocket) (2 occurrences)" + ], + "tests/services/attachmentService.test.mjs": [ + "Network dependence (fetch/WebSocket) (12 occurrences)" + ], + "tests/services/discussionCountService.test.mjs": [ + "Network dependence (fetch/WebSocket) (1 occurrences)" ], "tests/services/exploreDataService.test.mjs": [ - "Console usage inside test" + "Console usage (4 occurrences)" ], "tests/services/link-preview-service.test.mjs": [ - "Uses setTimeout/sleep (brittle)", - "Console usage inside test" + "Time dependence (sleep/timeout) (1 occurrences)", + "Network dependence (fetch/WebSocket) (7 occurrences)", + "Console usage (1 occurrences)" + ], + "tests/services/nostr-service.test.mjs": [ + "Network dependence (fetch/WebSocket) (3 occurrences)" ], "tests/services/playbackService_forcedSource.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (3 occurrences)" ], "tests/services/profileMetadataService.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (1 occurrences)", + "Network dependence (fetch/WebSocket) (13 occurrences)" ], "tests/services/relay-health-service.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (1 occurrences)", + "Network dependence (fetch/WebSocket) (1 occurrences)" ], "tests/services/trustBootstrap.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (2 occurrences)" ], "tests/sign-request-queue.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (3 occurrences)" ], "tests/state/cache.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (1 occurrences)" ], "tests/subscriptions-feed.test.mjs": [ - "Console usage inside test" + "Network dependence (fetch/WebSocket) (1 occurrences)", + "Console usage (1 occurrences)" ], "tests/subscriptions-manager.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (1 occurrences)", + "Network dependence (fetch/WebSocket) (3 occurrences)" + ], + "tests/torrent/service-worker-fallback-message.test.mjs": [ + "Network dependence (fetch/WebSocket) (1 occurrences)" ], "tests/torrent/toast-service.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (1 occurrences)" ], "tests/ui/components/debug_hashtag_strip_helper.test.mjs": [ - "No obvious assertions found", - "Console usage inside test" + "No assertions found", + "Console usage (3 occurrences)" + ], + "tests/ui/creatorProfileController.test.mjs": [ + "Network dependence (fetch/WebSocket) (12 occurrences)" + ], + "tests/ui/profile-modal-moderation-settings.test.mjs": [ + "Network dependence (fetch/WebSocket) (2 occurrences)" ], "tests/ui/profileModalController-addProfile.test.mjs": [ - "Console usage inside test" + "Network dependence (fetch/WebSocket) (2 occurrences)", + "Console usage (2 occurrences)" ], "tests/ui/uploadModal-integration.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (1 occurrences)", + "Network dependence (fetch/WebSocket) (7 occurrences)" ], "tests/ui/uploadModal-reset.test.mjs": [ - "Console usage inside test" + "Network dependence (fetch/WebSocket) (1 occurrences)", + "Console usage (1 occurrences)" ], "tests/ui/url-health-controller.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (5 occurrences)", + "Network dependence (fetch/WebSocket) (3 occurrences)" ], "tests/unit/client-count-resilience.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (1 occurrences)" + ], + "tests/unit/editModalController.test.mjs": [ + "Network dependence (fetch/WebSocket) (2 occurrences)" ], "tests/unit/notificationController.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (1 occurrences)" + ], + "tests/unit/services/r2Service.storage-config.test.mjs": [ + "Network dependence (fetch/WebSocket) (3 occurrences)" ], "tests/unit/ui/videoModalController.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (2 occurrences)" ], "tests/user-blocks.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (2 occurrences)" ], "tests/utils/domUtils.test.mjs": [ - "Console usage inside test" + "Console usage (2 occurrences)" ], "tests/video-card-source-visibility.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (2 occurrences)" ], "tests/video-modal-accessibility.test.mjs": [ - "Console usage inside test" + "Network dependence (fetch/WebSocket) (5 occurrences)", + "Console usage (4 occurrences)" + ], + "tests/video-modal-zap.test.mjs": [ + "Network dependence (fetch/WebSocket) (4 occurrences)" ], "tests/view-counter.test.mjs": [ - "Uses setTimeout/sleep (brittle)", - "Console usage inside test" + "Time dependence (sleep/timeout) (2 occurrences)", + "Network dependence (fetch/WebSocket) (6 occurrences)", + "Console usage (4 occurrences)" ], "tests/watch-history-feed.test.mjs": [ - "Console usage inside test" + "Console usage (1 occurrences)" ], "tests/watch-history.test.mjs": [ - "Uses setTimeout/sleep (brittle)", - "Console usage inside test" + "Time dependence (sleep/timeout) (4 occurrences)", + "Network dependence (fetch/WebSocket) (14 occurrences)", + "Console usage (19 occurrences)" ], "tests/watchHistory/watch-history-telemetry.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (1 occurrences)" ], "tests/webtorrent-handlers.test.mjs": [ - "Uses setTimeout/sleep (brittle)" + "Time dependence (sleep/timeout) (3 occurrences)" ], "tests/webtorrent-regression.test.mjs": [ - "Uses setTimeout/sleep (brittle)", - "Console usage inside test" + "Time dependence (sleep/timeout) (2 occurrences)", + "Console usage (1 occurrences)" ], "tests/zap-shared-state.test.mjs": [ - "Console usage inside test" + "Console usage (1 occurrences)" ], "tests/zap-split.test.mjs": [ - "Uses setTimeout/sleep (brittle)", - "Console usage inside test" + "Time dependence (sleep/timeout) (1 occurrences)", + "Network dependence (fetch/WebSocket) (7 occurrences)", + "Console usage (1 occurrences)" ] } \ No newline at end of file diff --git a/todo/TODO_2025-02-18.md b/todo/TODO_2025-02-18.md new file mode 100644 index 00000000..a1dde12d --- /dev/null +++ b/todo/TODO_2025-02-18.md @@ -0,0 +1,9 @@ +# Test Audit Todo - 2025-02-18 + +- [ ] Execute unit tests with coverage. +- [ ] Run flakiness detection loop (5x). +- [ ] Create `scripts/analyze-flakiness.mjs`. +- [ ] Create `scripts/static-test-analysis.mjs`. +- [ ] Create `scripts/coverage-gap-analysis.mjs`. +- [ ] Create `scripts/generate-audit-report.mjs`. +- [ ] Generate final audit report.