refactor: restructure core→server, split large entity files into database module
- Move src/core/ → src/server/ - Split SerieList.py (531 lines) and series.py (414 lines) into src/server/database/ - Add database/models.py for SQLAlchemy models - Update all test imports to reflect new structure - Remove deprecated test files (test_serie_class.py, test_serie_folder_with_year.py)
This commit is contained in:
@@ -7,16 +7,16 @@ from unittest.mock import MagicMock, Mock, PropertyMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from src.core.error_handler import (
|
||||
from src.server.error_handler import (
|
||||
DownloadError,
|
||||
NetworkError,
|
||||
NonRetryableError,
|
||||
RetryableError,
|
||||
)
|
||||
from src.core.providers.base_provider import Loader
|
||||
from src.server.providers.base_provider import Loader
|
||||
|
||||
# Import the class but we need a concrete subclass to test it
|
||||
from src.core.providers.enhanced_provider import EnhancedAniWorldLoader
|
||||
from src.server.providers.enhanced_provider import EnhancedAniWorldLoader
|
||||
|
||||
|
||||
class ConcreteEnhancedLoader(EnhancedAniWorldLoader):
|
||||
@@ -50,9 +50,9 @@ class ConcreteEnhancedLoader(EnhancedAniWorldLoader):
|
||||
def enhanced_loader():
|
||||
"""Create ConcreteEnhancedLoader with mocked externals."""
|
||||
with patch(
|
||||
"src.core.providers.enhanced_provider.UserAgent"
|
||||
"src.server.providers.enhanced_provider.UserAgent"
|
||||
) as mock_ua, patch(
|
||||
"src.core.providers.enhanced_provider.get_integrity_manager"
|
||||
"src.server.providers.enhanced_provider.get_integrity_manager"
|
||||
):
|
||||
mock_ua.return_value.random = "MockAgent/1.0"
|
||||
loader = ConcreteEnhancedLoader()
|
||||
@@ -360,7 +360,7 @@ class TestDownloadStatistics:
|
||||
class TestEnhancedDownloadValidation:
|
||||
"""Test download input validation."""
|
||||
|
||||
@patch("src.core.providers.enhanced_provider.get_integrity_manager")
|
||||
@patch("src.server.providers.enhanced_provider.get_integrity_manager")
|
||||
def test_download_missing_base_directory_raises(
|
||||
self, mock_integrity, enhanced_loader
|
||||
):
|
||||
@@ -368,7 +368,7 @@ class TestEnhancedDownloadValidation:
|
||||
with pytest.raises((ValueError, DownloadError)):
|
||||
enhanced_loader.Download("", "folder", 1, 1, "key")
|
||||
|
||||
@patch("src.core.providers.enhanced_provider.get_integrity_manager")
|
||||
@patch("src.server.providers.enhanced_provider.get_integrity_manager")
|
||||
def test_download_missing_serie_folder_raises(
|
||||
self, mock_integrity, enhanced_loader
|
||||
):
|
||||
@@ -376,7 +376,7 @@ class TestEnhancedDownloadValidation:
|
||||
with pytest.raises((ValueError, DownloadError)):
|
||||
enhanced_loader.Download("/base", "", 1, 1, "key")
|
||||
|
||||
@patch("src.core.providers.enhanced_provider.get_integrity_manager")
|
||||
@patch("src.server.providers.enhanced_provider.get_integrity_manager")
|
||||
def test_download_negative_season_raises(
|
||||
self, mock_integrity, enhanced_loader
|
||||
):
|
||||
@@ -384,7 +384,7 @@ class TestEnhancedDownloadValidation:
|
||||
with pytest.raises((ValueError, DownloadError)):
|
||||
enhanced_loader.Download("/base", "folder", -1, 1, "key")
|
||||
|
||||
@patch("src.core.providers.enhanced_provider.get_integrity_manager")
|
||||
@patch("src.server.providers.enhanced_provider.get_integrity_manager")
|
||||
def test_download_negative_episode_raises(
|
||||
self, mock_integrity, enhanced_loader
|
||||
):
|
||||
@@ -392,7 +392,7 @@ class TestEnhancedDownloadValidation:
|
||||
with pytest.raises((ValueError, DownloadError)):
|
||||
enhanced_loader.Download("/base", "folder", 1, -1, "key")
|
||||
|
||||
@patch("src.core.providers.enhanced_provider.get_integrity_manager")
|
||||
@patch("src.server.providers.enhanced_provider.get_integrity_manager")
|
||||
def test_download_increments_total_count(
|
||||
self, mock_integrity, enhanced_loader
|
||||
):
|
||||
@@ -459,7 +459,7 @@ class TestFetchAnimeListWithRecovery:
|
||||
mock_response.text = json.dumps([{"title": "Naruto"}])
|
||||
|
||||
with patch(
|
||||
"src.core.providers.enhanced_provider.recovery_strategies"
|
||||
"src.server.providers.enhanced_provider.recovery_strategies"
|
||||
) as mock_rs:
|
||||
mock_rs.handle_network_failure.return_value = mock_response
|
||||
result = enhanced_loader._fetch_anime_list_with_recovery(
|
||||
@@ -476,7 +476,7 @@ class TestFetchAnimeListWithRecovery:
|
||||
mock_response.status_code = 404
|
||||
|
||||
with patch(
|
||||
"src.core.providers.enhanced_provider.recovery_strategies"
|
||||
"src.server.providers.enhanced_provider.recovery_strategies"
|
||||
) as mock_rs:
|
||||
mock_rs.handle_network_failure.return_value = mock_response
|
||||
with pytest.raises(NonRetryableError, match="not found"):
|
||||
@@ -491,7 +491,7 @@ class TestFetchAnimeListWithRecovery:
|
||||
mock_response.status_code = 403
|
||||
|
||||
with patch(
|
||||
"src.core.providers.enhanced_provider.recovery_strategies"
|
||||
"src.server.providers.enhanced_provider.recovery_strategies"
|
||||
) as mock_rs:
|
||||
mock_rs.handle_network_failure.return_value = mock_response
|
||||
with pytest.raises(NonRetryableError, match="forbidden"):
|
||||
@@ -506,7 +506,7 @@ class TestFetchAnimeListWithRecovery:
|
||||
mock_response.status_code = 500
|
||||
|
||||
with patch(
|
||||
"src.core.providers.enhanced_provider.recovery_strategies"
|
||||
"src.server.providers.enhanced_provider.recovery_strategies"
|
||||
) as mock_rs:
|
||||
mock_rs.handle_network_failure.return_value = mock_response
|
||||
with pytest.raises(RetryableError, match="Server error"):
|
||||
@@ -519,7 +519,7 @@ class TestFetchAnimeListWithRecovery:
|
||||
import requests as req
|
||||
|
||||
with patch(
|
||||
"src.core.providers.enhanced_provider.recovery_strategies"
|
||||
"src.server.providers.enhanced_provider.recovery_strategies"
|
||||
) as mock_rs:
|
||||
mock_rs.handle_network_failure.side_effect = (
|
||||
req.RequestException("timeout")
|
||||
@@ -548,7 +548,7 @@ class TestGetKeyHTML:
|
||||
mock_response.ok = True
|
||||
|
||||
with patch(
|
||||
"src.core.providers.enhanced_provider.recovery_strategies"
|
||||
"src.server.providers.enhanced_provider.recovery_strategies"
|
||||
) as mock_rs:
|
||||
mock_rs.handle_network_failure.return_value = mock_response
|
||||
result = enhanced_loader._GetKeyHTML("new-key")
|
||||
@@ -563,7 +563,7 @@ class TestGetKeyHTML:
|
||||
mock_response.status_code = 404
|
||||
|
||||
with patch(
|
||||
"src.core.providers.enhanced_provider.recovery_strategies"
|
||||
"src.server.providers.enhanced_provider.recovery_strategies"
|
||||
) as mock_rs:
|
||||
mock_rs.handle_network_failure.return_value = mock_response
|
||||
with pytest.raises(NonRetryableError, match="not found"):
|
||||
@@ -628,7 +628,7 @@ class TestGetEmbeddedLink:
|
||||
"_get_redirect_link",
|
||||
return_value=("https://aniworld.to/redirect/100", "VOE"),
|
||||
), patch(
|
||||
"src.core.providers.enhanced_provider.recovery_strategies"
|
||||
"src.server.providers.enhanced_provider.recovery_strategies"
|
||||
) as mock_rs:
|
||||
mock_rs.handle_network_failure.return_value = mock_response
|
||||
result = enhanced_loader._get_embeded_link(
|
||||
@@ -718,11 +718,11 @@ class TestDownloadWithRecovery:
|
||||
return True
|
||||
|
||||
with patch(
|
||||
"src.core.providers.enhanced_provider.recovery_strategies"
|
||||
"src.server.providers.enhanced_provider.recovery_strategies"
|
||||
) as mock_rs, patch(
|
||||
"src.core.providers.enhanced_provider.file_corruption_detector"
|
||||
"src.server.providers.enhanced_provider.file_corruption_detector"
|
||||
) as mock_fcd, patch(
|
||||
"src.core.providers.enhanced_provider.get_integrity_manager"
|
||||
"src.server.providers.enhanced_provider.get_integrity_manager"
|
||||
) as mock_im:
|
||||
mock_rs.handle_network_failure.return_value = (
|
||||
"https://direct.example.com/v.mp4",
|
||||
@@ -746,7 +746,7 @@ class TestDownloadWithRecovery:
|
||||
output_path = str(tmp_path / "output.mp4")
|
||||
|
||||
with patch(
|
||||
"src.core.providers.enhanced_provider.recovery_strategies"
|
||||
"src.server.providers.enhanced_provider.recovery_strategies"
|
||||
) as mock_rs:
|
||||
mock_rs.handle_network_failure.side_effect = Exception("fail")
|
||||
|
||||
@@ -769,9 +769,9 @@ class TestDownloadWithRecovery:
|
||||
return True
|
||||
|
||||
with patch(
|
||||
"src.core.providers.enhanced_provider.recovery_strategies"
|
||||
"src.server.providers.enhanced_provider.recovery_strategies"
|
||||
) as mock_rs, patch(
|
||||
"src.core.providers.enhanced_provider.file_corruption_detector"
|
||||
"src.server.providers.enhanced_provider.file_corruption_detector"
|
||||
) as mock_fcd:
|
||||
mock_rs.handle_network_failure.return_value = (
|
||||
"https://direct.example.com/v.mp4",
|
||||
@@ -816,7 +816,7 @@ class TestGetSeasonEpisodeCount:
|
||||
]
|
||||
|
||||
with patch(
|
||||
"src.core.providers.enhanced_provider.recovery_strategies"
|
||||
"src.server.providers.enhanced_provider.recovery_strategies"
|
||||
) as mock_rs:
|
||||
mock_rs.handle_network_failure.side_effect = responses
|
||||
result = enhanced_loader.get_season_episode_count("test")
|
||||
@@ -828,7 +828,7 @@ class TestGetSeasonEpisodeCount:
|
||||
base_html = b"<html><body>No seasons</body></html>"
|
||||
|
||||
with patch(
|
||||
"src.core.providers.enhanced_provider.recovery_strategies"
|
||||
"src.server.providers.enhanced_provider.recovery_strategies"
|
||||
) as mock_rs:
|
||||
mock_rs.handle_network_failure.return_value = MagicMock(
|
||||
content=base_html
|
||||
@@ -844,7 +844,7 @@ class TestPerformYtdlDownload:
|
||||
def test_success(self, enhanced_loader):
|
||||
"""Should return True on successful download."""
|
||||
with patch(
|
||||
"src.core.providers.enhanced_provider.YoutubeDL"
|
||||
"src.server.providers.enhanced_provider.YoutubeDL"
|
||||
) as MockYDL:
|
||||
mock_ydl = MagicMock()
|
||||
MockYDL.return_value.__enter__ = MagicMock(return_value=mock_ydl)
|
||||
@@ -858,7 +858,7 @@ class TestPerformYtdlDownload:
|
||||
def test_failure_raises_download_error(self, enhanced_loader):
|
||||
"""yt-dlp failure should raise DownloadError."""
|
||||
with patch(
|
||||
"src.core.providers.enhanced_provider.YoutubeDL"
|
||||
"src.server.providers.enhanced_provider.YoutubeDL"
|
||||
) as MockYDL:
|
||||
mock_ydl = MagicMock()
|
||||
mock_ydl.download.side_effect = Exception("yt-dlp crash")
|
||||
@@ -873,7 +873,7 @@ class TestPerformYtdlDownload:
|
||||
class TestDownloadFlow:
|
||||
"""Test full Download method flow."""
|
||||
|
||||
@patch("src.core.providers.enhanced_provider.get_integrity_manager")
|
||||
@patch("src.server.providers.enhanced_provider.get_integrity_manager")
|
||||
def test_existing_valid_file_returns_true(
|
||||
self, mock_integrity, enhanced_loader, tmp_path
|
||||
):
|
||||
@@ -889,7 +889,7 @@ class TestDownloadFlow:
|
||||
)
|
||||
|
||||
with patch(
|
||||
"src.core.providers.enhanced_provider.file_corruption_detector"
|
||||
"src.server.providers.enhanced_provider.file_corruption_detector"
|
||||
) as mock_fcd:
|
||||
mock_fcd.is_valid_video_file.return_value = True
|
||||
mock_integrity.return_value.has_checksum.return_value = False
|
||||
@@ -901,7 +901,7 @@ class TestDownloadFlow:
|
||||
assert result is True
|
||||
assert enhanced_loader.download_stats["successful_downloads"] == 1
|
||||
|
||||
@patch("src.core.providers.enhanced_provider.get_integrity_manager")
|
||||
@patch("src.server.providers.enhanced_provider.get_integrity_manager")
|
||||
def test_missing_key_raises_value_error(
|
||||
self, mock_integrity, enhanced_loader, tmp_path
|
||||
):
|
||||
@@ -915,7 +915,7 @@ class TestAniworldLoaderCompat:
|
||||
|
||||
def test_inherits_from_enhanced(self):
|
||||
"""AniworldLoader should extend EnhancedAniWorldLoader."""
|
||||
from src.core.providers.enhanced_provider import AniworldLoader
|
||||
from src.server.providers.enhanced_provider import AniworldLoader
|
||||
|
||||
assert issubclass(AniworldLoader, EnhancedAniWorldLoader)
|
||||
|
||||
@@ -936,11 +936,11 @@ class TestFfmpegHlsOptions:
|
||||
return True
|
||||
|
||||
with patch(
|
||||
"src.core.providers.enhanced_provider.recovery_strategies"
|
||||
"src.server.providers.enhanced_provider.recovery_strategies"
|
||||
) as mock_rs, patch(
|
||||
"src.core.providers.enhanced_provider.file_corruption_detector"
|
||||
"src.server.providers.enhanced_provider.file_corruption_detector"
|
||||
) as mock_fcd, patch(
|
||||
"src.core.providers.enhanced_provider.get_integrity_manager"
|
||||
"src.server.providers.enhanced_provider.get_integrity_manager"
|
||||
) as mock_im:
|
||||
mock_rs.handle_network_failure.return_value = (
|
||||
"https://direct.example.com/v.mp4",
|
||||
@@ -969,7 +969,7 @@ class TestHlsUrlDetection:
|
||||
def test_voe_hls_pattern_extracts_hls_url(self):
|
||||
"""HLS_PATTERN should extract HLS URL from VOE embedded player HTML."""
|
||||
import re
|
||||
from src.core.providers.streaming.voe import HLS_PATTERN
|
||||
from src.server.providers.streaming.voe import HLS_PATTERN
|
||||
|
||||
html_with_hls = """
|
||||
var playerConfig = {
|
||||
@@ -984,7 +984,7 @@ class TestHlsUrlDetection:
|
||||
def test_voe_hls_pattern_returns_none_when_no_hls(self):
|
||||
"""HLS_PATTERN should return None when no HLS URL in HTML."""
|
||||
import re
|
||||
from src.core.providers.streaming.voe import HLS_PATTERN
|
||||
from src.server.providers.streaming.voe import HLS_PATTERN
|
||||
|
||||
html_no_hls = """
|
||||
var playerConfig = {
|
||||
@@ -997,7 +997,7 @@ class TestHlsUrlDetection:
|
||||
def test_hls_url_detection_in_provider_flow(self, enhanced_loader, tmp_path):
|
||||
"""Provider should detect and handle HLS URLs from VOE extractor."""
|
||||
import re
|
||||
from src.core.providers.streaming.voe import HLS_PATTERN
|
||||
from src.server.providers.streaming.voe import HLS_PATTERN
|
||||
|
||||
# Simulate VOE returning an HLS URL (base64 encoded .m3u8)
|
||||
encoded_hls = "aHR0cHM6Ly9leGFtcGxlLmNvbS92aWRlby5tM3U4"
|
||||
|
||||
Reference in New Issue
Block a user