mirror of
https://github.com/fastapi-users/fastapi-users.git
synced 2025-08-15 03:04:27 +08:00
336 lines
11 KiB
Python
336 lines
11 KiB
Python
from typing import Any, AsyncGenerator, Dict, cast
|
|
from unittest.mock import MagicMock
|
|
|
|
import asynctest
|
|
import httpx
|
|
import pytest
|
|
from fastapi import FastAPI, status
|
|
|
|
from fastapi_users.router import ErrorCode, get_verify_router
|
|
from fastapi_users.user import get_get_user, get_verify_user
|
|
from fastapi_users.utils import generate_jwt
|
|
from tests.conftest import User, UserDB
|
|
|
|
SECRET = "SECRET"
|
|
LIFETIME = 3600
|
|
VERIFY_USER_TOKEN_AUDIENCE = "fastapi-users:verify"
|
|
JWT_ALGORITHM = "HS256"
|
|
|
|
|
|
@pytest.fixture
|
|
def verify_token():
|
|
def _verify_token(user_id=None, email=None, lifetime=LIFETIME):
|
|
data = {"aud": VERIFY_USER_TOKEN_AUDIENCE}
|
|
if user_id is not None:
|
|
data["user_id"] = str(user_id)
|
|
if email is not None:
|
|
data["email"] = email
|
|
return generate_jwt(data, SECRET, lifetime, JWT_ALGORITHM)
|
|
|
|
return _verify_token
|
|
|
|
|
|
def after_verification_sync():
|
|
return MagicMock(return_value=None)
|
|
|
|
|
|
def after_verification_async():
|
|
return asynctest.CoroutineMock(return_value=None)
|
|
|
|
|
|
@pytest.fixture(params=[after_verification_sync, after_verification_async])
|
|
def after_verification(request):
|
|
return request.param()
|
|
|
|
|
|
def after_verification_request_sync():
|
|
return MagicMock(return_value=None)
|
|
|
|
|
|
def after_verification_request_async():
|
|
return asynctest.CoroutineMock(return_value=None)
|
|
|
|
|
|
@pytest.fixture(
|
|
params=[after_verification_request_sync, after_verification_request_async]
|
|
)
|
|
def after_verification_request(request):
|
|
return request.param()
|
|
|
|
|
|
@pytest.fixture
|
|
@pytest.mark.asyncio
|
|
async def test_app_client(
|
|
mock_user_db,
|
|
after_verification_request,
|
|
after_verification,
|
|
get_test_client,
|
|
) -> AsyncGenerator[httpx.AsyncClient, None]:
|
|
verify_user = get_verify_user(mock_user_db)
|
|
get_user = get_get_user(mock_user_db)
|
|
verify_router = get_verify_router(
|
|
verify_user,
|
|
get_user,
|
|
User,
|
|
SECRET,
|
|
LIFETIME,
|
|
after_verification_request,
|
|
after_verification,
|
|
)
|
|
|
|
app = FastAPI()
|
|
app.include_router(verify_router)
|
|
|
|
async for client in get_test_client(app):
|
|
yield client
|
|
|
|
|
|
@pytest.mark.router
|
|
@pytest.mark.asyncio
|
|
class TestVerifyTokenRequest:
|
|
async def test_empty_body(
|
|
self,
|
|
test_app_client: httpx.AsyncClient,
|
|
after_verification_request,
|
|
):
|
|
response = await test_app_client.post("/request-verify-token", json={})
|
|
assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY
|
|
assert after_verification_request.called is False
|
|
|
|
async def test_wrong_email(
|
|
self,
|
|
test_app_client: httpx.AsyncClient,
|
|
after_verification_request,
|
|
):
|
|
json = {"email": "king.arthur"}
|
|
response = await test_app_client.post("/request-verify-token", json=json)
|
|
assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY
|
|
assert after_verification_request.called is False
|
|
|
|
async def test_user_not_exists(
|
|
self,
|
|
test_app_client: httpx.AsyncClient,
|
|
after_verification_request,
|
|
):
|
|
json = {"email": "user@example.com"}
|
|
response = await test_app_client.post("/request-verify-token", json=json)
|
|
assert response.status_code == status.HTTP_202_ACCEPTED
|
|
assert after_verification_request.called is False
|
|
|
|
async def test_user_verified_valid_request(
|
|
self,
|
|
test_app_client: httpx.AsyncClient,
|
|
verified_user: UserDB,
|
|
after_verification_request,
|
|
):
|
|
input_user = verified_user
|
|
json = {"email": input_user.email}
|
|
response = await test_app_client.post("/request-verify-token", json=json)
|
|
assert after_verification_request.called is False
|
|
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
|
data = cast(Dict[str, Any], response.json())
|
|
assert data["detail"] == ErrorCode.VERIFY_USER_ALREADY_VERIFIED
|
|
|
|
async def test_user_inactive_valid_request(
|
|
self,
|
|
test_app_client: httpx.AsyncClient,
|
|
inactive_user: UserDB,
|
|
after_verification_request,
|
|
):
|
|
input_user = inactive_user
|
|
json = {"email": input_user.email}
|
|
response = await test_app_client.post("/request-verify-token", json=json)
|
|
assert after_verification_request.called is False
|
|
assert response.status_code == status.HTTP_202_ACCEPTED
|
|
|
|
async def test_user_active_valid_request(
|
|
self,
|
|
test_app_client: httpx.AsyncClient,
|
|
user: UserDB,
|
|
after_verification_request,
|
|
):
|
|
input_user = user
|
|
json = {"email": input_user.email}
|
|
response = await test_app_client.post("/request-verify-token", json=json)
|
|
assert response.status_code == status.HTTP_202_ACCEPTED
|
|
assert after_verification_request.called is True
|
|
|
|
|
|
@pytest.mark.router
|
|
@pytest.mark.asyncio
|
|
class TestVerify:
|
|
async def test_empty_body(
|
|
self,
|
|
test_app_client: httpx.AsyncClient,
|
|
after_verification_request,
|
|
after_verification,
|
|
):
|
|
response = await test_app_client.post("/verify", json={})
|
|
assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY
|
|
assert after_verification.called is False
|
|
assert after_verification_request.called is False
|
|
|
|
async def test_invalid_token(
|
|
self,
|
|
test_app_client: httpx.AsyncClient,
|
|
user: UserDB,
|
|
after_verification_request,
|
|
after_verification,
|
|
):
|
|
json = {"token": "foo"}
|
|
response = await test_app_client.post("/verify", json=json)
|
|
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
|
data = cast(Dict[str, Any], response.json())
|
|
assert data["detail"] == ErrorCode.VERIFY_USER_BAD_TOKEN
|
|
assert after_verification.called is False
|
|
assert after_verification_request.called is False
|
|
|
|
async def test_valid_token_missing_user_id(
|
|
self,
|
|
test_app_client: httpx.AsyncClient,
|
|
verify_token,
|
|
user: UserDB,
|
|
after_verification_request,
|
|
after_verification,
|
|
):
|
|
json = {"token": verify_token(None, user.email)}
|
|
response = await test_app_client.post("/verify", json=json)
|
|
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
|
data = cast(Dict[str, Any], response.json())
|
|
assert data["detail"] == ErrorCode.VERIFY_USER_BAD_TOKEN
|
|
assert after_verification.called is False
|
|
assert after_verification_request.called is False
|
|
|
|
async def test_valid_token_missing_email(
|
|
self,
|
|
test_app_client: httpx.AsyncClient,
|
|
verify_token,
|
|
user: UserDB,
|
|
after_verification_request,
|
|
after_verification,
|
|
):
|
|
json = {"token": verify_token(user.id, None)}
|
|
response = await test_app_client.post("/verify", json=json)
|
|
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
|
data = cast(Dict[str, Any], response.json())
|
|
assert data["detail"] == ErrorCode.VERIFY_USER_BAD_TOKEN
|
|
assert after_verification.called is False
|
|
assert after_verification_request.called is False
|
|
|
|
async def test_valid_token_invalid_uuid(
|
|
self,
|
|
test_app_client: httpx.AsyncClient,
|
|
verify_token,
|
|
user: UserDB,
|
|
after_verification_request,
|
|
after_verification,
|
|
):
|
|
json = {"token": verify_token("foo", user.email)}
|
|
response = await test_app_client.post("/verify", json=json)
|
|
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
|
data = cast(Dict[str, Any], response.json())
|
|
assert data["detail"] == ErrorCode.VERIFY_USER_BAD_TOKEN
|
|
assert after_verification.called is False
|
|
assert after_verification_request.called is False
|
|
|
|
async def test_valid_token_invalid_email(
|
|
self,
|
|
test_app_client: httpx.AsyncClient,
|
|
verify_token,
|
|
user: UserDB,
|
|
after_verification_request,
|
|
after_verification,
|
|
):
|
|
json = {"token": verify_token(user.id, "foo")}
|
|
response = await test_app_client.post("/verify", json=json)
|
|
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
|
data = cast(Dict[str, Any], response.json())
|
|
assert data["detail"] == ErrorCode.VERIFY_USER_BAD_TOKEN
|
|
assert after_verification.called is False
|
|
assert after_verification_request.called is False
|
|
|
|
async def test_valid_token_email_id_mismatch(
|
|
self,
|
|
test_app_client: httpx.AsyncClient,
|
|
verify_token,
|
|
user: UserDB,
|
|
inactive_user: UserDB,
|
|
after_verification_request,
|
|
after_verification,
|
|
):
|
|
json = {"token": verify_token(user.id, inactive_user.email)}
|
|
response = await test_app_client.post("/verify", json=json)
|
|
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
|
data = cast(Dict[str, Any], response.json())
|
|
assert data["detail"] == ErrorCode.VERIFY_USER_BAD_TOKEN
|
|
assert after_verification.called is False
|
|
assert after_verification_request.called is False
|
|
|
|
async def test_expired_token(
|
|
self,
|
|
test_app_client: httpx.AsyncClient,
|
|
verify_token,
|
|
user: UserDB,
|
|
after_verification_request,
|
|
after_verification,
|
|
):
|
|
json = {"token": verify_token(user.id, user.email, -1)}
|
|
response = await test_app_client.post("/verify", json=json)
|
|
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
|
data = cast(Dict[str, Any], response.json())
|
|
assert data["detail"] == ErrorCode.VERIFY_USER_TOKEN_EXPIRED
|
|
assert after_verification.called is False
|
|
assert after_verification_request.called is False
|
|
|
|
async def test_inactive_user(
|
|
self,
|
|
test_app_client: httpx.AsyncClient,
|
|
verify_token,
|
|
inactive_user: UserDB,
|
|
after_verification_request,
|
|
after_verification,
|
|
):
|
|
json = {"token": verify_token(inactive_user.id, inactive_user.email)}
|
|
response = await test_app_client.post("/verify", json=json)
|
|
|
|
assert response.status_code == status.HTTP_200_OK
|
|
assert after_verification.called is True
|
|
assert after_verification_request.called is False
|
|
data = cast(Dict[str, Any], response.json())
|
|
assert data["is_active"] is False
|
|
|
|
async def test_verified_user(
|
|
self,
|
|
test_app_client: httpx.AsyncClient,
|
|
verify_token,
|
|
verified_user: UserDB,
|
|
after_verification_request,
|
|
after_verification,
|
|
):
|
|
json = {"token": verify_token(verified_user.id, verified_user.email)}
|
|
response = await test_app_client.post("/verify", json=json)
|
|
|
|
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
|
data = cast(Dict[str, Any], response.json())
|
|
assert data["detail"] == ErrorCode.VERIFY_USER_ALREADY_VERIFIED
|
|
|
|
assert after_verification.called is False
|
|
assert after_verification_request.called is False
|
|
|
|
async def test_active_user(
|
|
self,
|
|
test_app_client: httpx.AsyncClient,
|
|
verify_token,
|
|
user: UserDB,
|
|
after_verification_request,
|
|
after_verification,
|
|
):
|
|
json = {"token": verify_token(user.id, user.email)}
|
|
response = await test_app_client.post("/verify", json=json)
|
|
|
|
assert response.status_code == status.HTTP_200_OK
|
|
assert after_verification.called is True
|
|
assert after_verification_request.called is False
|
|
data = cast(Dict[str, Any], response.json())
|
|
assert data["is_active"] is True
|