Repository navigation
fix(tests): conftest re-executed already-loaded modules, breaking isinstance across the suite (#12839) - #12851
Merged
Merged
Conversation
… + spec the KB mock (#12839) Two independent defects behind the two failing claim-classifier tests. 1. Duplicate module identity. `services.claim_classifier` imports `.knowledge_grounding_models` during collection; conftest's `_real_load_light_services()` then re-executed the SAME file via `_real_load_and_bind`, building a second set of class objects and swapping them into sys.modules while claim_classifier kept referencing the first. Result: `isinstance(result, Claim)` False against an object whose repr says it IS a Claim. Confirmed with a stack probe showing both executions. Re-execution exists only to replace a MagicMock stub, so a module already real-loaded from that same path is now left alone — the parent bind is still (re)applied, since patch("pkg.mod.NAME") resolves via getattr(pkg, "mod") and skipping it would resurrect the inert-patch bug from #11532/#11618. 2. The KB fixture was a bare MagicMock(), which auto-creates every attribute. `ClaimClassifier._search_kb` prefers `search_async` when `hasattr` says it exists, so the classifier awaited a plain MagicMock ("object MagicMock can't be used in 'await' expression"), caught it, and returned no results — the test was asserting against a degraded path rather than the one it meant to exercise. `spec=["search"]` makes the hasattr probe answer honestly. Verification (conftest is loaded by every backend test, so measured against a pristine origin/Dev_new_gui worktree with an identical command): baseline: 14 failed, 5304 passed, 130 skipped branch: 12 failed, 5310 passed, 130 skipped failures only on this branch (regressions): NONE failures fixed: the two test_claim_classifier cases The remaining 12 failures are identical on both trees (gitlab/gitea connector tests needing a live Redis, etc.) and are untouched by this change. +6 passes = 2 fixed + 4 new tests pinning the loader invariant: an already-real module is not re-executed, an instance stays isinstance-valid across a repeat load, a stub IS still replaced, and the parent bind is applied on the skip path.
Contributor
✅ SSOT Configuration Compliance: Passing🎉 No hardcoded values detected that have SSOT config equivalents! |
Applied Black, isort, and autoflake to match code-quality checks. Triggered by workflow auto-fix-formatting.yml.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #12839
Thinking Path
Two failing tests, and I assumed one cause. There were two independent defects — fixing the obvious one left both tests still red, which is what forced the second investigation.
Defect 1 — the KB mock lied about its own interface.
ClaimClassifier._search_kbpicks its branch withhasattr:The fixture was a bare
MagicMock(), which auto-creates every attribute, sohasattr(kb, "search_async")was True and the classifier awaited a plain MagicMock —object MagicMock can't be used in 'await' expression. The classifier caught that, logged it, and returned no results, so the test was quietly asserting against a degraded error path instead of the one it meant to exercise.spec=["search"]makes the probe answer honestly.Defect 2 — the same module executed twice. Fixing the mock did not fix
isinstance, so the duplicate-identity theory needed proof rather than assumption. A stack probe inknowledge_grounding_models.pyduring a real pytest run showed both executions:_real_load_and_bindre-executes the file and swaps a fresh module intosys.modules, whileservices.claim_classifierkeeps holding the classes from the first execution. Henceisinstance(x, Claim)returning False for an object whose repr says it is aClaim, with only onesys.modulesentry to inspect.Re-execution exists solely to replace a MagicMock stub, so an already-real module loaded from that same path is now left alone. The parent bind is still (re)applied on that skip path —
patch("pkg.mod.NAME")resolves viagetattr(pkg, "mod"), and dropping it would resurrect the inert-patch bug from #11532/#11618. That case has its own test.What Changed
conftest.py—_real_load_and_bindreturns early whensys.modules[name].__file__already matches the target path, after re-applying the parent bind.tests/services/test_claim_classifier.py— KB fixture usesMagicMock(spec=["search"]).tests/test_conftest_real_load_identity.py(new) — pins the loader invariant.Verification
conftest.pyis loaded by every backend test, so this was measured against a pristineorigin/Dev_new_guiworktree using an identical command:The 12 remaining failures are byte-identical on both trees (gitlab/gitea connector tests requiring a live Redis, etc.) and are untouched here. The +6 passes are the 2 fixed tests plus 4 new ones covering: an already-real module is not re-executed, an instance stays
isinstance-valid across a repeat load, a stub is still replaced by the real module, and the parent bind is applied on the skip path.A note on the first verification attempt, since it nearly produced a false conclusion: running
tests/ llc/tests/together reported "Interrupted: 49 errors during collection", which looked like this change breaking the suite. The identical command on the pristine baseline produced exactly the same 49 errors — pre-existing, triggered by combining those two paths in one invocation, and it meant nothing had actually executed. The numbers above come from a subset that collects cleanly on both trees.Model Used
Claude Opus 5