mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-07-09 22:43:44 +08:00
fix(subscribe): require complete coverage for full best versions (#5857)
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import app.chain.download as download_module
|
||||
from app.chain.download import DownloadChain
|
||||
from app.core.context import Context, MediaInfo, TorrentInfo
|
||||
from app.core.metainfo import MetaInfo
|
||||
from app.schemas import NotExistMediaInfo
|
||||
from app.schemas.types import MediaType
|
||||
|
||||
|
||||
@@ -95,3 +97,231 @@ def test_download_single_submits_download_added_to_background(monkeypatch):
|
||||
download_dir=Path("/downloads"),
|
||||
torrent_content=b"torrent-content",
|
||||
)
|
||||
|
||||
|
||||
class _FakeBatchTorrentHelper:
|
||||
"""
|
||||
为批量下载测试提供稳定排序和种子文件集数解析。
|
||||
"""
|
||||
|
||||
episodes = []
|
||||
|
||||
def sort_group_torrents(self, contexts):
|
||||
return contexts
|
||||
|
||||
def get_torrent_episodes(self, _files):
|
||||
return list(self.episodes)
|
||||
|
||||
|
||||
def _build_tv_context(episode_list=None):
|
||||
"""
|
||||
构造标题未显式标集数的单季电视剧候选。
|
||||
"""
|
||||
episodes = episode_list or []
|
||||
return SimpleNamespace(
|
||||
media_info=SimpleNamespace(type=MediaType.TV, tmdb_id=1, douban_id=None),
|
||||
meta_info=SimpleNamespace(
|
||||
season_list=[1],
|
||||
episode_list=episodes,
|
||||
title="Test Show",
|
||||
org_string="Test Show S01 2160p",
|
||||
set_episodes=lambda begin, end: None,
|
||||
),
|
||||
torrent_info=SimpleNamespace(title="Test Show S01 2160p", site_name="TestSite"),
|
||||
allowed_episodes=None,
|
||||
)
|
||||
|
||||
|
||||
def test_batch_download_rejects_complete_coverage_when_files_do_not_cover_target(monkeypatch):
|
||||
"""
|
||||
完整覆盖要求不能让 1-13 这种局部包冒充 1-143 的目标范围。
|
||||
"""
|
||||
_FakeBatchTorrentHelper.episodes = list(range(1, 14))
|
||||
monkeypatch.setattr(download_module, "TorrentHelper", _FakeBatchTorrentHelper)
|
||||
monkeypatch.setattr(download_module.eventmanager, "send_event", lambda *args, **kwargs: None)
|
||||
|
||||
chain = DownloadChain.__new__(DownloadChain)
|
||||
chain.download_torrent = MagicMock(return_value=(b"torrent-content", "", ["demo.mkv"]))
|
||||
chain.download_single = MagicMock(return_value="hash")
|
||||
|
||||
context = _build_tv_context()
|
||||
no_exists = {
|
||||
1: {
|
||||
1: NotExistMediaInfo(
|
||||
season=1,
|
||||
episodes=[],
|
||||
total_episode=143,
|
||||
start_episode=1,
|
||||
require_complete_coverage=True,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
downloads, lefts = chain.batch_download(contexts=[context], no_exists=no_exists)
|
||||
|
||||
assert downloads == []
|
||||
assert lefts == no_exists
|
||||
chain.download_single.assert_not_called()
|
||||
|
||||
|
||||
def test_batch_download_accepts_complete_coverage_when_files_cover_target_range(monkeypatch):
|
||||
"""
|
||||
自定义起始集场景按目标范围覆盖判断,100-143 可满足 start=100、total=143。
|
||||
"""
|
||||
_FakeBatchTorrentHelper.episodes = list(range(100, 144))
|
||||
monkeypatch.setattr(download_module, "TorrentHelper", _FakeBatchTorrentHelper)
|
||||
monkeypatch.setattr(download_module.eventmanager, "send_event", lambda *args, **kwargs: None)
|
||||
|
||||
chain = DownloadChain.__new__(DownloadChain)
|
||||
chain.download_torrent = MagicMock(return_value=(b"torrent-content", "", ["demo.mkv"]))
|
||||
chain.download_single = MagicMock(return_value="hash")
|
||||
|
||||
context = _build_tv_context()
|
||||
no_exists = {
|
||||
1: {
|
||||
1: NotExistMediaInfo(
|
||||
season=1,
|
||||
episodes=[],
|
||||
total_episode=143,
|
||||
start_episode=100,
|
||||
require_complete_coverage=True,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
downloads, lefts = chain.batch_download(contexts=[context], no_exists=no_exists)
|
||||
|
||||
assert downloads == [context]
|
||||
assert lefts == {}
|
||||
chain.download_single.assert_called_once()
|
||||
|
||||
|
||||
def test_batch_download_accepts_complete_coverage_when_title_episodes_cover_target(monkeypatch):
|
||||
"""
|
||||
显式标出完整范围的候选也可满足完整覆盖任务。
|
||||
"""
|
||||
_FakeBatchTorrentHelper.episodes = []
|
||||
monkeypatch.setattr(download_module, "TorrentHelper", _FakeBatchTorrentHelper)
|
||||
monkeypatch.setattr(download_module.eventmanager, "send_event", lambda *args, **kwargs: None)
|
||||
|
||||
chain = DownloadChain.__new__(DownloadChain)
|
||||
chain.download_torrent = MagicMock()
|
||||
chain.download_single = MagicMock(return_value="hash")
|
||||
|
||||
context = _build_tv_context(episode_list=list(range(1, 144)))
|
||||
no_exists = {
|
||||
1: {
|
||||
1: NotExistMediaInfo(
|
||||
season=1,
|
||||
episodes=[],
|
||||
total_episode=143,
|
||||
start_episode=1,
|
||||
require_complete_coverage=True,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
downloads, lefts = chain.batch_download(contexts=[context], no_exists=no_exists)
|
||||
|
||||
assert downloads == [context]
|
||||
assert lefts == {}
|
||||
chain.download_torrent.assert_not_called()
|
||||
chain.download_single.assert_called_once()
|
||||
|
||||
|
||||
def test_batch_download_rejects_complete_coverage_when_title_episodes_are_partial(monkeypatch):
|
||||
"""
|
||||
显式标出局部范围的候选不能满足完整覆盖任务。
|
||||
"""
|
||||
_FakeBatchTorrentHelper.episodes = []
|
||||
monkeypatch.setattr(download_module, "TorrentHelper", _FakeBatchTorrentHelper)
|
||||
monkeypatch.setattr(download_module.eventmanager, "send_event", lambda *args, **kwargs: None)
|
||||
|
||||
chain = DownloadChain.__new__(DownloadChain)
|
||||
chain.download_torrent = MagicMock()
|
||||
chain.download_single = MagicMock(return_value="hash")
|
||||
|
||||
context = _build_tv_context(episode_list=list(range(1, 14)))
|
||||
no_exists = {
|
||||
1: {
|
||||
1: NotExistMediaInfo(
|
||||
season=1,
|
||||
episodes=[],
|
||||
total_episode=143,
|
||||
start_episode=1,
|
||||
require_complete_coverage=True,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
downloads, lefts = chain.batch_download(contexts=[context], no_exists=no_exists)
|
||||
|
||||
assert downloads == []
|
||||
assert lefts == no_exists
|
||||
chain.download_torrent.assert_not_called()
|
||||
chain.download_single.assert_not_called()
|
||||
|
||||
|
||||
def test_batch_download_complete_coverage_ignores_allowed_episode_narrowing(monkeypatch):
|
||||
"""
|
||||
完整覆盖任务不能因候选允许集裁剪而把局部包误判为覆盖目标范围。
|
||||
"""
|
||||
_FakeBatchTorrentHelper.episodes = []
|
||||
monkeypatch.setattr(download_module, "TorrentHelper", _FakeBatchTorrentHelper)
|
||||
monkeypatch.setattr(download_module.eventmanager, "send_event", lambda *args, **kwargs: None)
|
||||
|
||||
chain = DownloadChain.__new__(DownloadChain)
|
||||
chain.download_torrent = MagicMock()
|
||||
chain.download_single = MagicMock(return_value="hash")
|
||||
|
||||
context = _build_tv_context(episode_list=[1, 2])
|
||||
context.allowed_episodes = {1, 2}
|
||||
no_exists = {
|
||||
1: {
|
||||
1: NotExistMediaInfo(
|
||||
season=1,
|
||||
episodes=[],
|
||||
total_episode=12,
|
||||
start_episode=1,
|
||||
require_complete_coverage=True,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
downloads, lefts = chain.batch_download(contexts=[context], no_exists=no_exists)
|
||||
|
||||
assert downloads == []
|
||||
assert lefts == no_exists
|
||||
chain.download_torrent.assert_not_called()
|
||||
chain.download_single.assert_not_called()
|
||||
|
||||
|
||||
def test_batch_download_keeps_count_check_without_complete_coverage(monkeypatch):
|
||||
"""
|
||||
普通整季缺失仍沿用数量判断,避免完整覆盖语义影响非严格场景。
|
||||
"""
|
||||
_FakeBatchTorrentHelper.episodes = list(range(2, 145))
|
||||
monkeypatch.setattr(download_module, "TorrentHelper", _FakeBatchTorrentHelper)
|
||||
monkeypatch.setattr(download_module.eventmanager, "send_event", lambda *args, **kwargs: None)
|
||||
|
||||
chain = DownloadChain.__new__(DownloadChain)
|
||||
chain.download_torrent = MagicMock(return_value=(b"torrent-content", "", ["demo.mkv"]))
|
||||
chain.download_single = MagicMock(return_value="hash")
|
||||
|
||||
context = _build_tv_context()
|
||||
no_exists = {
|
||||
1: {
|
||||
1: NotExistMediaInfo(
|
||||
season=1,
|
||||
episodes=[],
|
||||
total_episode=143,
|
||||
start_episode=1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
downloads, lefts = chain.batch_download(contexts=[context], no_exists=no_exists)
|
||||
|
||||
assert downloads == [context]
|
||||
assert lefts == {}
|
||||
chain.download_single.assert_called_once()
|
||||
|
||||
Reference in New Issue
Block a user