test: block real mouse/keyboard input and window moves during pytest

Many tests run real bot code paths with only the screen faked; those paths end in
win_input's SendInput/SetCursorPos, which act on the focused window. Running the
suite while the bot played on 2026-09-15 sent test clicks and keys into D2R (a
game started with no bot input, the inventory opened, the character got
stranded), and the bot was blamed for getting stuck.

conftest now wraps win_input.user32 so SendInput/SetCursorPos/keybd_event/
mouse_event report success and do nothing, and no-ops utils.misc window
SetWindowPos/SetForegroundWindow/ShowWindow. Verified: a test calling
win_input.mouse_move leaves the real cursor in place; with
BOTTY_TESTS_ALLOW_REAL_INPUT=1 the same test moves it.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
alexpolo1
2026-09-15 18:00:58 +02:00
co-authored by Claude Opus 5
parent f11034869b
commit b0d8fdf028
+38
View File
@@ -21,3 +21,41 @@ if sys.platform == "win32":
pass
ssl.SSLContext.load_default_certs = _safe_load_default_certs
# Never let a test drive the real desktop.
#
# Many tests run real bot code paths (town maintenance, waypoints, NPC clicks, game
# creation) with only the screen faked. Those paths end in win_input's SendInput /
# SetCursorPos, which act on whatever window has focus. On 2026-09-15 the suite was
# run repeatedly while the bot was playing: D2R received test keystrokes and clicks -
# a game started with no bot input, the inventory opened, and the character ended up
# stranded - and the bot was blamed for "getting stuck".
#
# Block it at the lowest layer so no test can reach the OS, whatever it forgets to
# mock. Set BOTTY_TESTS_ALLOW_REAL_INPUT=1 to opt out for a deliberate live test.
if sys.platform == "win32":
import os
if not os.environ.get("BOTTY_TESTS_ALLOW_REAL_INPUT"):
class _NoRealInputUser32:
"""user32 proxy: input and cursor calls report success and do nothing."""
def __init__(self, real):
self._real = real
def __getattr__(self, name):
if name == "SendInput":
return lambda count, *args: count # "all events inserted"
if name in ("SetCursorPos", "keybd_event", "mouse_event"):
return lambda *args: 1
return getattr(self._real, name)
from input_layer import win_input as _win_input
from utils import misc as _misc
_win_input.user32 = _NoRealInputUser32(_win_input.user32)
# Window moves / focus steals: tests must not reposition or front D2R either.
for _name in ("SetWindowPos", "SetForegroundWindow", "ShowWindow"):
setattr(_misc, _name, lambda *args, **kwargs: None)