test_ams_change_kprofile_reapply_44.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. """An AMS change no longer re-selects a spool's stored K-profile itself.
  2. It used to set the slot back to the stored profile whenever its cali_idx
  3. differed, so a profile picked on purpose in Bambu Studio or on the printer's
  4. screen was undone by the next AMS report that changed the slot. The #3219
  5. drift check already puts back a *lost* selection (cali_idx -1 or missing)
  6. while the printer is idle, and leaves a different real profile, a deliberate
  7. "Default" and the other hotend's calibration alone. It is now the only path.
  8. """
  9. from unittest.mock import AsyncMock, MagicMock, patch
  10. import pytest
  11. from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
  12. from backend.app.models.spool import Spool
  13. from backend.app.models.spool_assignment import SpoolAssignment
  14. from backend.app.models.spool_k_profile import SpoolKProfile
  15. from backend.app.services import kprofile_drift
  16. from backend.app.services.usage_tracker import _active_sessions
  17. TAG_UID = "A1B2C3D4E5F60708"
  18. TRAY_UUID = "0123456789ABCDEF0123456789ABCDEF"
  19. @pytest.fixture(autouse=True)
  20. def _no_sessions():
  21. _active_sessions.clear()
  22. yield
  23. _active_sessions.clear()
  24. async def _ams_push(test_engine, db_session, printer_factory, *, live_cali_idx, state="IDLE") -> MagicMock:
  25. from backend.app.main import on_ams_change
  26. printer = await printer_factory(name="X1C")
  27. spool = Spool(material="PLA", label_weight=1000, weight_used=0, tag_uid=TAG_UID, tray_uuid=TRAY_UUID)
  28. db_session.add(spool)
  29. await db_session.flush()
  30. db_session.add(
  31. SpoolKProfile(
  32. spool_id=spool.id, printer_id=printer.id, extruder=0, nozzle_diameter="0.4", k_value=0.02, cali_idx=7
  33. )
  34. )
  35. db_session.add(
  36. SpoolAssignment(
  37. spool_id=spool.id,
  38. printer_id=printer.id,
  39. ams_id=0,
  40. tray_id=0,
  41. fingerprint_color="FF0000FF",
  42. fingerprint_type="PLA",
  43. )
  44. )
  45. await db_session.commit()
  46. tray = {
  47. "id": 0,
  48. "tray_type": "PLA",
  49. "tray_color": "FF0000FF",
  50. "tag_uid": TAG_UID,
  51. "tray_uuid": TRAY_UUID,
  52. "tray_info_idx": "GFA00",
  53. "remain": -1,
  54. }
  55. if live_cali_idx is not None:
  56. tray["cali_idx"] = live_cali_idx
  57. ams_data = [{"id": 0, "tray": [tray]}]
  58. status = MagicMock()
  59. status.raw_data = {"ams": {"ams": ams_data}}
  60. status.state = state
  61. status.connected = True
  62. client = MagicMock()
  63. real_db = async_sessionmaker(test_engine, class_=AsyncSession, expire_on_commit=False)
  64. with (
  65. patch("backend.app.main.async_session", real_db),
  66. patch("backend.app.core.database.async_session", real_db),
  67. patch("backend.app.main.printer_manager") as mock_pm,
  68. patch("backend.app.main.mqtt_relay") as mock_relay,
  69. patch("backend.app.main.ws_manager") as mock_ws,
  70. ):
  71. mock_pm.get_printer.return_value = MagicMock(name="X1C", serial_number="KREAPPLY")
  72. mock_pm.get_status.return_value = status
  73. mock_pm.get_client.return_value = client
  74. mock_pm.get_model.return_value = "X1C"
  75. mock_relay.on_ams_change = AsyncMock()
  76. mock_ws.send_printer_status = AsyncMock()
  77. mock_ws.broadcast = AsyncMock()
  78. await on_ams_change(printer.id, ams_data)
  79. return client
  80. @pytest.mark.asyncio
  81. @pytest.mark.integration
  82. @pytest.mark.parametrize("live", [-1, None, 3])
  83. async def test_the_ams_change_leaves_the_selection_to_the_drift_check(test_engine, db_session, printer_factory, live):
  84. client = await _ams_push(test_engine, db_session, printer_factory, live_cali_idx=live)
  85. client.extrusion_cali_sel.assert_not_called()
  86. @pytest.mark.parametrize(("live", "picked_up"), [(-1, True), (None, True), (3, False)])
  87. def test_the_drift_check_picks_up_only_a_lost_selection(live, picked_up):
  88. kprofile_drift._watches.clear()
  89. tray = {"id": 0, "tray_type": "PLA"}
  90. if live is not None:
  91. tray["cali_idx"] = live
  92. state = MagicMock(connected=True, state="IDLE", raw_data={"ams": [{"id": 0, "tray": [tray]}]})
  93. try:
  94. assert kprofile_drift.needs_check(4242, state) is picked_up
  95. finally:
  96. kprofile_drift._watches.clear()