"""Tests for ``alfred.application.movies.search_movie.SearchMovieUseCase``. The use case wraps ``TMDBClient.search_media`` and converts results / errors into a ``SearchMovieResponse`` envelope (status="ok"|"error"). Coverage: - ``TestSuccess`` — full MediaResult with imdb_id → ok+imdb_id; missing imdb_id → ok+no_imdb_id; TV media_type preserved. - ``TestErrorTranslation`` — ``TMDBNotFoundError`` → not_found, ``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.search_movie import SearchMovieUseCase from alfred.infrastructure.api.tmdb.dto import MediaResult from alfred.infrastructure.api.tmdb.exceptions import ( TMDBAPIError, TMDBConfigurationError, TMDBNotFoundError, ) @pytest.fixture def client(): return MagicMock() @pytest.fixture def use_case(client): return SearchMovieUseCase(client) def _result(**kw) -> MediaResult: defaults = dict( tmdb_id=1, title="Inception", media_type="movie", imdb_id="tt1375666", overview="o", release_date="2010-07-15", poster_path="/x.jpg", vote_average=8.4, ) defaults.update(kw) return MediaResult(**defaults) # --------------------------------------------------------------------------- # # Success paths # # --------------------------------------------------------------------------- # class TestSuccess: def test_full_result_returns_ok_with_imdb_id(self, client, use_case): client.search_media.return_value = _result() r = use_case.execute("Inception") assert r.status == "ok" assert r.imdb_id == "tt1375666" assert r.title == "Inception" assert r.media_type == "movie" assert r.tmdb_id == 1 assert r.vote_average == 8.4 assert r.error is None def test_tv_result(self, client, use_case): client.search_media.return_value = _result( media_type="tv", title="Breaking Bad", imdb_id="tt0903747" ) r = use_case.execute("Breaking Bad") assert r.status == "ok" assert r.media_type == "tv" assert r.imdb_id == "tt0903747" def test_missing_imdb_id_returns_ok_with_no_imdb_id_error(self, client, use_case): client.search_media.return_value = _result(imdb_id=None) r = use_case.execute("Inception") assert r.status == "ok" assert r.error == "no_imdb_id" assert r.message is not None assert "Inception" in r.message assert r.imdb_id is None assert r.title == "Inception" # --------------------------------------------------------------------------- # # Error translation # # --------------------------------------------------------------------------- # class TestErrorTranslation: def test_not_found(self, client, use_case): client.search_media.side_effect = TMDBNotFoundError("no match") r = use_case.execute("ghost") assert r.status == "error" assert r.error == "not_found" assert "no match" in r.message def test_configuration_error(self, client, use_case): client.search_media.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_media.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_media.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_media.return_value = _result() use_case.execute("Inception") client.search_media.assert_called_once_with("Inception")