"""Tests for CSFD.cz scraper module.""" import pytest from unittest.mock import patch, MagicMock from src.core.csfd import ( CSFDMovie, fetch_movie, search_movies, fetch_movie_by_id, _extract_csfd_id, _parse_duration, _extract_json_ld, _extract_rating, _extract_poster, _extract_plot, _extract_genres, _extract_origin_info, _check_dependencies, ) # Sample HTML for testing SAMPLE_JSON_LD = """ { "@type": "Movie", "name": "Test Movie", "director": [{"@type": "Person", "name": "Test Director"}], "actor": [{"@type": "Person", "name": "Actor 1"}, {"@type": "Person", "name": "Actor 2"}], "aggregateRating": {"ratingValue": 85.5, "ratingCount": 1000}, "duration": "PT120M", "description": "A test movie description." } """ SAMPLE_HTML = """
85%%
Drama / Thriller
Česko, 2020, 120 min

Full plot description.

""" % SAMPLE_JSON_LD class TestCSFDMovie: """Tests for CSFDMovie dataclass.""" def test_csfd_movie_basic(self): """Test basic CSFDMovie creation.""" movie = CSFDMovie(title="Test", url="https://csfd.cz/film/123/") assert movie.title == "Test" assert movie.url == "https://csfd.cz/film/123/" assert movie.year is None assert movie.genres == [] assert movie.rating is None def test_csfd_movie_full(self): """Test CSFDMovie with all fields.""" movie = CSFDMovie( title="Test Movie", url="https://csfd.cz/film/123/", year=2020, genres=["Drama", "Thriller"], directors=["Director 1"], actors=["Actor 1", "Actor 2"], rating=85, rating_count=1000, duration=120, country="Česko", poster_url="https://image.example.com/poster.jpg", plot="A test movie.", csfd_id=123 ) assert movie.year == 2020 assert movie.genres == ["Drama", "Thriller"] assert movie.rating == 85 assert movie.duration == 120 assert movie.csfd_id == 123 def test_csfd_movie_str(self): """Test CSFDMovie string representation.""" movie = CSFDMovie( title="Test Movie", url="https://csfd.cz/film/123/", year=2020, genres=["Drama"], directors=["Director 1"], rating=85 ) s = str(movie) assert "Test Movie (2020)" in s assert "85%" in s assert "Drama" in s assert "Director 1" in s def test_csfd_movie_str_minimal(self): """Test CSFDMovie string with minimal data.""" movie = CSFDMovie(title="Test", url="https://csfd.cz/film/123/") s = str(movie) assert "Test" in s class TestHelperFunctions: """Tests for helper functions.""" def test_extract_csfd_id_valid(self): """Test extracting CSFD ID from valid URL.""" assert _extract_csfd_id("https://www.csfd.cz/film/9423-pane-vy-jste-vdova/") == 9423 assert _extract_csfd_id("https://www.csfd.cz/film/123456/") == 123456 assert _extract_csfd_id("/film/999/prehled/") == 999 def test_extract_csfd_id_invalid(self): """Test extracting CSFD ID from invalid URL.""" assert _extract_csfd_id("https://www.csfd.cz/") is None assert _extract_csfd_id("not-a-url") is None def test_parse_duration_valid(self): """Test parsing ISO 8601 duration.""" assert _parse_duration("PT97M") == 97 assert _parse_duration("PT120M") == 120 assert _parse_duration("PT60M") == 60 def test_parse_duration_invalid(self): """Test parsing invalid duration.""" assert _parse_duration("") is None assert _parse_duration("invalid") is None assert _parse_duration("PT") is None class TestHTMLExtraction: """Tests for HTML extraction functions.""" @pytest.fixture def soup(self): """Create BeautifulSoup object from sample HTML.""" from bs4 import BeautifulSoup return BeautifulSoup(SAMPLE_HTML, "html.parser") def test_extract_json_ld(self, soup): """Test extracting data from JSON-LD.""" data = _extract_json_ld(soup) assert data["title"] == "Test Movie" assert data["directors"] == ["Test Director"] assert data["actors"] == ["Actor 1", "Actor 2"] assert data["rating"] == 86 # Rounded from 85.5 assert data["rating_count"] == 1000 assert data["duration"] == 120 def test_extract_rating(self, soup): """Test extracting rating from HTML.""" rating = _extract_rating(soup) assert rating == 85 def test_extract_genres(self, soup): """Test extracting genres from HTML.""" genres = _extract_genres(soup) assert "Drama" in genres assert "Thriller" in genres def test_extract_poster(self, soup): """Test extracting poster URL.""" poster = _extract_poster(soup) assert poster == "https://image.example.com/poster.jpg" def test_extract_plot(self, soup): """Test extracting plot.""" plot = _extract_plot(soup) assert plot == "Full plot description." def test_extract_origin_info(self, soup): """Test extracting origin info.""" info = _extract_origin_info(soup) assert info["country"] == "Česko" assert info["year"] == 2020 assert info["duration"] == 120 class TestFetchMovie: """Tests for fetch_movie function.""" @patch("src.core.csfd.requests") def test_fetch_movie_success(self, mock_requests): """Test successful movie fetch.""" mock_response = MagicMock() mock_response.text = SAMPLE_HTML mock_response.raise_for_status = MagicMock() mock_requests.get.return_value = mock_response movie = fetch_movie("https://www.csfd.cz/film/123-test/") assert movie.title == "Test Movie" assert movie.csfd_id == 123 assert movie.rating == 86 assert "Drama" in movie.genres mock_requests.get.assert_called_once() @patch("src.core.csfd.requests") def test_fetch_movie_network_error(self, mock_requests): """Test network error handling.""" import requests as real_requests mock_requests.get.side_effect = real_requests.RequestException("Network error") with pytest.raises(real_requests.RequestException): fetch_movie("https://www.csfd.cz/film/123/") class TestSearchMovies: """Tests for search_movies function.""" @patch("src.core.csfd.requests") def test_search_movies(self, mock_requests): """Test movie search.""" search_html = """ Test Movie Another Movie """ mock_response = MagicMock() mock_response.text = search_html mock_response.raise_for_status = MagicMock() mock_requests.get.return_value = mock_response mock_requests.utils.quote = lambda x: x results = search_movies("test", limit=10) assert len(results) >= 1 assert any(m.csfd_id == 123 for m in results) class TestFetchMovieById: """Tests for fetch_movie_by_id function.""" @patch("src.core.csfd.fetch_movie") def test_fetch_by_id(self, mock_fetch): """Test fetching movie by ID.""" mock_fetch.return_value = CSFDMovie(title="Test", url="https://csfd.cz/film/9423/") movie = fetch_movie_by_id(9423) mock_fetch.assert_called_once_with("https://www.csfd.cz/film/9423/") assert movie.title == "Test" class TestDependencyCheck: """Tests for dependency checking.""" def test_dependencies_available(self): """Test that dependencies are available (they should be in test env).""" # Should not raise _check_dependencies()