mirror of
https://github.com/fastapi-users/fastapi-users.git
synced 2025-08-14 18:58:10 +08:00
Drop Python 3.8 support
This commit is contained in:
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@ -8,7 +8,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python_version: [3.8, 3.9, '3.10', '3.11', '3.12']
|
||||
python_version: [3.9, '3.10', '3.11', '3.12']
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@ -28,7 +28,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python_version: [3.8, 3.9, '3.10', '3.11', '3.12']
|
||||
python_version: [3.9, '3.10', '3.11', '3.12']
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@ -64,7 +64,7 @@ jobs:
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.8
|
||||
python-version: 3.9
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
|
2
.github/workflows/documentation.yml
vendored
2
.github/workflows/documentation.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.8
|
||||
python-version: 3.9
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
|
@ -1,5 +1,3 @@
|
||||
from typing import List
|
||||
|
||||
import motor.motor_asyncio
|
||||
from beanie import Document
|
||||
from fastapi_users.db import BaseOAuthAccount, BeanieBaseUser, BeanieUserDatabase
|
||||
@ -17,7 +15,7 @@ class OAuthAccount(BaseOAuthAccount):
|
||||
|
||||
|
||||
class User(BeanieBaseUser, Document):
|
||||
oauth_accounts: List[OAuthAccount] = Field(default_factory=list)
|
||||
oauth_accounts: list[OAuthAccount] = Field(default_factory=list)
|
||||
|
||||
|
||||
async def get_user_db():
|
||||
|
@ -1,4 +1,4 @@
|
||||
from typing import AsyncGenerator
|
||||
from collections.abc import AsyncGenerator
|
||||
|
||||
from fastapi import Depends
|
||||
from fastapi_users.db import SQLAlchemyBaseUserTableUUID, SQLAlchemyUserDatabase
|
||||
|
@ -1,4 +1,4 @@
|
||||
from typing import AsyncGenerator
|
||||
from collections.abc import AsyncGenerator
|
||||
|
||||
from fastapi import Depends
|
||||
from fastapi_users.db import SQLAlchemyBaseUserTableUUID, SQLAlchemyUserDatabase
|
||||
|
@ -1,4 +1,4 @@
|
||||
from typing import AsyncGenerator, List
|
||||
from collections.abc import AsyncGenerator
|
||||
|
||||
from fastapi import Depends
|
||||
from fastapi_users.db import (
|
||||
@ -21,7 +21,7 @@ class OAuthAccount(SQLAlchemyBaseOAuthAccountTableUUID, Base):
|
||||
|
||||
|
||||
class User(SQLAlchemyBaseUserTableUUID, Base):
|
||||
oauth_accounts: Mapped[List[OAuthAccount]] = relationship(
|
||||
oauth_accounts: Mapped[list[OAuthAccount]] = relationship(
|
||||
"OAuthAccount", lazy="joined"
|
||||
)
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
from typing import List
|
||||
|
||||
import motor.motor_asyncio
|
||||
from beanie import Document
|
||||
from fastapi_users.db import BaseOAuthAccount, BeanieBaseUser, BeanieUserDatabase
|
||||
@ -17,7 +15,7 @@ class OAuthAccount(BaseOAuthAccount):
|
||||
|
||||
|
||||
class User(BeanieBaseUser, Document):
|
||||
oauth_accounts: List[OAuthAccount] = Field(default_factory=list)
|
||||
oauth_accounts: list[OAuthAccount] = Field(default_factory=list)
|
||||
|
||||
|
||||
async def get_user_db():
|
||||
|
@ -1,4 +1,4 @@
|
||||
from typing import AsyncGenerator, List
|
||||
from collections.abc import AsyncGenerator
|
||||
|
||||
from fastapi import Depends
|
||||
from fastapi_users.db import (
|
||||
@ -21,7 +21,7 @@ class OAuthAccount(SQLAlchemyBaseOAuthAccountTableUUID, Base):
|
||||
|
||||
|
||||
class User(SQLAlchemyBaseUserTableUUID, Base):
|
||||
oauth_accounts: Mapped[List[OAuthAccount]] = relationship(
|
||||
oauth_accounts: Mapped[list[OAuthAccount]] = relationship(
|
||||
"OAuthAccount", lazy="joined"
|
||||
)
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
from typing import AsyncGenerator
|
||||
from collections.abc import AsyncGenerator
|
||||
|
||||
from fastapi import Depends
|
||||
from fastapi_users.db import SQLAlchemyBaseUserTableUUID, SQLAlchemyUserDatabase
|
||||
|
@ -1,6 +1,7 @@
|
||||
import re
|
||||
from collections.abc import Sequence
|
||||
from inspect import Parameter, Signature
|
||||
from typing import Any, Callable, Generic, List, Optional, Sequence, Tuple, cast
|
||||
from typing import Any, Callable, Generic, Optional, cast
|
||||
|
||||
from fastapi import Depends, HTTPException, status
|
||||
from makefun import with_signature
|
||||
@ -160,7 +161,7 @@ class Authenticator(Generic[models.UP, models.ID]):
|
||||
verified: bool = False,
|
||||
superuser: bool = False,
|
||||
**kwargs,
|
||||
) -> Tuple[Optional[models.UP], Optional[str]]:
|
||||
) -> tuple[Optional[models.UP], Optional[str]]:
|
||||
user: Optional[models.UP] = None
|
||||
token: Optional[str] = None
|
||||
enabled_backends: Sequence[AuthenticationBackend[models.UP, models.ID]] = (
|
||||
@ -203,7 +204,7 @@ class Authenticator(Generic[models.UP, models.ID]):
|
||||
This way, each security schemes are detected by the OpenAPI generator.
|
||||
"""
|
||||
try:
|
||||
parameters: List[Parameter] = [
|
||||
parameters: list[Parameter] = [
|
||||
Parameter(
|
||||
name="user_manager",
|
||||
kind=Parameter.POSITIONAL_OR_KEYWORD,
|
||||
|
@ -1,5 +1,5 @@
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, Generic, Optional, Protocol
|
||||
from typing import Any, Generic, Optional, Protocol
|
||||
|
||||
from fastapi_users.authentication.strategy.db.models import AP
|
||||
|
||||
@ -13,11 +13,11 @@ class AccessTokenDatabase(Protocol, Generic[AP]):
|
||||
"""Get a single access token by token."""
|
||||
... # pragma: no cover
|
||||
|
||||
async def create(self, create_dict: Dict[str, Any]) -> AP:
|
||||
async def create(self, create_dict: dict[str, Any]) -> AP:
|
||||
"""Create an access token."""
|
||||
... # pragma: no cover
|
||||
|
||||
async def update(self, access_token: AP, update_dict: Dict[str, Any]) -> AP:
|
||||
async def update(self, access_token: AP, update_dict: dict[str, Any]) -> AP:
|
||||
"""Update an access token."""
|
||||
... # pragma: no cover
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import secrets
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Any, Dict, Generic, Optional
|
||||
from typing import Any, Generic, Optional
|
||||
|
||||
from fastapi_users import exceptions, models
|
||||
from fastapi_users.authentication.strategy.base import Strategy
|
||||
@ -50,6 +50,6 @@ class DatabaseStrategy(
|
||||
if access_token is not None:
|
||||
await self.database.delete(access_token)
|
||||
|
||||
def _create_access_token_dict(self, user: models.UP) -> Dict[str, Any]:
|
||||
def _create_access_token_dict(self, user: models.UP) -> dict[str, Any]:
|
||||
token = secrets.token_urlsafe()
|
||||
return {"token": token, "user_id": user.id}
|
||||
|
@ -1,4 +1,4 @@
|
||||
from typing import Generic, List, Optional
|
||||
from typing import Generic, Optional
|
||||
|
||||
import jwt
|
||||
|
||||
@ -22,7 +22,7 @@ class JWTStrategy(Strategy[models.UP, models.ID], Generic[models.UP, models.ID])
|
||||
self,
|
||||
secret: SecretType,
|
||||
lifetime_seconds: Optional[int],
|
||||
token_audience: List[str] = ["fastapi-users:auth"],
|
||||
token_audience: list[str] = ["fastapi-users:auth"],
|
||||
algorithm: str = "HS256",
|
||||
public_key: Optional[SecretType] = None,
|
||||
):
|
||||
|
@ -1,4 +1,4 @@
|
||||
from typing import Any, Dict, Generic, Optional
|
||||
from typing import Any, Generic, Optional
|
||||
|
||||
from fastapi_users.models import ID, OAP, UOAP, UP
|
||||
from fastapi_users.types import DependencyCallable
|
||||
@ -19,11 +19,11 @@ class BaseUserDatabase(Generic[UP, ID]):
|
||||
"""Get a single user by OAuth account id."""
|
||||
raise NotImplementedError()
|
||||
|
||||
async def create(self, create_dict: Dict[str, Any]) -> UP:
|
||||
async def create(self, create_dict: dict[str, Any]) -> UP:
|
||||
"""Create a user."""
|
||||
raise NotImplementedError()
|
||||
|
||||
async def update(self, user: UP, update_dict: Dict[str, Any]) -> UP:
|
||||
async def update(self, user: UP, update_dict: dict[str, Any]) -> UP:
|
||||
"""Update a user."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@ -32,7 +32,7 @@ class BaseUserDatabase(Generic[UP, ID]):
|
||||
raise NotImplementedError()
|
||||
|
||||
async def add_oauth_account(
|
||||
self: "BaseUserDatabase[UOAP, ID]", user: UOAP, create_dict: Dict[str, Any]
|
||||
self: "BaseUserDatabase[UOAP, ID]", user: UOAP, create_dict: dict[str, Any]
|
||||
) -> UOAP:
|
||||
"""Create an OAuth account and add it to the user."""
|
||||
raise NotImplementedError()
|
||||
@ -41,7 +41,7 @@ class BaseUserDatabase(Generic[UP, ID]):
|
||||
self: "BaseUserDatabase[UOAP, ID]",
|
||||
user: UOAP,
|
||||
oauth_account: OAP,
|
||||
update_dict: Dict[str, Any],
|
||||
update_dict: dict[str, Any],
|
||||
) -> UOAP:
|
||||
"""Update an OAuth account on a user."""
|
||||
raise NotImplementedError()
|
||||
|
@ -1,4 +1,5 @@
|
||||
from typing import Generic, Optional, Sequence, Type
|
||||
from collections.abc import Sequence
|
||||
from typing import Generic, Optional
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
@ -20,7 +21,7 @@ try:
|
||||
from fastapi_users.router import get_oauth_router
|
||||
from fastapi_users.router.oauth import get_oauth_associate_router
|
||||
except ModuleNotFoundError: # pragma: no cover
|
||||
BaseOAuth2 = Type # type: ignore
|
||||
BaseOAuth2 = type # type: ignore
|
||||
|
||||
|
||||
class FastAPIUsers(Generic[models.UP, models.ID]):
|
||||
@ -47,7 +48,7 @@ class FastAPIUsers(Generic[models.UP, models.ID]):
|
||||
self.current_user = self.authenticator.current_user
|
||||
|
||||
def get_register_router(
|
||||
self, user_schema: Type[schemas.U], user_create_schema: Type[schemas.UC]
|
||||
self, user_schema: type[schemas.U], user_create_schema: type[schemas.UC]
|
||||
) -> APIRouter:
|
||||
"""
|
||||
Return a router with a register route.
|
||||
@ -59,7 +60,7 @@ class FastAPIUsers(Generic[models.UP, models.ID]):
|
||||
self.get_user_manager, user_schema, user_create_schema
|
||||
)
|
||||
|
||||
def get_verify_router(self, user_schema: Type[schemas.U]) -> APIRouter:
|
||||
def get_verify_router(self, user_schema: type[schemas.U]) -> APIRouter:
|
||||
"""
|
||||
Return a router with e-mail verification routes.
|
||||
|
||||
@ -126,7 +127,7 @@ class FastAPIUsers(Generic[models.UP, models.ID]):
|
||||
def get_oauth_associate_router(
|
||||
self,
|
||||
oauth_client: BaseOAuth2,
|
||||
user_schema: Type[schemas.U],
|
||||
user_schema: type[schemas.U],
|
||||
state_secret: SecretType,
|
||||
redirect_url: Optional[str] = None,
|
||||
requires_verification: bool = False,
|
||||
@ -154,8 +155,8 @@ class FastAPIUsers(Generic[models.UP, models.ID]):
|
||||
|
||||
def get_users_router(
|
||||
self,
|
||||
user_schema: Type[schemas.U],
|
||||
user_update_schema: Type[schemas.UU],
|
||||
user_schema: type[schemas.U],
|
||||
user_update_schema: type[schemas.UU],
|
||||
requires_verification: bool = False,
|
||||
) -> APIRouter:
|
||||
"""
|
||||
|
@ -1,5 +1,5 @@
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
from typing import Any, Optional, Union
|
||||
|
||||
import jwt
|
||||
from pydantic import SecretStr
|
||||
@ -30,9 +30,9 @@ def generate_jwt(
|
||||
def decode_jwt(
|
||||
encoded_jwt: str,
|
||||
secret: SecretType,
|
||||
audience: List[str],
|
||||
algorithms: List[str] = [JWT_ALGORITHM],
|
||||
) -> Dict[str, Any]:
|
||||
audience: list[str],
|
||||
algorithms: list[str] = [JWT_ALGORITHM],
|
||||
) -> dict[str, Any]:
|
||||
return jwt.decode(
|
||||
encoded_jwt,
|
||||
_get_secret_value(secret),
|
||||
|
@ -1,5 +1,5 @@
|
||||
import uuid
|
||||
from typing import Any, Dict, Generic, Optional, Union
|
||||
from typing import Any, Generic, Optional, Union
|
||||
|
||||
import jwt
|
||||
from fastapi import Request, Response
|
||||
@ -514,7 +514,7 @@ class BaseUserManager(Generic[models.UP, models.ID]):
|
||||
async def on_after_update(
|
||||
self,
|
||||
user: models.UP,
|
||||
update_dict: Dict[str, Any],
|
||||
update_dict: dict[str, Any],
|
||||
request: Optional[Request] = None,
|
||||
) -> None:
|
||||
"""
|
||||
@ -662,7 +662,7 @@ class BaseUserManager(Generic[models.UP, models.ID]):
|
||||
|
||||
return user
|
||||
|
||||
async def _update(self, user: models.UP, update_dict: Dict[str, Any]) -> models.UP:
|
||||
async def _update(self, user: models.UP, update_dict: dict[str, Any]) -> models.UP:
|
||||
validated_update_dict = {}
|
||||
for field, value in update_dict.items():
|
||||
if field == "email" and value != user.email:
|
||||
|
@ -1,4 +1,4 @@
|
||||
from typing import Generic, List, Optional, Protocol, TypeVar
|
||||
from typing import Generic, Optional, Protocol, TypeVar
|
||||
|
||||
ID = TypeVar("ID")
|
||||
|
||||
@ -39,7 +39,7 @@ class UserOAuthProtocol(UserProtocol[ID], Generic[ID, OAP]):
|
||||
is_active: bool
|
||||
is_superuser: bool
|
||||
is_verified: bool
|
||||
oauth_accounts: List[OAP]
|
||||
oauth_accounts: list[OAP]
|
||||
|
||||
|
||||
UOAP = TypeVar("UOAP", bound=UserOAuthProtocol)
|
||||
|
@ -1,3 +1,3 @@
|
||||
from typing import Any, Dict, Union
|
||||
from typing import Any, Union
|
||||
|
||||
OpenAPIResponseType = Dict[Union[int, str], Dict[str, Any]]
|
||||
OpenAPIResponseType = dict[Union[int, str], dict[str, Any]]
|
||||
|
@ -1,5 +1,5 @@
|
||||
import secrets
|
||||
from typing import Optional, Protocol, Tuple, Union
|
||||
from typing import Optional, Protocol, Union
|
||||
|
||||
from pwdlib import PasswordHash
|
||||
from pwdlib.hashers.argon2 import Argon2Hasher
|
||||
@ -9,7 +9,7 @@ from pwdlib.hashers.bcrypt import BcryptHasher
|
||||
class PasswordHelperProtocol(Protocol):
|
||||
def verify_and_update(
|
||||
self, plain_password: str, hashed_password: str
|
||||
) -> Tuple[bool, Union[str, None]]: ... # pragma: no cover
|
||||
) -> tuple[bool, Union[str, None]]: ... # pragma: no cover
|
||||
|
||||
def hash(self, password: str) -> str: ... # pragma: no cover
|
||||
|
||||
@ -30,7 +30,7 @@ class PasswordHelper(PasswordHelperProtocol):
|
||||
|
||||
def verify_and_update(
|
||||
self, plain_password: str, hashed_password: str
|
||||
) -> Tuple[bool, Union[str, None]]:
|
||||
) -> tuple[bool, Union[str, None]]:
|
||||
return self.password_hash.verify_and_update(plain_password, hashed_password)
|
||||
|
||||
def hash(self, password: str) -> str:
|
||||
|
@ -1,5 +1,3 @@
|
||||
from typing import Tuple
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
|
||||
@ -83,7 +81,7 @@ def get_auth_router(
|
||||
"/logout", name=f"auth:{backend.name}.logout", responses=logout_responses
|
||||
)
|
||||
async def logout(
|
||||
user_token: Tuple[models.UP, str] = Depends(get_current_user_token),
|
||||
user_token: tuple[models.UP, str] = Depends(get_current_user_token),
|
||||
strategy: Strategy[models.UP, models.ID] = Depends(backend.get_strategy),
|
||||
):
|
||||
user, token = user_token
|
||||
|
@ -1,11 +1,11 @@
|
||||
from enum import Enum
|
||||
from typing import Dict, Union
|
||||
from typing import Union
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class ErrorModel(BaseModel):
|
||||
detail: Union[str, Dict[str, str]]
|
||||
detail: Union[str, dict[str, str]]
|
||||
|
||||
|
||||
class ErrorCodeReasonModel(BaseModel):
|
||||
|
@ -1,4 +1,4 @@
|
||||
from typing import Dict, List, Optional, Tuple, Type
|
||||
from typing import Optional
|
||||
|
||||
import jwt
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Request, status
|
||||
@ -21,7 +21,7 @@ class OAuth2AuthorizeResponse(BaseModel):
|
||||
|
||||
|
||||
def generate_state_token(
|
||||
data: Dict[str, str], secret: SecretType, lifetime_seconds: int = 3600
|
||||
data: dict[str, str], secret: SecretType, lifetime_seconds: int = 3600
|
||||
) -> str:
|
||||
data["aud"] = STATE_TOKEN_AUDIENCE
|
||||
return generate_jwt(data, secret, lifetime_seconds)
|
||||
@ -57,14 +57,14 @@ def get_oauth_router(
|
||||
response_model=OAuth2AuthorizeResponse,
|
||||
)
|
||||
async def authorize(
|
||||
request: Request, scopes: List[str] = Query(None)
|
||||
request: Request, scopes: list[str] = Query(None)
|
||||
) -> OAuth2AuthorizeResponse:
|
||||
if redirect_url is not None:
|
||||
authorize_redirect_url = redirect_url
|
||||
else:
|
||||
authorize_redirect_url = str(request.url_for(callback_route_name))
|
||||
|
||||
state_data: Dict[str, str] = {}
|
||||
state_data: dict[str, str] = {}
|
||||
state = generate_state_token(state_data, state_secret)
|
||||
authorization_url = await oauth_client.get_authorization_url(
|
||||
authorize_redirect_url,
|
||||
@ -100,7 +100,7 @@ def get_oauth_router(
|
||||
)
|
||||
async def callback(
|
||||
request: Request,
|
||||
access_token_state: Tuple[OAuth2Token, str] = Depends(
|
||||
access_token_state: tuple[OAuth2Token, str] = Depends(
|
||||
oauth2_authorize_callback
|
||||
),
|
||||
user_manager: BaseUserManager[models.UP, models.ID] = Depends(get_user_manager),
|
||||
@ -158,7 +158,7 @@ def get_oauth_associate_router(
|
||||
oauth_client: BaseOAuth2,
|
||||
authenticator: Authenticator[models.UP, models.ID],
|
||||
get_user_manager: UserManagerDependency[models.UP, models.ID],
|
||||
user_schema: Type[schemas.U],
|
||||
user_schema: type[schemas.U],
|
||||
state_secret: SecretType,
|
||||
redirect_url: Optional[str] = None,
|
||||
requires_verification: bool = False,
|
||||
@ -190,7 +190,7 @@ def get_oauth_associate_router(
|
||||
)
|
||||
async def authorize(
|
||||
request: Request,
|
||||
scopes: List[str] = Query(None),
|
||||
scopes: list[str] = Query(None),
|
||||
user: models.UP = Depends(get_current_active_user),
|
||||
) -> OAuth2AuthorizeResponse:
|
||||
if redirect_url is not None:
|
||||
@ -198,7 +198,7 @@ def get_oauth_associate_router(
|
||||
else:
|
||||
authorize_redirect_url = str(request.url_for(callback_route_name))
|
||||
|
||||
state_data: Dict[str, str] = {"sub": str(user.id)}
|
||||
state_data: dict[str, str] = {"sub": str(user.id)}
|
||||
state = generate_state_token(state_data, state_secret)
|
||||
authorization_url = await oauth_client.get_authorization_url(
|
||||
authorize_redirect_url,
|
||||
@ -232,7 +232,7 @@ def get_oauth_associate_router(
|
||||
async def callback(
|
||||
request: Request,
|
||||
user: models.UP = Depends(get_current_active_user),
|
||||
access_token_state: Tuple[OAuth2Token, str] = Depends(
|
||||
access_token_state: tuple[OAuth2Token, str] = Depends(
|
||||
oauth2_authorize_callback
|
||||
),
|
||||
user_manager: BaseUserManager[models.UP, models.ID] = Depends(get_user_manager),
|
||||
|
@ -1,5 +1,3 @@
|
||||
from typing import Type
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
|
||||
from fastapi_users import exceptions, models, schemas
|
||||
@ -9,8 +7,8 @@ from fastapi_users.router.common import ErrorCode, ErrorModel
|
||||
|
||||
def get_register_router(
|
||||
get_user_manager: UserManagerDependency[models.UP, models.ID],
|
||||
user_schema: Type[schemas.U],
|
||||
user_create_schema: Type[schemas.UC],
|
||||
user_schema: type[schemas.U],
|
||||
user_create_schema: type[schemas.UC],
|
||||
) -> APIRouter:
|
||||
"""Generate a router with the register route."""
|
||||
router = APIRouter()
|
||||
|
@ -1,5 +1,3 @@
|
||||
from typing import Type
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, Response, status
|
||||
|
||||
from fastapi_users import exceptions, models, schemas
|
||||
@ -10,8 +8,8 @@ from fastapi_users.router.common import ErrorCode, ErrorModel
|
||||
|
||||
def get_users_router(
|
||||
get_user_manager: UserManagerDependency[models.UP, models.ID],
|
||||
user_schema: Type[schemas.U],
|
||||
user_update_schema: Type[schemas.UU],
|
||||
user_schema: type[schemas.U],
|
||||
user_update_schema: type[schemas.UU],
|
||||
authenticator: Authenticator[models.UP, models.ID],
|
||||
requires_verification: bool = False,
|
||||
) -> APIRouter:
|
||||
|
@ -1,5 +1,3 @@
|
||||
from typing import Type
|
||||
|
||||
from fastapi import APIRouter, Body, Depends, HTTPException, Request, status
|
||||
from pydantic import EmailStr
|
||||
|
||||
@ -10,7 +8,7 @@ from fastapi_users.router.common import ErrorCode, ErrorModel
|
||||
|
||||
def get_verify_router(
|
||||
get_user_manager: UserManagerDependency[models.UP, models.ID],
|
||||
user_schema: Type[schemas.U],
|
||||
user_schema: type[schemas.U],
|
||||
):
|
||||
router = APIRouter()
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
from typing import Any, Dict, Generic, List, Optional, Type, TypeVar
|
||||
from typing import Any, Generic, Optional, TypeVar
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, EmailStr
|
||||
from pydantic.version import VERSION as PYDANTIC_VERSION
|
||||
@ -11,18 +11,18 @@ SCHEMA = TypeVar("SCHEMA", bound=BaseModel)
|
||||
|
||||
if PYDANTIC_V2: # pragma: no cover
|
||||
|
||||
def model_dump(model: BaseModel, *args, **kwargs) -> Dict[str, Any]:
|
||||
def model_dump(model: BaseModel, *args, **kwargs) -> dict[str, Any]:
|
||||
return model.model_dump(*args, **kwargs) # type: ignore
|
||||
|
||||
def model_validate(schema: Type[SCHEMA], obj: Any, *args, **kwargs) -> SCHEMA:
|
||||
def model_validate(schema: type[SCHEMA], obj: Any, *args, **kwargs) -> SCHEMA:
|
||||
return schema.model_validate(obj, *args, **kwargs) # type: ignore
|
||||
|
||||
else: # pragma: no cover # type: ignore
|
||||
|
||||
def model_dump(model: BaseModel, *args, **kwargs) -> Dict[str, Any]:
|
||||
def model_dump(model: BaseModel, *args, **kwargs) -> dict[str, Any]:
|
||||
return model.dict(*args, **kwargs) # type: ignore
|
||||
|
||||
def model_validate(schema: Type[SCHEMA], obj: Any, *args, **kwargs) -> SCHEMA:
|
||||
def model_validate(schema: type[SCHEMA], obj: Any, *args, **kwargs) -> SCHEMA:
|
||||
return schema.from_orm(obj) # type: ignore
|
||||
|
||||
|
||||
@ -104,4 +104,4 @@ class BaseOAuthAccount(BaseModel, Generic[models.ID]):
|
||||
class BaseOAuthAccountMixin(BaseModel):
|
||||
"""Adds OAuth accounts list to a User model."""
|
||||
|
||||
oauth_accounts: List[BaseOAuthAccount] = []
|
||||
oauth_accounts: list[BaseOAuthAccount] = []
|
||||
|
@ -1,12 +1,5 @@
|
||||
from typing import (
|
||||
AsyncGenerator,
|
||||
AsyncIterator,
|
||||
Callable,
|
||||
Coroutine,
|
||||
Generator,
|
||||
TypeVar,
|
||||
Union,
|
||||
)
|
||||
from collections.abc import AsyncGenerator, AsyncIterator, Coroutine, Generator
|
||||
from typing import Callable, TypeVar, Union
|
||||
|
||||
RETURN_TYPE = TypeVar("RETURN_TYPE")
|
||||
|
||||
|
@ -32,7 +32,7 @@ markers = [
|
||||
]
|
||||
|
||||
[tool.ruff]
|
||||
target-version = "py38"
|
||||
target-version = "py39"
|
||||
|
||||
[tool.ruff.lint]
|
||||
extend-select = ["UP", "TRY"]
|
||||
@ -132,7 +132,6 @@ classifiers = [
|
||||
"Framework :: FastAPI",
|
||||
"Framework :: AsyncIO",
|
||||
"Intended Audience :: Developers",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
@ -140,7 +139,7 @@ classifiers = [
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
"Topic :: Internet :: WWW/HTTP :: Session",
|
||||
]
|
||||
requires-python = ">=3.8"
|
||||
requires-python = ">=3.9"
|
||||
dependencies = [
|
||||
"fastapi >=0.65.2",
|
||||
"pwdlib[argon2,bcrypt] ==0.2.0",
|
||||
|
@ -1,17 +1,8 @@
|
||||
import asyncio
|
||||
import dataclasses
|
||||
import uuid
|
||||
from typing import (
|
||||
Any,
|
||||
AsyncGenerator,
|
||||
Callable,
|
||||
Dict,
|
||||
Generic,
|
||||
List,
|
||||
Optional,
|
||||
Type,
|
||||
Union,
|
||||
)
|
||||
from collections.abc import AsyncGenerator
|
||||
from typing import Any, Callable, Generic, Optional, Union
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import httpx
|
||||
@ -66,7 +57,7 @@ class OAuthAccountModel(models.OAuthAccountProtocol[IDType]):
|
||||
|
||||
@dataclasses.dataclass
|
||||
class UserOAuthModel(UserModel):
|
||||
oauth_accounts: List[OAuthAccountModel] = dataclasses.field(default_factory=list)
|
||||
oauth_accounts: list[OAuthAccountModel] = dataclasses.field(default_factory=list)
|
||||
|
||||
|
||||
class User(schemas.BaseUser[IDType]):
|
||||
@ -344,11 +335,11 @@ def mock_user_db(
|
||||
return verified_superuser
|
||||
return None
|
||||
|
||||
async def create(self, create_dict: Dict[str, Any]) -> UserModel:
|
||||
async def create(self, create_dict: dict[str, Any]) -> UserModel:
|
||||
return UserModel(**create_dict)
|
||||
|
||||
async def update(
|
||||
self, user: UserModel, update_dict: Dict[str, Any]
|
||||
self, user: UserModel, update_dict: dict[str, Any]
|
||||
) -> UserModel:
|
||||
for field, value in update_dict.items():
|
||||
setattr(user, field, value)
|
||||
@ -414,11 +405,11 @@ def mock_user_db_oauth(
|
||||
return inactive_user_oauth
|
||||
return None
|
||||
|
||||
async def create(self, create_dict: Dict[str, Any]) -> UserOAuthModel:
|
||||
async def create(self, create_dict: dict[str, Any]) -> UserOAuthModel:
|
||||
return UserOAuthModel(**create_dict)
|
||||
|
||||
async def update(
|
||||
self, user: UserOAuthModel, update_dict: Dict[str, Any]
|
||||
self, user: UserOAuthModel, update_dict: dict[str, Any]
|
||||
) -> UserOAuthModel:
|
||||
for field, value in update_dict.items():
|
||||
setattr(user, field, value)
|
||||
@ -428,7 +419,7 @@ def mock_user_db_oauth(
|
||||
pass
|
||||
|
||||
async def add_oauth_account(
|
||||
self, user: UserOAuthModel, create_dict: Dict[str, Any]
|
||||
self, user: UserOAuthModel, create_dict: dict[str, Any]
|
||||
) -> UserOAuthModel:
|
||||
oauth_account = OAuthAccountModel(**create_dict)
|
||||
user.oauth_accounts.append(oauth_account)
|
||||
@ -438,7 +429,7 @@ def mock_user_db_oauth(
|
||||
self,
|
||||
user: UserOAuthModel,
|
||||
oauth_account: OAuthAccountModel,
|
||||
update_dict: Dict[str, Any],
|
||||
update_dict: dict[str, Any],
|
||||
) -> UserOAuthModel:
|
||||
for field, value in update_dict.items():
|
||||
setattr(oauth_account, field, value)
|
||||
@ -458,7 +449,7 @@ def mock_user_db_oauth(
|
||||
|
||||
@pytest.fixture
|
||||
def make_user_manager(mocker: MockerFixture):
|
||||
def _make_user_manager(user_manager_class: Type[BaseTestUserManager], mock_user_db):
|
||||
def _make_user_manager(user_manager_class: type[BaseTestUserManager], mock_user_db):
|
||||
user_manager = user_manager_class(mock_user_db)
|
||||
mocker.spy(user_manager, "get_by_email")
|
||||
mocker.spy(user_manager, "request_verify")
|
||||
|
@ -1,4 +1,5 @@
|
||||
from typing import AsyncGenerator, Generic, List, Optional, Sequence
|
||||
from collections.abc import AsyncGenerator, Sequence
|
||||
from typing import Generic, Optional
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
@ -70,7 +71,7 @@ def get_backend_user(user: UserModel):
|
||||
@pytest_asyncio.fixture
|
||||
def get_test_auth_client(get_user_manager, get_test_client):
|
||||
async def _get_test_auth_client(
|
||||
backends: List[AuthenticationBackend],
|
||||
backends: list[AuthenticationBackend],
|
||||
get_enabled_backends: Optional[
|
||||
DependencyCallable[Sequence[AuthenticationBackend]]
|
||||
] = None,
|
||||
|
@ -1,4 +1,4 @@
|
||||
from typing import Callable, Generic, Optional, Type, cast
|
||||
from typing import Callable, Generic, Optional, cast
|
||||
|
||||
import pytest
|
||||
from fastapi import Response
|
||||
@ -34,13 +34,13 @@ class MockStrategyDestroyNotSupported(Strategy, Generic[models.UP]):
|
||||
|
||||
@pytest.fixture(params=[MockTransport, MockTransportLogoutNotSupported])
|
||||
def transport(request) -> Transport:
|
||||
transport_class: Type[BearerTransport] = request.param
|
||||
transport_class: type[BearerTransport] = request.param
|
||||
return transport_class(tokenUrl="/login")
|
||||
|
||||
|
||||
@pytest.fixture(params=[MockStrategy, MockStrategyDestroyNotSupported])
|
||||
def get_strategy(request) -> Callable[..., Strategy]:
|
||||
strategy_class: Type[Strategy] = request.param
|
||||
strategy_class: type[Strategy] = request.param
|
||||
return lambda: strategy_class()
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import dataclasses
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any, Dict, Optional
|
||||
from typing import Any, Optional
|
||||
|
||||
import pytest
|
||||
|
||||
@ -24,7 +24,7 @@ class AccessTokenModel(AccessTokenProtocol[IDType]):
|
||||
|
||||
|
||||
class AccessTokenDatabaseMock(AccessTokenDatabase[AccessTokenModel]):
|
||||
store: Dict[str, AccessTokenModel]
|
||||
store: dict[str, AccessTokenModel]
|
||||
|
||||
def __init__(self):
|
||||
self.store = {}
|
||||
@ -41,13 +41,13 @@ class AccessTokenDatabaseMock(AccessTokenDatabase[AccessTokenModel]):
|
||||
else:
|
||||
return access_token
|
||||
|
||||
async def create(self, create_dict: Dict[str, Any]) -> AccessTokenModel:
|
||||
async def create(self, create_dict: dict[str, Any]) -> AccessTokenModel:
|
||||
access_token = AccessTokenModel(**create_dict)
|
||||
self.store[access_token.token] = access_token
|
||||
return access_token
|
||||
|
||||
async def update(
|
||||
self, access_token: AccessTokenModel, update_dict: Dict[str, Any]
|
||||
self, access_token: AccessTokenModel, update_dict: dict[str, Any]
|
||||
) -> AccessTokenModel:
|
||||
for field, value in update_dict.items():
|
||||
setattr(access_token, field, value)
|
||||
|
@ -1,5 +1,5 @@
|
||||
from datetime import datetime
|
||||
from typing import Dict, Optional, Tuple
|
||||
from typing import Optional
|
||||
|
||||
import pytest
|
||||
|
||||
@ -8,7 +8,7 @@ from tests.conftest import IDType, UserModel
|
||||
|
||||
|
||||
class RedisMock:
|
||||
store: Dict[str, Tuple[str, Optional[int]]]
|
||||
store: dict[str, tuple[str, Optional[int]]]
|
||||
|
||||
def __init__(self):
|
||||
self.store = {}
|
||||
|
@ -1,4 +1,5 @@
|
||||
from typing import AsyncGenerator, Optional
|
||||
from collections.abc import AsyncGenerator
|
||||
from typing import Optional
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
@ -1,4 +1,5 @@
|
||||
from typing import Any, AsyncGenerator, Dict, Tuple, cast
|
||||
from collections.abc import AsyncGenerator
|
||||
from typing import Any, cast
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
@ -45,7 +46,7 @@ def app_factory(get_user_manager, mock_authentication):
|
||||
)
|
||||
async def test_app_client(
|
||||
request, get_test_client, app_factory
|
||||
) -> AsyncGenerator[Tuple[httpx.AsyncClient, bool], None]:
|
||||
) -> AsyncGenerator[tuple[httpx.AsyncClient, bool], None]:
|
||||
requires_verification = request.param
|
||||
app = app_factory(requires_verification)
|
||||
|
||||
@ -60,7 +61,7 @@ class TestLogin:
|
||||
async def test_empty_body(
|
||||
self,
|
||||
path,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user_manager,
|
||||
):
|
||||
client, _ = test_app_client
|
||||
@ -71,7 +72,7 @@ class TestLogin:
|
||||
async def test_missing_username(
|
||||
self,
|
||||
path,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user_manager,
|
||||
):
|
||||
client, _ = test_app_client
|
||||
@ -83,7 +84,7 @@ class TestLogin:
|
||||
async def test_missing_password(
|
||||
self,
|
||||
path,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user_manager,
|
||||
):
|
||||
client, _ = test_app_client
|
||||
@ -95,28 +96,28 @@ class TestLogin:
|
||||
async def test_not_existing_user(
|
||||
self,
|
||||
path,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user_manager,
|
||||
):
|
||||
client, _ = test_app_client
|
||||
data = {"username": "lancelot@camelot.bt", "password": "guinevere"}
|
||||
response = await client.post(path, data=data)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["detail"] == ErrorCode.LOGIN_BAD_CREDENTIALS
|
||||
assert user_manager.on_after_login.called is False
|
||||
|
||||
async def test_wrong_password(
|
||||
self,
|
||||
path,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user_manager,
|
||||
):
|
||||
client, _ = test_app_client
|
||||
data = {"username": "king.arthur@camelot.bt", "password": "percival"}
|
||||
response = await client.post(path, data=data)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["detail"] == ErrorCode.LOGIN_BAD_CREDENTIALS
|
||||
assert user_manager.on_after_login.called is False
|
||||
|
||||
@ -127,7 +128,7 @@ class TestLogin:
|
||||
self,
|
||||
path,
|
||||
email,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user_manager,
|
||||
user: UserModel,
|
||||
):
|
||||
@ -136,7 +137,7 @@ class TestLogin:
|
||||
response = await client.post(path, data=data)
|
||||
if requires_verification:
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["detail"] == ErrorCode.LOGIN_USER_NOT_VERIFIED
|
||||
assert user_manager.on_after_login.called is False
|
||||
else:
|
||||
@ -152,7 +153,7 @@ class TestLogin:
|
||||
self,
|
||||
path,
|
||||
email,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user_manager,
|
||||
verified_user: UserModel,
|
||||
):
|
||||
@ -172,14 +173,14 @@ class TestLogin:
|
||||
async def test_inactive_user(
|
||||
self,
|
||||
path,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user_manager,
|
||||
):
|
||||
client, _ = test_app_client
|
||||
data = {"username": "percival@camelot.bt", "password": "angharad"}
|
||||
response = await client.post(path, data=data)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["detail"] == ErrorCode.LOGIN_BAD_CREDENTIALS
|
||||
assert user_manager.on_after_login.called is False
|
||||
|
||||
@ -191,7 +192,7 @@ class TestLogout:
|
||||
async def test_missing_token(
|
||||
self,
|
||||
path,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
):
|
||||
client, _ = test_app_client
|
||||
response = await client.post(path)
|
||||
@ -201,7 +202,7 @@ class TestLogout:
|
||||
self,
|
||||
mocker,
|
||||
path,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
):
|
||||
client, requires_verification = test_app_client
|
||||
@ -217,7 +218,7 @@ class TestLogout:
|
||||
self,
|
||||
mocker,
|
||||
path,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
verified_user: UserModel,
|
||||
):
|
||||
client, _ = test_app_client
|
||||
|
@ -1,4 +1,4 @@
|
||||
from typing import Any, Dict, cast
|
||||
from typing import Any, cast
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
@ -184,7 +184,7 @@ class TestCallback:
|
||||
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["detail"] == ErrorCode.OAUTH_USER_ALREADY_EXISTS
|
||||
|
||||
assert user_manager_oauth.on_after_login.called is False
|
||||
@ -214,7 +214,7 @@ class TestCallback:
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["access_token"] == str(user_oauth.id)
|
||||
|
||||
assert user_manager_oauth.on_after_login.called is True
|
||||
@ -278,7 +278,7 @@ class TestCallback:
|
||||
"CODE", "http://www.tintagel.bt/callback", None
|
||||
)
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["access_token"] == str(user_oauth.id)
|
||||
assert user_manager_oauth.on_after_login.called is True
|
||||
|
||||
@ -486,7 +486,7 @@ class TestAssociateCallback:
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["id"] == str(user_oauth.id)
|
||||
|
||||
async def test_redirect_url_router(
|
||||
@ -521,7 +521,7 @@ class TestAssociateCallback:
|
||||
"CODE", "http://www.tintagel.bt/callback", None
|
||||
)
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["id"] == str(user_oauth.id)
|
||||
|
||||
async def test_not_available_email(
|
||||
|
@ -1,4 +1,5 @@
|
||||
from typing import Any, AsyncGenerator, Dict, cast
|
||||
from collections.abc import AsyncGenerator
|
||||
from typing import Any, cast
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
@ -52,7 +53,7 @@ class TestRegister:
|
||||
json = {"email": "king.arthur@camelot.bt", "password": "g"}
|
||||
response = await test_app_client.post("/register", json=json)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["detail"] == {
|
||||
"code": ErrorCode.REGISTER_INVALID_PASSWORD,
|
||||
"reason": "Password should be at least 3 characters",
|
||||
@ -65,7 +66,7 @@ class TestRegister:
|
||||
json = {"email": email, "password": "guinevere"}
|
||||
response = await test_app_client.post("/register", json=json)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["detail"] == ErrorCode.REGISTER_USER_ALREADY_EXISTS
|
||||
|
||||
@pytest.mark.parametrize("email", ["lancelot@camelot.bt", "Lancelot@camelot.bt"])
|
||||
@ -74,7 +75,7 @@ class TestRegister:
|
||||
response = await test_app_client.post("/register", json=json)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert "hashed_password" not in data
|
||||
assert "password" not in data
|
||||
assert data["id"] is not None
|
||||
@ -88,7 +89,7 @@ class TestRegister:
|
||||
response = await test_app_client.post("/register", json=json)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["is_superuser"] is False
|
||||
|
||||
async def test_valid_body_is_active(self, test_app_client: httpx.AsyncClient):
|
||||
@ -100,7 +101,7 @@ class TestRegister:
|
||||
response = await test_app_client.post("/register", json=json)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["is_active"] is True
|
||||
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
from typing import Any, AsyncGenerator, Dict, cast
|
||||
from collections.abc import AsyncGenerator
|
||||
from typing import Any, cast
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
@ -106,7 +107,7 @@ class TestResetPassword:
|
||||
json = {"token": "foo", "password": "guinevere"}
|
||||
response = await test_app_client.post("/reset-password", json=json)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["detail"] == ErrorCode.RESET_PASSWORD_BAD_TOKEN
|
||||
|
||||
async def test_inactive_user(
|
||||
@ -118,7 +119,7 @@ class TestResetPassword:
|
||||
json = {"token": "foo", "password": "guinevere"}
|
||||
response = await test_app_client.post("/reset-password", json=json)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["detail"] == ErrorCode.RESET_PASSWORD_BAD_TOKEN
|
||||
|
||||
async def test_invalid_password(
|
||||
@ -132,7 +133,7 @@ class TestResetPassword:
|
||||
json = {"token": "foo", "password": "guinevere"}
|
||||
response = await test_app_client.post("/reset-password", json=json)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["detail"] == {
|
||||
"code": ErrorCode.RESET_PASSWORD_INVALID_PASSWORD,
|
||||
"reason": "Invalid",
|
||||
|
@ -1,4 +1,5 @@
|
||||
from typing import Any, AsyncGenerator, Dict, Tuple, cast
|
||||
from collections.abc import AsyncGenerator
|
||||
from typing import Any, cast
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
@ -39,7 +40,7 @@ def app_factory(get_user_manager, mock_authentication):
|
||||
)
|
||||
async def test_app_client(
|
||||
request, get_test_client, app_factory
|
||||
) -> AsyncGenerator[Tuple[httpx.AsyncClient, bool], None]:
|
||||
) -> AsyncGenerator[tuple[httpx.AsyncClient, bool], None]:
|
||||
requires_verification = request.param
|
||||
app = app_factory(requires_verification)
|
||||
|
||||
@ -50,14 +51,14 @@ async def test_app_client(
|
||||
@pytest.mark.router
|
||||
@pytest.mark.asyncio
|
||||
class TestMe:
|
||||
async def test_missing_token(self, test_app_client: Tuple[httpx.AsyncClient, bool]):
|
||||
async def test_missing_token(self, test_app_client: tuple[httpx.AsyncClient, bool]):
|
||||
client, _ = test_app_client
|
||||
response = await client.get("/me")
|
||||
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
||||
|
||||
async def test_inactive_user(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
inactive_user: UserModel,
|
||||
):
|
||||
client, _ = test_app_client
|
||||
@ -68,7 +69,7 @@ class TestMe:
|
||||
|
||||
async def test_active_user(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
):
|
||||
client, requires_verification = test_app_client
|
||||
@ -79,13 +80,13 @@ class TestMe:
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
else:
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["id"] == str(user.id)
|
||||
assert data["email"] == user.email
|
||||
|
||||
async def test_verified_user(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
verified_user: UserModel,
|
||||
):
|
||||
client, _ = test_app_client
|
||||
@ -93,7 +94,7 @@ class TestMe:
|
||||
"/me", headers={"Authorization": f"Bearer {verified_user.id}"}
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["id"] == str(verified_user.id)
|
||||
assert data["email"] == verified_user.email
|
||||
|
||||
@ -106,7 +107,7 @@ class TestMe:
|
||||
class TestUpdateMe:
|
||||
async def test_missing_token(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
):
|
||||
client, _ = test_app_client
|
||||
response = await client.patch("/me")
|
||||
@ -114,7 +115,7 @@ class TestUpdateMe:
|
||||
|
||||
async def test_inactive_user(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
inactive_user: UserModel,
|
||||
):
|
||||
client, _ = test_app_client
|
||||
@ -125,7 +126,7 @@ class TestUpdateMe:
|
||||
|
||||
async def test_existing_email(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
verified_user: UserModel,
|
||||
):
|
||||
@ -139,12 +140,12 @@ class TestUpdateMe:
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
else:
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["detail"] == ErrorCode.UPDATE_USER_EMAIL_ALREADY_EXISTS
|
||||
|
||||
async def test_invalid_password(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
):
|
||||
client, requires_verification = test_app_client
|
||||
@ -157,7 +158,7 @@ class TestUpdateMe:
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
else:
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["detail"] == {
|
||||
"code": ErrorCode.UPDATE_USER_INVALID_PASSWORD,
|
||||
"reason": "Password should be at least 3 characters",
|
||||
@ -165,7 +166,7 @@ class TestUpdateMe:
|
||||
|
||||
async def test_empty_body(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
):
|
||||
client, requires_verification = test_app_client
|
||||
@ -177,12 +178,12 @@ class TestUpdateMe:
|
||||
else:
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["email"] == user.email
|
||||
|
||||
async def test_valid_body(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
):
|
||||
client, requires_verification = test_app_client
|
||||
@ -195,12 +196,12 @@ class TestUpdateMe:
|
||||
else:
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["email"] == "king.arthur@tintagel.bt"
|
||||
|
||||
async def test_unverified_after_email_change(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
verified_user: UserModel,
|
||||
):
|
||||
client, _ = test_app_client
|
||||
@ -210,12 +211,12 @@ class TestUpdateMe:
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["is_verified"] is False
|
||||
|
||||
async def test_valid_body_is_superuser(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
):
|
||||
client, requires_verification = test_app_client
|
||||
@ -228,12 +229,12 @@ class TestUpdateMe:
|
||||
else:
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["is_superuser"] is False
|
||||
|
||||
async def test_valid_body_is_active(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
):
|
||||
client, requires_verification = test_app_client
|
||||
@ -246,12 +247,12 @@ class TestUpdateMe:
|
||||
else:
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["is_active"] is True
|
||||
|
||||
async def test_valid_body_is_verified(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
):
|
||||
client, requires_verification = test_app_client
|
||||
@ -264,14 +265,14 @@ class TestUpdateMe:
|
||||
else:
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["is_verified"] is False
|
||||
|
||||
async def test_valid_body_password(
|
||||
self,
|
||||
mocker,
|
||||
mock_user_db,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
):
|
||||
client, requires_verification = test_app_client
|
||||
@ -293,7 +294,7 @@ class TestUpdateMe:
|
||||
|
||||
async def test_empty_body_verified_user(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
verified_user: UserModel,
|
||||
):
|
||||
client, _ = test_app_client
|
||||
@ -302,12 +303,12 @@ class TestUpdateMe:
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["email"] == verified_user.email
|
||||
|
||||
async def test_valid_body_verified_user(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
verified_user: UserModel,
|
||||
):
|
||||
client, _ = test_app_client
|
||||
@ -317,12 +318,12 @@ class TestUpdateMe:
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["email"] == "king.arthur@tintagel.bt"
|
||||
|
||||
async def test_valid_body_is_superuser_verified_user(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
verified_user: UserModel,
|
||||
):
|
||||
client, _ = test_app_client
|
||||
@ -332,12 +333,12 @@ class TestUpdateMe:
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["is_superuser"] is False
|
||||
|
||||
async def test_valid_body_is_active_verified_user(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
verified_user: UserModel,
|
||||
):
|
||||
client, _ = test_app_client
|
||||
@ -347,12 +348,12 @@ class TestUpdateMe:
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["is_active"] is True
|
||||
|
||||
async def test_valid_body_is_verified_verified_user(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
verified_user: UserModel,
|
||||
):
|
||||
client, _ = test_app_client
|
||||
@ -362,14 +363,14 @@ class TestUpdateMe:
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["is_verified"] is True
|
||||
|
||||
async def test_valid_body_password_verified_user(
|
||||
self,
|
||||
mocker,
|
||||
mock_user_db,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
verified_user: UserModel,
|
||||
):
|
||||
client, _ = test_app_client
|
||||
@ -390,14 +391,14 @@ class TestUpdateMe:
|
||||
@pytest.mark.router
|
||||
@pytest.mark.asyncio
|
||||
class TestGetUser:
|
||||
async def test_missing_token(self, test_app_client: Tuple[httpx.AsyncClient, bool]):
|
||||
async def test_missing_token(self, test_app_client: tuple[httpx.AsyncClient, bool]):
|
||||
client, _ = test_app_client
|
||||
response = await client.get("/d35d213e-f3d8-4f08-954a-7e0d1bea286f")
|
||||
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
||||
|
||||
async def test_regular_user(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
):
|
||||
client, requires_verification = test_app_client
|
||||
@ -410,7 +411,7 @@ class TestGetUser:
|
||||
|
||||
async def test_verified_user(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
verified_user: UserModel,
|
||||
):
|
||||
client, _ = test_app_client
|
||||
@ -422,7 +423,7 @@ class TestGetUser:
|
||||
|
||||
async def test_not_existing_user_unverified_superuser(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
superuser: UserModel,
|
||||
):
|
||||
client, requires_verification = test_app_client
|
||||
@ -437,7 +438,7 @@ class TestGetUser:
|
||||
|
||||
async def test_not_existing_user_verified_superuser(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
verified_superuser: UserModel,
|
||||
):
|
||||
client, _ = test_app_client
|
||||
@ -449,7 +450,7 @@ class TestGetUser:
|
||||
|
||||
async def test_superuser(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
superuser: UserModel,
|
||||
):
|
||||
@ -462,13 +463,13 @@ class TestGetUser:
|
||||
else:
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["id"] == str(user.id)
|
||||
assert "hashed_password" not in data
|
||||
|
||||
async def test_verified_superuser(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
verified_superuser: UserModel,
|
||||
):
|
||||
@ -478,7 +479,7 @@ class TestGetUser:
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["id"] == str(user.id)
|
||||
assert "hashed_password" not in data
|
||||
|
||||
@ -489,14 +490,14 @@ class TestGetUser:
|
||||
@pytest.mark.router
|
||||
@pytest.mark.asyncio
|
||||
class TestUpdateUser:
|
||||
async def test_missing_token(self, test_app_client: Tuple[httpx.AsyncClient, bool]):
|
||||
async def test_missing_token(self, test_app_client: tuple[httpx.AsyncClient, bool]):
|
||||
client, _ = test_app_client
|
||||
response = await client.patch("/d35d213e-f3d8-4f08-954a-7e0d1bea286f")
|
||||
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
||||
|
||||
async def test_regular_user(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
):
|
||||
client, requires_verification = test_app_client
|
||||
@ -509,7 +510,7 @@ class TestUpdateUser:
|
||||
|
||||
async def test_verified_user(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
verified_user: UserModel,
|
||||
):
|
||||
client, _ = test_app_client
|
||||
@ -521,7 +522,7 @@ class TestUpdateUser:
|
||||
|
||||
async def test_not_existing_user_unverified_superuser(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
superuser: UserModel,
|
||||
):
|
||||
client, requires_verification = test_app_client
|
||||
@ -537,7 +538,7 @@ class TestUpdateUser:
|
||||
|
||||
async def test_not_existing_user_verified_superuser(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
verified_superuser: UserModel,
|
||||
):
|
||||
client, _ = test_app_client
|
||||
@ -550,7 +551,7 @@ class TestUpdateUser:
|
||||
|
||||
async def test_empty_body_unverified_superuser(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
superuser: UserModel,
|
||||
):
|
||||
@ -563,12 +564,12 @@ class TestUpdateUser:
|
||||
else:
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["email"] == user.email
|
||||
|
||||
async def test_empty_body_verified_superuser(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
verified_superuser: UserModel,
|
||||
):
|
||||
@ -580,12 +581,12 @@ class TestUpdateUser:
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["email"] == user.email
|
||||
|
||||
async def test_valid_body_unverified_superuser(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
superuser: UserModel,
|
||||
):
|
||||
@ -601,12 +602,12 @@ class TestUpdateUser:
|
||||
else:
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["email"] == "king.arthur@tintagel.bt"
|
||||
|
||||
async def test_existing_email_verified_superuser(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
verified_user: UserModel,
|
||||
verified_superuser: UserModel,
|
||||
@ -618,12 +619,12 @@ class TestUpdateUser:
|
||||
headers={"Authorization": f"Bearer {verified_superuser.id}"},
|
||||
)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["detail"] == ErrorCode.UPDATE_USER_EMAIL_ALREADY_EXISTS
|
||||
|
||||
async def test_invalid_password_verified_superuser(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
verified_superuser: UserModel,
|
||||
):
|
||||
@ -634,7 +635,7 @@ class TestUpdateUser:
|
||||
headers={"Authorization": f"Bearer {verified_superuser.id}"},
|
||||
)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["detail"] == {
|
||||
"code": ErrorCode.UPDATE_USER_INVALID_PASSWORD,
|
||||
"reason": "Password should be at least 3 characters",
|
||||
@ -642,7 +643,7 @@ class TestUpdateUser:
|
||||
|
||||
async def test_valid_body_verified_superuser(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
verified_superuser: UserModel,
|
||||
):
|
||||
@ -655,12 +656,12 @@ class TestUpdateUser:
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["email"] == "king.arthur@tintagel.bt"
|
||||
|
||||
async def test_valid_body_is_superuser_unverified_superuser(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
superuser: UserModel,
|
||||
):
|
||||
@ -676,12 +677,12 @@ class TestUpdateUser:
|
||||
else:
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["is_superuser"] is True
|
||||
|
||||
async def test_valid_body_is_superuser_verified_superuser(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
verified_superuser: UserModel,
|
||||
):
|
||||
@ -694,12 +695,12 @@ class TestUpdateUser:
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["is_superuser"] is True
|
||||
|
||||
async def test_valid_body_is_active_unverified_superuser(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
superuser: UserModel,
|
||||
):
|
||||
@ -715,12 +716,12 @@ class TestUpdateUser:
|
||||
else:
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["is_active"] is False
|
||||
|
||||
async def test_valid_body_is_active_verified_superuser(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
verified_superuser: UserModel,
|
||||
):
|
||||
@ -733,12 +734,12 @@ class TestUpdateUser:
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["is_active"] is False
|
||||
|
||||
async def test_valid_body_is_verified_unverified_superuser(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
superuser: UserModel,
|
||||
):
|
||||
@ -754,12 +755,12 @@ class TestUpdateUser:
|
||||
else:
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["is_verified"] is True
|
||||
|
||||
async def test_valid_body_is_verified_verified_superuser(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
verified_superuser: UserModel,
|
||||
):
|
||||
@ -772,14 +773,14 @@ class TestUpdateUser:
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["is_verified"] is True
|
||||
|
||||
async def test_valid_body_password_unverified_superuser(
|
||||
self,
|
||||
mocker,
|
||||
mock_user_db,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
superuser: UserModel,
|
||||
):
|
||||
@ -806,7 +807,7 @@ class TestUpdateUser:
|
||||
self,
|
||||
mocker,
|
||||
mock_user_db,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
verified_superuser: UserModel,
|
||||
):
|
||||
@ -830,7 +831,7 @@ class TestUpdateUser:
|
||||
self,
|
||||
mocker,
|
||||
mock_user_db,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
superuser: UserModel,
|
||||
):
|
||||
@ -857,14 +858,14 @@ class TestUpdateUser:
|
||||
@pytest.mark.router
|
||||
@pytest.mark.asyncio
|
||||
class TestDeleteUser:
|
||||
async def test_missing_token(self, test_app_client: Tuple[httpx.AsyncClient, bool]):
|
||||
async def test_missing_token(self, test_app_client: tuple[httpx.AsyncClient, bool]):
|
||||
client, _ = test_app_client
|
||||
response = await client.delete("/d35d213e-f3d8-4f08-954a-7e0d1bea286f")
|
||||
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
||||
|
||||
async def test_regular_user(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
):
|
||||
client, requires_verification = test_app_client
|
||||
@ -877,7 +878,7 @@ class TestDeleteUser:
|
||||
|
||||
async def test_verified_user(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
verified_user: UserModel,
|
||||
):
|
||||
client, _ = test_app_client
|
||||
@ -889,7 +890,7 @@ class TestDeleteUser:
|
||||
|
||||
async def test_not_existing_user_unverified_superuser(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
superuser: UserModel,
|
||||
):
|
||||
client, requires_verification = test_app_client
|
||||
@ -904,7 +905,7 @@ class TestDeleteUser:
|
||||
|
||||
async def test_not_existing_user_verified_superuser(
|
||||
self,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
verified_superuser: UserModel,
|
||||
):
|
||||
client, _ = test_app_client
|
||||
@ -918,7 +919,7 @@ class TestDeleteUser:
|
||||
self,
|
||||
mocker,
|
||||
mock_user_db,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
superuser: UserModel,
|
||||
):
|
||||
@ -942,7 +943,7 @@ class TestDeleteUser:
|
||||
self,
|
||||
mocker,
|
||||
mock_user_db,
|
||||
test_app_client: Tuple[httpx.AsyncClient, bool],
|
||||
test_app_client: tuple[httpx.AsyncClient, bool],
|
||||
user: UserModel,
|
||||
verified_superuser: UserModel,
|
||||
):
|
||||
|
@ -1,4 +1,5 @@
|
||||
from typing import Any, AsyncGenerator, Dict, cast
|
||||
from collections.abc import AsyncGenerator
|
||||
from typing import Any, cast
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
@ -136,7 +137,7 @@ class TestVerify:
|
||||
response = await test_app_client.post("/verify", json={"token": "foo"})
|
||||
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["detail"] == ErrorCode.VERIFY_USER_BAD_TOKEN
|
||||
|
||||
async def test_user_not_exists(
|
||||
@ -148,7 +149,7 @@ class TestVerify:
|
||||
response = await test_app_client.post("/verify", json={"token": "foo"})
|
||||
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["detail"] == ErrorCode.VERIFY_USER_BAD_TOKEN
|
||||
|
||||
async def test_user_already_verified(
|
||||
@ -160,7 +161,7 @@ class TestVerify:
|
||||
response = await test_app_client.post("/verify", json={"token": "foo"})
|
||||
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["detail"] == ErrorCode.VERIFY_USER_ALREADY_VERIFIED
|
||||
|
||||
async def test_success(
|
||||
@ -174,7 +175,7 @@ class TestVerify:
|
||||
response = await test_app_client.post("/verify", json={"token": "foo"})
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = cast(Dict[str, Any], response.json())
|
||||
data = cast(dict[str, Any], response.json())
|
||||
assert data["id"] == str(user.id)
|
||||
|
||||
async def test_verify_namespace(
|
||||
|
Reference in New Issue
Block a user