update
Some checks failed
Tests / test (ubuntu-latest, 3.8) (push) Failing after 24m2s
Tests / test (ubuntu-latest, 3.10) (push) Failing after 24m4s
Tests / test (ubuntu-latest, 3.9) (push) Failing after 10m52s
Tests / test (macos-latest, 3.10) (push) Has been cancelled
Tests / test (macos-latest, 3.8) (push) Has been cancelled
Tests / test (macos-latest, 3.9) (push) Has been cancelled
Tests / test (windows-latest, 3.10) (push) Has been cancelled
Tests / test (windows-latest, 3.8) (push) Has been cancelled
Tests / test (windows-latest, 3.9) (push) Has been cancelled
Some checks failed
Tests / test (ubuntu-latest, 3.8) (push) Failing after 24m2s
Tests / test (ubuntu-latest, 3.10) (push) Failing after 24m4s
Tests / test (ubuntu-latest, 3.9) (push) Failing after 10m52s
Tests / test (macos-latest, 3.10) (push) Has been cancelled
Tests / test (macos-latest, 3.8) (push) Has been cancelled
Tests / test (macos-latest, 3.9) (push) Has been cancelled
Tests / test (windows-latest, 3.10) (push) Has been cancelled
Tests / test (windows-latest, 3.8) (push) Has been cancelled
Tests / test (windows-latest, 3.9) (push) Has been cancelled
This commit is contained in:
50
tests/README.md
Normal file
50
tests/README.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# Jimaku-DL Tests
|
||||
|
||||
This directory contains tests for the jimaku-dl package using pytest.
|
||||
|
||||
## Running Tests
|
||||
|
||||
To run all tests:
|
||||
|
||||
```bash
|
||||
pytest
|
||||
```
|
||||
|
||||
To run with verbose output:
|
||||
|
||||
```bash
|
||||
pytest -v
|
||||
```
|
||||
|
||||
To run a specific test file:
|
||||
|
||||
```bash
|
||||
pytest tests/test_downloader.py
|
||||
```
|
||||
|
||||
To run a specific test:
|
||||
|
||||
```bash
|
||||
pytest tests/test_downloader.py::TestJimakuDownloader::test_init
|
||||
```
|
||||
|
||||
## Test Coverage
|
||||
|
||||
To generate a test coverage report:
|
||||
|
||||
```bash
|
||||
pytest --cov=jimaku_dl
|
||||
```
|
||||
|
||||
For an HTML coverage report:
|
||||
|
||||
```bash
|
||||
pytest --cov=jimaku_dl --cov-report=html
|
||||
```
|
||||
|
||||
## Adding Tests
|
||||
|
||||
1. Create test files with the naming convention `test_*.py`
|
||||
2. Create test classes with the naming convention `Test*`
|
||||
3. Create test methods with the naming convention `test_*`
|
||||
4. Use the fixtures defined in `conftest.py` for common functionality
|
||||
1
tests/__init__.py
Normal file
1
tests/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Test package for jimaku-dl."""
|
||||
147
tests/conftest.py
Normal file
147
tests/conftest.py
Normal file
@@ -0,0 +1,147 @@
|
||||
"""Global pytest fixtures for jimaku-dl tests."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
# Add the src directory to the Python path
|
||||
project_root = Path(__file__).parent.parent
|
||||
src_path = project_root / "src"
|
||||
sys.path.insert(0, str(src_path))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def temp_dir():
|
||||
"""Create a temporary directory for test files."""
|
||||
with tempfile.TemporaryDirectory() as tmpdirname:
|
||||
yield tmpdirname
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_anilist_response():
|
||||
"""Mock response from AniList API."""
|
||||
return {
|
||||
"data": {
|
||||
"Media": {
|
||||
"id": 123456,
|
||||
"title": {
|
||||
"romaji": "Test Anime",
|
||||
"english": "Test Anime English",
|
||||
"native": "テストアニメ",
|
||||
},
|
||||
"synonyms": ["Test Show"],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_jimaku_entries_response():
|
||||
"""Mock response from Jimaku entries endpoint."""
|
||||
return [
|
||||
{
|
||||
"id": 1,
|
||||
"english_name": "Test Anime",
|
||||
"japanese_name": "テストアニメ",
|
||||
"anilist_id": 123456,
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_jimaku_files_response():
|
||||
"""Mock response from Jimaku files endpoint."""
|
||||
return [
|
||||
{
|
||||
"id": 101,
|
||||
"name": "Test Anime - 01.srt",
|
||||
"url": "https://jimaku.cc/api/files/101",
|
||||
},
|
||||
{
|
||||
"id": 102,
|
||||
"name": "Test Anime - 02.srt",
|
||||
"url": "https://jimaku.cc/api/files/102",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_requests(
|
||||
monkeypatch,
|
||||
mock_anilist_response,
|
||||
mock_jimaku_entries_response,
|
||||
mock_jimaku_files_response,
|
||||
):
|
||||
"""Mock requests module for API calls."""
|
||||
mock_response = MagicMock()
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
mock_response.json = MagicMock()
|
||||
|
||||
def mock_requests_post(url, **kwargs):
|
||||
if "anilist.co" in url:
|
||||
mock_response.json.return_value = mock_anilist_response
|
||||
return mock_response
|
||||
|
||||
def mock_requests_get(url, **kwargs):
|
||||
if "entries/search" in url:
|
||||
mock_response.json.return_value = mock_jimaku_entries_response
|
||||
elif "entries/" in url and "/files" in url:
|
||||
mock_response.json.return_value = mock_jimaku_files_response
|
||||
return mock_response
|
||||
|
||||
# Patch both the direct imports used in downloader.py and the regular requests module
|
||||
monkeypatch.setattr("requests.post", mock_requests_post)
|
||||
monkeypatch.setattr("requests.get", mock_requests_get)
|
||||
monkeypatch.setattr("jimaku_dl.downloader.requests_post", mock_requests_post)
|
||||
monkeypatch.setattr("jimaku_dl.downloader.requests_get", mock_requests_get)
|
||||
|
||||
return {
|
||||
"post": mock_requests_post,
|
||||
"get": mock_requests_get,
|
||||
"response": mock_response,
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_subprocess(monkeypatch):
|
||||
"""Mock subprocess module for fzf and mpv calls."""
|
||||
mock_run = MagicMock()
|
||||
mock_result = MagicMock()
|
||||
mock_result.stdout = "1. Test Selection"
|
||||
mock_run.return_value = mock_result
|
||||
|
||||
monkeypatch.setattr("subprocess.run", mock_run)
|
||||
return mock_run
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_video_file(temp_dir):
|
||||
"""Create a sample video file."""
|
||||
file_path = os.path.join(temp_dir, "Test Anime S01E01 [1080p].mkv")
|
||||
with open(file_path, "wb") as f:
|
||||
f.write(b"dummy video content")
|
||||
return file_path
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_anime_directory(temp_dir):
|
||||
"""Create a sample directory structure for anime."""
|
||||
# Main directory
|
||||
anime_dir = os.path.join(temp_dir, "Test Anime")
|
||||
os.makedirs(anime_dir)
|
||||
|
||||
# Season subdirectory
|
||||
season_dir = os.path.join(anime_dir, "Season-1")
|
||||
os.makedirs(season_dir)
|
||||
|
||||
# Episode files
|
||||
for i in range(1, 3):
|
||||
file_path = os.path.join(season_dir, f"Test Anime S01E0{i} [1080p].mkv")
|
||||
with open(file_path, "wb") as f:
|
||||
f.write(b"dummy video content")
|
||||
|
||||
return anime_dir
|
||||
5
tests/coverage.sh
Executable file
5
tests/coverage.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
coverage run -m pytest
|
||||
coverage html
|
||||
coverage report -m
|
||||
2
tests/fixtures/__init__.py
vendored
Normal file
2
tests/fixtures/__init__.py
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
"""Test fixtures package."""
|
||||
# This file can be empty, it's just to make the directory a proper package
|
||||
318
tests/test_cli.py
Normal file
318
tests/test_cli.py
Normal file
@@ -0,0 +1,318 @@
|
||||
"""Tests for the command line interface module."""
|
||||
|
||||
import sys
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from jimaku_dl.cli import __version__, main
|
||||
|
||||
|
||||
class TestCli:
|
||||
"""Tests for the command line interface."""
|
||||
|
||||
def test_main_success(self, monkeypatch):
|
||||
"""Test successful execution of the CLI main function."""
|
||||
mock_downloader = MagicMock()
|
||||
mock_downloader.return_value.download_subtitles.return_value = [
|
||||
"/path/to/subtitle.srt"
|
||||
]
|
||||
monkeypatch.setattr("jimaku_dl.cli.JimakuDownloader", mock_downloader)
|
||||
|
||||
with patch("sys.argv", ["jimaku-dl", "/path/to/video.mkv"]):
|
||||
with patch("jimaku_dl.cli.ArgumentParser.parse_args") as mock_args:
|
||||
mock_args.return_value.media_path = "/path/to/video.mkv"
|
||||
mock_args.return_value.dest = None
|
||||
mock_args.return_value.play = False
|
||||
mock_args.return_value.api_token = "test_token"
|
||||
mock_args.return_value.log_level = "INFO"
|
||||
mock_args.return_value.anilist_id = None
|
||||
|
||||
result = main()
|
||||
|
||||
assert result == 0
|
||||
|
||||
mock_downloader.assert_called_once_with(
|
||||
api_token="test_token", log_level="INFO"
|
||||
)
|
||||
mock_downloader.return_value.download_subtitles.assert_called_once_with(
|
||||
media_path="/path/to/video.mkv",
|
||||
dest_dir=None,
|
||||
play=False,
|
||||
anilist_id=None,
|
||||
)
|
||||
|
||||
def test_main_error(self, monkeypatch):
|
||||
"""Test CLI error handling."""
|
||||
mock_downloader = MagicMock()
|
||||
mock_downloader.return_value.download_subtitles.side_effect = ValueError(
|
||||
"Test error"
|
||||
)
|
||||
monkeypatch.setattr("jimaku_dl.cli.JimakuDownloader", mock_downloader)
|
||||
|
||||
with patch("sys.argv", ["jimaku-dl", "/path/to/video.mkv"]):
|
||||
with patch("jimaku_dl.cli.ArgumentParser.parse_args") as mock_args:
|
||||
mock_args.return_value.media_path = "/path/to/video.mkv"
|
||||
mock_args.return_value.dest = None
|
||||
mock_args.return_value.play = False
|
||||
mock_args.return_value.api_token = "test_token"
|
||||
mock_args.return_value.log_level = "INFO"
|
||||
mock_args.return_value.anilist_id = None
|
||||
|
||||
with patch("builtins.print") as mock_print:
|
||||
result = main()
|
||||
|
||||
assert result == 1
|
||||
|
||||
mock_print.assert_called_with("Error: Test error")
|
||||
|
||||
def test_main_unexpected_error(self, monkeypatch):
|
||||
"""Test CLI handling of unexpected errors."""
|
||||
mock_downloader = MagicMock()
|
||||
mock_downloader.return_value.download_subtitles.side_effect = Exception(
|
||||
"Unexpected error"
|
||||
)
|
||||
monkeypatch.setattr("jimaku_dl.cli.JimakuDownloader", mock_downloader)
|
||||
|
||||
with patch("sys.argv", ["jimaku-dl", "/path/to/video.mkv"]):
|
||||
with patch("jimaku_dl.cli.ArgumentParser.parse_args") as mock_args:
|
||||
mock_args.return_value.media_path = "/path/to/video.mkv"
|
||||
mock_args.return_value.dest = None
|
||||
mock_args.return_value.play = False
|
||||
mock_args.return_value.api_token = "test_token"
|
||||
mock_args.return_value.log_level = "INFO"
|
||||
mock_args.return_value.anilist_id = None
|
||||
|
||||
with patch("builtins.print") as mock_print:
|
||||
result = main()
|
||||
|
||||
assert result == 1
|
||||
mock_print.assert_called_with("Unexpected error: Unexpected error")
|
||||
|
||||
def test_anilist_id_arg(self, monkeypatch):
|
||||
"""Test CLI with anilist_id argument."""
|
||||
mock_downloader = MagicMock()
|
||||
mock_downloader.return_value.download_subtitles.return_value = [
|
||||
"/path/to/subtitle.srt"
|
||||
]
|
||||
monkeypatch.setattr("jimaku_dl.cli.JimakuDownloader", mock_downloader)
|
||||
|
||||
with patch(
|
||||
"sys.argv", ["jimaku-dl", "/path/to/video.mkv", "--anilist-id", "123456"]
|
||||
):
|
||||
with patch("jimaku_dl.cli.ArgumentParser.parse_args") as mock_args:
|
||||
mock_args.return_value.media_path = "/path/to/video.mkv"
|
||||
mock_args.return_value.dest = None
|
||||
mock_args.return_value.play = False
|
||||
mock_args.return_value.api_token = "test_token"
|
||||
mock_args.return_value.log_level = "INFO"
|
||||
mock_args.return_value.anilist_id = 123456
|
||||
|
||||
result = main()
|
||||
|
||||
assert result == 0
|
||||
|
||||
mock_downloader.return_value.download_subtitles.assert_called_once_with(
|
||||
media_path="/path/to/video.mkv",
|
||||
dest_dir=None,
|
||||
play=False,
|
||||
anilist_id=123456,
|
||||
)
|
||||
|
||||
def test_dest_arg(self, monkeypatch):
|
||||
"""Test CLI with dest argument."""
|
||||
mock_downloader = MagicMock()
|
||||
mock_downloader.return_value.download_subtitles.return_value = [
|
||||
"/custom/path/subtitle.srt"
|
||||
]
|
||||
monkeypatch.setattr("jimaku_dl.cli.JimakuDownloader", mock_downloader)
|
||||
|
||||
with patch(
|
||||
"sys.argv", ["jimaku-dl", "/path/to/video.mkv", "--dest", "/custom/path"]
|
||||
):
|
||||
with patch("jimaku_dl.cli.ArgumentParser.parse_args") as mock_args:
|
||||
mock_args.return_value.media_path = "/path/to/video.mkv"
|
||||
mock_args.return_value.dest = "/custom/path"
|
||||
mock_args.return_value.play = False
|
||||
mock_args.return_value.api_token = "test_token"
|
||||
mock_args.return_value.log_level = "INFO"
|
||||
mock_args.return_value.anilist_id = None
|
||||
|
||||
result = main()
|
||||
|
||||
assert result == 0
|
||||
mock_downloader.return_value.download_subtitles.assert_called_once_with(
|
||||
media_path="/path/to/video.mkv",
|
||||
dest_dir="/custom/path",
|
||||
play=False,
|
||||
anilist_id=None,
|
||||
)
|
||||
|
||||
def test_play_arg(self, monkeypatch):
|
||||
"""Test CLI with play argument."""
|
||||
mock_downloader = MagicMock()
|
||||
mock_downloader.return_value.download_subtitles.return_value = [
|
||||
"/path/to/subtitle.srt"
|
||||
]
|
||||
monkeypatch.setattr("jimaku_dl.cli.JimakuDownloader", mock_downloader)
|
||||
|
||||
with patch("sys.argv", ["jimaku-dl", "/path/to/video.mkv", "--play"]):
|
||||
with patch("jimaku_dl.cli.ArgumentParser.parse_args") as mock_args:
|
||||
mock_args.return_value.media_path = "/path/to/video.mkv"
|
||||
mock_args.return_value.dest = None
|
||||
mock_args.return_value.play = True
|
||||
mock_args.return_value.api_token = "test_token"
|
||||
mock_args.return_value.log_level = "INFO"
|
||||
mock_args.return_value.anilist_id = None
|
||||
|
||||
result = main()
|
||||
|
||||
assert result == 0
|
||||
mock_downloader.return_value.download_subtitles.assert_called_once_with(
|
||||
media_path="/path/to/video.mkv",
|
||||
dest_dir=None,
|
||||
play=True,
|
||||
anilist_id=None,
|
||||
)
|
||||
|
||||
def test_token_arg(self, monkeypatch):
|
||||
"""Test CLI with token argument."""
|
||||
mock_downloader = MagicMock()
|
||||
mock_downloader.return_value.download_subtitles.return_value = [
|
||||
"/path/to/subtitle.srt"
|
||||
]
|
||||
monkeypatch.setattr("jimaku_dl.cli.JimakuDownloader", mock_downloader)
|
||||
|
||||
with patch(
|
||||
"sys.argv", ["jimaku-dl", "/path/to/video.mkv", "--token", "custom_token"]
|
||||
):
|
||||
with patch("jimaku_dl.cli.ArgumentParser.parse_args") as mock_args:
|
||||
mock_args.return_value.media_path = "/path/to/video.mkv"
|
||||
mock_args.return_value.dest = None
|
||||
mock_args.return_value.play = False
|
||||
mock_args.return_value.api_token = "custom_token"
|
||||
mock_args.return_value.log_level = "INFO"
|
||||
mock_args.return_value.anilist_id = None
|
||||
|
||||
result = main()
|
||||
|
||||
assert result == 0
|
||||
mock_downloader.assert_called_once_with(
|
||||
api_token="custom_token", log_level="INFO"
|
||||
)
|
||||
|
||||
def test_log_level_arg(self, monkeypatch):
|
||||
"""Test CLI with log_level argument."""
|
||||
mock_downloader = MagicMock()
|
||||
mock_downloader.return_value.download_subtitles.return_value = [
|
||||
"/path/to/subtitle.srt"
|
||||
]
|
||||
monkeypatch.setattr("jimaku_dl.cli.JimakuDownloader", mock_downloader)
|
||||
|
||||
with patch(
|
||||
"sys.argv", ["jimaku-dl", "/path/to/video.mkv", "--log-level", "DEBUG"]
|
||||
):
|
||||
with patch("jimaku_dl.cli.ArgumentParser.parse_args") as mock_args:
|
||||
mock_args.return_value.media_path = "/path/to/video.mkv"
|
||||
mock_args.return_value.dest = None
|
||||
mock_args.return_value.play = False
|
||||
mock_args.return_value.api_token = "test_token"
|
||||
mock_args.return_value.log_level = "DEBUG"
|
||||
mock_args.return_value.anilist_id = None
|
||||
|
||||
result = main()
|
||||
|
||||
assert result == 0
|
||||
mock_downloader.assert_called_once_with(
|
||||
api_token="test_token", log_level="DEBUG"
|
||||
)
|
||||
|
||||
def test_version_arg(self, capsys, monkeypatch):
|
||||
"""Test CLI with version argument."""
|
||||
with patch("sys.argv", ["jimaku-dl", "--version"]):
|
||||
with pytest.raises(SystemExit) as excinfo:
|
||||
main()
|
||||
assert excinfo.value.code == 0
|
||||
|
||||
# Check that version is printed
|
||||
captured = capsys.readouterr()
|
||||
assert f"jimaku-dl {__version__}" in captured.out
|
||||
|
||||
def test_help_arg(self, capsys, monkeypatch):
|
||||
"""Test CLI with help argument."""
|
||||
with patch("sys.argv", ["jimaku-dl", "--help"]):
|
||||
with pytest.raises(SystemExit) as excinfo:
|
||||
main()
|
||||
assert excinfo.value.code == 0
|
||||
|
||||
# Help text is printed to stdout by argparse
|
||||
captured = capsys.readouterr()
|
||||
assert "usage:" in captured.out
|
||||
|
||||
def test_keyboard_interrupt(self, monkeypatch):
|
||||
"""Test handling of keyboard interrupt."""
|
||||
mock_downloader = MagicMock()
|
||||
mock_downloader.return_value.download_subtitles.side_effect = (
|
||||
KeyboardInterrupt()
|
||||
)
|
||||
monkeypatch.setattr("jimaku_dl.cli.JimakuDownloader", mock_downloader)
|
||||
|
||||
with patch("sys.argv", ["jimaku-dl", "/path/to/video.mkv"]):
|
||||
with patch("jimaku_dl.cli.ArgumentParser.parse_args") as mock_args:
|
||||
mock_args.return_value.media_path = "/path/to/video.mkv"
|
||||
mock_args.return_value.dest = None
|
||||
mock_args.return_value.play = False
|
||||
mock_args.return_value.api_token = "test_token"
|
||||
mock_args.return_value.log_level = "INFO"
|
||||
mock_args.return_value.anilist_id = None
|
||||
|
||||
with patch("builtins.print") as mock_print:
|
||||
result = main()
|
||||
|
||||
assert result == 1
|
||||
mock_print.assert_called_with("\nOperation cancelled by user.")
|
||||
|
||||
def test_short_options(self, monkeypatch):
|
||||
"""Test CLI with short options instead of long options."""
|
||||
mock_downloader = MagicMock()
|
||||
mock_downloader.return_value.download_subtitles.return_value = [
|
||||
"/path/to/subtitle.srt"
|
||||
]
|
||||
monkeypatch.setattr("jimaku_dl.cli.JimakuDownloader", mock_downloader)
|
||||
|
||||
with patch(
|
||||
"sys.argv",
|
||||
[
|
||||
"jimaku-dl",
|
||||
"/path/to/video.mkv",
|
||||
"-d",
|
||||
"/custom/path",
|
||||
"-p",
|
||||
"-t",
|
||||
"short_token",
|
||||
"-l",
|
||||
"DEBUG",
|
||||
"-a",
|
||||
"789",
|
||||
],
|
||||
):
|
||||
with patch("jimaku_dl.cli.ArgumentParser.parse_args") as mock_args:
|
||||
mock_args.return_value.media_path = "/path/to/video.mkv"
|
||||
mock_args.return_value.dest = "/custom/path"
|
||||
mock_args.return_value.play = True
|
||||
mock_args.return_value.api_token = "short_token"
|
||||
mock_args.return_value.log_level = "DEBUG"
|
||||
mock_args.return_value.anilist_id = 789
|
||||
|
||||
result = main()
|
||||
|
||||
assert result == 0
|
||||
mock_downloader.assert_called_once_with(
|
||||
api_token="short_token", log_level="DEBUG"
|
||||
)
|
||||
mock_downloader.return_value.download_subtitles.assert_called_once_with(
|
||||
media_path="/path/to/video.mkv",
|
||||
dest_dir="/custom/path",
|
||||
play=True,
|
||||
anilist_id=789,
|
||||
)
|
||||
1004
tests/test_downloader.py
Normal file
1004
tests/test_downloader.py
Normal file
File diff suppressed because it is too large
Load Diff
200
tests/test_filter_files_by_episode.py
Normal file
200
tests/test_filter_files_by_episode.py
Normal file
@@ -0,0 +1,200 @@
|
||||
"""Tests specifically for the filter_files_by_episode method."""
|
||||
|
||||
import pytest
|
||||
|
||||
from jimaku_dl.downloader import JimakuDownloader
|
||||
|
||||
|
||||
class TestFilterFilesByEpisode:
|
||||
"""Test suite for filter_files_by_episode method."""
|
||||
|
||||
def setup_method(self):
|
||||
"""Set up test method with a fresh downloader instance."""
|
||||
self.downloader = JimakuDownloader(api_token="test_token")
|
||||
|
||||
# Setup common test files
|
||||
self.all_files = [
|
||||
{"id": 1, "name": "Show - 01.srt"},
|
||||
{"id": 2, "name": "Show - 02.srt"},
|
||||
{"id": 3, "name": "Show - 03.srt"},
|
||||
{"id": 4, "name": "Show - E04.srt"},
|
||||
{"id": 5, "name": "Show Episode 05.srt"},
|
||||
{"id": 6, "name": "Show #06.srt"},
|
||||
{"id": 7, "name": "Show.S01E07.srt"},
|
||||
{"id": 8, "name": "Show - BATCH.srt"},
|
||||
{"id": 9, "name": "Show - Complete.srt"},
|
||||
{"id": 10, "name": "Show - All Episodes.srt"},
|
||||
]
|
||||
|
||||
def test_exact_episode_matches(self):
|
||||
"""Test finding exact episode matches with different filename patterns."""
|
||||
# Test standard episode format
|
||||
filtered = self.downloader.filter_files_by_episode(self.all_files, 1)
|
||||
assert len(filtered) == 4 # 1 specific match + 3 batch files
|
||||
assert filtered[0]["name"] == "Show - 01.srt" # Specific match should be first
|
||||
|
||||
# Test E## format
|
||||
filtered = self.downloader.filter_files_by_episode(self.all_files, 4)
|
||||
assert len(filtered) == 4 # 1 specific match + 3 batch files
|
||||
assert filtered[0]["name"] == "Show - E04.srt" # Specific match should be first
|
||||
|
||||
# Test 'Episode ##' format
|
||||
filtered = self.downloader.filter_files_by_episode(self.all_files, 5)
|
||||
assert len(filtered) == 4 # 1 specific match + 3 batch files
|
||||
assert (
|
||||
filtered[0]["name"] == "Show Episode 05.srt"
|
||||
) # Specific match should be first
|
||||
|
||||
# Test '#' format
|
||||
filtered = self.downloader.filter_files_by_episode(self.all_files, 6)
|
||||
assert len(filtered) == 4 # 1 specific match + 3 batch files
|
||||
assert filtered[0]["name"] == "Show #06.srt" # Specific match should be first
|
||||
|
||||
# Test S##E## format
|
||||
filtered = self.downloader.filter_files_by_episode(self.all_files, 7)
|
||||
assert len(filtered) == 4 # 1 specific match + 3 batch files
|
||||
assert (
|
||||
filtered[0]["name"] == "Show.S01E07.srt"
|
||||
) # Specific match should be first
|
||||
|
||||
def test_batch_files_inclusion(self):
|
||||
"""Test that batch files are always included but sorted after specific matches."""
|
||||
# For all episodes, batch files should be included now
|
||||
filtered = self.downloader.filter_files_by_episode(self.all_files, 1)
|
||||
assert len(filtered) == 4 # 1 specific + 3 batch
|
||||
assert any("BATCH" in f["name"] for f in filtered)
|
||||
assert any("Complete" in f["name"] for f in filtered)
|
||||
assert any("All Episodes" in f["name"] for f in filtered)
|
||||
|
||||
# Specific match should be first, followed by batch files
|
||||
assert filtered[0]["name"] == "Show - 01.srt"
|
||||
assert all(
|
||||
keyword in f["name"]
|
||||
for f, keyword in zip(filtered[1:], ["BATCH", "Complete", "All Episodes"])
|
||||
)
|
||||
|
||||
# Same for episode 3
|
||||
filtered = self.downloader.filter_files_by_episode(self.all_files, 3)
|
||||
assert len(filtered) == 4 # 1 specific + 3 batch
|
||||
assert filtered[0]["name"] == "Show - 03.srt"
|
||||
assert all(
|
||||
keyword in " ".join([f["name"] for f in filtered[1:]])
|
||||
for keyword in ["BATCH", "Complete", "All Episodes"]
|
||||
)
|
||||
|
||||
# For high episode numbers with no match, only batch files should be returned
|
||||
filtered = self.downloader.filter_files_by_episode(self.all_files, 10)
|
||||
assert len(filtered) == 3
|
||||
assert all(
|
||||
f["name"]
|
||||
in ["Show - BATCH.srt", "Show - Complete.srt", "Show - All Episodes.srt"]
|
||||
for f in filtered
|
||||
)
|
||||
|
||||
def test_no_episode_matches(self):
|
||||
"""Test behavior when no episodes match."""
|
||||
# For non-existent episodes, should return batch files
|
||||
filtered = self.downloader.filter_files_by_episode(self.all_files, 99)
|
||||
assert len(filtered) == 3
|
||||
assert all(
|
||||
f["name"]
|
||||
in ["Show - BATCH.srt", "Show - Complete.srt", "Show - All Episodes.srt"]
|
||||
for f in filtered
|
||||
)
|
||||
|
||||
# For a list with no batch files and no matches, should return all files
|
||||
no_batch_files = [
|
||||
f
|
||||
for f in self.all_files
|
||||
if not any(
|
||||
keyword in f["name"].lower()
|
||||
for keyword in ["batch", "complete", "all", "season"]
|
||||
)
|
||||
]
|
||||
filtered = self.downloader.filter_files_by_episode(no_batch_files, 99)
|
||||
assert filtered == no_batch_files
|
||||
|
||||
def test_ordering_of_results(self):
|
||||
"""Test that specific episode matches are always before batch files."""
|
||||
# Create a reversed test set to ensure sorting works
|
||||
reversed_files = list(reversed(self.all_files))
|
||||
|
||||
# Test with episode that has a specific match
|
||||
filtered = self.downloader.filter_files_by_episode(reversed_files, 4)
|
||||
|
||||
# Verify specific match is first
|
||||
assert filtered[0]["name"] == "Show - E04.srt"
|
||||
|
||||
# Verify batch files follow
|
||||
for f in filtered[1:]:
|
||||
assert any(
|
||||
keyword in f["name"].lower()
|
||||
for keyword in ["batch", "complete", "all episodes"]
|
||||
)
|
||||
|
||||
def test_edge_case_episode_formats(self):
|
||||
"""Test edge case episode number formats."""
|
||||
# Create test files with unusual formats
|
||||
edge_case_files = [
|
||||
{"id": 1, "name": "Show - ep.01.srt"}, # With period
|
||||
{"id": 2, "name": "Show - ep01v2.srt"}, # With version
|
||||
{"id": 3, "name": "Show - e.03.srt"}, # Abbreviated with period
|
||||
{"id": 4, "name": "Show - episode.04.srt"}, # Full word with period
|
||||
{"id": 5, "name": "Show - 05.v2.srt"}, # Version format
|
||||
{"id": 6, "name": "Show - [06].srt"}, # Bracketed number
|
||||
]
|
||||
|
||||
# Test detection of 01 in filenames
|
||||
filtered = self.downloader.filter_files_by_episode(edge_case_files, 1)
|
||||
# In the current implementation, these might all be included since regex matching is imperfect
|
||||
# So we just check that the correct ones are present and first
|
||||
assert any(f["name"] == "Show - ep.01.srt" for f in filtered)
|
||||
assert any(f["name"] == "Show - ep01v2.srt" for f in filtered)
|
||||
|
||||
# Test detection of episode.04
|
||||
filtered = self.downloader.filter_files_by_episode(edge_case_files, 4)
|
||||
assert any(f["name"] == "Show - episode.04.srt" for f in filtered)
|
||||
|
||||
# Test detection of [06]
|
||||
filtered = self.downloader.filter_files_by_episode(edge_case_files, 6)
|
||||
assert any(f["name"] == "Show - [06].srt" for f in filtered)
|
||||
|
||||
# Test episode that doesn't exist
|
||||
filtered = self.downloader.filter_files_by_episode(edge_case_files, 99)
|
||||
# Should return all files when no batch files and no matches
|
||||
assert len(filtered) == len(edge_case_files)
|
||||
|
||||
def test_duplicate_episode_matches(self):
|
||||
"""Test handling of duplicate episode matches in filenames."""
|
||||
# Files with multiple episode numbers in the name
|
||||
dup_files = [
|
||||
{"id": 1, "name": "Show - 01 - Episode 1.srt"}, # Same number twice
|
||||
{"id": 2, "name": "Show 02 - Ep02.srt"}, # Same number twice
|
||||
{"id": 3, "name": "Show - 03 - 04.srt"}, # Different numbers
|
||||
{"id": 4, "name": "Show - Ep05 Extra 06.srt"}, # Different numbers
|
||||
]
|
||||
|
||||
# Should match the first number for episode 1
|
||||
filtered = self.downloader.filter_files_by_episode(dup_files, 1)
|
||||
assert len(filtered) == 1
|
||||
assert filtered[0]["name"] == "Show - 01 - Episode 1.srt"
|
||||
|
||||
# Should match both formats for episode 2
|
||||
filtered = self.downloader.filter_files_by_episode(dup_files, 2)
|
||||
assert len(filtered) == 1
|
||||
assert filtered[0]["name"] == "Show 02 - Ep02.srt"
|
||||
|
||||
# Should match the first number for episode 3
|
||||
filtered = self.downloader.filter_files_by_episode(dup_files, 3)
|
||||
assert len(filtered) == 1
|
||||
assert filtered[0]["name"] == "Show - 03 - 04.srt"
|
||||
|
||||
# Should match the second number for episode 4
|
||||
filtered = self.downloader.filter_files_by_episode(dup_files, 4)
|
||||
assert len(filtered) == 1
|
||||
assert filtered[0]["name"] == "Show - 03 - 04.srt"
|
||||
|
||||
def test_empty_file_list(self):
|
||||
"""Test behavior with empty file list."""
|
||||
filtered = self.downloader.filter_files_by_episode([], 1)
|
||||
assert filtered == []
|
||||
126
tests/test_parse_directory_name.py
Normal file
126
tests/test_parse_directory_name.py
Normal file
@@ -0,0 +1,126 @@
|
||||
"""Tests specifically for the parse_directory_name method."""
|
||||
|
||||
import pytest
|
||||
|
||||
from jimaku_dl.downloader import JimakuDownloader
|
||||
|
||||
|
||||
class TestParseDirectoryName:
|
||||
"""Test suite for parse_directory_name method."""
|
||||
|
||||
def setup_method(self):
|
||||
"""Set up test method with a fresh downloader instance."""
|
||||
self.downloader = JimakuDownloader(api_token="test_token")
|
||||
|
||||
def test_basic_directory_names(self):
|
||||
"""Test basic directory name parsing."""
|
||||
# Standard name
|
||||
success, title, season, episode = self.downloader.parse_directory_name(
|
||||
"/path/to/My Anime Show"
|
||||
)
|
||||
assert success is True
|
||||
assert title == "My Anime Show"
|
||||
assert season == 1
|
||||
assert episode == 0
|
||||
|
||||
# Name with underscores
|
||||
success, title, season, episode = self.downloader.parse_directory_name(
|
||||
"/path/to/My_Anime_Show"
|
||||
)
|
||||
assert success is True
|
||||
assert title == "My Anime Show" # Underscores should be converted to spaces
|
||||
assert season == 1
|
||||
assert episode == 0
|
||||
|
||||
# Name with dots
|
||||
success, title, season, episode = self.downloader.parse_directory_name(
|
||||
"/path/to/My.Anime.Show"
|
||||
)
|
||||
assert success is True
|
||||
assert title == "My Anime Show" # Dots should be converted to spaces
|
||||
assert season == 1
|
||||
assert episode == 0
|
||||
|
||||
def test_common_system_directories(self):
|
||||
"""Test handling of common system directories that should be rejected."""
|
||||
# Common system directories
|
||||
for sys_dir in [
|
||||
"bin",
|
||||
"etc",
|
||||
"lib",
|
||||
"home",
|
||||
"usr",
|
||||
"var",
|
||||
"tmp",
|
||||
"opt",
|
||||
"media",
|
||||
"mnt",
|
||||
]:
|
||||
success, _, _, _ = self.downloader.parse_directory_name(
|
||||
f"/path/to/{sys_dir}"
|
||||
)
|
||||
assert success is False, f"Directory '{sys_dir}' should be rejected"
|
||||
|
||||
# Root directory
|
||||
success, _, _, _ = self.downloader.parse_directory_name("/")
|
||||
assert success is False
|
||||
|
||||
# Current directory
|
||||
success, _, _, _ = self.downloader.parse_directory_name(".")
|
||||
assert success is False
|
||||
|
||||
# Parent directory
|
||||
success, _, _, _ = self.downloader.parse_directory_name("..")
|
||||
assert success is False
|
||||
|
||||
def test_short_directory_names(self):
|
||||
"""Test handling of directory names that are too short."""
|
||||
# One-character name
|
||||
success, _, _, _ = self.downloader.parse_directory_name("/path/to/A")
|
||||
assert success is False
|
||||
|
||||
# Two-character name
|
||||
success, _, _, _ = self.downloader.parse_directory_name("/path/to/AB")
|
||||
assert success is False
|
||||
|
||||
# Three-character name (should be accepted)
|
||||
success, title, _, _ = self.downloader.parse_directory_name("/path/to/ABC")
|
||||
assert success is True
|
||||
assert title == "ABC"
|
||||
|
||||
def test_special_characters(self):
|
||||
"""Test directories with special characters."""
|
||||
# Directory with parentheses
|
||||
success, title, _, _ = self.downloader.parse_directory_name(
|
||||
"/path/to/My Anime (2022)"
|
||||
)
|
||||
assert success is True
|
||||
assert title == "My Anime (2022)"
|
||||
|
||||
# Directory with brackets
|
||||
success, title, _, _ = self.downloader.parse_directory_name(
|
||||
"/path/to/My Anime [Uncensored]"
|
||||
)
|
||||
assert success is True
|
||||
assert title == "My Anime [Uncensored]"
|
||||
|
||||
# Directory with other special characters
|
||||
success, title, _, _ = self.downloader.parse_directory_name(
|
||||
"/path/to/My Anime: The Movie - Part 2!"
|
||||
)
|
||||
assert success is True
|
||||
assert title == "My Anime: The Movie - Part 2!"
|
||||
|
||||
def test_directory_with_season_info(self):
|
||||
"""Test directories with season information."""
|
||||
# Directory with season in name
|
||||
success, title, _, _ = self.downloader.parse_directory_name(
|
||||
"/path/to/Anime Season 2"
|
||||
)
|
||||
assert success is True
|
||||
assert title == "Anime Season 2"
|
||||
|
||||
# Directory that only specifies season
|
||||
success, title, _, _ = self.downloader.parse_directory_name("/path/to/Season 3")
|
||||
assert success is True
|
||||
assert title == "Season 3"
|
||||
254
tests/test_parse_filename.py
Normal file
254
tests/test_parse_filename.py
Normal file
@@ -0,0 +1,254 @@
|
||||
"""Tests specifically for the parse_filename method."""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from jimaku_dl.downloader import JimakuDownloader
|
||||
|
||||
|
||||
class TestParseFilename:
|
||||
"""Test suite for parse_filename method."""
|
||||
|
||||
def setup_method(self):
|
||||
"""Set up test method with a fresh downloader instance."""
|
||||
self.downloader = JimakuDownloader(api_token="test_token")
|
||||
|
||||
def test_trash_guides_format(self):
|
||||
"""Test parsing filenames that follow Trash Guides naming convention."""
|
||||
# Basic Trash Guides format
|
||||
title, season, episode = self.downloader.parse_filename(
|
||||
"Show Title - S01E02 - Episode Name [1080p]"
|
||||
)
|
||||
assert title == "Show Title"
|
||||
assert season == 1
|
||||
assert episode == 2
|
||||
|
||||
# With year included
|
||||
title, season, episode = self.downloader.parse_filename(
|
||||
"Show Title (2020) - S03E04 - Episode Name [1080p]"
|
||||
)
|
||||
assert title == "Show Title"
|
||||
assert season == 3
|
||||
assert episode == 4
|
||||
|
||||
# More complex example
|
||||
title, season, episode = self.downloader.parse_filename(
|
||||
"My Favorite Anime (2023) - S02E05 - The Big Battle [1080p][10bit][h265][Dual-Audio]"
|
||||
)
|
||||
assert title == "My Favorite Anime"
|
||||
assert season == 2
|
||||
assert episode == 5
|
||||
|
||||
def test_standard_formats(self):
|
||||
"""Test parsing standard filename formats."""
|
||||
# S01E01 format
|
||||
title, season, episode = self.downloader.parse_filename(
|
||||
"Show.Name.S01E02.1080p.mkv"
|
||||
)
|
||||
assert title == "Show Name"
|
||||
assert season == 1
|
||||
assert episode == 2
|
||||
|
||||
# Separated by dots
|
||||
title, season, episode = self.downloader.parse_filename(
|
||||
"Show.Name.S03E04.x264.mkv"
|
||||
)
|
||||
assert title == "Show Name"
|
||||
assert season == 3
|
||||
assert episode == 4
|
||||
|
||||
# Separated by underscores
|
||||
title, season, episode = self.downloader.parse_filename(
|
||||
"Show_Name_S05E06_HEVC.mkv"
|
||||
)
|
||||
assert title == "Show Name"
|
||||
assert season == 5
|
||||
assert episode == 6
|
||||
|
||||
def test_directory_structure_extraction(self):
|
||||
"""Test extracting info from directory structure."""
|
||||
# Standard Season-## format
|
||||
title, season, episode = self.downloader.parse_filename(
|
||||
"/path/to/Show Name/Season-1/Show Name - 02 [1080p].mkv"
|
||||
)
|
||||
assert title == "Show Name"
|
||||
assert season == 1
|
||||
assert episode == 2
|
||||
|
||||
# Season ## format
|
||||
title, season, episode = self.downloader.parse_filename(
|
||||
"/path/to/Show Name/Season 03/Episode 4.mkv"
|
||||
)
|
||||
assert title == "Show Name"
|
||||
assert season == 3
|
||||
assert episode == 4
|
||||
|
||||
# Simple number in season directory
|
||||
title, season, episode = self.downloader.parse_filename(
|
||||
"/path/to/My Anime/Season 2/5.mkv"
|
||||
)
|
||||
assert title == "My Anime"
|
||||
assert season == 2
|
||||
assert episode == 5
|
||||
|
||||
# Long pathname with complex directory structure
|
||||
title, season, episode = self.downloader.parse_filename(
|
||||
"/media/user/Anime/Long Anime Title With Spaces/Season-1/Long Anime Title With Spaces - 03.mkv"
|
||||
)
|
||||
assert title == "Long Anime Title With Spaces"
|
||||
assert season == 1
|
||||
assert episode == 3
|
||||
|
||||
def test_complex_titles(self):
|
||||
"""Test parsing filenames with complex titles."""
|
||||
# Since we now prompt for non-standard formats, we need to mock the input
|
||||
with patch.object(self.downloader, "_prompt_for_title_info") as mock_prompt:
|
||||
# Set up the return values for the mock
|
||||
mock_prompt.return_value = (
|
||||
"Trapped in a Dating Sim - The World of Otome Games Is Tough for Mobs",
|
||||
1,
|
||||
11,
|
||||
)
|
||||
|
||||
title, season, episode = self.downloader.parse_filename(
|
||||
"Trapped in a Dating Sim - The World of Otome Games Is Tough for Mobs - S01E11.mkv"
|
||||
)
|
||||
assert (
|
||||
title
|
||||
== "Trapped in a Dating Sim - The World of Otome Games Is Tough for Mobs"
|
||||
)
|
||||
assert season == 1
|
||||
assert episode == 11
|
||||
|
||||
# Reset the mock for the next call
|
||||
mock_prompt.reset_mock()
|
||||
mock_prompt.return_value = ("Re:Zero kara Hajimeru Isekai Seikatsu", 1, 15)
|
||||
|
||||
# Titles with special characters and patterns
|
||||
title, season, episode = self.downloader.parse_filename(
|
||||
"Re:Zero kara Hajimeru Isekai Seikatsu S01E15 [1080p].mkv"
|
||||
)
|
||||
assert title == "Re:Zero kara Hajimeru Isekai Seikatsu"
|
||||
assert season == 1
|
||||
assert episode == 15
|
||||
|
||||
def test_fallback_title_extraction(self):
|
||||
"""Test fallback to user input for non-standard formats."""
|
||||
with patch.object(self.downloader, "_prompt_for_title_info") as mock_prompt:
|
||||
# Set up the mock to return specific values
|
||||
mock_prompt.return_value = ("My Show", 1, 5)
|
||||
|
||||
# With various tags
|
||||
title, season, episode = self.downloader.parse_filename(
|
||||
"My Show [1080p] [HEVC] [10bit] [Dual-Audio] - 05.mkv"
|
||||
)
|
||||
assert title == "My Show"
|
||||
assert season == 1
|
||||
assert episode == 5
|
||||
mock_prompt.assert_called_once()
|
||||
|
||||
# Reset mock for next test
|
||||
mock_prompt.reset_mock()
|
||||
mock_prompt.return_value = ("Great Anime", 1, 3)
|
||||
|
||||
# With episode at the end
|
||||
title, season, episode = self.downloader.parse_filename(
|
||||
"Great Anime 1080p BluRay x264 - 03.mkv"
|
||||
)
|
||||
assert title == "Great Anime"
|
||||
assert season == 1
|
||||
assert episode == 3
|
||||
mock_prompt.assert_called_once()
|
||||
|
||||
def test_unparsable_filenames(self):
|
||||
"""Test handling of filenames that can't be parsed."""
|
||||
with patch.object(self.downloader, "_prompt_for_title_info") as mock_prompt:
|
||||
mock_prompt.return_value = ("Manual Title", 2, 3)
|
||||
|
||||
title, season, episode = self.downloader.parse_filename("randomstring.mkv")
|
||||
assert title == "Manual Title"
|
||||
assert season == 2
|
||||
assert episode == 3
|
||||
mock_prompt.assert_called_once_with("randomstring.mkv")
|
||||
|
||||
# Test with completely random string
|
||||
mock_prompt.reset_mock()
|
||||
mock_prompt.return_value = ("Another Title", 4, 5)
|
||||
|
||||
title, season, episode = self.downloader.parse_filename("abc123xyz.mkv")
|
||||
assert title == "Another Title"
|
||||
assert season == 4
|
||||
assert episode == 5
|
||||
mock_prompt.assert_called_once_with("abc123xyz.mkv")
|
||||
|
||||
def test_unicode_filenames(self):
|
||||
"""Test parsing filenames with unicode characters."""
|
||||
# Testing with both Japanese title formats
|
||||
|
||||
# Standard format with Japanese title - parser can handle this without prompting
|
||||
title, season, episode = self.downloader.parse_filename(
|
||||
"この素晴らしい世界に祝福を! S01E03 [1080p].mkv"
|
||||
)
|
||||
assert title == "この素晴らしい世界に祝福を!"
|
||||
assert season == 1
|
||||
assert episode == 3
|
||||
|
||||
# For complex cases that might require prompting, use the mock
|
||||
with patch.object(self.downloader, "_prompt_for_title_info") as mock_prompt:
|
||||
# Mock the prompt for a case where the parser likely can't determine the structure
|
||||
mock_prompt.return_value = ("この素晴らしい世界に祝福を!", 2, 4)
|
||||
|
||||
# Non-standard format with Japanese title
|
||||
title, season, episode = self.downloader.parse_filename(
|
||||
"この素晴らしい世界に祝福を! #04 [BD 1080p].mkv"
|
||||
)
|
||||
|
||||
# Either the parser handles it or falls back to prompting
|
||||
# We're mainly checking that the result is correct
|
||||
assert title == "この素晴らしい世界に祝福を!"
|
||||
# Season might be detected as 1 from parser or 2 from mock
|
||||
# Episode might be detected as 4 from parser or from mock
|
||||
assert episode == 4
|
||||
|
||||
# We don't assert on whether mock_prompt was called since that
|
||||
# depends on implementation details of the parser
|
||||
|
||||
def test_unusual_formats(self):
|
||||
"""Test handling of unusual filename formats."""
|
||||
with patch.object(self.downloader, "_prompt_for_title_info") as mock_prompt:
|
||||
# Reset after each test to check if prompt was called
|
||||
mock_prompt.reset_mock()
|
||||
mock_prompt.return_value = ("Show Title", 2, 5)
|
||||
|
||||
# Double episode format
|
||||
title, season, episode = self.downloader.parse_filename(
|
||||
"Show.Title.S02E05E06.1080p.mkv"
|
||||
)
|
||||
# Should extract the first episode number
|
||||
assert title == "Show Title"
|
||||
assert season == 2
|
||||
assert episode == 5
|
||||
mock_prompt.assert_not_called()
|
||||
|
||||
# Episode with zero padding
|
||||
mock_prompt.reset_mock()
|
||||
title, season, episode = self.downloader.parse_filename(
|
||||
"Show Name - S03E009 - Episode Title.mkv"
|
||||
)
|
||||
assert title == "Show Name"
|
||||
assert season == 3
|
||||
assert episode == 9
|
||||
mock_prompt.assert_not_called()
|
||||
|
||||
# Episode with decimal point
|
||||
mock_prompt.reset_mock()
|
||||
mock_prompt.return_value = ("Show Name", 1, 5)
|
||||
title, season, episode = self.downloader.parse_filename(
|
||||
"Show Name - 5.5 - Special Episode.mkv"
|
||||
)
|
||||
# This will likely prompt due to unusual format
|
||||
assert title == "Show Name"
|
||||
assert season == 1
|
||||
assert episode == 5
|
||||
mock_prompt.assert_called_once()
|
||||
Reference in New Issue
Block a user