Bot paths under test call _save_error_screenshot -> messenger.send_error, and Config reads the real webhook from the repo's .env. On 2026-09-15 a test run sent a real 'maintenance failed - Buy consumables failed (vendor not found)' alert while the live bot was completing every game. conftest now blanks the message hooks and disables SyncWebhook/generic_api posting. Co-Authored-By: Claude Opus 5 <[email protected]>
81 lines
3.5 KiB
Python
81 lines
3.5 KiB
Python
# Apply the same SSL cert-store workaround the bot entry points use, so the
|
|
# test suite can be collected on Windows machines whose cert store is corrupted.
|
|
# Importing the bot code pulls in discord/aiohttp, which calls
|
|
# ssl.create_default_context() at import time; a malformed Windows cert raises
|
|
# ssl.SSLError: [ASN1: NOT_ENOUGH_DATA]. No-op on a healthy cert store (incl. CI).
|
|
import sys
|
|
|
|
if sys.platform == "win32":
|
|
import ssl
|
|
|
|
_orig_load_default_certs = ssl.SSLContext.load_default_certs
|
|
|
|
def _safe_load_default_certs(self, purpose=ssl.Purpose.CLIENT_AUTH):
|
|
try:
|
|
_orig_load_default_certs(self, purpose)
|
|
except ssl.SSLError:
|
|
try:
|
|
import certifi
|
|
self.load_verify_locations(certifi.where())
|
|
except Exception:
|
|
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)
|
|
# Tests must not post to the real Discord either. Config reads the webhook
|
|
# from the repo's .env, and bot paths under test call
|
|
# _save_error_screenshot -> messenger.send_error: on 2026-09-15 a town-manager
|
|
# test sent a real "maintenance failed - Buy consumables failed (vendor not
|
|
# found)" alert while the live bot was fine.
|
|
from config import Config as _Config
|
|
from messages import discord_embeds as _discord_embeds, generic_api as _generic_api
|
|
|
|
_Config().general["custom_message_hook"] = ""
|
|
_Config().general["custom_loot_message_hook"] = ""
|
|
_Config().general["message_api_type"] = ""
|
|
|
|
class _NoWebhook:
|
|
@staticmethod
|
|
def from_url(*args, **kwargs):
|
|
raise RuntimeError("Discord disabled under pytest")
|
|
|
|
_discord_embeds.SyncWebhook = _NoWebhook
|
|
_generic_api.requests = type("_NoRequests", (), {"post": staticmethod(lambda *a, **k: None)})()
|
|
# 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)
|