The wrapper API is contract-bound; the model behind it isn't
An AI service has two layers to test in CI:
- API layer — request/response shape, status codes, auth, rate limits. Conventional contract testing.
- Model layer — semantic / behavioral correctness. Eval-style tests.
The first layer is what your downstream consumers depend on; it must be stable. The second layer is allowed to vary — within agreed bounds.
API contract tests
- OpenAPI / Pydantic schema — every response validates against the documented shape.
- Status code matrix — empty input → 400; unauthorized → 401; rate limit → 429; happy path → 200.
- Streaming protocol — for SSE / WebSocket, assert event types, chunk separators, terminating events.
- Backward compatibility — old clients keep working when fields are added; never remove fields without a deprecation window.
Mocking the model
For API contract tests, mock the underlying model. A real model call is slow, expensive, and non-deterministic. Inject a fake adapter that returns a canned response that matches the schema. The eval layer (separate jobs) handles real model behavior.