mirror of
https://github.com/Benexl/FastAnime.git
synced 2026-02-04 19:11:55 -08:00
chore: cleanup old testing strategy
This commit is contained in:
@@ -1,115 +0,0 @@
|
||||
"""Tests for episode range parser."""
|
||||
|
||||
import pytest
|
||||
|
||||
from fastanime.cli.utils.parser import parse_episode_range
|
||||
|
||||
|
||||
class TestParseEpisodeRange:
|
||||
"""Test cases for the parse_episode_range function."""
|
||||
|
||||
@pytest.fixture
|
||||
def episodes(self):
|
||||
"""Sample episode list for testing."""
|
||||
return ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
|
||||
|
||||
def test_no_range_returns_all_episodes(self, episodes):
|
||||
"""Test that None or empty range returns all episodes."""
|
||||
result = list(parse_episode_range(None, episodes))
|
||||
assert result == episodes
|
||||
|
||||
def test_colon_only_returns_all_episodes(self, episodes):
|
||||
"""Test that ':' returns all episodes."""
|
||||
result = list(parse_episode_range(":", episodes))
|
||||
assert result == episodes
|
||||
|
||||
def test_start_end_range(self, episodes):
|
||||
"""Test start:end range format."""
|
||||
result = list(parse_episode_range("2:5", episodes))
|
||||
assert result == ["3", "4", "5"]
|
||||
|
||||
def test_start_only_range(self, episodes):
|
||||
"""Test start: range format."""
|
||||
result = list(parse_episode_range("5:", episodes))
|
||||
assert result == ["6", "7", "8", "9", "10"]
|
||||
|
||||
def test_end_only_range(self, episodes):
|
||||
"""Test :end range format."""
|
||||
result = list(parse_episode_range(":3", episodes))
|
||||
assert result == ["1", "2", "3"]
|
||||
|
||||
def test_start_end_step_range(self, episodes):
|
||||
"""Test start:end:step range format."""
|
||||
result = list(parse_episode_range("2:8:2", episodes))
|
||||
assert result == ["3", "5", "7"]
|
||||
|
||||
def test_single_number_range(self, episodes):
|
||||
"""Test single number format (start from index)."""
|
||||
result = list(parse_episode_range("5", episodes))
|
||||
assert result == ["6", "7", "8", "9", "10"]
|
||||
|
||||
def test_empty_start_end_in_three_part_range_raises_error(self, episodes):
|
||||
"""Test that empty parts in start:end:step format raise error."""
|
||||
with pytest.raises(ValueError, match="When using 3 parts"):
|
||||
list(parse_episode_range(":5:2", episodes))
|
||||
|
||||
with pytest.raises(ValueError, match="When using 3 parts"):
|
||||
list(parse_episode_range("2::2", episodes))
|
||||
|
||||
with pytest.raises(ValueError, match="When using 3 parts"):
|
||||
list(parse_episode_range("2:5:", episodes))
|
||||
|
||||
def test_invalid_integer_raises_error(self, episodes):
|
||||
"""Test that invalid integers raise ValueError."""
|
||||
with pytest.raises(ValueError, match="Must be a valid integer"):
|
||||
list(parse_episode_range("abc", episodes))
|
||||
|
||||
with pytest.raises(ValueError, match="Start and end must be valid integers"):
|
||||
list(parse_episode_range("2:abc", episodes))
|
||||
|
||||
with pytest.raises(ValueError, match="All parts must be valid integers"):
|
||||
list(parse_episode_range("2:5:abc", episodes))
|
||||
|
||||
def test_zero_step_raises_error(self, episodes):
|
||||
"""Test that zero step raises ValueError."""
|
||||
with pytest.raises(ValueError, match="Step value must be positive"):
|
||||
list(parse_episode_range("2:5:0", episodes))
|
||||
|
||||
def test_negative_step_raises_error(self, episodes):
|
||||
"""Test that negative step raises ValueError."""
|
||||
with pytest.raises(ValueError, match="Step value must be positive"):
|
||||
list(parse_episode_range("2:5:-1", episodes))
|
||||
|
||||
def test_too_many_colons_raises_error(self, episodes):
|
||||
"""Test that too many colons raise ValueError."""
|
||||
with pytest.raises(ValueError, match="Too many colon separators"):
|
||||
list(parse_episode_range("2:5:7:9", episodes))
|
||||
|
||||
def test_edge_case_empty_list(self):
|
||||
"""Test behavior with empty episode list."""
|
||||
result = list(parse_episode_range(":", []))
|
||||
assert result == []
|
||||
|
||||
def test_edge_case_single_episode(self):
|
||||
"""Test behavior with single episode."""
|
||||
episodes = ["1"]
|
||||
result = list(parse_episode_range(":", episodes))
|
||||
assert result == ["1"]
|
||||
|
||||
result = list(parse_episode_range("0:1", episodes))
|
||||
assert result == ["1"]
|
||||
|
||||
def test_numerical_sorting(self):
|
||||
"""Test that episodes are sorted numerically, not lexicographically."""
|
||||
episodes = ["10", "2", "1", "11", "3"]
|
||||
result = list(parse_episode_range(":", episodes))
|
||||
assert result == ["1", "2", "3", "10", "11"]
|
||||
|
||||
def test_index_out_of_bounds_behavior(self, episodes):
|
||||
"""Test behavior when indices exceed available episodes."""
|
||||
# Python slicing handles out-of-bounds gracefully
|
||||
result = list(parse_episode_range("15:", episodes))
|
||||
assert result == [] # No episodes beyond index 15
|
||||
|
||||
result = list(parse_episode_range(":20", episodes))
|
||||
assert result == episodes # All episodes (slice stops at end)
|
||||
@@ -1,210 +0,0 @@
|
||||
"""
|
||||
Tests for the TorrentDownloader class.
|
||||
"""
|
||||
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
|
||||
from fastanime.core.downloader.torrents import (
|
||||
TorrentDownloader,
|
||||
TorrentDownloadError,
|
||||
LIBTORRENT_AVAILABLE,
|
||||
)
|
||||
from fastanime.core.exceptions import DependencyNotFoundError
|
||||
|
||||
|
||||
class TestTorrentDownloader(unittest.TestCase):
|
||||
"""Test cases for TorrentDownloader class."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures."""
|
||||
self.temp_dir = Path(tempfile.mkdtemp())
|
||||
self.downloader = TorrentDownloader(
|
||||
download_path=self.temp_dir,
|
||||
max_upload_rate=100,
|
||||
max_download_rate=200,
|
||||
max_connections=50,
|
||||
)
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up test fixtures."""
|
||||
import shutil
|
||||
|
||||
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
||||
|
||||
def test_init(self):
|
||||
"""Test TorrentDownloader initialization."""
|
||||
self.assertEqual(self.downloader.download_path, self.temp_dir)
|
||||
self.assertEqual(self.downloader.max_upload_rate, 100)
|
||||
self.assertEqual(self.downloader.max_download_rate, 200)
|
||||
self.assertEqual(self.downloader.max_connections, 50)
|
||||
self.assertTrue(self.temp_dir.exists())
|
||||
|
||||
def test_init_creates_download_directory(self):
|
||||
"""Test that download directory is created if it doesn't exist."""
|
||||
non_existent_dir = self.temp_dir / "new_dir"
|
||||
self.assertFalse(non_existent_dir.exists())
|
||||
|
||||
downloader = TorrentDownloader(download_path=non_existent_dir)
|
||||
self.assertTrue(non_existent_dir.exists())
|
||||
|
||||
@patch("fastanime.core.downloader.torrents.shutil.which")
|
||||
def test_download_with_webtorrent_cli_not_available(self, mock_which):
|
||||
"""Test webtorrent CLI fallback when not available."""
|
||||
mock_which.return_value = None
|
||||
|
||||
with self.assertRaises(DependencyNotFoundError) as context:
|
||||
self.downloader.download_with_webtorrent_cli("magnet:test")
|
||||
|
||||
self.assertIn("webtorrent CLI is not available", str(context.exception))
|
||||
|
||||
@patch("fastanime.core.downloader.torrents.subprocess.run")
|
||||
@patch("fastanime.core.downloader.torrents.shutil.which")
|
||||
def test_download_with_webtorrent_cli_success(self, mock_which, mock_run):
|
||||
"""Test successful webtorrent CLI download."""
|
||||
mock_which.return_value = "/usr/bin/webtorrent"
|
||||
mock_result = Mock()
|
||||
mock_result.stdout = f"Downloaded test-file to {self.temp_dir}/test-file"
|
||||
mock_run.return_value = mock_result
|
||||
|
||||
# Create a dummy file to simulate download
|
||||
test_file = self.temp_dir / "test-file"
|
||||
test_file.touch()
|
||||
|
||||
result = self.downloader.download_with_webtorrent_cli("magnet:test")
|
||||
|
||||
mock_run.assert_called_once()
|
||||
self.assertEqual(result, test_file)
|
||||
|
||||
@patch("fastanime.core.downloader.torrents.subprocess.run")
|
||||
@patch("fastanime.core.downloader.torrents.shutil.which")
|
||||
def test_download_with_webtorrent_cli_failure(self, mock_which, mock_run):
|
||||
"""Test webtorrent CLI download failure."""
|
||||
mock_which.return_value = "/usr/bin/webtorrent"
|
||||
mock_run.side_effect = subprocess.CalledProcessError(
|
||||
1, "webtorrent", stderr="Error"
|
||||
)
|
||||
|
||||
with self.assertRaises(TorrentDownloadError) as context:
|
||||
self.downloader.download_with_webtorrent_cli("magnet:test")
|
||||
|
||||
self.assertIn("webtorrent CLI failed", str(context.exception))
|
||||
|
||||
@unittest.skipUnless(LIBTORRENT_AVAILABLE, "libtorrent not available")
|
||||
def test_setup_libtorrent_session(self):
|
||||
"""Test libtorrent session setup when available."""
|
||||
session = self.downloader._setup_libtorrent_session()
|
||||
self.assertIsNotNone(session)
|
||||
|
||||
@unittest.skipIf(LIBTORRENT_AVAILABLE, "libtorrent is available")
|
||||
def test_setup_libtorrent_session_not_available(self):
|
||||
"""Test libtorrent session setup when not available."""
|
||||
with self.assertRaises(DependencyNotFoundError):
|
||||
self.downloader._setup_libtorrent_session()
|
||||
|
||||
@patch("fastanime.core.downloader.torrents.LIBTORRENT_AVAILABLE", False)
|
||||
def test_download_with_libtorrent_not_available(self):
|
||||
"""Test libtorrent download when not available."""
|
||||
with self.assertRaises(DependencyNotFoundError) as context:
|
||||
self.downloader.download_with_libtorrent("magnet:test")
|
||||
|
||||
self.assertIn("libtorrent is not available", str(context.exception))
|
||||
|
||||
def test_progress_callback(self):
|
||||
"""Test progress callback functionality."""
|
||||
callback_mock = Mock()
|
||||
downloader = TorrentDownloader(
|
||||
download_path=self.temp_dir, progress_callback=callback_mock
|
||||
)
|
||||
|
||||
# The callback should be stored
|
||||
self.assertEqual(downloader.progress_callback, callback_mock)
|
||||
|
||||
@patch.object(TorrentDownloader, "download_with_webtorrent_cli")
|
||||
@patch.object(TorrentDownloader, "download_with_libtorrent")
|
||||
def test_download_prefers_libtorrent(self, mock_libtorrent, mock_webtorrent):
|
||||
"""Test that download method prefers libtorrent by default."""
|
||||
mock_libtorrent.return_value = self.temp_dir / "test"
|
||||
|
||||
with patch("fastanime.core.downloader.torrents.LIBTORRENT_AVAILABLE", True):
|
||||
result = self.downloader.download("magnet:test", prefer_libtorrent=True)
|
||||
|
||||
mock_libtorrent.assert_called_once()
|
||||
mock_webtorrent.assert_not_called()
|
||||
|
||||
@patch.object(TorrentDownloader, "download_with_webtorrent_cli")
|
||||
@patch.object(TorrentDownloader, "download_with_libtorrent")
|
||||
def test_download_fallback_to_webtorrent(self, mock_libtorrent, mock_webtorrent):
|
||||
"""Test fallback to webtorrent when libtorrent fails."""
|
||||
mock_libtorrent.side_effect = DependencyNotFoundError("libtorrent not found")
|
||||
mock_webtorrent.return_value = self.temp_dir / "test"
|
||||
|
||||
with patch("fastanime.core.downloader.torrents.LIBTORRENT_AVAILABLE", True):
|
||||
result = self.downloader.download("magnet:test")
|
||||
|
||||
mock_libtorrent.assert_called_once()
|
||||
mock_webtorrent.assert_called_once()
|
||||
self.assertEqual(result, self.temp_dir / "test")
|
||||
|
||||
@patch.object(TorrentDownloader, "download_with_webtorrent_cli")
|
||||
@patch.object(TorrentDownloader, "download_with_libtorrent")
|
||||
def test_download_all_methods_fail(self, mock_libtorrent, mock_webtorrent):
|
||||
"""Test when all download methods fail."""
|
||||
mock_libtorrent.side_effect = DependencyNotFoundError("libtorrent not found")
|
||||
mock_webtorrent.side_effect = DependencyNotFoundError("webtorrent not found")
|
||||
|
||||
with self.assertRaises(TorrentDownloadError) as context:
|
||||
self.downloader.download("magnet:test")
|
||||
|
||||
self.assertIn("All torrent download methods failed", str(context.exception))
|
||||
|
||||
def test_magnet_link_detection(self):
|
||||
"""Test detection of magnet links."""
|
||||
magnet_link = "magnet:?xt=urn:btih:test"
|
||||
http_link = "http://example.com/test.torrent"
|
||||
file_path = "/path/to/test.torrent"
|
||||
|
||||
# These would be tested in integration tests with actual libtorrent
|
||||
# Here we just verify the method exists and handles different input types
|
||||
self.assertTrue(magnet_link.startswith("magnet:"))
|
||||
self.assertTrue(http_link.startswith(("http://", "https://")))
|
||||
self.assertFalse(file_path.startswith(("magnet:", "http://", "https://")))
|
||||
|
||||
|
||||
class TestLegacyFunction(unittest.TestCase):
|
||||
"""Test the legacy function for backward compatibility."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures."""
|
||||
self.temp_dir = Path(tempfile.mkdtemp())
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up test fixtures."""
|
||||
import shutil
|
||||
|
||||
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
||||
|
||||
@patch.object(TorrentDownloader, "download_with_webtorrent_cli")
|
||||
def test_legacy_function(self, mock_download):
|
||||
"""Test the legacy download_torrent_with_webtorrent_cli function."""
|
||||
from fastanime.core.downloader.torrents import (
|
||||
download_torrent_with_webtorrent_cli,
|
||||
)
|
||||
|
||||
test_path = self.temp_dir / "test.mkv"
|
||||
mock_download.return_value = test_path
|
||||
|
||||
result = download_torrent_with_webtorrent_cli(test_path, "magnet:test")
|
||||
|
||||
mock_download.assert_called_once_with("magnet:test")
|
||||
self.assertEqual(result, test_path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Add subprocess import for the test
|
||||
import subprocess
|
||||
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user