Repository navigation
api/*.py consumers still bind MagicMock code_intelligence stubs at import time (precommit_analyzer, redis_optimizer, anti_pattern_detector) #13181
Description
Activity
- addedarea: code-intelligenceWave 5 · cluster I — Code intelligence & analyticsWave 5 · cluster I — Code intelligence & analytics
on Sep 1, 2026 A fourth instance tonight, and it arrived from the opposite direction — which is the part that generalises this issue beyond the three consumers in the table.
New instance (#17342). #14881 collapsed four hand-copied enums, and the natural collapse target looked like the engine that owns the vocabulary:
# api/schemas_analytics.py from code_intelligence.code_review_engine import ReviewCategory, ReviewSeverity from code_intelligence.precommit_analyzer import CheckCategory, CheckSeverityautobot-backend/conftest.py:810stubs the wholecode_intelligencepackage, so those four names are MagicMock attributes under the harness. A pydantic field annotated with a MagicMock has no schema, and the import dies:pydantic.errors.PydanticSchemaGenerationError: Unable to generate pydantic-core schema for <MagicMock id='139795823827936'>42 tests failed, every one citing the same mock id —
api.metrics,api.rum,api.usage,api.knowledge_vectorizationand the rest, all of which merely reachschemas_analyticstransitively. One leaked object, 42 red tests, and nothing wrong with the production code.Why this is not just a fifth row in the table. The existing entries are pre-existing consumers that were left poisoned. This one was created by a refactor that was correct by every rule we enforce: it removed duplication, it imported rather than redeclared, and the enum guard added in the same PR passes. Nothing in the repo told the author that
code_intelligenceis a boundary production code must not import across. The stub list is in a conftest; the import is in an API module; the two never meet until CI.So the rule this issue should carry, stated once:
A module stubbed wholesale in a conftest is a dependency boundary. Production code may not import a symbol across it. The stub cannot simply be removed —
code_intelligence/__init__.pycarries Python-3.10-only syntax, which is why it is stubbed at all.The fix that works, for the next person who hits it. Move the shared vocabulary to a leaf nothing stubs, rather than trying to un-stub the engine. For #17342 that is a new
autobot_shared/code_review_enums.pywith the four enums and three importers. Verified:grep -rn "from code_intelligence" autobot_shared/is empty so there is no cycle, andautobot_shared.status_enumsalready resolves to real enums on this exact import path today. Notstatus_enums.pyitself — it is 559 lines against the 600MAX_LINESceiling.Same class, different stub — worth linking here rather than filing twice. #17343 is this defect with
testkit/auth_middleware_stub.py:194replacingauth_middlewarewholesale: an unauthenticated-access test returned 501 instead of 401 because the stub'scheck_admin_permissiontakes noRequestand refuses nothing. The naive assertion (!= 200) passes, so that one produces a green security test that never touched an auth gate.That is the direction worth flagging loudest. This issue's four instances all fail loudly. #17343 shows the same root cause can pass silently, and a harness stub that makes a test pass for the wrong reason is strictly more dangerous than one that makes 42 fail for the right one.
conftest.py:786-802already records a third variant in its own comments — stubbingorchestration.causal_error_recoverygaveTestCausalValidatora mock whose.validwas truthy, so 2 tests failed outright and 3 silently asserted nothing.Three of the four known variants therefore include a silent-pass mode. Any fix here should be judged on whether it catches those, not only the import-time explosions.
Summary
Discovered while fixing #13111 (PR #13180). #13111 fixed the test-file side of the
code_intelligenceMagicMock-stub poisoning. The consumer side is still poisoned for three modules: severalapi/*.pyroute modules bind mocked symbols from the top-level conftest'scode_intelligence.*stubs into their own module globals at import time, and unlikemerge_conflict_resolver(fixed in #13180) they were left alone because the blast radius could not be measured without a full-suite run.Affected
autobot-backend/api/analytics_precommit.py:37-40BUILTIN_CHECKS,CheckDefinition,CheckResult,PrecommitAnalyzerfromcode_intelligence.precommit_analyzerBUILTIN_CHECKS = {cid: _engine_check_to_schema(c) for cid, c in _ENGINE_BUILTIN_CHECKS.items()}evaluates to{}— iterating aMagicMockyields nothing, so the module's entire check catalog is silently empty in every testautobot-backend/api/code_intelligence.py:79OptimizationSeverity,RedisOptimizerfromcode_intelligence.redis_optimizerRedisOptimizer(...)calls operate on mocksautobot-backend/api/code_intelligence.py:70,api/codebase_analytics/analyzers.py:32AntiPatternDetector& co. fromcode_intelligence.anti_pattern_detectoroptional_import"succeeds" against the stubThe subdirectory fix in #13180 (
code_intelligence/conftest.pyreal-loads) cannot reach these: pytest collectsapi/beforecode_intelligence/, so those globals are already bound by the time that conftest runs. The fix has to be a top-level_real_load_and_bind(...)inautobot-backend/conftest.py, exactly as done there formerge_conflict_resolver.Why it was not bundled into #13180
All three are import-light (statically verified: stdlib +
autobot_shared.logging_manager+utils.line_index, no chromadb/torch/redis/fastapi), so a top-level real-load is mechanically the same one-liner. What is unknown is whether any currently-passingapi/test depends on the mocked binding.api/analytics_precommit_test.pydoes not exist, and no test file importsapi.code_intelligence, so the likely delta is small — but it needs a full-suite run to confirm, which #13180 could not do.Secondary finding: pre-existing vacuous assertions
code_intelligence/llm_code_generator_test.py::TestModuleImports::test_import_from_code_intelligenceandcode_intelligence/llm_pattern_analyzer_test.py::TestModuleImports::test_all_exports_importabledofrom code_intelligence import Xandassert X is not None.code_intelligenceitself is a package stub whose catch-all__getattr__returns aMagicMock, so these assertions pass unconditionally and would pass even if the symbol did not exist.llm_code_generator_test.py::test_all_exports_accessibleis literallyassert True.This predates #13111 and was deliberately left unchanged there (fixing it needs the real
code_intelligence/__init__.py, which is the heavy import chain the stub system exists to avoid). Either bind the real submodules' public symbols onto the stub namespace, or delete these three assertions as non-tests.Verification steps
precommit_analyzer,redis_optimizer,anti_pattern_detectorfrom the top-level stub lists to_real_load_and_bind(...)calls (keeping thecode_intelligence/conftest.pyentries harmless —_real_load_and_bindno-ops on an already-real module from the same file, per bug(tests): 2 pre-existing failures in test_claim_classifier — duplicate module identity breaks isinstance + MagicMock awaited #12839).api/.api.analytics_precommit.BUILTIN_CHECKSis non-empty under test.Related: #13111, PR #13180, #9856, #12362, #12421, #12437, #12438, #12686