Surfaced while reviewing PR #54's test/install/load.sql TEST_SCHEMA guard.
Why TEST_SCHEMA exists at all (this took a couple of rounds to nail down, recording it here so the design intent doesn't get lost again): prove cat_tools works correctly even when the cat_tools schema itself is NEVER part of the active search_path. A real deployment commonly keeps a tooling extension's schema deliberately off every role's default search_path (so it never shadows anything, and callers must always schema-qualify it) -- if cat_tools' own internal SQL (views/functions referencing each other) secretly relied on unqualified name resolution somewhere, it would keep working by accident in every ordinary fresh-install test run (which never touches search_path) and only break in that realistic deployment. test/install/load.sql's TEST_SCHEMA mechanism now (as of the fix landing alongside this issue) actually exercises that: it creates an unrelated schema, makes it the ONLY entry on search_path, runs CREATE EXTENSION cat_tools WITH SCHEMA cat_tools explicitly against that, and asserts cat_tools never resolves via search_path anyway.
The gap this issue is actually about: that check happens once, at install time, under the superuser connection load.sql always runs as (confirmed: test/roles.sql never does SET ROLE/SET SESSION AUTHORIZATION). It can't catch anything privilege-related, since USAGE-based search_path filtering only kicks in for a non-superuser role -- and this suite's actual security-relevant role switching (SET LOCAL ROLE :"use_role") happens per test file, inside test/sql/*.sql, not in load.sql.
The real invariant worth checking lives at the test-file level, under the actual restricted role each test runs as -- e.g. that cat_tools' functionality doesn't silently depend on some particular schema being on search_path, checked under the SAME restricted-privilege conditions real callers would have (not the superuser load.sql runs as). Design intentionally left open for whoever picks this up.
Structural constraints to design around (from discussion, not yet solved):
- Can't live in
test/deps.sql: that file runs before each test file's own plan() call, and pgTAP requires plan() to be the first thing a test file does.
- So it needs to be a shared helper (SQL function or similar) that each test file calls explicitly, after its own
plan(), contributing a fixed, known count to that file's plan total (same pattern already used for one-off assertions like extension.sql's "+ 1 -- view count sanity").
- Output must be deterministic/constant across environments and roles, since this suite's expected-output files are compared byte-for-byte (pg_regress diffing) -- any environment-dependent value (actual schema names, role names, ordering) needs to be normalized or asserted in a way that doesn't leak into the expected file.
Not being tackled now -- parking the idea here for later design.
Surfaced while reviewing PR #54's
test/install/load.sqlTEST_SCHEMA guard.Why TEST_SCHEMA exists at all (this took a couple of rounds to nail down, recording it here so the design intent doesn't get lost again): prove cat_tools works correctly even when the
cat_toolsschema itself is NEVER part of the activesearch_path. A real deployment commonly keeps a tooling extension's schema deliberately off every role's defaultsearch_path(so it never shadows anything, and callers must always schema-qualify it) -- if cat_tools' own internal SQL (views/functions referencing each other) secretly relied on unqualified name resolution somewhere, it would keep working by accident in every ordinary fresh-install test run (which never touchessearch_path) and only break in that realistic deployment.test/install/load.sql's TEST_SCHEMA mechanism now (as of the fix landing alongside this issue) actually exercises that: it creates an unrelated schema, makes it the ONLY entry onsearch_path, runsCREATE EXTENSION cat_tools WITH SCHEMA cat_toolsexplicitly against that, and assertscat_toolsnever resolves viasearch_pathanyway.The gap this issue is actually about: that check happens once, at install time, under the superuser connection
load.sqlalways runs as (confirmed:test/roles.sqlnever doesSET ROLE/SET SESSION AUTHORIZATION). It can't catch anything privilege-related, since USAGE-basedsearch_pathfiltering only kicks in for a non-superuser role -- and this suite's actual security-relevant role switching (SET LOCAL ROLE :"use_role") happens per test file, insidetest/sql/*.sql, not inload.sql.The real invariant worth checking lives at the test-file level, under the actual restricted role each test runs as -- e.g. that cat_tools' functionality doesn't silently depend on some particular schema being on
search_path, checked under the SAME restricted-privilege conditions real callers would have (not the superuserload.sqlruns as). Design intentionally left open for whoever picks this up.Structural constraints to design around (from discussion, not yet solved):
test/deps.sql: that file runs before each test file's ownplan()call, and pgTAP requiresplan()to be the first thing a test file does.plan(), contributing a fixed, known count to that file's plan total (same pattern already used for one-off assertions likeextension.sql's "+ 1 -- view count sanity").Not being tackled now -- parking the idea here for later design.