143 lines
3.7 KiB
Python
143 lines
3.7 KiB
Python
"""Test doubles for the Telegram bot and the CDN fetcher."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
from dataclasses import dataclass, field
|
|
from typing import Any
|
|
|
|
import pytest
|
|
from telegram.error import BadRequest
|
|
|
|
from sticker_downloader.config import DownloadConfig
|
|
|
|
|
|
@dataclass
|
|
class FakeFile:
|
|
file_path: str
|
|
|
|
|
|
@dataclass
|
|
class FakeThumbnail:
|
|
file_id: str
|
|
|
|
|
|
@dataclass
|
|
class FakeSticker:
|
|
file_id: str
|
|
file_unique_id: str = "uniq"
|
|
emoji: str | None = "😀"
|
|
is_animated: bool = False
|
|
is_video: bool = False
|
|
thumbnail: FakeThumbnail | None = None
|
|
width: int = 512
|
|
height: int = 512
|
|
type: str = "regular"
|
|
|
|
|
|
@dataclass
|
|
class FakeStickerSet:
|
|
name: str
|
|
title: str = "A Pack"
|
|
sticker_type: str = "regular"
|
|
stickers: list[FakeSticker] = field(default_factory=list)
|
|
thumbnail: FakeThumbnail | None = None
|
|
|
|
|
|
class FakeBot:
|
|
"""Implements just the two coroutines the downloader calls."""
|
|
|
|
def __init__(
|
|
self,
|
|
sets: dict[str, FakeStickerSet] | None = None,
|
|
*,
|
|
extensions: dict[str, str] | None = None,
|
|
) -> None:
|
|
self._sets = sets or {}
|
|
#: file_id -> extension served by the fake CDN URL.
|
|
self._extensions = extensions or {}
|
|
self.get_sticker_set_calls: list[str] = []
|
|
self.get_file_calls: list[str] = []
|
|
|
|
async def get_sticker_set(self, name: str) -> FakeStickerSet:
|
|
self.get_sticker_set_calls.append(name)
|
|
try:
|
|
return self._sets[name]
|
|
except KeyError:
|
|
raise BadRequest("Stickerset_invalid") from None
|
|
|
|
async def get_file(self, file_id: str) -> FakeFile:
|
|
self.get_file_calls.append(file_id)
|
|
extension = self._extensions.get(file_id, "webp")
|
|
return FakeFile(file_path=f"https://cdn.example/file/{file_id}.{extension}")
|
|
|
|
|
|
class FakeFetcher:
|
|
"""Returns deterministic bytes and can be told to fail for some URLs."""
|
|
|
|
def __init__(
|
|
self,
|
|
*,
|
|
payload: bytes = b"sticker-bytes",
|
|
failures: dict[str, Exception] | None = None,
|
|
) -> None:
|
|
self._payload = payload
|
|
self._failures = failures or {}
|
|
self.urls: list[str] = []
|
|
self.max_concurrent = 0
|
|
self._in_flight = 0
|
|
|
|
async def fetch(self, url: str) -> bytes:
|
|
self.urls.append(url)
|
|
self._in_flight += 1
|
|
self.max_concurrent = max(self.max_concurrent, self._in_flight)
|
|
try:
|
|
await asyncio.sleep(0)
|
|
for needle, error in self._failures.items():
|
|
if needle in url:
|
|
raise error
|
|
return self._payload
|
|
finally:
|
|
self._in_flight -= 1
|
|
|
|
|
|
async def no_sleep(_delay: float) -> None:
|
|
"""Drop-in for ``asyncio.sleep`` so retry tests run instantly."""
|
|
return None
|
|
|
|
|
|
def make_sticker(index: int, **overrides: Any) -> FakeSticker:
|
|
"""A static sticker with a PNG thumbnail, unless overridden."""
|
|
defaults: dict[str, Any] = {
|
|
"file_id": f"file{index}",
|
|
"file_unique_id": f"uniq{index}",
|
|
"thumbnail": FakeThumbnail(file_id=f"thumb{index}"),
|
|
}
|
|
defaults.update(overrides)
|
|
return FakeSticker(**defaults)
|
|
|
|
|
|
@pytest.fixture
|
|
def pack() -> FakeStickerSet:
|
|
return FakeStickerSet(
|
|
name="TestPack",
|
|
title="Test Pack",
|
|
stickers=[make_sticker(1), make_sticker(2)],
|
|
thumbnail=FakeThumbnail(file_id="packthumb"),
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def bot(pack: FakeStickerSet) -> FakeBot:
|
|
return FakeBot({pack.name: pack})
|
|
|
|
|
|
@pytest.fixture
|
|
def fetcher() -> FakeFetcher:
|
|
return FakeFetcher()
|
|
|
|
|
|
@pytest.fixture
|
|
def config(tmp_path) -> DownloadConfig:
|
|
return DownloadConfig(output_dir=tmp_path / "downloads")
|