Skip to content

Coverage exclusion policy

RAG006 sets two coverage floors on src/rag_sample/, enforced as a CI hard-fail: a 90% aggregate floor on combined line and branch coverage, and a 70% per-file floor on combined line and branch coverage for any non-excluded file. This page is the companion exclusion policy. It is intentionally maintained outside the ADR so it can evolve without amending the decision.

Both numbers in RAG006 are floors, not targets. Coverage above the floors is fine; below either floor fails CI.

The following are excluded from coverage measurement, in the order they appear in pyproject.toml [tool.coverage.run] omit:

Path or patternReason
src/rag_sample/__init__.py and any other package __init__.pyTrivial re-exports; coverage adds no signal
src/rag_sample/settings.py (and any pydantic-settings model module)Configuration dataclasses; covered indirectly by every test that loads settings
src/rag_sample/__main__.py and module entrypointsImported as scripts at runtime; not exercised by unit tests
src/rag_sample/api/lambda_handler.py (the Mangum entrypoint)One-liner handler = Mangum(app); covered functionally via integration testing only
scripts/export_openapi.pyBuild-time helper, not application code
Generated code under src/rag_sample/_generated/ if anyRe-generated; tests target the inputs and the consumers

Code may opt out of coverage at line granularity using # pragma: no cover. Use this only for branches that cannot meaningfully be tested:

  • defensive if TYPE_CHECKING: blocks
  • platform-specific code paths that the CI environment does not exercise (rare in this product)
  • raise NotImplementedError placeholders in unfinished abstract methods, removed once implemented

Do not use # pragma: no cover to bypass coverage on logic that is testable but inconvenient. If a branch is hard to test, write a test or refactor the code.

This list is the policy of record. Add or remove entries here and update pyproject.toml [tool.coverage.run] omit in the same PR. No ADR amendment is required — RAG006 references this page rather than enumerating the exclusions itself.

Two operational notes:

  1. Never add an entry just to dodge a failing CI run. Investigate why coverage dropped and fix the underlying gap before considering an exclusion. Exclusions are for code that genuinely cannot be unit-tested, not for code that is hard to unit-test.
  2. Keep the list short. A growing exclusion list silently lowers the effective floor. If the list grows beyond a handful of entries, that is a signal to revisit RAG006 itself.

The canonical invocation is documented in RAG006. The exclusion list applies through pyproject.toml:

[tool.coverage.run]
branch = true
source = ["rag_sample"]
omit = [
"src/rag_sample/**/__init__.py",
"src/rag_sample/settings.py",
"src/rag_sample/__main__.py",
"src/rag_sample/api/lambda_handler.py",
"scripts/export_openapi.py",
]

Keep this snippet in sync with the table above.

The per-file 70% floor is enforced as a separate CI step after the suite (since pytest-cov has no native per-file --fail-under); the canonical implementation is coverage report --fail-under=70 --skip-covered, which fails if any non-excluded file is below 70% combined line and branch.