from types import SimpleNamespace from unittest.mock import patch import pytest from app.modules.emby.emby import Emby from app.modules.jellyfin.jellyfin import Jellyfin from app.modules.plex.plex import Plex from app.modules.trimemedia.api import Api as TrimeMediaApi from app.modules.trimemedia.api import Type as TrimeMediaType from app.modules.ugreen.ugreen import Ugreen from app.modules.zspace.zspace import ZSpace 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 @pytest.mark.parametrize( ("client_class", "request_utils_path", "url_path"), [ (Emby, "app.modules.emby.emby.RequestUtils", "emby/Users/user-id/Items"), (Jellyfin, "app.modules.jellyfin.jellyfin.RequestUtils", "Users/user-id/Items"), ], ) def test_emby_compatible_items_count_uses_recursive_type_filter( client_class, request_utils_path, url_path ): """Emby 兼容服务应通过递归类型过滤查询媒体库总数。""" client = client_class.__new__(client_class) client._host = "http://media.local/" client._apikey = "token" client.user = "user-id" with patch(request_utils_path) as request_utils_cls: request_utils_cls.return_value.get_res.return_value = _FakeResponse( {"TotalRecordCount": 23} ) result = client.get_items_count("library-id") assert result == 23 args = request_utils_cls.return_value.get_res.call_args.args assert args[0] == f"http://media.local/{url_path}" assert args[1]["Recursive"] == "true" assert args[1]["IncludeItemTypes"] == "Movie,Series" assert args[1]["Limit"] == 0 def test_zspace_items_count_uses_total_record_count(): """极影视应使用兼容接口返回的 TotalRecordCount。""" client = ZSpace.__new__(ZSpace) client._host = "http://zspace.local/" client._apikey = "token" client.user = "user-id" with patch("app.modules.zspace.zspace.RequestUtils") as request_utils_cls: request_utils_cls.return_value.get_res.return_value = _FakeResponse( {"TotalRecordCount": 17} ) result = client.get_items_count("library-id") assert result == 17 params = request_utils_cls.return_value.get_res.call_args.kwargs["params"] assert params == { "ParentId": "library-id", "Recursive": "true", "Limit": 0, } def test_plex_items_count_uses_section_total_size(): """Plex 应直接读取媒体库分区的条目总数。""" client = Plex.__new__(Plex) section = SimpleNamespace(totalSize=31) client._plex = SimpleNamespace( library=SimpleNamespace(sectionByID=lambda _library_id: section) ) assert client.get_items_count("9") == 31 def test_ugreen_items_count_uses_library_video_count(): """绿联影视应复用媒体库列表中的视频总数。""" client = Ugreen.__new__(Ugreen) client._host = "http://ugreen.local" client._username = "tester" client._password = "secret" client._userinfo = {"name": "tester"} client._api = SimpleNamespace(token="token") client._libraries = {"library-id": {"video_count": 42}} assert client.get_items_count("library-id") == 42 def test_trimemedia_item_count_reads_list_total(): """飞牛影视应通过媒体列表接口的 total 字段获取总数。""" api = TrimeMediaApi.__new__(TrimeMediaApi) response = SimpleNamespace(success=True, data={"list": [], "total": 19}) with patch.object(TrimeMediaApi, "request", return_value=response) as request: result = api.item_count( guid="library-id", types=[TrimeMediaType.MOVIE, TrimeMediaType.TV], ) assert result == 19 request.assert_called_once_with( "/item/list", data={ "ancestor_guid": "library-id", "tags": {"type": [TrimeMediaType.MOVIE, TrimeMediaType.TV]}, "exclude_grouped_video": 1, "page": 1, "page_size": 1, }, )