test_reprint_unplated_timelapse.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. """A reprint of a single-plate archive does not leave the old video behind.
  2. A video attached by hand, or before the plate column existed, has no plate on
  3. record. On a reprint it was always kept, because on a Send All archive it can
  4. be another plate's only copy; on an archive with one plate there is no other
  5. plate, so it was just an orphan file per reprint. It is now deleted there, and
  6. still kept when the archive's 3MF has several plates or cannot be read.
  7. """
  8. import zipfile
  9. from contextlib import ExitStack
  10. from unittest.mock import AsyncMock, MagicMock, patch
  11. import pytest
  12. from backend.app.core.config import settings as app_settings
  13. from backend.app.main import register_expected_print
  14. from backend.tests.unit.test_reprint_clears_stale_timelapse import ( # noqa: F401
  15. _build_mocks,
  16. _clear_dicts,
  17. _patches,
  18. )
  19. pytestmark = pytest.mark.asyncio
  20. def _three_mf(path, plates: int | None):
  21. path.parent.mkdir(parents=True, exist_ok=True)
  22. if plates is None:
  23. path.write_bytes(b"not a zip")
  24. return
  25. body = "".join(f'<plate><metadata key="index" value="{n}"/></plate>' for n in range(1, plates + 1))
  26. with zipfile.ZipFile(path, "w") as zf:
  27. zf.writestr("Metadata/slice_info.config", f"<config>{body}</config>")
  28. async def _reprint(tmp_path, plates):
  29. relpath = "archives/42/timelapse/by-hand.mp4"
  30. video = tmp_path / relpath
  31. video.parent.mkdir(parents=True, exist_ok=True)
  32. video.write_bytes(b"old video")
  33. _three_mf(tmp_path / "archives/42/MyModel.3mf", plates)
  34. printer = MagicMock(id=1, auto_archive=True, external_camera_enabled=False, external_camera_url=None)
  35. archive = MagicMock(
  36. id=42,
  37. filename="MyModel.3mf",
  38. subtask_id=None,
  39. print_time_seconds=None,
  40. created_by_id=None,
  41. printer_id=1,
  42. print_name="MyModel",
  43. status="archived",
  44. file_path="archives/42/MyModel.3mf",
  45. energy_start_kwh=None,
  46. timelapse_path=relpath,
  47. timelapse_plate_id=None, # not on record
  48. )
  49. register_expected_print(1, "MyModel.3mf", archive_id=42, ams_mapping=None, plate_id=1)
  50. session = _build_mocks(printer, archive)
  51. patches = _patches()
  52. with ExitStack() as stack:
  53. maker, notif, plug, ws, pm, relay = (stack.enter_context(p) for p in patches[:6])
  54. for extra in patches[6:]:
  55. stack.enter_context(extra)
  56. stack.enter_context(patch.object(app_settings, "base_dir", tmp_path))
  57. # The mock printer has no camera to reach.
  58. stack.enter_context(
  59. patch("backend.app.services.camera.capture_camera_frame_bytes", AsyncMock(return_value=None))
  60. )
  61. maker.return_value = session
  62. notif.on_print_start = AsyncMock()
  63. plug.on_print_start = AsyncMock()
  64. ws.send_print_start = AsyncMock()
  65. ws.send_archive_updated = AsyncMock()
  66. relay.on_print_start = AsyncMock()
  67. pm.get_printer = MagicMock(return_value=MagicMock(name="Test", serial_number="TEST123"))
  68. from backend.app.main import on_print_start
  69. await on_print_start(1, {"filename": "MyModel.3mf", "subtask_name": "MyModel"})
  70. return video, archive
  71. async def test_on_a_single_plate_archive_it_is_deleted(tmp_path):
  72. video, archive = await _reprint(tmp_path, plates=1)
  73. assert archive.timelapse_path is None
  74. assert not video.exists()
  75. @pytest.mark.parametrize("plates", [2, None], ids=["several-plates", "unreadable-3mf"])
  76. async def test_where_it_could_be_another_plates_it_is_kept(tmp_path, plates):
  77. video, archive = await _reprint(tmp_path, plates=plates)
  78. assert archive.timelapse_path is None
  79. assert video.exists()