fix(site): skip search parameters when listing site categories

- 站点分类接口只收集分类配置中的列表分组和字典项;field、delimiter、
  param 等搜索请求参数不再被当成分类逐字符返回,避免响应校验失败导致
  站点分类请求 500。
- 补充带搜索参数和非法分类项的回归测试,并校验结果符合接口响应模型。

Refs #7250

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
LIN student
2026-10-31 16:02:12 +08:00
parent 142f41cd3d
commit 4dfcac8d04
2 changed files with 31 additions and 5 deletions

View File

@@ -2,7 +2,7 @@
提供站点配置、认证、数据查询和状态管理接口。
"""
from typing import Annotated, Any, List, Literal, Optional, cast
from typing import Annotated, Any, List, Literal, Optional
from fastapi import Depends, HTTPException, Query, Response
@@ -691,11 +691,13 @@ async def site_category(
category_value = indexer.get("category")
if not isinstance(category_value, dict):
return []
category = cast(dict[str, list[dict[str, Any]]], category_value)
result = []
for cats in category.values():
# field、delimiter、param 等是搜索请求参数,只有列表值才是分类项
result: list[dict[str, Any]] = []
for cats in category_value.values():
if not isinstance(cats, list):
continue
for cat in cats:
if cat not in result:
if isinstance(cat, dict) and cat not in result:
result.append(cat)
return result

View File

@@ -8,12 +8,14 @@ from unittest.mock import AsyncMock, Mock
import pytest
from fastapi import HTTPException
from pydantic import TypeAdapter
from app.api.endpoints import bangumi, download, history, mediaserver, message, search, site, tmdb, transfer
from app.domain.context import MediaInfo
from app.schemas.context import MediaInfo as MediaInfoInput
from app.schemas.context import SubtitleInfo
from app.schemas.file import FileItem
from app.schemas.site import SiteCategory
from app.schemas.transfer import ManualTransferItem
from app.schemas.types import MediaType
@@ -124,6 +126,28 @@ def test_site_category_only_projects_category_mappings(category, monkeypatch):
assert result == ([{"id": 1}, {"id": 2}] if isinstance(category, dict) else [])
def test_site_category_skips_search_parameter_settings(monkeypatch):
"""
分类配置中的 field、delimiter、param 等搜索参数不能被当成分类项逐字符返回。
"""
category = {
"field": "cat[]",
"delimiter": "&",
"param": "cat",
"movie": [{"id": 401, "cat": "Movies", "desc": "电影"}, "invalid"],
"tv": [{"id": 402, "cat": "TV", "desc": "剧集"}],
}
query = SimpleNamespace(get=AsyncMock(return_value=SimpleNamespace(domain="https://example.test")))
helper = SimpleNamespace(async_get_indexer=AsyncMock(return_value={"category": category}))
monkeypatch.setattr(site, "SitesHelper", Mock(return_value=helper))
result = asyncio.run(site.site_category(1, query=query, _=None))
assert result == [{"id": 401, "cat": "Movies", "desc": "电影"}, {"id": 402, "cat": "TV", "desc": "剧集"}]
assert TypeAdapter(list[SiteCategory]).validate_python(result) == [
SiteCategory(id=401, cat="Movies", desc="电影"),
SiteCategory(id=402, cat="TV", desc="剧集"),
]
@pytest.mark.parametrize("batch", [False, True])
@pytest.mark.parametrize("moved", [False, True])
def test_manual_transfer_history_without_file_snapshot_returns_business_error(batch, moved):