test_tray_change_callback.py 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. """The tray-change log has to leave the process.
  2. ``PrinterState.tray_change_log`` is what the usage tracker splits filament
  3. weight on when AMS filament backup swaps in a fresh spool mid-print. It lived
  4. only in memory, so a restart during a long print erased the segment boundaries
  5. and the whole job got charged to the tray that finished it. The client now
  6. reports every appended entry so main.py can persist it.
  7. """
  8. import pytest
  9. from backend.app.services.bambu_mqtt import BambuMQTTClient
  10. def _tray_msg(tray_now: int):
  11. """A partial AMS update carrying only tray_now, as P-series and H2D send."""
  12. return {"print": {"ams": {"tray_now": str(tray_now)}}}
  13. class TestTrayChangeCallback:
  14. @pytest.fixture
  15. def mqtt_client(self):
  16. client = BambuMQTTClient(
  17. ip_address="192.168.1.100",
  18. serial_number="TEST123",
  19. access_code="12345678",
  20. )
  21. client._was_running = True
  22. client._completion_triggered = False
  23. return client
  24. def test_every_logged_change_is_reported(self, mqtt_client):
  25. seen: list[tuple[int, int]] = []
  26. mqtt_client.on_tray_change = lambda tray, layer: seen.append((tray, layer))
  27. mqtt_client.state.layer_num = 0
  28. mqtt_client._process_message(_tray_msg(2))
  29. mqtt_client.state.layer_num = 670
  30. mqtt_client._process_message(_tray_msg(254))
  31. mqtt_client.state.layer_num = 675
  32. mqtt_client._process_message(_tray_msg(3))
  33. assert seen == [(2, 0), (254, 670), (3, 675)]
  34. assert mqtt_client.state.tray_change_log == [(2, 0), (254, 670), (3, 675)]
  35. def test_repeat_of_the_same_tray_is_not_reported(self, mqtt_client):
  36. """The printer republishes tray_now on every push; only transitions
  37. are segment boundaries."""
  38. seen: list[tuple[int, int]] = []
  39. mqtt_client.on_tray_change = lambda tray, layer: seen.append((tray, layer))
  40. mqtt_client._process_message(_tray_msg(2))
  41. mqtt_client.state.layer_num = 40
  42. mqtt_client._process_message(_tray_msg(2))
  43. assert seen == [(2, 0)]
  44. def test_no_callback_outside_a_running_print(self, mqtt_client):
  45. seen: list[tuple[int, int]] = []
  46. mqtt_client.on_tray_change = lambda tray, layer: seen.append((tray, layer))
  47. mqtt_client._was_running = False
  48. mqtt_client._process_message(_tray_msg(2))
  49. assert seen == []
  50. assert mqtt_client.state.tray_change_log == []
  51. def test_missing_callback_does_not_break_logging(self, mqtt_client):
  52. """The callback is optional — the in-memory log still has to work."""
  53. mqtt_client.on_tray_change = None
  54. mqtt_client._process_message(_tray_msg(2))
  55. assert mqtt_client.state.tray_change_log == [(2, 0)]