fix: align AniList and TheAudioDB network probes

This commit is contained in:
jxxghp
2026-09-27 15:57:10 +08:00
parent f0cfaa8e3a
commit cf13f2e114
2 changed files with 101 additions and 5 deletions

View File

@@ -8,6 +8,8 @@ from app.foundation.url import UrlUtils
from .domain import NetworkTestRule, SettingsReader
_DEFAULT_THEAUDIODB_API_KEY = "123"
def _configured_service_rule(
*,
@@ -90,7 +92,7 @@ def build_network_rules(
rules.extend(_build_metadata_module_rules(settings))
rules.extend(_build_notification_module_rules())
rules.extend(_build_external_media_rules(settings))
rules.extend(_build_music_service_rules())
rules.extend(_build_music_service_rules(settings))
rules.extend(_build_configured_module_rules(settings))
rules.extend(_build_fixed_service_rules(settings, github_proxy))
if enabled_module_ids is None:
@@ -325,10 +327,16 @@ def _build_external_media_rules(settings: SettingsReader) -> list[NetworkTestRul
id="anilist_api",
name="AniList 番剧元数据",
icon="anilist",
url=("https://graphql.anilist.co/?query=%7B%20Media%28id%3A1%29%20%7Bid%7D%20%7D"),
url="https://graphql.anilist.co",
proxy=True,
allowed_redirect_prefixes=("https://graphql.anilist.co/",),
module_ids=("AniListModule",),
http_method="POST",
request_json=(("query", "{ Media(id: 1) { id } }"),),
headers=(
("Accept", "application/json"),
("Content-Type", "application/json"),
),
),
NetworkTestRule(
id="anilist_trace",
@@ -367,8 +375,8 @@ def _build_external_media_rules(settings: SettingsReader) -> list[NetworkTestRul
]
def _build_music_service_rules() -> list[NetworkTestRule]:
"""构建 MusicBrainz、ListenBrainz 和 TheAudioDB 服务目标。"""
def _build_music_service_rules(settings: SettingsReader) -> list[NetworkTestRule]:
"""构建音乐服务目标,并让 TheAudioDB 使用模块当前配置的 API Key。"""
return [
NetworkTestRule(
id="musicbrainz_api",
@@ -392,7 +400,11 @@ def _build_music_service_rules() -> list[NetworkTestRule]:
id="theaudiodb_api",
name="TheAudioDB 音乐信息",
icon="theaudiodb",
url=("https://www.theaudiodb.com/api/v1/json/123/search.php?s=coldplay"),
url=(
"https://www.theaudiodb.com/api/v1/json/"
f"{settings('THEAUDIODB_API_KEY', _DEFAULT_THEAUDIODB_API_KEY)}"
"/search.php?s=coldplay"
),
proxy=True,
allowed_redirect_prefixes=("https://www.theaudiodb.com/",),
module_ids=("TheAudioDbModule",),

View File

@@ -0,0 +1,84 @@
"""验证内置网络探针与对应模块请求契约保持一致。"""
import asyncio
from types import SimpleNamespace
from unittest.mock import AsyncMock, Mock
from app.application.network import NetworkTestService
def _network_test_service(transport, **settings) -> NetworkTestService:
"""创建读取独立设置快照的网络探测服务。"""
values = {"NORMAL_USER_AGENT": "MoviePilot-Test", **settings}
return NetworkTestService(
transport=transport,
settings=lambda key, default=None: values.get(key, default),
logger=Mock(),
)
def test_anilist_probe_uses_post_graphql_request() -> None:
"""AniList 官方 GraphQL 探针应使用模块相同的 POST JSON 协议。"""
captured = {}
response = SimpleNamespace(
status_code=200,
headers={},
text='{"data":{"Media":{"id":1}}}',
aclose=AsyncMock(),
)
async def respond(method, url, **options):
"""记录 AniList 探测请求并返回成功响应。"""
captured.update(method=method, url=url, **options)
return response
transport = SimpleNamespace(request=AsyncMock(side_effect=respond))
result = asyncio.run(
_network_test_service(transport).execute(target_id="anilist_api")
)
assert result.success
assert captured["method"] == "POST"
assert captured["url"] == "https://graphql.anilist.co"
assert captured["json_body"] == {"query": "{ Media(id: 1) { id } }"}
assert captured["headers"] == {
"Accept": "application/json",
"Content-Type": "application/json",
}
def test_theaudiodb_probe_uses_configured_key_and_module_health_endpoint() -> None:
"""TheAudioDB 探针应使用模块配置的 Key 和相同的艺术家搜索接口。"""
configured_key = "custom-audio-key"
captured = {}
response = SimpleNamespace(
status_code=200,
headers={},
text='{"artists":[]}',
aclose=AsyncMock(),
)
async def respond(method, url, **options):
"""记录 TheAudioDB 探测请求并返回成功响应。"""
captured.update(method=method, url=url, **options)
return response
transport = SimpleNamespace(request=AsyncMock(side_effect=respond))
service = _network_test_service(
transport,
THEAUDIODB_API_KEY=configured_key,
)
public_target = next(
target for target in service.list_targets() if target.id == "theaudiodb_api"
)
result = asyncio.run(service.execute(target_id="theaudiodb_api"))
assert result.success
assert public_target.address == "https://www.theaudiodb.com"
assert configured_key not in public_target.address
assert captured["method"] == "GET"
assert captured["url"] == (
"https://www.theaudiodb.com/api/v1/json/"
"custom-audio-key/search.php?s=coldplay"
)