Full-suite pytest collision: RLS tests incompatible with function-scoped test_db fixture #144
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Running
pytest tests/end-to-end produces ~75 failures + ~96 errors, almost entirely caused by a fixture-scope collision betweentest_rls_isolation.pyand every other test file's function-scopedtest_dbfixture. Phase 9 did not introduce this — it was present before and was flagged during the Phase 9 Task 14 full-suite run as a blocker for usingpytest tests/as a single regression gate.Repro
Observed on
feat/flowpilot-migrationatd386d11:935 passed, 75 failed, 96 errors. Running individual files (pytest tests/test_foo.py) passes cleanly.Root cause
Two incompatible test-setup strategies live in the same suite:
backend/tests/conftest.pytest_dbfixture (function scope, used by most tests):DROP SCHEMA public CASCADE; CREATE SCHEMA public; Base.metadata.create_all(...). Creates tables from SQLAlchemy metadata only — no RLS policies, no DB roles, no migration-managed grants.backend/tests/test_rls_isolation.py_ensure_rls_schemafixture (module scope): runsalembic upgrade headonce to set up the full migration-managed schema including RLS policies and per-tenant DB roles. Then uses an_ADMIN_DSNasyncpg connection plus per-tenant role DSNs for ownership checks.Collisions that trigger the failures:
_ADMIN_DSN/ role DSNs not configured in the test env → every test intest_rls_isolation.pyerrors at connect time (matches the 8+ errors clustered in that file).test_dbfixture runs before an RLS test picks back up, the Alembic-managed schema has been replaced with the bare-metadata version. Roles that were created by migrations may still exist at the DB level (roles are per-cluster, not per-schema), but policies attached to now-dropped tables are gone, so subsequent queries in RLS tests see inconsistent state.test_trees.py,test_uploads.py, etc.) → the pre-existing test order can leave the DB in a half-state where a later non-RLS test sees lingering role grants or missing seed data, surfacing as errors rather than assertion failures.Why this didn't show up earlier
pytest-xdistworker-per-file), sidestepping cross-file schema state. The collision only appears when a singlepytest tests/invocation walks all files in one process.Options for fixing
In rough order of scope:
test_rls_isolation.pyfrom the default collection when run alongside others — e.g.,pytest tests/ --ignore=tests/test_rls_isolation.pyas the default, with RLS tests run in a separate CI step. Unblocks the full-suite regression gate immediately.conftest.py'stest_dbfixture to usealembic upgrade headinstead ofBase.metadata.create_all, so every test gets the migration-managed schema (RLS policies + roles + seeds). ~100 tests depend on this fixture; they should continue to work if the migration set covers everythingcreate_alldoes (which it should). Risk: slower fixture setup; may surface other latent bugs.Option 1 is the right default until someone has bandwidth for Option 2.
Impact
pytest tests/is not currently a valid full-suite gate. Contributors who try to verify "nothing broke" by running the full suite will see a noisy failure report and may incorrectly attribute those failures to their own change.Not a code bug, but tooling debt worth tracking.
Closing as duplicate of #145 (which has the post-mortem and resolution detail). Fixed in
dab740d+b14a16a.