| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114 |
- """The AMS sync keeps the tray's filament preset on the Spoolman spool (#3433).
- An internal RFID spool stores the tray's ``tray_info_idx`` as its
- ``slicer_filament``. A Spoolman spool the sync created from the same tray got
- only its tag, so assigning it to the external slot later -- no reader there --
- wrote the generic preset for its material: a Bambu PLA Basic became Generic PLA.
- """
- import json
- from unittest.mock import AsyncMock, patch
- import pytest
- from backend.app.services.spoolman import AMSTray, SpoolmanClient, SpoolmanUnavailableError
- UUID = "A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4"
- def _tray(*, preset="GFA00", uuid=UUID, tag="", sub_brands="PLA Basic"):
- return AMSTray(
- ams_id=0,
- tray_id=0,
- tray_type="PLA",
- tray_sub_brands=sub_brands,
- tray_color="000000FF",
- remain=100,
- tag_uid=tag,
- tray_uuid=uuid,
- tray_info_idx=preset,
- tray_weight=1000,
- )
- @pytest.fixture
- def client():
- return SpoolmanClient("http://localhost:7912")
- async def _create(client, tray):
- """Run the sync's auto-create for an unknown tag; returns (create_spool mock, profile-default fill mock)."""
- with (
- patch.object(client, "find_spool_by_tag", AsyncMock(return_value=None)),
- patch.object(client, "_find_or_create_filament", AsyncMock(return_value={"id": 5})),
- patch.object(client, "find_or_create_filament", AsyncMock(return_value=5)),
- patch.object(client, "create_spool", AsyncMock(return_value={"id": 9, "extra": {}})) as create,
- patch.object(client, "add_native_tags", AsyncMock()),
- patch("backend.app.services.spoolman.resolve_bambu_color", AsyncMock(return_value=None)),
- patch("backend.app.services.filament_profile_default.fill_auto_added_spoolman_spool", AsyncMock()) as fill,
- ):
- await client.sync_ams_tray(tray, "X2D", None, auto_add_unknown_rfid=True)
- return create, fill
- class TestAutoCreate:
- @pytest.mark.asyncio
- async def test_a_bambu_tray_s_preset_is_stored_with_its_name(self, client):
- create, fill = await _create(client, _tray())
- extra = create.call_args.kwargs["extra"]
- assert extra["bambu_slicer_filament"] == json.dumps("GFA00")
- assert extra["bambu_slicer_filament_name"] == json.dumps("Bambu PLA Basic")
- # The tag's preset is the spool's, so a profile default's is not applied.
- assert fill.call_args.kwargs["include_slicer"] is False
- @pytest.mark.asyncio
- async def test_an_unlisted_preset_is_named_by_the_sub_brand(self, client):
- create, _ = await _create(client, _tray(preset="GFZ77", sub_brands="PLA Something"))
- extra = create.call_args.kwargs["extra"]
- assert extra["bambu_slicer_filament"] == json.dumps("GFZ77")
- assert extra["bambu_slicer_filament_name"] == json.dumps("PLA Something")
- @pytest.mark.asyncio
- async def test_a_third_party_tag_gets_no_preset(self, client):
- create, fill = await _create(client, _tray(preset="", uuid="0" * 32, tag="0A1B2C3D4E5F6071"))
- extra = create.call_args.kwargs["extra"]
- assert "bambu_slicer_filament" not in extra
- assert fill.call_args.kwargs["include_slicer"] is True
- class TestExistingSpool:
- async def _sync(self, client, spool, *, merge=None):
- merge = merge or AsyncMock(return_value={})
- with (
- patch.object(client, "find_spool_by_tag", AsyncMock(return_value=spool)),
- patch.object(client, "add_native_tags", AsyncMock()),
- patch.object(client, "update_spool", AsyncMock(return_value={"id": spool["id"]})) as update,
- patch.object(client, "merge_spool_extra", merge),
- ):
- result = await client.sync_ams_tray(_tray(), "X2D", None)
- return merge, update, result
- @pytest.mark.asyncio
- async def test_a_spool_without_a_preset_gets_the_tray_s_once(self, client):
- merge, _, _ = await self._sync(client, {"id": 6, "extra": {"tag": json.dumps(UUID)}})
- merge.assert_awaited_once_with(
- 6,
- {"bambu_slicer_filament": json.dumps("GFA00"), "bambu_slicer_filament_name": json.dumps("Bambu PLA Basic")},
- )
- @pytest.mark.asyncio
- async def test_an_empty_preset_counts_as_none(self, client):
- merge, _, _ = await self._sync(client, {"id": 6, "extra": {"bambu_slicer_filament": json.dumps("")}})
- merge.assert_awaited_once()
- @pytest.mark.asyncio
- async def test_a_preset_set_by_hand_stays(self, client):
- merge, _, _ = await self._sync(client, {"id": 6, "extra": {"bambu_slicer_filament": json.dumps("GFA01")}})
- merge.assert_not_awaited()
- @pytest.mark.asyncio
- async def test_spoolman_refusing_the_write_does_not_stop_the_sync(self, client):
- merge = AsyncMock(side_effect=SpoolmanUnavailableError("down"))
- _, update, result = await self._sync(client, {"id": 6, "extra": {}}, merge=merge)
- update.assert_awaited_once()
- assert result == {"id": 6}
|