| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- """Tests for the shared slot_preset_mappings writer.
- The writer keeps PrintersPage's slot-card preset_name in sync with the
- currently-assigned spool from three different inventory paths:
- * internal manual assign (inventory.apply_spool_to_slot_via_mqtt)
- * internal RFID auto-assign (spool_tag_matcher.auto_assign_spool)
- * Spoolman RFID sync (main.auto_sync_spoolman_ams_trays)
- Internal-mode regression coverage lives in test_spool_tag_matcher.py
- (which exercises the call site end-to-end). This file focuses on the
- helper-level contracts — local-preset id formatting, soft-fail on
- no preset_id, and the Spoolman derivation path.
- """
- import pytest
- from sqlalchemy import select
- from backend.app.models.slot_preset import SlotPresetMapping
- from backend.app.services.slot_preset_writer import (
- upsert_slot_preset,
- upsert_slot_preset_for_spoolman_spool,
- )
- @pytest.mark.asyncio
- async def test_upsert_no_op_when_preset_id_empty(db_session, printer_factory):
- """An empty preset_id is not a useful key — the model's column is NOT
- NULL and an empty string would overwrite the user's last good preset
- with garbage. Skip without raising."""
- printer = await printer_factory()
- await upsert_slot_preset(
- db=db_session,
- printer_id=printer.id,
- ams_id=0,
- tray_id=0,
- preset_id="",
- preset_name="ignored",
- )
- result = await db_session.execute(select(SlotPresetMapping).where(SlotPresetMapping.printer_id == printer.id))
- assert result.scalar_one_or_none() is None
- @pytest.mark.asyncio
- async def test_upsert_inserts_then_updates(db_session, printer_factory):
- """First call inserts, second call on same (printer, ams, tray) updates
- in place rather than violating the unique constraint."""
- printer = await printer_factory()
- await upsert_slot_preset(
- db=db_session,
- printer_id=printer.id,
- ams_id=0,
- tray_id=1,
- preset_id="GFSA50",
- preset_name="Bambu PLA-CF",
- preset_source="cloud",
- )
- await upsert_slot_preset(
- db=db_session,
- printer_id=printer.id,
- ams_id=0,
- tray_id=1,
- preset_id="GFSA00",
- preset_name="Bambu PLA Basic",
- preset_source="cloud",
- )
- rows = (
- (await db_session.execute(select(SlotPresetMapping).where(SlotPresetMapping.printer_id == printer.id)))
- .scalars()
- .all()
- )
- assert len(rows) == 1
- assert rows[0].preset_id == "GFSA00"
- assert rows[0].preset_name == "Bambu PLA Basic"
- # -- Spoolman derivation ----------------------------------------------------
- @pytest.mark.asyncio
- async def test_spoolman_helper_uses_filament_name_and_tray_info_idx(db_session, printer_factory):
- """A Spoolman spool with a typical filament shape — name + material —
- should land in the row with the AMS-reported tray_info_idx as
- preset_id (normalised to setting_id form) and the filament name as
- preset_name."""
- printer = await printer_factory()
- spoolman_spool = {
- "id": 42,
- "filament": {
- "id": 7,
- "name": "Bambu PLA-CF Burgundy Red",
- "material": "PLA-CF",
- },
- }
- await upsert_slot_preset_for_spoolman_spool(
- db=db_session,
- spoolman_spool=spoolman_spool,
- tray_info_idx="GFA50",
- tray_sub_brands="PLA-CF",
- tray_type="PLA",
- printer_id=printer.id,
- ams_id=1,
- tray_id=2,
- )
- mapping = (
- await db_session.execute(
- select(SlotPresetMapping).where(
- SlotPresetMapping.printer_id == printer.id,
- SlotPresetMapping.ams_id == 1,
- SlotPresetMapping.tray_id == 2,
- )
- )
- ).scalar_one()
- assert mapping.preset_id == "GFSA50"
- assert mapping.preset_name == "Bambu PLA-CF Burgundy Red"
- assert mapping.preset_source == "cloud"
- @pytest.mark.asyncio
- async def test_spoolman_helper_falls_back_to_material_when_name_missing(db_session, printer_factory):
- """Some Spoolman setups have unnamed filaments — fall back through
- material → tray_sub_brands → tray_type so we never write an empty
- preset_name."""
- printer = await printer_factory()
- spoolman_spool = {"id": 99, "filament": {"material": "PETG"}}
- await upsert_slot_preset_for_spoolman_spool(
- db=db_session,
- spoolman_spool=spoolman_spool,
- tray_info_idx="GFG00",
- tray_sub_brands="PETG Basic",
- tray_type="PETG",
- printer_id=printer.id,
- ams_id=0,
- tray_id=0,
- )
- mapping = (
- await db_session.execute(select(SlotPresetMapping).where(SlotPresetMapping.printer_id == printer.id))
- ).scalar_one()
- assert mapping.preset_name == "PETG"
- @pytest.mark.asyncio
- async def test_spoolman_helper_overwrites_stale_internal_row(db_session, printer_factory):
- """Mirror of the internal-mode regression: pre-seed a stale row with a
- previous spool's name, run the Spoolman helper, verify the row now
- reflects the freshly-synced Spoolman spool. This is the bug shape
- that would surface on a Spoolman user with a manually-set preset."""
- printer = await printer_factory()
- db_session.add(
- SlotPresetMapping(
- printer_id=printer.id,
- ams_id=1,
- tray_id=2,
- preset_id="GFSA06_09",
- preset_name="Bambu PLA Silk+",
- preset_source="cloud",
- )
- )
- await db_session.commit()
- await upsert_slot_preset_for_spoolman_spool(
- db=db_session,
- spoolman_spool={"id": 42, "filament": {"name": "Bambu PLA-CF"}},
- tray_info_idx="GFA50",
- tray_sub_brands="PLA-CF",
- tray_type="PLA",
- printer_id=printer.id,
- ams_id=1,
- tray_id=2,
- )
- mapping = (
- await db_session.execute(
- select(SlotPresetMapping).where(
- SlotPresetMapping.printer_id == printer.id,
- SlotPresetMapping.ams_id == 1,
- SlotPresetMapping.tray_id == 2,
- )
- )
- ).scalar_one()
- assert mapping.preset_name == "Bambu PLA-CF"
- assert mapping.preset_id == "GFSA50"
- @pytest.mark.asyncio
- async def test_spoolman_helper_skips_when_tray_info_idx_unknown(db_session, printer_factory):
- """No tray_info_idx → no preset_id → upsert skips. Caller must not
- write an empty-string preset_id (would clobber any existing row)."""
- printer = await printer_factory()
- await upsert_slot_preset_for_spoolman_spool(
- db=db_session,
- spoolman_spool={"id": 1, "filament": {"name": "Random"}},
- tray_info_idx="",
- tray_sub_brands="",
- tray_type="PLA",
- printer_id=printer.id,
- ams_id=0,
- tray_id=0,
- )
- result = await db_session.execute(select(SlotPresetMapping).where(SlotPresetMapping.printer_id == printer.id))
- assert result.scalar_one_or_none() is None
|