test(checks): fix cross-test pollution in test_checks.py (#1741)#1743
Conversation
…ttr on module restore (#1741) The block_watchdog fixture in test_dev_server_watchdog_missing.py evicts and re-imports djust.checks / djust.dev_server to exercise the no-watchdog import path (#994). A re-import rebinds BOTH sys.modules["djust.checks"] AND the parent-package attribute djust.checks. The fixture restored only sys.modules, leaving the two pointing at different module objects. A later cross-dir test (python/tests/test_checks.py) then did `from djust import checks` (resolves the package attribute -> module A) and `from djust.checks import check_configuration` (resolves sys.modules -> module B). monkeypatch.setattr(checks, "_has_asgi_server", ...) patched A, but check_configuration's globals belonged to B, so the patch silently no-op'd and C003 deterministically failed (0 == 1) under the cited order: pytest python/djust/tests/ python/tests/test_checks.py Fix: snapshot + restore the parent-package attribute alongside sys.modules so the two stay consistent after teardown. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Stage 11 Adversarial Review — PR #1743 (fix #1741 cross-test pollution)Verdict: 🟢 APPROVE. Test-only fix, root cause correctly diagnosed, independently reproduced pre-fix / confirmed post-fix, 9/9 clean pollution-gate runs, restore is complete and order-correct, no second/latent polluter found. Mandatory pre-review checks
Load-bearing empirical reproduction (ran independently)
3-clean-runs pollution gate (re-ran myself, did not trust the report)
Second-/latent-polluter probe
Restore correctness (#1741 invariant)
Module-cache audit completeness (#1741 "stop the class recurring")
CHANGELOG
No 🔴, no 🟡. Recommend merge. |
Retrospective — PR #1743 (pipeline-drain v1.0.2-3)Task: v1.0.2-3 — #1741 (Action Tracker #289): fix 2 deterministic test_checks.py pollution failures. Quality: 5/5Pollution proven by causation (not guessed), narrowest fix, 3-clean-runs gate honored, reviewer independently reproduced both directions. What went well
What didn't
Process findingThe module re-import / Verified
|
Summary
Fixes #1741 — two tests in
python/tests/test_checks.pyfailed deterministically under cross-test ordering but passed in isolation:TestC003DaphneOrdering::test_c003_daphne_missing_infoTestSuppressChecks::test_no_suppress_by_defaultBoth failed with a polluted C003 assertion (
0 == 1— C003 did not fire despite all firing conditions being met).Proven polluter + mutated global state
Polluter:
python/djust/tests/test_dev_server_watchdog_missing.py— specifically theblock_watchdogfixture, exercised bytest_check_hot_view_replacement_survives_without_watchdog, which evicts and re-importsdjust.checks(anddjust.dev_server) to test the no-watchdog import path (#994).Mutated state (proven, not guessed): a re-import via
importlib.import_module("djust.checks")rebinds bothsys.modules["djust.checks"]and the parent-package attributedjust.checks. The fixture restored onlysys.modules, leaving the two pointing at different module objects.Diagnostic probe in the failing order showed:
So the failing test did:
from djust import checks→ resolves the package attribute → module A (which the fixture restored)from djust.checks import check_configuration→ resolvessys.modules→ module B (the stale re-imported copy)monkeypatch.setattr(checks, "_has_asgi_server", lambda: False)patched A, butcheck_configuration's globals belonged to B, so the patch silently no-op'd → the real_has_asgi_server()returnedTrue→ C003 was suppressed →len(c003) == 0.Reproduced failing order
(Also surfaces intermittently in CI shards / xdist, per #1736/#1739 Stage-11 reviewers.)
Isolation fix
In
block_watchdog(function-scoped fixture): snapshot and restore the parent-package attribute (djust.checks,djust.dev_server) alongsidesys.modules, so the two stay consistent after teardown andfrom djust import checks/from djust.checks import Xresolve the same object. Test-only change; no runtime behavior change.Module-cache / registry audit (recurring class)
@register("djust")into Django's global registry): the actual tech-debt: fix 2 pre-existing test-pollution failures in test_checks.py + audit module-level caches for test-reset #1741 cause was the module re-import desyncing the registered-check module copies — fixed by the package-attr restore above. ✓_route_map_cache(routing.py:27, has_reset_route_map_cache()): already wired into autouse reset fixtures in both tests that mutate it —test_route_map_derivation.py(autouse_reset_state) andtest_client_config_tag.py(autouse_reset_route_map). No gap. ✓Verification
Gate-off (#254): reverted the fix (
git stash) → the exact 2 tests fail again; restored → pass. The fix is confirmed to be what resolves the failure.3-clean-runs gate (pollution-class, mandatory):
test_checks.py3× consecutive →204 passedeach. ✓python/djust/tests/ python/tests/3× consecutive →5471 passed, 9 skippedeach. ✓-n autocross-dir →5471 passed, 9 skipped. ✓python/djust/tests/ python/tests/test_checks.py→3296 passed. ✓Closes #1741
🤖 Generated with Claude Code