"""认证相关测试。""" 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, "debug", True) 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, "debug", True) 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"