feat(attachments): fail-soft archive_extractor (zip/cbz/rar/7z)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-19 11:11:37 -04:00
parent 1475bad48b
commit df76fd75d8
3 changed files with 125 additions and 0 deletions
+64
View File
@@ -0,0 +1,64 @@
import zipfile
from pathlib import Path
import pytest
from backend.app.services.archive_extractor import extract_archive, is_archive
def test_is_archive():
assert is_archive(Path("a.zip"))
assert is_archive(Path("a.CBZ"))
assert is_archive(Path("a.rar"))
assert is_archive(Path("a.7z"))
assert not is_archive(Path("a.jpg"))
def _zip(path, entries):
with zipfile.ZipFile(path, "w") as zf:
for name, data in entries.items():
zf.writestr(name, data)
def test_zip_members_enumerated(tmp_path):
z = tmp_path / "set.zip"
_zip(z, {"a.jpg": b"img", "sub/b.png": b"img2", "notes.txt": b"hi"})
with extract_archive(z) as members:
names = sorted(n for n, _ in members)
assert names == ["a.jpg", "notes.txt", "sub/b.png"]
for _, p in members:
assert p.is_file()
for _, p in members:
assert not p.exists() # temp cleaned on exit
def test_cbz_alias(tmp_path):
z = tmp_path / "set.cbz"
_zip(z, {"p1.jpg": b"i"})
with extract_archive(z) as members:
assert [n for n, _ in members] == ["p1.jpg"]
def test_corrupt_archive_yields_nothing(tmp_path):
bad = tmp_path / "broken.zip"
bad.write_bytes(b"not a zip at all")
with extract_archive(bad) as members:
assert members == []
def test_7z_roundtrip(tmp_path):
py7zr = pytest.importorskip("py7zr")
a = tmp_path / "s.7z"
(tmp_path / "x.jpg").write_bytes(b"img")
with py7zr.SevenZipFile(a, "w") as zf:
zf.write(tmp_path / "x.jpg", "x.jpg")
with extract_archive(a) as members:
assert [n for n, _ in members] == ["x.jpg"]
def test_rar_corrupt_is_failsoft(tmp_path):
pytest.importorskip("rarfile")
bad = tmp_path / "b.rar"
bad.write_bytes(b"Rar! not really")
with extract_archive(bad) as members:
assert members == [] # invalid / no unrar → fail-soft, nothing lost