Add a right-click copy link and share menu on gallery photos with stable per-photo permalinks
CI / build-and-scan (pull_request) Successful in 2m9s
CI / build-and-scan (pull_request) Successful in 2m9s
This commit is contained in:
@@ -135,3 +135,79 @@ def test_generate_favicon_ico_no_favicon_skips(tmp_path):
|
||||
output_path = tmp_path / "output" / "favicon.ico"
|
||||
ip.generate_favicon_ico({}, tmp_path, output_path)
|
||||
assert not output_path.exists()
|
||||
|
||||
|
||||
def test_compute_photo_ids_hashes_original_files(tmp_path, make_image):
|
||||
import hashlib
|
||||
|
||||
img_dir = tmp_path / "photos"
|
||||
a = make_image(img_dir / "gallery" / "a.jpg", color=(1, 2, 3))
|
||||
make_image(img_dir / "gallery" / "b.jpg", color=(200, 10, 10))
|
||||
images = [{"src": "gallery/a.jpg"}, {"src": "gallery/b.jpg"}, {"src": "gallery/missing.jpg"}]
|
||||
|
||||
ip.compute_photo_ids(images, img_dir)
|
||||
|
||||
assert images[0]["photo_id"] == hashlib.sha256(a.read_bytes()).hexdigest()
|
||||
assert len(images[1]["photo_id"]) == 64
|
||||
assert images[0]["photo_id"] != images[1]["photo_id"]
|
||||
assert "photo_id" not in images[2]
|
||||
|
||||
|
||||
def test_compute_photo_ids_is_independent_of_order_and_name(tmp_path, make_image):
|
||||
img_dir = tmp_path / "photos"
|
||||
make_image(img_dir / "gallery" / "a.jpg", color=(1, 2, 3))
|
||||
(img_dir / "gallery" / "renamed.jpg").write_bytes((img_dir / "gallery" / "a.jpg").read_bytes())
|
||||
make_image(img_dir / "gallery" / "b.jpg", color=(9, 9, 9))
|
||||
first = [{"src": "gallery/a.jpg"}, {"src": "gallery/b.jpg"}]
|
||||
second = [{"src": "gallery/b.jpg"}, {"src": "gallery/renamed.jpg"}]
|
||||
|
||||
ip.compute_photo_ids(first, img_dir)
|
||||
ip.compute_photo_ids(second, img_dir)
|
||||
|
||||
assert first[0]["photo_id"] == second[1]["photo_id"]
|
||||
assert first[1]["photo_id"] == second[0]["photo_id"]
|
||||
|
||||
|
||||
def test_compute_photo_ids_suffixes_duplicates(tmp_path, make_image):
|
||||
img_dir = tmp_path / "photos"
|
||||
make_image(img_dir / "gallery" / "a.jpg")
|
||||
images = [{"src": "gallery/a.jpg"}, {"src": "gallery/a.jpg"}, {"src": "gallery/a.jpg"}]
|
||||
|
||||
ip.compute_photo_ids(images, img_dir)
|
||||
|
||||
base = images[0]["photo_id"]
|
||||
assert images[1]["photo_id"] == f"{base}-2"
|
||||
assert images[2]["photo_id"] == f"{base}-3"
|
||||
|
||||
|
||||
def test_generate_share_preview_resizes_to_jpeg(tmp_path, make_image):
|
||||
input_path = make_image(tmp_path / "in.png", size=(2400, 1600), fmt="PNG")
|
||||
output_path = tmp_path / "share" / "x.jpg"
|
||||
|
||||
size = ip.generate_share_preview(input_path, output_path)
|
||||
|
||||
assert size == (1200, 800)
|
||||
with Image.open(output_path) as img:
|
||||
assert img.format == "JPEG"
|
||||
assert img.size == (1200, 800)
|
||||
|
||||
|
||||
def test_generate_share_preview_converts_embedded_profile_to_srgb(tmp_path):
|
||||
from PIL import ImageCms
|
||||
|
||||
input_path = tmp_path / "in.jpg"
|
||||
srgb_bytes = ImageCms.ImageCmsProfile(ImageCms.createProfile("sRGB")).tobytes()
|
||||
Image.new("RGB", (300, 200), (10, 200, 30)).save(input_path, "JPEG", icc_profile=srgb_bytes)
|
||||
output_path = tmp_path / "out.jpg"
|
||||
|
||||
size = ip.generate_share_preview(input_path, output_path)
|
||||
|
||||
assert size == (300, 200)
|
||||
with Image.open(output_path) as img:
|
||||
assert img.mode == "RGB"
|
||||
|
||||
|
||||
def test_generate_share_preview_failure_returns_none(tmp_path):
|
||||
bad = tmp_path / "bad.jpg"
|
||||
bad.write_bytes(b"not an image")
|
||||
assert ip.generate_share_preview(bad, tmp_path / "out.jpg") is None
|
||||
|
||||
Reference in New Issue
Block a user