mirror of
				https://github.com/fastapi-users/fastapi-users.git
				synced 2025-10-31 09:28:45 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			507 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			507 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| from collections.abc import AsyncGenerator
 | |
| from typing import Optional
 | |
| 
 | |
| import httpx
 | |
| import pytest
 | |
| import pytest_asyncio
 | |
| from fastapi import Depends, FastAPI, status
 | |
| 
 | |
| from fastapi_users import FastAPIUsers, schemas
 | |
| from tests.conftest import IDType, User, UserCreate, UserModel, UserUpdate
 | |
| 
 | |
| 
 | |
| @pytest_asyncio.fixture
 | |
| async def test_app_client(
 | |
|     secret,
 | |
|     get_user_manager,
 | |
|     mock_authentication,
 | |
|     oauth_client,
 | |
|     get_test_client,
 | |
| ) -> AsyncGenerator[httpx.AsyncClient, None]:
 | |
|     fastapi_users = FastAPIUsers[UserModel, IDType](
 | |
|         get_user_manager, [mock_authentication]
 | |
|     )
 | |
| 
 | |
|     app = FastAPI()
 | |
|     app.include_router(fastapi_users.get_register_router(User, UserCreate))
 | |
|     app.include_router(fastapi_users.get_reset_password_router())
 | |
|     app.include_router(fastapi_users.get_auth_router(mock_authentication))
 | |
|     app.include_router(
 | |
|         fastapi_users.get_oauth_router(oauth_client, mock_authentication, secret)
 | |
|     )
 | |
|     app.include_router(
 | |
|         fastapi_users.get_oauth_associate_router(oauth_client, User, secret)
 | |
|     )
 | |
| 
 | |
|     @app.delete("/users/me")
 | |
|     def custom_users_route():
 | |
|         return None
 | |
| 
 | |
|     app.include_router(
 | |
|         fastapi_users.get_users_router(User, UserUpdate), prefix="/users"
 | |
|     )
 | |
|     app.include_router(fastapi_users.get_verify_router(User))
 | |
| 
 | |
|     @app.get("/current-user", response_model=User)
 | |
|     def current_user(user: UserModel = Depends(fastapi_users.current_user())):
 | |
|         return user
 | |
| 
 | |
|     @app.get("/current-active-user", response_model=User)
 | |
|     def current_active_user(
 | |
|         user: UserModel = Depends(fastapi_users.current_user(active=True)),
 | |
|     ):
 | |
|         return user
 | |
| 
 | |
|     @app.get("/current-verified-user", response_model=User)
 | |
|     def current_verified_user(
 | |
|         user: UserModel = Depends(fastapi_users.current_user(verified=True)),
 | |
|     ):
 | |
|         return user
 | |
| 
 | |
|     @app.get("/current-superuser", response_model=User)
 | |
|     def current_superuser(
 | |
|         user: UserModel = Depends(
 | |
|             fastapi_users.current_user(active=True, superuser=True)
 | |
|         ),
 | |
|     ):
 | |
|         return user
 | |
| 
 | |
|     @app.get("/current-verified-superuser", response_model=User)
 | |
|     def current_verified_superuser(
 | |
|         user: UserModel = Depends(
 | |
|             fastapi_users.current_user(active=True, verified=True, superuser=True)
 | |
|         ),
 | |
|     ):
 | |
|         return user
 | |
| 
 | |
|     @app.get("/optional-current-user")
 | |
|     def optional_current_user(
 | |
|         user: Optional[UserModel] = Depends(fastapi_users.current_user(optional=True)),
 | |
|     ):
 | |
|         return schemas.model_validate(User, user) if user else None
 | |
| 
 | |
|     @app.get("/optional-current-active-user")
 | |
|     def optional_current_active_user(
 | |
|         user: Optional[UserModel] = Depends(
 | |
|             fastapi_users.current_user(optional=True, active=True)
 | |
|         ),
 | |
|     ):
 | |
|         return schemas.model_validate(User, user) if user else None
 | |
| 
 | |
|     @app.get("/optional-current-verified-user")
 | |
|     def optional_current_verified_user(
 | |
|         user: Optional[UserModel] = Depends(
 | |
|             fastapi_users.current_user(optional=True, verified=True)
 | |
|         ),
 | |
|     ):
 | |
|         return schemas.model_validate(User, user) if user else None
 | |
| 
 | |
