99 lines
3.5 KiB
Python
99 lines
3.5 KiB
Python
"""认证相关测试。"""
|
|
|
|
from fastapi.testclient import TestClient
|
|
|
|
from app.core.config import settings
|
|
from app.core.security import create_access_token, decode_access_token
|
|
|
|
|
|
class TestJWT:
|
|
"""JWT token 测试。"""
|
|
|
|
def test_create_and_decode_token(self):
|
|
"""测试 token 创建和解码。"""
|
|
payload = {"sub": "github:12345"}
|
|
token = create_access_token(payload)
|
|
decoded = decode_access_token(token)
|
|
assert decoded is not None
|
|
assert decoded["sub"] == "github:12345"
|
|
|
|
def test_decode_invalid_token(self):
|
|
"""测试无效 token 解码。"""
|
|
result = decode_access_token("invalid-token")
|
|
assert result is None
|
|
|
|
def test_decode_empty_token(self):
|
|
"""测试空 token 解码。"""
|
|
result = decode_access_token("")
|
|
assert result is None
|
|
|
|
|
|
class TestSession:
|
|
"""Session 端点测试。"""
|
|
|
|
def test_session_without_auth(self, client: TestClient):
|
|
"""未登录时获取 session。"""
|
|
response = client.get("/auth/session")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["user"] is None
|
|
|
|
def test_session_with_auth(self, auth_client: TestClient, test_user):
|
|
"""已登录时获取 session。"""
|
|
response = auth_client.get("/auth/session")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["user"] is not None
|
|
assert data["user"]["id"] == test_user.id
|
|
assert data["user"]["name"] == test_user.name
|
|
|
|
def test_session_with_invalid_token(self, client: TestClient):
|
|
"""无效 token 获取 session。"""
|
|
client.cookies.set("access_token", "invalid-token")
|
|
response = client.get("/auth/session")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["user"] is None
|
|
|
|
|
|
class TestSignout:
|
|
"""登出测试。"""
|
|
|
|
def test_signout(self, auth_client: TestClient):
|
|
"""测试登出。"""
|
|
response = auth_client.post("/auth/signout")
|
|
assert response.status_code == 204
|
|
assert response.content == b""
|
|
set_cookie_headers = response.headers.get_list("set-cookie")
|
|
assert any("access_token=" in value for value in set_cookie_headers)
|
|
|
|
|
|
class TestDevSigninRedirect:
|
|
"""开发登录重定向测试。"""
|
|
|
|
def test_dev_signin_uses_allowed_next_url(self, client: TestClient, monkeypatch):
|
|
"""允许的 next 参数应作为登录完成后的回跳地址。"""
|
|
monkeypatch.setattr(settings, "cors_origins", ["http://localhost:5173", "http://localhost:5174"])
|
|
|
|
response = client.get(
|
|
"/auth/dev/signin",
|
|
params={"next": "http://localhost:5174/console/providers"},
|
|
follow_redirects=False,
|
|
)
|
|
|
|
assert response.status_code == 302
|
|
assert response.headers["location"] == "http://localhost:5174/console/providers"
|
|
|
|
def test_dev_signin_rejects_untrusted_next_url(self, client: TestClient, monkeypatch):
|
|
"""不可信的 next 参数应回退到默认前端地址,避免开放重定向。"""
|
|
monkeypatch.setattr(settings, "cors_origins", ["http://localhost:5173", "http://localhost:5174"])
|
|
|
|
response = client.get(
|
|
"/auth/dev/signin",
|
|
params={"next": "https://evil.example/steal"},
|
|
follow_redirects=False,
|
|
)
|
|
|
|
assert response.status_code == 302
|
|
assert response.headers["location"] == "http://localhost:5173/my-stories"
|