Add sydtest-sqitch-postgres and sydtest-sqitch-postgres-persistent ## Summary Two new sydtest companion packages: - **`sydtest-sqitch-postgres`** — sanity checks for a sqitch project against a temporary PostgreSQL database: - **Per-change round-trip**: deploy through a change, revert one step, redeploy. Schema must match. - **Per-change idempotence**: re-execute the deploy script's raw SQL on a database where the change has already been applied. Schema must be unchanged. - **Whole-plan deploy/revert/redeploy cycle**: deploy, snapshot, revert all, redeploy, snapshot, compare. - **`sydtest-sqitch-postgres-persistent`** — on top of `sydtest-sqitch-postgres`, asserts that the schema after deploying every sqitch migration matches what `persistent`'s automatic migration produces against an empty database (columns + indices). These replace the bespoke migration sanity-check code currently sitting in `NorfairKing/nix-ci#295`. ## API shape ```haskell sqitchPostgresqlSpec :: SqitchSettings -> TestDef outers a -> TestDef outers a sqitchPersistentPostgresqlSpec :: SqitchPersistentSettings -> TestDef outers a -> TestDef outers a ``` The combinators allocate fresh empty postgres databases internally (one per check), so nothing about the postgres machinery surfaces in the caller's outer-type stack. IO-level entry points are also exposed for negative tests under `expectFailing`. ## Design highlights - **Grandfather tag**: a single `Maybe Text` referring to a tag in `sqitch.plan`. Changes at or before the tag are exempt from the per-change round-trip + idempotence checks (legacy migrations that shipped before the checks existed). Changes after must satisfy both. - **Rework heads** (the post-tag head of a reworked change, with deploy target `name@HEAD`) skip the round-trip check, since sqitch's revert convention undoes the whole change rather than only the rework. The whole-plan cycle test still exercises these. - **`SchemaSnapshot`** record bundles columns + indices, compared via `compareSchemaSnapshots` with the undefined-trick to prompt for an update when new fields are added. - **Phase isolation**: `sqitchPersistentPostgresqlSpec`'s schema-equality check runs the persistent baseline (phase 1) on a separate database from the sqitch deploy (phase 2), so they can't contaminate each other. - **`Path Rel Dir`/`Path Abs File`** for sqitch project dir + executable; `path`/`path-io` used throughout. ## Companion change to `sydtest-persistent-postgresql` (0.3.0.0 → 0.4.0.0) The internal `adminDBSetupFunc`, `tempUserSetupFunc`, `tempNewDatabaseSetupFunc`, and `dbConnectionOptionsPoolSetupFunc` are renamed to `postgresqlServerSetupFunc`, `postgresqlUserSetupFunc`, `postgresqlDatabaseSetupFunc`, `postgresqlPoolSetupFunc` and promoted to the public API. New convenience `emptyPostgresOptionsSetupFunc` / `emptyPostgresPoolSetupFunc` give a fresh empty database without going through a migrated template. `sydtest-sqitch-postgres` consumes these. ## Test plan - [x] `stack test sydtest-sqitch-postgres sydtest-sqitch-postgres-persistent --pedantic` — 21/21 + 6/6 passing - [x] `stack test sydtest-persistent-postgresql --pedantic` — 5/5 still passing - [x] `nix flake check` — green (pre-commit, hlint, ormolu, cabal2nix, hpack, cross-nixpkgs release builds) - [x] Toy fixtures cover each failure mode in isolation: - `toy-sqitch-ok` (happy path) - `toy-sqitch-non-idempotent` - `toy-sqitch-broken-revert` - `toy-sqitch-grandfathered` (with and without the tag — round-trip and idempotence both skipped pre-tag) - `toy-sqitch-drift` (column mismatch between sqitch and persistent) - `toy-sqitch-index-drift` (columns match, indices don't) - [x] `readSqitchPlan` has 12 unit tests covering empty plan, single change, rework, double rework, grandfather tag at start/middle/end, missing tag, blank lines, on-disk fixture - [x] Replaces bespoke code in `NorfairKing/nix-ci#295` — local branch `use-sydtest-sqitch` confirms 6/6 Migration tests pass under the new API