|     @app.get("/optional-current-superuser")
 | |
|     def optional_current_superuser(
 | |
|         user: Optional[UserModel] = Depends(
 | |
|             fastapi_users.current_user(optional=True, active=True, superuser=True)
 | |
|         ),
 | |
|     ):
 | |
|         return schemas.model_validate(User, user) if user else None
 | |
| 
 | |
|     @app.get("/optional-current-verified-superuser")
 | |
|     def optional_current_verified_superuser(
 | |
|         user: Optional[UserModel] = Depends(
 | |
|             fastapi_users.current_user(
 | |
|                 optional=True, active=True, verified=True, superuser=True
 | |
|             )
 | |
|         ),
 | |
|     ):
 | |
|         return schemas.model_validate(User, user) if user else None
 | |
| 
 | |
|     async for client in get_test_client(app):
 | |
|         yield client
 | |
| 
 | |
| 
 | |
| @pytest.mark.fastapi_users
 | |
| @pytest.mark.asyncio
 | |
| @pytest.mark.parametrize(
 | |
|     "path,method",
 | |
|     [
 | |
|         ("/register", "POST"),
 | |
|         ("/request-verify-token", "POST"),
 | |
|         ("/verify", "POST"),
 | |
|         ("/forgot-password", "POST"),
 | |
|         ("/reset-password", "POST"),
 | |
|         ("/login", "POST"),
 | |
|         ("/logout", "POST"),
 | |
|         ("/register", "POST"),
 | |
|         ("/users/d35d213e-f3d8-4f08-954a-7e0d1bea286f", "GET"),
 | |
|         ("/users/d35d213e-f3d8-4f08-954a-7e0d1bea286f", "PATCH"),
 | |
|         ("/users/d35d213e-f3d8-4f08-954a-7e0d1bea286f", "DELETE"),
 | |
|     ],
 | |
| )
 | |
| async def test_route_exists(test_app_client: httpx.AsyncClient, path: str, method: str):
 | |
|     response = await test_app_client.request(method, path)
 | |
|     assert response.status_code not in (
 | |
|         status.HTTP_404_NOT_FOUND,
 | |
|         status.HTTP_405_METHOD_NOT_ALLOWED,
 | |
|     )
 | |
| 
 | |
| 
 | |
| @pytest.mark.fastapi_users
 | |
| @pytest.mark.asyncio
 | |
| async def test_custom_users_route_not_catched(test_app_client: httpx.AsyncClient):
 | |
|     response = await test_app_client.request("DELETE", "/users/me")
 | |
|     assert response.status_code == status.HTTP_200_OK
 | |
| 
 | |
| 
 | |
| @pytest.mark.fastapi_users
 | |
| @pytest.mark.asyncio
 | |
| class TestGetCurrentUser:
 | |
|     async def test_missing_token(self, test_app_client: httpx.AsyncClient):
 | |
|         response = await test_app_client.get("/current-user")
 | |
|         assert response.status_code == status.HTTP_401_UNAUTHORIZED
 | |
| 
 | |
|     async def test_invalid_token(self, test_app_client: httpx.AsyncClient):
 | |
