From d2ff70eac95e1941a1c158b0334db145bf5907af Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Thu, 16 Jul 2026 16:49:25 -0500 Subject: [PATCH 1/2] Add extension update test that runs the full suite against an upgrade MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit test/build/upgrade.sql is a fast smoke check (via pgxntool 2.1.0's test-build feature): install 0.9.6 (oldest version we ship a full script for) and ALTER EXTENSION UPDATE, rolled back. That alone doesn't prove much — it never runs the actual test suite against the upgraded state. test/deps.sql now selects between installing fresh (current version) or via the upgrade path, based on a count_nulls.test_load_mode GUC the Makefile sets via PGOPTIONS (TEST_LOAD_SOURCE=fresh|upgrade). Since every test file loads deps.sql, `make test-update` reruns the whole existing suite (extension_tests, sanity, simple) against the upgraded install, comparing against the exact same expected/*.out as `make test` — proving the upgrade behaves identically to a fresh install rather than just completing without error. Modeled on cat_tools's test/install/load.sql approach (see their new_functions-pgxntool-2.1.0 branch), adapted to count_nulls's simpler single-file test/deps.sql structure instead of introducing a separate test/install fixture, since count_nulls's suite already installs the extension per test file (into different schemas) rather than once globally. sql/count_nulls--0.9.6.sql isn't picked up by base.mk's DATA wildcard (which only covers upgrade scripts and the current version file), so it's added to DATA explicitly. Filed as a pgxntool bug: Postgres-Extensions/pgxntool#48. Co-Authored-By: Claude Sonnet 5 --- Makefile | 34 +++++++++++++++++++++++++++++ test/build/expected/upgrade.out | 1 + test/build/upgrade.sql | 11 ++++++++++ test/deps.sql | 38 +++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+) create mode 100644 test/build/expected/upgrade.out create mode 100644 test/build/upgrade.sql diff --git a/Makefile b/Makefile index e57011a..48c4320 100644 --- a/Makefile +++ b/Makefile @@ -2,3 +2,37 @@ include pgxntool/base.mk # Temporary hack testdeps: $(wildcard test/*/*.sql) $(wildcard test/*.sql) # Be careful not to include directories in this + +# Install the oldest historical version's full install script so the upgrade +# test in test/build/upgrade.sql and test/deps.sql's upgrade mode can +# CREATE EXTENSION count_nulls VERSION '0.9.6'. +# Not covered by base.mk's DATA wildcard, which only picks up upgrade scripts +# (sql/*--*--*.sql) and the current version file. +DATA += sql/count_nulls--0.9.6.sql + +# TEST_LOAD_SOURCE selects how test/deps.sql installs the extension for the +# WHOLE test suite: +# - fresh (default): CREATE EXTENSION count_nulls (current version). +# - upgrade: CREATE EXTENSION at the oldest version we still ship a full +# install script for (0.9.6), then ALTER EXTENSION UPDATE to current. +# Running the SAME suite with the SAME expected output against the upgraded +# database proves it behaves identically to a fresh install. +# +# The mode is signalled to test/deps.sql via the count_nulls.test_load_mode +# placeholder GUC. pg_regress does not forward make variables, but the psql +# processes it spawns inherit the environment, so PGOPTIONS reaches deps.sql. +# It's exported unconditionally so deps.sql can read it without missing_ok +# and fail loudly if it didn't propagate, instead of silently defaulting to +# fresh and running the wrong suite. +TEST_LOAD_SOURCE ?= fresh +ifeq ($(filter $(TEST_LOAD_SOURCE),fresh upgrade),) +$(error TEST_LOAD_SOURCE must be 'fresh' or 'upgrade', got '$(TEST_LOAD_SOURCE)') +endif +export PGOPTIONS := $(PGOPTIONS) -c count_nulls.test_load_mode=$(TEST_LOAD_SOURCE) + +# Convenience wrapper: `make test-update` == `make test TEST_LOAD_SOURCE=upgrade`. +# Must recurse (a fresh $(MAKE)) rather than depend on `test`, so the +# parse-time TEST_LOAD_SOURCE conditional above re-evaluates with upgrade set. +.PHONY: test-update +test-update: + $(MAKE) test TEST_LOAD_SOURCE=upgrade diff --git a/test/build/expected/upgrade.out b/test/build/expected/upgrade.out new file mode 100644 index 0000000..25fdbb1 --- /dev/null +++ b/test/build/expected/upgrade.out @@ -0,0 +1 @@ +\set ECHO none diff --git a/test/build/upgrade.sql b/test/build/upgrade.sql new file mode 100644 index 0000000..c5a5946 --- /dev/null +++ b/test/build/upgrade.sql @@ -0,0 +1,11 @@ +\set ECHO none +\i test/pgxntool/psql.sql +\t + +-- Sanity check: install the oldest available version and upgrade to current. +-- sql/count_nulls--0.9.6.sql is the oldest full install script we still ship +-- (0.9.0/0.9.2/0.9.5 only exist as upgrade diffs, not standalone installs). +BEGIN; +CREATE EXTENSION count_nulls VERSION '0.9.6'; +ALTER EXTENSION count_nulls UPDATE; +ROLLBACK; diff --git a/test/deps.sql b/test/deps.sql index 9705f9f..4dbd4c1 100644 --- a/test/deps.sql +++ b/test/deps.sql @@ -5,4 +5,42 @@ CREATE SCHEMA IF NOT EXISTS :schema; SET search_path = :schema; SET client_min_messages = NOTICE; +/* + * Mode selection: 'fresh' installs the current version directly; 'upgrade' + * installs the oldest version we still ship a full script for (0.9.6) and + * runs ALTER EXTENSION UPDATE. Since every test file loads this via + * test/load.sql, running the suite in each mode (make test / make + * test-update) exercises the SAME tests against a fresh vs. an upgraded + * install, with the same expected output either way. + * + * The Makefile always exports this GUC, so we read it without missing_ok: + * if it failed to propagate we want a loud error here, not a silent + * fall-through to 'fresh'. + */ +SELECT current_setting('count_nulls.test_load_mode') AS count_nulls_test_load_mode +\gset + +DO $$ +BEGIN + IF current_setting('count_nulls.test_load_mode') NOT IN ('fresh', 'upgrade') THEN + RAISE EXCEPTION + 'count_nulls.test_load_mode must be ''fresh'' or ''upgrade'', got ''%''' + , current_setting('count_nulls.test_load_mode') + ; + END IF; +END +$$; + +SELECT :'count_nulls_test_load_mode' = 'upgrade' AS count_nulls_upgrade_mode +\gset + +\if :count_nulls_upgrade_mode +CREATE EXTENSION count_nulls VERSION '0.9.6'; +-- Suppress the "already installed, no update" NOTICE class of messages any +-- upgrade script might emit. +SET client_min_messages = WARNING; +ALTER EXTENSION count_nulls UPDATE; +SET client_min_messages = NOTICE; +\else CREATE EXTENSION count_nulls; +\endif From c7286c02abbb8dc0cabadb6a16b16e35e2c61602 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Thu, 16 Jul 2026 18:25:57 -0500 Subject: [PATCH 2/2] Rename test-load mode from 'upgrade' to 'update' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per the terminology convention: 'update' is extension-level (ALTER EXTENSION UPDATE), 'upgrade' is cluster-level (pg_upgrade) — a separate, not-yet-covered axis. The wrapper target was already called test-update; the mode value and GUC name were the odd one out. Co-Authored-By: Claude Sonnet 5 --- Makefile | 22 +++++++++++++--------- test/deps.sql | 17 ++++++++++------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index 48c4320..98b9c3f 100644 --- a/Makefile +++ b/Makefile @@ -3,8 +3,8 @@ include pgxntool/base.mk # Temporary hack testdeps: $(wildcard test/*/*.sql) $(wildcard test/*.sql) # Be careful not to include directories in this -# Install the oldest historical version's full install script so the upgrade -# test in test/build/upgrade.sql and test/deps.sql's upgrade mode can +# Install the oldest historical version's full install script so the update +# test in test/build/upgrade.sql and test/deps.sql's update mode can # CREATE EXTENSION count_nulls VERSION '0.9.6'. # Not covered by base.mk's DATA wildcard, which only picks up upgrade scripts # (sql/*--*--*.sql) and the current version file. @@ -13,11 +13,15 @@ DATA += sql/count_nulls--0.9.6.sql # TEST_LOAD_SOURCE selects how test/deps.sql installs the extension for the # WHOLE test suite: # - fresh (default): CREATE EXTENSION count_nulls (current version). -# - upgrade: CREATE EXTENSION at the oldest version we still ship a full +# - update: CREATE EXTENSION at the oldest version we still ship a full # install script for (0.9.6), then ALTER EXTENSION UPDATE to current. -# Running the SAME suite with the SAME expected output against the upgraded +# Running the SAME suite with the SAME expected output against the updated # database proves it behaves identically to a fresh install. # +# "update" (this) is extension-level (ALTER EXTENSION UPDATE); "upgrade" is +# cluster-level (pg_upgrade) and is a separate, not-yet-implemented axis. Don't +# conflate the two in variable names or comments. +# # The mode is signalled to test/deps.sql via the count_nulls.test_load_mode # placeholder GUC. pg_regress does not forward make variables, but the psql # processes it spawns inherit the environment, so PGOPTIONS reaches deps.sql. @@ -25,14 +29,14 @@ DATA += sql/count_nulls--0.9.6.sql # and fail loudly if it didn't propagate, instead of silently defaulting to # fresh and running the wrong suite. TEST_LOAD_SOURCE ?= fresh -ifeq ($(filter $(TEST_LOAD_SOURCE),fresh upgrade),) -$(error TEST_LOAD_SOURCE must be 'fresh' or 'upgrade', got '$(TEST_LOAD_SOURCE)') +ifeq ($(filter $(TEST_LOAD_SOURCE),fresh update),) +$(error TEST_LOAD_SOURCE must be 'fresh' or 'update', got '$(TEST_LOAD_SOURCE)') endif export PGOPTIONS := $(PGOPTIONS) -c count_nulls.test_load_mode=$(TEST_LOAD_SOURCE) -# Convenience wrapper: `make test-update` == `make test TEST_LOAD_SOURCE=upgrade`. +# Convenience wrapper: `make test-update` == `make test TEST_LOAD_SOURCE=update`. # Must recurse (a fresh $(MAKE)) rather than depend on `test`, so the -# parse-time TEST_LOAD_SOURCE conditional above re-evaluates with upgrade set. +# parse-time TEST_LOAD_SOURCE conditional above re-evaluates with update set. .PHONY: test-update test-update: - $(MAKE) test TEST_LOAD_SOURCE=upgrade + $(MAKE) test TEST_LOAD_SOURCE=update diff --git a/test/deps.sql b/test/deps.sql index 4dbd4c1..454be4b 100644 --- a/test/deps.sql +++ b/test/deps.sql @@ -6,13 +6,16 @@ SET search_path = :schema; SET client_min_messages = NOTICE; /* - * Mode selection: 'fresh' installs the current version directly; 'upgrade' + * Mode selection: 'fresh' installs the current version directly; 'update' * installs the oldest version we still ship a full script for (0.9.6) and * runs ALTER EXTENSION UPDATE. Since every test file loads this via * test/load.sql, running the suite in each mode (make test / make - * test-update) exercises the SAME tests against a fresh vs. an upgraded + * test-update) exercises the SAME tests against a fresh vs. an updated * install, with the same expected output either way. * + * "update" here is extension-level (ALTER EXTENSION UPDATE); it is not + * "upgrade" (cluster-level pg_upgrade), a separate axis this doesn't cover. + * * The Makefile always exports this GUC, so we read it without missing_ok: * if it failed to propagate we want a loud error here, not a silent * fall-through to 'fresh'. @@ -22,22 +25,22 @@ SELECT current_setting('count_nulls.test_load_mode') AS count_nulls_test_load_mo DO $$ BEGIN - IF current_setting('count_nulls.test_load_mode') NOT IN ('fresh', 'upgrade') THEN + IF current_setting('count_nulls.test_load_mode') NOT IN ('fresh', 'update') THEN RAISE EXCEPTION - 'count_nulls.test_load_mode must be ''fresh'' or ''upgrade'', got ''%''' + 'count_nulls.test_load_mode must be ''fresh'' or ''update'', got ''%''' , current_setting('count_nulls.test_load_mode') ; END IF; END $$; -SELECT :'count_nulls_test_load_mode' = 'upgrade' AS count_nulls_upgrade_mode +SELECT :'count_nulls_test_load_mode' = 'update' AS count_nulls_update_mode \gset -\if :count_nulls_upgrade_mode +\if :count_nulls_update_mode CREATE EXTENSION count_nulls VERSION '0.9.6'; -- Suppress the "already installed, no update" NOTICE class of messages any --- upgrade script might emit. +-- update script might emit. SET client_min_messages = WARNING; ALTER EXTENSION count_nulls UPDATE; SET client_min_messages = NOTICE;