test_vp_restamp_claimed_item.py 3.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. """The late slicer update leaves a queue job the scheduler has claimed alone.
  2. The slicer's options can arrive up to 30 s after a virtual-printer job was
  3. queued. By then the scheduler may have claimed the job (``dispatching_at``
  4. set, status still "pending") and started its upload with the old mapping;
  5. rewriting the row then made it, and the spool charge read from it, disagree
  6. with what the printer printed.
  7. """
  8. import json
  9. from datetime import datetime
  10. from unittest.mock import MagicMock, patch
  11. import pytest
  12. from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
  13. from backend.app.models.print_queue import PrintQueueItem
  14. from backend.app.services.virtual_printer.manager import VirtualPrinterInstance
  15. pytestmark = [pytest.mark.asyncio, pytest.mark.integration]
  16. async def test_only_the_unclaimed_job_is_restamped(test_engine, db_session, printer_factory, tmp_path):
  17. printer = await printer_factory()
  18. claimed = PrintQueueItem(printer_id=printer.id, status="pending", position=1, dispatching_at=datetime.utcnow())
  19. waiting = PrintQueueItem(printer_id=printer.id, status="pending", position=2)
  20. db_session.add_all([claimed, waiting])
  21. await db_session.commit()
  22. inst = VirtualPrinterInstance(
  23. target_printer_id=printer.id,
  24. vp_id=97,
  25. name="LateMQTTClaimed",
  26. mode="queue",
  27. model="O1C2",
  28. access_code="12345678",
  29. serial_suffix="391800097",
  30. base_dir=tmp_path,
  31. session_factory=async_sessionmaker(test_engine, class_=AsyncSession, expire_on_commit=False),
  32. )
  33. inst._mqtt = MagicMock()
  34. inst._recent_queue_items["test.3mf"] = ([claimed.id, waiting.id], 1_000_000.0)
  35. with patch("backend.app.services.virtual_printer.manager.time.monotonic", return_value=1_000_001.0):
  36. await inst.on_print_command(
  37. "test.3mf", {"command": "project_file", "file": "test.3mf", "nozzle_mapping": [16, -1]}
  38. )
  39. await db_session.refresh(claimed)
  40. await db_session.refresh(waiting)
  41. assert claimed.nozzle_mapping is None
  42. assert json.loads(waiting.nozzle_mapping) == [16, -1]
  43. async def test_the_slicer_s_pick_is_still_kept_on_a_claimed_job_s_archive(
  44. test_engine, db_session, printer_factory, archive_factory, tmp_path
  45. ):
  46. """The archive copy only serves later reprints, so it does not race the
  47. upload: a claimed job's archive still gets the slicer's AMS pick."""
  48. printer = await printer_factory()
  49. archive = await archive_factory(printer.id)
  50. claimed = PrintQueueItem(
  51. printer_id=printer.id, archive_id=archive.id, status="pending", position=1, dispatching_at=datetime.utcnow()
  52. )
  53. db_session.add(claimed)
  54. await db_session.commit()
  55. inst = VirtualPrinterInstance(
  56. target_printer_id=printer.id,
  57. vp_id=96,
  58. name="LateMQTTArchive",
  59. mode="queue",
  60. model="O1C2",
  61. access_code="12345678",
  62. serial_suffix="391800096",
  63. base_dir=tmp_path,
  64. session_factory=async_sessionmaker(test_engine, class_=AsyncSession, expire_on_commit=False),
  65. )
  66. inst._mqtt = MagicMock()
  67. inst.save_ams_mapping = True
  68. inst._recent_queue_items["test.3mf"] = ([claimed.id], 1_000_000.0)
  69. with patch("backend.app.services.virtual_printer.manager.time.monotonic", return_value=1_000_001.0):
  70. await inst.on_print_command("test.3mf", {"command": "project_file", "file": "test.3mf", "ams_mapping": [2]})
  71. await db_session.refresh(archive)
  72. await db_session.refresh(claimed)
  73. assert (archive.extra_data or {}).get("slicer_ams_mapping", {}).get("mapping") == [2]
  74. assert claimed.ams_mapping is None