This repository has been archived on 2026-10-02. You can view files and clone it. You cannot open issues or pull requests or push a commit.
snowy-yang f27bd7dd74 refactor: /song 端点拆解(99 行 / 圈 18 → 主函数 27 行 / 圈 8)
为什么:song 是端点层最重的函数——参数校验(4 条)、账号 Cookie 选择、四种 type
分发、json 分支的三源合并与降级全塞在一处;而四种 type 的返回体形状与降级口径
**没有任何测试记录**(golden 里的 /song 只有一条参数校验用例 song_badid)。

改法:
- _parse_song_query:提取 + 校验,返回 _SongQuery 或**错误消息字符串**。返回
  字符串而非响应对象,是为了让它不依赖响应层、可单独断言(校验段 4 条错误都是
  默认 400,无需状态码)。
- _SongQuery:frozen dataclass。music_id 保持 extract_id 的**字符串原值**——它
  原样进响应体 id 字段、golden 按字符串比对;上游要的 int 走 .mid property。
- _song_url_response / _song_json_response:两个实体分支各自成函数。
- song 主函数只剩「校验 → 选 Cookie → 四路分发」。

刻意保留的语义(是决定,不是疏漏):
- 解析日志**只在 json 分支**记录,url/name/lyric 三路不记——历史实现如此。
  已写进 _song_json_response 的 docstring:不要为了「统一」把它上提到主函数,
  那会新增三路的可观察副作用。
- 校验顺序(缺参 → ID 非数字 → 音质 → 类型)原样保留,并用测试钉住「同时传错
  时报哪条」。顺序是用户可见行为。
- 三条 404 降级(上游 data 为空 / data=[null] / songs 为空)逐条保留。

复杂度(同口径 AST 实测,重构前后):
  song                 99 行 / 圈 18 / 嵌套 4  →  27 行 / 圈 8 / 嵌套 2
  _song_json_response   新增                  →  49 行 / 圈 6 / 嵌套 1
  _parse_song_query     新增                  →  21 行 / 圈 5 / 嵌套 1
  文件内 >80 行函数 1 → 0;最大圈 18 → 11(余下最大的是本次未动的 mask_server_nickname)
  注:上一轮口头报的「圈 44」与本轮口径不同,此处以本轮统一口径为准,重测为准绳。

等价性证据(三道,互相独立):
1. AST 字面量审计:字符串字面量 **227 处完全一致**;数值唯一差异是新增的
   dataclass frozen=True。即没有一条用户可见文案或阈值被改动。
2. 反向注入验证:把 HEAD 的旧 song 换回去,用同一套 tests/test_song_types.py 跑
   → 19 例**全过**,证明这批测试忠实编码了旧行为、对新实现构成等价性验证。
3. 新增 19 例行为契约测试:四种 type 正路、json 的三源合并与三种降级(歌词失败
   不阻断 / 直链失败置空 / 详情失败 404)、校验优先级、分享链接解析、
   account=user 无 Cookie 401、全局 Cookie 冒用拦截。

顺带修掉一个测试自身缺陷:新用例里「个人 Cookie 与全局账号一致」一条**单跑通过、
全套失败**——根因是它依赖 GLOBAL_MUSIC_U_CACHE(5 分钟粘性的模块级缓存),别的
文件先跑会把值填上,于是拿到缓存值而非本处 patch 的 Cookie。已改为 fixture 里
用 monkeypatch.setitem 显式重置该缓存,让用例自足。

验证:ruff 全过 · pyright 0/0 · pytest 233 passed(214→233)· 生产 data/ 未被
触碰(全部用例走 tmp_path 沙箱)。
2026-09-27 07:41:16 +08:00
S
Description
云村小助手 · 网易云音乐解析/下载服务(FastAPI + asyncio + aiohttp)
1.5 MiB
0 Stars 1 Watchers 0 Forks
Languages
Python 61%
TypeScript 32.2%
CSS 6.6%
Dockerfile 0.1%