"""Navidrome 媒体服务器模块接入测试。""" from unittest.mock import Mock from app import schemas from app.runtime.extensions.module_manager import ModuleManager from app.domain.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_discovered_without_unconfigured_activation(): """Navidrome 始终可发现,但没有启用配置时不应创建服务资源。""" manager = ModuleManager() assert "NavidromeModule" in manager.get_module_ids() assert manager.get_running_module("NavidromeModule") is None def test_navidrome_module_ignores_non_music_media(): """Navidrome 只管理音乐,影视存在性检查应交给其它媒体服务器。""" from app.domain.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( media_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"