test_accessory_plug_queue_stall_2629.py 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. """End-to-end regression test for the #2629 queue stall.
  2. Exercises the real objects rather than mocks: a real ``BambuMQTTClient``
  3. registered on the real ``printer_manager`` singleton, driven through the real
  4. ``_on_message`` path, and read back through the scheduler's own idle check.
  5. That chain — presume power off, printer keeps talking, scheduler sees it as
  6. dispatchable again — is what actually broke for the reporter, and no single
  7. unit test covers it.
  8. """
  9. from __future__ import annotations
  10. import json
  11. import pytest
  12. from backend.app.services.bambu_mqtt import BambuMQTTClient
  13. from backend.app.services.print_scheduler import PrintScheduler
  14. from backend.app.services.printer_manager import printer_manager
  15. PRINTER_ID = 9629 # unlikely to collide with any other test's registrations
  16. class _Msg:
  17. def __init__(self, topic: str, data: dict):
  18. self.topic = topic
  19. self.payload = json.dumps(data).encode()
  20. @pytest.fixture
  21. def registered_client():
  22. """A connected client sitting on FINISH, as after a completed print."""
  23. client = BambuMQTTClient(ip_address="10.0.0.5", serial_number="SER2629", access_code="12345678")
  24. client.state.connected = True
  25. client.state.state = "FINISH"
  26. printer_manager._clients[PRINTER_ID] = client
  27. try:
  28. yield client
  29. finally:
  30. printer_manager._clients.pop(PRINTER_ID, None)
  31. def _partial_push(client: BambuMQTTClient) -> None:
  32. """A steady-state push_status carrying no gcode_state — the frame shape the
  33. reporter's P1S sends between state transitions."""
  34. client._on_message(None, None, _Msg(client.topic_subscribe, {"print": {"wifi_signal": "-30dBm"}}))
  35. def test_printer_recovers_and_queue_can_dispatch_again(registered_client):
  36. scheduler = PrintScheduler()
  37. assert scheduler._is_printer_idle(PRINTER_ID, require_plate_clear=False) is True
  38. # An accessory plug (filter fan) switches off; Bambuddy presumes power loss.
  39. printer_manager.mark_printer_offline(PRINTER_ID)
  40. assert printer_manager.get_status(PRINTER_ID).state == "unknown"
  41. assert scheduler._is_printer_idle(PRINTER_ID, require_plate_clear=False) is False
  42. # The printer never stopped talking.
  43. _partial_push(registered_client)
  44. assert printer_manager.get_status(PRINTER_ID).state == "FINISH"
  45. assert printer_manager.is_connected(PRINTER_ID) is True
  46. assert scheduler._is_printer_idle(PRINTER_ID, require_plate_clear=False) is True
  47. def test_real_power_cut_still_leaves_printer_unavailable(registered_client):
  48. """The recovery must key off actual traffic, not off time passing — a plug
  49. that really cut power produces silence, and the printer stays offline."""
  50. scheduler = PrintScheduler()
  51. printer_manager.mark_printer_offline(PRINTER_ID)
  52. # No messages arrive at all.
  53. assert printer_manager.get_status(PRINTER_ID).state == "unknown"
  54. assert printer_manager.is_connected(PRINTER_ID) is False
  55. assert scheduler._is_printer_idle(PRINTER_ID, require_plate_clear=False) is False