Testing
Turn Claude Code into a long-term collaborator on PostgreSQL internals — cited knowledge corpus, agent skills, slash commands, and task-shaped scenarios for backend hacking.
npx -y skills add matejformanek/postgres-claude --skill testingAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Pick the right PostgreSQL test flavor for a core / contrib patch — covers pg_regress .sql / .out pairs, isolationtester specs and permutations for concurrency / deadlock races, TAP (PostgreSQL::Test::Cluster) for multi-node and pg_basebackup / replication / recovery scenarios, and src/test/modules for in-tree C test modules. Spans where test files live, how to wire them into the right schedule / meson.build, and how to run a single test fast (REGRESS_OPTS, --temp-instance, PROVE_FLAGS). Use whenever adding tests to a PG patch, reviewing a patch's test coverage, picking between regress / isolation / TAP for a new feature, debugging a regression diff, or asked "where should the test for X live". Skip for pytest / unittest / Jest / Vitest / Mocha / RSpec / JUnit / Go testing / Rust cargo-test app testing, pgbench / sysbench / HammerDB performance benchmarking, end-to-end / Selenium / Playwright web testing, and load / stress testing of production PG.
SKILL.md
7.5 KB, as published. Nobody here has run it
Testing PostgreSQL — decision tree
Companion to knowledge/conventions/testing.md. That file is the long-form
reference; this is the action card.
Step 1: Pick the flavor
Answer these in order. First "yes" wins.
-
Does the behavior need multiple sessions / concurrent transactions / heavyweight-lock contention? → isolation spec in
source/src/test/isolation/specs/<name>.spec. Caveat:isolationtesteronly sees heavyweight locks viapg_locks. LWLock / buffer-pin contention → usesource/src/test/modules/injection_points/(or a custom C test module) instead.Minimal spec grammar:
setup { CREATE TABLE t (a int); } teardown { DROP TABLE t; } session "s1" { step "s1a" { BEGIN; } step "s1b" { COMMIT; } } session "s2" { step "s2a" { SELECT * FROM t; } } permutation "s1a" "s2a" "s1b"When any step blocks, the
permutationline is mandatory — auto-generated permutations will hang CI. -
Does it need initdb, a restart, multiple clusters, replication, a backup, a signal, or testing a CLI tool (
pg_dump,pg_basebackup,pg_rewind)? → TAP test in the appropriatet/dir:- Recovery / replication →
source/src/test/recovery/t/NNN_name.pl - Logical replication →
source/src/test/subscription/t/NNN_name.pl - Auth →
source/src/test/authentication/t/NNN_name.pl - SSL →
source/src/test/ssl/t/NNN_name.pl - A specific CLI tool →
source/src/bin/<tool>/t/NNN_name.pl - Contrib extension →
source/contrib/<ext>/t/NNN_name.pl - Brand new area without an obvious home → ask before creating a directory.
- Recovery / replication →
-
Are you testing a C-level hook, internal API, or something only reachable from C? → Add (or extend) a test module under
source/src/test/modules/<name>/, then drive it from a.sqlor TAP test in the same module dir. -
Else (pure SQL: planner, builtin function, DDL, error message, catalog behavior): regress test.
source/src/test/regress/sql/<name>.sql+expected/<name>.out, wired intoparallel_schedule.
Step 2: Wire it in (don't skip — easiest mistake)
| Flavor | File you must edit |
|---|---|
| regress | source/src/test/regress/parallel_schedule (add to a test: line, max 20 per group) |
| isolation | source/src/test/isolation/isolation_schedule (test: <name>) |
| TAP | meson.build in the same dir as t/ (e.g. source/src/test/recovery/meson.build) AND the Makefile if one exists |
| modules | source/src/test/modules/meson.build if adding a new module dir |
Step 3: Generate expected output
For regress and isolation: write the .sql / .spec, run the test, copy the
produced output to expected/:
# regress
meson test -C build regress/regress # will FAIL the first time
cp source/src/test/regress/results/<name>.out source/src/test/regress/expected/<name>.out
# now re-run and check it passes
meson test -C build regress/regress
Always read the generated .out before committing. Anything OID-y,
timestamp-y, or row-order-y is a future buildfarm failure. Cast it, ORDER BY it,
or SET it away.
Step 4: Run only your test (fast loop)
TAP replication-sync idiom (in src/test/recovery/t/):
$primary->wait_for_replay_catchup($standby);
# or, explicitly:
my $lsn = $primary->lsn('insert');
$standby->poll_query_until('postgres',
"SELECT pg_last_wal_replay_lsn() >= '$lsn'::pg_lsn");
End every TAP file with done_testing();. TAP tests verify replicated state
(LSN advance, table contents), not WAL-record identity — use pg_waldump from
a shell step if you really need the latter.
# Single TAP test
meson test -C build --suite recovery 001_stream_rep -v
# Single isolation spec
cd build/src/test/isolation && \
./pg_isolation_regress --temp-instance=/tmp/iso \
--top-builddir=../../../.. <spec_name>
# Single regress test
cd build/src/test/regress && \
./pg_regress --temp-instance=/tmp/pgr \
--top-builddir=../../../.. <test_name>
# Whole suite by name
meson test -C build --suite regress
meson test -C build --suite isolation
meson test -C build --suite recovery
make equivalents (from source/src/test/<area>/):
make check— fresh temp instance, what CI does. Use before submitting.make installcheck— against your running cluster. Faster, less safe.make check PROVE_TESTS=t/NNN_name.pl— just one TAP test.
Step 5: When it fails
- regress: open
build/src/test/regress/regression.diffsfirst. If the diff is what you want, copyresults/<name>.outoverexpected/<name>.out. - isolation:
build/src/test/isolation/output_iso/regression.diffs. Hanging = invalid permutation (a step blocks with nothing to unblock it). - TAP:
build/testrun/<suite>/<test>/log/regress_log_*is the Perl-side log. Read it before the postmaster logs. SetPG_TEST_NOCLEAN=1to keeptmp_check/even on success.
Cheat sheet — decision in one line
"Multiple sessions? → isolation. Initdb-or-restart? → TAP. C hook? → module. Otherwise → regress."
Common rejections in review
- "Use a regress test, not TAP" — for anything expressible in SQL.
- "Output isn't stable" — OIDs, timestamps, plan costs, unsorted results.
- "Add it to the schedule" — file exists but isn't wired into
parallel_schedule/isolation_schedule/meson.build. - "Don't introduce
_1.outvariants" — prefer to stabilize the test (markers in isolation;SET/ casts in regress). - "Renumbered existing TAP tests" — breaks backports. Always append
NNN. - "Uses non-core Perl module" — only
IPC::Runis allowed; everything else must be in Perl 5.14 core.
See knowledge/conventions/testing.md for full citations and the long form.
Cross-references
knowledge/conventions/testing.md— long-form reference with citations..claude/skills/build-and-run/SKILL.md—--suite setuprequirement, single-test invocations under meson..claude/skills/debugging/SKILL.md— waitpoints +PGOPTIONS="-W N"for catching things that happen too fast..claude/skills/coding-style/SKILL.md—regress_*naming,EXPLAIN (COSTS OFF)portability rule..claude/skills/review-checklist/SKILL.md— the test-coverage phase consults this skill..claude/commands/pg-test.md— slash-command wrapper that always prepends--suite setup.