ytptube/app/tests/test_images_routes.py

97 lines
3.3 KiB
Python

from __future__ import annotations
from typing import Generator
from unittest.mock import AsyncMock
import pytest
from aiohttp import web
from aiohttp.test_utils import make_mocked_request
from app.library.config import Config
from app.routes.api import images
@pytest.fixture(autouse=True)
def reset_config() -> Generator[None, None, None]:
Config._reset_singleton()
yield
Config._reset_singleton()
class _Resp:
def __init__(self, *, status_code: int = 200, content: bytes = b"img", content_type: str = "image/jpeg") -> None:
self.status_code = status_code
self.content = content
self.headers = {"Content-Type": content_type}
@pytest.mark.asyncio
async def test_thumb_thread(monkeypatch: pytest.MonkeyPatch) -> None:
config = Config.get_instance()
req = make_mocked_request("GET", "/api/thumbnail?url=https://example.com/a.jpg")
req._rel_url = req._rel_url.with_query({"url": "https://example.com/a.jpg"})
seen = {"to_thread": False, "validate": False}
def fake_validate_url(url: str, allow_internal: bool = False) -> bool:
seen["validate"] = True
assert url == "https://example.com/a.jpg"
assert allow_internal is config.allow_internal_urls
return True
async def fake_to_thread(func, *args, **kwargs):
seen["to_thread"] = True
return func(*args, **kwargs)
client = AsyncMock()
client.request.return_value = _Resp()
monkeypatch.setattr(images, "validate_url", fake_validate_url)
monkeypatch.setattr(images.asyncio, "to_thread", fake_to_thread)
monkeypatch.setattr(images, "get_async_client", lambda **_kwargs: client)
monkeypatch.setattr(images, "resolve_curl_transport", lambda: False)
monkeypatch.setattr(images, "build_request_headers", lambda **_kwargs: {})
monkeypatch.setattr(images.Globals, "get_random_agent", staticmethod(lambda: "agent"))
monkeypatch.setattr(
images.YTDLPOpts,
"get_instance",
staticmethod(
lambda: type(
"Opts",
(),
{
"preset": lambda self, name: self,
"get_all": lambda self: {},
},
)()
),
)
response = await images.get_thumbnail(req, config)
assert response.status == web.HTTPOk.status_code
assert seen["to_thread"] is True
assert seen["validate"] is True
client.request.assert_awaited_once()
@pytest.mark.asyncio
async def test_thumb_reject(monkeypatch: pytest.MonkeyPatch) -> None:
config = Config.get_instance()
req = make_mocked_request("GET", "/api/thumbnail?url=https://bad.example/a.jpg")
req._rel_url = req._rel_url.with_query({"url": "https://bad.example/a.jpg"})
def fake_validate_url(_url: str, allow_internal: bool = False) -> bool:
assert allow_internal is config.allow_internal_urls
raise ValueError("Invalid hostname.")
async def fake_to_thread(func, *args, **kwargs):
return func(*args, **kwargs)
monkeypatch.setattr(images, "validate_url", fake_validate_url)
monkeypatch.setattr(images.asyncio, "to_thread", fake_to_thread)
response = await images.get_thumbnail(req, config)
assert response.status == web.HTTPForbidden.status_code
assert response.text == '{"error": "Invalid hostname."}'