# Python testing

Test your integration without API calls using the [Python SDK](/docs/sdk/python).

## Mock client

```python
from calmlens import CalmLensAPIError, CalmLensError
from calmlens.testing import MockCalmLensClient

client = MockCalmLensClient(project_id="project", timeout=5)
assert client.text.moderate(text="Hello")["status"] == "approved"
assert client.requests[-1].path == "/projects/project/assets/upload"

client.enqueue_error(TimeoutError("timed out"))  # next call raises CalmLensTimeoutError ("sdk/timeout")
client.enqueue_error(CalmLensError("asset/not-found"))  # next call raises CalmLensAPIError with status 404
client.enqueue_event("asset.classified", facts=[{"key": "status", "value": "approved"}])
```

`MockCalmLensClient` is the real client with an in-memory API behind it, so every call goes through the real validation, encoding, error handling and retries. The in-memory API answers with the same status codes, error bodies and headers as CalmLens, including request IDs and idempotent replays.

The mock accepts the real client's configuration and records each request in `client.requests` (`method`, `url`, `path`, decoded `query`, `body`, `headers`). `enqueue_error` fails the next request: a `CalmLensError` becomes an API error response with the matching HTTP status, and a network exception fails like a real connection. `enqueue_event` adds an event to the feed that `events.list` and `events.iter` return. Lists honour filters and ordering, and search matches keywords.

It returns configured decisions; it does not run a moderation model, fetch remote URLs or transform media. Keep `calmlens.testing` out of production imports.
