test_library_photo_move.py 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. """``move_library_photos`` edge cases (#3077).
  2. The happy path is pinned by the scheduler's cleanup tests; this covers what
  3. they cannot reach — a name already taken in the destination, and a move that
  4. fails halfway.
  5. """
  6. import pytest
  7. from backend.app.core.config import settings
  8. from backend.app.utils.library_paths import library_photos_dir, move_library_photos
  9. @pytest.fixture
  10. def photo_dirs(monkeypatch, tmp_path):
  11. monkeypatch.setattr(settings, "archive_dir", tmp_path / "archive")
  12. destination = tmp_path / "archives" / "1" / "photos"
  13. source = library_photos_dir(7)
  14. source.mkdir(parents=True)
  15. return source, destination
  16. def test_moves_every_photo_and_drops_the_directory(photo_dirs):
  17. source, destination = photo_dirs
  18. (source / "a1b2c3d4.jpg").write_bytes(b"one")
  19. (source / "e5f6a7b8.png").write_bytes(b"two")
  20. moved = move_library_photos(7, ["a1b2c3d4.jpg", "e5f6a7b8.png"], destination)
  21. assert moved == ["a1b2c3d4.jpg", "e5f6a7b8.png"]
  22. assert (destination / "a1b2c3d4.jpg").read_bytes() == b"one"
  23. assert not source.exists()
  24. def test_renames_around_a_name_the_destination_already_holds(photo_dirs):
  25. source, destination = photo_dirs
  26. (source / "a1b2c3d4.jpg").write_bytes(b"library")
  27. destination.mkdir(parents=True)
  28. (destination / "a1b2c3d4.jpg").write_bytes(b"archive")
  29. moved = move_library_photos(7, ["a1b2c3d4.jpg"], destination)
  30. assert moved != ["a1b2c3d4.jpg"]
  31. assert moved[0].endswith(".jpg")
  32. assert (destination / "a1b2c3d4.jpg").read_bytes() == b"archive"
  33. assert (destination / moved[0]).read_bytes() == b"library"
  34. def test_a_traversal_name_is_skipped_and_keeps_the_directory(photo_dirs):
  35. # A stored name is uuid-generated, so this only happens to a row someone
  36. # has written to by hand — but the photos are then left alone rather than
  37. # swept away by a cleanup that could not move them.
  38. source, destination = photo_dirs
  39. (source / "a1b2c3d4.jpg").write_bytes(b"one")
  40. moved = move_library_photos(7, ["../escape.jpg", "a1b2c3d4.jpg"], destination)
  41. assert moved == ["a1b2c3d4.jpg"]
  42. assert source.is_dir()
  43. def test_a_file_without_photos_is_a_no_op(photo_dirs):
  44. source, destination = photo_dirs
  45. source.rmdir()
  46. assert move_library_photos(7, [], destination) == []
  47. assert not destination.exists()