from app.models.channel import Channel from app.models.video import Video from app.services import sync def _fake_credentials(): return object() def test_sync_subscriptions_idempotent_and_unsubscribes(monkeypatch, db_session): monkeypatch.setattr(sync.google_oauth, "get_credentials", lambda db: _fake_credentials()) monkeypatch.setattr( sync.youtube_client, "fetch_subscriptions", lambda creds: [ {"youtube_channel_id": "chanA", "title": "Channel A", "description": "d", "thumbnail_url": "t"}, {"youtube_channel_id": "chanB", "title": "Channel B", "description": "d", "thumbnail_url": "t"}, ], ) monkeypatch.setattr( sync.youtube_client, "fetch_uploads_playlists", lambda creds, ids: {cid: f"UU{cid}" for cid in ids}, ) result = sync.sync_subscriptions(db_session) assert result["status"] == "completed" assert result["channels_added"] == 2 assert result["channels_unsubscribed"] == 0 channels = db_session.query(Channel).order_by(Channel.youtube_channel_id).all() assert [c.youtube_channel_id for c in channels] == ["chanA", "chanB"] assert all(c.subscribed for c in channels) assert channels[0].uploads_playlist_id == "UUchanA" # Second sync: chanA disappears from subscriptions, chanC appears. monkeypatch.setattr( sync.youtube_client, "fetch_subscriptions", lambda creds: [ {"youtube_channel_id": "chanB", "title": "Channel B", "description": "d", "thumbnail_url": "t"}, {"youtube_channel_id": "chanC", "title": "Channel C", "description": "d", "thumbnail_url": "t"}, ], ) result2 = sync.sync_subscriptions(db_session) assert result2["channels_added"] == 1 assert result2["channels_updated"] == 1 assert result2["channels_unsubscribed"] == 1 channels = {c.youtube_channel_id: c for c in db_session.query(Channel).all()} assert len(channels) == 3 assert channels["chanA"].subscribed is False assert channels["chanB"].subscribed is True assert channels["chanC"].subscribed is True def test_sync_in_progress_raises(monkeypatch, db_session): sync._subscriptions_lock.acquire() try: try: sync.sync_subscriptions(db_session) assert False, "expected SyncInProgress" except sync.SyncInProgress: pass finally: sync._subscriptions_lock.release() def _seed_channel(db_session, youtube_channel_id="chanA", uploads_playlist_id="UUchanA"): channel = Channel( youtube_channel_id=youtube_channel_id, title="Channel", subscribed=True, uploads_playlist_id=uploads_playlist_id, ) db_session.add(channel) db_session.commit() db_session.refresh(channel) return channel def test_sync_videos_adds_and_updates(monkeypatch, db_session): channel = _seed_channel(db_session) monkeypatch.setattr(sync.google_oauth, "get_credentials", lambda db: object()) monkeypatch.setattr( sync.youtube_client, "fetch_playlist_video_ids", lambda creds, playlist_id, max_results: ["vid1"] ) monkeypatch.setattr( sync.youtube_client, "fetch_videos_details", lambda creds, ids: [ { "youtube_video_id": "vid1", "youtube_channel_id": channel.youtube_channel_id, "title": "Video One", "description": "d", "thumbnail_url": "t", "published_at": "2026-09-10T12:00:00Z", "duration_iso8601": "PT5M", } ], ) result = sync.sync_videos(db_session) assert result["status"] == "completed" assert result["videos_added"] == 1 video = db_session.query(Video).filter_by(youtube_video_id="vid1").one() assert video.title == "Video One" assert video.duration_seconds == 300 assert video.youtube_url == "https://www.youtube.com/watch?v=vid1" monkeypatch.setattr( sync.youtube_client, "fetch_videos_details", lambda creds, ids: [ { "youtube_video_id": "vid1", "youtube_channel_id": channel.youtube_channel_id, "title": "Video One Updated", "description": "d2", "thumbnail_url": "t2", "published_at": "2026-09-10T12:00:00Z", "duration_iso8601": "PT6M", } ], ) result2 = sync.sync_videos(db_session) assert result2["videos_added"] == 0 assert result2["videos_updated"] == 1 videos = db_session.query(Video).all() assert len(videos) == 1 assert videos[0].title == "Video One Updated" def test_sync_videos_skips_unknown_channel(monkeypatch, db_session): _seed_channel(db_session, "chanA") monkeypatch.setattr(sync.google_oauth, "get_credentials", lambda db: object()) monkeypatch.setattr(sync.youtube_client, "fetch_playlist_video_ids", lambda creds, playlist_id, max_results: []) monkeypatch.setattr( sync.youtube_client, "fetch_videos_details", lambda creds, ids: [ { "youtube_video_id": "vidX", "youtube_channel_id": "unknown-channel", "title": "Orphan", "description": "", "thumbnail_url": None, "published_at": "2026-09-10T12:00:00Z", "duration_iso8601": None, } ], ) result = sync.sync_videos(db_session) assert result["videos_skipped"] == 1 assert db_session.query(Video).count() == 0