"""Path policy: supported extensions, exclusion, deterministic discovery, and symlink-escape rejection.""" import os import pytest from photo_pipeline import path_policy def test_is_supported_is_case_insensitive(): assert path_policy.is_supported("a.jpg") assert path_policy.is_supported("a.JPG") assert path_policy.is_supported("a.HEIC") assert not path_policy.is_supported("a.txt") assert not path_policy.is_supported("a.mp4") def test_is_excluded_matches_ignore_and_thumb_anywhere(): assert path_policy.is_excluded("lib/_IGNORE/secret.jpg") assert path_policy.is_excluded("lib/album/.@__thumb/t.jpg") assert not path_policy.is_excluded("lib/album/photo.jpg") def test_discovery_finds_supported_and_skips_excluded_and_unsupported(tmp_path): (tmp_path / "album").mkdir() (tmp_path / "_IGNORE").mkdir() (tmp_path / "album" / ".@__thumb").mkdir() keep = [tmp_path / "root.jpg", tmp_path / "album" / "a.PNG"] for p in keep: p.write_bytes(b"x") (tmp_path / "notes.txt").write_bytes(b"x") (tmp_path / "_IGNORE" / "secret.jpg").write_bytes(b"x") (tmp_path / "album" / ".@__thumb" / "thumb.jpg").write_bytes(b"x") found = path_policy.discover([tmp_path]) assert found == sorted(keep) def test_discovery_is_deterministic(tmp_path): for i in range(5): (tmp_path / f"{i}.jpg").write_bytes(b"x") assert path_policy.discover([tmp_path]) == path_policy.discover([tmp_path]) def test_resolve_within_accepts_inside_and_rejects_outside(tmp_path): root = tmp_path / "lib" root.mkdir() inside = root / "a.jpg" inside.write_bytes(b"x") assert path_policy.resolve_within(root, inside) == inside.resolve() with pytest.raises(path_policy.PathPolicyError): path_policy.resolve_within(root, tmp_path / "outside.jpg") @pytest.mark.skipif(not hasattr(os, "symlink"), reason="symlinks unsupported") def test_symlink_escaping_root_fails_discovery(tmp_path): root = tmp_path / "lib" root.mkdir() outside = tmp_path / "outside.jpg" outside.write_bytes(b"x") link = root / "link.jpg" try: os.symlink(outside, link) except (OSError, NotImplementedError): pytest.skip("cannot create symlink on this platform") with pytest.raises(path_policy.PathPolicyError): path_policy.discover([root]) @pytest.mark.skipif(not hasattr(os, "symlink"), reason="symlinks unsupported") def test_symlink_within_root_is_allowed(tmp_path): root = tmp_path / "lib" root.mkdir() target = root / "real.jpg" target.write_bytes(b"x") link = root / "alias.jpg" try: os.symlink(target, link) except (OSError, NotImplementedError): pytest.skip("cannot create symlink on this platform") found = path_policy.discover([root]) assert target in found and link in found