test_run_estimate_plate.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. """The print-log estimate covers the plate that printed.
  2. The archive keeps the plate of its first print. A later run of another plate
  3. of the same multi-plate file, dispatched directly or from the queue, has to be
  4. estimated for that plate, not for the archive's.
  5. """
  6. import asyncio
  7. from unittest.mock import AsyncMock, MagicMock, patch
  8. import pytest
  9. from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
  10. from backend.app import main as main_module
  11. from backend.app.models.print_queue import PrintQueueItem
  12. from backend.app.services.bambu_ftp import DeleteResult
  13. @pytest.fixture(autouse=True)
  14. def _clean_state():
  15. main_module._active_prints.clear()
  16. main_module._print_plate_ids.clear()
  17. yield
  18. main_module._active_prints.clear()
  19. main_module._print_plate_ids.clear()
  20. async def _complete(test_engine, printer_id: int) -> list:
  21. """Run on_print_complete against the test database; return the plates the
  22. estimates were asked for (billing first, then the print log)."""
  23. real_db = async_sessionmaker(test_engine, class_=AsyncSession, expire_on_commit=False)
  24. asked: list = []
  25. def _estimate(archive, full_path, plate_id=None):
  26. asked.append(plate_id)
  27. return None, None
  28. tasks_before = set(asyncio.all_tasks())
  29. with (
  30. patch("backend.app.main.async_session", real_db),
  31. patch("backend.app.core.database.async_session", real_db),
  32. patch("backend.app.main._plate_scoped_run_estimate", side_effect=_estimate),
  33. patch("backend.app.main.ws_manager") as mock_ws,
  34. patch("backend.app.main.printer_manager") as mock_pm,
  35. patch("backend.app.main.mqtt_relay") as mock_relay,
  36. patch("backend.app.main.notification_service") as mock_notif,
  37. patch("backend.app.main.smart_plug_manager") as mock_plug,
  38. patch("backend.app.main.clear_3mf_cache"),
  39. patch("backend.app.services.bambu_ftp.delete_file_async", new=AsyncMock(return_value=DeleteResult.DELETED)),
  40. ):
  41. mock_ws.send_print_complete = AsyncMock()
  42. mock_ws.broadcast = AsyncMock()
  43. mock_relay.on_print_complete = AsyncMock()
  44. mock_relay.on_queue_job_completed = AsyncMock()
  45. mock_notif.on_print_complete = AsyncMock()
  46. mock_plug.on_print_complete = AsyncMock()
  47. mock_pm.get_printer = MagicMock(return_value=MagicMock(name="Test", serial_number="TEST123"))
  48. mock_pm.get_current_print_user = MagicMock(return_value=None)
  49. mock_pm.clear_current_print_user = MagicMock()
  50. mock_pm.set_awaiting_plate_clear = MagicMock()
  51. await main_module.on_print_complete(
  52. printer_id,
  53. {"status": "completed", "filename": "multi.gcode.3mf", "subtask_name": "multi", "progress": 100},
  54. )
  55. for task in asyncio.all_tasks() - tasks_before:
  56. task.cancel()
  57. try:
  58. await task
  59. except (asyncio.CancelledError, Exception):
  60. pass
  61. return asked
  62. @pytest.mark.asyncio
  63. async def test_a_direct_print_is_estimated_for_its_plate(test_engine, db_session, printer_factory, archive_factory):
  64. printer = await printer_factory()
  65. archive = await archive_factory(
  66. printer.id, filename="multi.gcode.3mf", plate_id=1, status="printing", with_run=False
  67. )
  68. main_module._active_prints[(printer.id, "multi.gcode.3mf")] = archive.id
  69. main_module._print_plate_ids[archive.id] = 3
  70. assert await _complete(test_engine, printer.id) == [3, 3]
  71. @pytest.mark.asyncio
  72. async def test_a_queue_run_is_estimated_for_the_queue_items_plate(
  73. test_engine, db_session, printer_factory, archive_factory
  74. ):
  75. """After a restart the in-memory plate is gone; the queue item still has it."""
  76. printer = await printer_factory()
  77. archive = await archive_factory(
  78. printer.id, filename="multi.gcode.3mf", plate_id=1, status="printing", with_run=False
  79. )
  80. db_session.add(PrintQueueItem(printer_id=printer.id, archive_id=archive.id, plate_id=2, status="printing"))
  81. await db_session.commit()
  82. main_module._active_prints[(printer.id, "multi.gcode.3mf")] = archive.id
  83. assert await _complete(test_engine, printer.id) == [2, 2]