diff --git a/.claude/commands/commit.md b/.claude/commands/commit.md deleted file mode 100644 index 5ddbd74..0000000 --- a/.claude/commands/commit.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -description: Create a git commit following project standards and safety protocols -allowed-tools: Bash(git status:*), Bash(git log:*), Bash(git add:*), Bash(git diff:*), Bash(git commit:*), Bash(make test:*) ---- - -# commit - -Create a git commit following all project standards and safety protocols for pgxntool-test. - -**CRITICAL REQUIREMENTS:** - -1. **Git Safety**: Never update `git config`, never force push to `main`/`master`, never skip hooks unless explicitly requested - -2. **Commit Attribution**: Do NOT add "Generated with Claude Code" to commit message body. The standard Co-Authored-By trailer is acceptable per project CLAUDE.md. - -3. **Testing**: ALL tests must pass before committing: - - Run `make test` - - Check the output carefully for any "not ok" lines - - Count passing vs total tests - - **If ANY tests fail: STOP. Do NOT commit. Ask the user what to do.** - - There is NO such thing as an "acceptable" failing test - - Do NOT rationalize failures as "pre-existing" or "unrelated" - -**WORKFLOW:** - -1. Run in parallel: `git status`, `git diff --stat`, `git log -10 --oneline` - -2. Check test status - THIS IS MANDATORY: - - Run `make test 2>&1 | tee /tmp/test-output.txt` - - Check for failing tests: `grep "^not ok" /tmp/test-output.txt` - - If ANY tests fail: STOP immediately and inform the user - - Only proceed if ALL tests pass - -3. Analyze changes and draft concise commit message following this repo's style: - - Look at `git log -10 --oneline` to match existing style - - Be factual and direct (e.g., "Fix BATS dist test to create its own distribution") - - Focus on "why" when it adds value, otherwise just describe "what" - - List items in roughly decreasing order of impact - - Keep related items grouped together - - **In commit messages**: Wrap all code references in backticks - filenames, paths, commands, function names, variables, make targets, etc. - - Examples: `helpers.bash`, `make test-recursion`, `setup_sequential_test()`, `TEST_REPO`, `.envs/`, `01-meta.bats` - - Prevents markdown parsing issues and improves clarity - -4. **PRESENT the proposed commit message to the user and WAIT for approval before proceeding** - -5. After receiving approval, stage changes appropriately using `git add` - -6. **VERIFY staged files with `git status`**: - - If user did NOT specify a subset: Confirm ALL modified/untracked files are staged - - If user specified only certain files: Confirm ONLY those files are staged - - STOP and ask user if staging doesn't match intent - -7. After verification, commit using `HEREDOC` format: -```bash -git commit -m "$(cat <<'EOF' -Subject line (imperative mood, < 72 chars) - -Additional context if needed, wrapped at 72 characters. - -Co-Authored-By: Claude -EOF -)" -``` - -8. Run `git status` after commit to verify success - -9. If pre-commit hook modifies files: Check authorship (`git log -1 --format='%an %ae'`) and branch status, then amend if safe or create new commit - -**REPOSITORY CONTEXT:** - -This is pgxntool-test, a test harness for the pgxntool framework. Key facts: -- Tests live in `tests/` directory -- `.envs/` contains test environments (gitignored) - -**RESTRICTIONS:** -- DO NOT push unless explicitly asked -- DO NOT commit files with actual secrets (`.env`, `credentials.json`, etc.) -- Never use `-i` flags (`git commit -i`, `git rebase -i`, etc.) diff --git a/.claude/settings.json b/.claude/settings.json deleted file mode 100644 index e7bf5a9..0000000 --- a/.claude/settings.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "permissions": { - "allow": [ - "Bash(cat:*)", - "Bash(make test:*)", - "Bash(tee:*)", - "Bash(echo:*)", - "Bash(git show:*)", - "Bash(git log:*)", - "Bash(ls:*)", - "Bash(find:*)", - "Bash(git checkout:*)", - "Bash(head:*)" - ], - "additionalDirectories": [ - "../pgxntool-test/" - ] - } -} diff --git a/HISTORY.asc b/HISTORY.asc index bedc0b5..1a8a841 100644 --- a/HISTORY.asc +++ b/HISTORY.asc @@ -1,5 +1,14 @@ 1.0.0 ----- +== Add test-build for pre-test validation +New optional feature validates extension SQL syntax before running the full test suite. Place SQL files in `test/build/` to catch syntax errors early with better error messages than `CREATE EXTENSION` failures. Auto-detects based on file presence. + +== Add test/install for one-time test setup +New optional feature runs `test/install/` SQL files before the main test suite via a schedule file, all within a single `pg_regress` invocation. State created by install files (tables, extensions, etc.) persists into regular tests. Auto-detects based on file presence. + +== Add verify-results safeguard for make results +`make results` now refuses to run when tests are failing (detected via `regression.diffs`). Prevents accidentally updating expected files with incorrect output. Enabled by default; disable with `PGXNTOOL_ENABLE_VERIFY_RESULTS=no`. + == Fix broken multi-extension support Prior to this fix, distributions with multiple extensions or extensions with versions different from the PGXN distribution version were completely broken. Extension versions are now correctly read from each `.control` file's `default_version` instead of using META.json's distribution version. diff --git a/README.asc b/README.asc index bf3559d..b6a7a4c 100644 --- a/README.asc +++ b/README.asc @@ -47,6 +47,119 @@ Runs unit tests via the PGXS `installcheck` target. Unlike a simple `make instal NOTE: While you can still run `make installcheck` or any other valid PGXS make target directly, it's recommended to use `make test` when using pgxntool. The `test` target ensures clean builds, proper test isolation, and correct dependency installation. +=== test-build +Validates that extension SQL files are syntactically correct before running the full test suite. This feature runs SQL files from `test/build/` through `pg_regress`, providing better error messages than `CREATE EXTENSION` failures when there are syntax errors in your extension code. + +**How it works:** + +1. Place SQL files in `test/build/*.sql` (or `test/build/input/*.source` for source-processed files) +2. Place expected output in `test/build/expected/*.out` +3. These files run through `pg_regress` before `make test` runs the main test suite +4. If any build test fails, the test run stops immediately with clear error messages + +**Directory structure:** + +---- +test/build/ +├── *.sql # SQL test files (checked in) +├── input/ # Optional: .source files for pg_regress processing +│ └── *.source +├── expected/ # Expected output files (checked in) +│ └── *.out +└── sql/ # GENERATED - do not edit or check in + └── *.sql # Copied from *.sql above; generated from input/*.source +---- + +The `sql/` subdirectory is generated automatically by `make test-build`. It is listed in `.gitignore` and removed by `make clean`. Do not place files directly in `test/build/sql/`. + +**Configuration:** + +The feature auto-detects based on whether `test/build/*.sql` or `test/build/input/*.source` files exist: + +- Files present → feature enabled automatically +- No files → feature disabled (no impact on existing projects) + +You can override auto-detection by setting `PGXNTOOL_ENABLE_TEST_BUILD`: +---- +# In your Makefile +PGXNTOOL_ENABLE_TEST_BUILD = yes # or no +---- + +**Example: Validate extension SQL compiles** + +Create `test/build/build.sql` to run your extension's SQL directly: + +---- +\set ECHO none +\i test/pgxntool/psql.sql +\t + +BEGIN; +SET client_min_messages = WARNING; + +-- Install dependencies your extension requires +CREATE EXTENSION IF NOT EXISTS pgtap CASCADE; +CREATE EXTENSION IF NOT EXISTS some_dependency CASCADE; + +-- Clean slate +DROP EXTENSION IF EXISTS myext; +DROP SCHEMA IF EXISTS myext; +CREATE SCHEMA myext; + +-- Run the actual extension SQL (not CREATE EXTENSION) +\i sql/myext.sql + +\echo # BUILD TEST SUCCEEDED +ROLLBACK; +---- + +This approach catches SQL syntax errors *before* running `CREATE EXTENSION`, giving clearer error messages with line numbers. The `ROLLBACK` ensures nothing persists—this is purely validation. + +**Why use `\i` instead of `CREATE EXTENSION`?** + +When `CREATE EXTENSION` fails, PostgreSQL shows only "syntax error" with limited context. Running the SQL directly via `\i` shows the exact line and position of errors, making debugging much faster. + +=== test/install +Runs setup files before the main test suite within the same `pg_regress` invocation. This allows expensive one-time operations (like extension installation) to set up state that persists into the regular test files. + +**How it works:** + +1. Place SQL files in `test/install/*.sql` +2. Place expected output alongside as `test/install/*.out` +3. A schedule file is auto-generated that lists install files with `../install/` relative paths +4. `pg_regress` processes the install schedule first, then runs regular test files — all in one invocation, so database state persists + +**Directory structure:** + +---- +test/install/ +├── *.sql # SQL setup files (checked in) +├── *.out # Expected output (checked in, alongside .sql) +├── .gitignore # Ignores pg_regress artifacts (*.out.diff) +└── schedule # GENERATED - auto-created by make +---- + +The `schedule` file is generated automatically and listed in `.gitignore`. Do not edit it. + +**Configuration:** + +The feature auto-detects based on whether `test/install/*.sql` files exist: + +- Files present → feature enabled automatically +- No files → feature disabled (no impact on existing projects) + +You can override auto-detection by setting `PGXNTOOL_ENABLE_TEST_INSTALL`: +---- +# In your Makefile +PGXNTOOL_ENABLE_TEST_INSTALL = yes # or no +---- + +**Why this is useful:** + +Without `test/install`, each test file typically needs to run `CREATE EXTENSION` in its setup, which adds overhead and doesn't allow validating the installation step separately. With `test/install`, setup runs once before all tests, and any state it creates (tables, extensions, etc.) is available to every subsequent test file. + +**Key detail:** Install files and regular tests run in a single `pg_regress` invocation. This means the database is NOT dropped between install and test phases — state created by install files persists into the main test suite. + === testdeps This rule allows you to ensure certain actions have taken place before running tests. By default it has a single prerequisite, `pgtap`, which will attempt to install http://pgtap.org[pgtap] from PGXN. This depneds on having the pgxn client installed. @@ -78,6 +191,24 @@ IMPORTANT: *`make results` requires manual verification first*. The correct work Never run `make results` without first verifying the test changes are correct. The `results` target copies files from `test/results/` to `test/expected/`, so running it blindly will make incorrect output become the new expected behavior. +==== verify-results safeguard +By default, `make results` will refuse to run if `test/results/regression.diffs` exists (indicating failing tests). This prevents accidentally updating expected files with incorrect output. + +If tests are failing, you'll see: +---- +ERROR: Tests are failing. Cannot run 'make results'. +Fix test failures first, then run 'make results'. +---- + +To disable this safeguard (not recommended): +---- +# In your Makefile +PGXNTOOL_ENABLE_VERIFY_RESULTS = no + +# Or on the command line +make PGXNTOOL_ENABLE_VERIFY_RESULTS=no results +---- + === tag `make tag` will create a git branch for the current version of your extension, as determined by the META.json file. The reason to do this is so you can always refer to the exact code that went into a released version. @@ -223,7 +354,7 @@ Location of `asciidoc` or equivalent executable. If not set PGXNtool will search for first `asciidoctor`, then `asciidoc`. ASCIIDOC_EXTS:: File extensions to consider as Asciidoc. -Defined as `+= adoc asciidoc`. +Defined as `+= adoc asciidoc asc`. ASCIIDOC_FILES:: Asciidoc input files. PGXNtool searches each `$(DOC_DIRS)` directory, looking for files with any `$(ASCIIDOC_EXTS)` extension. diff --git a/README.html b/README.html index 41200aa..627cc6b 100644 --- a/README.html +++ b/README.html @@ -451,14 +451,16 @@

