test: isolate computer-use approval globals between tests
tools/computer_use/tool.py keeps the CLI approval flow in module-globals: _approval_callback plus the per-session unlock stores _always_allow / _session_auto_approve. Any test that installs a callback (or drives CLI init far enough that the real one is registered) and does not reset it poisons every later computer-use test in the same process: * a leaked callback that raises — dead UI infra or a stale two-argument signature (the contract is (action, args, summary)) — becomes verdict='deny' in _request_approval, so dispatch tests fail with an empty backend call list; * a leaked callback that blocks (the real CLI one waits on an answer queue) hangs a single-process run forever. Both are order-dependent: tests/tools/test_computer_use.py passes 220/220 in isolation but shows dispatch failures in single-process full-suite runs (and, with a blocking leak, a permanent hang observed via py-spy inside _request_approval -> callback -> queue.get with no timeout). Fix: an autouse teardown-only fixture resets callback + unlock stores after every test; tests that install their own callback keep it for their own duration. Regression pair included: a 'forgetful' test leaves a stale two-arg callback behind, the next test asserts dispatch still routes to the backend — red without the fixture (1 failed), green with it (225 passed together with the whole computer-use file).
This commit is contained in:
@@ -1088,3 +1088,38 @@ def _audio_playback_guard(request, monkeypatch):
|
||||
monkeypatch.setattr(_voice, "play_audio_file", _blocked_play_audio_file)
|
||||
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _isolate_computer_use_approval_state():
|
||||
"""Reset computer-use approval globals after every test.
|
||||
|
||||
``tools.computer_use.tool`` keeps three module-globals for the CLI
|
||||
approval flow: ``_approval_callback`` (set by the CLI console on init)
|
||||
plus the per-session unlock stores ``_always_allow`` /
|
||||
``_session_auto_approve``. A test that installs a callback — or drives
|
||||
CLI init far enough that the real one is registered — and does not reset
|
||||
it poisons every later computer-use test in the same process:
|
||||
|
||||
* a leaked callback that raises (dead UI/queue infra, or a stale
|
||||
two-argument signature — the real contract is ``(action, args,
|
||||
summary)``) turns into ``verdict = "deny"`` in ``_request_approval``,
|
||||
so dispatch tests fail with an empty backend call list;
|
||||
* a leaked callback that blocks (the real CLI one waits on an answer
|
||||
queue) hangs the whole single-process run forever — pytest-timeout is
|
||||
the only thing that can cut it.
|
||||
|
||||
Both symptoms are order-dependent: the affected files pass in isolation
|
||||
and only fail in full-suite runs. Teardown-only, so tests that install
|
||||
their own callback keep it for their own duration.
|
||||
"""
|
||||
yield
|
||||
try:
|
||||
from tools.computer_use import tool as _cu_tool
|
||||
|
||||
_cu_tool.set_approval_callback(None)
|
||||
with _cu_tool._approval_lock:
|
||||
_cu_tool._always_allow.clear()
|
||||
_cu_tool._session_auto_approve.clear()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user