Add update+upgrade (U&U) test foundation: test/install, load-mode switch, TEST_SCHEMA, dependency guard - #3
Conversation
…tch, TEST_SCHEMA, dependency guard, and the missing 0.1.0->0.2.0 update script Modeled on cat_tools PR Postgres-Extensions#16/#46's test/install/load.sql pattern (fresh / update / existing load modes via a test_load_mode GUC propagated through PGOPTIONS), adapted to object_reference's own conventions rather than copied wholesale: - test/install/load.sql: committed-once installer, replacing the per-test CREATE EXTENSION that used to live in test/load.sql. No test/roles.sql: object_reference's own tests never create actor roles to test permission boundaries (grepped test/sql and test/helpers to confirm), so there's nothing to centralize. - TEST_LOAD_SOURCE/TEST_UPDATE_FROM/TEST_UPDATE_TO GUCs + a test-update wrapper target, same propagation mechanism as cat_tools. - TEST_SCHEMA (doc's own guidance, not something cat_tools implements yet): a second, independent GUC for where the test session's ambient search_path points, read via test/schema.sql and applied both at install time and per-test (test/deps.sql), since search_path is session-local. - sql/object_reference--0.1.0--0.2.0.sql: object_reference had no update path from 0.1.0 at all; this backfills one, converging the reg*-pseudotype removal and object__cleanup trigger forward from the frozen 0.1.0/0.2.0 sources (never edited directly). - test/guard.sql: a dependency-guard view anchored on _object_reference.object.object_id, proven (manually) to block a non-CASCADE DROP EXTENSION. Not wired into `make test` -- it's for a follow-up CI PR's existing-mode flow. Fixed along the way (surfaced by actually running fresh/update/existing locally, not just reviewing the diff): - zzz_build.sql's raw-source-load sanity check collided with the now- persistent object_reference schema/extension; it drops its own session-local copy first (transaction is never committed). - test/dump/load_all.sql exercises a separate, non-pg_regress database and needs its own explicit CREATE EXTENSION now that test/load.sql itself no longer installs one. - test/install/load.sql needs \i test/pgxntool/psql.sql for ON_ERROR_STOP: its expected-output path collapses to a self- comparison (../install/load resolves to the same file under both --inputdir/expected and --outputdir/results), so only a non-zero psql exit code -- not a content diff -- can actually fail it. - the update script must disable the zzz__object_reference_drop event trigger for its own duration; _etg_drop() queries the very view the script drops and recreates. Verified locally on PG12 and PG17: fresh, update (test-update), and existing (against both a fresh install and deliberately broken states) all behave correctly, and the TEST_SCHEMA switch was exercised with a mixed-case value to confirm quoting works. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
CI status note: the current CI run fails, but this is a pre-existing, external dependency issue predating this branch, not something introduced here. Root cause: Confirmed this predates this branch: the most recent CI run on the unmodified This is out of scope for this PR (no Locally (which uses a locally-built/newer cat_tools, 0.3.0, rather than the PGXN-published 0.2.1), the exact same code passes cleanly in |
|
Superseded by upstream Postgres-Extensions#18 (u-and-u-testing), which implements the same U&U test foundation against the current stable-version model. Postgres-Extensions#18's own description already flags this PR as superseded (built against the outdated 0.2.0/reg*-pseudotype model, and includes a TEST_SCHEMA dimension that test-fixes.md's item 9 identifies as a mistake for this schema-pinned extension). Closing without merging. |
Summary
Foundation-only slice of the update+upgrade (U&U) testing pattern (§1-§4, §7,
checklist items 1-6/9/10 of the internal design doc this implements). A
follow-up PR will build the CI job (
bin/test_existing-style script,ci.ymlmatrix) on top of this branch.make test,make test TEST_LOAD_SOURCE=update(ormake test-update), andmake test TEST_LOAD_SOURCE=existing(paired withCONTRIB_TESTDB=<db> EXTRA_REGRESS_OPTS=--use-existing PGXNTOOL_ENABLE_TEST_BUILD=no) all now workcorrectly by hand -- verified locally on both PG12 and PG17, including
deliberately-broken
existing-mode scenarios (missing extension,version-mismatched extension) to confirm the assert actually fails loudly
rather than silently passing.
Base branch note: this targets
new_featureson this fork (notupstream
masterdirectly) because upstream (Postgres-Extensions/object_reference)doesn't have a
new_featuresbranch at all -- it only exists here, feedinginto upstream via the already-open
Postgres-Extensions/object_reference#2.This PR stacks on top of that.
What this does
PGXNTOOL_ENABLE_TEST_INSTALL = yes+test/install/load.sql: asingle, committed-once installer (runs once, before the whole pgTAP suite,
in pgxntool's own
test/installschedule) replacing the per-testCREATE EXTENSION object_reference CASCADEthat used to live intest/load.sql. Three modes selected by anobject_reference.test_load_modeGUC (
fresh/update/existing), matching cat_tools PR Add SQL style linter (vendored Postgres-Extensions/linter) Postgres-Extensions/object_reference#16/#46's pattern.TEST_LOAD_SOURCE/TEST_UPDATE_FROM/TEST_UPDATE_TOmake vars,parse-time validated, exported unconditionally via
PGOPTIONS, plus amake test-updateconvenience wrapper.TEST_SCHEMA: a second, independent GUC for where the test session'sambient
search_pathtargets (not how the extension got installed, butwhere). Read once via
test/schema.sql, applied at install time(
test/install/load.sql) and per-test (test/deps.sql, sincesearch_pathis session-local and doesn't carry over from the committedinstall session). Exercised locally with a mixed-case value
(
MixedCaseSchema) to confirm identifier quoting works correctlyend-to-end.
sql/object_reference--0.1.0--0.2.0.sql: object_reference had noupdate path from 0.1.0 at all (only full install scripts existed), so
ALTER EXTENSION UPDATEfrom 0.1.0 would have failed outright. Thisbackfills a real, hand-authored script covering both actual deltas between
the two frozen versions:
reg*pseudotype columns from_object_reference._object_oidin favor of a single plain
object_oid oid NOT NULLcolumn (withclassidalso changed fromregclasstooid) -- requires droppingand recreating
_object_v/_object_v__for_update(can'tCREATE OR REPLACE VIEWa column removal), backfillingobject_oidfromobjid(safe because the old
objid_must_matchCHECK already guaranteesthey're equal), and dropping the now-pointless
null_counttrigger.object_reference.object__cleanup()function and theobject_group__object__cleanuptrigger that fires it.sql/object_reference--0.1.0.sqlandsql/object_reference--0.2.0.sql(the frozen versioned files) were not touched, per this repo's own
convention -- only new files were added.
TEST_UPDATE_FROMdefaults to0.1.0, the only older released versionthat exists right now, so there's no multi-origin duplicity to worry
about yet.
default_versioninobject_reference.controlis untouched (still0.2.0) -- this PR backfills a missing historical path, it doesn't shipa new version.
test/guard.sql, not wired intomake test): a viewin a new
object_reference_drop_guardschema hard-depending on_object_reference.object.object_id-- the extension's own surrogate-keyidentity registry, which nothing in its update path has ever dropped or
renamed. Manually verified: a non-
CASCADEDROP EXTENSIONfails withthis guard planted, and
CASCADEremoves it cleanly along with theextension. Left for the follow-up CI PR to actually plant+prove
automatically as part of an
existing-mode flow.What I took verbatim / adapted / skipped from cat_tools PR Postgres-Extensions#16/#46
test_load_modeGUCdispatch structure in
test/install/load.sql(fresh/update/existing\ifblocks, the existing-modeDOblock assertingextversion/default_version), theTEST_LOAD_SOURCEMakefile block(parse-time validation, unconditional
PGOPTIONSexport, thetest-updatewrapper), and the dependency-guard technique (a viewhard-depending on a stable member).
pg_temp.drop_role()/pg_temp.create_role()helpers because its 0.2.2 install script uses a bare, non-idempotent
CREATE ROLE. object_reference's own role creation (both 0.1.0 and 0.2.0)already wraps
CREATE ROLEin aDO/EXCEPTION WHEN duplicate_objectblock, so none of that machinery is needed here -- plain
DROP EXTENSION ... CASCADEis sufficient before a fresh/updatere-install.
test/roles.sql. I greppedtest/sql/*.sqlandtest/helpers/*.sqlfor role/grant usage first: object_reference's owninstall creates
object_reference__usage/object_reference__dependencyroles, and existing tests assert privileges on those roles
(
schema_privs_are(..., 'object_reference__dependency', ...)), but nothingcreates or uses a test-actor role the way cat_tools's suite does to probe
permission boundaries. Inventing that just to justify a
roles.sqlfilewould be scope creep beyond test-infrastructure work, so I skipped it.
Also skipped: the bridge-update machinery (cat_tools-specific technical
debt for its own old unsafe releases -- doesn't apply, object_reference has
no known
pg_upgrade-unsafe historical version) and testing multipleupdate origins (only one exists right now).
TEST_SCHEMA-- cat_tools's repo has zerohits for
test_schema/TEST_SCHEMA, so this is doc guidance implementedahead of the reference implementation, worth watching for
convergence/divergence.
Bugs found (and fixed) only by actually running this, not just reviewing the diff
test/sql/*.sql-style glob accidentally opens a nested comment(
l/*reads as a comment-start), and a stray nested*/inside aclarifying comment (
reg*/object_oid) closes one early -- both producedreal syntax errors that only surfaced when the affected file actually ran.
DROP VIEW _object_reference._object_vneedsCASCADE: two functions declareRETURNS _object_reference._object_v(including, confusingly, a function of the same name as the other view,
_object_reference._object_v__for_update), which only showed up as a real"other objects depend on it" error when run through actual
ALTER EXTENSION UPDATE, not a plainpsql -f(which additionally isn'ta valid way to test this at all -- extension-member objects can only be
dropped inside real update-script execution context).
object_reference's ownzzz__object_reference_dropevent trigger (fires on everysql_drop,including the update script's own
DROP VIEW), which queries the exactview being dropped -- needed an explicit
ALTER EVENT TRIGGER ... DISABLE/
ENABLEaround the update script.test/install/load.sqlneeded\i test/pgxntool/psql.sqlforON_ERROR_STOP: pg_regress resolves the../install/loadscheduleentry's expected-output path to the same file under both
--inputdir/expectedand--outputdir/results, making content-diffing ano-op self-comparison that can never fail on its own -- confirmed by
deliberately breaking
existing-mode assertions (missing extension,wrong version) and observing they were silently swallowed until this fix.
test/installbroke two things that assumed a freshCREATE EXTENSIONper test (the old, pre-test/installbehavior):zzz_build.sql(raw-source-load sanity check, now needs to drop its own session-local
copy of the persistent schema first) and
test/dump/load_all.sql(aseparate, non-pg_regress database that now needs its own explicit
CREATE EXTENSION, sincetest/load.sqlno longer installs one for it).Testing
Ran locally (both PG12 and PG17):
make test(fresh) -- all 8 passmake test TEST_LOAD_SOURCE=update/make test-update-- all 8 pass,identical expected output to fresh (proves update ≡ fresh)
make test TEST_LOAD_SOURCE=existing CONTRIB_TESTDB=<db> EXTRA_REGRESS_OPTS=--use-existing PGXNTOOL_ENABLE_TEST_BUILD=noagainst agenuinely fresh install -- all 8 pass; against a missing or
version-mismatched install -- fails loudly and correctly, as designed
make test TEST_SCHEMA=MixedCaseSchema-- confirmed the schema isactually created and targeted, with only the expected extra
"already exists" NOTICE as a diff
test/guard.sql): non-CASCADEDROP EXTENSIONfails,
CASCADEsucceeds and removes the guard🤖 Generated with Claude Code