Skip to content

test(slm): test_health_redis_14299 fails on base — MagicMock reaches inet_pton #14535

Closed

Description

@mrveiss

Summary

tests/api/test_health_redis_14299.py::TestCheckRedisHealth::test_healthy_when_client_available fails on origin/Dev_new_gui.

501 passed, 1 failed
ERROR autobot_shared.redis_management.connection_manager: Error checking Redis readiness
      for 'main': inet_pton() argument 2 must be str, not MagicMock
ERROR autobot_shared.retry_mechanism: create_async_pool[main] failed after 5 attempts

Verified as pre-existing by running the same suite at base in a clean worktree, not only on a feature branch.

What it looks like

The mock supplies a MagicMock where the readiness check expects a host string, so inet_pton rejects it, the pool creation retries five times and exhausts, and the "healthy when a client is available" case never reaches its assertion. So the test is exercising the retry path rather than the health path it names.

Why it matters beyond the one test

The Python suite is not a required check, so a red test here does not block a merge and can sit indefinitely. That is the same shape this umbrella keeps finding: a signal that exists, is failing, and is not surfaced anywhere that stops work.

Work

  • Give the mock a real host string so the readiness check reaches the branch under test
  • Confirm the test then fails when the health logic is broken — it currently cannot distinguish that from the mock defect

Acceptance

The test passes on base and fails when check_redis_health stops reporting healthy for an available client.

Related: #13852, #14299.

Activity

  1. mrveiss commented on Aug 18, 2026

    @mrveiss
    OwnerAuthor

    Correcting this issue — it is test pollution, not a broken mock

    I filed this as "the mock supplies a MagicMock where a host string is expected". That is wrong, and the correction matters for whoever picks it up.

    The test passes in isolation. pytest tests/api/test_health_redis_14299.py → 6 passed. It only fails as part of the wider directory.

    The interference is tests/api/test_auth_logout.py, found by bisecting the 38 files in tests/api/. The pair alone reproduces it:

    pytest tests/api/test_auth_logout.py \
           tests/api/test_health_redis_14299.py::TestCheckRedisHealth::test_healthy_when_client_available
    -> 1 failed, 10 passed
    

    The actual failure is assert 'unhealthy' == 'healthy', with this in the captured log:

    PoolConfig received non-numeric value(s) — falling back to defaults:
      max_connections=<MagicMock name='mock.config.get_redis_config().get()'> ...
    Error checking Redis readiness for 'main': inet_pton() argument 2 must be str, not MagicMock
    

    So a MagicMock-derived Redis config reaches PoolConfig, readiness fails, and _check_redis_health correctly returns unhealthy. The health code and its mock are both fine — they are being handed a poisoned Redis configuration.

    What I ruled out

    test_auth_logout installs MagicMocks into sys.modules for its bootstrap (line 78) and does restore them afterwards (lines 126-134), so the obvious explanation is not it. Probing after importing that module:

    • sys.modules entries for config, autobot_shared.ssot_config, autobot_shared.redis_client and redis_management.connection_manager — all clean
    • ssot_config.get_config's lru_cache — holds a real config, not a mock
    • connection_manager._config_manager — None
    • a sweep of every loaded autobot_shared / services / api / config module for module-level MagicMock attributes — 0 hits

    I also tried the obvious fix, adding RedisConnectionManager.reset_instance() to that module's restore block — its docstring cites exactly this scenario (#11830, "a singleton constructed under test-stubbed config caches the stub"). It does not fix the failure, so I reverted it rather than leave an ineffective change in the tree.

    Where that leaves it

    Nothing is poisoned at import time, so the poisoning happens while test_auth_logout's tests execute — its own per-test patching, or something constructed lazily on first use afterwards. That is the next place to look, and it is a narrower target than when this issue was filed.

    Severity, restated

    Lower than the original text implies. The health logic is correct and its own tests pass; this is an ordering artifact. It still matters because the Python suite is not a required check, so a red test here does not block anything and can sit indefinitely — but it is not evidence of a health-check defect.

  2. mrveiss commented on Aug 19, 2026

    @mrveiss
    OwnerAuthor

    Delivered by PR #14576, squash-merged as f1c42c7e7 and verified in origin/Dev_new_gui.

    The issue was filed as a broken mock. It was test pollution, and the chain is worth recording — I verified all six links against real pytest and CPython behaviour rather than accepting the narrative:

    1. services/token_denylist.py imports autobot_shared.redis_client transitively, pulled in by test_auth_logout.py's bootstrap.
    2. The bootstrap's restore loop was unconditional — for _k in list(sys.modules): if _k not in _PRE_BOOTSTRAP_MODULES: del sys.modules[_k] — so it deleted a real module it never stubbed.
    3. Deleting the sys.modules entry leaves the parent package's .redis_client attribute bound to the orphan.
    4. _pytest/monkeypatch.py:resolve() does getattr on the parent before any import fallback, so the patch landed on that orphan.
    5. _check_redis_health's function-local import then re-imported fresh and unpatched.
    6. The root conftest MagicMocks config session-wide, so redis_config.get("host") returned a MagicMock — which is what reached inet_pton.

    Link 4 is the non-obvious one and it checks out in the installed pytest source. Scoping the restore to _BOOTSTRAP_MANAGED_MODULES fixes the cause rather than the symptom.

    A second defect surfaced in CI and is worth its own note: the auto-stub built MagicMock stubs with no __spec__. NonCallableMock.__getattr__ raises AttributeError for unconfigured dunder names, and CPython's _find_and_load_unlocked reads parent_module.__spec__ unguarded — only the neighbouring __path__ read is wrapped — so it broke whenever a stub served as an import parent. It reproduces on Python 3.14 (what the shards run) and not on 3.10, which is why local runs were clean. Reproduced on 3.14 directly with a dependency-free harness rather than inferred from the traceback.

    Base-verified: ModuleSpec present, 12/12 shards green, and nothing added to sys_modules_leak_baseline.txt — adding a line there to go green was the one move that would have made this worse.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtesting

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions