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.
Files
NeteaseUrl/tests/test_session.py
T
snowy-yang 8446166120 refactor!: uv 管理环境 + 彻底删除技术债(Flask 与 requests 全部移除)
## uv 环境管理
- pyproject.toml 声明 dependencies + [dependency-groups].dev,uv.lock 精确锁定
- 删除 requirements.txt / requirements-fastapi.txt / requirements-dev.txt
- .python-version = 3.12;Dockerfile 改用 uv sync --frozen(可复现构建)
- 新增 jinja2(首页渲染)

## 删除 Flask 技术债
- 从 main.py 用 ast 精确剥离 56 个 Flask 节点(路由/中间件/错误处理/启动),
  框架无关的服务层落为 services/runtime.py(MusicAPIService、下载/打包 worker、
  任务监控、缓存清扫),零 Flask 依赖
- 删除 main.py(4527 行)与 auth_routes.py(1050 行)
- auth_system.py 移除会话/请求上下文层(login_session/current_user/login_required/
  admin_required/_client_ip 等),保留数据与业务层
- 新增 core/net.py(IP 解析)、web/auth_state.py(登录锁定/2FA 票据/发码限流)
- FastAPI 侧改直接依赖 services.runtime,不再 import main
- web/app.py 去掉 Flask fallback;入口默认根路径模式(API_PREFIX 可选)

## 删除 requests 技术债
- 新增 core/sync_http.py:requests 风格的同步门面(底层 aiohttp 连接池,
  流式读取经有界队列背压),含 Session/headers/(connect,read) 元组超时兼容
- core/utils、auth_system、podcast_api、music_downloader、services 全部切换
- 实测真实下载 5.2MB 音频完整成功

## 质量门禁归零
- ruff:0 错误(B904 全部修复,不再忽略)
- pyright:0 错误(类型收窄修复)
- pytest:140/140(含 golden 契约回归、root 模式 66 项路径覆盖)
- 真实 uvicorn 启动:/health、/、/static、受保护端鉴权全部正确

新增 tests/golden/responses.json:删除 Flask 前捕获的 32 例响应快照,
FastAPI 逐字段复现——删除巨石后行为回归仍有硬证据。
2026-09-26 11:10:17 +08:00

76 lines
2.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""Flask 兼容会话编解码的单元测试。
验证 core.session 与 itsdangerous(Flask 会话所用库)**双向互通**:
Flask 时代签发的会话 Cookie 必须仍能被本站解析(用户不必重新登录)。
Flask 本身已从依赖中移除,这里直接用 itsdangerous 构造/校验
(Flask 会话即 itsdangerous URLSafeTimedSerializer:盐 cookie-session、HMAC-SHA1)。
"""
import hashlib
import time
import pytest
from itsdangerous import URLSafeTimedSerializer
from core import session as cs
SECRET = 'unit-test-secret-key'
def _flask_serializer():
"""等价于 Flask 的会话序列化器(同盐、同摘要算法、同 key 派生)。"""
return URLSafeTimedSerializer(
SECRET, salt='cookie-session',
signer_kwargs={'key_derivation': 'hmac', 'digest_method': hashlib.sha1})
def test_itsdangerous_to_core():
"""itsdangerous(Flask)签发的会话 Cookie 能被 core.session 解出。"""
cookie = _flask_serializer().dumps({'site_uid': 26, 'site_uid_seq': 5})
data = cs.loads(SECRET, cookie)
assert data == {'site_uid': 26, 'site_uid_seq': 5}
assert cs.get_uid(data) == 26
def test_core_to_itsdangerous():
"""core.session 签发的 Cookie 能被 itsdangerous 解出。"""
cookie = cs.dumps(SECRET, {'site_uid': 74, 'site_uid_seq': 8})
assert _flask_serializer().loads(cookie) == {'site_uid': 74, 'site_uid_seq': 8}
def test_core_timestamp_valid_for_timed_check():
"""时间戳编码与 itsdangerous 一致:max_age 校验可用(未过期)。"""
cookie = cs.dumps(SECRET, {'site_uid': 1})
assert _flask_serializer().loads(cookie, max_age=3600) == {'site_uid': 1}
def test_tampered_returns_none():
cookie = _flask_serializer().dumps({'site_uid': 26})
assert cs.loads(SECRET, cookie[:-3] + 'xyz') is None
def test_wrong_key_returns_none():
cookie = _flask_serializer().dumps({'site_uid': 26})
assert cs.loads('wrong-secret', cookie) is None
def test_garbage_returns_none():
assert cs.loads(SECRET, 'not-a-cookie') is None
assert cs.loads(SECRET, '') is None
def test_roundtrip_selfconsistency():
payload = {'site_uid': 9, 'site_uid_seq': 3, 'site_uid_ts': int(time.time())}
assert cs.loads(SECRET, cs.dumps(SECRET, payload)) == payload
def test_get_uid_none():
assert cs.get_uid(None) is None
assert cs.get_uid({}) is None
@pytest.mark.parametrize('uid', [1, 26, 99999])
def test_various_uids(uid):
assert cs.get_uid(cs.loads(SECRET, cs.dumps(SECRET, {'site_uid': uid}))) == uid