-
Notifications
You must be signed in to change notification settings - Fork 2
Add test/install foundation for update+upgrade testing (fresh/update/existing) #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,10 +20,61 @@ The test_factory extension uses **pgTAP** (PostgreSQL's unit testing framework) | |
| ### Test Helpers | ||
| - `test/helpers/setup.sql` - Test environment initialization and pgTAP setup | ||
| - `test/helpers/create.sql` - Test data registration and security validation | ||
| - `test/helpers/create_extension.sql` - Extension creation wrapper | ||
| - `test/helpers/deps.sql` - Test dependency management | ||
| - `test/helpers/create_extension.sql` - Extension creation wrapper; skips | ||
| `CREATE EXTENSION` when `test/install/load.sql` already installed it | ||
| (`test_load_mode` is not `fresh`) | ||
| - `test/helpers/deps.sql` - Test dependency management (`\i`'s `test/roles.sql`) | ||
| - `test/roles.sql` - Single source of truth for test-only role names | ||
| - Other helper files for role management and pgTAP integration | ||
|
|
||
| ## Load Modes (`TEST_LOAD_SOURCE`) | ||
|
|
||
| `test/install/load.sql` runs once, committed, before the regular test files | ||
| (pgxntool's `PGXNTOOL_ENABLE_TEST_INSTALL` feature), so its state survives | ||
| into every test file. `TEST_LOAD_SOURCE` (default `fresh`) picks how the | ||
| extension gets to its target state: | ||
|
|
||
| - **fresh** (default) - `load.sql` does nothing extra; `test/sql/*.sql` | ||
| install the extension themselves, exactly as before this feature existed. | ||
| - **update** - `load.sql` does `CREATE EXTENSION test_factory VERSION | ||
| :from` then `ALTER EXTENSION UPDATE` (`TEST_UPDATE_FROM`/`TEST_UPDATE_TO` | ||
| make vars). test_factory has only ever shipped one version (0.5.0), so | ||
| this is a no-op today -- the mechanism exists for when a second version | ||
| ships, but no CI job drives it yet. `make test-update` is a shorthand for | ||
| `make test TEST_LOAD_SOURCE=update`. | ||
| - **existing** - the extension is already installed (a real `pg_upgrade` | ||
| target, or an out-of-band update) -- `load.sql` only asserts it's present | ||
| at the current version, plants a dependency guard (see below), and | ||
| proves it. `test/sql/install.sql` and the install/dependency-order part of | ||
| `test/sql/pgtap.sql` are skipped in this mode (see the `\if :is_existing` | ||
| branches in those files) since they'd otherwise defeat the guard by doing | ||
| their own from-scratch drop/recreate. | ||
|
|
||
| Run against a real pre-existing install with: | ||
| ``` | ||
| make test TEST_LOAD_SOURCE=existing CONTRIB_TESTDB=<db> \ | ||
| EXTRA_REGRESS_OPTS=--use-existing PGXNTOOL_ENABLE_TEST_BUILD=no | ||
| ``` | ||
|
Comment on lines
+53
to
+57
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Make the command example lint-clean. Add blank lines around the fence and declare the shell language. Proposed fix Run against a real pre-existing install with:
- ```
+
+ ```bash
make test TEST_LOAD_SOURCE=existing CONTRIB_TESTDB=<db> \
EXTRA_REGRESS_OPTS=--use-existing PGXNTOOL_ENABLE_TEST_BUILD=no🧰 Tools🪛 markdownlint-cli2 (0.23.1)[warning] 54-54: Fenced code blocks should be surrounded by blank lines (MD031, blanks-around-fences) [warning] 54-54: Fenced code blocks should have a language specified (MD040, fenced-code-language) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
|
|
||
| `existing` mode legitimately produces different (but equally valid) | ||
| output than `fresh`/`update` for `base`/`install`/`pgtap` (skipped | ||
| sections, a skipped role-restore check), so it has alternate expected | ||
| files: `test/expected/{base,install,pgtap}_1.out` (pg_regress's numbered | ||
| alternate-expected-file convention). | ||
|
|
||
| ### Dependency Guard | ||
|
|
||
| Planted only in `existing` mode (see `load.sql`): a view in schema | ||
| `test_factory_drop_guard` depending on `tf.tap(text,text)` blocks a | ||
| non-CASCADE `DROP EXTENSION test_factory_pgtap`. `test_factory` itself | ||
| doesn't need an artificial guard -- `test_factory_pgtap`'s own control file | ||
| (`requires = 'pgtap, test_factory'`) already blocks a non-CASCADE | ||
| `DROP EXTENSION test_factory` as long as `test_factory_pgtap` is installed; | ||
| `load.sql` proves that natural protection too. The point of the guard: in | ||
| `existing` mode, nothing else stops a stray drop (or a logic bug that falls | ||
| through to the fresh/update branch) from silently destroying the real | ||
| upgraded/updated objects this mode exists to test. | ||
|
|
||
| ## Test Coverage Analysis | ||
|
|
||
| ### Core Functionality Tests (`base.sql`) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| \set ECHO none | ||
| Creating extension test_factory | ||
| test_factory already installed -- skipping CREATE EXTENSION (test_load_mode is not fresh) | ||
| ok 1 - Register test customers | ||
| ok 2 - Create function customer__add | ||
| ok 3 - Register test invoices | ||
| ok 4 - Ensure original_role temp table was dropped | ||
| ok 5 # SKIP role-restore check only applies when this session ran CREATE EXTENSION itself (test_load_mode=fresh) | ||
| ok 6 - Security definer function _tf.schema__getsert has search_path=pg_catalog | ||
| ok 7 - Security definer function _tf.test_factory__get has search_path=pg_catalog | ||
| ok 8 - Security definer function _tf.test_factory__set has search_path=pg_catalog | ||
| ok 9 - Security definer function _tf.table_create has search_path=pg_catalog | ||
| ok 10 - Security definer function _tf.get has search_path=pg_catalog | ||
| ok 11 - customer table is empty | ||
| ok 12 - invoice table is empty | ||
| ok 13 - invoice factory output | ||
| ok 14 - invoice table content | ||
| ok 15 - customer table content | ||
| ok 16 - invoice factory second call | ||
| ok 17 - invoice table content stayed constant | ||
| ok 18 - customer table content stayed constant | ||
| ok 19 - Test function factory | ||
| ok 20 - customer table has new row | ||
| ok 21 - truncate invoice | ||
| ok 22 - invoice factory get remains the same after truncate |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| \set ECHO none | ||
| ok 1 - install.sql skipped under test_load_mode=existing -- see comment in test/sql/install.sql |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| \set ECHO none | ||
| ok 1 - Register test customers | ||
| ok 2 - Create function customer__add | ||
| ok 3 - Register test invoices | ||
| ok 4 - Ensure original_role temp table was dropped | ||
| ok 5 # SKIP role-restore check only applies when this session ran CREATE EXTENSION itself (test_load_mode=fresh) | ||
| ok 6 - Security definer function _tf.schema__getsert has search_path=pg_catalog | ||
| ok 7 - Security definer function _tf.test_factory__get has search_path=pg_catalog | ||
| ok 8 - Security definer function _tf.test_factory__set has search_path=pg_catalog | ||
| ok 9 - Security definer function _tf.table_create has search_path=pg_catalog | ||
| ok 10 - Security definer function _tf.get has search_path=pg_catalog | ||
| ok 11 - Get test data set "base" for table invoice | ||
| ok 12 - Get test data set "base" for table invoice | ||
| ok 13 - Ensure we get sane error for a non-existent table |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,7 +1,24 @@ | ||||||||||||||||||||||||||
| \echo Creating extension :extension_name | ||||||||||||||||||||||||||
| -- No IF NOT EXISTS because we'll be confused if we're not loading the new stuff | ||||||||||||||||||||||||||
| /* | ||||||||||||||||||||||||||
| * In 'update'/'existing' mode, test/install/load.sql already installed (and, | ||||||||||||||||||||||||||
| * for 'update', updated) the extensions in its own earlier committed | ||||||||||||||||||||||||||
| * session -- skip re-creating here instead of erroring or, worse, silently | ||||||||||||||||||||||||||
| * replacing the state those modes exist to test. In 'fresh' mode (the only | ||||||||||||||||||||||||||
| * mode test/install/load.sql leaves untouched), keep the original | ||||||||||||||||||||||||||
| * behavior: no IF NOT EXISTS, so we're confused loudly if something's | ||||||||||||||||||||||||||
| * already there instead of silently testing stale state. | ||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||
| SELECT | ||||||||||||||||||||||||||
| current_setting('test_factory.test_load_mode') <> 'fresh' | ||||||||||||||||||||||||||
| AND EXISTS (SELECT 1 FROM pg_extension WHERE extname = :'extension_name') | ||||||||||||||||||||||||||
| AS already_installed | ||||||||||||||||||||||||||
|
Comment on lines
+11
to
+14
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Fix the SQLFluff layout violation. Line 11 places Proposed fix- AND EXISTS (SELECT 1 FROM pg_extension WHERE extname = :'extension_name')
+ AND EXISTS (
+ SELECT 1
+ FROM pg_extension
+ WHERE extname = :'extension_name'
+ )📝 Committable suggestion
Suggested change
🧰 Tools🪛 SQLFluff (4.2.2)[error] 11-11: The 'WHERE' keyword should always start a new line. (LT14) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||||||||||||||||||||||||||
| \gset | ||||||||||||||||||||||||||
| \if :already_installed | ||||||||||||||||||||||||||
| \echo :extension_name already installed -- skipping CREATE EXTENSION (test_load_mode is not fresh) | ||||||||||||||||||||||||||
| \else | ||||||||||||||||||||||||||
| CREATE TEMP TABLE pre_install_role AS SELECT current_user; | ||||||||||||||||||||||||||
| GRANT SELECT ON pre_install_role TO public; -- In case role is different | ||||||||||||||||||||||||||
| CREATE EXTENSION :extension_name; | ||||||||||||||||||||||||||
| CREATE TEMP TABLE post_install_role AS SELECT current_user; | ||||||||||||||||||||||||||
| GRANT SELECT ON post_install_role TO public; -- In case role is different | ||||||||||||||||||||||||||
| \endif | ||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| \i test/roles.sql |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| \set ECHO none |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Reject wildcard and multi-word load modes.
Line 11 reverses
filter’s pattern/text arguments, so values such as%orfresh updatepass validation and are exported throughPGOPTIONS. Validate an exact, single allowed token instead.Proposed fix
🤖 Prompt for AI Agents