The existing suite already exercises uploads, conversions, compression, redaction, metadata queries, and file utilities through both synchronous and asynchronous clients. The expectations below condense every technique we rely on so new endpoints launch with complete coverage on the first pass—no reviewer iteration required.
- Cover both transports everywhere. Write distinct sync (
PdfRestClient) and async (AsyncPdfRestClient) tests for every scenario—such as happy paths, request customization, validation failures, file helpers, and live calls. Do not hide the transport behind a parameter; the test name itself should reveal which client is under test. - Maintain high client coverage.
PdfRestClientandAsyncPdfRestClientare the primary customer-facing entry points. Every public client method must have at least one unit test that exercises the REST call path (MockTransport asserting method/path/headers/body). Optional payload branches (for example,pages,output,rgb_color, and output-prefix fields) require explicit tests so serialization differences are caught early. - Keep endpoint tests in their home files. When adding or restoring coverage
for an endpoint, place the test in that endpoint's existing test module (for
example,
tests/test_convert_to_excel.py), not in a generic cross-endpoint coverage file. - Check client coverage regularly. Run
uvx nox -s class-coverageto enforce minimum function-level coverage forPdfRestClientandAsyncPdfRestClient. - Exercise both sides of the contract. Hermetic tests (via
httpx.MockTransport) validate serialization and local validation. Live suites prove the server behaves the same way, including invalid literal handling. - Know where coverage lands. The nox
testssession writes coverage reports tocoverage/py<version>/(XML, Markdown, and HTML). Example:coverage/py3.12/coverage.xml,coverage/py3.12/coverage.md,coverage/py3.12/html/. - Reset global state per test. Use
monkeypatch.delenv("PDFREST_API_KEY", raising=False)(orsetenv) so clients never inherit accidental API keys. Patchimportlib.metadata.versionwhen asserting SDK headers. - Lean on shared helpers. Reuse
tests/graphics_test_helpers.py(make_pdf_file,build_file_info_payload,PdfRestFileID.generate()),tests/resources/, and fixtures fromtests/conftest.pyto keep payloads deterministic. - Assert behaviour, not just invocation. Validate outbound payloads,
headers, query params, response contents, warnings, and timeouts. Track
request counts (
seen = {"post": 0, "get": 0}) so redundant HTTP calls fail loudly.
- Verify API keys sourced from kwargs vs environment variables, and ensure
invalid/missing keys raise
PdfRestConfigurationError. - Confirm SDK identity headers (
wsn,User-Agent,Accept) by patchingimportlib.metadata.version. - Assert
PdfRestClientomitsApi-Keywhen pointed at custom hosts and honors caller-provided headers/query params even for control-plane calls like.up().
- Use
httpx.MockTransporthandlers that assert:- HTTP method + path (
/png,/compressed-pdf,/resource/{id}, etc.). - Query parameters and headers (trace/debug flags, mode switches, custom auth headers).
- JSON payloads obtained via
json.loads(request.content)and compared to the relevant Pydantic payload’s.model_dump(mode="json", by_alias=True, exclude_none=True, exclude_unset=True).
- HTTP method + path (
- For “should not happen” cases (invalid IDs, missing profiles), set the
transport to immediately raise
(
lambda request: (_ for _ in ()).throw(RuntimeError)orpytest.fail) so local validation is guaranteed.
- Simulate 4xx/5xx responses and assert the correct exception surfaces:
PdfRestAuthenticationError(401/403),PdfRestApiError(other status codes, include error text),PdfRestTimeoutError(raisehttpx.TimeoutException),PdfRestTransportError(raisehttpx.TransportError). - Cover retry logic by returning retriable responses multiple times and
confirming the client retries before raising (see
tests/test_client.pypatterns forRetry-After). - Include
pytest.raises(..., match="...")to ensure exception messages capture server warnings, retry hints, or timeout wording.
- Sync tests wrap clients in
with PdfRestClient(...):. - Async tests use
@pytest.mark.asyncioandasync with AsyncPdfRestClient(...):. - When asserting async failures, place
pytest.raisesinside theasync withblock. Python forbids mixingwithandasync within a single statement.
- For every endpoint that accepts
extra_query,extra_headers,extra_body, ortimeout, add explicit tests (sync + async) proving those options propagate. Capturerequest.extensions["timeout"]and assert every component equalspytest.approx(expected). - For both sync and async endpoint helpers, ensure request-customization or
success tests also exercise endpoint-specific optional payload branches (for
example
output,output_prefix,pages,page_groups, redaction payloads) so non-live class-function coverage does not depend on live suites.
- Use the payload models directly (
PngPdfRestPayload,PdfCompressPayload,PdfMergePayload,PdfSplitPayload,PdfRedactionApplyPayload, etc.) to assert serialization, output-prefix validation, and range normalization in isolation from the client. - Through the client surface, pair calls with
pytest.raises(ValidationError, match="...")for MIME enforcement (“Must be a PDF file”), dependency rules (“compression_level 'custom' requires a profile”), profile MIME validation (“Profile must be a JSON file”), and list length bounds. - Cover all accepted literal shapes: single literal vs list vs tuple for
PdfInfoQuery; dict vs tuple vs sequence forPdfMergeInput; tuple/list/None forPdfRGBColor; JSON-friendly dicts for redaction instructions.
- Use
pytest.mark.parametrizewithpytest.param(..., id="friendly")to enumerate literals such ascolor_model,smoothing,compression_level,page_range, merge selectors, JPEG quality boundaries, or any future literal surfaced by new APIs. - Include invalid literals (such as
"extreme"compression levels, unsupportedcolor_modelvalues, or smoothing arrays containing duplicates/more-than-allowed entries) to ensure validation errors remain descriptive—usere.escape(...)when asserting. - For numeric fields (resolution, DPI, percentages, counts, radii, opacity,
etc.), exercise the extremes: the documented minimum/maximum, the first legal
value just inside each bound, and at least one value just outside the range.
Treat every
ge,le,gt,lt, orAnnotatedconstraint as requiring explicit boundary tests. - For textual ranges, cover ascending permutations,
"last","1-last", descending segments (where allowed), and disallowed selectors (such as"even"/"odd"when the server forbids them). - When endpoints expose optional payload arguments (output prefixes, diagnostics toggles, merge metadata, future knobs), include both defaulted and explicitly provided cases so serialization doesn’t regress.
- Assert the concrete response types (
PdfRestFileBasedResponse,PdfRestFile,PdfRestInfoResponse, etc.). - Inspect every relevant attribute:
- File metadata (
name,type,size,url,warning). input_idechoes the uploaded file ID (string comparison).output_filescount matches the number of IDs returned by the mock service.
- File metadata (
- For file-service helpers, compare results against
_build_file_info_payloadvia_assert_file_matches_payload.
- Uploads: assert multipart bodies include the correct number of
name="file"parts and filenames, and thatclient.files.create/create_from_paths/create_from_urlsfetch info documents afterward. - Downloads: cover
download_file,files.read_bytes/text/json, andfiles.write_byteswithtmp_path. Confirm file contents match expected bytes. - Streaming: tests should enter
files.stream()viawith/async with, iterate overiter_raw,iter_bytes,iter_text, anditer_lines, and join chunks back to the original payload. Manage nested async context managers usingExitStack/AsyncExitStack. - ID validation: ensure malformed IDs raise before sending HTTP requests (transport should error if called).
- Conversions (such as
convert_to_png,convert_to_jpeg,convert_to_word, or any future format helper) must verify payload serialization, request customization, MIME enforcement, multi-file guards, and smoothing/quality enumerations. - Compression helpers (such as
compress_pdf) enforce the profile dependency (custom requires JSON profile; presets reject profiles) and validate MIME types for both PDFs and profiles in sync + async contexts. - Split/Merge style endpoints (such as
split_pdformerge_pdfs) should exercise tuples/dicts/lists, ensure payload serializers emit the correct parallel arrays, and include validation errors for insufficient sources or invalid page groups. - Redaction and metadata helpers (such as
preview_redactions,apply_redactions,query_pdf_info) must cover literal shapes, optional parameters, and invalid presets. - Treat these as templates for any future API from
pdfrest_api_reference_guide.html: identify its payload model, enumerate literals/numeric bounds, and apply the same sync+async/unit+live layering.
- Generate fake uploads with
make_pdf_file,build_file_info_payload, andPdfRestFileID.generate()to keep IDs valid. - When triggering MIME validation, fabricate
PdfRestFileobjects with deliberately incorrecttypevalues (PNG for PDF-only endpoints, PDF for JSON-only profiles). - Use
_StaticStream/_StaticAsyncStreamfromtests/test_files.pyto simulate streaming responses without touching disk.
- Location & structure: Place suites under
tests/live/with one module per endpoint (test_live_compress_pdf.py,test_live_convert_to_png.py,test_live_files.py, etc.). - Fixtures: Reuse shared fixtures (
pdfrest_api_key,pdfrest_live_base_url). Upload deterministic assets fromtests/resources/viacreate_from_paths(orclient.files.create) so responses are predictable. Usepytest.fixture(scope="module"/"class")andpytest_asyncio.fixtureto cache uploaded PDFs/profiles for both transports. - Sync + async parity: Every live module should contain matching sync and
async tests for success, streaming, and invalid paths (compression levels,
conversion options, file streaming helpers). Treat transport-plumbing
customization (
extra_query,extra_headers,extra_body,timeout) as a unit-test responsibility unless the endpoint exposes server-observable behavior tied to those options. - Enumerate literals: Parameterize over every accepted literal (compression
levels,
color_model,smoothing, merge selectors, redaction presets). Each literal should hit the server once per transport. - Optional arguments: Exercise options such as custom output prefixes, diagnostics toggles, merge metadata, and URL uploads. Validate the server honors them (filenames start with the user-provided prefix, warnings appear when expected).
- Customization placement: Verify request customization wiring in mocked tests (sync + async) for each endpoint. Live customization tests are optional and should be added only when they validate server-side behavior beyond client request assembly.
- Negative live cases: Override JSON via
extra_body/extra_queryto bypass local validation and assertPdfRestApiError(or the exact server exception) surfaces—for example, sending an invalid compression literal or smoothing option. - Streaming + downloads: In live
filessuites, coverwrite_bytes,files.stream().iter_*, and URL uploads. Manage nestedasync withblocks usingAsyncExitStackto ensure resources are released. - Assertions: Verify file names, MIME types, sizes, warnings, and that
input_idmatches the uploaded ID. When fixtures are deterministic (report.pdf,compression_profile.json), assert exact values rather than generic truthiness. - Resource reuse: For
create_from_urls, first upload files to retrieve stable URLs, then call the URL endpoint—never rely on arbitrary third-party hosts.
- Always combine clients with
pytest.raises(including descriptivematch=) when testing validation or HTTP errors. For sync contexts you can use a compoundwith (PdfRestClient(...) as client, pytest.raises(...)):; for async, placepytest.raisesinside theasync withblock. - Distinguish between:
- Local validation failures (
ValidationError,ValueError) that should prevent HTTP calls. - Server/transport failures (
PdfRestApiError,PdfRestAuthenticationError,PdfRestTimeoutError,PdfRestTransportError,PdfRestErrorGroup, etc.).
- Local validation failures (
- When behaviour should short-circuit locally (bad UUIDs, empty query lists, missing profiles), configure the transport to raise if invoked so the test proves no HTTP request occurs.
- When endpoints intentionally raise pdfRest-specific
ExceptionGroupsubclasses (such asPdfRestErrorGroupproduced by delete failures), capture them withpytest.RaisesGroup/pytest.RaisesExc, and use thecheck=hook to assert the aggregate is the expected group class. This verifies both the group message and each individual member (PdfRestDeleteError, future custom errors) instead of relying on the aggregate text alone.
- Context managers everywhere: Treat clients and file streams as context managers so transports close cleanly.
- pytest fixtures: Use readable fixtures (such as
client,uploaded_pdf_for_compression,live_async_file) with appropriate scopes. Preferpytest.param(..., id="...")so parametrized IDs stay intelligible. - No real network in unit tests: Hermetic tests must rely solely on
httpx.MockTransport. - ID serialization: Confirm payloads serialize uploaded
PdfRestFileobjects as IDs via_serialize_as_first_file_idrather than embedding nested structures. - Timeout propagation: Every endpoint that accepts
timeoutneeds both sync and async coverage that inspectsrequest.extensions["timeout"]. - Multi-file safeguards: Assert endpoints that accept exactly one file/profile reject extra inputs (such as conversions or compression profiles). Conversely, endpoints that require multiple sources (such as merge operations) should test both valid (≥2) and invalid (<2) cases.
- Shared validation suites: When new payload shapes or validators emerge,
add/update suites such as
tests/test_graphic_payload_validation.pyso every endpoint inheriting the behaviour gains coverage automatically.
pdfRest will keep expanding. When implementing a new helper from
pdfrest_api_reference_guide.html—whether it resembles existing conversions,
merges, inspections, or something entirely new—follow this playbook:
- Capture inputs and constraints. Translate every documented literal, numeric range, dependency, and optional field into payload annotations/tests. Cover boundary values (minimum, maximum, first legal values inside the range, and at least one outside value).
- Map outputs. Determine whether the endpoint returns files, JSON, or both, and assert every returned attribute or warning.
- Layer coverage. For each behaviour, add sync + async unit tests (mocked) plus sync + async live tests hitting the real service with both valid and intentionally invalid requests.
- Reuse patterns. If the endpoint resembles an existing suite (such as conversions, redaction, compression, file uploads, metadata queries), mirror the structure and assertions to stay consistent.
- Evolve shared tests. Whenever a new validation rule becomes reusable—such as a fresh output-prefix constraint or numeric range validator—extend the shared helper modules and suites so future endpoints benefit automatically.
Following these rules ensures new endpoints debut with deterministic unit tests and fully instrumented live coverage. Treat the existing conversion, compression, redaction, split/merge, and file suites as templates—if a behaviour exists today (or will exist tomorrow), there should either be a matching test pattern already or one added alongside the new API.