Files
ohm_streaming/tests/test_anime_sama_seasons.py
root 7dabce1c3c refactor: Apply code quality improvements from PR review
This commit implements the optional improvements identified during code review:

**Backend (animesama.py):**
- Replace all print() statements with logger calls for consistency
  - Use logger.debug() for detailed debugging information
  - Use logger.info() for general operational messages
  - Use logger.warning() for non-critical issues
  - Use logger.error() for error conditions
- Add comprehensive docstring to get_seasons() method:
  - Document two-phase parallel loading strategy
  - Explain performance characteristics (200x faster)
  - Document timeout behavior and error handling
  - Include usage examples and return value format
- Import logging module and initialize logger

**Frontend (anime.js & api.js):**
- Create providerSupportsSeasons() helper function in api.js:
  - Uses provider configuration as single source of truth
  - Eliminates hardcoded 'animesama' and 'anime-sama' checks
  - Supports explicit supports_seasons flag in provider config
  - Fallback to domain detection for unknown URLs
- Update renderAnimeCard() to use async helper function
- Update loadSeasonsForAnime() to use provider configuration
- Update displaySearchResults() to handle async card rendering
- Export helper function globally for use across modules

**Tests (test_anime_sama_seasons.py):**
- Fix import paths for new animesama.py location
  - Update from app.downloaders.animesama to app.downloaders.anime_sites.animesama
- All tests passing with new structure

**Benefits:**
- Consistent logging throughout the codebase
- Better maintainability with configuration-driven behavior
- Improved documentation for complex async logic
- Easier to add new season-supporting providers in future
- No hardcoded provider checks in frontend code

All tests passing: 5/5 

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-01-29 19:19:53 +00:00

195 lines
7.4 KiB
Python

"""
Unit tests for AnimeSama season detection
"""
import pytest
from unittest.mock import Mock, AsyncMock, patch, MagicMock
from bs4 import BeautifulSoup
class TestAnimeSamaSeasons:
"""Tests for AnimeSamaDownloader season detection"""
@pytest.mark.asyncio
async def test_get_seasons_no_seasons_available(self):
"""Test get_seasons when no seasons exist"""
from app.downloaders.anime_sites.animesama import AnimeSamaDownloader
downloader = AnimeSamaDownloader()
# Mock the response for main anime page
with patch.object(downloader, 'client') as mock_client:
# Mock response for main page
mock_response = Mock()
mock_response.status_code = 200
mock_response.text = """
<html>
<body>
<div class="episode-list">
<a href="/episode1">Episode 1</a>
</div>
</body>
</html>
"""
mock_client.get = AsyncMock(return_value=mock_response)
# Mock season checks (all return 404)
async def mock_get(url, timeout=None):
response = Mock()
if "saison1" in url:
response.status_code = 404
elif "saison2" in url:
response.status_code = 404
else:
response.status_code = 200
response.text = mock_response.text
return response
mock_client.get.side_effect = mock_get
seasons = await downloader.get_seasons("https://anime-sama.si/catalogue/test-anime/saison1/vostfr/")
# Should return empty list if no seasons found
assert isinstance(seasons, list)
@pytest.mark.asyncio
async def test_get_seasons_with_multiple_seasons(self):
"""Test get_seasons when multiple seasons exist"""
from app.downloaders.anime_sites.animesama import AnimeSamaDownloader
downloader = AnimeSamaDownloader()
with patch.object(downloader, 'client') as mock_client:
# Mock get_episodes to return different counts for each season
async def mock_get(url, timeout=None):
response = Mock()
if "/saison1/" in url:
response.status_code = 200
response.text = 'episodes.js = [{"url": "/ep1", "episode": "1"}, {"url": "/ep2", "episode": "2"}]'
elif "/saison2/" in url:
response.status_code = 200
response.text = 'episodes.js = [{"url": "/ep3", "episode": "1"}]'
elif "/saison3/" in url:
response.status_code = 404
else:
# Main page
response.status_code = 200
response.text = '<html><body>No season links</body></html>'
return response
mock_client.get.side_effect = mock_get
# Mock get_episodes
with patch.object(downloader, 'get_episodes') as mock_get_episodes:
mock_get_episodes.side_effect = [
[{"url": "/ep1", "episode": "1"}, {"url": "/ep2", "episode": "2"}], # Season 1
[{"url": "/ep3", "episode": "1"}], # Season 2
]
seasons = await downloader.get_seasons("https://anime-sama.si/catalogue/test/vostfr/")
# Should return multiple seasons
assert len(seasons) >= 0
# Check season structure
for season in seasons:
assert "season" in season
assert "title" in season
assert "url" in season
assert "episode_count" in season
assert isinstance(season["season"], int)
assert isinstance(season["episode_count"], int)
@pytest.mark.asyncio
async def test_get_seasons_url_parsing(self):
"""Test that get_seasons correctly parses URLs"""
from app.downloaders.anime_sites.animesama import AnimeSamaDownloader
downloader = AnimeSamaDownloader()
with patch.object(downloader, 'client') as mock_client:
# All seasons return 404
async def mock_get(url, timeout=None):
response = Mock()
response.status_code = 404
return response
mock_client.get.side_effect = mock_get
# Test with various URL formats
test_urls = [
"https://anime-sama.si/catalogue/test-anime/saison1/vostfr/",
"https://anime-sama.si/catalogue/test-anime/vostfr/",
"https://anime-sama.si/catalogue/naruto-shippuden/saison3/vostfr/",
]
for url in test_urls:
seasons = await downloader.get_seasons(url)
# Should not crash and should return a list
assert isinstance(seasons, list)
@pytest.mark.asyncio
async def test_get_seasons_sorting(self):
"""Test that seasons are returned in correct order"""
from app.downloaders.anime_sites.animesama import AnimeSamaDownloader
downloader = AnimeSamaDownloader()
with patch.object(downloader, 'client') as mock_client:
async def mock_get(url, timeout=None):
response = Mock()
response.status_code = 404
return response
mock_client.get.side_effect = mock_get
seasons = await downloader.get_seasons("https://anime-sama.si/catalogue/test/vostfr/")
# If seasons are found, they should be sorted by season number
if seasons:
season_numbers = [s["season"] for s in seasons]
assert season_numbers == sorted(season_numbers)
@pytest.mark.asyncio
async def test_get_seasons_with_season_links_in_html(self):
"""Test get_seasons when season links are present in HTML"""
from app.downloaders.anime_sites.animesama import AnimeSamaDownloader
downloader = AnimeSamaDownloader()
with patch.object(downloader, 'client') as mock_client:
# Mock main page with season links
main_page_response = Mock()
main_page_response.status_code = 200
main_page_response.text = """
<html>
<body>
<nav>
<a href="/catalogue/test/saison1/vostfr/">Saison 1</a>
<a href="/catalogue/test/saison2/vostfr/">Saison 2</a>
</nav>
</body>
</html>
"""
async def mock_get(url, timeout=None):
if "saison" not in url:
# Main page
return main_page_response
else:
# Season page
response = Mock()
response.status_code = 200
response.text = 'episodes.js = [{"url": "/ep1", "episode": "1"}]'
return response
mock_client.get.side_effect = mock_get
with patch.object(downloader, 'get_episodes') as mock_get_episodes:
mock_get_episodes.return_value = [{"url": "/ep1", "episode": "1"}]
seasons = await downloader.get_seasons("https://anime-sama.si/catalogue/test/vostfr/")
# Should find seasons from HTML links
assert isinstance(seasons, list)