"""An empty answer is not an emptied library (E32 S2). Every mirror here reconciles: what the remote no longer lists is removed locally. The catalog (`_prune_stale`) and the subscriptions (`should_prune`) had long refused to do that on an empty listing that contradicts what we hold — a 200 with no items is a blip far more often than a user who removed everything. Two mirrors did not: the Plex collections (a row carries the user-set `editable` flag, and every member its keys) and the YouTube playlist mirrors. These hold both. """ from sqlalchemy import select from app.models import Playlist, PlexCollection, PlexItem, PlexLibrary from app.plex import sync as plex_sync from app.sync import playlists as plsync class _Plex: def __init__(self, collections: list[dict], children: dict[str, list[dict]]): self._collections = collections self._children = children def collections(self, section_key): return self._collections def collection_children(self, rk): return self._children.get(rk, []) def _library_with_a_collection(db): lib = PlexLibrary(plex_key="4", title="Movies", kind="movie") db.add(lib) db.flush() db.add(PlexCollection(rating_key="c1", library_id=lib.id, title="Bond", editable=True)) movie = PlexItem( rating_key="m1", library_id=lib.id, kind="movie", title="Dr. No", collection_keys=["c1"] ) db.add(movie) db.commit() return lib, movie def test_an_empty_collection_listing_keeps_the_collections_and_their_members(db): lib, movie = _library_with_a_collection(db) stats = {"collections": 0} plex_sync._sync_collections(db, _Plex([], {}), lib, stats) db.commit() db.expire_all() col = db.execute(select(PlexCollection).where(PlexCollection.rating_key == "c1")).scalar_one() assert col.editable is True assert db.get(PlexItem, movie.id).collection_keys == ["c1"] def test_a_listing_with_something_in_it_still_prunes_what_it_no_longer_has(db): lib, movie = _library_with_a_collection(db) stats = {"collections": 0} other = {"ratingKey": "c2", "title": "Alien"} plex_sync._sync_collections(db, _Plex([other], {"c2": []}), lib, stats) db.commit() db.expire_all() keys = set(db.execute(select(PlexCollection.rating_key)).scalars()) assert keys == {"c2"} assert db.get(PlexItem, movie.id).collection_keys is None class _YT: def __init__(self, playlists: list[dict]): self._playlists = playlists def __call__(self, *_): return self def __enter__(self): return self def __exit__(self, *_): return False def iter_my_playlists(self): return self._playlists def iter_playlist_items_with_ids(self, _ytid): return [] def iter_my_playlist_video_ids(self, _ytid): return [] def test_an_empty_playlist_answer_keeps_the_youtube_mirrors(db, make_user, monkeypatch): user = make_user() db.add(Playlist(user_id=user.id, name="Mixes", source="youtube", yt_playlist_id="PL1")) db.commit() monkeypatch.setattr(plsync, "YouTubeClient", _YT([])) plsync.pull_user_playlists(db, user) db.expire_all() names = db.execute(select(Playlist.name).where(Playlist.user_id == user.id)).scalars().all() assert "Mixes" in names def test_a_playlist_answer_with_something_in_it_still_drops_a_vanished_mirror( db, make_user, monkeypatch ): user = make_user() db.add(Playlist(user_id=user.id, name="Mixes", source="youtube", yt_playlist_id="PL1")) db.commit() monkeypatch.setattr(plsync, "YouTubeClient", _YT([{"id": "PL2", "title": "New"}])) plsync.pull_user_playlists(db, user) db.expire_all() ytids = set( db.execute(select(Playlist.yt_playlist_id).where(Playlist.source == "youtube")).scalars() ) assert "PL1" not in ytids