# Mock Utilities

Mock clients, record factories, and recording a real API into fixtures, from @noverax/courier-mock.

## Mock client and factories

`@noverax/courier-mock/testing` builds a typed client that answers from mocks and records every call, and `defineFactory` builds the records it answers with:

```ts
import { courier, defineApi, defineGroup } from "@noverax/courier/define"
import { defineFactory } from "@noverax/courier-mock"
import { createMockClient } from "@noverax/courier-mock/testing"

type User = { id: number; name: string; email: string }

const { endpoint } = courier()
const users = defineGroup({ prefix: "users", endpoints: { getAll: endpoint.get("").response<User[]>() } })
const api = defineApi({ baseURL: "https://api.example.com", groups: { users } })

const userFactory = defineFactory<User>({ defaults: { id: 1, name: "Alice", email: "alice@test.com" } })

const { client, mock, history } = createMockClient<typeof api>()
mock("users", "getAll").resolves(
  userFactory.buildSequence(3, (index, base) => ({ ...base, id: index + 1, name: `User ${index + 1}` })),
)

const all = await client.users.getAll() // three users
history.length // 1
```

`createMockClient({ network })` answers as a slow or failing network would: it fails a share of calls and delays the rest, with each route's own override applied, and refuses an upload over its size limit or of a type it does not take, with the status a mock server answers it with. See [Network conditions](/docs/studio/network-conditions#in-code) for every field, and how each fault fails a call.

### Recording a real API

`createRecorder` calls a real backend from the API's definition and records each response as a fixture — a failed one too. Its client answers as the source's own client does: the data, or an `HTTPError` with the status and body.

```ts
import { createMockClient, createRecorder, replayFixtures } from "@noverax/courier-mock/testing"

const recorder = createRecorder({ source: api, targetUrl: "https://staging.example.com" })
await recorder.client.users.getAll()

const fixtures = recorder.getFixtures() // or recorder.toHar()
const { client: replayed } = createMockClient<typeof api>({ scenario: replayFixtures(fixtures) })
```

- The calls go to each endpoint's own method and path, at `targetUrl` or at the source's `baseURL`. To record through a Courier proxy — `createCourierServer`, or an app with Courier's handler — pass `proxy: { url, source }`: the proxy route's URL and the name the source has there.
- `mode` is `"record"` by default. `"passthrough"` calls the backend and records nothing; `"replay"` answers from the `fixtures` given, without the network, and rejects a call it has no recording of; `"record-missing"` replays what `fixtures` has and records the rest.
- `sanitize(data, part)` rewrites what is recorded — each call's variables (`part` is `"variables"`) and each answer's body (`"response"`) — never what the call returns. A recording made with it keeps no query in its URL, and replay passes a call's variables through the same function before looking for their recording; give `replayFixtures` and `replayHar` the sanitizer as their third argument for the same reason. `Authorization`, `Proxy-Authorization`, `Cookie` and `Set-Cookie` are never recorded. `filter` names the endpoints the recorder may call.

## Exports

| Entry Point | Description |
|---|---|
| `@noverax/courier-mock` | `defineMocks`, `defineMockServer`, `defineFactory`, `defineScenario`, `MockHttpError` |
| `@noverax/courier-mock/testing` | `createMockClient`, `createMockStore`, `createRecorder`, `replayFixtures`, `readRecordings`, `verifyRecordings` — see [Tests from Recorded Traffic](/docs/testing/recorded-tests) |
| `@noverax/courier-mock/server` | `createMockServer` — a standalone Hono-based mock server; `persist: { file }` keeps its store across restarts in an SQLite database it creates and migrates itself |
| `@noverax/courier-mock/contract` | Contract testing utilities |
| `@noverax/courier-mock/managed` | `runMockWorker` — the process Studio starts for each mock it runs |
