"""Unit tests for anime Pydantic models. This module tests all anime-related models including validation, serialization, and field constraints. """ import pytest from pydantic import ValidationError from src.server.models.anime import ( AnimeSeriesResponse, EpisodeInfo, MissingEpisodeInfo, SearchRequest, SearchResult, ) class TestEpisodeInfo: """Tests for EpisodeInfo model.""" def test_episode_info_basic(self): """Test creating a basic episode info.""" ep = EpisodeInfo(episode_number=1, title="Pilot", duration_seconds=1500) assert ep.episode_number == 1 assert ep.title == "Pilot" assert ep.duration_seconds == 1500 assert ep.available is True def test_episode_info_without_optional_fields(self): """Test episode info with only required fields.""" ep = EpisodeInfo(episode_number=5) assert ep.episode_number == 5 assert ep.title is None assert ep.duration_seconds is None assert ep.available is True def test_invalid_episode_number(self): """Test that episode number must be positive.""" with pytest.raises(ValidationError): EpisodeInfo(episode_number=0) class TestMissingEpisodeInfo: """Tests for MissingEpisodeInfo model.""" def test_missing_episode_count(self): """Test count property calculation.""" m = MissingEpisodeInfo(from_episode=5, to_episode=7) assert m.count == 3 def test_single_missing_episode(self): """Test count for single missing episode.""" m = MissingEpisodeInfo(from_episode=5, to_episode=5) assert m.count == 1 class TestAnimeSeriesResponse: """Tests for AnimeSeriesResponse model.""" def test_anime_series_response_with_key(self): """Test creating series response with key as identifier.""" ep = EpisodeInfo(episode_number=1, title="Ep1") series = AnimeSeriesResponse( key="attack-on-titan", title="Attack on Titan", folder="Attack on Titan (2013)", episodes=[ep], ) assert series.key == "attack-on-titan" assert series.title == "Attack on Titan" assert series.folder == "Attack on Titan (2013)" assert series.episodes[0].title == "Ep1" def test_key_normalization(self): """Test that key is normalized to lowercase.""" series = AnimeSeriesResponse( key="ATTACK-ON-TITAN", title="Attack on Titan" ) assert series.key == "attack-on-titan" def test_key_whitespace_stripped(self): """Test that key whitespace is stripped.""" series = AnimeSeriesResponse( key=" attack-on-titan ", title="Attack on Titan" ) assert series.key == "attack-on-titan" def test_folder_is_optional(self): """Test that folder is optional metadata.""" series = AnimeSeriesResponse( key="my-anime", title="My Anime" ) assert series.folder is None class TestSearchRequest: """Tests for SearchRequest model.""" def test_search_request_validation(self): """Test valid search request.""" req = SearchRequest(query="naruto", limit=5) assert req.query == "naruto" assert req.limit == 5 def test_search_request_empty_query_rejected(self): """Test that empty query is rejected.""" with pytest.raises(ValidationError): SearchRequest(query="", limit=5) def test_search_request_defaults(self): """Test default values.""" req = SearchRequest(query="test") assert req.limit == 10 assert req.include_adult is False class TestSearchResult: """Tests for SearchResult model.""" def test_search_result_with_key(self): """Test search result with key as identifier.""" res = SearchResult( key="naruto", title="Naruto", folder="Naruto (2002)", snippet="A ninja story", score=0.9 ) assert res.key == "naruto" assert res.title == "Naruto" assert res.folder == "Naruto (2002)" assert res.score == 0.9 def test_key_normalization(self): """Test that key is normalized to lowercase.""" res = SearchResult(key="NARUTO", title="Naruto") assert res.key == "naruto" def test_folder_is_optional(self): """Test that folder is optional metadata.""" res = SearchResult(key="test", title="Test") assert res.folder is None def test_optional_fields(self): """Test optional fields.""" res = SearchResult(key="s1", title="T1", snippet="snip", score=0.9) assert res.score == 0.9 assert res.snippet == "snip"