Skip to content

test(denies): give each xdist worker its own deny principal - #823

Merged
axellpadilla merged 2 commits into
dbt-msft:masterfrom
Benjamin-Knight:fix/denies-test-principal-isolation
Aug 19, 2026
Merged

test(denies): give each xdist worker its own deny principal#823
axellpadilla merged 2 commits into
dbt-msft:masterfrom
Benjamin-Knight:fix/denies-test-principal-isolation

Conversation

@Benjamin-Knight

Copy link
Copy Markdown
Collaborator

Problem

The denies suite failed non-deterministically under pytest -n auto — which is how
make functional runs it. Six of the eight tests, different ones each time. It passed
reliably single-process.

Cause

The suite created and dropped one database-wide user, dbt_deny_reader, from a
class-scoped fixture shared by all eight test classes. A database principal is
database-scoped: unlike project.test_schema, it is not isolated per test. xdist
spreads the classes across worker processes sharing one TestDB, so the first class to
finish dropped the user out from under every class still running, taking its
sys.database_permissions rows with it.

Nothing failed loudly at that point, because apply_denies is deliberately warn-and-skip
on an absent principal. The rebuild succeeded having applied no DENY, and the assertions
saw an empty set:

assert ('SELECT', 'dbt_deny_reader') in set()

Fix

Derive the principal name from PYTEST_XDIST_WORKER (dbt_deny_reader_gw3, or _main off
xdist). Classes on one worker run sequentially, so per-worker names are enough to keep
setup and teardown from overlapping.

The model templates carry a __DENY_PRINCIPAL__ placeholder swapped at import via a small
with_principal() helper. str.format and f-strings are unusable on them — they're Jinja,
and {{ config(...) }} would be eaten as an escaped brace.

Test-only: no adapter behaviour changes.

Benjamin-Knight and others added 2 commits August 19, 2026 05:17
The denies suite created and dropped one database-wide user,
`dbt_deny_reader`, from a class-scoped fixture shared by all eight test
classes. A database principal is database-scoped: unlike
`project.test_schema` it is not isolated per test. Under `pytest -n auto`
(how `make functional` runs) the classes are spread across worker
processes sharing one TestDB, so the first class to finish dropped the
user out from under every class still running, taking its
sys.database_permissions rows with it.

Nothing failed loudly at that point, because apply_denies is
deliberately warn-and-skip on an absent principal: the rebuild succeeded
having applied no DENY, and the assertions saw an empty set --
`assert ('SELECT', 'dbt_deny_reader') in set()`. Six of the eight tests
failed this way, non-deterministically and only under xdist.

Derive the principal name from PYTEST_XDIST_WORKER instead. Classes on
one worker run sequentially, so per-worker names are enough to keep
setup and teardown from overlapping. The model templates carry a
`__DENY_PRINCIPAL__` placeholder swapped at import; str.format and
f-strings are unusable on them because they are Jinja and `{{ config() }}`
would be eaten as an escaped brace.

Test-only: no adapter behaviour changes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@axellpadilla
axellpadilla merged commit 3d731ee into dbt-msft:master Aug 19, 2026
20 checks passed
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.

2 participants