|
|
@@ -324,3 +324,147 @@ class TestAssignSpoolTrayInfoIdx:
|
|
|
call_kwargs = mock_client.ams_set_filament_setting.call_args
|
|
|
# Slot's specific preset is reused when spool has no own preset
|
|
|
assert call_kwargs.kwargs["tray_info_idx"] == "GFA05"
|
|
|
+
|
|
|
+
|
|
|
+class TestAssignSpoolPresetMapping:
|
|
|
+ """Tests that assign_spool saves the slot preset mapping for correct UI display."""
|
|
|
+
|
|
|
+ @pytest.mark.asyncio
|
|
|
+ @pytest.mark.integration
|
|
|
+ async def test_preset_mapping_saved_with_slicer_filament_name(
|
|
|
+ self, async_client: AsyncClient, printer_factory, spool_factory
|
|
|
+ ):
|
|
|
+ """Slot preset mapping uses slicer_filament_name (not material+subtype)."""
|
|
|
+
|
|
|
+ printer = await printer_factory(name="X1C")
|
|
|
+ spool = await spool_factory(
|
|
|
+ slicer_filament="GFA05",
|
|
|
+ slicer_filament_name="Bambu PLA Silk",
|
|
|
+ material="PLA",
|
|
|
+ subtype="Silk",
|
|
|
+ brand="Bambu",
|
|
|
+ )
|
|
|
+
|
|
|
+ mock_client = MagicMock()
|
|
|
+ mock_client.ams_set_filament_setting.return_value = True
|
|
|
+ mock_client.extrusion_cali_sel.return_value = True
|
|
|
+ status = _make_mock_status(ams_data=[])
|
|
|
+
|
|
|
+ with patch("backend.app.services.printer_manager.printer_manager") as mock_pm:
|
|
|
+ mock_pm.get_client.return_value = mock_client
|
|
|
+ mock_pm.get_status.return_value = status
|
|
|
+
|
|
|
+ response = await async_client.post(
|
|
|
+ "/api/v1/inventory/assignments",
|
|
|
+ json={"spool_id": spool.id, "printer_id": printer.id, "ams_id": 0, "tray_id": 1},
|
|
|
+ )
|
|
|
+
|
|
|
+ assert response.status_code == 200
|
|
|
+
|
|
|
+ # Verify via the slot presets API
|
|
|
+ presets_resp = await async_client.get(f"/api/v1/printers/{printer.id}/slot-presets")
|
|
|
+ assert presets_resp.status_code == 200
|
|
|
+ presets = presets_resp.json()
|
|
|
+ # Key is str(ams_id * 4 + tray_id) — ams 0, tray 1 → "1"
|
|
|
+ assert "1" in presets
|
|
|
+ # Must use slicer_filament_name, NOT "PLA Silk" from material+subtype
|
|
|
+ assert presets["1"]["preset_name"] == "Bambu PLA Silk"
|
|
|
+ assert presets["1"]["preset_id"] == "GFSA05"
|
|
|
+
|
|
|
+ @pytest.mark.asyncio
|
|
|
+ @pytest.mark.integration
|
|
|
+ async def test_preset_mapping_overwrites_old_mapping(
|
|
|
+ self, async_client: AsyncClient, printer_factory, spool_factory, db_session: AsyncSession
|
|
|
+ ):
|
|
|
+ """Assigning a new spool overwrites the old slot preset mapping."""
|
|
|
+ from backend.app.models.slot_preset import SlotPresetMapping
|
|
|
+
|
|
|
+ printer = await printer_factory(name="X1C")
|
|
|
+
|
|
|
+ # Pre-existing mapping (e.g. from previous manual configuration)
|
|
|
+ old_mapping = SlotPresetMapping(
|
|
|
+ printer_id=printer.id,
|
|
|
+ ams_id=0,
|
|
|
+ tray_id=2,
|
|
|
+ preset_id="GFSA01",
|
|
|
+ preset_name="Bambu PLA Matte",
|
|
|
+ preset_source="cloud",
|
|
|
+ )
|
|
|
+ db_session.add(old_mapping)
|
|
|
+ await db_session.commit()
|
|
|
+
|
|
|
+ # Assign a "Generic PLA Silk" spool to same slot
|
|
|
+ spool = await spool_factory(
|
|
|
+ slicer_filament="GFL96",
|
|
|
+ slicer_filament_name="Generic PLA Silk",
|
|
|
+ material="PLA",
|
|
|
+ subtype="Silk",
|
|
|
+ )
|
|
|
+
|
|
|
+ mock_client = MagicMock()
|
|
|
+ mock_client.ams_set_filament_setting.return_value = True
|
|
|
+ mock_client.extrusion_cali_sel.return_value = True
|
|
|
+ status = _make_mock_status(ams_data=[])
|
|
|
+
|
|
|
+ with patch("backend.app.services.printer_manager.printer_manager") as mock_pm:
|
|
|
+ mock_pm.get_client.return_value = mock_client
|
|
|
+ mock_pm.get_status.return_value = status
|
|
|
+
|
|
|
+ response = await async_client.post(
|
|
|
+ "/api/v1/inventory/assignments",
|
|
|
+ json={"spool_id": spool.id, "printer_id": printer.id, "ams_id": 0, "tray_id": 2},
|
|
|
+ )
|
|
|
+
|
|
|
+ assert response.status_code == 200
|
|
|
+
|
|
|
+ # Verify via the slot presets API to avoid stale session cache
|
|
|
+ presets_resp = await async_client.get(f"/api/v1/printers/{printer.id}/slot-presets")
|
|
|
+ assert presets_resp.status_code == 200
|
|
|
+ presets = presets_resp.json()
|
|
|
+ # Key is str(ams_id * 4 + tray_id) — ams 0, tray 2 → "2"
|
|
|
+ assert "2" in presets
|
|
|
+ # Old "Bambu PLA Matte" must be overwritten
|
|
|
+ assert presets["2"]["preset_name"] == "Generic PLA Silk"
|
|
|
+ assert presets["2"]["preset_id"] == "GFSL96"
|
|
|
+
|
|
|
+ @pytest.mark.asyncio
|
|
|
+ @pytest.mark.integration
|
|
|
+ async def test_preset_mapping_fallback_to_tray_sub_brands(
|
|
|
+ self, async_client: AsyncClient, printer_factory, spool_factory
|
|
|
+ ):
|
|
|
+ """When slicer_filament_name is null, falls back to tray_sub_brands."""
|
|
|
+ from backend.app.models.slot_preset import SlotPresetMapping
|
|
|
+
|
|
|
+ printer = await printer_factory(name="A1M")
|
|
|
+ spool = await spool_factory(
|
|
|
+ slicer_filament="GFL05",
|
|
|
+ slicer_filament_name=None,
|
|
|
+ material="PLA",
|
|
|
+ subtype="Matte",
|
|
|
+ brand="Overture",
|
|
|
+ )
|
|
|
+
|
|
|
+ mock_client = MagicMock()
|
|
|
+ mock_client.ams_set_filament_setting.return_value = True
|
|
|
+ mock_client.extrusion_cali_sel.return_value = True
|
|
|
+ status = _make_mock_status(ams_data=[])
|
|
|
+
|
|
|
+ with patch("backend.app.services.printer_manager.printer_manager") as mock_pm:
|
|
|
+ mock_pm.get_client.return_value = mock_client
|
|
|
+ mock_pm.get_status.return_value = status
|
|
|
+
|
|
|
+ response = await async_client.post(
|
|
|
+ "/api/v1/inventory/assignments",
|
|
|
+ json={"spool_id": spool.id, "printer_id": printer.id, "ams_id": 0, "tray_id": 0},
|
|
|
+ )
|
|
|
+
|
|
|
+ assert response.status_code == 200
|
|
|
+
|
|
|
+ # Verify via the slot presets API
|
|
|
+ presets_resp = await async_client.get(f"/api/v1/printers/{printer.id}/slot-presets")
|
|
|
+ assert presets_resp.status_code == 200
|
|
|
+ presets = presets_resp.json()
|
|
|
+ # Key is str(ams_id * 4 + tray_id) — ams 0, tray 0 → "0"
|
|
|
+ assert "0" in presets
|
|
|
+ # Falls back to tray_sub_brands ("Overture PLA Matte")
|
|
|
+ assert presets["0"]["preset_name"] == "Overture PLA Matte"
|