Skip to content

Testing

Garot Conklin edited this page Jun 10, 2026 · 3 revisions

Testing

Signal has a full unit test suite covering all pipeline modules including Pass 6 (weekly) and Pass 7 (monthly). All tests run offline — LLM calls and network I/O are mocked, so the suite completes in under two minutes with no external dependencies.

Running the tests

From the repo root:

.venv/bin/python -m pytest

Coverage is printed to the terminal and written as HTML to htmlcov/. Open htmlcov/index.html in a browser for a line-by-line view.

# Run a single file
.venv/bin/python -m pytest tests/test_store.py

# Run a single test
.venv/bin/python -m pytest tests/test_store.py::TestRuns::test_finish_run

# Skip coverage for faster feedback
.venv/bin/python -m pytest --no-cov

Test files

File What it tests
tests/conftest.py Shared fixtures used by all test files
tests/test_store.py SQLite persistence layer (pipeline/store.py)
tests/test_collector.py Feed collection and article normalization (pipeline/collector.py)
tests/test_analyzer.py All five analysis passes, LLM dispatch, clustering (pipeline/analyzer.py)
tests/test_reporter.py HTML report generation, GA snippet, markdown rendering (pipeline/reporter.py)
tests/test_weekly.py Pass 6 weekly synthesis logic (pipeline/weekly.py)
tests/test_monthly.py Pass 7 monthly synthesis logic (pipeline/monthly.py)

Coverage

Module Coverage Notes
analyzer.py 100% All passes, helpers, LLM dispatch, and error paths
store.py 94% Remaining gaps are error-path JSON decode branches
collector.py 92% Missing: load_config() (reads real YAML), minor _fetch_full_text edge cases
weekly.py 86% Ollama provider branch intentionally untested — Ollama is disabled in scheduled runs
monthly.py 98% Pass 7 synthesis, partial month detection, DB persistence
reporter.py 84% Daily, weekly, and monthly HTML template helpers
Overall ~93% 239 tests

Key design decisions

All LLM calls are mocked. Tests never invoke Claude or Ollama. The mock targets the _llm_call function inside the module under test:

@patch("pipeline.analyzer._llm_call")
def test_something(self, mock_llm, mock_entity_response):
    mock_llm.return_value = mock_entity_response

Database isolation. The tmp_db fixture patches store.DB_PATH to a temporary file scoped to each test. Tests never touch signal.db.

File system isolation. Reporter tests patch pipeline.reporter.REPORTS_DIR with tmp_path. No files are ever written to the real reports/ directory during a test run.

Adding a new test

  1. Add cases to the relevant existing file, or create tests/test_<module>.py for a new module.
  2. Use make_article() from conftest.py to build article fixtures — don't hand-craft dicts inline.
  3. Mock external I/O at the lowest boundary: LLM → patch _llm_call; HTTP → patch httpx.get or feedparser.parse; DB → use tmp_db; file writes → patch *_DIR with tmp_path.
  4. Group related cases into a class (e.g. class TestRuns:) for readable output.
  5. Verify with pytest --no-cov, then confirm coverage didn't regress with pytest.

For full details on fixtures and conventions, see tests/README.md in the repository.

Clone this wiki locally