PGXNtool

  • 5. Version-Specific SQL Files @@ -599,7 +601,189 @@

    -

    4.3. testdeps

    +

    4.3. test-build

    +
    +

    Validates that extension SQL files are syntactically correct before running the full test suite. This feature runs SQL files from test/build/ through pg_regress, providing better error messages than CREATE EXTENSION failures when there are syntax errors in your extension code.

    +
    +
    +

    How it works:

    +
    +
    +
      +
    1. +

      Place SQL files in test/build/.sql (or test/build/input/.source for source-processed files)

      +
    2. +
    3. +

      Place expected output in test/build/expected/*.out

      +
    4. +
    5. +

      These files run through pg_regress before make test runs the main test suite

      +
    6. +
    7. +

      If any build test fails, the test run stops immediately with clear error messages

      +
    8. +
    +
    +
    +

    Directory structure:

    +
    +
    +
    +
    test/build/
    +├── *.sql              # SQL test files (checked in)
    +├── input/             # Optional: .source files for pg_regress processing
    +│   └── *.source
    +├── expected/          # Expected output files (checked in)
    +│   └── *.out
    +└── sql/               # GENERATED - do not edit or check in
    +    └── *.sql          # Copied from *.sql above; generated from input/*.source
    +
    +
    +
    +

    The sql/ subdirectory is generated automatically by make test-build. It is listed in .gitignore and removed by make clean. Do not place files directly in test/build/sql/.

    +
    +
    +

    Configuration:

    +
    +
    +

    The feature auto-detects based on whether test/build/.sql or test/build/input/.source files exist:

    +
    +
    +
      +
    • +

      Files present → feature enabled automatically

      +
    • +
    • +

      No files → feature disabled (no impact on existing projects)

      +
    • +
    +
    +
    +

    You can override auto-detection by setting PGXNTOOL_ENABLE_TEST_BUILD:

    +
    +
    +
    +
    # In your Makefile
    +PGXNTOOL_ENABLE_TEST_BUILD = yes  # or no
    +
    +
    +
    +

    Example: Validate extension SQL compiles

    +
    +
    +

    Create test/build/build.sql to run your extension’s SQL directly:

    +
    +
    +
    +
    \set ECHO none
    +\i test/pgxntool/psql.sql
    +\t
    +
    +BEGIN;
    +SET client_min_messages = WARNING;
    +
    +-- Install dependencies your extension requires
    +CREATE EXTENSION IF NOT EXISTS pgtap CASCADE;
    +CREATE EXTENSION IF NOT EXISTS some_dependency CASCADE;
    +
    +-- Clean slate
    +DROP EXTENSION IF EXISTS myext;
    +DROP SCHEMA IF EXISTS myext;
    +CREATE SCHEMA myext;
    +
    +-- Run the actual extension SQL (not CREATE EXTENSION)
    +\i sql/myext.sql
    +
    +\echo # BUILD TEST SUCCEEDED
    +ROLLBACK;
    +
    +
    +
    +

    This approach catches SQL syntax errors before running CREATE EXTENSION, giving clearer error messages with line numbers. The ROLLBACK ensures nothing persists—this is purely validation.

    +
    +
    +

    Why use \i instead of CREATE EXTENSION?

    +
    +
    +

    When CREATE EXTENSION fails, PostgreSQL shows only "syntax error" with limited context. Running the SQL directly via \i shows the exact line and position of errors, making debugging much faster.

    +
    +
    +
    +

    4.4. test/install

    +
    +

    Runs setup files before the main test suite within the same pg_regress invocation. This allows expensive one-time operations (like extension installation) to set up state that persists into the regular test files.

    +
    +
    +

    How it works:

    +
    +
    +
      +
    1. +

      Place SQL files in test/install/*.sql

      +
    2. +
    3. +

      Place expected output alongside as test/install/*.out

      +
    4. +
    5. +

      A schedule file is auto-generated that lists install files with ../install/ relative paths

      +
    6. +
    7. +

      pg_regress processes the install schedule first, then runs regular test files — all in one invocation, so database state persists

      +
    8. +
    +
    +
    +

    Directory structure:

    +
    +
    +
    +
    test/install/
    +├── *.sql              # SQL setup files (checked in)
    +├── *.out              # Expected output (checked in, alongside .sql)
    +├── .gitignore         # Ignores pg_regress artifacts (*.out.diff)
    +└── schedule           # GENERATED - auto-created by make
    +
    +
    +
    +

    The schedule file is generated automatically and listed in .gitignore. Do not edit it.

    +
    +
    +

    Configuration:

    +
    +
    +

    The feature auto-detects based on whether test/install/*.sql files exist:

    +
    +
    +
      +
    • +

      Files present → feature enabled automatically

      +
    • +
    • +

      No files → feature disabled (no impact on existing projects)

      +
    • +
    +
    +
    +

    You can override auto-detection by setting PGXNTOOL_ENABLE_TEST_INSTALL:

    +
    +
    +
    +
    # In your Makefile
    +PGXNTOOL_ENABLE_TEST_INSTALL = yes  # or no
    +
    +
    +
    +

    Why this is useful:

    +
    +
    +

    Without test/install, each test file typically needs to run CREATE EXTENSION in its setup, which adds overhead and doesn’t allow validating the installation step separately. With test/install, setup runs once before all tests, and any state it creates (tables, extensions, etc.) is available to every subsequent test file.

    +
    +
    +

    Key detail: Install files and regular tests run in a single pg_regress invocation. This means the database is NOT dropped between install and test phases — state created by install files persists into the main test suite.

    +
    +
    +
    +

    4.5. testdeps

    This rule allows you to ensure certain actions have taken place before running tests. By default it has a single prerequisite, pgtap, which will attempt to install pgtap from PGXN. This depneds on having the pgxn client installed.

    @@ -635,7 +819,7 @@

    -

    4.4. results

    +

    4.6. results

    Because make test ultimately runs installcheck, it’s using the Postgres test suite. Unfortunately, that suite is based on running diff between a raw output file and expected results. I STRONGLY recommend you use pgTap instead! With pgTap, it’s MUCH easier to determine whether a test is passing or not - tests explicitly pass or fail rather than requiring you to examine diff output. The extra effort of learning pgTap will quickly pay for itself. This example might help get you started.

    @@ -670,9 +854,36 @@

    Never run make results without first verifying the test changes are correct. The results target copies files from test/results/ to test/expected/, so running it blindly will make incorrect output become the new expected behavior.

    +
    +

    4.6.1. verify-results safeguard

    +
    +

    By default, make results will refuse to run if test/results/regression.diffs exists (indicating failing tests). This prevents accidentally updating expected files with incorrect output.

    +
    +
    +

    If tests are failing, you’ll see:

    +
    +
    +
    +
    ERROR: Tests are failing. Cannot run 'make results'.
    +Fix test failures first, then run 'make results'.
    +
    +
    +
    +

    To disable this safeguard (not recommended):

    +
    +
    +
    +
    # In your Makefile
    +PGXNTOOL_ENABLE_VERIFY_RESULTS = no
    +
    +# Or on the command line
    +make PGXNTOOL_ENABLE_VERIFY_RESULTS=no results
    +
    +
    +
    -

    4.5. tag

    +

    4.7. tag

    make tag will create a git branch for the current version of your extension, as determined by the META.json file. The reason to do this is so you can always refer to the exact code that went into a released version.

    @@ -693,7 +904,7 @@

    4.

    -

    4.6. dist

    +

    4.8. dist

    make dist will create a .zip file for your current version that you can upload to PGXN. The file is named after the PGXN name and version (the top-level "name" and "version" attributes in META.json). The .zip file is placed in the parent directory so as not to clutter up your git repo.

    @@ -711,7 +922,7 @@

    -

    4.7. pgxntool-sync

    +

    4.9. pgxntool-sync

    This rule will pull down the latest released version of PGXNtool via git subtree pull.

    @@ -741,7 +952,7 @@

    -

    4.8. pgtle

    +

    4.10. pgtle

    Generates pg_tle (Trusted Language Extensions) registration SQL files for deploying extensions in managed environments like AWS RDS/Aurora. See [_pg_tle_Support] for complete documentation.

    @@ -750,7 +961,7 @@

    -

    4.9. check-pgtle

    +

    4.11. check-pgtle

    Checks if pg_tle is installed and reports the version. This target: - Reports the version from pg_extension if CREATE EXTENSION pg_tle has been run in the database @@ -766,7 +977,7 @@

    -

    4.10. run-pgtle

    +

    4.12. run-pgtle

    Registers all extensions with pg_tle by executing the generated pg_tle registration SQL files in a PostgreSQL database. This target: - Requires pg_tle extension to be installed (checked via check-pgtle) @@ -974,7 +1185,7 @@

    <
    ASCIIDOC_EXTS

    File extensions to consider as Asciidoc. -Defined as += adoc asciidoc.

    +Defined as += adoc asciidoc asc.

    ASCIIDOC_FILES
    @@ -1259,7 +1470,7 @@

    diff --git a/_.gitignore b/_.gitignore index 0c14928..1873c2c 100644 --- a/_.gitignore +++ b/_.gitignore @@ -24,6 +24,14 @@ results/ regression.diffs regression.out +# Generated sql/ directory for test/build +# Created by make test-build. See README.asc for details. +test/build/sql/ + +# Auto-generated schedule file for test/install +# Created by make when test/install/*.sql files exist. +test/install/schedule + # Misc tmp/ .DS_Store diff --git a/base.mk b/base.mk index f46bbe9..7c6a3bb 100644 --- a/base.mk +++ b/base.mk @@ -44,7 +44,7 @@ DOCS += $(foreach dir,$(DOC_DIRS),$(wildcard $(dir)/*)) # Find all asciidoc targets ASCIIDOC ?= $(shell which asciidoctor 2>/dev/null || which asciidoc 2>/dev/null) -ASCIIDOC_EXTS += adoc asciidoc +ASCIIDOC_EXTS += adoc asciidoc asc ASCIIDOC_FILES += $(foreach dir,$(DOC_DIRS),$(foreach ext,$(ASCIIDOC_EXTS),$(wildcard $(dir)/*.$(ext)))) PG_CONFIG ?= pg_config @@ -64,6 +64,129 @@ TEST__SOURCE__SQL_FILES = $(patsubst $(TESTDIR)/input/%.source,$(TESTDIR)/sql/% TEST__SOURCE__EXPECTED_FILES = $(patsubst $(TESTDIR)/output/%.source,$(TESTDIR)/expected/%.out,$(TEST__SOURCE__OUTPUT_FILES)) REGRESS = $(sort $(notdir $(subst .source,,$(TEST_FILES:.sql=)))) # Sort is to get unique list REGRESS_OPTS = --inputdir=$(TESTDIR) --outputdir=$(TESTOUT) # See additional setup below + +# +# OPTIONAL TEST FEATURES +# +# These sections configure optional test features. Each feature can be enabled/disabled +# via a makefile variable. If not explicitly set, features auto-detect based on +# directory existence or default behavior. The actual feature implementation is +# located later in this file (see test-build target, schedule file generation, etc.). +# + +# ------------------------------------------------------------------------------ +# test-build: Sanity check extension files before running full test suite +# ------------------------------------------------------------------------------ +# Purpose: Validates that extension SQL files are syntactically correct by running +# files from test/build/ through pg_regress. This provides better error +# messages than CREATE EXTENSION failures. +# +# Variable: PGXNTOOL_ENABLE_TEST_BUILD +# - Can be set manually in Makefile or command line +# - Allowed values: "yes" or "no" (case-insensitive) +# - If not set: Auto-detects based on existence of test/build/*.sql files +# - Usage: Controls whether test-build target exists and runs before make test +# +# Implementation: See test-build target definition (search for "test-build:" in this file) +# +# Files can be *.sql in test/build/ or *.source in test/build/input/ +# The sql/ subdirectory is generated (cleaned, gitignored) +# +TEST_BUILD_SQL_FILES = $(wildcard $(TESTDIR)/build/*.sql) +TEST_BUILD_SOURCE_FILES = $(wildcard $(TESTDIR)/build/input/*.source) +TEST_BUILD_FILES = $(TEST_BUILD_SQL_FILES) $(TEST_BUILD_SOURCE_FILES) +ifdef PGXNTOOL_ENABLE_TEST_BUILD + # User explicitly set the variable - validate and use their value + PGXNTOOL_ENABLE_TEST_BUILD_NORM = $(strip $(shell echo "$(PGXNTOOL_ENABLE_TEST_BUILD)" | tr '[:upper:]' '[:lower:]')) + ifneq ($(PGXNTOOL_ENABLE_TEST_BUILD_NORM),yes) + ifneq ($(PGXNTOOL_ENABLE_TEST_BUILD_NORM),no) + $(error PGXNTOOL_ENABLE_TEST_BUILD must be "yes" or "no", got "$(PGXNTOOL_ENABLE_TEST_BUILD)") + endif + endif + # Use normalized value + PGXNTOOL_ENABLE_TEST_BUILD = $(PGXNTOOL_ENABLE_TEST_BUILD_NORM) +else + # Auto-detect: enable if test/build/ directory has SQL files + ifneq ($(strip $(TEST_BUILD_FILES)),) + PGXNTOOL_ENABLE_TEST_BUILD = yes + else + PGXNTOOL_ENABLE_TEST_BUILD = no + endif +endif + +# ------------------------------------------------------------------------------ +# test/install: Run setup files before all tests in the same pg_regress session +# ------------------------------------------------------------------------------ +# Purpose: Runs files from test/install/ before all test/sql/ files within a +# SINGLE pg_regress invocation via schedule files. This ensures that +# state created by install files (tables, extensions, etc.) persists +# into the main test suite. +# +# Variable: PGXNTOOL_ENABLE_TEST_INSTALL +# - Can be set manually in Makefile or command line +# - Allowed values: "yes" or "no" (case-insensitive) +# - If not set: Auto-detects based on existence of test/install/*.sql files +# - Usage: Controls whether schedule files are generated for test/install +# +# Directory layout (follows ~/code/extensions/archive/ pattern): +# test/install/*.sql - Install SQL files +# test/install/*.out - Expected output (lives alongside .sql files) +# test/install/schedule - Auto-generated schedule file +# test/sql/schedule - Auto-generated schedule file for regular tests +# +# The schedule files use relative paths (../install/testname) so pg_regress +# resolves install files from their original location without copying. +# +TEST_INSTALL_SQL_FILES = $(wildcard $(TESTDIR)/install/*.sql) +ifdef PGXNTOOL_ENABLE_TEST_INSTALL + # User explicitly set the variable - validate and use their value + PGXNTOOL_ENABLE_TEST_INSTALL_NORM = $(strip $(shell echo "$(PGXNTOOL_ENABLE_TEST_INSTALL)" | tr '[:upper:]' '[:lower:]')) + ifneq ($(PGXNTOOL_ENABLE_TEST_INSTALL_NORM),yes) + ifneq ($(PGXNTOOL_ENABLE_TEST_INSTALL_NORM),no) + $(error PGXNTOOL_ENABLE_TEST_INSTALL must be "yes" or "no", got "$(PGXNTOOL_ENABLE_TEST_INSTALL)") + endif + endif + # Use normalized value + PGXNTOOL_ENABLE_TEST_INSTALL = $(PGXNTOOL_ENABLE_TEST_INSTALL_NORM) +else + # Auto-detect: enable if test/install/ directory has SQL files + ifneq ($(strip $(TEST_INSTALL_SQL_FILES)),) + PGXNTOOL_ENABLE_TEST_INSTALL = yes + else + PGXNTOOL_ENABLE_TEST_INSTALL = no + endif +endif + +# ------------------------------------------------------------------------------ +# verify-results: Safeguard for make results +# ------------------------------------------------------------------------------ +# Purpose: Prevents accidentally running 'make results' when tests are failing. +# Checks for existence of regression.diffs file before allowing results update. +# +# Variable: PGXNTOOL_ENABLE_VERIFY_RESULTS +# - Can be set manually in Makefile or command line +# - Allowed values: "yes" or "no" (case-insensitive) +# - If not set: Defaults to "yes" (enabled by default for all pgxntool projects) +# - Usage: Controls whether verify-results target exists and blocks make results +# +# Implementation: See verify-results target definition and results target modification +# (search for "verify-results" and "results:" in this file) +# +ifdef PGXNTOOL_ENABLE_VERIFY_RESULTS + # User explicitly set the variable - validate and use their value + PGXNTOOL_ENABLE_VERIFY_RESULTS_NORM = $(strip $(shell echo "$(PGXNTOOL_ENABLE_VERIFY_RESULTS)" | tr '[:upper:]' '[:lower:]')) + ifneq ($(PGXNTOOL_ENABLE_VERIFY_RESULTS_NORM),yes) + ifneq ($(PGXNTOOL_ENABLE_VERIFY_RESULTS_NORM),no) + $(error PGXNTOOL_ENABLE_VERIFY_RESULTS must be "yes" or "no", got "$(PGXNTOOL_ENABLE_VERIFY_RESULTS)") + endif + endif + # Use normalized value - use := for immediate evaluation to avoid recursion + override PGXNTOOL_ENABLE_VERIFY_RESULTS := $(PGXNTOOL_ENABLE_VERIFY_RESULTS_NORM) +else + # Auto-detect: default to yes (enabled by default for all pgxntool projects) + PGXNTOOL_ENABLE_VERIFY_RESULTS = yes +endif + MODULES = $(patsubst %.c,%,$(wildcard src/*.c)) ifeq ($(strip $(MODULES)),) MODULES =# Set to NUL so PGXS doesn't puke @@ -90,6 +213,35 @@ ifeq ($($call test, $(MAJORVER), -lt 13), yes) REGRESS_OPTS += --load-language=plpgsql endif +# +# test/install: Schedule-based approach +# +# When enabled, generates a schedule file listing install files, and adds it +# to REGRESS_OPTS. pg_regress processes --schedule tests before command-line +# test names, so install files run first in the SAME pg_regress invocation. +# This ensures state created by install files persists into the main test suite. +# +# The schedule uses relative paths (../install/testname) so pg_regress finds +# install files in their original location without copying. +# +ifeq ($(PGXNTOOL_ENABLE_TEST_INSTALL),yes) +PGXNTOOL_INSTALL_SCHEDULE = $(TESTDIR)/install/schedule +EXTRA_CLEAN += $(PGXNTOOL_INSTALL_SCHEDULE) + +# Add install schedule; REGRESS stays as-is (regular tests run after schedule) +REGRESS_OPTS += --schedule=$(PGXNTOOL_INSTALL_SCHEDULE) + +# Always regenerate schedule file to catch added/removed files +.PHONY: $(PGXNTOOL_INSTALL_SCHEDULE) +$(PGXNTOOL_INSTALL_SCHEDULE): + @echo "# Auto-generated - DO NOT EDIT" > $@ + @for f in $(notdir $(basename $(TEST_INSTALL_SQL_FILES))); do \ + echo "test: ../install/$$f" >> $@; \ + done + +installcheck: $(PGXNTOOL_INSTALL_SCHEDULE) +endif + PGXS := $(shell $(PG_CONFIG) --pgxs) # Need to do this because we're not setting EXTENSION MODULEDIR = extension @@ -111,9 +263,33 @@ installcheck: $(TEST_RESULT_FILES) $(TEST_SQL_FILES) $(TEST__SOURCE__INPUT_FILES # watch-make if you're generating intermediate files. If tests end up needing # clean it's an indication of a missing dependency anyway. .PHONY: test -test: testdeps install installcheck +# Build test dependencies list based on enabled features +TEST_DEPS = testdeps +ifeq ($(PGXNTOOL_ENABLE_TEST_BUILD),yes) +TEST_DEPS += test-build +endif +TEST_DEPS += install +TEST_DEPS += installcheck +test: $(TEST_DEPS) @if [ -r $(TESTOUT)/regression.diffs ]; then cat $(TESTOUT)/regression.diffs; fi +# +# verify-results: Safeguard for make results +# +# Checks if tests are passing before allowing make results to proceed +ifeq ($(PGXNTOOL_ENABLE_VERIFY_RESULTS),yes) +.PHONY: verify-results +verify-results: + @if [ -r $(TESTOUT)/regression.diffs ]; then \ + echo "ERROR: Tests are failing. Cannot run 'make results'."; \ + echo "Fix test failures first, then run 'make results'."; \ + echo ""; \ + echo "See $(TESTOUT)/regression.diffs for details:"; \ + cat $(TESTOUT)/regression.diffs; \ + exit 1; \ + fi +endif + # make results: runs `make test` and copy all result files to expected # DO NOT RUN THIS UNLESS YOU'RE CERTAIN ALL YOUR TESTS ARE PASSING! # @@ -132,7 +308,11 @@ test: testdeps install installcheck # those are the source of truth and will be regenerated by pg_regress from the .source files. # Only copy files from results/ that don't have output/*.source counterparts. .PHONY: results +ifeq ($(PGXNTOOL_ENABLE_VERIFY_RESULTS),yes) +results: verify-results test +else results: test +endif @# Copy .out files from results/ to expected/, excluding those with output/*.source counterparts @# .out files with output/*.source counterparts are generated from .source files and should NOT be overwritten @$(PGXNTOOL_DIR)/make_results.sh $(TESTDIR) $(TESTOUT) @@ -192,6 +372,48 @@ $(TESTDIR)/sql $(TESTDIR)/expected/ $(TESTOUT)/results/: $(TEST_RESULT_FILES): | $(TESTDIR)/expected/ @touch $@ +# +# test-build: Sanity check extension files in test/build/ +# +# The sql/ subdirectory is generated - files are copied from test/build/*.sql +# and pg_regress generates sql/ from input/*.source files. +# This directory should be in .gitignore and is cleaned by make clean. +# +ifeq ($(PGXNTOOL_ENABLE_TEST_BUILD),yes) +TEST_BUILD_SQL_DIR = $(TESTDIR)/build/sql +TEST_BUILD_REGRESS = $(sort $(notdir $(basename $(TEST_BUILD_SQL_FILES))) $(notdir $(basename $(TEST_BUILD_SOURCE_FILES:.source=)))) +.PHONY: test-build +test-build: install + @if [ -z "$(strip $(TEST_BUILD_FILES))" ]; then \ + echo "No files found in $(TESTDIR)/build/"; \ + exit 1; \ + fi + @mkdir -p $(TEST_BUILD_SQL_DIR) + @mkdir -p $(TESTDIR)/build/expected + @# Copy .sql files to sql/ directory + @for file in $(TEST_BUILD_SQL_FILES); do \ + cp "$$file" "$(TEST_BUILD_SQL_DIR)/$$(basename $$file)"; \ + done + @# Create empty expected files if needed + @for file in $(TEST_BUILD_SQL_FILES); do \ + if [ ! -f "$(TESTDIR)/build/expected/$$(basename $$file .sql).out" ]; then \ + touch "$(TESTDIR)/build/expected/$$(basename $$file .sql).out"; \ + fi; \ + done + @for file in $(TEST_BUILD_SOURCE_FILES); do \ + base=$$(basename $$file .source); \ + if [ ! -f "$(TESTDIR)/build/expected/$$base.out" ]; then \ + touch "$(TESTDIR)/build/expected/$$base.out"; \ + fi; \ + done + $(MAKE) -C . REGRESS="$(TEST_BUILD_REGRESS)" REGRESS_OPTS="--inputdir=$(TESTDIR)/build --outputdir=$(TESTDIR)/build" installcheck + @if [ -r $(TESTDIR)/build/regression.diffs ]; then \ + echo "test-build failed - see $(TESTDIR)/build/regression.diffs"; \ + cat $(TESTDIR)/build/regression.diffs; \ + exit 1; \ + fi +endif + # # DOC SUPPORT @@ -319,6 +541,19 @@ DOCS =# Set to NUL so PGXS doesn't puke endif include $(PGXS) +# +# Make clean also run distclean to remove PGXNTOOL_distclean files +# This must be after PGXS is included so we can add distclean as a prerequisite +clean: distclean + +# Clean generated sql/ directory for test-build +ifeq ($(PGXNTOOL_ENABLE_TEST_BUILD),yes) +.PHONY: clean-test-build +clean-test-build: + rm -rf $(TEST_BUILD_SQL_DIR) +clean: clean-test-build +endif + # # pgtap # diff --git a/lib.sh b/lib.sh index 41de512..a95195f 100644 --- a/lib.sh +++ b/lib.sh @@ -33,7 +33,7 @@ die() { local exit_code=$1 shift error "$@" - exit $exit_code + exit "$exit_code" } # Debug function