psf/requests
Testing is pytest-only, concentrated in one 108 KB file: `tests/test_requests.py` holds 237 of the ~347 test functions, with `tests/test_utils.py` (64) and `tests/test_lowlevel.py` (13) next. Integration tests run against a real local `httpbin` via the `pytest-httpbin` fixture (wrapped by `conftest.prepare_url` to guarantee a trailing slash), while wire-level behaviour uses a hand-rolled socket `Server` thread in `tests/testserver/server.py`. TLS behaviour is covered by checked-in fixture certificates (`tests/certs/{valid,expired,mtls}`) plus a `trustme`-generated no-SAN server fixture. Doctests in the library modules are executed by `--doctest-modules`. Gaps: `adapters.py` — the single most security-relevant module — has a one-test dedicated file, and there is no coverage threshold enforced anywhere.
- The framework is pytest with `minversion = 6.2`, `testpaths = ["tests"]`, and `--doctest-modules` plus `NORMALIZE_WHITESPACE ELLIPSIS` optionflags, so module docstring examples are part of the suite.
- Test distribution is extremely lopsided: `tests/test_requests.py` defines 237 `test_` functions across 7 classes (`TestRequests`, `TestCaseInsensitiveDict`, `TestMorselToCookieExpires`, `TestMorselToCookieMaxAge`, `TestTimeout`, `RedirectSession`, `TestPreparingURLs`), versus 64 in `test_utils.py` and 13 in `test_lowlevel.py`.
- An autouse fixture `clean_proxy_environ` monkeypatches away `http_proxy`, `https_proxy`, `no_proxy`, `ftp_proxy` and `all_proxy` (both cases) for every test, so proxy tests must set env vars explicitly.
- Integration coverage runs against a live httpbin server through the `httpbin`/`httpbin_secure` fixtures, pinned via `pytest-httpbin==2.1.0` and `httpbin~=0.10.0` in the `test` dependency group.
- Wire-level tests use a purpose-built threaded socket server with `text_response_server` / `basic_response_server` helpers, exercising chunked upload, conflicting Content-Length, RFC 1808 non-ASCII redirects and fragment handling.
- Digest auth is covered at the socket level by three dedicated tests: `test_digestauth_401_count_reset_on_redirect`, `test_digestauth_401_only_sent_once`, and `test_digestauth_only_on_4xx`.
- TLS paths are tested with committed fixture certs — expired server cert, valid server cert, and an mTLS client cert — including `test_different_connection_pool_for_mtls_settings` verifying pool-key separation.
- A `nosan_server` fixture mints a CA and a commonName-only (no subjectAltName) certificate at runtime using `trustme`, backing the SNI/SAN warning tests.
- `test_response_iter_lines_reentrant` is marked `@pytest.mark.xfail` and documents a known defect: `Response.iter_lines()` is not reentrant-safe, matching the docstring note on the method itself.
- Behaviour differences between urllib3 1.x and 2.x are handled by skips keyed on `is_urllib3_1`, e.g. `test_content_length_for_string_data_counts_bytes` skips on 1.x because it encodes strings as latin-1 rather than utf-8.
- `adapters.py` has a single dedicated regression test (`test_request_url_handles_leading_path_separators`); the rest of the adapter is exercised only indirectly through `test_requests.py`.
- CI runs `make ci` (`pytest tests --junitxml=report.xml`) across 8 Python versions × 3 OSes, plus two special jobs: `no_chardet` uninstalls both detection libraries, and `urllib3` pins `urllib3<2`.
- Typechecking is enforced in CI as a separate gate: pyright in `strict` mode over `include = ["src/requests"]`, run on Python 3.10 and 3.14 — note `tests/` is deliberately outside the typechecked surface.
- Coverage is available locally via `make coverage` with a `.coveragerc` whose only directive omits `requests/packages/*` — a stale path given the package now lives under `src/requests/`; no minimum threshold is configured and no workflow uploads coverage.
- Lint runs as its own workflow executing pre-commit (`ruff-check --fix`, `ruff-format`, plus whitespace/merge-conflict hooks), with `tests/test_lowlevel.py` explicitly excluded from `ruff-format`.