Skip to content

feature: seed and validate testing harness#30

Merged
randoneering merged 23 commits into
mainfrom
feature/load_testing
Apr 21, 2026
Merged

feature: seed and validate testing harness#30
randoneering merged 23 commits into
mainfrom
feature/load_testing

Conversation

@randoneering
Copy link
Copy Markdown
Owner

@randoneering randoneering commented Apr 21, 2026

Pull Request Summary

The following PR includes a new testing harness that will seed a target database and validate health checks. For each new health check, we are asking that contributors add one test via pgTAP and through the seed and validate scripts. If you do not know how, that is totally fine! We can help you along the way.

Type of Change

  • New health check
  • Bug fix
  • Performance improvement
  • Documentation update
  • Refactoring/code cleanup
  • Breaking change

Related Issues

  • Fixes #
  • Related to #
  • Closes #

Testing

PostgreSQL Version Compatibility

Has this code been tested against the following PostgreSQL versions?

  • PostgreSQL 15
  • PostgreSQL 16
  • PostgreSQL 17
  • PostgreSQL 18

Testing notes:

Managed Database Platforms

Has this code been deployed and tested on the following platforms?

  • Amazon RDS for PostgreSQL
  • Google Cloud SQL for PostgreSQL (currently unable to test)
  • Azure Database for PostgreSQL (currently unable to test)
  • Neon
  • Supabase
  • Self-managed PostgreSQL

Platform-specific notes:


Additional Notes


Greptile Summary

This PR introduces a seed-and-validate testing harness (testing/seed_and_validate.py) that spins up a throwaway PostgreSQL database, seeds data and live sessions that trigger every pgFirstAid health check, then validates all expected check names appear in output. It also adds structured unit tests, per-check seed SQL scripts, and CI wiring for PG15–18 self-hosted and managed-DB targets.

  • P1 — CI gap: integration-pg-matrix.yml's paths trigger omits testing/seed_and_validate.py and testing/healthcheck_seed/**; future changes to the core harness won't trigger the integration workflow.
  • P2 — empty script: 04_session_blocked.sql is empty despite being listed in the README as the "Session B" blocked-session script needed for manual testing.

Confidence Score: 4/5

Safe to merge after fixing the CI path filter; remaining findings are cleanup/documentation gaps.

One P1 issue: the paths trigger in integration-pg-matrix.yml omits the new seed harness files, meaning future PRs touching those files won't trigger CI validation. The remaining findings (dead stop parameter, leaked role, empty session B script) are P2 quality/documentation issues that don't affect runtime correctness.

.github/workflows/integration-pg-matrix.yml (path filter), testing/healthcheck_seed/04_session_blocked.sql (empty file)

Important Files Changed

