"""Hashing: byte identity vs normalized-pixel identity.""" import numpy as np from PIL import Image from photo_pipeline.services import hashing def _write(path, seed, size=(48, 32)): rng = np.random.default_rng(seed) arr = rng.integers(0, 256, (size[1], size[0], 3), dtype=np.uint8) Image.fromarray(arr).save(path, quality=95) def test_sha256_is_deterministic_and_distinct(tmp_path): a, b = tmp_path / "a.jpg", tmp_path / "b.jpg" _write(a, 1) _write(b, 2) assert hashing.sha256_file(a) == hashing.sha256_file(a) assert hashing.sha256_file(a) != hashing.sha256_file(b) def test_trailing_bytes_change_sha_but_not_pixels(tmp_path): """Appending bytes after a JPEG (an EXIF-only-style edit) changes the file hash while decoded pixels — and the pixel hash — stay the same.""" img = tmp_path / "a.jpg" _write(img, 1) sha_before = hashing.sha256_file(img) pix_before = hashing.pixel_sha256(img) with open(img, "ab") as handle: handle.write(b"\xff\xfe trailing comment bytes") assert hashing.sha256_file(img) != sha_before assert hashing.pixel_sha256(img) == pix_before def test_different_images_have_different_pixel_hashes(tmp_path): a, b = tmp_path / "a.jpg", tmp_path / "b.jpg" _write(a, 1) _write(b, 2) assert hashing.pixel_sha256(a) != hashing.pixel_sha256(b) def test_safe_pixel_sha256_returns_none_for_garbage(tmp_path): bad = tmp_path / "bad.jpg" bad.write_bytes(b"not an image") assert hashing.safe_pixel_sha256(bad) is None