Skip to content

feat(testing): adopter type-checking test suite — verify extension patterns are clean under strict mypy #625

@bokelley

Description

@bokelley

Problem

The SDK itself runs mypy --strict (pyproject.toml [tool.mypy] strict = true) and ships type-check tests under tests/test_*_types.py. Those tests validate the SDK's internal types — they don't validate that adopter extension patterns type-check cleanly under the same strictness.

This means: the SDK can be internally strict-clean while still forcing every adopter who follows the recommended patterns to carry # type: ignore comments. We do — ~50 of them across our schema package, and a handful of [arg-type] / [attr-defined] ignores at SDK call sites. Each one is "the docs told me to do this; mypy disagrees; I shrug and silence it."

Without an adopter-pattern test suite, regressions can ship silently — the SDK passes its own tests, but downstream mypy --strict runs go from "100 ignores" to "150 ignores" between releases and nobody notices.

What the JS SDK has

@adcp/sdk (TypeScript) has dedicated *.type-checks.ts files alongside its runtime tests:

  • src/lib/server/adcp-server.type-checks.ts
  • src/lib/server/decisioning.type-checks.ts
  • src/lib/server/decisioning.type-checks.ts

These exercise extension patterns under TypeScript's strict mode, with the test passing iff the file compiles. tsc-equivalent CI gate.

Proposed SDK shape

A tests/type_checks/ directory of small, self-contained Python files that:

  1. Exercise documented adopter patterns (extend LibraryProduct, override model_dump, return CreateMediaBuySubmittedResponse | CreateMediaBuySuccessResponse | CreateMediaBuyErrorResponse from a handler, construct BearerTokenAuth(validate_token=...) callbacks, etc.).
  2. Are run through mypy --strict as a CI gate.
  3. Have zero # type: ignore lines — that's the contract. If a pattern needs an ignore, the pattern is broken.
tests/type_checks/
├── extend_library_product.py        # subclass + add internal field
├── extend_response_with_override.py # the pattern in #620
├── handler_three_branch_return.py   # PlatformHandler.create_media_buy union
├── bearer_token_auth_callback.py    # BearerTokenAuth(validate_token=...)
├── webhook_payload_construction.py  # create_mcp_webhook_payload usage
├── lazy_platform_router_factory.py  # async factory signature
└── nested_model_dump_subclass.py    # the pattern in #615

CI step (Makefile target):

test-type-checks:
	mypy --strict tests/type_checks/

How adopters contribute

Adopters who hit a strictness wall can submit a failing tests/type_checks/<my_pattern>.py as a reproducer. The SDK either:

  1. Fixes the typing so the pattern is clean (becomes part of CI), or
  2. Documents that the pattern requires a specific cast(...) / explicit annotation (and adds the documented form to the test).

Either outcome is better than "shrug and silence" — the gap becomes visible.

Why this matters

Type ergonomics compound over time. If the SDK ships v5.0 and we discover at upgrade time that 30 new type: ignore lines are required, that's a high-friction migration. With an adopter-pattern test suite, those would surface during SDK PR review, not during downstream upgrades.

Concrete examples this would have caught (or made trivially diagnosable):

We can contribute the initial test files based on patterns we've already validated in production. Happy to send a draft PR if you like the shape.

Related

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions