| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384 |
- """The manual Spoolman sync and slots that read empty.
- The automatic pass unlinks a Spoolman slot only when it reads empty while the
- printer is not printing and the presence bit does not say a spool is there.
- The Sync buttons in Settings deleted the row whenever the tray had no type or
- colour: mid-print (a runout reads empty), or a non-RFID spool whose tray type
- was never set.
- """
- from types import SimpleNamespace
- from unittest.mock import AsyncMock, MagicMock, patch
- import pytest
- from httpx import AsyncClient
- from sqlalchemy import select
- from backend.app.models.settings import Settings
- from backend.app.models.spoolman_slot_assignment import SpoolmanSlotAssignment
- @pytest.fixture
- async def printer_id(db_session, printer_factory):
- db_session.add(Settings(key="spoolman_enabled", value="true"))
- db_session.add(Settings(key="spoolman_url", value="http://localhost:7912"))
- printer = await printer_factory()
- db_session.add(SpoolmanSlotAssignment(printer_id=printer.id, ams_id=0, tray_id=0, spoolman_spool_id=42))
- await db_session.commit()
- return printer.id
- async def _sync(async_client, printer_id, path, gcode_state, exists):
- tray = {"id": 0, "tray_type": "", "tray_color": ""}
- if exists is not None:
- tray["exists"] = exists
- state = SimpleNamespace(state=gcode_state, raw_data={"ams": [{"id": 0, "tray": [tray]}]})
- client = MagicMock()
- client.health_check = AsyncMock(return_value=True)
- client.get_spools = AsyncMock(return_value=[])
- client.parse_ams_tray = MagicMock(return_value=None)
- with (
- patch("backend.app.api.routes.spoolman.get_spoolman_client", AsyncMock(return_value=client)),
- patch("backend.app.api.routes.spoolman.printer_manager") as pm,
- ):
- pm.get_status = MagicMock(return_value=state)
- response = await async_client.post(path.format(printer_id=printer_id))
- assert response.status_code == 200, response.text
- PATHS = ["/api/v1/spoolman/sync/{printer_id}", "/api/v1/spoolman/sync-all"]
- @pytest.mark.asyncio
- @pytest.mark.integration
- @pytest.mark.parametrize("path", PATHS)
- @pytest.mark.parametrize("gcode_state", ["RUNNING", "PAUSE"])
- async def test_a_slot_reading_empty_mid_print_keeps_its_spool(
- async_client: AsyncClient, db_session, printer_id, path, gcode_state
- ):
- await _sync(async_client, printer_id, path, gcode_state, exists=None)
- rows = (await db_session.execute(select(SpoolmanSlotAssignment))).scalars().all()
- assert len(rows) == 1
- @pytest.mark.asyncio
- @pytest.mark.integration
- @pytest.mark.parametrize("path", PATHS)
- async def test_a_slot_the_presence_bit_calls_occupied_keeps_its_spool(
- async_client: AsyncClient, db_session, printer_id, path
- ):
- await _sync(async_client, printer_id, path, "IDLE", exists=True)
- rows = (await db_session.execute(select(SpoolmanSlotAssignment))).scalars().all()
- assert len(rows) == 1
- @pytest.mark.asyncio
- @pytest.mark.integration
- @pytest.mark.parametrize("path", PATHS)
- async def test_an_empty_slot_of_an_idle_printer_is_unlinked(async_client: AsyncClient, db_session, printer_id, path):
- await _sync(async_client, printer_id, path, "IDLE", exists=False)
- rows = (await db_session.execute(select(SpoolmanSlotAssignment))).scalars().all()
- assert rows == []
|