Compare commits

...
Author SHA1 Message Date
ethernet 3330e7112e fix(tests): stop log queue listener to prevent FATAL on 3.11 shutdown
The TestRunConversationSurrogateSanitization test creates a full
AIAgent, which starts a QueueListener daemon thread (_monitor) via
setup_logging(). On Python 3.11 the daemon can still be mid-loop when
the interpreter begins shutdown, and accessing partially-torn-down
logging objects produces "FATAL: exception not rethread" at process
exit — a non-zero exit code in CI even though every test passed.

Add an autouse fixture that calls _reset_queued_handlers() after each
test to stop the listener and join its worker thread cleanly.
2026-07-13 16:48:00 -04:00
ethernet 5e732af591 fix(tests): force manual approval mode in E2E blocking tests
Commit 62a76bd3d (feat: make smart approvals the default, #62661)
changed approvals.mode default from "manual" to "smart". The
TestBlockingApprovalE2E tests did not patch the approval mode, so
check_all_command_guards routed through _smart_approve() first —
calling call_llm() which tried all auxiliary providers, failed (no
API keys in test env), and returned "escalate" before falling through
to the gateway blocking path. The LLM failure cascade took longer
than the test's 2.5s wait window (50 × 0.05s), so the notify
callback had not fired yet when assert len(notified) == 1 ran.

Force {"mode": "manual"} via patch in setup_method/teardown_method,
matching the pattern already used by test_blocking_approval_uses_
canonical_timeout in the same class.
2026-07-13 16:24:59 -04:00
2 changed files with 30 additions and 0 deletions
+18
View File
@@ -16,6 +16,24 @@ from run_agent import (
)
@pytest.fixture(autouse=True)
def _stop_log_queue_listener():
"""Stop the async logging QueueListener after each test.
AIAgent.__init__ → setup_logging() starts a QueueListener daemon
thread (_monitor). On Python 3.11 the daemon can still be mid-loop
when the interpreter begins shutdown, and accessing partially-torn-down
logging objects produces "FATAL: exception not rethread" at process
exit — a non-zero exit code in CI even though every test passed.
"""
yield
try:
from hermes_logging import _reset_queued_handlers
_reset_queued_handlers()
except Exception:
pass
class TestSanitizeSurrogates:
"""Test the _sanitize_surrogates() helper."""
@@ -421,6 +421,18 @@ class TestBlockingApprovalE2E:
os.environ.pop("HERMES_GATEWAY_SESSION", None)
os.environ.pop("HERMES_EXEC_ASK", None)
os.environ.pop("HERMES_SESSION_KEY", None)
# Force manual approval mode — these tests exercise the blocking
# gateway flow, not smart approval. The default became "smart"
# (#62661), which routes through _smart_approve() first and
# delays the notify callback past the test's wait window.
self._approval_mode_patch = patch(
"tools.approval._get_approval_config",
return_value={"mode": "manual"},
)
self._approval_mode_patch.start()
def teardown_method(self):
self._approval_mode_patch.stop()
def test_blocking_approval_approve_once(self):
"""check_all_command_guards blocks until resolve_gateway_approval is called."""