postjevsql.git / tests / support / README.md
1# Chapter 14: tests/support, the harness
2
3Every test in chapter 13 begins the same way: start a Postgres, install the
4extension, point it at a mock Jev, make a table with a row in it. This
5library is that beginning, written once.
6
7```rust
8let mock = MockJev::start(|_| Reply::json(200, noul(0.95))).await;
9let (_pg, client) = jev_instance(&mock).await;
10let row = client.query_one("SELECT jev_prob(t, $1) FROM tickets t", &[&QUESTION]).await?;
11```
12
13Three lines, and behind them: `initdb` into a temporary directory, a
14`postgres` child process listening on a Unix socket in that directory, the
15extension served in place from the buck build, `jev.endpoint`, `jev.model`
16and `jev.ca_file` set to the mock, `TYPESAFE_API_KEY=test-key` in the
17server's environment, `statement_timeout` at 20 seconds, `CREATE EXTENSION
18postjevsql`, and a `tickets` table with one row ("Help! My payouts have been
19failing for 3 days."). When `_pg` goes out of scope, the server goes too.
20
21> **Aside: a server that cannot outlive its test.** Tests crash, and buck
22> kills test binaries that run too long. A Postgres left running after either
23> would hold its port and its memory until someone noticed. So the
24> postmaster is started with `PR_SET_PDEATHSIG`: the kernel itself sends it
25> SIGQUIT the moment the thread that started it dies, SIGKILL included.
26> Nothing in the test has to remember to clean up, because nothing can
27> forget.
28
29> **Aside: at most one server per core.** buck runs several test binaries at
30> once, each running a test per core, each with its own Postgres. On 3 cores
31> that once reached a load average of 28. So each instance holds an `flock`
32> on one of a fixed set of slot files for its life, machine-wide, and a test
33> that needs two at once (a target and a sidecar) takes them together with
34> `TestPostgres::start_all`, all or none.
35
36Postgres 17 needed one more trick. Postgres 18 can be told where to find an
37extension (`extension_control_path`); 17 reads extensions only from its own
38install directory. So on 17 the harness builds a prefix of symlinks in the
39temporary directory, with the built extension linked into its
40`share/postgresql/extension`, and runs the server from there.
41
42> **Try it.** Read [mod.rs](mod.rs): `jev_instance`, `jev_instance_with`
43> (extra settings) and `jev_instance_env` (the server's environment in full)
44> are the whole public face of it.
45
46## For the people who maintain it
47
48| File | What |
49| --- | --- |
50| [mod.rs](mod.rs) | `jev_instance` and its variants, `TICKET`, `noul` (a canned answer), `assert_json_eq`, and `mock_jev`, re-exporting jevcrates' `jev-mock`. |
51| [postgres.rs](postgres.rs) | `TestPostgres` and `Instance`: the throwaway server, its slots, its teardown, and the PG17 symlink prefix. |
52| [dns.rs](dns.rs) | A DNS server on loopback answering from a fixed table and recording every question, for `tests/dns.rs`. |
53| [gates.rs](gates.rs) | What the release gates' recordings ask (rows, question, SQL, fixture lookup), shared by `tools/record-gates`, which records them, and `tests/gate_*.rs`, which replay them. |
54| [BUCK](BUCK) | The `support` library, visible to `//tests/...` and `//tools/record-gates`. |
55| [Cargo.toml](Cargo.toml) | Generated by `tools/cargo-gen` (chapter 20). |
56
57← Previous: [Chapter 13, tests/](../) · Up: [tests](../) · Next: [Chapter 15, fixtures/](../fixtures/) →