"""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")