From b0d8fdf02864064b99e3de5f938693d2ffcc05a6 Mon Sep 17 00:00:00 2001 From: alexpolo1 Date: Tue, 15 Sep 2026 18:00:58 +0200 Subject: [PATCH] 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 --- test/conftest.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/test/conftest.py b/test/conftest.py index ff51b18..213dc5b 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -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)