Add test/install foundation for update+upgrade testing (fresh/update/existing) - #34
Open
jnasbyupgrade wants to merge 3 commits into
Open
Add test/install foundation for update+upgrade testing (fresh/update/existing)#34jnasbyupgrade wants to merge 3 commits into
jnasbyupgrade wants to merge 3 commits into
Conversation
…existing) Implements the load-mode switch, dependency guard, and single-source-of-truth role name from the advanced update+upgrade testing pattern (modeled on Postgres-Extensions/cat_tools PR #16 and what has landed on its master since), scoped to what test_factory can actually exercise today: - test/install/load.sql (pgxntool's PGXNTOOL_ENABLE_TEST_INSTALL) now owns getting the extension to its target state via TEST_LOAD_SOURCE= fresh|update|existing, propagated as GUCs (test_factory.test_load_mode etc), validated at both make-parse-time and read time. - test/helpers/create_extension.sql skips CREATE EXTENSION when load.sql already installed it (update/existing), while keeping fresh mode's original no-IF-NOT-EXISTS behavior (hard error on stale state) unchanged. - test/sql/install.sql and the install-ordering half of test/sql/pgtap.sql are skipped under existing mode: their own non-CASCADE DROP EXTENSION is a deliberate fresh-mode-only test that would otherwise trip the guard. - Dependency guard (existing mode only): a view depending on tf.tap(text,text) blocks a stray non-CASCADE DROP EXTENSION test_factory_pgtap; test_factory itself already has a natural guard for free via test_factory_pgtap's own `requires` clause, which load.sql also proves still holds. - test/roles.sql is now the single source of truth for the test_role name, \i'd via test/helpers/deps.sql and test/install/load.sql. - Alternate expected output (test/expected/{base,install,pgtap}_1.out) for the existing-mode leg, since it legitimately produces different (but equally valid) output -- generated from real `existing`-mode runs, no raw "not ok" TAP lines in either leg. Verified against both PG12 and PG17: fresh (default), TEST_LOAD_SOURCE=update (currently a no-op -- see comment in load.sql for why no CI job drives it yet), and TEST_LOAD_SOURCE=existing against a real pre-populated database (the make test ... --use-existing recipe). Fresh mode's expected output is byte-for-byte unchanged from before this change. Skipped/deferred (see PR description): TEST_SCHEMA (test_factory is non-relocatable with hardcoded schema names, so the ambient-search_path failure mode it protects against can't occur here); an update-path CI job (no second version has ever shipped); the bridge-update/multi-origin machinery from PR #16 (cat_tools-specific technical debt, not applicable). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Several review comments added by the test/install foundation work used consecutive -- lines for what was really one continuous remark. This repo's convention (see the pre-existing test/helpers/create.sql and test/sql/install.sql) is to use C-style /* */ blocks for any comment spanning more than one line, reserving -- for single-line remarks. While converting test/roles.sql, reworded "test/sql/*.sql" to "*.sql files under test/sql/" -- the original phrasing contained a literal /* immediately after test/sql, which Postgres's nesting-aware block comment parser reads as an unwanted nested comment opener, leaving the enclosing comment unterminated.
… crash test/CLAUDE.md still referenced test/sql/install.sql (deleted by add-test-build, now this branch's base) in the existing-mode section and the alternate-expected-file list; updated both to reflect that packaging/dependency-declaration checks now live in test/build/install.sql instead. Also fixes a real, pre-existing bug in test/sql/pgtap.sql surfaced by testing TEST_LOAD_SOURCE=update locally (not just the fresh/existing legs the rebase itself required): test/install/load.sql's update mode installs test_factory only, never test_factory_pgtap. pgtap.sql's non-existing-mode branch calls test/helpers/create_extension.sql for test_factory (a no-op when already installed, per its own already_installed check) and then unconditionally DROP TABLEs pre_install_role/post_install_role -- which that no-op never created, so the DROP errored under update mode. Guarded the same way test/helpers/create.sql already guards its own read of those tables (to_regclass(...) IS NOT NULL), rather than inventing a new pattern.
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rebase note
Rebased onto
add-test-build(this stack is nowsync-pgxntool-2.3.0->add-test-build-> this PR -> #23; coordinator is handlinggh stack linkseparately).add-test-build(#26) deletedtest/sql/install.sqland trimmed the leading dependency-check block fromtest/sql/pgtap.sql(moved totest/build/install.sql); this PR's own mode-gating touched both of those same files, so real conflicts had to be resolved with judgment rather than a mechanical merge:test/sql/install.sqlno longer exists after Add test/build: extension-metadata tests + raw SQL syntax checks #26, so this PR's mode-gating commit for that file is now a no-op -- dropped rather than resurrecting the file. The alternate expected filetest/expected/install_1.outis correspondingly removed too (nothing left for it to be an alternate of).test/sql/pgtap.sql: kept Add test/build: extension-metadata tests + raw SQL syntax checks #26's trim (no dependency-check block, noSET search_path = tap) and re-derived the\if :is_existinggating around it -- the\else(fresh/update) branch is Add test/build: extension-metadata tests + raw SQL syntax checks #26's already-trimmed content; the existing-mode branch is unchanged (it never had the dependency-check block to begin with).test/CLAUDE.mdupdated to stop referencing the now-deletedtest/sql/install.sqland the now-three-file-turned-two-file alternate-expected-output list.A real, pre-existing bug found and fixed during this rebase's own local verification (not introduced by the rebase itself -- present in this PR's original design regardless):
TEST_LOAD_SOURCE=updatecrashed intest/sql/pgtap.sql.test/install/load.sql's update mode installstest_factoryonly, nevertest_factory_pgtap;pgtap.sql's non-existing-mode branch callstest/helpers/create_extension.sqlfortest_factory(a no-op since it's already installed) and then unconditionallyDROP TABLEspre_install_role/post_install_role-- which that no-op never created. Guarded the same waytest/helpers/create.sqlalready guards its own read of those tables (to_regclass(...) IS NOT NULL), rather than inventing a new pattern. This wasn't caught before because no CI job drivesupdatemode yet (per this PR's own "what's skipped" section below) -- found by testing it locally anyway while verifying the rebase.Re-verified all three modes locally after resolving conflicts: fresh (default),
update(now doesn't crash), andexistingagainst a real pre-populated database (tapschema + pgtap installed first, thentest_factory_pgtap CASCADE) -- zero rawnot okTAP lines in the fresh and existing legs, output byte-identical to the pre-recorded_1.outalternates.Summary
First of a small stack implementing the advanced update+upgrade (U&U)
testing pattern for this repo, modeled on
Postgres-Extensions/cat_toolsPR #16 and what has landed on its
mastersince (PR #44 test-harnessoverhaul, PR #46 update-path convergence, PR #48 PG-major single source of
truth). This PR is the foundation piece: the
test/install/load.sqlload-mode switch, the dependency guard, and single-sourcing the test role
name. A follow-up PR will restructure CI on top of this (docs-only gate,
PG-major single source of truth,
all-checks-passed, thepush/pull_requestdouble-trigger fix, and a binarypg_upgradejob).Now stacked on
add-test-build(#26) ->sync-pgxntool-2.3.0, in turn built on currentmaster(pgxntool 2.3.0, which is what providesPGXNTOOL_ENABLE_TEST_INSTALL/test/install/in the first place).What this adds
TEST_LOAD_SOURCE=fresh|update|existing(Makefile), propagated astest_factory.test_load_mode/test_update_from/test_update_toGUCs,validated at both
make-parse-time ($(error ...)) and read time (nomissing_ok, reject unknown values).test/install/load.sql: pgxntool'sPGXNTOOL_ENABLE_TEST_INSTALLfeature runs this once, committed, before the regular test files, so its
state survives into every one of them.
fresh(default) leavestest/sql/*.sqlto install the extension themselves exactly as before;updatedoesCREATE EXTENSION VERSION :from+ALTER EXTENSION UPDATE;existingonly asserts the extensions are present at the currentversion and never drops/creates.
test/helpers/create_extension.sqlnow skipsCREATE EXTENSIONwhenload.sqlalready installed it (update/existing), while fresh mode keepsits original behavior unchanged (no
IF NOT EXISTS, so a stale/unexpectedinstall still errors loudly instead of silently testing wrong state).
tf.tap(text,text)blocks a stray non-CASCADEDROP EXTENSION test_factory_pgtap.test_factoryitself doesn't need an artificialguard --
test_factory_pgtap's own control file (requires = 'pgtap, test_factory') already blocks a non-CASCADEDROP EXTENSION test_factoryas long as
test_factory_pgtapis installed, andload.sqlproves thatnatural protection still holds too. This matters because the
install-ordering half of
pgtap.sqlis exactly the kind of thing theguard exists to catch if it ran under existing mode -- so it's skipped
under
test_load_mode=existinginstead. (Packaging/dependency-declarationchecks that used to additionally live in
test/sql/install.sqlweremoved to
test/build/install.sqlby Add test/build: extension-metadata tests + raw SQL syntax checks #26, stacked below this PR.)test/roles.sql: single source of truth for thetest_rolename,\i'd viatest/helpers/deps.sql(test files) andload.sql(which nowowns creating the role idempotently, once, instead of each test file
doing its own unguarded
CREATE ROLE).(
test/expected/{base,pgtap}_1.out) for theexisting-mode leg,which legitimately produces different-but-correct output (skipped
sections, a skipped role-restore check) -- pg_regress's native numbered
alternate-file convention, generated from real
existing-mode runs.Verification
Ran on both PG12 and PG17:
make test(fresh, default) -- unchanged, byte-identical expected output.make test TEST_LOAD_SOURCE=update-- doesn't error (currently a no-op;see below).
make test TEST_LOAD_SOURCE=existing CONTRIB_TESTDB=<db> EXTRA_REGRESS_OPTS=--use-existing PGXNTOOL_ENABLE_TEST_BUILD=noagainst areal pre-populated database (extension installed,
tapschema set upfirst) -- passes cleanly with zero raw
not okTAP lines in either leg.make check-stale-expected-- clean (it already recognizes pg_regress's_N.outalternate-file convention).What's skipped, and why (per the doc's "ultimate goal" -- this record is
meant to help decide what's generic enough for pgxntool itself)
TEST_LOAD_SOURCE=updateyet. test_factory has onlyever shipped one version (0.5.0), so there's no historical update script
to exercise --
CREATE EXTENSION VERSION '0.5.0'+ALTER EXTENSION UPDATEis a no-op today and wouldn't prove anything fresh-mode CIdoesn't already cover. The mechanism is built now (per the doc's
checklist items 3-5) so it's ready the moment a second version ships.
TEST_SCHEMA(doc §3a) is not implemented. test_factory isnon-relocatable with hardcoded internal schema names (
tf,_tf,_test_factory_test_data-- seesql/test_factory.sql), notsearch_path-relative. The ambient-search_path failure mode
TEST_SCHEMAexists to catch (code accidentally assuming
public) structurally cannotoccur here. This is a genuine divergence, not an oversight.
That's cat_tools-specific technical debt (recovering from old
pg_upgrade-unsafe releases cut before this testing existed); nothingtest_factory has shipped needs it.
pg-upgrade-stepwise(every-major climb) and pg_tle testing are leftfor a possible follow-up, not this PR -- no evidence test_factory
targets pg_tle/RDS deployment, and test_factory has no catalog-touching
views (checked
sql/test_factory--0.5.0.sql) so the per-major-boundaryrisk
pg-upgrade-stepwiseprotects against is low.scope for an extension-level PR):
test/install/*.sql's actual output andits "expected" comparison target resolve to the same physical path
(
test/install/<name>.out, since--inputdir'sexpected/../install/and
--outputdir'sresults/../install/both collapse toinstall/).In practice this means pg_regress can never detect a real regression in a
test/install/*.sqlfile via output diffing -- it always overwrites and"passes". Worth a hard failure via
RAISE EXCEPTION-style assertionsinstead of relying on diffed output for anything that must actually catch
a regression (what
load.sqldoes throughout this PR).Convergence with cat_tools PR #16
propagation mechanism (
TEST_LOAD_SOURCE-> GUC ->current_setting(),no
missing_ok), the dependency-guard technique (plant a stable-memberview, prove drop is blocked, re-prove after every step), and "factor the
install->guard->assert flow into the install script itself" instead of
inline duplication.
type grown via
ADD VALUE; test_factory has no enum, so it targetstf.tap(text,text)instead (and gets test_factory's own protection forfree via
test_factory_pgtap'srequiresclause -- cat_tools has noequivalent second extension, so it needed an explicit guard for
everything).
Recreated from #22 (fork-headed) as an upstream-branch PR so it can be part of a formal GitHub stack (
gh stack link), which refuses fork PRs. Same content/commits, same CI results.