"""旧 JWT 认证核心(app.core.auth)的单元测试 API 层(/api/v1/auth/*)已切换为会话制认证,端点契约测试见 tests/test_auth_api.py; 本文件仅保留 app.core.auth 的函数级覆盖: - require_admin:非 admin 抛 FORBIDDEN(直接测函数) - create_access_token + decode_token 往返一致 / 非法 token 拒绝 - hash_password + verify_password 正确 / 错误 / 非法哈希 """ from datetime import UTC, datetime import pytest from app.api.response import ApiError from app.config import settings from app.core.auth import ( ERR_FORBIDDEN, ERR_TOKEN_INVALID, create_access_token, decode_token, hash_password, require_admin, verify_password, ) from app.models.auth import AuthUser def _now() -> datetime: return datetime.now(UTC) class TestRequireAdmin: """require_admin:admin 通过、非 admin 抛 FORBIDDEN(直接测函数,不经 API)""" async def test_admin_passes(self): admin = AuthUser(username="alice", role="admin", created_at=_now()) result = await require_admin(user=admin) assert result.role == "admin" async def test_non_admin_raises_forbidden(self): normal = AuthUser(username="bob", role="user", created_at=_now()) with pytest.raises(ApiError) as exc: await require_admin(user=normal) assert exc.value.code == ERR_FORBIDDEN class TestAuthCoreFunctions: """核心函数:token 往返 / 密码哈希""" def test_create_and_decode_token_roundtrip(self): token, expires_in = create_access_token("alice", "admin") assert expires_in == settings.jwt_expire_minutes * 60 payload = decode_token(token) assert payload["sub"] == "alice" assert payload["role"] == "admin" assert "iat" in payload and "exp" in payload def test_decode_invalid_token_raises(self): with pytest.raises(ApiError) as exc: decode_token("not-a-jwt") assert exc.value.code == ERR_TOKEN_INVALID def test_hash_password_not_plaintext(self): hashed = hash_password("my-secret") assert hashed != "my-secret" assert hashed.startswith("$2") # bcrypt 哈希前缀 def test_verify_password_correct(self): hashed = hash_password("my-secret") assert verify_password("my-secret", hashed) is True def test_verify_password_wrong(self): hashed = hash_password("my-secret") assert verify_password("wrong-password", hashed) is False def test_verify_password_garbage_hash_returns_false(self): # 非法 hash 会被 bcrypt 拒绝,verify_password 捕获异常返回 False assert verify_password("any", "not-a-valid-hash") is False