# Test Coverage Gaps — v2 (2026-03-27) ## Summary | Tier | Count | |------|-------| | Unit tests (`cargo test --lib`) | **358** | | Integration tests (backend/tests/*.rs) | **175** across 15 files | | E2E tests (e2e/tests/*.spec.ts) | **7** (1 per spec file, some multi-step) | ### Integration test breakdown by file | File | Tests | |------|-------| | api_admin_test.rs | 30 | | api_auth_test.rs | 16 | | api_sources_test.rs | 36 | | api_syntheses_test.rs | 17 | | api_keys_test.rs | 18 | | api_export_test.rs | 13 | | api_themes_test.rs | 10 | | api_schedules_test.rs | 9 | | api_settings_test.rs | 7 | | pipeline_test.rs | 5 | | api_article_history_test.rs | 4 | | api_csrf_test.rs | 4 | | api_sources_preferred_test.rs | 3 | | api_health_test.rs | 1 | | minimal_test.rs | 2 | --- ## Gaps Found ### GAP-01 — `stop_generate` endpoint has zero test coverage **Priority: High** `POST /api/v1/syntheses/generate/{job_id}/stop` is implemented in `backend/src/handlers/generation.rs` and registered in `router.rs`, but there is no integration test (no call to this route appears anywhere in `backend/tests/`), and no E2E test exercises the stop/cancel flow. **What to add:** - Integration test in `api_syntheses_test.rs`: - `stop_generate_without_auth_returns_401` - `stop_generate_unknown_job_returns_404` - `stop_generate_owned_job_returns_200` (trigger generation, then immediately stop it) - `stop_generate_other_users_job_returns_404` --- ### GAP-02 — `GET /api/v1/llm-logs/{job_id}` has zero integration test coverage **Priority: Medium** The handler exists (`backend/src/handlers/llm_logs.rs`) and the route is registered (`router.rs:71`). It is exercised only by the live E2E test (`generation-live.spec.ts`), which is gated on `OPENAI_TEST_API_KEY` and therefore does not run in CI. **What to add:** - New `backend/tests/api_llm_logs_test.rs`: - `get_llm_logs_without_auth_returns_401` - `get_llm_logs_unknown_job_returns_empty_array` (or 404 — clarify contract) - `get_llm_logs_returns_entries_for_known_job` (requires seeding a job_id in `llm_call_log`) --- ### GAP-03 — `is_preferred` ordering not covered in pipeline tests **Priority: Medium** `backend/src/services/synthesis.rs` implements preferred-first URL ordering (lines 320–422). `api_sources_preferred_test.rs` verifies the CRUD side but neither `pipeline_test.rs` nor any other test asserts that preferred sources are actually processed before non-preferred ones during a generation run. **What to add:** - Pipeline test: construct sources with mixed `is_preferred` values, run the pipeline with the mock provider, and assert preferred-source URLs appear before non-preferred ones in the scrape wave. --- ### GAP-04 — `source_diversity_window` feature is unimplemented (plans only) **Priority: Low / Tracking** Migration `20260323000013_add_source_diversity_window.sql` exists, but the corresponding Rust field is absent from `backend/src/models/settings.rs` and the pipeline does not yet use it. No tests exist because there is nothing to test yet. **What to add (when feature is implemented):** - Settings round-trip test: store and retrieve `source_diversity_window` - Pipeline test: verify that domains from recent syntheses are injected into the search prompt when `source_diversity_window > 0` - Settings boundary test: `source_diversity_window = 0` disables the feature --- ### GAP-05 — Settings tests do not cover `max_articles_per_source` boundary enforcement **Priority: Low** `api_settings_test.rs` includes `put_settings_boundary_values_succeed` but does not assert that values outside `[1, 10]` are rejected with 422. The validation logic exists in `models/settings.rs:53–54`. **What to add:** - `put_settings_max_articles_per_source_zero_returns_422` - `put_settings_max_articles_per_source_eleven_returns_422` --- ### GAP-06 — E2E tests use `test(` count of 1 per file; multi-scenario coverage is thin **Priority: Low** Every E2E spec registers exactly one Playwright `test()` (several use `test.describe` internally). The `generation-live.spec.ts` test is gated on `OPENAI_TEST_API_KEY` and does not run in normal CI. The remaining six specs cover: registration, settings/export, sources, themes (including schedules and preferred), admin providers. The stop-generation and llm-logs flows have no E2E counterpart. **What to add:** - Stop-generation E2E scenario inside `generation-live.spec.ts` (trigger then cancel before completion; assert SSE emits a cancelled/error event) --- ## Coverage by Feature | Feature | Unit | Integration | E2E | Notes | |---------|------|-------------|-----|-------| | Authentication (register / login / verify / logout) | — | 16 tests | 1 spec | Full coverage | | CSRF middleware | — | 4 tests | — | Good | | Settings CRUD | — | 7 tests | 1 spec | Missing out-of-range rejection tests | | Sources CRUD + bulk import + CSV | — | 36 tests | 1 spec | Strong | | Preferred sources (CRUD) | — | 3 tests | 1 spec (shared) | CRUD covered; pipeline ordering not tested | | Themes CRUD | — | 10 tests | 1 spec | Good | | Schedules CRUD | — | 9 tests (in api_themes_test.rs) | 1 spec (shared) | Good | | API keys (CRUD + encrypt/decrypt) | — | 18 tests | — | Good | | Admin (providers / rate-limits / users / audit) | — | 30 tests | 1 spec | Good | | Syntheses (CRUD + generation trigger) | — | 17 tests | 1 spec (live) | Good | | Stop generation | — | **0 tests** | **0 tests** | **GAP-01** | | Export (email / PDF / Markdown) | — | 13 tests | 1 spec | Good | | LLM logs | — | **0 tests** | live only (gated) | **GAP-02** | | Article history + provenance (CRUD) | — | 4 tests | live only (gated) | Thin; provenance success path missing | | Pipeline (heuristic / search / overflow / diversity / dedup) | 358 unit | 5 integration | live only (gated) | Preferred ordering not tested (GAP-03) | | Source diversity via history | — | **0 tests** | **0 tests** | Feature not yet implemented (GAP-04) | | `max_articles_per_source` validation | — | partial | — | GAP-05 | | Health check | — | 1 test | — | OK |