Filename Overview
testing/seed_and_validate.py Core orchestrator: creates a throwaway DB, seeds structural/session checks, validates all expected check names fire; dead stop parameter in _long_query_thread and missing role cleanup in finally block are minor issues.
testing/test_seed_and_validate.py Good unit-test coverage of connection params, threshold patching, build_report logic, psql invocation, and a cross-file coverage guard that diffs check names in the SQL against Python constants.
.github/workflows/integration-pg-matrix.yml Runs integration tests + seed/validate harness against PG15–18; paths trigger is missing testing/seed_and_validate.py and testing/healthcheck_seed/**, so future changes to those files won't re-run CI.
.github/workflows/managed-db-validate.yml Reusable workflow for cloud-managed DB validation; correctly resolves host via AWS/GCP APIs, validates secrets, and runs the managed seed harness.
testing/healthcheck_seed/01_seed_static_checks.sql Idempotent structural seed covering all static health checks; schema/role teardown at top ensures clean re-runs.
testing/healthcheck_seed/04_session_blocked.sql Empty file — the README documents it as the 'Session B' blocked-session script but it contains no SQL, making manual validation of the blocked-query check impossible without it.
testing/healthcheck_seed/02_seed_pg_stat_statements.sql Uses \gexec to generate workload patterns covering all PSS-dependent checks; guarded by psql availability check in the Python harness.
testing/healthcheck_seed/99_validate_seed_results.sql Simple manual validation query grouping pg_firstAid() output by severity and check name.

Sequence Diagram

sequenceDiagram
    participant M as main()
    participant A as admin_conn (postgres DB)
    participant T as test_conn (pgfirstaid_test DB)
    participant Th as Session Threads

    M->>A: connect_admin()
    M->>A: create_test_db() — DROP/CREATE pgfirstaid_test
    M->>T: connect_test()
    M->>T: install_function() — patch thresholds & install SQL
    M->>T: run_sql_file(01_seed_static_checks.sql)
    M->>T: seed_low_usage_index_scans()
    M->>T: run_psql_file(02_seed_pg_stat_statements.sql)
    M->>T: try_create_replication_slot()
    M->>Th: start blocker thread (holds UPDATE lock)
    M->>Th: start blocked thread (waits on lock)
    M->>Th: start idle-in-transaction thread
    M->>Th: start long-query thread (pg_sleep 700s)
    M->>A: _wait_for_active_query() — poll pg_stat_activity
    Note over M: sleep 6 minutes for 5-min session thresholds
    M->>T: run_validation() — SELECT from pg_firstAid()
    T-->>M: fired check names
    M->>M: build_report(fired, expected, skipped)
    Note over M: finally block
    M->>Th: stop_event.set() + sleep 2s
    M->>T: drop_replication_slot()
    M->>A: drop_test_db() — pg_terminate_backend + DROP DATABASE
Loading

Fix All in Claude Code

Reviews (1): Last reviewed commit: "added seed/validation testing into workf..." | Re-trigger Greptile

Greptile also left 2 inline comments on this PR.

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

Review Summary by Qodo

Add seed-and-validate testing harness with end-to-end check validation

✨ Enhancement 🧪 Tests

Grey Divider

Walkthroughs

Description
• Add comprehensive seed-and-validate Python orchestrator for end-to-end testing
  - Creates throwaway test database, seeds all pgFirstAid checks, validates firing
  - Patches thresholds for test-friendly sizes, runs live session threads
  - Supports both standard and managed database modes
• Implement full structural seed SQL with 15+ check patterns
  - Tables, indexes, statistics, roles, locks, replication slots
  - Idempotent schema-based seeding with proper cleanup
• Add pytest test suite validating seed logic and check classification
  - Tests connection params, threshold patching, report building, PSS state detection
  - Validates expected check sets match pgFirstAid.sql definitions
• Integrate harness into CI workflows for managed database validation
  - Runs after integration tests in GitHub Actions matrix and managed-db workflows
• Add design specification document and Python project configuration
Diagram
flowchart LR
  A["seed_and_validate.py<br/>Orchestrator"] --> B["Create Test DB"]
  B --> C["Install pgFirstAid<br/>with patched thresholds"]
  C --> D["Run Static Seed<br/>01_seed_static_checks.sql"]
  D --> E["Run PSS Seed<br/>02_seed_pg_stat_statements.sql"]
  E --> F["Start Live Session<br/>Threads"]
  F --> G["Wait 6 minutes<br/>for thresholds"]
  G --> H["Run Validation<br/>pg_firstAid()"]
  H --> I["Report Results<br/>PASS/FAIL/SKIP"]
  I --> J["Drop Test DB<br/>Cleanup"]
Loading

Grey Divider

File Changes

1. testing/seed_and_validate.py ✨ Enhancement +819/-0

Main orchestrator script for seed-and-validate harness

testing/seed_and_validate.py


2. testing/test_seed_and_validate.py 🧪 Tests +323/-0

Pytest test suite for orchestrator functions

testing/test_seed_and_validate.py


3. testing/healthcheck_seed/01_seed_static_checks.sql ✨ Enhancement +286/-0

Comprehensive structural seed for 15+ checks

testing/healthcheck_seed/01_seed_static_checks.sql


View more (17)
4. testing/healthcheck_seed/02_seed_pg_stat_statements.sql ✨ Enhancement +112/-0

pg_stat_statements workload seed patterns

testing/healthcheck_seed/02_seed_pg_stat_statements.sql


5. testing/healthcheck_seed/03_session_blocker.sql 📝 Documentation +23/-0

Manual session blocker script for lock checks

testing/healthcheck_seed/03_session_blocker.sql


6. testing/healthcheck_seed/05_session_idle_in_transaction.sql 📝 Documentation +1/-0

Manual idle-in-transaction session script

testing/healthcheck_seed/05_session_idle_in_transaction.sql


7. testing/healthcheck_seed/06_session_long_running_query.sql 📝 Documentation +6/-0

Manual long-running query session script

testing/healthcheck_seed/06_session_long_running_query.sql


8. testing/healthcheck_seed/07_pgbench_active_query.sql 📝 Documentation +7/-0

pgbench workload template for high connection testing

testing/healthcheck_seed/07_pgbench_active_query.sql


9. testing/healthcheck_seed/99_validate_seed_results.sql 📝 Documentation +17/-0

Manual validation query for seed results

testing/healthcheck_seed/99_validate_seed_results.sql


10. testing/healthcheck_seed/README.md 📝 Documentation +42/-0

Documentation for seed kit usage and constraints

testing/healthcheck_seed/README.md


11. docs/superpowers/specs/2026-04-15-seed-and-validate-design.md 📝 Documentation +188/-0

Design specification for seed-and-validate architecture

docs/superpowers/specs/2026-04-15-seed-and-validate-design.md


12. .github/workflows/integration-pg-matrix.yml ⚙️ Configuration changes +3/-0

Add seed-and-validate harness to matrix workflow

.github/workflows/integration-pg-matrix.yml


13. .github/workflows/managed-db-validate.yml ⚙️ Configuration changes +3/-0

Add seed-and-validate harness to managed DB workflow

.github/workflows/managed-db-validate.yml


14. .github/workflows/README.md 📝 Documentation +1/-1

Update workflow documentation with harness details

.github/workflows/README.md


15. pgFirstAid.sql 🐞 Bug fix +4/-0

Wrap pg_stat_statements checks in exception handler

pgFirstAid.sql


16. pyproject.toml ⚙️ Configuration changes +11/-0

Add Python project config with dependencies

pyproject.toml


17. .python-version ⚙️ Configuration changes +1/-0

Specify Python 3.13 as target version

.python-version


18. README.md 📝 Documentation +1/-0

Document seed-and-validate in validation section

README.md


19. testing/integration/README.md 📝 Documentation +2/-0

Document seed-and-validate harness in integration tests

testing/integration/README.md


20. testing/healthcheck_seed/04_session_blocked.sql Additional files +0/-0

...

testing/healthcheck_seed/04_session_blocked.sql


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects Bot commented Apr 21, 2026

Code Review by Qodo

🐞 Bugs (4) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Managed view PSS crash 🐞 Bug ≡ Correctness
Description
pgFirstAid.sql now guards pg_stat_statements queries with an object_not_in_prerequisite_state
exception handler, but view_pgFirstAid_managed.sql still calls pg_stat_statements without any
exception block, so v_pgfirstAid can error when the extension is installed but not preload-enabled.
This breaks the new seed_and_validate.py --managed flow which explicitly expects this state and
tries to treat it as a skip.
Code

pgFirstAid.sql[R268-270]

+    exception when object_not_in_prerequisite_state then
+        return;
+    end;
Evidence
The PR adds a BEGIN/EXCEPTION wrapper around pg_stat_statements reads in pgFirstAid.sql, handling
object_not_in_prerequisite_state by returning no rows. However, the managed view SQL defines the
same pgfirstaid_pg_stat_statements_checks() without that exception handling and v_pgfirstAid
unconditionally UNION ALLs that function when the extension exists; seed_and_validate.py notes this
exact failure mode (installed but not queryable) and relies on the SQL layer not crashing so it can
mark checks as skipped.

pgFirstAid.sql[14-27]
pgFirstAid.sql[241-272]
view_pgFirstAid_managed.sql[236-268]
view_pgFirstAid_managed.sql[943-957]
testing/seed_and_validate.py[152-163]
testing/seed_and_validate.py[630-638]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`view_pgFirstAid_managed.sql`'s `pgfirstaid_pg_stat_statements_checks()` can raise `object_not_in_prerequisite_state` when `pg_stat_statements` is installed but not actually queryable (not in `shared_preload_libraries`). The PR fixed this in `pgFirstAid.sql` but the managed view SQL still lacks the same exception guard, so `v_pgfirstAid` can crash and break `testing/seed_and_validate.py --managed`.

### Issue Context
- `testing/seed_and_validate.py` explicitly treats “extension installed but not queryable” as a normal skip case.
- The managed view SQL currently UNION ALLs the pg_stat_statements function when the extension exists, so an exception inside that function aborts the entire view query.

### Fix Focus Areas
- view_pgFirstAid_managed.sql[2-268]
- view_pgFirstAid_managed.sql[943-957]

### Suggested change
Wrap the body of `pgfirstaid_pg_stat_statements_checks()` in `view_pgFirstAid_managed.sql` with the same `BEGIN ... EXCEPTION WHEN object_not_in_prerequisite_state THEN RETURN; END;` pattern already added to `pgFirstAid.sql` so managed-mode queries return an empty set instead of erroring.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Blocked session script empty 🐞 Bug ≡ Correctness
Description
testing/healthcheck_seed/README.md instructs running 04_session_blocked.sql to create the blocked
session, but 04_session_blocked.sql is empty so the documented blocker/blocked scenario cannot be
reproduced manually. This prevents manually triggering lock-related checks using the seed kit as
documented.
Code

testing/healthcheck_seed/README.md[R16-20]

+4. In separate terminals, run runtime scripts together:
+   - session A: `03_session_blocker.sql`
+   - session B: `04_session_blocked.sql`
+   - session C: `05_session_idle_in_transaction.sql`
+   - session D: `06_session_long_running_query.sql`
Evidence
The seed kit README explicitly lists 04_session_blocked.sql as the blocked session script, but the
file has no content, so following the documented run order cannot create a blocked session.

testing/healthcheck_seed/README.md[16-20]
testing/healthcheck_seed/04_session_blocked.sql[1-1]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`testing/healthcheck_seed/04_session_blocked.sql` is empty, but the seed-kit README tells users to run it to create a blocked session.

### Issue Context
Session A (`03_session_blocker.sql`) takes a row lock on `pgfirstaid_seed.lock_target(id=1)`. Session B should attempt to update the same row and block.

### Fix Focus Areas
- testing/healthcheck_seed/04_session_blocked.sql[1-1]
- testing/healthcheck_seed/README.md[16-20]

### Suggested change
Add SQL to:
- connect/run in a separate session
- `BEGIN;`
- attempt `UPDATE pgfirstaid_seed.lock_target SET payload = 'blocked_by_session_b' WHERE id = 1;` (this should block)
- optionally include comments instructing the user to keep the session open until validation completes, then `ROLLBACK;`

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Idle-in-txn script not idle 🐞 Bug ≡ Correctness
Description
05_session_idle_in_transaction.sql runs SELECT pg_sleep(360), which keeps the session in an active
query rather than the required pg_stat_activity.state='idle in transaction'. As a result, it won't
trigger the 'Idle In Transaction Over 5 Minutes' check when used per the seed kit README.
Code

testing/healthcheck_seed/05_session_idle_in_transaction.sql[1]

+SELECT pg_sleep(360);
Evidence
The script only executes pg_sleep, which makes the server consider the session 'active'. The
pgFirstAid check filters specifically for sessions in state 'idle in transaction' for more than 5
minutes, so this script does not meet the check’s predicate.

testing/healthcheck_seed/05_session_idle_in_transaction.sql[1-1]
pgFirstAid.sql[1103-1130]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The idle-in-transaction seed script runs `SELECT pg_sleep(360)`, which creates an active query instead of an idle-in-transaction session.

### Issue Context
The check requires `pg_stat_activity.state = 'idle in transaction'` for >5 minutes.

### Fix Focus Areas
- testing/healthcheck_seed/05_session_idle_in_transaction.sql[1-1]
- testing/healthcheck_seed/README.md[16-21]

### Suggested change
Convert the script to a psql-friendly sequence that leaves an open transaction with no active query, e.g.:
- `BEGIN;`
- `SELECT 1;`
- `\echo 'Holding idle-in-transaction...';`
- `\sleep 360` (client-side sleep keeps server session idle)
- `ROLLBACK;`
Document that it should be run via `psql -f` (psql meta-commands required).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Advisory comments

4. pgbench workload file empty 🐞 Bug ⚙ Maintainability
Description
The seed kit README instructs running pgbench with 07_pgbench_active_query.sql to create a
high-connection burst, but the file contains no statements so it won't generate the intended
workload. This undermines the documented approach for manually exercising the 'High Connection
Count' scenario (which seed_and_validate.py explicitly does not seed).
Code

testing/healthcheck_seed/07_pgbench_active_query.sql[R1-6]

+-- Use with pgbench to create many active connections.
+-- Example:
+--   pgbench "$PGDATABASE" -n -c 55 -j 10 -T 120 -f testing/healthcheck_seed/07_pgbench_active_query.sql
+
+
+
Evidence
README references the file as the script pgbench should execute, but the file is empty (comments
only). The main harness also documents that 'High Connection Count' is intentionally not seeded,
implying pgbench is the intended manual mechanism and should be functional.

testing/healthcheck_seed/README.md[21-25]
testing/healthcheck_seed/07_pgbench_active_query.sql[1-6]
testing/seed_and_validate.py[530-537]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`07_pgbench_active_query.sql` is empty, so running pgbench with `-f` on this file will not execute meaningful SQL.

### Issue Context
The seed kit README positions this as the way to create a high-connection workload burst.

### Fix Focus Areas
- testing/healthcheck_seed/07_pgbench_active_query.sql[1-6]
- testing/healthcheck_seed/README.md[21-25]

### Suggested change
Add a simple, safe statement for pgbench to run repeatedly, e.g.:
- `SELECT pg_sleep(1);` (keeps sessions active)
(or `SELECT 1;` if “active” is not required), and update README wording if necessary to clarify the intended session state.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

CI Feedback 🧐

A test triggered by this PR failed. Here is an AI-generated analysis of the failure:

Action: PG15 integration

Failed stage: Setup Python [❌]

Failed test name: ""

Failure summary:

The action failed during the Python setup step because the workflow requested Python 3.14 (x64), but
that version is not available in the GitHub Actions runner cache or supported for the current
operating system.
The failure is reported by actions/setup-python:
- Version 3.14 was not found in
the local cache
- ##[error]The version '3.14' with architecture 'x64' was not found for this
operating system.

Relevant error logs:
1:  Runner name: 'pgfirstaid'
2:  Runner group name: 'Default'
...

114:  freethreaded: false
115:  env:
116:  PGHOST: ***
117:  PGPORT: ***
118:  PGUSER: ***
119:  PGPASSWORD: ***
120:  PGDATABASE: ***
121:  PGSSLMODE: require
122:  PGFA_TEST_VIEW_MODE: managed
123:  PGFA_TEST_ACTIVE_CONN_TARGET: 52
124:  PGFA_TEST_ACTIVE_CONN_SLEEP_SECONDS: 20
125:  PGFA_TEST_WAIT_TIMEOUT_SECONDS: 45
126:  ##[endgroup]
127:  ##[group]Installed versions
128:  Version 3.14 was not found in the local cache
129:  ##[error]The version '3.14' with architecture 'x64' was not found for this operating system.
130:  The list of all available versions can be found here: https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json

Design doc for the Python orchestration script that seeds all pgFirstAid
health checks and validates each one fires in a throwaway test database.
…case

pgfirstaid_pg_stat_statements_checks() now wraps all RETURN QUERY statements
in a begin/exception block to gracefully handle ObjectNotInPrerequisiteState
when the extension is installed but not in shared_preload_libraries.

seed_and_validate.py gains is_pss_queryable() to detect this condition,
tracks pss_extension_installed separately from pss_seeded, and re-verifies
PSS accessibility before validation to guard against connection state changes
during the 6-minute session wait. run_validation() skips the "Extension Missing"
check when the extension is installed but not queryable.
@randoneering randoneering force-pushed the feature/load_testing branch from bf91118 to ec5e7fc Compare April 21, 2026 01:37
Comment thread testing/seed_and_validate.py Outdated
Comment thread testing/seed_and_validate.py
Comment thread pgFirstAid.sql
Installs view_pgFirstAid_managed.sql and queries v_pgfirstAid instead
of pg_firstaid() when --managed is passed. Allows validating the managed
service path (no superuser-only queries) with the same seed data.
@randoneering randoneering force-pushed the feature/load_testing branch from ec5e7fc to 56856c8 Compare April 21, 2026 01:52
@randoneering
Copy link
Copy Markdown
Owner Author

Will create a new branch to fix runner.

@randoneering randoneering merged commit acf8a2c into main Apr 21, 2026
0 of 4 checks passed
@randoneering randoneering deleted the feature/load_testing branch April 21, 2026 03:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant