Skip to content

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

@michael-wojcik

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions