1# Chapter 21½: inside jev-mock/src — the set, the script and the recorder 2 3Three files: the server itself, and the two ways to give it someone else's 4lines. 5 6## The files, in reading order 7 81. **[lib.rs](lib.rs)**: the server. 9 - `Recorded` is one request as the server saw it: HTTP version, method, 10 path, headers, body. 11 - `Reply` is what to answer: `Reply::json(status, body)`, then 12 `.header(name, value)`, `.after(delay)` (stall first; a client that 13 gives up resets the stream, which `abandoned()` counts) and 14 `.after_requests(n)` (stall until n requests have arrived, so a test 15 can count what a client sends before its first answer without racing 16 it). `raw_body` sends exact bytes instead of the JSON. 17 - `MockJev::start(respond)` serves on `127.0.0.1` with a certificate for 18 that address; `start_for(names, respond)` adds host names; 19 `start_limited(max_streams, respond)` advertises 20 `SETTINGS_MAX_CONCURRENT_STREAMS`. Each start makes a fresh CA with 21 `rcgen`, writes it to a temp directory (`ca_file()`), and accepts only 22 ALPN `h2`, as the real endpoint does. 23 - Reading the result: `endpoint()`, `endpoint_via(host)`, `requests()`, 24 `connections()`, `accepts()`, `peak_in_flight()`, `abandoned()`, 25 `pings()`. 26 - Making trouble: `goaway()` sends GOAWAY on every open connection; 27 `ignore_pings_on_next(n)` makes the next n connections swallow their 28 PING acknowledgements. 29 - `Frames`, at the bottom, sits between TLS and hyper and reads the 30 plaintext HTTP/2 stream frame by frame. hyper answers PINGs itself, so 31 this is the only place the mock can count them or withhold the replies. 322. **[replay.rs](replay.rs)**: `Fixture` and `Replay`. A fixture is JSON, 33 `{"model", "exchanges": [{"request", "responses": [{"status", "headers": 34 [[name, value]], "body"}]}]}`, with `request` and `body` as the bytes on 35 the wire, as text. `Fixture::parse`, `load` and `to_json` read and write 36 it. `Replay::new(&fixture).responder()` serves each request's responses 37 in order; `misses()` lists what it lacked, and `assert_complete()` panics 38 naming them, as the last handle's `Drop` does unless the test is already 39 panicking. 403. **[forward.rs](forward.rs)**: `Forward::new(upstream, api_key, model)` 41 and its `responder()`, which sends each request upstream with 42 `block_in_place` (so the mock must run on a multi-threaded tokio 43 runtime), one fresh connection per request. Upstream it passes only 44 `content-type` and `x-typesafe-retry-count` and sets its own 45 `authorization`; from the answer it keeps only `content-type`, 46 `x-typesafe-request-id`, `retry-after` and `retry-after-ms`. A retried 47 request records its answers in order. A failure to reach the endpoint is 48 answered with a 400 (`forward_failed`) and recorded as nothing. 49 `exchange(body)` sends a request built elsewhere; `fixture()` returns 50 everything recorded so far. 51 52## Try it 53 54``` 55cargo test -p jev-http --test mock 56``` 57 58jev-mock has no tests of its own; its users' tests exercise it. 59 60## For the people who maintain it 61 62### In this folder 63 64| File | What | 65| --- | --- | 66| [lib.rs](lib.rs) | `MockJev`, `Reply`, `Recorded`, the frame watcher. | 67| [replay.rs](replay.rs) | `Fixture`, `Replay`, `Response`. | 68| [forward.rs](forward.rs) | `Forward`: record a fixture from a real endpoint. | 69| [CLAUDE.md](CLAUDE.md) | Invariants for agents. | 70 71← Previous: [Chapter 21: jev-mock](../) · Up: [jev-mock](../) · The end of the guide. [Back to Chapter 16: jevcrates](../../)