"""Navidrome 媒体服务器模块接入测试。""" from unittest.mock import Mock from app import schemas from app.core.module import ModuleManager from app.core.context import MusicInfo from app.modules.navidrome import NavidromeModule from app.modules.navidrome.navidrome import Navidrome from app.schemas.types import MediaServerType, ModuleType def test_navidrome_module_declares_media_server_identity(): """Navidrome 应以媒体服务器身份注册,供统一媒体服务器链调用。""" assert NavidromeModule.get_name() == "Navidrome" assert NavidromeModule.get_type() == ModuleType.MediaServer assert NavidromeModule.get_subtype() == MediaServerType.Navidrome def test_navidrome_module_has_no_system_switch(): """Navidrome 由服务配置控制启用,不能返回无效的系统开关名。""" assert NavidromeModule().init_setting() is None def test_navidrome_module_is_loaded_by_module_manager(): """模块管理器应能加载 Navidrome,否则媒体服务器列表里不会出现该类型。""" assert "NavidromeModule" in ModuleManager()._running_modules def test_navidrome_module_ignores_non_music_media(): """Navidrome 只管理音乐,影视存在性检查应交给其它媒体服务器。""" from app.core.context import MediaInfo from app.schemas.types import MediaType mediainfo = MediaInfo() mediainfo.type = MediaType.MOVIE assert NavidromeModule().media_exists(mediainfo) is None def test_navidrome_refresh_requests_incremental_scan(monkeypatch): """音乐入库完成后应通过 Subsonic startScan 触发 Navidrome 增量扫描。""" client = object.__new__(Navidrome) requested = [] def fake_call(method, **kwargs): """记录媒体库刷新使用的 Subsonic 方法和参数。""" requested.append((method, kwargs)) return {"status": "ok"} monkeypatch.setattr(client, "_call", fake_call) assert client.refresh_root_library() is True assert requested == [("startScan", {"fullScan": False})] def test_navidrome_search_filters_exact_album_and_song(monkeypatch): """Navidrome 模糊搜索结果必须按实体名称和艺术家精确过滤。""" client = object.__new__(Navidrome) monkeypatch.setattr( client, "_call", lambda *_args, **_kwargs: { "searchResult3": { "album": [ {"id": "wrong", "name": "叶惠美 演唱会", "artist": "周杰伦", "songCount": 12}, {"id": "album-1", "name": "叶惠美", "artist": "周杰伦", "songCount": 11}, ], "song": [ {"id": "song-wrong", "title": "晴天 Live", "artist": "周杰伦"}, {"id": "song-1", "title": "晴天", "artist": "周杰伦"}, ], } }, ) albums = client.search_music(album="叶惠美", artist="周杰伦") songs = client.search_music(title="晴天", artist="周杰伦") assert [item.item_id for item in albums] == ["album-1"] assert albums[0].note["song_count"] == 11 assert [item.item_id for item in songs] == ["song-1"] assert songs[0].title == "晴天" def test_navidrome_now_playing_uses_song_title_instead_of_album(monkeypatch): """正在播放接口返回单曲时,仪表盘标题必须显示曲名而不是所属专辑名。""" client = object.__new__(Navidrome) client._play_host = "https://music.example.com" monkeypatch.setattr( client, "_call", lambda *_args, **_kwargs: { "nowPlaying": { "entry": [ { "id": "song-1", "title": "晴天", "album": "叶惠美", "artist": "周杰伦", "coverArt": "cover-1", } ] } }, ) monkeypatch.setattr(client, "_album_cover", lambda _item: "cover-url") items = client.get_resume() assert len(items) == 1 assert items[0].item_id == "song-1" assert items[0].title == "晴天" assert items[0].subtitle == "周杰伦" assert items[0].image == "cover-url" def test_navidrome_album_exists_requires_complete_track_count(monkeypatch): """同名专辑曲目不足时不得把整专订阅判定为已完整入库。""" module = NavidromeModule() service = Mock() service.get_iteminfo.return_value = schemas.MediaServerItem( item_id="album-1", title="叶惠美", item_type="音乐", note={"artist": "周杰伦", "song_count": 10}, ) service.search_music.return_value = [] monkeypatch.setattr(module, "get_instances", lambda: {"music": service}) album = MusicInfo( source="musicbrainz", media_id="release-group-1", music_type="album", title="叶惠美", artists=["周杰伦"], total_tracks=11, ) assert module.media_exists(album, itemid="album-1") is None service.get_iteminfo.return_value.note["song_count"] = 11 exists = module.media_exists(album, itemid="album-1") assert exists is not None assert exists.itemid == "album-1"