Files
alfred/tests/application/test_search_torrents.py
T
2026-05-26 21:45:11 +02:00

148 lines
4.9 KiB
Python

"""Tests for ``alfred.application.torrents.search_torrents.SearchTorrentsUseCase``.
Wraps ``KnabenClient.search`` and converts ``TorrentResult`` objects into
plain dicts inside a ``SearchTorrentsResponse`` envelope.
Coverage:
- ``TestSuccess`` — multiple results → status="ok" + ``count`` + dict shape.
- ``TestEmptyResults`` — empty list from client → status="error",
error="not_found".
- ``TestErrorTranslation`` — ``KnabenNotFoundError`` → not_found,
``KnabenAPIError`` → api_error, ``ValueError`` → validation_failed.
- ``TestPassThrough`` — query + limit are forwarded to the client.
KnabenClient is fully mocked — no real HTTP.
"""
from __future__ import annotations
from unittest.mock import MagicMock
import pytest
from alfred.application.torrents_TO_CHECK.search_torrents import SearchTorrentsUseCase
from alfred.infrastructure.api_TO_CHECK.knaben.dto import TorrentResult
from alfred.infrastructure.api_TO_CHECK.knaben.exceptions import (
KnabenAPIError,
KnabenNotFoundError,
)
@pytest.fixture
def client():
return MagicMock()
@pytest.fixture
def use_case(client):
return SearchTorrentsUseCase(client)
def _torrent(**kw) -> TorrentResult:
defaults = dict(
title="Inception.2010.1080p",
size="10 GB",
seeders=500,
leechers=50,
magnet="magnet:?xt=abc",
info_hash="abc",
tracker="rarbg",
upload_date="2020-01-01",
category="movie",
)
defaults.update(kw)
return TorrentResult(**defaults)
# --------------------------------------------------------------------------- #
# Success #
# --------------------------------------------------------------------------- #
class TestSuccess:
def test_single_result_serialized_to_dict(self, client, use_case):
client.search.return_value = [_torrent()]
r = use_case.execute("Inception")
assert r.status == "ok"
assert r.count == 1
assert len(r.torrents) == 1
t = r.torrents[0]
assert t["name"] == "Inception.2010.1080p"
assert t["size"] == "10 GB"
assert t["seeders"] == 500
assert t["leechers"] == 50
assert t["magnet"].startswith("magnet:")
assert t["info_hash"] == "abc"
assert t["tracker"] == "rarbg"
assert t["upload_date"] == "2020-01-01"
assert t["category"] == "movie"
def test_multiple_results(self, client, use_case):
client.search.return_value = [
_torrent(title="A"),
_torrent(title="B"),
_torrent(title="C"),
]
r = use_case.execute("x")
assert r.count == 3
assert [t["name"] for t in r.torrents] == ["A", "B", "C"]
# --------------------------------------------------------------------------- #
# Empty #
# --------------------------------------------------------------------------- #
class TestEmptyResults:
def test_empty_list_becomes_not_found(self, client, use_case):
client.search.return_value = []
r = use_case.execute("ghost")
assert r.status == "error"
assert r.error == "not_found"
assert "ghost" in r.message
# --------------------------------------------------------------------------- #
# Error translation #
# --------------------------------------------------------------------------- #
class TestErrorTranslation:
def test_not_found(self, client, use_case):
client.search.side_effect = KnabenNotFoundError("nope")
r = use_case.execute("x")
assert r.status == "error"
assert r.error == "not_found"
assert "nope" in r.message
def test_api_error(self, client, use_case):
client.search.side_effect = KnabenAPIError("rate limited")
r = use_case.execute("x")
assert r.status == "error"
assert r.error == "api_error"
assert "rate" in r.message
def test_validation_error(self, client, use_case):
client.search.side_effect = ValueError("too long")
r = use_case.execute("x")
assert r.status == "error"
assert r.error == "validation_failed"
# --------------------------------------------------------------------------- #
# Pass-through #
# --------------------------------------------------------------------------- #
class TestPassThrough:
def test_default_limit_forwarded(self, client, use_case):
client.search.return_value = [_torrent()]
use_case.execute("Inception")
client.search.assert_called_once_with("Inception", limit=10)
def test_custom_limit_forwarded(self, client, use_case):
client.search.return_value = [_torrent()]
use_case.execute("Inception", limit=25)
client.search.assert_called_once_with("Inception", limit=25)