Files
odysseus/tests/test_youtube_comments_timeout.py
SurprisedDuck d73c0a13f4 YouTube: enforce comment fetch timeout while waiting
asyncio.wait_for wrapped create_subprocess_exec, which returns as soon
as the child is spawned, so the timeout never bounded the actual work.
yt-dlp could hang indefinitely on proc.communicate() and the
except asyncio.TimeoutError branch was unreachable. Bind the wait to
communicate() and kill/reap the child if it overruns.
2026-06-02 20:44:24 +09:00

48 lines
1.4 KiB
Python

"""Regression: fetch_youtube_comments must actually honour its timeout.
The timeout previously wrapped ``create_subprocess_exec`` (which returns as soon
as the child is spawned) instead of ``proc.communicate()`` (the step that waits
for yt-dlp to finish). A hung yt-dlp would therefore block forever and the
``except asyncio.TimeoutError`` handler was unreachable. The wait must be bound
to communicate(), and the child killed when it overruns.
"""
import asyncio
from src import youtube_handler
def test_comment_fetch_honours_timeout(monkeypatch):
monkeypatch.setattr(youtube_handler, "_find_ytdlp", lambda: "yt-dlp")
killed = {"value": False}
class HangingProc:
returncode = None
async def communicate(self):
await asyncio.sleep(30) # far longer than the test timeout
return (b"", b"")
def kill(self):
killed["value"] = True
async def wait(self):
return 0
async def fake_create_subprocess_exec(*args, **kwargs):
return HangingProc()
monkeypatch.setattr(
asyncio, "create_subprocess_exec", fake_create_subprocess_exec
)
result = asyncio.run(
youtube_handler.fetch_youtube_comments("vid123", timeout=0.1)
)
assert result["success"] is False
assert "timed out" in result["error"].lower()
assert result["comments"] == []
# The overrunning child must be killed, not left running.
assert killed["value"] is True