Files
xiaoxia-saas/tests/unit/test_jwt_handler.py
xiaoxia 2972f19954
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Successful in 58s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m38s
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m46s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 1m42s
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 1m18s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 3m23s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 5m27s
CI/CD Pipeline / Unit Tests (push) Failing after 5m54s
CI/CD Pipeline / Integration Tests (push) Successful in 2m26s
CI/CD Pipeline / Build Staging API Image (push) Successful in 11m39s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m14s
CI/CD Pipeline / ACR Image Cleanup (push) Failing after 14s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 2m3s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 4m14s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
test(ci): 修复10个ruff错误 + Unit Tests dedup收集失败 (#839 #881)
2026-07-25 17:38:17 +08:00

171 lines
5.7 KiB
Python
Executable File

"""JWT Handler 单元测试."""
from __future__ import annotations
import time
import pytest
from jwt.exceptions import ExpiredSignatureError, InvalidTokenError
from packages.application.auth.jwt_handler import (
JWTHandler,
configure_jwt_handler,
get_jwt_handler,
)
@pytest.fixture
def jwt_handler():
return JWTHandler(
secret_key="test-secret-key-12345",
algorithm="HS256",
access_token_expire_minutes=30,
)
class TestJWTHandler:
"""JWTHandler 测试"""
def test_create_access_token_returns_string(self, jwt_handler):
"""创建 access_token 返回非空字符串"""
token = jwt_handler.create_access_token(user_id="user_001")
assert isinstance(token, str)
assert len(token) > 0
def test_create_access_token_with_role(self, jwt_handler):
"""创建带 role 的 access_token"""
token = jwt_handler.create_access_token(user_id="user_001", role="admin")
payload = jwt_handler.verify_access_token(token)
assert payload["sub"] == "user_001"
assert payload["role"] == "admin"
def test_create_access_token_with_additional_claims(self, jwt_handler):
"""创建带额外声明的 access_token"""
token = jwt_handler.create_access_token(
user_id="user_001",
additional_claims={"email": "test@example.com", "tenant": "t1"},
)
payload = jwt_handler.verify_access_token(token)
assert payload["sub"] == "user_001"
assert payload["email"] == "test@example.com"
assert payload["tenant"] == "t1"
def test_verify_access_token_success(self, jwt_handler):
"""验证有效 access_token"""
token = jwt_handler.create_access_token(user_id="user_001")
payload = jwt_handler.verify_access_token(token)
assert payload["sub"] == "user_001"
assert "exp" in payload
assert "iat" in payload
def test_verify_access_token_type_check(self, jwt_handler):
"""verify_access_token 验证 token 类型为 access"""
token = jwt_handler.create_access_token(user_id="user_001")
payload = jwt_handler.verify_access_token(token)
assert payload.get("type") == "access" or "type" in payload
def test_verify_token_no_type_restriction(self, jwt_handler):
"""verify_token 不限制 token 类型"""
token = jwt_handler.create_access_token(user_id="user_001")
payload = jwt_handler.verify_token(token)
assert payload["sub"] == "user_001"
def test_expired_token_raises_error(self):
"""过期 token 验证失败"""
handler = JWTHandler(
secret_key="test-secret",
access_token_expire_minutes=-1, # 立即过期
)
token = handler.create_access_token(user_id="user_001")
# 等待一小段时间确保过期
time.sleep(0.1)
with pytest.raises(ExpiredSignatureError):
handler.verify_access_token(token)
def test_invalid_token_raises_error(self, jwt_handler):
"""无效 token 验证失败"""
with pytest.raises(InvalidTokenError):
jwt_handler.verify_access_token("invalid.token.here")
def test_empty_token_raises_error(self, jwt_handler):
"""空字符串 token 验证失败"""
with pytest.raises(InvalidTokenError):
jwt_handler.verify_access_token("")
def test_different_secret_fails_verification(self):
"""不同密钥生成的 token 无法互相验证"""
handler1 = JWTHandler(secret_key="secret-one")
handler2 = JWTHandler(secret_key="secret-two")
token = handler1.create_access_token(user_id="user_001")
with pytest.raises(InvalidTokenError):
handler2.verify_access_token(token)
def test_custom_algorithm(self):
"""支持自定义算法"""
handler = JWTHandler(
secret_key="test-secret",
algorithm="HS256",
)
token = handler.create_access_token(user_id="user_001")
payload = handler.verify_access_token(token)
assert payload["sub"] == "user_001"
def test_default_role_is_empty_string(self, jwt_handler):
"""不传 role 时默认为空字符串"""
token = jwt_handler.create_access_token(user_id="user_001")
payload = jwt_handler.verify_access_token(token)
assert payload.get("role", "") == ""
class TestGlobalJWTHandler:
"""全局 JWT handler 配置测试"""
def test_configure_creates_handler(self):
"""configure_jwt_handler 创建并返回 handler"""
import packages.application.auth.jwt_handler as jwt_module
# 重置全局状态
jwt_module._default_handler = None
handler = configure_jwt_handler(
secret_key="global-secret",
access_token_expire_minutes=60,
)
assert isinstance(handler, JWTHandler)
assert get_jwt_handler() is handler
def test_get_jwt_handler_without_config_raises(self):
"""未配置时调用 get_jwt_handler 抛出 RuntimeError"""
import packages.application.auth.jwt_handler as jwt_module
# 重置全局状态
jwt_module._default_handler = None
with pytest.raises(RuntimeError, match="JWT handler not configured"):
get_jwt_handler()
def test_configure_overwrites_existing(self):
"""重新配置会覆盖之前的 handler"""
import packages.application.auth.jwt_handler as jwt_module
jwt_module._default_handler = None
handler1 = configure_jwt_handler(secret_key="first-secret")
handler2 = configure_jwt_handler(secret_key="second-secret")
assert handler1 is not handler2
assert get_jwt_handler() is handler2