Skip to content

test_staleness × pact_context cache pollution leak (importlib.reload workaround; PR #641 TEST follow-up) #650

Description

@michael-wojcik

Background

PR #641 TEST-phase surfaced a test-infra debt: test_staleness.py and pact_context.py interact through a module-level cache that bleeds across tests. The pollution requires importlib.reload(staleness) workarounds in test fixtures to isolate state, surfaced during the post-#628 coverage adversarial review.

Secretary Pass-2 consolidation flagged this as outstanding test-infra debt (recommendation #4).

Symptom

When test_staleness.py runs in the same pytest session as a test that imports pact_context.py, the module-level cache in pact_context retains state from a prior test's setup. Tests that depend on a fresh cache must:

import importlib
import pact_context
import staleness

def test_with_clean_state():
    importlib.reload(pact_context)  # workaround for cache pollution
    importlib.reload(staleness)
    # ...

Without reload, tests pass in isolation but flake when ordering changes.

Root cause hypothesis

pact_context.py likely uses a module-level dict / singleton-ish pattern for context state (legitimate runtime optimization, but problematic for testability):

# pact_context.py (hypothesized — to be verified)
_CONTEXT_CACHE = {}  # module-level state — survives between tests

def get_session_dir():
    if "session_dir" not in _CONTEXT_CACHE:
        _CONTEXT_CACHE["session_dir"] = _compute()
    return _CONTEXT_CACHE["session_dir"]

staleness.py likely consumes pact_context and inherits the polluted state.

Proposal

Option A — explicit cache-reset hook for tests

# pact_context.py
def reset_for_testing():
    """Pytest fixture hook — clears module-level state."""
    _CONTEXT_CACHE.clear()

Test fixtures call pact_context.reset_for_testing() in setup. No importlib.reload needed.

Option B — dependency injection

Refactor pact_context to accept context as a parameter rather than reading from module state. More invasive but eliminates the pollution class entirely.

Option C — pytest fixture autouse

# conftest.py
@pytest.fixture(autouse=True)
def reset_pact_context_cache():
    pact_context._CONTEXT_CACHE.clear()
    yield
    pact_context._CONTEXT_CACHE.clear()

Cheapest fix; doesn't require changes to source modules.

Recommendation

Start with Option A (explicit reset hook) — least invasive, most discoverable. If the pattern recurs across other modules, escalate to Option B (dependency injection) as a broader refactor.

Acceptance criteria

  • pact_context.py exposes a reset_for_testing() hook that clears module-level cache state.
  • test_staleness.py fixtures use the explicit hook instead of importlib.reload.
  • Test order independence verified: pytest with --randomly-seed plugin produces stable results across multiple seeds.
  • Documentation note in pact_context.py docstring explaining the cache and the test-reset hook.

Cross-references

Activity

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions