""" Unit tests for provider detection and routing Tests URL-to-provider matching and downloader factory """ import pytest from app.providers import ( detect_provider_from_url, ANIME_PROVIDERS, FILE_HOSTS ) from app.downloaders import get_downloader, get_anime_site, get_series_site, get_video_player class TestDetectProviderFromURL: """Tests for detect_provider_from_url function""" def test_detect_anime_sama(self): """Test detection of Anime-Sama provider""" urls = [ "https://anime-sama.si/catalogue/naruto/s1/vostfr/", "https://www.anime-sama.fi/anime/test", "https://anime-sama.pw/test", ] for url in urls: provider = detect_provider_from_url(url) assert provider is not None assert provider["name"] == "anime-sama" def test_detect_neko_sama(self): """Test detection of Neko-Sama provider""" urls = [ "https://neko-sama.fr/anime/naruto", "https://www.neko-sama.fr/anime/one-piece", ] for url in urls: provider = detect_provider_from_url(url) assert provider is not None assert provider["name"] == "neko-sama" def test_detect_anime_ultime(self): """Test detection of Anime-Ultime provider""" urls = [ "https://anime-ultime.net/fiche-anime/naruto", "https://www.anime-ultime.net/anime/test", ] for url in urls: provider = detect_provider_from_url(url) assert provider is not None assert provider["name"] == "anime-ultime" def test_detect_vostfree(self): """Test detection of Vostfree provider""" urls = [ "https://vostfree.cc/anime/naruto", "https://www.vostfree.cc/anime/test", ] for url in urls: provider = detect_provider_from_url(url) assert provider is not None assert provider["name"] == "vostfree" def test_detect_french_manga(self): """Test detection of French-Manga provider""" urls = [ "https://french-manga.net/anime/naruto", "https://www.french-manga.net/anime/test", ] for url in urls: provider = detect_provider_from_url(url) assert provider is not None assert provider["name"] == "french-manga" def test_detect_fs7(self): """Test detection of FS7 (French Stream) provider""" urls = [ "https://fs7.space/series/test", "https://www.fs7.space/series/breaking-bad", ] for url in urls: provider = detect_provider_from_url(url) assert provider is not None assert provider["name"] == "fs7" def test_detect_file_hosts(self): """Test detection of file hosting services""" test_cases = [ ("https://doodstream.com/test/abc", "doodstream"), ("https://ds2play.com/test/abc", "doodstream"), ("https://rapidfile.com/test/abc", "rapidfile"), ("https://uptobox.com/test/abc", "uptobox"), ("https://1fichier.com/test", "unfichier"), ("https://vidmoly.to/test", "vidmoly"), ("https://sendvid.com/test", "sendvid"), ("https://sibnet.ru/test", "sibnet"), ("https://lpayer.com/test", "lpayer"), ("https://vidzy.com/test", "vidzy"), ("https://luluv.com/test", "luluv"), ("https://uqload.com/test", "uqload"), ] for url, expected_name in test_cases: provider = detect_provider_from_url(url) assert provider is not None, f"Failed to detect {expected_name} from {url}" assert provider["name"] == expected_name def test_detect_unknown_provider(self): """Test that unknown URLs return None""" unknown_urls = [ "https://unknown-site.com/test", "https://google.com/search", "https://example.com/anime", ] for url in unknown_urls: provider = detect_provider_from_url(url) assert provider is None def test_detect_empty_url(self): """Test detection with empty URL""" assert detect_provider_from_url("") is None assert detect_provider_from_url(None) is None def test_detect_case_insensitive(self): """Test that detection is case-insensitive for domains""" url = "https://Anime-Sama.si/test" provider = detect_provider_from_url(url) assert provider is not None assert provider["name"] == "anime-sama" def test_detect_with_path_and_query(self): """Test detection with complex paths and query strings""" urls = [ "https://anime-sama.si/catalogue/naruto/s1/vostfr/?page=1", "https://neko-sama.fr/anime/one-piece?ep=1", "https://doodstream.com/e/abc123#start=0", ] for url in urls: provider = detect_provider_from_url(url) assert provider is not None def test_provider_structure(self): """Test that detected provider has correct structure""" provider = detect_provider_from_url("https://anime-sama.si/test") assert "name" in provider assert "icon" in provider assert "color" in provider assert "domains" in provider assert isinstance(provider["domains"], list) class TestAnimeProvidersConfig: """Tests for ANIME_PROVIDERS configuration""" def test_anime_providers_structure(self): """Test that all anime providers have required fields""" for provider_name, provider_data in ANIME_PROVIDERS.items(): assert "name" in provider_data assert "domains" in provider_data assert "icon" in provider_data assert "color" in provider_data assert "url_pattern" in provider_data assert isinstance(provider_data["domains"], list) def test_known_anime_providers_exist(self): """Test that known anime providers are configured""" known_providers = [ "anime-sama", "neko-sama", "anime-ultime", "vostfree", "french-manga" ] for provider in known_providers: assert provider in ANIME_PROVIDERS def test_anime_provider_domains(self): """Test that anime providers have valid domains""" for provider_data in ANIME_PROVIDERS.values(): assert len(provider_data["domains"]) > 0 for domain in provider_data["domains"]: assert isinstance(domain, str) assert "." in domain # Basic domain validation def test_anime_provider_url_patterns(self): """Test that URL patterns are valid""" for provider_data in ANIME_PROVIDERS.values(): pattern = provider_data["url_pattern"] assert isinstance(pattern, str) assert len(pattern) > 0 class TestFileHostsConfig: """Tests for FILE_HOSTS configuration""" def test_file_hosts_structure(self): """Test that all file hosts have required fields""" for host_name, host_data in FILE_HOSTS.items(): assert "name" in host_data assert "domains" in host_data assert "icon" in host_data assert "color" in host_data assert isinstance(host_data["domains"], list) def test_known_file_hosts_exist(self): """Test that known file hosts are configured""" known_hosts = [ "unfichier", "doodstream", "rapidfile", "uptobox", "vidmoly", "sendvid", "sibnet", "lpayer", "vidzy", "luluv", "uqload" ] for host in known_hosts: assert host in FILE_HOSTS def test_file_host_domains(self): """Test that file hosts have valid domains""" for host_data in FILE_HOSTS.values(): assert len(host_data["domains"]) > 0 for domain in host_data["domains"]: assert isinstance(domain, str) assert "." in domain class TestGetDownloader: """Tests for get_downloader factory function""" @pytest.mark.asyncio async def test_get_anime_site_downloader(self): """Test getting anime site downloader""" url = "https://anime-sama.si/catalogue/naruto/" downloader = await get_downloader(url) assert downloader is not None # Should return an anime site downloader @pytest.mark.asyncio async def test_get_series_site_downloader(self): """Test getting series site downloader""" url = "https://fs7.space/series/test" downloader = await get_downloader(url) assert downloader is not None # Should return a series site downloader @pytest.mark.asyncio async def test_get_video_player_downloader(self): """Test getting video player downloader""" url = "https://doodstream.com/e/abc123" downloader = await get_downloader(url) assert downloader is not None # Should return a video player downloader @pytest.mark.asyncio async def test_get_unknown_url_downloader(self): """Test getting generic downloader for unknown URL""" url = "https://unknown-site.com/video" downloader = await get_downloader(url) assert downloader is not None # Should return GenericDownloader class TestGetAnimeSite: """Tests for get_anime_site factory function""" @pytest.mark.asyncio async def test_get_anime_sama_site(self): """Test getting Anime-Sama site""" from app.downloaders.anime_sites import AnimeSamaDownloader url = "https://anime-sama.si/catalogue/naruto/" downloader = await get_anime_site(url) assert isinstance(downloader, AnimeSamaDownloader) @pytest.mark.asyncio async def test_get_neko_sama_site(self): """Test getting Neko-Sama site""" from app.downloaders.anime_sites import NekoSamaDownloader url = "https://neko-sama.fr/anime/one-piece" downloader = await get_anime_site(url) assert isinstance(downloader, NekoSamaDownloader) @pytest.mark.asyncio async def test_get_anime_site_with_series_url(self): """Test that series URL returns None for anime site""" url = "https://fs7.space/series/test" downloader = await get_anime_site(url) assert downloader is None @pytest.mark.asyncio async def test_get_anime_site_with_video_player_url(self): """Test that video player URL returns None for anime site""" url = "https://doodstream.com/e/abc123" downloader = await get_anime_site(url) assert downloader is None class TestGetSeriesSite: """Tests for get_series_site factory function""" @pytest.mark.asyncio async def test_get_fs7_site(self): """Test getting FS7 series site""" from app.downloaders.series_sites import FS7Downloader url = "https://fs7.space/series/test" downloader = await get_series_site(url) assert isinstance(downloader, FS7Downloader) @pytest.mark.asyncio async def test_get_series_site_with_anime_url(self): """Test that anime URL returns None for series site""" url = "https://anime-sama.si/catalogue/naruto/" downloader = await get_series_site(url) assert downloader is None @pytest.mark.asyncio async def test_get_series_site_with_video_player_url(self): """Test that video player URL returns None for series site""" url = "https://doodstream.com/e/abc123" downloader = await get_series_site(url) assert downloader is None class TestGetVideoPlayer: """Tests for get_video_player factory function""" @pytest.mark.asyncio async def test_get_doodstream_player(self): """Test getting Doodstream player""" from app.downloaders.video_players import DoodstreamDownloader url = "https://doodstream.com/e/abc123" player = await get_video_player(url) assert isinstance(player, DoodstreamDownloader) @pytest.mark.asyncio async def test_get_unfichier_player(self): """Test getting 1fichier player""" from app.downloaders.video_players import UnFichierDownloader url = "https://1fichier.com/?abc123" player = await get_video_player(url) assert isinstance(player, UnFichierDownloader) @pytest.mark.asyncio async def test_get_vidmoly_player(self): """Test getting VidMoly player""" from app.downloaders.video_players import VidMolyDownloader url = "https://vidmoly.to/abc123" player = await get_video_player(url) assert isinstance(player, VidMolyDownloader) @pytest.mark.asyncio async def test_get_video_player_with_anime_url(self): """Test that anime site URL returns None for video player""" url = "https://anime-sama.si/catalogue/naruto/" player = await get_video_player(url) assert player is None @pytest.mark.asyncio async def test_get_video_player_with_unknown_url(self): """Test that unknown URL returns None for video player""" url = "https://unknown-site.com/video" player = await get_video_player(url) assert player is None class TestDownloaderPriority: """Tests for downloader priority and routing""" @pytest.mark.asyncio async def test_anime_site_has_priority_over_series(self): """Test that anime sites are checked before series sites""" # This is implicit in the get_downloader implementation # We just verify it works correctly url = "https://anime-sama.si/catalogue/naruto/" downloader = await get_downloader(url) assert downloader is not None # Should be an anime site, not series site or video player from app.downloaders.anime_sites import BaseAnimeSite assert isinstance(downloader, BaseAnimeSite) @pytest.mark.asyncio async def test_series_site_has_priority_over_video_player(self): """Test that series sites are checked before video players""" url = "https://fs7.space/series/test" downloader = await get_downloader(url) assert downloader is not None # Should be a series site, not video player from app.downloaders.series_sites import BaseSeriesSite assert isinstance(downloader, BaseSeriesSite) class TestProviderDomains: """Tests for provider domain matching""" def test_anime_sama_domains(self): """Test Anime-Sama domain variations""" from app.downloaders.anime_sites import AnimeSamaDownloader downloader = AnimeSamaDownloader() # These should be handled assert downloader.can_handle("https://anime-sama.si/test") assert downloader.can_handle("https://www.anime-sama.fi/test") # These should not assert not downloader.can_handle("https://neko-sama.fr/test") assert not downloader.can_handle("https://doodstream.com/test") def test_neko_sama_domains(self): """Test Neko-Sama domain variations""" from app.downloaders.anime_sites import NekoSamaDownloader downloader = NekoSamaDownloader() assert downloader.can_handle("https://neko-sama.fr/anime/test") assert not downloader.can_handle("https://anime-sama.si/test") def test_doodstream_domains(self): """Test Doodstream domain variations""" from app.downloaders.video_players import DoodstreamDownloader downloader = DoodstreamDownloader() assert downloader.can_handle("https://doodstream.com/e/abc") assert downloader.can_handle("https://ds2play.com/e/abc") assert not downloader.can_handle("https://vidmoly.to/abc") def test_subdomain_handling(self): """Test that subdomains are handled correctly""" from app.downloaders.anime_sites import AnimeSamaDownloader downloader = AnimeSamaDownloader() # With and without www assert downloader.can_handle("https://anime-sama.si/test") assert downloader.can_handle("https://www.anime-sama.si/test") def test_protocol_handling(self): """Test that both HTTP and HTTPS are handled""" from app.downloaders.anime_sites import AnimeSamaDownloader downloader = AnimeSamaDownloader() assert downloader.can_handle("https://anime-sama.si/test") # HTTP should also work (though less secure) assert downloader.can_handle("http://anime-sama.si/test") class TestProviderEdgeCases: """Tests for edge cases in provider detection""" def test_url_with_port(self): """Test URL with port number""" provider = detect_provider_from_url("https://anime-sama.si:443/test") assert provider is not None assert provider["name"] == "anime-sama" def test_url_with_fragment(self): """Test URL with fragment identifier""" provider = detect_provider_from_url("https://anime-sama.si/test#section") assert provider is not None assert provider["name"] == "anime-sama" def test_url_with_auth(self): """Test URL with authentication (should not happen in practice)""" # URLs with auth @ should still be detected provider = detect_provider_from_url("https://user:pass@anime-sama.si/test") # Detection might fail due to parsing, but shouldn't crash assert provider is not None or provider is None def test_idn_domains(self): """Test internationalized domain names""" # Most providers use ASCII domains, but let's test the logic url = "https://xn--anime-sama-test.si/catalogue/test" provider = detect_provider_from_url(url) # Should not crash def test_punycode_domains(self): """Test punycode-encoded domains""" # ASCII encoding of international domains url = "https://anime-sama.si/catalogue/test" provider = detect_provider_from_url(url) assert provider is not None