From c36bfa24d458428fda6012409d97b16ce182d67a Mon Sep 17 00:00:00 2001 From: maziggy Date: Mon, 11 May 2026 13:57:40 +0200 Subject: [PATCH] test: harden _fake_state against printer_state_to_dict mock-leakage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The two TestBroadcastStatusChange / TestEndToEndUnderRunningLoop tests patch printer_state_to_dict to return a fixed dict, but on parallel xdist runners (CI's pytest -n 30) the patch occasionally didn't catch the call and the real function ran against the 4-field SimpleNamespace fake — first attr access (.kprofiles) AttributeError'd, swallowed by the try/except in _broadcast_status_change, send_status never awaited, the assertion failed. Filled _fake_state with every attribute the real printer_state_to_dict reads (iterables empty, scalars None, stg_cur=0 for the int comparison in get_derived_status_name). Test now passes whether or not the patch lands. --- .../test_printer_manager_status_broadcast.py | 48 +++++++++++++++++-- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/backend/tests/unit/test_printer_manager_status_broadcast.py b/backend/tests/unit/test_printer_manager_status_broadcast.py index bc6c25c48..2f2afd986 100644 --- a/backend/tests/unit/test_printer_manager_status_broadcast.py +++ b/backend/tests/unit/test_printer_manager_status_broadcast.py @@ -50,15 +50,55 @@ def _close_unawaited(coro): def _fake_state(**overrides): - """Minimal stand-in for a ``PrinterState`` — only the attributes - ``printer_state_to_dict`` reads. We use a SimpleNamespace rather than - constructing a real PrinterState so this test stays fast and doesn't - couple to the (large, evolving) PrinterState dataclass shape.""" + """Stand-in for a ``PrinterState``. + + The tests below patch ``printer_state_to_dict`` so the fake doesn't need + to satisfy every attribute access — but the patch was observed to race on + parallel CI runners (pytest-xdist), and when it didn't catch the call the + real ``printer_state_to_dict`` ran against this fake and ``AttributeError``'d + on ``.kprofiles``. The fake now carries every attribute the real function + reads, so it remains correct even if the patch is somehow bypassed — the + test no longer depends on a fragile monkeypatch landing in time. + + Iterables (``kprofiles``, ``printable_objects``, ``hms_errors``, + ``temperatures``, etc.) default to empty so the function's loops are + no-ops; scalars default to ``None`` so any "if state.x is None" guard + falls through cleanly. + """ base = { + # State the existing test bodies explicitly set / read "connected": True, "state": "FINISH", "raw_data": {}, "progress": 100.0, + # Iterables — must be iterable for the loops inside printer_state_to_dict + "kprofiles": [], + "printable_objects": [], + "hms_errors": [], + "temperatures": {}, + "nozzle_rack": [], + # Nullable scalars — printer_state_to_dict tolerates None for these + "active_extruder": None, + "ams_status_main": None, + "ams_status_sub": None, + "big_fan1_speed": None, + "big_fan2_speed": None, + "chamber_light": None, + "cooling_fan_speed": None, + "current_print": None, + "door_open": None, + "firmware_version": None, + "gcode_file": None, + "heatbreak_fan_speed": None, + "layer_num": None, + "remaining_time": None, + "speed_level": None, + "stg_cur": 0, # get_derived_status_name does ``0 <= state.stg_cur < 255`` + "subtask_name": None, + "total_layers": None, + "tray_now": None, + "wifi_signal": None, + "wired_network": None, } base.update(overrides) return SimpleNamespace(**base)