|         response = await test_app_client.get(
 | |
|             "/current-user", headers={"Authorization": "Bearer foo"}
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_401_UNAUTHORIZED
 | |
| 
 | |
|     async def test_valid_token(
 | |
|         self, test_app_client: httpx.AsyncClient, user: UserModel
 | |
|     ):
 | |
|         response = await test_app_client.get(
 | |
|             "/current-user", headers={"Authorization": f"Bearer {user.id}"}
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
| 
 | |
| 
 | |
| @pytest.mark.fastapi_users
 | |
| @pytest.mark.asyncio
 | |
| class TestGetCurrentActiveUser:
 | |
|     async def test_missing_token(self, test_app_client: httpx.AsyncClient):
 | |
|         response = await test_app_client.get("/current-active-user")
 | |
|         assert response.status_code == status.HTTP_401_UNAUTHORIZED
 | |
| 
 | |
|     async def test_invalid_token(self, test_app_client: httpx.AsyncClient):
 | |
|         response = await test_app_client.get(
 | |
|             "/current-active-user", headers={"Authorization": "Bearer foo"}
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_401_UNAUTHORIZED
 | |
| 
 | |
|     async def test_valid_token_inactive_user(
 | |
|         self, test_app_client: httpx.AsyncClient, inactive_user: UserModel
 | |
|     ):
 | |
|         response = await test_app_client.get(
 | |
|             "/current-active-user",
 | |
|             headers={"Authorization": f"Bearer {inactive_user.id}"},
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_401_UNAUTHORIZED
 | |
| 
 | |
|     async def test_valid_token(
 | |
|         self, test_app_client: httpx.AsyncClient, user: UserModel
 | |
|     ):
 | |
|         response = await test_app_client.get(
 | |
|             "/current-active-user", headers={"Authorization": f"Bearer {user.id}"}
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
| 
 | |
| 
 | |
| @pytest.mark.fastapi_users
 | |
| @pytest.mark.asyncio
 | |
| class TestGetCurrentVerifiedUser:
 | |
|     async def test_missing_token(self, test_app_client: httpx.AsyncClient):
 | |
|         response = await test_app_client.get("/current-verified-user")
 | |
|         assert response.status_code == status.HTTP_401_UNAUTHORIZED
 | |
| 
 | |
|     async def test_invalid_token(self, test_app_client: httpx.AsyncClient):
 | |
|         response = await test_app_client.get(
 | |
|             "/current-verified-user", headers={"Authorization": "Bearer foo"}
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_401_UNAUTHORIZED
 | |
| 
 | |
|     async def test_valid_token_unverified_user(
 | |
|         self, test_app_client: httpx.AsyncClient, user: UserModel
 | |
|     ):
 | |
|         response = await test_app_client.get(
 | |
|             "/current-verified-user",
 | |
|             headers={"Authorization": f"Bearer {user.id}"},
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_403_FORBIDDEN
 | |
| 
 | |
|     async def test_valid_token_verified_user(
 | |
|         self, test_app_client: httpx.AsyncClient, verified_user: UserModel
 | |
|     ):
 | |
|         response = await test_app_client.get(
 | |
|             "/current-verified-user",
 | |
|             headers={"Authorization": f"Bearer {verified_user.id}"},
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
| 
 | |
| 
 | |
| @pytest.mark.fastapi_users
 | |
| @pytest.mark.asyncio
 | |
| class TestGetCurrentSuperuser:
 | |
|     async def test_missing_token(self, test_app_client: httpx.AsyncClient):
 | |
|         response = await test_app_client.get("/current-superuser")
 | |
|         assert response.status_code == status.HTTP_401_UNAUTHORIZED
 | |
| 
 | |
|     async def test_invalid_token(self, test_app_client: httpx.AsyncClient):
 | |
|         response = await test_app_client.get(
 | |
|             "/current-superuser", headers={"Authorization": "Bearer foo"}
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_401_UNAUTHORIZED
 | |
| 
 | |
|     async def test_valid_token_regular_user(
 | |
|         self, test_app_client: httpx.AsyncClient, user: UserModel
 | |
|     ):
 | |
|         response = await test_app_client.get(
 | |
|             "/current-superuser", headers={"Authorization": f"Bearer {user.id}"}
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_403_FORBIDDEN
 | |
| 
 | |
|     async def test_valid_token_superuser(
 | |
|         self, test_app_client: httpx.AsyncClient, superuser: UserModel
 | |
|     ):
 | |
|         response = await test_app_client.get(
 | |
|             "/current-superuser", headers={"Authorization": f"Bearer {superuser.id}"}
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
| 
 | |
| 
 | |
| @pytest.mark.fastapi_users
 | |
| @pytest.mark.asyncio
 | |
| class TestGetCurrentVerifiedSuperuser:
 | |
|     async def test_missing_token(self, test_app_client: httpx.AsyncClient):
 | |
|         response = await test_app_client.get("/current-verified-superuser")
 | |
|         assert response.status_code == status.HTTP_401_UNAUTHORIZED
 | |
| 
 | |
|     async def test_invalid_token(self, test_app_client: httpx.AsyncClient):
 | |
|         response = await test_app_client.get(
 | |
|             "/current-verified-superuser", headers={"Authorization": "Bearer foo"}
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_401_UNAUTHORIZED
 | |
| 
 | |
|     async def test_valid_token_regular_user(
 | |
|         self, test_app_client: httpx.AsyncClient, user: UserModel
 | |
|     ):
 | |
|         response = await test_app_client.get(
 | |
|             "/current-verified-superuser",
 | |
|             headers={"Authorization": f"Bearer {user.id}"},
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_403_FORBIDDEN
 | |
| 
 | |
|     async def test_valid_token_verified_user(
 | |
|         self, test_app_client: httpx.AsyncClient, verified_user: UserModel
 | |
|     ):
 | |
|         response = await test_app_client.get(
 | |
|             "/current-verified-superuser",
 | |
|             headers={"Authorization": f"Bearer {verified_user.id}"},
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_403_FORBIDDEN
 | |
| 
 | |
|     async def test_valid_token_superuser(
 | |
|         self, test_app_client: httpx.AsyncClient, superuser: UserModel
 | |
|     ):
 | |
|         response = await test_app_client.get(
 | |
|             "/current-verified-superuser",
 | |
|             headers={"Authorization": f"Bearer {superuser.id}"},
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_403_FORBIDDEN
 | |
| 
 | |
|     async def test_valid_token_verified_superuser(
 | |
|         self, test_app_client: httpx.AsyncClient, verified_superuser: UserModel
 | |
|     ):
 | |
|         response = await test_app_client.get(
 | |
|             "/current-verified-superuser",
 | |
|             headers={"Authorization": f"Bearer {verified_superuser.id}"},
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
| 
 | |
| 
 | |
| @pytest.mark.fastapi_users
 | |
| @pytest.mark.asyncio
 | |
| class TestOptionalGetCurrentUser:
 | |
|     async def test_missing_token(self, test_app_client: httpx.AsyncClient):
 | |
|         response = await test_app_client.get("/optional-current-user")
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
|         assert response.json() is None
 | |
| 
 | |
|     async def test_invalid_token(self, test_app_client: httpx.AsyncClient):
 | |
|         response = await test_app_client.get(
 | |
|             "/optional-current-user", headers={"Authorization": "Bearer foo"}
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
|         assert response.json() is None
 | |
| 
 | |
|     async def test_valid_token(
 | |
|         self, test_app_client: httpx.AsyncClient, user: UserModel
 | |
|     ):
 | |
|         response = await test_app_client.get(
 | |
|             "/optional-current-user", headers={"Authorization": f"Bearer {user.id}"}
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
|         assert response.json() is not None
 | |
| 
 | |
| 
 | |
| @pytest.mark.fastapi_users
 | |
| @pytest.mark.asyncio
 | |
| class TestOptionalGetCurrentVerifiedUser:
 | |
|     async def test_missing_token(self, test_app_client: httpx.AsyncClient):
 | |
|         response = await test_app_client.get("/optional-current-verified-user")
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
|         assert response.json() is None
 | |
| 
 | |
|     async def test_invalid_token(self, test_app_client: httpx.AsyncClient):
 | |
|         response = await test_app_client.get(
 | |
|             "/optional-current-verified-user", headers={"Authorization": "Bearer foo"}
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
|         assert response.json() is None
 | |
| 
 | |
|     async def test_valid_token_unverified_user(
 | |
|         self, test_app_client: httpx.AsyncClient, user: UserModel
 | |
|     ):
 | |
|         response = await test_app_client.get(
 | |
|             "/optional-current-verified-user",
 | |
|             headers={"Authorization": f"Bearer {user.id}"},
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
|         assert response.json() is None
 | |
| 
 | |
|     async def test_valid_token_verified_user(
 | |
|         self, test_app_client: httpx.AsyncClient, verified_user: UserModel
 | |
|     ):
 | |
|         response = await test_app_client.get(
 | |
|             "/optional-current-verified-user",
 | |
|             headers={"Authorization": f"Bearer {verified_user.id}"},
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
|         assert response.json() is not None
 | |
| 
 | |
| 
 | |
| @pytest.mark.fastapi_users
 | |
| @pytest.mark.asyncio
 | |
| class TestOptionalGetCurrentActiveUser:
 | |
|     async def test_missing_token(self, test_app_client: httpx.AsyncClient):
 | |
|         response = await test_app_client.get("/optional-current-active-user")
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
|         assert response.json() is None
 | |
| 
 | |
|     async def test_invalid_token(self, test_app_client: httpx.AsyncClient):
 | |
|         response = await test_app_client.get(
 | |
|             "/optional-current-active-user", headers={"Authorization": "Bearer foo"}
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
|         assert response.json() is None
 | |
| 
 | |
|     async def test_valid_token_inactive_user(
 | |
|         self, test_app_client: httpx.AsyncClient, inactive_user: UserModel
 | |
|     ):
 | |
|         response = await test_app_client.get(
 | |
|             "/optional-current-active-user",
 | |
|             headers={"Authorization": f"Bearer {inactive_user.id}"},
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
|         assert response.json() is None
 | |
| 
 | |
|     async def test_valid_token(
 | |
|         self, test_app_client: httpx.AsyncClient, user: UserModel
 | |
|     ):
 | |
|         response = await test_app_client.get(
 | |
|             "/optional-current-active-user",
 | |
|             headers={"Authorization": f"Bearer {user.id}"},
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
|         assert response.json() is not None
 | |
| 
 | |
| 
 | |
| @pytest.mark.fastapi_users
 | |
| @pytest.mark.asyncio
 | |
| class TestOptionalGetCurrentSuperuser:
 | |
|     async def test_missing_token(self, test_app_client: httpx.AsyncClient):
 | |
|         response = await test_app_client.get("/optional-current-superuser")
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
|         assert response.json() is None
 | |
| 
 | |
|     async def test_invalid_token(self, test_app_client: httpx.AsyncClient):
 | |
|         response = await test_app_client.get(
 | |
|             "/optional-current-superuser", headers={"Authorization": "Bearer foo"}
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
|         assert response.json() is None
 | |
| 
 | |
|     async def test_valid_token_regular_user(
 | |
|         self, test_app_client: httpx.AsyncClient, user: UserModel
 | |
|     ):
 | |
|         response = await test_app_client.get(
 | |
|             "/optional-current-superuser",
 | |
|             headers={"Authorization": f"Bearer {user.id}"},
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
|         assert response.json() is None
 | |
| 
 | |
|     async def test_valid_token_superuser(
 | |
|         self, test_app_client: httpx.AsyncClient, superuser: UserModel
 | |
|     ):
 | |
|         response = await test_app_client.get(
 | |
|             "/optional-current-superuser",
 | |
|             headers={"Authorization": f"Bearer {superuser.id}"},
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
|         assert response.json() is not None
 | |
| 
 | |
| 
 | |
| @pytest.mark.fastapi_users
 | |
| @pytest.mark.asyncio
 | |
| class TestOptionalGetCurrentVerifiedSuperuser:
 | |
|     async def test_missing_token(self, test_app_client: httpx.AsyncClient):
 | |
|         response = await test_app_client.get("/optional-current-verified-superuser")
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
|         assert response.json() is None
 | |
| 
 | |
|     async def test_invalid_token(self, test_app_client: httpx.AsyncClient):
 | |
|         response = await test_app_client.get(
 | |
|             "/optional-current-verified-superuser",
 | |
|             headers={"Authorization": "Bearer foo"},
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
|         assert response.json() is None
 | |
| 
 | |
|     async def test_valid_token_regular_user(
 | |
|         self, test_app_client: httpx.AsyncClient, user: UserModel
 | |
|     ):
 | |
|         response = await test_app_client.get(
 | |
|             "/optional-current-verified-superuser",
 | |
|             headers={"Authorization": f"Bearer {user.id}"},
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
|         assert response.json() is None
 | |
| 
 | |
|     async def test_valid_token_verified_user(
 | |
|         self, test_app_client: httpx.AsyncClient, verified_user: UserModel
 | |
|     ):
 | |
|         response = await test_app_client.get(
 | |
|             "/optional-current-verified-superuser",
 | |
|             headers={"Authorization": f"Bearer {verified_user.id}"},
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
|         assert response.json() is None
 | |
| 
 | |
|     async def test_valid_token_superuser(
 | |
|         self, test_app_client: httpx.AsyncClient, superuser: UserModel
 | |
|     ):
 | |
|         response = await test_app_client.get(
 | |
|             "/optional-current-verified-superuser",
 | |
|             headers={"Authorization": f"Bearer {superuser.id}"},
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
|         assert response.json() is None
 | |
| 
 | |
|     async def test_valid_token_verified_superuser(
 | |
|         self, test_app_client: httpx.AsyncClient, verified_superuser: UserModel
 | |
|     ):
 | |
|         response = await test_app_client.get(
 | |
|             "/optional-current-verified-superuser",
 | |
|             headers={"Authorization": f"Bearer {verified_superuser.id}"},
 | |
|         )
 | |
|         assert response.status_code == status.HTTP_200_OK
 | |
|         assert response.json() is not None
 | 
