"""FC-2d-iii: loose non-media → PostAttachment (no skip).""" import pytest from sqlalchemy import func, select from backend.app.models import Artist, ImportSettings, PostAttachment from backend.app.services.importer import Importer from backend.app.services.thumbnailer import Thumbnailer pytestmark = pytest.mark.integration @pytest.fixture def import_layout(tmp_path): import_root = tmp_path / "import" images_root = tmp_path / "images" import_root.mkdir() images_root.mkdir() return import_root, images_root @pytest.fixture def importer(db_sync, import_layout): import_root, images_root = import_layout settings = db_sync.execute( select(ImportSettings).where(ImportSettings.id == 1) ).scalar_one() return Importer( session=db_sync, images_root=images_root, import_root=import_root, thumbnailer=Thumbnailer(images_root=images_root), settings=settings, ) def test_loose_non_media_becomes_attachment(importer, import_layout): import_root, _ = import_layout f = import_root / "Alice" / "manifesto.pdf" f.parent.mkdir(parents=True, exist_ok=True) f.write_bytes(b"%PDF-1.4 stuff") r = importer.import_one(f) assert r.status == "attached" att = importer.session.execute(select(PostAttachment)).scalar_one() artist = importer.session.execute( select(Artist).where(Artist.slug == "alice") ).scalar_one() assert att.artist_id == artist.id assert att.post_id is None assert att.original_filename == "manifesto.pdf" assert att.ext == ".pdf" def test_json_sidecar_is_not_attached(importer, import_layout): import_root, _ = import_layout j = import_root / "Alice" / "x.json" j.parent.mkdir(parents=True, exist_ok=True) j.write_text("{}") r = importer.import_one(j) assert r.status == "skipped" n = importer.session.execute( select(func.count()).select_from(PostAttachment) ).scalar_one() assert n == 0 def test_mangled_filename_extension_is_sanitized(importer, import_layout): """gallery-dl sometimes URL-encodes a query string into the basename (`...https___www.patreon.com_media-u_Z0F...`). Python's Path.suffix returns 50+ chars of base64-ish junk for those, which blows the PostAttachment.ext varchar(32) column. Operator-flagged 2026-05-25. The importer should record an empty ext rather than crash.""" import_root, _ = import_layout f = ( import_root / "Alice" / "79507046_media_https___www.patreon.com_media-u_Z0FBQUFBQm5q" ) f.parent.mkdir(parents=True, exist_ok=True) f.write_bytes(b"binary blob") r = importer.import_one(f) assert r.status == "attached" att = importer.session.execute(select(PostAttachment)).scalar_one() # Junk "extension" -> stored as empty string (not the 50-char garbage). assert att.ext == "" # original_filename is Text-typed so the full name survives intact. assert att.original_filename.endswith("_Z0FBQUFBQm5q")