# CLAUDE.md: Testing with pytest

## Commands
- `uv run pytest -q` · one file: `uv run pytest tests/test_x.py -q` · last failures: `uv run pytest --lf -x`
- Coverage: `uv run pytest --cov=src --cov-report=term-missing`

## Conventions
- Tests in `tests/`, mirroring `src/` paths; files `test_*.py`, functions `test_<behavior>`.
- Plain `assert` statements; `pytest.raises(ValueError, match="...")` for errors.
- Shared setup as fixtures in `conftest.py` with the narrowest scope that works. Use `tmp_path` for files and `monkeypatch` for env vars.
- `@pytest.mark.parametrize` for input tables with readable `ids`.
- Mock at boundaries (HTTP, clock, external services): `respx`/`responses` for HTTP, `freezegun` or injected clocks for time. Use `mocker` (pytest-mock) or `monkeypatch` rather than nested `unittest.mock.patch` decorators.
- Async tests use the plugin already in the repo (`pytest-asyncio` or `anyio`).
- Database tests use transactions rolled back per test or a throwaway container, never a shared dev DB.
- Mark slow or networked tests (`@pytest.mark.slow`) and keep the default run fast.

## Rules for agents
- Reproduce a bug with a failing test before fixing it.
- Never mark tests `skip`/`xfail`, loosen assertions, or edit expected values just to get green. Explain instead.
- Keep tests deterministic: no real network, no randomness without a seed, no order dependence.

## Done when
The full suite passes and new code has tests for the happy path and at least one failure path.
