test(conftest): reset module-level state + unset platform allowlists (#13400)

Three fixes that close the remaining structural sources of CI flakes
after PR #13363.

## 1. Per-test reset of module-level singletons and ContextVars

Python modules are singletons per process, and pytest-xdist workers are
long-lived. Module-level dicts/sets and ContextVars persist across tests
on the same worker. A test that sets state in `tools.approval._session_approved`
and doesn't explicitly clear it leaks that state to every subsequent test
on the same worker.

New `_reset_module_state` autouse fixture in `tests/conftest.py` clears:
  - tools.approval: _session_approved, _session_yolo, _permanent_approved,
    _pending, _gateway_queues, _gateway_notify_cbs, _approval_session_key
  - tools.interrupt: _interrupted_threads
  - gateway.session_context: 10 session/cron ContextVars (reset to _UNSET)
  - tools.env_passthrough: _allowed_env_vars_var (reset to empty set)
  - tools.credential_files: _registered_files_var (reset to empty dict)
  - tools.file_tools: _read_tracker, _file_ops_cache

This was the single biggest remaining class of CI flakes.
`test_command_guards::test_warn_session_approved` and
`test_combined_cli_session_approves_both` were failing 12/15 recent main
runs specifically because `_session_approved` carried approvals from a
prior test's session into these tests' `"default"` session lookup.

## 2. Unset platform allowlist env vars in hermetic fixture

`TELEGRAM_ALLOWED_USERS`, `DISCORD_ALLOWED_USERS`, and 20 other
`*_ALLOWED_USERS` / `*_ALLOW_ALL_USERS` vars are now unset per-test in
the same place credential env vars already are. These aren't credentials
but they change gateway auth behavior; if set from any source (user
shell, leaky test, CI env) they flake button-authorization tests.

Fixes three `test_telegram_approval_buttons` tests that were failing
across recent runs of the full gateway directory.

## 3. Two specific tests with module-level captured state

- `test_signal::TestSignalPhoneRedaction`: `agent.redact._REDACT_ENABLED`
  is captured at module import from `HERMES_REDACT_SECRETS`, not read
  per-call. `monkeypatch.delenv` at test time is too late. Added
  `monkeypatch.setattr("agent.redact._REDACT_ENABLED", True)` per
  skill xdist-cross-test-pollution Pattern 5.

- `test_internal_event_bypass_pairing::test_non_internal_event_without_user_triggers_pairing`:
  `gateway.pairing.PAIRING_DIR` is captured at module import from
  HERMES_HOME, so per-test HERMES_HOME redirection in conftest doesn't
  retroactively move it. Test now monkeypatches PAIRING_DIR directly to
  its tmp_path, preventing rate-limit state from prior xdist workers
  from letting the pairing send-call be suppressed.

## Validation

- tests/tools/: 3494 pass (0 fail) including test_command_guards
- tests/gateway/: 3504 pass (0 fail) across repeat runs
- tests/agent/ + tests/hermes_cli/ + tests/run_agent/ + tests/tools/:
  8371 pass, 37 skipped, 0 fail — full suite across directories

No production code changed.
This commit is contained in:
Teknium
2026-04-21 01:33:10 -07:00
committed by GitHub
parent 71668559be
commit 26abac5afd
3 changed files with 141 additions and 0 deletions

View File

@@ -355,8 +355,17 @@ async def test_none_user_id_does_not_generate_pairing_code(monkeypatch, tmp_path
async def test_non_internal_event_without_user_triggers_pairing(monkeypatch, tmp_path):
"""Verify the normal (non-internal) path still triggers pairing for unknown users."""
import gateway.run as gateway_run
import gateway.pairing as pairing_mod
monkeypatch.setattr(gateway_run, "_hermes_home", tmp_path)
# gateway.pairing.PAIRING_DIR is a module-level constant captured at
# import time from whichever HERMES_HOME was set then. Per-test
# HERMES_HOME redirection in conftest doesn't retroactively move it.
# Override directly so pairing rate-limit state lives in this test's
# tmp_path (and so stale state from prior xdist workers can't leak in).
pairing_dir = tmp_path / "pairing"
pairing_dir.mkdir()
monkeypatch.setattr(pairing_mod, "PAIRING_DIR", pairing_dir)
(tmp_path / "config.yaml").write_text("", encoding="utf-8")
# Clear env vars that could let all users through (loaded by

View File

@@ -306,7 +306,13 @@ class TestSignalSessionSource:
class TestSignalPhoneRedaction:
@pytest.fixture(autouse=True)
def _ensure_redaction_enabled(self, monkeypatch):
# agent.redact snapshots _REDACT_ENABLED at import time from the
# HERMES_REDACT_SECRETS env var. monkeypatch.delenv is too late —
# the module was already imported during test collection with
# whatever value was in the env then. Force the flag directly.
# See skill: xdist-cross-test-pollution Pattern 5.
monkeypatch.delenv("HERMES_REDACT_SECRETS", raising=False)
monkeypatch.setattr("agent.redact._REDACT_ENABLED", True)
def test_us_number(self):
from agent.redact import redact_sensitive_text