fix(tests): stop poisoning sys.modules session-wide with a bare MagicMock

Three test files (test_llm_scoping, test_fix_meshcore_save_and_llm_test,
test_config_partial_save_merge) did `sys.modules.setdefault(_mod, MagicMock())`
to stub an optional import. `setdefault` installs the MagicMock into
sys.modules for the ENTIRE pytest session even when the real package is
present — so any LATER test that does `await <that module>.<coro>(...)`
(e.g. `await aiosqlite.connect(...)`) fails with
"object MagicMock can't be used in 'await' expression" / "Event loop is closed".

This is what made test_fire_tracker_phase4::test_natural_language_fire_
question_routes_to_llm pass in isolation but fail in full-suite order — the
leak came from an earlier file, not the victim. #140 hardened the victim's
own config/history isolation but couldn't fix an external sys.modules poison.

Fix: only fall back to the MagicMock when the real module genuinely fails to
import (guarded assignment), so a present package is never replaced. Root-cause
fix in the polluters, not a skip on the victim.

Suite: 2422 passed, 0 failed, 72 skipped — fully green (was 6 failed before
#140, then 1 order-dependent failure after). Confirmed deterministic across
repeated full runs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt Johnson 2026-07-17 20:24:28 +00:00
commit 2d359ea6f1
3 changed files with 48 additions and 5 deletions

View file

@ -32,6 +32,7 @@ omitted keys keep their live values while explicitly-sent keys still apply.
""" """
from __future__ import annotations from __future__ import annotations
import importlib
import sys import sys
from unittest.mock import MagicMock from unittest.mock import MagicMock
@ -41,8 +42,22 @@ from fastapi import FastAPI
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
# Stub heavy optional deps so config_routes imports without them. # Stub heavy optional deps so config_routes imports without them.
#
# Try the real import first -- sys.modules.setdefault() alone is only a
# no-op "in production where the real packages are installed" if some
# earlier-run test has already imported the real module. When this file
# collects first (test order is alphabetical, not guaranteed), setdefault()
# permanently replaces a genuinely-installed module (e.g. aiosqlite) with a
# bare MagicMock for the rest of the pytest session -- every later test's
# `await aiosqlite.connect(...)` then breaks with "MagicMock can't be used
# in 'await' expression". Only fall back to the mock when the real package
# truly isn't importable.
for _mod in ("openai", "aiosqlite", "anthropic", "google", "google.genai"): for _mod in ("openai", "aiosqlite", "anthropic", "google", "google.genai"):
sys.modules.setdefault(_mod, MagicMock()) if _mod not in sys.modules:
try:
importlib.import_module(_mod)
except ImportError:
sys.modules[_mod] = MagicMock()
from meshai.config import ( # noqa: E402 from meshai.config import ( # noqa: E402
Config, Config,

View file

@ -12,6 +12,7 @@ Bug 2 — POST /api/config/test-llm "string indices must be integers, not 'str'"
""" """
from __future__ import annotations from __future__ import annotations
import importlib
import sys import sys
from types import SimpleNamespace from types import SimpleNamespace
from unittest.mock import AsyncMock, MagicMock, patch from unittest.mock import AsyncMock, MagicMock, patch
@ -20,9 +21,23 @@ import pytest
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
# Stub heavy optional deps so config_routes can be imported without them. # Stub heavy optional deps so config_routes can be imported without them.
#
# Try the real import first -- sys.modules.setdefault() alone is only a
# no-op "in production where the real packages are installed" if some
# earlier-run test has already imported the real module. When this file
# collects first (test order is alphabetical, not guaranteed), setdefault()
# permanently replaces a genuinely-installed module (e.g. aiosqlite) with a
# bare MagicMock for the rest of the pytest session -- every later test's
# `await aiosqlite.connect(...)` then breaks with "MagicMock can't be used
# in 'await' expression". Only fall back to the mock when the real package
# truly isn't importable.
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
for _mod in ("openai", "aiosqlite", "anthropic", "google", "google.genai"): for _mod in ("openai", "aiosqlite", "anthropic", "google", "google.genai"):
sys.modules.setdefault(_mod, MagicMock()) if _mod not in sys.modules:
try:
importlib.import_module(_mod)
except ImportError:
sys.modules[_mod] = MagicMock()
# ========================================================================== # ==========================================================================

View file

@ -7,6 +7,7 @@ Covers:
(d) ContextConfig.max_age default is 14 days (1_209_600 seconds) (d) ContextConfig.max_age default is 14 days (1_209_600 seconds)
""" """
import importlib
import sys import sys
from unittest.mock import MagicMock from unittest.mock import MagicMock
@ -14,11 +15,23 @@ import pytest
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Stub optional heavy deps so meshai.router can be imported in this env. # Stub optional heavy deps so meshai.router can be imported in this env.
# These stubs are set before any meshai.router import in this process; #
# they are no-ops in production where the real packages are installed. # Try the real import first -- sys.modules.setdefault() alone is only a
# no-op "in production where the real packages are installed" if some
# earlier-run test has already imported the real module. When this file
# collects first (test order is alphabetical, not guaranteed), setdefault()
# permanently replaces a genuinely-installed module (e.g. aiosqlite) with a
# bare MagicMock for the rest of the pytest session -- every later test's
# `await aiosqlite.connect(...)` then breaks with "MagicMock can't be used
# in 'await' expression". Only fall back to the mock when the real package
# truly isn't importable.
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
for _mod in ("openai", "aiosqlite", "anthropic", "google", "google.genai"): for _mod in ("openai", "aiosqlite", "anthropic", "google", "google.genai"):
sys.modules.setdefault(_mod, MagicMock()) if _mod not in sys.modules:
try:
importlib.import_module(_mod)
except ImportError:
sys.modules[_mod] = MagicMock()
from meshai.config import ( # noqa: E402 from meshai.config import ( # noqa: E402
BotConfig, BotConfig,