在准备环境前提交次全部更改。

This commit is contained in:
Neo
2026-02-19 08:35:13 +08:00
parent ded6dfb9d8
commit 4eac07da47
1387 changed files with 6107191 additions and 33002 deletions

View File

@@ -0,0 +1,137 @@
"""
认证模块属性测试Property-Based Testing
使用 hypothesis 验证认证系统的通用正确性属性:
- Property 2: 无效凭据始终被拒绝
- Property 3: 有效 JWT 令牌授权访问
"""
import os
os.environ.setdefault("JWT_SECRET_KEY", "test-secret-key-for-property-tests")
from unittest.mock import MagicMock, patch
from hypothesis import given, settings
from hypothesis import strategies as st
from app.auth.dependencies import CurrentUser, get_current_user
from app.auth.jwt import create_access_token
from app.main import app
from app.routers.auth import router
# 确保路由已挂载
if router not in [r for r in app.routes]:
app.include_router(router)
from fastapi.testclient import TestClient
client = TestClient(app)
# ---------------------------------------------------------------------------
# 策略Strategies
# ---------------------------------------------------------------------------
# 用户名策略1~64 字符的可打印字符串(排除控制字符)
_username_st = st.text(
alphabet=st.characters(whitelist_categories=("L", "N", "P", "S")),
min_size=1,
max_size=64,
)
# 密码策略1~128 字符的可打印字符串
_password_st = st.text(
alphabet=st.characters(whitelist_categories=("L", "N", "P", "S")),
min_size=1,
max_size=128,
)
# user_id 策略:正整数
_user_id_st = st.integers(min_value=1, max_value=2**31 - 1)
# site_id 策略:正整数
_site_id_st = st.integers(min_value=1, max_value=2**63 - 1)
def _mock_db_returning(row):
"""构造 mock get_connectioncursor.fetchone() 返回指定行。"""
mock_conn = MagicMock()
mock_cursor = MagicMock()
mock_cursor.fetchone.return_value = row
mock_conn.cursor.return_value.__enter__ = lambda _: mock_cursor
mock_conn.cursor.return_value.__exit__ = MagicMock(return_value=False)
return mock_conn
# ---------------------------------------------------------------------------
# Feature: admin-web-console, Property 2: 无效凭据始终被拒绝
# **Validates: Requirements 1.2**
# ---------------------------------------------------------------------------
@settings(max_examples=100)
@given(username=_username_st, password=_password_st)
@patch("app.routers.auth.get_connection")
def test_invalid_credentials_always_rejected(mock_get_conn, username, password):
"""
Property 2: 无效凭据始终被拒绝。
对于任意用户名/密码组合当数据库中不存在该用户时fetchone 返回 None
登录接口应始终返回 401 状态码。
"""
# mock 数据库返回 None — 用户不存在
mock_get_conn.return_value = _mock_db_returning(None)
resp = client.post(
"/api/auth/login",
json={"username": username, "password": password},
)
assert resp.status_code == 401, (
f"期望 401实际 {resp.status_code}username={username!r}"
)
# ---------------------------------------------------------------------------
# Feature: admin-web-console, Property 3: 有效 JWT 令牌授权访问
# **Validates: Requirements 1.3**
# ---------------------------------------------------------------------------
import asyncio
from fastapi.security import HTTPAuthorizationCredentials
def _run_async(coro):
"""在同步上下文中执行异步协程,避免 DeprecationWarning。"""
loop = asyncio.new_event_loop()
try:
return loop.run_until_complete(coro)
finally:
loop.close()
@settings(max_examples=100)
@given(user_id=_user_id_st, site_id=_site_id_st)
def test_valid_jwt_grants_access(user_id, site_id):
"""
Property 3: 有效 JWT 令牌授权访问。
对于任意 user_id 和 site_id由系统签发的未过期 access_token
应能被 get_current_user 依赖成功解析为 CurrentUser 对象,
且解析出的 user_id 和 site_id 与签发时一致。
"""
# 生成有效的 access_token
token = create_access_token(user_id=user_id, site_id=site_id)
# 直接调用依赖函数验证令牌解析
credentials = HTTPAuthorizationCredentials(scheme="Bearer", credentials=token)
result = _run_async(get_current_user(credentials))
assert isinstance(result, CurrentUser)
assert result.user_id == user_id, (
f"user_id 不匹配:期望 {user_id},实际 {result.user_id}"
)
assert result.site_id == site_id, (
f"site_id 不匹配:期望 {site_id},实际 {result.site_id}"
)