diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4dbfdb7..ba9a555 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,14 +17,22 @@ jobs: # is what actually proves that works from a plain clone. run: make lint - # Fresh install, across the PG matrix. Every TEST_SCHEMA value (empty - - # no schema targeting at all - and 'Quoted', a name requiring SQL - # identifier quoting) is exercised too, via `make test-schema-all`'s - # in-Makefile loop rather than a CI matrix dimension - a schema name is + # Fresh install, then the in-place extension update path, both across the + # PG matrix. The update leg CREATE EXTENSIONs at the oldest version we + # still ship a full install script for (0.9.6), then ALTER EXTENSION + # UPDATEs to current (no pg_upgrade, same PostgreSQL) and reruns the + # suite - a single job rather than a dedicated one, since a load mode is # just an input the same assertions run against, not a real environment + # difference (same reasoning as TEST_SCHEMA below), and the per-version + # container/checkout setup would otherwise be duplicated across two jobs + # with the same PG matrix. Every TEST_SCHEMA value (empty - no schema + # targeting at all - and 'Quoted', a name requiring SQL identifier + # quoting) is exercised too, via `make test-schema-all`'s in-Makefile + # loop rather than a CI matrix dimension - a schema name is just an + # input the same assertions run against, not a real environment # difference, so crossing it into the matrix would only multiply job # count for no added confidence (see the Makefile's TEST_SCHEMA_VALUES - # comment). Both legs pass against the SAME + # comment). Every leg passes against the SAME # test/expected/extension_tests.out (see test/README.md for how the # suite keeps its output schema-invariant). test: @@ -41,6 +49,10 @@ jobs: uses: actions/checkout@v4 - name: Test on PostgreSQL ${{ matrix.pg }}, across every TEST_SCHEMA value run: make test-schema-all + - name: Install count_nulls + run: make install + - name: Update 0.9.6 -> current and run the suite + run: make verify-results TEST_LOAD_SOURCE=update pg-tle-test: strategy: diff --git a/Makefile b/Makefile index 7a0a979..1199bb6 100644 --- a/Makefile +++ b/Makefile @@ -42,10 +42,10 @@ TEST_SCHEMA ?= export PGOPTIONS := $(PGOPTIONS) -c count_nulls.test_schema=$(TEST_SCHEMA) # Every TEST_SCHEMA value the suite is tested against. A single source so -# test-schema-all and CI (once collapsed - see the "why not a CI matrix" -# note below) can't silently drift onto different sets. See the TEST_SCHEMA -# comment above for why exercising more than one value here is meaningful -# (search_path exclusion), not just "install into schema A vs schema B". +# test-schema-all/test-update-schema-all and CI can't silently drift onto +# different sets. See the TEST_SCHEMA comment above for why exercising more +# than one value here is meaningful (search_path exclusion), not just +# "install into schema A vs schema B". TEST_SCHEMA_VALUES = "" Quoted # TEST_SCHEMA is deliberately NOT a CI matrix dimension: unlike PostgreSQL @@ -69,3 +69,35 @@ test-schema-all: echo "=== TEST_SCHEMA=$$schema ==="; \ $(MAKE) test TEST_SCHEMA="$$schema" || exit 1; \ done + +# TEST_LOAD_SOURCE selects how test/install/load.sql installs count_nulls +# for the WHOLE test run: +# - fresh (default): CREATE EXTENSION count_nulls (current version). +# - update: CREATE EXTENSION at the oldest version we still ship a full +# install script for (0.9.6), then ALTER EXTENSION UPDATE to current - +# committed, since test/install runs outside any per-test rolled-back +# transaction (see pgxntool/README.asc's Update & Upgrade (U&U) Testing +# section for why the commit matters). +# - existing: count_nulls is already installed (a real `pg_upgrade` run, +# external to this invocation) - test/install only asserts it's present +# and current, it does not drop/create/update anything. Meant to be run +# with CONTRIB_TESTDB= EXTRA_REGRESS_OPTS=--use-existing against a +# real database, not via a make wrapper here. +# +# "update" (this) is extension-level (ALTER EXTENSION UPDATE); "upgrade" is +# cluster-level (pg_upgrade) - 'existing' is how that axis is exercised. +# +# Propagated the same way as TEST_SCHEMA: via the count_nulls.test_load_mode +# GUC, exported unconditionally through PGOPTIONS, read without missing_ok. +TEST_LOAD_SOURCE ?= fresh +ifeq ($(filter $(TEST_LOAD_SOURCE),fresh update existing),) +$(error TEST_LOAD_SOURCE must be 'fresh', 'update' or 'existing', 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=update`. +# Must recurse (a fresh $(MAKE)) rather than depend on `test`, so the +# parse-time TEST_LOAD_SOURCE conditional above re-evaluates with update set. +.PHONY: test-update +test-update: + $(MAKE) test TEST_LOAD_SOURCE=update diff --git a/test/install/load.sql b/test/install/load.sql index 37db853..41fa1a8 100644 --- a/test/install/load.sql +++ b/test/install/load.sql @@ -59,4 +59,62 @@ SELECT CASE WHEN :'count_nulls_has_schema' CREATE SCHEMA IF NOT EXISTS :"schema"; \endif +/* + * 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, committed (this file runs outside any + * per-test rolled-back transaction, unlike the old test/deps.sql approach - + * see pgxntool/README.asc's U&U section for why the commit matters); + * 'existing' asserts count_nulls is already installed (a real `pg_upgrade` + * run, external to this invocation) and touches nothing. + * + * Read without missing_ok, same reasoning as count_nulls.test_schema above. + */ +SELECT current_setting('count_nulls.test_load_mode') AS count_nulls_test_load_mode + , current_setting('count_nulls.test_load_mode') = 'update' AS count_nulls_update_mode + , current_setting('count_nulls.test_load_mode') = 'existing' AS count_nulls_existing_mode +\gset + +DO $$ +BEGIN + IF current_setting('count_nulls.test_load_mode') NOT IN ('fresh', 'update', 'existing') THEN + RAISE EXCEPTION + 'count_nulls.test_load_mode must be ''fresh'', ''update'' or ''existing'', got ''%''' + , current_setting('count_nulls.test_load_mode') + ; + END IF; +END +$$; + +\if :count_nulls_existing_mode +/* + * Already installed by something external to this pg_regress invocation + * (a real pg_upgrade run - see the pg-upgrade-test CI job). Only assert + * it's present and at the current version; do NOT drop/create/update it - + * the whole point of this mode is testing the REAL migrated objects. + */ +DO $$ +DECLARE + v_installed text := (SELECT extversion FROM pg_extension WHERE extname = 'count_nulls'); + v_default text := (SELECT default_version FROM pg_available_extensions WHERE name = 'count_nulls'); +BEGIN + IF v_installed IS NULL THEN + RAISE EXCEPTION 'count_nulls.test_load_mode=existing but count_nulls is not installed'; + END IF; + IF v_installed IS DISTINCT FROM v_default THEN + RAISE EXCEPTION 'count_nulls installed at % but default_version is %', v_installed, v_default; + END IF; +END +$$; +\elif :count_nulls_update_mode +CREATE EXTENSION count_nulls:with_schema_clause VERSION '0.9.6'; +/* + * Suppress the "already installed, no update" NOTICE class of messages any + * update script might emit. + */ +SET client_min_messages = WARNING; +ALTER EXTENSION count_nulls UPDATE; +SET client_min_messages = NOTICE; +\else CREATE EXTENSION count_nulls:with_schema_clause; +\endif