84 lines
2.8 KiB
Python
84 lines
2.8 KiB
Python
"""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
|