test_spoolman_slot_own_preset.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. """A Spoolman spool's own slicer preset is used for its slot.
  2. The spool keeps its preset in Spoolman (extra.bambu_slicer_filament). Configure
  3. AMS Slot and the K-profile lookup passed no fallback for a Spoolman spool, so
  4. one without a per-model override opened with no preset and the K-profile
  5. matched against the tray's own filament id. Read from Spoolman, remembered for
  6. a minute per spool.
  7. """
  8. from unittest.mock import AsyncMock, MagicMock, patch
  9. import pytest
  10. from httpx import AsyncClient
  11. from backend.app.services import spool_filament_preset
  12. URL = "/api/v1/printers/{pid}/slots/0/0/spool-defaults"
  13. SPOOL = {
  14. "id": 42,
  15. "filament": {"id": 1, "name": "PLA Matte", "material": "PLA", "color_hex": "000000", "weight": 1000},
  16. "remaining_weight": 800.0,
  17. "used_weight": 200.0,
  18. "archived": False,
  19. "extra": {
  20. "bambu_slicer_filament": '"GFA01"',
  21. "bambu_slicer_filament_name": '"Bambu PLA Matte @BBL X1C"',
  22. },
  23. }
  24. @pytest.fixture(autouse=True)
  25. def _empty_cache():
  26. spool_filament_preset._spoolman_own_presets.clear()
  27. yield
  28. spool_filament_preset._spoolman_own_presets.clear()
  29. @pytest.fixture
  30. async def spoolman_slot(db_session, printer_factory):
  31. from backend.app.models.settings import Settings
  32. from backend.app.models.spoolman_k_profile import SpoolmanKProfile
  33. from backend.app.models.spoolman_slot_assignment import SpoolmanSlotAssignment
  34. db_session.add(Settings(key="spoolman_enabled", value="true"))
  35. db_session.add(Settings(key="spoolman_url", value="http://localhost:7912"))
  36. printer = await printer_factory(model="X1C")
  37. db_session.add(SpoolmanSlotAssignment(printer_id=printer.id, ams_id=0, tray_id=0, spoolman_spool_id=42))
  38. db_session.add(
  39. SpoolmanKProfile(
  40. spoolman_spool_id=42, printer_id=printer.id, extruder=0, nozzle_diameter="0.4", k_value=0.02, cali_idx=7
  41. )
  42. )
  43. await db_session.commit()
  44. return printer.id
  45. @pytest.fixture
  46. def spoolman():
  47. client = MagicMock()
  48. client.get_spool = AsyncMock(return_value=SPOOL)
  49. with patch("backend.app.services.spoolman.get_spoolman_client", AsyncMock(return_value=client)):
  50. yield client
  51. class _Nozzle:
  52. nozzle_diameter = "0.4"
  53. @pytest.fixture
  54. def printer_state():
  55. state = MagicMock(nozzles=[_Nozzle()], ams_extruder_map={"0": 0}, ams_switch_inlet=None, raw_data={})
  56. with patch("backend.app.api.routes.printers.printer_manager") as pm:
  57. pm.get_status = MagicMock(return_value=state)
  58. pm.get_model = MagicMock(return_value="X1C")
  59. yield pm
  60. @pytest.mark.asyncio
  61. @pytest.mark.integration
  62. async def test_configure_slot_opens_with_the_spools_own_preset(
  63. async_client: AsyncClient, spoolman_slot, spoolman, printer_state
  64. ):
  65. body = (await async_client.get(URL.format(pid=spoolman_slot))).json()
  66. assert body["slicer_filament"] == "GFA01"
  67. assert body["slicer_filament_name"] == "Bambu PLA Matte @BBL X1C"
  68. @pytest.mark.asyncio
  69. @pytest.mark.integration
  70. async def test_the_k_profile_lookup_uses_the_spools_own_preset(db_session, spoolman_slot, spoolman):
  71. from backend.app.services.slot_kprofile import find_slot_kprofile_for_extruder
  72. profile = await find_slot_kprofile_for_extruder(db_session, spoolman_slot, 0, 0, 0, "0.4", "X1C", None)
  73. assert (profile.cali_idx, profile.filament_id) == (7, "GFA01")
  74. @pytest.mark.asyncio
  75. @pytest.mark.integration
  76. async def test_a_per_model_override_wins_without_asking_spoolman(db_session, spoolman_slot, spoolman):
  77. from backend.app.models.spool_filament_preset import SpoolmanFilamentPreset
  78. from backend.app.services.slot_kprofile import find_slot_kprofile_for_extruder
  79. db_session.add(
  80. SpoolmanFilamentPreset(spoolman_spool_id=42, printer_model="X1C", nozzle_diameter=None, slicer_filament="GFA05")
  81. )
  82. await db_session.commit()
  83. profile = await find_slot_kprofile_for_extruder(db_session, spoolman_slot, 0, 0, 0, "0.4", "X1C", None)
  84. assert profile.filament_id == "GFA05"
  85. spoolman.get_spool.assert_not_called()
  86. @pytest.mark.asyncio
  87. @pytest.mark.integration
  88. async def test_the_own_preset_is_remembered_and_dropped_on_an_edit(spoolman):
  89. await spool_filament_preset.spoolman_spool_own_preset(42)
  90. await spool_filament_preset.spoolman_spool_own_preset(42)
  91. assert spoolman.get_spool.await_count == 1
  92. spool_filament_preset.forget_spoolman_spool_own_preset(42)
  93. await spool_filament_preset.spoolman_spool_own_preset(42)
  94. assert spoolman.get_spool.await_count == 2
  95. @pytest.mark.asyncio
  96. @pytest.mark.integration
  97. async def test_an_override_left_blank_on_purpose_keeps_the_slot_without_a_preset(db_session, spoolman_slot, spoolman):
  98. from backend.app.models.spool_filament_preset import SpoolmanFilamentPreset
  99. from backend.app.services.slot_kprofile import find_slot_kprofile_for_extruder
  100. db_session.add(
  101. SpoolmanFilamentPreset(spoolman_spool_id=42, printer_model="X1C", nozzle_diameter=None, slicer_filament=None)
  102. )
  103. await db_session.commit()
  104. profile = await find_slot_kprofile_for_extruder(db_session, spoolman_slot, 0, 0, 0, "0.4", "X1C", None)
  105. assert profile.filament_id is None
  106. spoolman.get_spool.assert_not_called()
  107. @pytest.mark.asyncio
  108. @pytest.mark.integration
  109. @pytest.mark.parametrize("stored,expected", [('"GFSA01_02"', "GFA01"), ('"38"', None)])
  110. async def test_the_k_profile_filament_id_is_the_one_the_printer_takes(
  111. db_session, spoolman_slot, spoolman, stored, expected
  112. ):
  113. """The cloud picker stores the setting-id form; a local preset is stored
  114. by its number, which only the slicer resolver turns into a filament id."""
  115. from backend.app.services.slot_kprofile import find_slot_kprofile_for_extruder
  116. spoolman.get_spool = AsyncMock(return_value={**SPOOL, "extra": {"bambu_slicer_filament": stored}})
  117. profile = await find_slot_kprofile_for_extruder(db_session, spoolman_slot, 0, 0, 0, "0.4", "X1C", None)
  118. assert profile.filament_id == expected