"""Migration 0114 (#4435): the undated shell post a misfiled attachment made on the artist's first Discord source is folded into the real, dated post.""" import importlib.util from datetime import UTC, datetime from pathlib import Path import pytest from sqlalchemy import select from backend.app.models import ( Artist, ImageProvenance, Post, PostAttachment, Source, ) from tests.factories import make_image as _img pytestmark = pytest.mark.integration _MIGRATION = ( Path(__file__).resolve().parents[1] / "alembic" / "versions" / "0114_fold_misfiled_attachment_posts.py" ) def _fold(): spec = importlib.util.spec_from_file_location("m0114", _MIGRATION) mod = importlib.util.module_from_spec(spec) spec.loader.exec_module(mod) return mod.fold_misfiled_attachment_posts def _source(db, artist, channel): s = Source( artist_id=artist.id, platform="discord", url=f"https://discord.com/channels/1/{channel}", ) db.add(s) db.flush() return s def _post(db, artist, source, epid, when=None): p = Post( artist_id=artist.id, source_id=source.id, external_post_id=epid, post_date=when, post_url=f"https://discord.com/channels/1/x/{epid}" if when else None, ) db.add(p) db.flush() return p def _attach(db, post, sha, name): db.add(PostAttachment( post_id=post.id, artist_id=post.artist_id, sha256=sha, path=f"/att/{sha}", original_filename=name, ext=".rar", size_bytes=1, )) db.flush() def test_shells_fold_into_their_dated_twin_and_nothing_else_moves(db_sync): sent = datetime(2024, 12, 26, 1, 42, tzinfo=UTC) artist = Artist(name="Yellow", slug="yellow") db_sync.add(artist) db_sync.flush() first = _source(db_sync, artist, 100) second = _source(db_sync, artist, 200) # The #4435 shape: shell on the first source, real post on the second. shell = _post(db_sync, artist, first, "555") real = _post(db_sync, artist, second, "555", sent) _attach(db_sync, shell, "a" * 64, "pack.rar") # A shell whose attachment the real post already has: dropped, not doubled. shell2 = _post(db_sync, artist, first, "556") real2 = _post(db_sync, artist, second, "556", sent) _attach(db_sync, shell2, "b" * 64, "same.rar") _attach(db_sync, real2, "b" * 64, "same.rar") # Left alone: no dated twin, and an undated post that holds an image. lonely = _post(db_sync, artist, first, "557") _attach(db_sync, lonely, "c" * 64, "lonely.rar") with_image = _post(db_sync, artist, first, "558") _post(db_sync, artist, second, "558", sent) img = _img(db_sync, "d" * 64) db_sync.add(ImageProvenance(image_record_id=img.id, post_id=with_image.id)) db_sync.flush() shell_id, shell2_id = shell.id, shell2.id folded = _fold()(db_sync.connection()) db_sync.expire_all() assert folded == 2 assert db_sync.get(Post, shell_id) is None assert db_sync.get(Post, shell2_id) is None owners = dict(db_sync.execute( select(PostAttachment.original_filename, PostAttachment.post_id) ).all()) assert owners["pack.rar"] == real.id assert owners["lonely.rar"] == lonely.id kept = db_sync.execute( select(PostAttachment.id).where(PostAttachment.post_id == real2.id) ).scalars().all() assert len(kept) == 1 assert db_sync.get(Post, lonely.id) is not None assert db_sync.get(Post, with_image.id) is not None