import pytest import os import json import aiosqlite import fetcher import asyncio from unittest.mock import patch, AsyncMock class MockProcess: def __init__(self, stdout_data, returncode=0, stderr_data=b""): self.stdout_data = stdout_data self.stderr_data = stderr_data self.returncode = returncode async def communicate(self): return self.stdout_data, self.stderr_data @pytest.mark.asyncio async def test_fetch_subscriptions_no_cookies(tmp_path, monkeypatch): monkeypatch.setattr(fetcher, "COOKIES_PATH", "/non/existent/path/cookies.txt") count = await fetcher.fetch_subscriptions() assert count == 0 @pytest.mark.asyncio async def test_fetch_subscriptions_success(test_db, tmp_path, monkeypatch): cookies_file = os.path.join(tmp_path, "mock_cookies.txt") with open(cookies_file, "w") as f: f.write("# Netscape HTTP Cookie File") monkeypatch.setattr(fetcher, "COOKIES_PATH", cookies_file) video1 = { "id": "vid123", "title": "Mock Video 1", "uploader": "Uploader One", "uploader_id": "chan123", "upload_date": "20260601", "duration": 360, "thumbnail": "https://img.youtube.com/vi/vid123/hqdefault.jpg" } video2 = { "id": "vid456", "title": "Mock Video 2", "uploader": "Uploader Two", "uploader_id": "chan456", "upload_date": "20260602", "duration": 720 } stdout_bytes = (json.dumps(video1) + "\n" + json.dumps(video2) + "\n").encode("utf-8") mock_process = MockProcess(stdout_data=stdout_bytes, returncode=0) with patch("asyncio.create_subprocess_exec", return_value=mock_process) as mock_exec: count = await fetcher.fetch_subscriptions() mock_exec.assert_called_once() args = mock_exec.call_args[0] assert "yt-dlp" in args assert "https://www.youtube.com/feed/subscriptions" in args assert cookies_file in args assert count == 2 # Query db to verify values cursor = await test_db.execute("SELECT * FROM videos ORDER BY video_id") videos = await cursor.fetchall() assert len(videos) == 2 assert videos[0]["video_id"] == "vid123" assert videos[0]["title"] == "Mock Video 1" assert videos[0]["channel_name"] == "Uploader One" assert videos[0]["channel_id"] == "chan123" assert videos[0]["url"] == "https://www.youtube.com/watch?v=vid123" assert videos[0]["thumbnail_url"] == "https://img.youtube.com/vi/vid123/hqdefault.jpg" assert videos[0]["publish_date"] == "2026-06-01" assert videos[0]["duration"] == 360 assert videos[0]["status"] == "pending" assert videos[1]["video_id"] == "vid456" assert videos[1]["title"] == "Mock Video 2" assert videos[1]["publish_date"] == "2026-06-02" assert videos[1]["duration"] == 720 @pytest.mark.asyncio async def test_fetch_subscriptions_skip_duplicates(test_db, tmp_path, monkeypatch): cookies_file = os.path.join(tmp_path, "mock_cookies.txt") with open(cookies_file, "w") as f: f.write("# Netscape HTTP Cookie File") monkeypatch.setattr(fetcher, "COOKIES_PATH", cookies_file) # Insert an existing video record first await test_db.execute(""" INSERT INTO videos (video_id, title, url, status) VALUES ('vid123', 'Mock Video 1', 'https://www.youtube.com/watch?v=vid123', 'pending') """) await test_db.commit() video1 = { "id": "vid123", "title": "Mock Video 1", "upload_date": "20260601" } video2 = { "id": "vid456", "title": "Mock Video 2", "upload_date": "20260602" } stdout_bytes = (json.dumps(video1) + "\n" + json.dumps(video2) + "\n").encode("utf-8") mock_process = MockProcess(stdout_data=stdout_bytes, returncode=0) with patch("asyncio.create_subprocess_exec", return_value=mock_process): count = await fetcher.fetch_subscriptions() assert count == 1 # Only 1 new video should be added (vid456)