postjevsql.git / tests / support / README.md

Chapter 14: tests/support, the harness

Every test in chapter 13 begins the same way: start a Postgres, install the extension, point it at a mock Jev, make a table with a row in it. This library is that beginning, written once.

let mock = MockJev::start(|_| Reply::json(200, noul(0.95))).await;
let (_pg, client) = jev_instance(&mock).await;
let row = client.query_one("SELECT jev_prob(t, $1) FROM tickets t", &[&QUESTION]).await?;

Three lines, and behind them: initdb into a temporary directory, a postgres child process listening on a Unix socket in that directory, the extension served in place from the buck build, jev.endpoint, jev.model and jev.ca_file set to the mock, TYPESAFE_API_KEY=test-key in the server's environment, statement_timeout at 20 seconds, CREATE EXTENSION postjevsql, and a tickets table with one row ("Help! My payouts have been failing for 3 days."). When _pg goes out of scope, the server goes too.

Aside: a server that cannot outlive its test. Tests crash, and buck kills test binaries that run too long. A Postgres left running after either would hold its port and its memory until someone noticed. So the postmaster is started with PR_SET_PDEATHSIG: the kernel itself sends it SIGQUIT the moment the thread that started it dies, SIGKILL included. Nothing in the test has to remember to clean up, because nothing can forget.

Aside: at most one server per core. buck runs several test binaries at once, each running a test per core, each with its own Postgres. On 3 cores that once reached a load average of 28. So each instance holds an flock on one of a fixed set of slot files for its life, machine-wide, and a test that needs two at once (a target and a sidecar) takes them together with TestPostgres::start_all, all or none.

Postgres 17 needed one more trick. Postgres 18 can be told where to find an extension (extension_control_path); 17 reads extensions only from its own install directory. So on 17 the harness builds a prefix of symlinks in the temporary directory, with the built extension linked into its share/postgresql/extension, and runs the server from there.

Try it. Read mod.rs: jev_instance, jev_instance_with (extra settings) and jev_instance_env (the server's environment in full) are the whole public face of it.

For the people who maintain it

FileWhat
mod.rsjev_instance and its variants, TICKET, noul (a canned answer), assert_json_eq, and mock_jev, re-exporting jevcrates' jev-mock.
postgres.rsTestPostgres and Instance: the throwaway server, its slots, its teardown, and the PG17 symlink prefix.
dns.rsA DNS server on loopback answering from a fixed table and recording every question, for tests/dns.rs.
gates.rsWhat 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.
BUCKThe support library, visible to //tests/... and //tools/record-gates.
Cargo.tomlGenerated by tools/cargo-gen (chapter 20).

← Previous: Chapter 13, tests/ · Up: tests · Next: Chapter 15, fixtures/ →