Files
MoviePilot/tests/test_jellyfin_medias_count.py

127 lines
4.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
from unittest.mock import patch
from app.modules.jellyfin.jellyfin import Jellyfin
class _FakeResponse:
"""模拟媒体服务器 HTTP 响应。"""
def __init__(self, payload: dict, status_code: int = 200):
"""保存响应数据和状态码。"""
self._payload = payload
self.status_code = status_code
def json(self) -> dict:
"""返回模拟的 JSON 数据。"""
return self._payload
def _make_client(user: str = "user-id") -> Jellyfin:
"""构造跳过初始化的 Jellyfin 客户端。"""
client = Jellyfin.__new__(Jellyfin)
client._host = "http://media.local/"
client._apikey = "token"
client.user = user
client._sync_libraries = []
return client
def _routed_get_res(views: dict, counts: dict, global_counts: dict = None):
"""按 URL 分发的 get_res 模拟:视图列表、单库统计与全局统计。"""
def _get_res(url, params=None, **_kwargs):
if url.endswith("/Views"):
return _FakeResponse(views)
if url.endswith("/Items") and params:
key = (params.get("ParentId"), params.get("IncludeItemTypes"))
return _FakeResponse({"TotalRecordCount": counts.get(key, 0)})
if url.endswith("Items/Counts"):
return _FakeResponse(global_counts or {})
raise AssertionError(f"意外的请求地址:{url}")
return _get_res
def test_medias_count_deduplicates_multi_folder_library():
"""多文件夹电影库应按用户视图统计,避免版本重复计数(#5915"""
client = _make_client()
views = {"Items": [{"Id": "lib-movie", "CollectionType": "movies"}]}
# 用户级查询会折叠同一影片的多个版本,返回 67 而非数据库原始行数 201
counts = {("lib-movie", "Movie"): 67}
with patch("app.modules.jellyfin.jellyfin.RequestUtils") as request_utils_cls:
request_utils_cls.return_value.get_res.side_effect = _routed_get_res(
views, counts, global_counts={"MovieCount": 201}
)
stat = client.get_medias_count()
assert stat.movie_count == 67
assert stat.tv_count == 0
assert stat.episode_count == 0
def test_medias_count_buckets_by_collection_type():
"""电影与剧集库应按视图类型分桶累计,未知类型库不参与统计。"""
client = _make_client()
views = {
"Items": [
{"Id": "lib-movie", "CollectionType": "movies"},
{"Id": "lib-tv", "CollectionType": "tvshows"},
{"Id": "lib-music", "CollectionType": "music"},
{"Id": None, "CollectionType": "movies"},
]
}
counts = {
("lib-movie", "Movie"): 12,
("lib-tv", "Series"): 3,
("lib-tv", "Episode"): 45,
}
with patch("app.modules.jellyfin.jellyfin.RequestUtils") as request_utils_cls:
request_utils_cls.return_value.get_res.side_effect = _routed_get_res(
views, counts
)
stat = client.get_medias_count()
assert stat.movie_count == 12
assert stat.tv_count == 3
assert stat.episode_count == 45
def test_medias_count_falls_back_without_user():
"""无可用用户时应回退到全局 Items/Counts 统计。"""
client = _make_client(user=None)
with patch("app.modules.jellyfin.jellyfin.RequestUtils") as request_utils_cls:
request_utils_cls.return_value.get_res.return_value = _FakeResponse(
{"MovieCount": 5, "SeriesCount": 2, "EpisodeCount": 30}
)
stat = client.get_medias_count()
assert stat.movie_count == 5
assert stat.tv_count == 2
assert stat.episode_count == 30
args = request_utils_cls.return_value.get_res.call_args.args
assert args[0] == "http://media.local/Items/Counts"
def test_medias_count_falls_back_when_views_unavailable():
"""媒体库视图查询失败时应回退到全局 Items/Counts 统计。"""
client = _make_client()
def _get_res(url, params=None, **_kwargs):
if url.endswith("/Views"):
return None
if url.endswith("Items/Counts"):
return _FakeResponse({"MovieCount": 7, "SeriesCount": 1, "EpisodeCount": 9})
raise AssertionError(f"意外的请求地址:{url}")
with patch("app.modules.jellyfin.jellyfin.RequestUtils") as request_utils_cls:
request_utils_cls.return_value.get_res.side_effect = _get_res
stat = client.get_medias_count()
assert stat.movie_count == 7
assert stat.tv_count == 1
assert stat.episode_count == 9