129 lines
4.4 KiB
Python
129 lines
4.4 KiB
Python
"""Tests for ``alfred.application.movies.search_movie.SearchMovieUseCase``.
|
|
|
|
The use case wraps :meth:`TMDBClient.search_movies` and flattens each
|
|
hit's domain VOs into agent-friendly primitives wrapped in a
|
|
:class:`SearchMovieResponse` envelope (status="ok"|"error").
|
|
|
|
Coverage:
|
|
|
|
- ``TestSuccess`` — list of hits flattened, year present/absent,
|
|
empty list still ``status="ok"``.
|
|
- ``TestErrorTranslation`` — ``TMDBConfigurationError`` →
|
|
configuration_error, ``TMDBAPIError`` → api_error, ``ValueError``
|
|
→ validation_failed.
|
|
- ``TestPassThrough`` — query is forwarded to the client unchanged.
|
|
|
|
TMDBClient is fully mocked — no real HTTP.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from unittest.mock import MagicMock
|
|
|
|
import pytest
|
|
|
|
from alfred.application.movies_TO_CHECK.search_movie import SearchMovieUseCase
|
|
from alfred.domain.movies_TO_CHECK.value_objects import MovieTitle, ReleaseYear
|
|
from alfred.domain.shared_TO_CHECK.value_objects import TmdbId
|
|
from alfred.infrastructure.api_TO_CHECK.tmdb.dto import TmdbMovieSearchResult
|
|
from alfred.infrastructure.api_TO_CHECK.tmdb.exceptions import (
|
|
TMDBAPIError,
|
|
TMDBConfigurationError,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def client():
|
|
return MagicMock()
|
|
|
|
|
|
@pytest.fixture
|
|
def use_case(client):
|
|
return SearchMovieUseCase(client)
|
|
|
|
|
|
def _hit(**kw) -> TmdbMovieSearchResult:
|
|
defaults = dict(
|
|
tmdb_id=TmdbId(27205),
|
|
title=MovieTitle("Inception"),
|
|
release_year=ReleaseYear(2010),
|
|
)
|
|
defaults.update(kw)
|
|
return TmdbMovieSearchResult(**defaults)
|
|
|
|
|
|
# --------------------------------------------------------------------------- #
|
|
# Success paths #
|
|
# --------------------------------------------------------------------------- #
|
|
|
|
|
|
class TestSuccess:
|
|
def test_single_hit_is_flattened(self, client, use_case):
|
|
client.search_movies.return_value = [_hit()]
|
|
r = use_case.execute("Inception")
|
|
assert r.status == "ok"
|
|
assert len(r.hits) == 1
|
|
h = r.hits[0]
|
|
assert h.tmdb_id == 27205
|
|
assert h.title == "Inception"
|
|
assert h.release_year == 2010
|
|
|
|
def test_multiple_hits_preserve_order(self, client, use_case):
|
|
client.search_movies.return_value = [
|
|
_hit(),
|
|
_hit(tmdb_id=TmdbId(42), title=MovieTitle("Inception 2")),
|
|
]
|
|
r = use_case.execute("Inception")
|
|
assert [h.tmdb_id for h in r.hits] == [27205, 42]
|
|
|
|
def test_hit_without_release_year(self, client, use_case):
|
|
client.search_movies.return_value = [_hit(release_year=None)]
|
|
r = use_case.execute("Inception")
|
|
assert r.hits[0].release_year is None
|
|
|
|
def test_empty_results_returns_ok_with_no_hits(self, client, use_case):
|
|
client.search_movies.return_value = []
|
|
r = use_case.execute("nothing")
|
|
assert r.status == "ok"
|
|
assert r.hits == []
|
|
assert r.error is None
|
|
|
|
|
|
# --------------------------------------------------------------------------- #
|
|
# Error translation #
|
|
# --------------------------------------------------------------------------- #
|
|
|
|
|
|
class TestErrorTranslation:
|
|
def test_configuration_error(self, client, use_case):
|
|
client.search_movies.side_effect = TMDBConfigurationError("missing key")
|
|
r = use_case.execute("x")
|
|
assert r.status == "error"
|
|
assert r.error == "configuration_error"
|
|
|
|
def test_api_error(self, client, use_case):
|
|
client.search_movies.side_effect = TMDBAPIError("500 oops")
|
|
r = use_case.execute("x")
|
|
assert r.status == "error"
|
|
assert r.error == "api_error"
|
|
assert "500" in r.message
|
|
|
|
def test_validation_error(self, client, use_case):
|
|
client.search_movies.side_effect = ValueError("query too long")
|
|
r = use_case.execute("x")
|
|
assert r.status == "error"
|
|
assert r.error == "validation_failed"
|
|
assert "too long" in r.message
|
|
|
|
|
|
# --------------------------------------------------------------------------- #
|
|
# Pass-through #
|
|
# --------------------------------------------------------------------------- #
|
|
|
|
|
|
class TestPassThrough:
|
|
def test_query_forwarded_verbatim(self, client, use_case):
|
|
client.search_movies.return_value = []
|
|
use_case.execute("Inception")
|
|
client.search_movies.assert_called_once_with("Inception")
|