From b42aa4d76d44364cc2e85363e18be01a492eb728 Mon Sep 17 00:00:00 2001 From: Leonardo Santiago Date: Fri, 31 Oct 2025 10:30:56 -0300 Subject: [PATCH 1/6] fix(auth): add more linting rules to ruff in order to catch untyped function return types, which cause mypy to infer `Any` and wreak havoc inside our codebase --- src/auth/pyproject.toml | 3 +- src/auth/scripts/gh-download.py | 4 +- src/auth/src/supabase_auth/__init__.py | 18 +-- .../supabase_auth/_async/gotrue_admin_api.py | 5 +- .../supabase_auth/_async/gotrue_base_api.py | 4 +- .../src/supabase_auth/_async/gotrue_client.py | 21 ++- .../supabase_auth/_sync/gotrue_admin_api.py | 5 +- .../supabase_auth/_sync/gotrue_base_api.py | 4 +- .../src/supabase_auth/_sync/gotrue_client.py | 19 +-- src/auth/src/supabase_auth/errors.py | 2 +- src/auth/src/supabase_auth/helpers.py | 11 +- src/auth/src/supabase_auth/timer.py | 4 +- src/auth/tests/_async/clients.py | 12 +- src/auth/tests/_async/test_gotrue.py | 41 +++-- .../tests/_async/test_gotrue_admin_api.py | 143 ++++++++++-------- src/auth/tests/_async/test_utils.py | 7 +- src/auth/tests/_async/utils.py | 0 src/auth/tests/_sync/clients.py | 12 +- src/auth/tests/_sync/test_gotrue.py | 41 +++-- src/auth/tests/_sync/test_gotrue_admin_api.py | 139 +++++++++-------- src/auth/tests/_sync/test_utils.py | 7 +- src/auth/tests/_sync/utils.py | 0 src/auth/tests/conftest.py | 4 +- src/auth/tests/test_helpers.py | 79 +++++----- 24 files changed, 293 insertions(+), 292 deletions(-) delete mode 100644 src/auth/tests/_async/utils.py delete mode 100644 src/auth/tests/_sync/utils.py diff --git a/src/auth/pyproject.toml b/src/auth/pyproject.toml index 2a718564..a681e761 100644 --- a/src/auth/pyproject.toml +++ b/src/auth/pyproject.toml @@ -64,8 +64,9 @@ select = [ # "SIM", # isort "I", + "ANN2" ] -ignore = ["F401", "F403", "F841", "E712", "E501", "E402", "E722", "E731", "UP006", "UP035"] +ignore = ["F403", "E712", "E501", "E402", "E722", "E731", "UP006", "UP035"] # isort.required-imports = ["from __future__ import annotations"] [tool.ruff.lint.pyupgrade] diff --git a/src/auth/scripts/gh-download.py b/src/auth/scripts/gh-download.py index 78934f3d..b2f4c123 100644 --- a/src/auth/scripts/gh-download.py +++ b/src/auth/scripts/gh-download.py @@ -73,14 +73,14 @@ def download_directory(repository: Repository, sha: str, server_path: str) -> No print("Error processing %s: %s", content.path, exc) -def usage(): +def usage() -> None: """ Prints the usage command lines """ print("usage: gh-download --repo=repo --branch=branch --folder=folder") -def main(argv): +def main(argv) -> None: """ Main function block """ diff --git a/src/auth/src/supabase_auth/__init__.py b/src/auth/src/supabase_auth/__init__.py index eb058ac2..35352171 100644 --- a/src/auth/src/supabase_auth/__init__.py +++ b/src/auth/src/supabase_auth/__init__.py @@ -1,16 +1,16 @@ from __future__ import annotations -from ._async.gotrue_admin_api import AsyncGoTrueAdminAPI -from ._async.gotrue_client import AsyncGoTrueClient +from ._async.gotrue_admin_api import AsyncGoTrueAdminAPI # noqa +from ._async.gotrue_client import AsyncGoTrueClient # noqa from ._async.storage import ( - AsyncMemoryStorage, - AsyncSupportedStorage, + AsyncMemoryStorage, # noqa + AsyncSupportedStorage, # noqa ) -from ._sync.gotrue_admin_api import SyncGoTrueAdminAPI -from ._sync.gotrue_client import SyncGoTrueClient +from ._sync.gotrue_admin_api import SyncGoTrueAdminAPI # noqa +from ._sync.gotrue_client import SyncGoTrueClient # noqa from ._sync.storage import ( - SyncMemoryStorage, - SyncSupportedStorage, + SyncMemoryStorage, # noqa + SyncSupportedStorage, # noqa ) from .types import * -from .version import __version__ +from .version import __version__ # noqa diff --git a/src/auth/src/supabase_auth/_async/gotrue_admin_api.py b/src/auth/src/supabase_auth/_async/gotrue_admin_api.py index 4d80f4f5..a2eeaf3a 100644 --- a/src/auth/src/supabase_auth/_async/gotrue_admin_api.py +++ b/src/auth/src/supabase_auth/_async/gotrue_admin_api.py @@ -1,9 +1,8 @@ from __future__ import annotations -from typing import Any, Dict, List, Optional +from typing import Dict, List, Optional -from httpx import QueryParams, Response -from pydantic import TypeAdapter +from httpx import QueryParams from ..helpers import ( model_validate, diff --git a/src/auth/src/supabase_auth/_async/gotrue_base_api.py b/src/auth/src/supabase_auth/_async/gotrue_base_api.py index 84faffbc..76b63634 100644 --- a/src/auth/src/supabase_auth/_async/gotrue_base_api.py +++ b/src/auth/src/supabase_auth/_async/gotrue_base_api.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Callable, Dict, Optional, TypeVar, overload +from typing import Any, Dict, Optional from httpx import HTTPStatusError, QueryParams, Response from pydantic import BaseModel @@ -20,7 +20,7 @@ def __init__( http_client: Optional[AsyncClient], verify: bool = True, proxy: Optional[str] = None, - ): + ) -> None: self._url = url self._headers = headers self._http_client = http_client or AsyncClient( diff --git a/src/auth/src/supabase_auth/_async/gotrue_client.py b/src/auth/src/supabase_auth/_async/gotrue_client.py index bb6da182..16cbf964 100644 --- a/src/auth/src/supabase_auth/_async/gotrue_client.py +++ b/src/auth/src/supabase_auth/_async/gotrue_client.py @@ -2,13 +2,11 @@ import time from contextlib import suppress -from functools import partial -from json import loads -from typing import Callable, Dict, List, Mapping, Optional, Tuple, Union -from urllib.parse import parse_qs, urlencode, urlparse +from typing import Callable, Dict, List, Optional, Tuple +from urllib.parse import parse_qs, urlparse from uuid import uuid4 -from httpx import QueryParams +from httpx import QueryParams, Response from jwt import get_algorithm_by_name from typing_extensions import cast @@ -33,7 +31,6 @@ decode_jwt, generate_pkce_challenge, generate_pkce_verifier, - model_dump, model_dump_json, model_validate, parse_auth_otp_response, @@ -50,7 +47,6 @@ JWK, AMREntry, AuthChangeEvent, - AuthenticatorAssuranceLevels, AuthFlowType, AuthMFAChallengeResponse, AuthMFAEnrollResponse, @@ -86,6 +82,7 @@ SignUpWithEmailAndPasswordCredentialsOptions, SignUpWithPasswordCredentials, SignUpWithPhoneAndPasswordCredentialsOptions, + SSOResponse, Subscription, UpdateUserOptions, UserAttributes, @@ -361,7 +358,9 @@ async def sign_in_with_id_token( self._notify_all_subscribers("SIGNED_IN", auth_response.session) return auth_response - async def sign_in_with_sso(self, credentials: SignInWithSSOCredentials): + async def sign_in_with_sso( + self, credentials: SignInWithSSOCredentials + ) -> SSOResponse: """ Attempts a single-sign on using an enterprise Identity Provider. A successful SSO attempt will redirect the current page to the identity @@ -476,7 +475,7 @@ async def get_user_identities(self) -> IdentitiesResponse: return IdentitiesResponse(identities=response.user.identities or []) raise AuthSessionMissingError() - async def unlink_identity(self, identity: UserIdentity): + async def unlink_identity(self, identity: UserIdentity) -> Response: session = await self.get_session() if not session: raise AuthSessionMissingError() @@ -621,7 +620,7 @@ async def reauthenticate(self) -> AuthResponse: if not session: raise AuthSessionMissingError() - response = await self._request( + await self._request( "GET", "reauthenticate", jwt=session.access_token, @@ -1090,7 +1089,7 @@ async def _start_auto_refresh_token(self, value: float) -> None: if value <= 0 or not self._auto_refresh_token: return - async def refresh_token_function(): + async def refresh_token_function() -> None: self._network_retries += 1 try: session = await self.get_session() diff --git a/src/auth/src/supabase_auth/_sync/gotrue_admin_api.py b/src/auth/src/supabase_auth/_sync/gotrue_admin_api.py index 8e554500..e034b699 100644 --- a/src/auth/src/supabase_auth/_sync/gotrue_admin_api.py +++ b/src/auth/src/supabase_auth/_sync/gotrue_admin_api.py @@ -1,9 +1,8 @@ from __future__ import annotations -from typing import Any, Dict, List, Optional +from typing import Dict, List, Optional -from httpx import QueryParams, Response -from pydantic import TypeAdapter +from httpx import QueryParams from ..helpers import ( model_validate, diff --git a/src/auth/src/supabase_auth/_sync/gotrue_base_api.py b/src/auth/src/supabase_auth/_sync/gotrue_base_api.py index dbb8b171..56859992 100644 --- a/src/auth/src/supabase_auth/_sync/gotrue_base_api.py +++ b/src/auth/src/supabase_auth/_sync/gotrue_base_api.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Callable, Dict, Optional, TypeVar, overload +from typing import Any, Dict, Optional from httpx import HTTPStatusError, QueryParams, Response from pydantic import BaseModel @@ -20,7 +20,7 @@ def __init__( http_client: Optional[SyncClient], verify: bool = True, proxy: Optional[str] = None, - ): + ) -> None: self._url = url self._headers = headers self._http_client = http_client or SyncClient( diff --git a/src/auth/src/supabase_auth/_sync/gotrue_client.py b/src/auth/src/supabase_auth/_sync/gotrue_client.py index a0026826..afbf7a27 100644 --- a/src/auth/src/supabase_auth/_sync/gotrue_client.py +++ b/src/auth/src/supabase_auth/_sync/gotrue_client.py @@ -2,13 +2,11 @@ import time from contextlib import suppress -from functools import partial -from json import loads -from typing import Callable, Dict, List, Mapping, Optional, Tuple, Union -from urllib.parse import parse_qs, urlencode, urlparse +from typing import Callable, Dict, List, Optional, Tuple +from urllib.parse import parse_qs, urlparse from uuid import uuid4 -from httpx import QueryParams +from httpx import QueryParams, Response from jwt import get_algorithm_by_name from typing_extensions import cast @@ -33,7 +31,6 @@ decode_jwt, generate_pkce_challenge, generate_pkce_verifier, - model_dump, model_dump_json, model_validate, parse_auth_otp_response, @@ -50,7 +47,6 @@ JWK, AMREntry, AuthChangeEvent, - AuthenticatorAssuranceLevels, AuthFlowType, AuthMFAChallengeResponse, AuthMFAEnrollResponse, @@ -86,6 +82,7 @@ SignUpWithEmailAndPasswordCredentialsOptions, SignUpWithPasswordCredentials, SignUpWithPhoneAndPasswordCredentialsOptions, + SSOResponse, Subscription, UpdateUserOptions, UserAttributes, @@ -361,7 +358,7 @@ def sign_in_with_id_token( self._notify_all_subscribers("SIGNED_IN", auth_response.session) return auth_response - def sign_in_with_sso(self, credentials: SignInWithSSOCredentials): + def sign_in_with_sso(self, credentials: SignInWithSSOCredentials) -> SSOResponse: """ Attempts a single-sign on using an enterprise Identity Provider. A successful SSO attempt will redirect the current page to the identity @@ -474,7 +471,7 @@ def get_user_identities(self) -> IdentitiesResponse: return IdentitiesResponse(identities=response.user.identities or []) raise AuthSessionMissingError() - def unlink_identity(self, identity: UserIdentity): + def unlink_identity(self, identity: UserIdentity) -> Response: session = self.get_session() if not session: raise AuthSessionMissingError() @@ -619,7 +616,7 @@ def reauthenticate(self) -> AuthResponse: if not session: raise AuthSessionMissingError() - response = self._request( + self._request( "GET", "reauthenticate", jwt=session.access_token, @@ -1086,7 +1083,7 @@ def _start_auto_refresh_token(self, value: float) -> None: if value <= 0 or not self._auto_refresh_token: return - def refresh_token_function(): + def refresh_token_function() -> None: self._network_retries += 1 try: session = self.get_session() diff --git a/src/auth/src/supabase_auth/errors.py b/src/auth/src/supabase_auth/errors.py index 67eca908..e7a10539 100644 --- a/src/auth/src/supabase_auth/errors.py +++ b/src/auth/src/supabase_auth/errors.py @@ -91,7 +91,7 @@ class UserDoesntExist(Exception): - def __init__(self, access_token: str): + def __init__(self, access_token: str) -> None: self.access_token = access_token diff --git a/src/auth/src/supabase_auth/helpers.py b/src/auth/src/supabase_auth/helpers.py index 05c117c9..4b31e7ad 100644 --- a/src/auth/src/supabase_auth/helpers.py +++ b/src/auth/src/supabase_auth/helpers.py @@ -9,7 +9,7 @@ import uuid from base64 import urlsafe_b64decode from datetime import datetime -from typing import Any, Dict, Optional, Type, TypedDict, TypeVar, Union, cast +from typing import Any, Dict, Optional, Type, TypedDict, TypeVar, Union from urllib.parse import urlparse from httpx import HTTPStatusError, Response @@ -18,7 +18,6 @@ from .constants import ( API_VERSION_HEADER_NAME, API_VERSIONS_2024_01_01_TIMESTAMP, - BASE64URL_REGEX, ) from .errors import ( AuthApiError, @@ -240,7 +239,7 @@ def decode_jwt(token: str) -> DecodedJWT: ) -def generate_pkce_verifier(length=64): +def generate_pkce_verifier(length=64) -> str: """Generate a random PKCE verifier of the specified length.""" if length < 43 or length > 128: raise ValueError("PKCE verifier length must be between 43 and 128 characters") @@ -251,7 +250,7 @@ def generate_pkce_verifier(length=64): return "".join(secrets.choice(charset) for _ in range(length)) -def generate_pkce_challenge(code_verifier): +def generate_pkce_challenge(code_verifier) -> str: """Generate a code challenge from a PKCE verifier.""" # Hash the verifier using SHA-256 verifier_bytes = code_verifier.encode("utf-8") @@ -263,7 +262,7 @@ def generate_pkce_challenge(code_verifier): API_VERSION_REGEX = r"^2[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-9]|3[0-1])$" -def parse_response_api_version(response: Response): +def parse_response_api_version(response: Response) -> Optional[datetime]: api_version = response.headers.get(API_VERSION_HEADER_NAME) if not api_version: @@ -275,7 +274,7 @@ def parse_response_api_version(response: Response): try: dt = datetime.strptime(api_version, "%Y-%m-%d") return dt - except Exception as e: + except Exception: return None diff --git a/src/auth/src/supabase_auth/timer.py b/src/auth/src/supabase_auth/timer.py index 11f11536..15d5fa0b 100644 --- a/src/auth/src/supabase_auth/timer.py +++ b/src/auth/src/supabase_auth/timer.py @@ -17,11 +17,11 @@ def __init__( def start(self) -> None: if asyncio.iscoroutinefunction(self._function): - async def schedule(): + async def schedule() -> None: await asyncio.sleep(self._milliseconds / 1000) await cast(Coroutine[Any, Any, None], self._function()) - def cleanup(_): + def cleanup(_) -> None: self._task = None self._task = asyncio.create_task(schedule()) diff --git a/src/auth/tests/_async/clients.py b/src/auth/tests/_async/clients.py index 655873ab..a6ed3f3c 100644 --- a/src/auth/tests/_async/clients.py +++ b/src/auth/tests/_async/clients.py @@ -50,14 +50,22 @@ def mock_verification_otp() -> str: return str(int(100000 + random() * 900000)) -def mock_user_metadata(): +class UserMetadata(TypedDict): + profile_image: str + + +def mock_user_metadata() -> UserMetadata: fake = Faker() return { "profile_image": fake.url(), } -def mock_app_metadata(): +class AppMetadata(TypedDict): + roles: list[str] + + +def mock_app_metadata() -> AppMetadata: return { "roles": ["editor", "publisher"], } diff --git a/src/auth/tests/_async/test_gotrue.py b/src/auth/tests/_async/test_gotrue.py index d7f3789e..16905fa5 100644 --- a/src/auth/tests/_async/test_gotrue.py +++ b/src/auth/tests/_async/test_gotrue.py @@ -1,5 +1,4 @@ import time -import unittest from uuid import uuid4 import pytest @@ -22,12 +21,12 @@ ) -async def test_get_claims_returns_none_when_session_is_none(): +async def test_get_claims_returns_none_when_session_is_none() -> None: claims = await auth_client().get_claims() assert claims is None -async def test_get_claims_calls_get_user_if_symmetric_jwt(mocker): +async def test_get_claims_calls_get_user_if_symmetric_jwt(mocker) -> None: client = auth_client() spy = mocker.spy(client, "get_user") credentials = mock_user_credentials() @@ -47,7 +46,7 @@ async def test_get_claims_calls_get_user_if_symmetric_jwt(mocker): spy.assert_called_once() -async def test_get_claims_fetches_jwks_to_verify_asymmetric_jwt(mocker): +async def test_get_claims_fetches_jwks_to_verify_asymmetric_jwt(mocker) -> None: client = auth_client_with_asymmetric_session() credentials = mock_user_credentials() options: SignUpWithEmailAndPasswordCredentials = { @@ -73,7 +72,7 @@ async def test_get_claims_fetches_jwks_to_verify_asymmetric_jwt(mocker): assert client._jwks["keys"][0]["kid"] == expected_keyid -async def test_jwks_ttl_cache_behavior(mocker): +async def test_jwks_ttl_cache_behavior(mocker) -> None: client = auth_client_with_asymmetric_session() spy = mocker.spy(client, "_request") @@ -109,7 +108,7 @@ async def test_jwks_ttl_cache_behavior(mocker): mocker.patch("time.time", original_time) -async def test_set_session_with_valid_tokens(): +async def test_set_session_with_valid_tokens() -> None: client = auth_client() credentials = mock_user_credentials() @@ -140,7 +139,7 @@ async def test_set_session_with_valid_tokens(): assert response.user.email == credentials.email -async def test_set_session_with_expired_token(): +async def test_set_session_with_expired_token() -> None: client = auth_client() credentials = mock_user_credentials() @@ -180,7 +179,7 @@ async def test_set_session_with_expired_token(): assert response.user.email == credentials.email -async def test_set_session_without_refresh_token(): +async def test_set_session_without_refresh_token() -> None: client = auth_client() credentials = mock_user_credentials() @@ -213,7 +212,7 @@ async def test_set_session_without_refresh_token(): await client.set_session(expired_access_token, "") -async def test_set_session_with_invalid_token(): +async def test_set_session_with_invalid_token() -> None: client = auth_client() # Try to set the session with invalid tokens @@ -221,7 +220,7 @@ async def test_set_session_with_invalid_token(): await client.set_session("invalid.token.here", "invalid_refresh_token") -async def test_mfa_enroll(): +async def test_mfa_enroll() -> None: client = await auth_client_with_session() credentials = mock_user_credentials() @@ -246,7 +245,7 @@ async def test_mfa_enroll(): assert enroll_response.totp.qr_code is not None -async def test_mfa_challenge(): +async def test_mfa_challenge() -> None: client = auth_client() credentials = mock_user_credentials() @@ -270,7 +269,7 @@ async def test_mfa_challenge(): assert challenge_response.expires_at is not None -async def test_mfa_unenroll(): +async def test_mfa_unenroll() -> None: client = auth_client() credentials = mock_user_credentials() @@ -293,7 +292,7 @@ async def test_mfa_unenroll(): assert unenroll_response.id == enroll_response.id -async def test_mfa_list_factors(): +async def test_mfa_list_factors() -> None: client = auth_client() credentials = mock_user_credentials() @@ -316,7 +315,7 @@ async def test_mfa_list_factors(): assert len(list_response.all) == 1 -async def test_exchange_code_for_session(): +async def test_exchange_code_for_session() -> None: client = auth_client() # We'll test the flow type setting instead of the actual exchange, since the @@ -344,7 +343,7 @@ async def test_exchange_code_for_session(): assert code_verifier is not None -async def test_get_authenticator_assurance_level(): +async def test_get_authenticator_assurance_level() -> None: client = auth_client() credentials = mock_user_credentials() @@ -369,7 +368,7 @@ async def test_get_authenticator_assurance_level(): assert aal_response.current_authentication_methods is not None -async def test_link_identity(): +async def test_link_identity() -> None: client = auth_client() credentials = mock_user_credentials() @@ -386,8 +385,6 @@ async def test_link_identity(): from httpx import Response - from supabase_auth.types import OAuthResponse - # Since the test server has manual linking disabled, we'll mock the URL generation with patch.object(client, "_get_url_for_provider") as mock_url_provider: mock_url = "http://example.com/authorize?provider=github" @@ -408,7 +405,7 @@ async def test_link_identity(): assert response.url == mock_url -async def test_get_user_identities(): +async def test_get_user_identities() -> None: client = auth_client() credentials = mock_user_credentials() @@ -428,7 +425,7 @@ async def test_get_user_identities(): assert hasattr(identities_response, "identities") -async def test_sign_in_with_password(): +async def test_sign_in_with_password() -> None: client = auth_client() credentials = mock_user_credentials() from supabase_auth.errors import AuthApiError, AuthInvalidCredentialsError @@ -479,7 +476,7 @@ async def test_sign_in_with_password(): pass -async def test_sign_in_with_otp(): +async def test_sign_in_with_otp() -> None: client = auth_client() # Test with email OTP @@ -577,7 +574,7 @@ async def test_sign_in_with_otp(): pass -async def test_sign_out(): +async def test_sign_out() -> None: from datetime import datetime from unittest.mock import patch diff --git a/src/auth/tests/_async/test_gotrue_admin_api.py b/src/auth/tests/_async/test_gotrue_admin_api.py index 4c54dc0e..7dfa691f 100644 --- a/src/auth/tests/_async/test_gotrue_admin_api.py +++ b/src/auth/tests/_async/test_gotrue_admin_api.py @@ -5,11 +5,10 @@ from supabase_auth.errors import ( AuthApiError, AuthError, - AuthInvalidCredentialsError, AuthSessionMissingError, AuthWeakPasswordError, ) -from supabase_auth.types import CreateOAuthClientParams, UpdateOAuthClientParams +from supabase_auth.types import CreateOAuthClientParams from .clients import ( auth_client, @@ -25,13 +24,13 @@ ) -async def test_create_user_should_create_a_new_user(): +async def test_create_user_should_create_a_new_user() -> None: credentials = mock_user_credentials() response = await create_new_user_with_email(email=credentials.email) assert response.email == credentials.email -async def test_create_user_with_user_metadata(): +async def test_create_user_with_user_metadata() -> None: user_metadata = mock_user_metadata() credentials = mock_user_credentials() response = await service_role_api_client().create_user( @@ -46,7 +45,7 @@ async def test_create_user_with_user_metadata(): assert "profile_image" in response.user.user_metadata -async def test_create_user_with_user_and_app_metadata(): +async def test_create_user_with_user_and_app_metadata() -> None: user_metadata = mock_user_metadata() app_metadata = mock_app_metadata() credentials = mock_user_credentials() @@ -64,7 +63,7 @@ async def test_create_user_with_user_and_app_metadata(): assert "providers" in response.user.app_metadata -async def test_list_users_should_return_registered_users(): +async def test_list_users_should_return_registered_users() -> None: credentials = mock_user_credentials() await create_new_user_with_email(email=credentials.email) users = await service_role_api_client().list_users() @@ -74,7 +73,9 @@ async def test_list_users_should_return_registered_users(): assert credentials.email in emails -async def test_get_user_by_id_should_a_registered_user_given_its_user_identifier(): +async def test_get_user_by_id_should_a_registered_user_given_its_user_identifier() -> ( + None +): credentials = mock_user_credentials() user = await create_new_user_with_email(email=credentials.email) assert user.id @@ -82,7 +83,7 @@ async def test_get_user_by_id_should_a_registered_user_given_its_user_identifier assert response.user.email == credentials.email -async def test_modify_email_using_update_user_by_id(): +async def test_modify_email_using_update_user_by_id() -> None: credentials = mock_user_credentials() user = await create_new_user_with_email(email=credentials.email) response = await service_role_api_client().update_user_by_id( @@ -94,7 +95,7 @@ async def test_modify_email_using_update_user_by_id(): assert response.user.email == f"new_{user.email}" -async def test_modify_user_metadata_using_update_user_by_id(): +async def test_modify_user_metadata_using_update_user_by_id() -> None: credentials = mock_user_credentials() user = await create_new_user_with_email(email=credentials.email) user_metadata = {"favorite_color": "yellow"} @@ -108,7 +109,7 @@ async def test_modify_user_metadata_using_update_user_by_id(): assert response.user.user_metadata == user_metadata -async def test_modify_app_metadata_using_update_user_by_id(): +async def test_modify_app_metadata_using_update_user_by_id() -> None: credentials = mock_user_credentials() user = await create_new_user_with_email(email=credentials.email) app_metadata = {"roles": ["admin", "publisher"]} @@ -122,7 +123,7 @@ async def test_modify_app_metadata_using_update_user_by_id(): assert "roles" in response.user.app_metadata -async def test_modify_confirm_email_using_update_user_by_id(): +async def test_modify_confirm_email_using_update_user_by_id() -> None: credentials = mock_user_credentials() response = await client_api_auto_confirm_off_signups_enabled_client().sign_up( { @@ -132,40 +133,44 @@ async def test_modify_confirm_email_using_update_user_by_id(): ) assert response.user assert not response.user.email_confirmed_at - response = await service_role_api_client().update_user_by_id( + auth_response = await service_role_api_client().update_user_by_id( response.user.id, { "email_confirm": True, }, ) - assert response.user.email_confirmed_at + assert auth_response.user.email_confirmed_at -async def test_invalid_credential_sign_in_with_phone(): +async def test_invalid_credential_sign_in_with_phone() -> None: try: - response = await client_api_auto_confirm_off_signups_enabled_client().sign_in_with_password( - { - "phone": "+123456789", - "password": "strong_pwd", - } + await ( + client_api_auto_confirm_off_signups_enabled_client().sign_in_with_password( + { + "phone": "+123456789", + "password": "strong_pwd", + } + ) ) except AuthApiError as e: assert e.to_dict() -async def test_invalid_credential_sign_in_with_email(): +async def test_invalid_credential_sign_in_with_email() -> None: try: - response = await client_api_auto_confirm_off_signups_enabled_client().sign_in_with_password( - { - "email": "unknown_user@unknowndomain.com", - "password": "strong_pwd", - } + await ( + client_api_auto_confirm_off_signups_enabled_client().sign_in_with_password( + { + "email": "unknown_user@unknowndomain.com", + "password": "strong_pwd", + } + ) ) except AuthApiError as e: assert e.to_dict() -async def test_sign_in_with_otp_email(): +async def test_sign_in_with_otp_email() -> None: try: await client_api_auto_confirm_off_signups_enabled_client().sign_in_with_otp( { @@ -176,7 +181,7 @@ async def test_sign_in_with_otp_email(): assert e.to_dict() -async def test_sign_in_with_otp_phone(): +async def test_sign_in_with_otp_phone() -> None: try: await client_api_auto_confirm_off_signups_enabled_client().sign_in_with_otp( { @@ -187,41 +192,41 @@ async def test_sign_in_with_otp_phone(): assert e.to_dict() -async def test_resend(): +async def test_resend() -> None: await client_api_auto_confirm_off_signups_enabled_client().resend( {"phone": "+112345678", "type": "sms"} ) -async def test_reauthenticate(): +async def test_reauthenticate() -> None: client = await auth_client_with_session() await client.reauthenticate() -async def test_refresh_session(): +async def test_refresh_session() -> None: client = await auth_client_with_session() await client.refresh_session() -async def test_reset_password_for_email(): +async def test_reset_password_for_email() -> None: credentials = mock_user_credentials() client = await auth_client_with_session() await client.reset_password_email(email=credentials.email) -async def test_resend_missing_credentials(): +async def test_resend_missing_credentials() -> None: credentials = mock_user_credentials() await client_api_auto_confirm_off_signups_enabled_client().resend( {"type": "email_change", "email": credentials.email} ) -async def test_sign_in_anonymously(): +async def test_sign_in_anonymously() -> None: client = await auth_client_with_session() await client.sign_in_anonymously() -async def test_delete_user_should_be_able_delete_an_existing_user(): +async def test_delete_user_should_be_able_delete_an_existing_user() -> None: credentials = mock_user_credentials() user = await create_new_user_with_email(email=credentials.email) await service_role_api_client().delete_user(user.id) @@ -230,7 +235,9 @@ async def test_delete_user_should_be_able_delete_an_existing_user(): assert credentials.email not in emails -async def test_generate_link_supports_sign_up_with_generate_confirmation_signup_link(): +async def test_generate_link_supports_sign_up_with_generate_confirmation_signup_link() -> ( + None +): credentials = mock_user_credentials() redirect_to = "http://localhost:9999/welcome" user_metadata = {"status": "alpha"} @@ -248,7 +255,9 @@ async def test_generate_link_supports_sign_up_with_generate_confirmation_signup_ assert response.user.user_metadata == user_metadata -async def test_generate_link_supports_updating_emails_with_generate_email_change_links(): # noqa: E501 +async def test_generate_link_supports_updating_emails_with_generate_email_change_links() -> ( + None +): # noqa: E501 credentials = mock_user_credentials() user = await create_new_user_with_email(email=credentials.email) assert user.email @@ -268,7 +277,9 @@ async def test_generate_link_supports_updating_emails_with_generate_email_change assert response.user.new_email == credentials.email -async def test_invite_user_by_email_creates_a_new_user_with_an_invited_at_timestamp(): +async def test_invite_user_by_email_creates_a_new_user_with_an_invited_at_timestamp() -> ( + None +): credentials = mock_user_credentials() redirect_to = "http://localhost:9999/welcome" user_metadata = {"status": "alpha"} @@ -282,7 +293,7 @@ async def test_invite_user_by_email_creates_a_new_user_with_an_invited_at_timest assert response.user.invited_at -async def test_sign_out_with_an_valid_access_token(): +async def test_sign_out_with_an_valid_access_token() -> None: credentials = mock_user_credentials() client = await auth_client_with_session() response = await client.sign_up( @@ -295,7 +306,7 @@ async def test_sign_out_with_an_valid_access_token(): await service_role_api_client().sign_out(response.session.access_token) -async def test_sign_out_with_an_invalid_access_token(): +async def test_sign_out_with_an_invalid_access_token() -> None: try: await service_role_api_client().sign_out("this-is-a-bad-token") assert False @@ -303,7 +314,7 @@ async def test_sign_out_with_an_invalid_access_token(): pass -async def test_verify_otp_with_non_existent_phone_number(): +async def test_verify_otp_with_non_existent_phone_number() -> None: credentials = mock_user_credentials() otp = mock_verification_otp() try: @@ -319,7 +330,7 @@ async def test_verify_otp_with_non_existent_phone_number(): assert e.message == "Token has expired or is invalid" -async def test_verify_otp_with_invalid_phone_number(): +async def test_verify_otp_with_invalid_phone_number() -> None: credentials = mock_user_credentials() otp = mock_verification_otp() try: @@ -335,7 +346,7 @@ async def test_verify_otp_with_invalid_phone_number(): assert e.message == "Invalid phone number format (E.164 required)" -async def test_sign_in_with_id_token(): +async def test_sign_in_with_id_token() -> None: try: await ( client_api_auto_confirm_off_signups_enabled_client().sign_in_with_id_token( @@ -349,7 +360,7 @@ async def test_sign_in_with_id_token(): assert e.to_dict() -async def test_sign_in_with_sso(): +async def test_sign_in_with_sso() -> None: with pytest.raises(AuthApiError, match=r"SAML 2.0 is disabled") as exc: await client_api_auto_confirm_off_signups_enabled_client().sign_in_with_sso( { @@ -359,7 +370,7 @@ async def test_sign_in_with_sso(): assert exc.value is not None -async def test_sign_in_with_oauth(): +async def test_sign_in_with_oauth() -> None: assert ( await client_api_auto_confirm_off_signups_enabled_client().sign_in_with_oauth( { @@ -369,7 +380,7 @@ async def test_sign_in_with_oauth(): ) -async def test_link_identity_missing_session(): +async def test_link_identity_missing_session() -> None: with pytest.raises(AuthSessionMissingError) as exc: await client_api_auto_confirm_off_signups_enabled_client().link_identity( { @@ -379,7 +390,7 @@ async def test_link_identity_missing_session(): assert exc.value is not None -async def test_get_item_from_memory_storage(): +async def test_get_item_from_memory_storage() -> None: credentials = mock_user_credentials() client = auth_client() await client.sign_up( @@ -398,7 +409,7 @@ async def test_get_item_from_memory_storage(): assert await client._storage.get_item(client._storage_key) is not None -async def test_remove_item_from_memory_storage(): +async def test_remove_item_from_memory_storage() -> None: credentials = mock_user_credentials() client = auth_client() await client.sign_up( @@ -417,7 +428,7 @@ async def test_remove_item_from_memory_storage(): await client._storage.remove_item(client._storage_key) -async def test_list_factors(): +async def test_list_factors() -> None: credentials = mock_user_credentials() client = auth_client() await client.sign_up( @@ -438,7 +449,7 @@ async def test_list_factors(): assert isinstance(factors.totp, list) and isinstance(factors.phone, list) -async def test_start_auto_refresh_token(): +async def test_start_auto_refresh_token() -> None: credentials = mock_user_credentials() client = auth_client() client._auto_refresh_token = True @@ -457,7 +468,7 @@ async def test_start_auto_refresh_token(): ) -async def test_recover_and_refresh(): +async def test_recover_and_refresh() -> None: credentials = mock_user_credentials() client = auth_client() client._auto_refresh_token = True @@ -477,7 +488,7 @@ async def test_recover_and_refresh(): await client._recover_and_refresh() -async def test_get_user_identities(): +async def test_get_user_identities() -> None: credentials = mock_user_credentials() client = auth_client() client._auto_refresh_token = True @@ -499,7 +510,7 @@ async def test_get_user_identities(): ] == credentials.email -async def test_update_user(): +async def test_update_user() -> None: credentials = mock_user_credentials() client = auth_client() client._auto_refresh_token = True @@ -518,7 +529,7 @@ async def test_update_user(): ) -async def test_create_user_with_app_metadata(): +async def test_create_user_with_app_metadata() -> None: app_metadata = mock_app_metadata() credentials = mock_user_credentials() response = await service_role_api_client().create_user( @@ -533,7 +544,7 @@ async def test_create_user_with_app_metadata(): assert "providers" in response.user.app_metadata -async def test_weak_email_password_error(): +async def test_weak_email_password_error() -> None: credentials = mock_user_credentials() try: await client_api_auto_confirm_off_signups_enabled_client().sign_up( @@ -546,7 +557,7 @@ async def test_weak_email_password_error(): assert e.to_dict() -async def test_weak_phone_password_error(): +async def test_weak_phone_password_error() -> None: credentials = mock_user_credentials() try: await client_api_auto_confirm_off_signups_enabled_client().sign_up( @@ -559,14 +570,14 @@ async def test_weak_phone_password_error(): assert e.to_dict() -async def test_get_user_by_id_invalid_id_raises_error(): +async def test_get_user_by_id_invalid_id_raises_error() -> None: with pytest.raises( ValueError, match=r"Invalid id, 'invalid_id' is not a valid uuid" ): await service_role_api_client().get_user_by_id("invalid_id") -async def test_update_user_by_id_invalid_id_raises_error(): +async def test_update_user_by_id_invalid_id_raises_error() -> None: with pytest.raises( ValueError, match=r"Invalid id, 'invalid_id' is not a valid uuid" ): @@ -575,21 +586,21 @@ async def test_update_user_by_id_invalid_id_raises_error(): ) -async def test_delete_user_invalid_id_raises_error(): +async def test_delete_user_invalid_id_raises_error() -> None: with pytest.raises( ValueError, match=r"Invalid id, 'invalid_id' is not a valid uuid" ): await service_role_api_client().delete_user("invalid_id") -async def test_list_factors_invalid_id_raises_error(): +async def test_list_factors_invalid_id_raises_error() -> None: with pytest.raises( ValueError, match=r"Invalid id, 'invalid_id' is not a valid uuid" ): await service_role_api_client()._list_factors({"user_id": "invalid_id"}) -async def test_delete_factor_invalid_id_raises_error(): +async def test_delete_factor_invalid_id_raises_error() -> None: # invalid user id with pytest.raises( ValueError, match=r"Invalid id, 'invalid_id' is not a valid uuid" @@ -607,7 +618,7 @@ async def test_delete_factor_invalid_id_raises_error(): ) -async def test_create_oauth_client(): +async def test_create_oauth_client() -> None: """Test creating an OAuth client.""" response = await service_role_api_client().oauth.create_client( CreateOAuthClientParams( @@ -620,7 +631,7 @@ async def test_create_oauth_client(): assert response.client.client_id is not None -async def test_list_oauth_clients(): +async def test_list_oauth_clients() -> None: """Test listing OAuth clients.""" client = service_role_api_client() await client.oauth.create_client( @@ -635,7 +646,7 @@ async def test_list_oauth_clients(): assert any(client.client_id is not None for client in response.clients) -async def test_get_oauth_client(): +async def test_get_oauth_client() -> None: """Test getting an OAuth client by ID.""" # First create a client create_response = await service_role_api_client().oauth.create_client( @@ -652,7 +663,7 @@ async def test_get_oauth_client(): # Server is not yet released, so this test is not yet relevant. -# async def test_update_oauth_client(): +# async def test_update_oauth_client() -> None: # """Test updating an OAuth client.""" # # First create a client # client = service_role_api_client() @@ -674,7 +685,7 @@ async def test_get_oauth_client(): # assert response.client.client_name == "Updated Test OAuth Client" -async def test_delete_oauth_client(): +async def test_delete_oauth_client() -> None: """Test deleting an OAuth client.""" # First create a client client = service_role_api_client() @@ -689,7 +700,7 @@ async def test_delete_oauth_client(): await client.oauth.delete_client(client_id) -async def test_regenerate_oauth_client_secret(): +async def test_regenerate_oauth_client_secret() -> None: """Test regenerating an OAuth client secret.""" # First create a client create_response = await service_role_api_client().oauth.create_client( diff --git a/src/auth/tests/_async/test_utils.py b/src/auth/tests/_async/test_utils.py index 865f1e95..ed0a22ff 100644 --- a/src/auth/tests/_async/test_utils.py +++ b/src/auth/tests/_async/test_utils.py @@ -3,24 +3,23 @@ from .clients import ( create_new_user_with_email, mock_app_metadata, - mock_user_credentials, mock_user_metadata, ) -async def test_create_new_user_with_email(): +async def test_create_new_user_with_email() -> None: email = f"user+{int(time())}@example.com" user = await create_new_user_with_email(email=email) assert user.email == email -def test_mock_user_metadata(): +def test_mock_user_metadata() -> None: user_metadata = mock_user_metadata() assert user_metadata assert user_metadata.get("profile_image") -def test_mock_app_metadata(): +def test_mock_app_metadata() -> None: app_metadata = mock_app_metadata() assert app_metadata assert app_metadata.get("roles") diff --git a/src/auth/tests/_async/utils.py b/src/auth/tests/_async/utils.py deleted file mode 100644 index e69de29b..00000000 diff --git a/src/auth/tests/_sync/clients.py b/src/auth/tests/_sync/clients.py index 38a0938d..752e16e8 100644 --- a/src/auth/tests/_sync/clients.py +++ b/src/auth/tests/_sync/clients.py @@ -50,14 +50,22 @@ def mock_verification_otp() -> str: return str(int(100000 + random() * 900000)) -def mock_user_metadata(): +class UserMetadata(TypedDict): + profile_image: str + + +def mock_user_metadata() -> UserMetadata: fake = Faker() return { "profile_image": fake.url(), } -def mock_app_metadata(): +class AppMetadata(TypedDict): + roles: list[str] + + +def mock_app_metadata() -> AppMetadata: return { "roles": ["editor", "publisher"], } diff --git a/src/auth/tests/_sync/test_gotrue.py b/src/auth/tests/_sync/test_gotrue.py index f15dcbd8..7b880ed5 100644 --- a/src/auth/tests/_sync/test_gotrue.py +++ b/src/auth/tests/_sync/test_gotrue.py @@ -1,5 +1,4 @@ import time -import unittest from uuid import uuid4 import pytest @@ -22,12 +21,12 @@ ) -def test_get_claims_returns_none_when_session_is_none(): +def test_get_claims_returns_none_when_session_is_none() -> None: claims = auth_client().get_claims() assert claims is None -def test_get_claims_calls_get_user_if_symmetric_jwt(mocker): +def test_get_claims_calls_get_user_if_symmetric_jwt(mocker) -> None: client = auth_client() spy = mocker.spy(client, "get_user") credentials = mock_user_credentials() @@ -47,7 +46,7 @@ def test_get_claims_calls_get_user_if_symmetric_jwt(mocker): spy.assert_called_once() -def test_get_claims_fetches_jwks_to_verify_asymmetric_jwt(mocker): +def test_get_claims_fetches_jwks_to_verify_asymmetric_jwt(mocker) -> None: client = auth_client_with_asymmetric_session() credentials = mock_user_credentials() options: SignUpWithEmailAndPasswordCredentials = { @@ -73,7 +72,7 @@ def test_get_claims_fetches_jwks_to_verify_asymmetric_jwt(mocker): assert client._jwks["keys"][0]["kid"] == expected_keyid -def test_jwks_ttl_cache_behavior(mocker): +def test_jwks_ttl_cache_behavior(mocker) -> None: client = auth_client_with_asymmetric_session() spy = mocker.spy(client, "_request") @@ -109,7 +108,7 @@ def test_jwks_ttl_cache_behavior(mocker): mocker.patch("time.time", original_time) -def test_set_session_with_valid_tokens(): +def test_set_session_with_valid_tokens() -> None: client = auth_client() credentials = mock_user_credentials() @@ -140,7 +139,7 @@ def test_set_session_with_valid_tokens(): assert response.user.email == credentials.email -def test_set_session_with_expired_token(): +def test_set_session_with_expired_token() -> None: client = auth_client() credentials = mock_user_credentials() @@ -180,7 +179,7 @@ def test_set_session_with_expired_token(): assert response.user.email == credentials.email -def test_set_session_without_refresh_token(): +def test_set_session_without_refresh_token() -> None: client = auth_client() credentials = mock_user_credentials() @@ -213,7 +212,7 @@ def test_set_session_without_refresh_token(): client.set_session(expired_access_token, "") -def test_set_session_with_invalid_token(): +def test_set_session_with_invalid_token() -> None: client = auth_client() # Try to set the session with invalid tokens @@ -221,7 +220,7 @@ def test_set_session_with_invalid_token(): client.set_session("invalid.token.here", "invalid_refresh_token") -def test_mfa_enroll(): +def test_mfa_enroll() -> None: client = auth_client_with_session() credentials = mock_user_credentials() @@ -246,7 +245,7 @@ def test_mfa_enroll(): assert enroll_response.totp.qr_code is not None -def test_mfa_challenge(): +def test_mfa_challenge() -> None: client = auth_client() credentials = mock_user_credentials() @@ -270,7 +269,7 @@ def test_mfa_challenge(): assert challenge_response.expires_at is not None -def test_mfa_unenroll(): +def test_mfa_unenroll() -> None: client = auth_client() credentials = mock_user_credentials() @@ -293,7 +292,7 @@ def test_mfa_unenroll(): assert unenroll_response.id == enroll_response.id -def test_mfa_list_factors(): +def test_mfa_list_factors() -> None: client = auth_client() credentials = mock_user_credentials() @@ -316,7 +315,7 @@ def test_mfa_list_factors(): assert len(list_response.all) == 1 -def test_exchange_code_for_session(): +def test_exchange_code_for_session() -> None: client = auth_client() # We'll test the flow type setting instead of the actual exchange, since the @@ -342,7 +341,7 @@ def test_exchange_code_for_session(): assert code_verifier is not None -def test_get_authenticator_assurance_level(): +def test_get_authenticator_assurance_level() -> None: client = auth_client() credentials = mock_user_credentials() @@ -367,7 +366,7 @@ def test_get_authenticator_assurance_level(): assert aal_response.current_authentication_methods is not None -def test_link_identity(): +def test_link_identity() -> None: client = auth_client() credentials = mock_user_credentials() @@ -384,8 +383,6 @@ def test_link_identity(): from httpx import Response - from supabase_auth.types import OAuthResponse - # Since the test server has manual linking disabled, we'll mock the URL generation with patch.object(client, "_get_url_for_provider") as mock_url_provider: mock_url = "http://example.com/authorize?provider=github" @@ -406,7 +403,7 @@ def test_link_identity(): assert response.url == mock_url -def test_get_user_identities(): +def test_get_user_identities() -> None: client = auth_client() credentials = mock_user_credentials() @@ -426,7 +423,7 @@ def test_get_user_identities(): assert hasattr(identities_response, "identities") -def test_sign_in_with_password(): +def test_sign_in_with_password() -> None: client = auth_client() credentials = mock_user_credentials() from supabase_auth.errors import AuthApiError, AuthInvalidCredentialsError @@ -477,7 +474,7 @@ def test_sign_in_with_password(): pass -def test_sign_in_with_otp(): +def test_sign_in_with_otp() -> None: client = auth_client() # Test with email OTP @@ -575,7 +572,7 @@ def test_sign_in_with_otp(): pass -def test_sign_out(): +def test_sign_out() -> None: from datetime import datetime from unittest.mock import patch diff --git a/src/auth/tests/_sync/test_gotrue_admin_api.py b/src/auth/tests/_sync/test_gotrue_admin_api.py index 3f4e63b3..20fa4835 100644 --- a/src/auth/tests/_sync/test_gotrue_admin_api.py +++ b/src/auth/tests/_sync/test_gotrue_admin_api.py @@ -5,11 +5,10 @@ from supabase_auth.errors import ( AuthApiError, AuthError, - AuthInvalidCredentialsError, AuthSessionMissingError, AuthWeakPasswordError, ) -from supabase_auth.types import CreateOAuthClientParams, UpdateOAuthClientParams +from supabase_auth.types import CreateOAuthClientParams from .clients import ( auth_client, @@ -25,13 +24,13 @@ ) -def test_create_user_should_create_a_new_user(): +def test_create_user_should_create_a_new_user() -> None: credentials = mock_user_credentials() response = create_new_user_with_email(email=credentials.email) assert response.email == credentials.email -def test_create_user_with_user_metadata(): +def test_create_user_with_user_metadata() -> None: user_metadata = mock_user_metadata() credentials = mock_user_credentials() response = service_role_api_client().create_user( @@ -46,7 +45,7 @@ def test_create_user_with_user_metadata(): assert "profile_image" in response.user.user_metadata -def test_create_user_with_user_and_app_metadata(): +def test_create_user_with_user_and_app_metadata() -> None: user_metadata = mock_user_metadata() app_metadata = mock_app_metadata() credentials = mock_user_credentials() @@ -64,7 +63,7 @@ def test_create_user_with_user_and_app_metadata(): assert "providers" in response.user.app_metadata -def test_list_users_should_return_registered_users(): +def test_list_users_should_return_registered_users() -> None: credentials = mock_user_credentials() create_new_user_with_email(email=credentials.email) users = service_role_api_client().list_users() @@ -74,7 +73,7 @@ def test_list_users_should_return_registered_users(): assert credentials.email in emails -def test_get_user_by_id_should_a_registered_user_given_its_user_identifier(): +def test_get_user_by_id_should_a_registered_user_given_its_user_identifier() -> None: credentials = mock_user_credentials() user = create_new_user_with_email(email=credentials.email) assert user.id @@ -82,7 +81,7 @@ def test_get_user_by_id_should_a_registered_user_given_its_user_identifier(): assert response.user.email == credentials.email -def test_modify_email_using_update_user_by_id(): +def test_modify_email_using_update_user_by_id() -> None: credentials = mock_user_credentials() user = create_new_user_with_email(email=credentials.email) response = service_role_api_client().update_user_by_id( @@ -94,7 +93,7 @@ def test_modify_email_using_update_user_by_id(): assert response.user.email == f"new_{user.email}" -def test_modify_user_metadata_using_update_user_by_id(): +def test_modify_user_metadata_using_update_user_by_id() -> None: credentials = mock_user_credentials() user = create_new_user_with_email(email=credentials.email) user_metadata = {"favorite_color": "yellow"} @@ -108,7 +107,7 @@ def test_modify_user_metadata_using_update_user_by_id(): assert response.user.user_metadata == user_metadata -def test_modify_app_metadata_using_update_user_by_id(): +def test_modify_app_metadata_using_update_user_by_id() -> None: credentials = mock_user_credentials() user = create_new_user_with_email(email=credentials.email) app_metadata = {"roles": ["admin", "publisher"]} @@ -122,7 +121,7 @@ def test_modify_app_metadata_using_update_user_by_id(): assert "roles" in response.user.app_metadata -def test_modify_confirm_email_using_update_user_by_id(): +def test_modify_confirm_email_using_update_user_by_id() -> None: credentials = mock_user_credentials() response = client_api_auto_confirm_off_signups_enabled_client().sign_up( { @@ -132,44 +131,40 @@ def test_modify_confirm_email_using_update_user_by_id(): ) assert response.user assert not response.user.email_confirmed_at - response = service_role_api_client().update_user_by_id( + auth_response = service_role_api_client().update_user_by_id( response.user.id, { "email_confirm": True, }, ) - assert response.user.email_confirmed_at + assert auth_response.user.email_confirmed_at -def test_invalid_credential_sign_in_with_phone(): +def test_invalid_credential_sign_in_with_phone() -> None: try: - response = ( - client_api_auto_confirm_off_signups_enabled_client().sign_in_with_password( - { - "phone": "+123456789", - "password": "strong_pwd", - } - ) + client_api_auto_confirm_off_signups_enabled_client().sign_in_with_password( + { + "phone": "+123456789", + "password": "strong_pwd", + } ) except AuthApiError as e: assert e.to_dict() -def test_invalid_credential_sign_in_with_email(): +def test_invalid_credential_sign_in_with_email() -> None: try: - response = ( - client_api_auto_confirm_off_signups_enabled_client().sign_in_with_password( - { - "email": "unknown_user@unknowndomain.com", - "password": "strong_pwd", - } - ) + client_api_auto_confirm_off_signups_enabled_client().sign_in_with_password( + { + "email": "unknown_user@unknowndomain.com", + "password": "strong_pwd", + } ) except AuthApiError as e: assert e.to_dict() -def test_sign_in_with_otp_email(): +def test_sign_in_with_otp_email() -> None: try: client_api_auto_confirm_off_signups_enabled_client().sign_in_with_otp( { @@ -180,7 +175,7 @@ def test_sign_in_with_otp_email(): assert e.to_dict() -def test_sign_in_with_otp_phone(): +def test_sign_in_with_otp_phone() -> None: try: client_api_auto_confirm_off_signups_enabled_client().sign_in_with_otp( { @@ -191,41 +186,41 @@ def test_sign_in_with_otp_phone(): assert e.to_dict() -def test_resend(): +def test_resend() -> None: client_api_auto_confirm_off_signups_enabled_client().resend( {"phone": "+112345678", "type": "sms"} ) -def test_reauthenticate(): +def test_reauthenticate() -> None: client = auth_client_with_session() client.reauthenticate() -def test_refresh_session(): +def test_refresh_session() -> None: client = auth_client_with_session() client.refresh_session() -def test_reset_password_for_email(): +def test_reset_password_for_email() -> None: credentials = mock_user_credentials() client = auth_client_with_session() client.reset_password_email(email=credentials.email) -def test_resend_missing_credentials(): +def test_resend_missing_credentials() -> None: credentials = mock_user_credentials() client_api_auto_confirm_off_signups_enabled_client().resend( {"type": "email_change", "email": credentials.email} ) -def test_sign_in_anonymously(): +def test_sign_in_anonymously() -> None: client = auth_client_with_session() client.sign_in_anonymously() -def test_delete_user_should_be_able_delete_an_existing_user(): +def test_delete_user_should_be_able_delete_an_existing_user() -> None: credentials = mock_user_credentials() user = create_new_user_with_email(email=credentials.email) service_role_api_client().delete_user(user.id) @@ -234,7 +229,9 @@ def test_delete_user_should_be_able_delete_an_existing_user(): assert credentials.email not in emails -def test_generate_link_supports_sign_up_with_generate_confirmation_signup_link(): +def test_generate_link_supports_sign_up_with_generate_confirmation_signup_link() -> ( + None +): credentials = mock_user_credentials() redirect_to = "http://localhost:9999/welcome" user_metadata = {"status": "alpha"} @@ -252,7 +249,9 @@ def test_generate_link_supports_sign_up_with_generate_confirmation_signup_link() assert response.user.user_metadata == user_metadata -def test_generate_link_supports_updating_emails_with_generate_email_change_links(): # noqa: E501 +def test_generate_link_supports_updating_emails_with_generate_email_change_links() -> ( + None +): # noqa: E501 credentials = mock_user_credentials() user = create_new_user_with_email(email=credentials.email) assert user.email @@ -272,7 +271,7 @@ def test_generate_link_supports_updating_emails_with_generate_email_change_links assert response.user.new_email == credentials.email -def test_invite_user_by_email_creates_a_new_user_with_an_invited_at_timestamp(): +def test_invite_user_by_email_creates_a_new_user_with_an_invited_at_timestamp() -> None: credentials = mock_user_credentials() redirect_to = "http://localhost:9999/welcome" user_metadata = {"status": "alpha"} @@ -286,7 +285,7 @@ def test_invite_user_by_email_creates_a_new_user_with_an_invited_at_timestamp(): assert response.user.invited_at -def test_sign_out_with_an_valid_access_token(): +def test_sign_out_with_an_valid_access_token() -> None: credentials = mock_user_credentials() client = auth_client_with_session() response = client.sign_up( @@ -299,7 +298,7 @@ def test_sign_out_with_an_valid_access_token(): service_role_api_client().sign_out(response.session.access_token) -def test_sign_out_with_an_invalid_access_token(): +def test_sign_out_with_an_invalid_access_token() -> None: try: service_role_api_client().sign_out("this-is-a-bad-token") assert False @@ -307,7 +306,7 @@ def test_sign_out_with_an_invalid_access_token(): pass -def test_verify_otp_with_non_existent_phone_number(): +def test_verify_otp_with_non_existent_phone_number() -> None: credentials = mock_user_credentials() otp = mock_verification_otp() try: @@ -323,7 +322,7 @@ def test_verify_otp_with_non_existent_phone_number(): assert e.message == "Token has expired or is invalid" -def test_verify_otp_with_invalid_phone_number(): +def test_verify_otp_with_invalid_phone_number() -> None: credentials = mock_user_credentials() otp = mock_verification_otp() try: @@ -339,7 +338,7 @@ def test_verify_otp_with_invalid_phone_number(): assert e.message == "Invalid phone number format (E.164 required)" -def test_sign_in_with_id_token(): +def test_sign_in_with_id_token() -> None: try: ( client_api_auto_confirm_off_signups_enabled_client().sign_in_with_id_token( @@ -353,7 +352,7 @@ def test_sign_in_with_id_token(): assert e.to_dict() -def test_sign_in_with_sso(): +def test_sign_in_with_sso() -> None: with pytest.raises(AuthApiError, match=r"SAML 2.0 is disabled") as exc: client_api_auto_confirm_off_signups_enabled_client().sign_in_with_sso( { @@ -363,7 +362,7 @@ def test_sign_in_with_sso(): assert exc.value is not None -def test_sign_in_with_oauth(): +def test_sign_in_with_oauth() -> None: assert client_api_auto_confirm_off_signups_enabled_client().sign_in_with_oauth( { "provider": "google", @@ -371,7 +370,7 @@ def test_sign_in_with_oauth(): ) -def test_link_identity_missing_session(): +def test_link_identity_missing_session() -> None: with pytest.raises(AuthSessionMissingError) as exc: client_api_auto_confirm_off_signups_enabled_client().link_identity( { @@ -381,7 +380,7 @@ def test_link_identity_missing_session(): assert exc.value is not None -def test_get_item_from_memory_storage(): +def test_get_item_from_memory_storage() -> None: credentials = mock_user_credentials() client = auth_client() client.sign_up( @@ -400,7 +399,7 @@ def test_get_item_from_memory_storage(): assert client._storage.get_item(client._storage_key) is not None -def test_remove_item_from_memory_storage(): +def test_remove_item_from_memory_storage() -> None: credentials = mock_user_credentials() client = auth_client() client.sign_up( @@ -419,7 +418,7 @@ def test_remove_item_from_memory_storage(): client._storage.remove_item(client._storage_key) -def test_list_factors(): +def test_list_factors() -> None: credentials = mock_user_credentials() client = auth_client() client.sign_up( @@ -440,7 +439,7 @@ def test_list_factors(): assert isinstance(factors.totp, list) and isinstance(factors.phone, list) -def test_start_auto_refresh_token(): +def test_start_auto_refresh_token() -> None: credentials = mock_user_credentials() client = auth_client() client._auto_refresh_token = True @@ -459,7 +458,7 @@ def test_start_auto_refresh_token(): ) -def test_recover_and_refresh(): +def test_recover_and_refresh() -> None: credentials = mock_user_credentials() client = auth_client() client._auto_refresh_token = True @@ -479,7 +478,7 @@ def test_recover_and_refresh(): client._recover_and_refresh() -def test_get_user_identities(): +def test_get_user_identities() -> None: credentials = mock_user_credentials() client = auth_client() client._auto_refresh_token = True @@ -501,7 +500,7 @@ def test_get_user_identities(): ] == credentials.email -def test_update_user(): +def test_update_user() -> None: credentials = mock_user_credentials() client = auth_client() client._auto_refresh_token = True @@ -520,7 +519,7 @@ def test_update_user(): ) -def test_create_user_with_app_metadata(): +def test_create_user_with_app_metadata() -> None: app_metadata = mock_app_metadata() credentials = mock_user_credentials() response = service_role_api_client().create_user( @@ -535,7 +534,7 @@ def test_create_user_with_app_metadata(): assert "providers" in response.user.app_metadata -def test_weak_email_password_error(): +def test_weak_email_password_error() -> None: credentials = mock_user_credentials() try: client_api_auto_confirm_off_signups_enabled_client().sign_up( @@ -548,7 +547,7 @@ def test_weak_email_password_error(): assert e.to_dict() -def test_weak_phone_password_error(): +def test_weak_phone_password_error() -> None: credentials = mock_user_credentials() try: client_api_auto_confirm_off_signups_enabled_client().sign_up( @@ -561,14 +560,14 @@ def test_weak_phone_password_error(): assert e.to_dict() -def test_get_user_by_id_invalid_id_raises_error(): +def test_get_user_by_id_invalid_id_raises_error() -> None: with pytest.raises( ValueError, match=r"Invalid id, 'invalid_id' is not a valid uuid" ): service_role_api_client().get_user_by_id("invalid_id") -def test_update_user_by_id_invalid_id_raises_error(): +def test_update_user_by_id_invalid_id_raises_error() -> None: with pytest.raises( ValueError, match=r"Invalid id, 'invalid_id' is not a valid uuid" ): @@ -577,21 +576,21 @@ def test_update_user_by_id_invalid_id_raises_error(): ) -def test_delete_user_invalid_id_raises_error(): +def test_delete_user_invalid_id_raises_error() -> None: with pytest.raises( ValueError, match=r"Invalid id, 'invalid_id' is not a valid uuid" ): service_role_api_client().delete_user("invalid_id") -def test_list_factors_invalid_id_raises_error(): +def test_list_factors_invalid_id_raises_error() -> None: with pytest.raises( ValueError, match=r"Invalid id, 'invalid_id' is not a valid uuid" ): service_role_api_client()._list_factors({"user_id": "invalid_id"}) -def test_delete_factor_invalid_id_raises_error(): +def test_delete_factor_invalid_id_raises_error() -> None: # invalid user id with pytest.raises( ValueError, match=r"Invalid id, 'invalid_id' is not a valid uuid" @@ -609,7 +608,7 @@ def test_delete_factor_invalid_id_raises_error(): ) -def test_create_oauth_client(): +def test_create_oauth_client() -> None: """Test creating an OAuth client.""" response = service_role_api_client().oauth.create_client( CreateOAuthClientParams( @@ -622,7 +621,7 @@ def test_create_oauth_client(): assert response.client.client_id is not None -def test_list_oauth_clients(): +def test_list_oauth_clients() -> None: """Test listing OAuth clients.""" client = service_role_api_client() client.oauth.create_client( @@ -637,7 +636,7 @@ def test_list_oauth_clients(): assert any(client.client_id is not None for client in response.clients) -def test_get_oauth_client(): +def test_get_oauth_client() -> None: """Test getting an OAuth client by ID.""" # First create a client create_response = service_role_api_client().oauth.create_client( @@ -654,7 +653,7 @@ def test_get_oauth_client(): # Server is not yet released, so this test is not yet relevant. -# async def test_update_oauth_client(): +# async def test_update_oauth_client() -> None: # """Test updating an OAuth client.""" # # First create a client # client = service_role_api_client() @@ -676,7 +675,7 @@ def test_get_oauth_client(): # assert response.client.client_name == "Updated Test OAuth Client" -def test_delete_oauth_client(): +def test_delete_oauth_client() -> None: """Test deleting an OAuth client.""" # First create a client client = service_role_api_client() @@ -691,7 +690,7 @@ def test_delete_oauth_client(): client.oauth.delete_client(client_id) -def test_regenerate_oauth_client_secret(): +def test_regenerate_oauth_client_secret() -> None: """Test regenerating an OAuth client secret.""" # First create a client create_response = service_role_api_client().oauth.create_client( diff --git a/src/auth/tests/_sync/test_utils.py b/src/auth/tests/_sync/test_utils.py index 87ca2db4..9f237520 100644 --- a/src/auth/tests/_sync/test_utils.py +++ b/src/auth/tests/_sync/test_utils.py @@ -3,24 +3,23 @@ from .clients import ( create_new_user_with_email, mock_app_metadata, - mock_user_credentials, mock_user_metadata, ) -def test_create_new_user_with_email(): +def test_create_new_user_with_email() -> None: email = f"user+{int(time())}@example.com" user = create_new_user_with_email(email=email) assert user.email == email -def test_mock_user_metadata(): +def test_mock_user_metadata() -> None: user_metadata = mock_user_metadata() assert user_metadata assert user_metadata.get("profile_image") -def test_mock_app_metadata(): +def test_mock_app_metadata() -> None: app_metadata = mock_app_metadata() assert app_metadata assert app_metadata.get("roles") diff --git a/src/auth/tests/_sync/utils.py b/src/auth/tests/_sync/utils.py deleted file mode 100644 index e69de29b..00000000 diff --git a/src/auth/tests/conftest.py b/src/auth/tests/conftest.py index 79b61d44..1f010de0 100644 --- a/src/auth/tests/conftest.py +++ b/src/auth/tests/conftest.py @@ -7,7 +7,7 @@ _test_failed_incremental: Dict[str, Dict[Tuple[int, ...], str]] = {} -def pytest_runtest_makereport(item, call): +def pytest_runtest_makereport(item, call) -> None: if "incremental" in item.keywords: # incremental marker is used if call.excinfo is not None: @@ -29,7 +29,7 @@ def pytest_runtest_makereport(item, call): ) -def pytest_runtest_setup(item): +def pytest_runtest_setup(item) -> None: if "incremental" in item.keywords: # retrieve the class name of the test cls_name = str(item.cls) diff --git a/src/auth/tests/test_helpers.py b/src/auth/tests/test_helpers.py index 68abffdf..16a213b2 100644 --- a/src/auth/tests/test_helpers.py +++ b/src/auth/tests/test_helpers.py @@ -25,27 +25,17 @@ model_dump, model_dump_json, model_validate, - parse_auth_response, - parse_jwks, parse_link_identity_response, - parse_link_response, parse_response_api_version, - parse_sso_response, - parse_user_response, validate_exp, ) -from supabase_auth.types import ( - GenerateLinkResponse, - Session, - User, -) from ._sync.clients import mock_access_token TEST_URL = "http://localhost" -def test_handle_exception_with_api_version_and_error_code(): +def test_handle_exception_with_api_version_and_error_code() -> None: err = { "name": "without API version and error code", "code": "unexpected_failure", @@ -65,7 +55,7 @@ def test_handle_exception_with_api_version_and_error_code(): assert exc.value.name == err["ename"] -def test_handle_exception_without_api_version_and_weak_password_error_code(): +def test_handle_exception_without_api_version_and_weak_password_error_code() -> None: err = { "name": "without API version and weak password error code with payload", "code": "weak_password", @@ -87,7 +77,7 @@ def test_handle_exception_without_api_version_and_weak_password_error_code(): assert exc.value.name == err["ename"] -def test_handle_exception_with_api_version_2024_01_01_and_error_code(): +def test_handle_exception_with_api_version_2024_01_01_and_error_code() -> None: err = { "name": "with API version 2024-01-01 and error code", "code": "unexpected_failure", @@ -107,16 +97,17 @@ def test_handle_exception_with_api_version_2024_01_01_and_error_code(): assert exc.value.name == err["ename"] -def test_parse_response_api_version_with_valid_date(): +def test_parse_response_api_version_with_valid_date() -> None: headers = Headers({API_VERSION_HEADER_NAME: "2024-01-01"}) response = Response(headers=headers, status_code=200) api_ver = parse_response_api_version(response) + assert api_ver assert datetime.timestamp(api_ver) == datetime.timestamp( datetime.strptime("2024-01-01", "%Y-%m-%d") ) -def test_parse_response_api_version_with_invalid_dates(): +def test_parse_response_api_version_with_invalid_dates() -> None: dates = ["2024-01-32", "", "notadate", "Sat Feb 24 2024 17:59:17 GMT+0100"] for date in dates: headers = Headers({API_VERSION_HEADER_NAME: date}) @@ -125,12 +116,12 @@ def test_parse_response_api_version_with_invalid_dates(): assert api_ver is None -def test_parse_link_identity_response(): +def test_parse_link_identity_response() -> None: resp = Response(content=f'{{"url": "{TEST_URL}/hello-world"}}', status_code=200) assert parse_link_identity_response(resp) -def test_decode_jwt(): +def test_decode_jwt() -> None: assert decode_jwt(mock_access_token()) with pytest.raises(AuthInvalidJwtError, match=r"Invalid JWT structure") as exc: @@ -138,7 +129,7 @@ def test_decode_jwt(): assert exc.value is not None -def test_generate_pkce_verifier(): +def test_generate_pkce_verifier() -> None: assert isinstance(generate_pkce_verifier(45), str) with pytest.raises( ValueError, match=r"PKCE verifier length must be between 43 and 128 characters" @@ -147,12 +138,12 @@ def test_generate_pkce_verifier(): assert exc.value is not None -def test_generate_pkce_challenge(): +def test_generate_pkce_challenge() -> None: pkce = generate_pkce_verifier(45) assert isinstance(generate_pkce_challenge(pkce), str) -def test_parse_response_api_version_invalid_date(): +def test_parse_response_api_version_invalid_date() -> None: mock_response = MagicMock(spec=Response) mock_response.headers = {API_VERSION_HEADER_NAME: "2023-02-30"} # Invalid date @@ -161,24 +152,22 @@ def test_parse_response_api_version_invalid_date(): # Test for pydantic v1 compatibility in model_validate -def test_model_validate_pydantic_v1(): - # We need to patch the actual calls inside the function - with patch("supabase_auth.helpers.TBaseModel") as MockType: - # Mock the behavior of the try block to raise AttributeError - mock_model = MagicMock() - mock_model.model_validate_json.side_effect = AttributeError - mock_model.parse_raw.return_value = "parsed_obj_result" +def test_model_validate_pydantic_v1() -> None: + # Mock the behavior of the try block to raise AttributeError + mock_model = MagicMock() + mock_model.model_validate_json.side_effect = AttributeError + mock_model.parse_raw.return_value = "parsed_obj_result" - # Use the patched model in the actual function - result = model_validate(mock_model, {"test": "data"}) # type: ignore + # Use the patched model in the actual function + result = model_validate(mock_model, {"test": "data"}) # type: ignore - # Check that parse_obj was called - mock_model.parse_raw.assert_called_once_with({"test": "data"}) - assert result == "parsed_obj_result" + # Check that parse_obj was called + mock_model.parse_raw.assert_called_once_with({"test": "data"}) + assert result == "parsed_obj_result" # Test for pydantic v1 compatibility in model_dump -def test_model_dump_pydantic_v1(): +def test_model_dump_pydantic_v1() -> None: # Create a mock model with necessary behavior mock_model = MagicMock(spec=BaseModel) mock_model.model_dump.side_effect = AttributeError @@ -193,7 +182,7 @@ def test_model_dump_pydantic_v1(): # Test for pydantic v1 compatibility in model_dump_json -def test_model_dump_json_pydantic_v1(): +def test_model_dump_json_pydantic_v1() -> None: # Create a mock model with necessary behavior mock_model = MagicMock(spec=BaseModel) mock_model.model_dump_json.side_effect = AttributeError @@ -207,7 +196,7 @@ def test_model_dump_json_pydantic_v1(): mock_model.json.assert_called_once() -def test_handle_exception_network_error(): +def test_handle_exception_network_error() -> None: # Test case for network errors (502, 503, 504) mock_response = MagicMock(spec=Response) mock_response.status_code = 503 @@ -221,7 +210,7 @@ def test_handle_exception_network_error(): assert result.status == 503 -def test_handle_exception_with_weak_password_attribute(): +def test_handle_exception_with_weak_password_attribute() -> None: # In the implementation there's a logical error in the code: # It checks if data.get("weak_password") is BOTH a dict AND a list # This can never be true. Let's just test the error_code path which works. @@ -246,7 +235,7 @@ def test_handle_exception_with_weak_password_attribute(): assert result.code is None -def test_handle_exception_weak_password_with_error_code(): +def test_handle_exception_weak_password_with_error_code() -> None: # Test case for weak password identified by error_code mock_response = MagicMock(spec=Response) mock_response.status_code = 400 @@ -269,7 +258,7 @@ def test_handle_exception_weak_password_with_error_code(): assert result.reasons == ["Password too simple"] -def test_handle_exception_with_new_api_version(): +def test_handle_exception_with_new_api_version() -> None: # Test case for new API version with "code" field mock_response = MagicMock(spec=Response) mock_response.status_code = 400 @@ -296,7 +285,7 @@ def test_handle_exception_with_new_api_version(): assert result.status == 400 -def test_handle_exception_unknown_error(): +def test_handle_exception_unknown_error() -> None: # Test case for when json() raises an exception mock_response = MagicMock(spec=Response) mock_response.status_code = 500 @@ -311,7 +300,7 @@ def test_handle_exception_unknown_error(): assert "Server error" in result.message -def test_validate_exp_with_expired_exp(): +def test_validate_exp_with_expired_exp() -> None: # Set expiry to 1 hour ago exp = int(datetime.now().timestamp()) - 3600 @@ -319,7 +308,7 @@ def test_validate_exp_with_expired_exp(): validate_exp(exp) -def test_validate_exp_with_valid_exp(): +def test_validate_exp_with_valid_exp() -> None: # Set expiry to 1 hour in the future exp = int(datetime.now().timestamp()) + 3600 @@ -327,7 +316,7 @@ def test_validate_exp_with_valid_exp(): validate_exp(exp) -def test_is_http_url(): +def test_is_http_url() -> None: from supabase_auth.helpers import is_http_url # Test valid HTTP URLs @@ -343,7 +332,7 @@ def test_is_http_url(): assert is_http_url("not a url") is False -def test_handle_exception_weak_password_branch(): +def test_handle_exception_weak_password_branch() -> None: """Specifically targeting the unreachable branch in handle_exception with weak_password. This test attempts to test the branch where weak_password needs to be both a dict and a list, @@ -361,7 +350,7 @@ def test_handle_exception_weak_password_branch(): # Create a special mock dict that pretends to be both a dict and a list class WeirdDict(dict): - def __init__(self, *args, **kwargs): + def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) self.reasons = ["Password too short"] @@ -380,7 +369,7 @@ def __init__(self, *args, **kwargs): # First, we need to monkey patch the implementation temporarily to reach our branch original_isinstance = isinstance - def patched_isinstance(obj, cls): + def patched_isinstance(obj, cls): # noqa # Make weak_password appear as both dict and list when needed if obj == mock_response.json()["weak_password"] and cls in (dict, list): return True From 77465803b4a7ba79080f5e250a17b4787bad3ac8 Mon Sep 17 00:00:00 2001 From: Leonardo Santiago Date: Fri, 31 Oct 2025 10:33:06 -0300 Subject: [PATCH 2/6] chore(auth): ensure ruff checks are run in tests --- src/auth/Makefile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/auth/Makefile b/src/auth/Makefile index 8d4db0ca..ea77bef2 100644 --- a/src/auth/Makefile +++ b/src/auth/Makefile @@ -2,10 +2,15 @@ help:: @echo "Available commands" @echo " help -- (default) print this message" -tests: mypy pytest +tests: ruff mypy pytest help:: @echo " tests -- run all tests for supabase_auth" +ruff: + uv run ruff check tests src/supabase_auth +help:: + @echo " ruff -- run ruff checks" + pytest: start-infra uv run --package supabase_auth pytest --cov=./ --cov-report=xml --cov-report=html -vv From c4a1544d36db236cd1ce3098c974c01be810cf0b Mon Sep 17 00:00:00 2001 From: Leonardo Santiago Date: Fri, 31 Oct 2025 10:40:04 -0300 Subject: [PATCH 3/6] chore: remove ruff from auth tests, root one is able to catch errors --- src/auth/Makefile | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/auth/Makefile b/src/auth/Makefile index ea77bef2..8d4db0ca 100644 --- a/src/auth/Makefile +++ b/src/auth/Makefile @@ -2,15 +2,10 @@ help:: @echo "Available commands" @echo " help -- (default) print this message" -tests: ruff mypy pytest +tests: mypy pytest help:: @echo " tests -- run all tests for supabase_auth" -ruff: - uv run ruff check tests src/supabase_auth -help:: - @echo " ruff -- run ruff checks" - pytest: start-infra uv run --package supabase_auth pytest --cov=./ --cov-report=xml --cov-report=html -vv From 7757d130a93988bd591ed81dbc62f12e95cc9f89 Mon Sep 17 00:00:00 2001 From: Leonardo Santiago Date: Fri, 31 Oct 2025 14:12:37 -0300 Subject: [PATCH 4/6] chore: remove more lints from ignores --- src/auth/pyproject.toml | 3 +-- .../src/supabase_auth/_async/gotrue_client.py | 2 +- .../src/supabase_auth/_sync/gotrue_client.py | 2 +- src/auth/src/supabase_auth/helpers.py | 11 +++++---- src/auth/tests/_async/test_gotrue.py | 4 ++-- src/auth/tests/_sync/test_gotrue.py | 4 ++-- src/auth/tests/_sync/test_gotrue_admin_api.py | 24 +++++++++++-------- 7 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/auth/pyproject.toml b/src/auth/pyproject.toml index a681e761..96c336e6 100644 --- a/src/auth/pyproject.toml +++ b/src/auth/pyproject.toml @@ -66,8 +66,7 @@ select = [ "I", "ANN2" ] -ignore = ["F403", "E712", "E501", "E402", "E722", "E731", "UP006", "UP035"] -# isort.required-imports = ["from __future__ import annotations"] +ignore = ["F403", "E501", "E402", "UP006", "UP035"] [tool.ruff.lint.pyupgrade] # Preserve types, even if a file imports `from __future__ import annotations`. diff --git a/src/auth/src/supabase_auth/_async/gotrue_client.py b/src/auth/src/supabase_auth/_async/gotrue_client.py index 16cbf964..9f7359f0 100644 --- a/src/auth/src/supabase_auth/_async/gotrue_client.py +++ b/src/auth/src/supabase_auth/_async/gotrue_client.py @@ -1274,7 +1274,7 @@ def __del__(self) -> None: try: # Try to cancel the timer self._refresh_token_timer.cancel() - except: + except Exception: # Ignore errors if event loop is closed or selector is not registered pass finally: diff --git a/src/auth/src/supabase_auth/_sync/gotrue_client.py b/src/auth/src/supabase_auth/_sync/gotrue_client.py index afbf7a27..365022b6 100644 --- a/src/auth/src/supabase_auth/_sync/gotrue_client.py +++ b/src/auth/src/supabase_auth/_sync/gotrue_client.py @@ -1264,7 +1264,7 @@ def __del__(self) -> None: try: # Try to cancel the timer self._refresh_token_timer.cancel() - except: + except Exception: # Ignore errors if event loop is closed or selector is not registered pass finally: diff --git a/src/auth/src/supabase_auth/helpers.py b/src/auth/src/supabase_auth/helpers.py index 4b31e7ad..6d9d5ef7 100644 --- a/src/auth/src/supabase_auth/helpers.py +++ b/src/auth/src/supabase_auth/helpers.py @@ -13,7 +13,7 @@ from urllib.parse import urlparse from httpx import HTTPStatusError, Response -from pydantic import BaseModel, TypeAdapter +from pydantic import BaseModel, TypeAdapter, ValidationError from .constants import ( API_VERSION_HEADER_NAME, @@ -80,7 +80,7 @@ def parse_auth_response(response: Response) -> AuthResponse: try: session = model_validate(Session, response.content) user = session.user - except: + except ValidationError: session = None user = model_validate(User, response.content) return AuthResponse(user=user, session=session) @@ -125,9 +125,10 @@ def parse_jwks(response: Response) -> JWKSet: def get_error_message(error: Any) -> str: props = ["msg", "message", "error_description", "error"] - filter = lambda prop: ( - prop in error if isinstance(error, dict) else hasattr(error, prop) - ) + + def filter(prop) -> bool: + return prop in error if isinstance(error, dict) else hasattr(error, prop) + return next((error[prop] for prop in props if filter(prop)), str(error)) diff --git a/src/auth/tests/_async/test_gotrue.py b/src/auth/tests/_async/test_gotrue.py index 16905fa5..a3aae55d 100644 --- a/src/auth/tests/_async/test_gotrue.py +++ b/src/auth/tests/_async/test_gotrue.py @@ -516,7 +516,7 @@ async def test_sign_in_with_otp() -> None: assert args[0] == "POST" assert args[1] == "otp" assert kwargs["body"]["email"] == email - assert kwargs["body"]["create_user"] == True + assert kwargs["body"]["create_user"] assert kwargs["body"]["data"] == {"custom": "data"} assert ( kwargs["body"]["gotrue_meta_security"]["captcha_token"] @@ -552,7 +552,7 @@ async def test_sign_in_with_otp() -> None: assert args[0] == "POST" assert args[1] == "otp" assert kwargs["body"]["phone"] == phone - assert kwargs["body"]["create_user"] == True + assert kwargs["body"]["create_user"] assert kwargs["body"]["data"] == {"custom": "data"} assert kwargs["body"]["channel"] == "whatsapp" assert ( diff --git a/src/auth/tests/_sync/test_gotrue.py b/src/auth/tests/_sync/test_gotrue.py index 7b880ed5..ed28f54e 100644 --- a/src/auth/tests/_sync/test_gotrue.py +++ b/src/auth/tests/_sync/test_gotrue.py @@ -514,7 +514,7 @@ def test_sign_in_with_otp() -> None: assert args[0] == "POST" assert args[1] == "otp" assert kwargs["body"]["email"] == email - assert kwargs["body"]["create_user"] == True + assert kwargs["body"]["create_user"] assert kwargs["body"]["data"] == {"custom": "data"} assert ( kwargs["body"]["gotrue_meta_security"]["captcha_token"] @@ -550,7 +550,7 @@ def test_sign_in_with_otp() -> None: assert args[0] == "POST" assert args[1] == "otp" assert kwargs["body"]["phone"] == phone - assert kwargs["body"]["create_user"] == True + assert kwargs["body"]["create_user"] assert kwargs["body"]["data"] == {"custom": "data"} assert kwargs["body"]["channel"] == "whatsapp" assert ( diff --git a/src/auth/tests/_sync/test_gotrue_admin_api.py b/src/auth/tests/_sync/test_gotrue_admin_api.py index 20fa4835..66a055ac 100644 --- a/src/auth/tests/_sync/test_gotrue_admin_api.py +++ b/src/auth/tests/_sync/test_gotrue_admin_api.py @@ -142,11 +142,13 @@ def test_modify_confirm_email_using_update_user_by_id() -> None: def test_invalid_credential_sign_in_with_phone() -> None: try: - client_api_auto_confirm_off_signups_enabled_client().sign_in_with_password( - { - "phone": "+123456789", - "password": "strong_pwd", - } + ( + client_api_auto_confirm_off_signups_enabled_client().sign_in_with_password( + { + "phone": "+123456789", + "password": "strong_pwd", + } + ) ) except AuthApiError as e: assert e.to_dict() @@ -154,11 +156,13 @@ def test_invalid_credential_sign_in_with_phone() -> None: def test_invalid_credential_sign_in_with_email() -> None: try: - client_api_auto_confirm_off_signups_enabled_client().sign_in_with_password( - { - "email": "unknown_user@unknowndomain.com", - "password": "strong_pwd", - } + ( + client_api_auto_confirm_off_signups_enabled_client().sign_in_with_password( + { + "email": "unknown_user@unknowndomain.com", + "password": "strong_pwd", + } + ) ) except AuthApiError as e: assert e.to_dict() From 6a20f0086cf52078584351f162183baccecdd6c2 Mon Sep 17 00:00:00 2001 From: Leonardo Santiago Date: Fri, 31 Oct 2025 14:26:56 -0300 Subject: [PATCH 5/6] chore: more linting rules --- src/auth/pyproject.toml | 2 +- .../supabase_auth/_async/gotrue_admin_api.py | 12 +++++---- .../supabase_auth/_async/gotrue_base_api.py | 2 +- .../src/supabase_auth/_async/gotrue_client.py | 26 ++++++++++++------- .../supabase_auth/_sync/gotrue_admin_api.py | 12 +++++---- .../supabase_auth/_sync/gotrue_base_api.py | 2 +- .../src/supabase_auth/_sync/gotrue_client.py | 26 ++++++++++++------- src/auth/src/supabase_auth/helpers.py | 4 +-- src/auth/tests/_async/clients.py | 9 ++++--- src/auth/tests/_async/test_gotrue.py | 8 +++--- .../tests/_async/test_gotrue_admin_api.py | 6 ++--- src/auth/tests/_sync/clients.py | 9 ++++--- src/auth/tests/_sync/test_gotrue.py | 8 +++--- src/auth/tests/_sync/test_gotrue_admin_api.py | 6 ++--- 14 files changed, 77 insertions(+), 55 deletions(-) diff --git a/src/auth/pyproject.toml b/src/auth/pyproject.toml index 96c336e6..55f2d987 100644 --- a/src/auth/pyproject.toml +++ b/src/auth/pyproject.toml @@ -59,7 +59,7 @@ select = [ # pyupgrade "UP", # flake8-bugbear - # "B", + "B", # flake8-simplify # "SIM", # isort diff --git a/src/auth/src/supabase_auth/_async/gotrue_admin_api.py b/src/auth/src/supabase_auth/_async/gotrue_admin_api.py index a2eeaf3a..e2443148 100644 --- a/src/auth/src/supabase_auth/_async/gotrue_admin_api.py +++ b/src/auth/src/supabase_auth/_async/gotrue_admin_api.py @@ -41,15 +41,16 @@ def __init__( self, *, url: str = "", - headers: Dict[str, str] = {}, + headers: Optional[Dict[str, str]] = None, http_client: Optional[AsyncClient] = None, verify: bool = True, proxy: Optional[str] = None, ) -> None: + http_headers = headers or {} AsyncGoTrueBaseAPI.__init__( self, url=url, - headers=headers, + headers=http_headers, http_client=http_client, verify=verify, proxy=proxy, @@ -81,16 +82,17 @@ async def sign_out(self, jwt: str, scope: SignOutScope = "global") -> None: async def invite_user_by_email( self, email: str, - options: InviteUserByEmailOptions = {}, + options: Optional[InviteUserByEmailOptions] = None, ) -> UserResponse: """ Sends an invite link to an email address. """ + email_options = options or {} response = await self._request( "POST", "invite", - body={"email": email, "data": options.get("data")}, - redirect_to=options.get("redirect_to"), + body={"email": email, "data": email_options.get("data")}, + redirect_to=email_options.get("redirect_to"), ) return parse_user_response(response) diff --git a/src/auth/src/supabase_auth/_async/gotrue_base_api.py b/src/auth/src/supabase_auth/_async/gotrue_base_api.py index 76b63634..4483495d 100644 --- a/src/auth/src/supabase_auth/_async/gotrue_base_api.py +++ b/src/auth/src/supabase_auth/_async/gotrue_base_api.py @@ -74,4 +74,4 @@ async def _request( response.raise_for_status() return response except (HTTPStatusError, RuntimeError) as e: - raise handle_exception(e) + raise handle_exception(e) # noqa diff --git a/src/auth/src/supabase_auth/_async/gotrue_client.py b/src/auth/src/supabase_auth/_async/gotrue_client.py index 9f7359f0..a721c8d5 100644 --- a/src/auth/src/supabase_auth/_async/gotrue_client.py +++ b/src/auth/src/supabase_auth/_async/gotrue_client.py @@ -673,7 +673,7 @@ async def get_user(self, jwt: Optional[str] = None) -> Optional[UserResponse]: return parse_user_response(await self._request("GET", "user", jwt=jwt)) async def update_user( - self, attributes: UserAttributes, options: UpdateUserOptions = {} + self, attributes: UserAttributes, options: Optional[UpdateUserOptions] ) -> UserResponse: """ Updates user data, if there is a logged in user. @@ -681,11 +681,12 @@ async def update_user( session = await self.get_session() if not session: raise AuthSessionMissingError() + update_options = options or {} response = await self._request( "PUT", "user", body=attributes, - redirect_to=options.get("email_redirect_to"), + redirect_to=update_options.get("email_redirect_to"), jwt=session.access_token, ) user_response = parse_user_response(response) @@ -761,7 +762,7 @@ async def refresh_session( session = await self._call_refresh_token(refresh_token) return AuthResponse(session=session, user=session.user) - async def sign_out(self, options: SignOutOptions = {"scope": "global"}) -> None: + async def sign_out(self, options: Optional[SignOutOptions] = None) -> None: """ `sign_out` will remove the logged in user from the current session and log them out - removing all items from storage and then trigger a `"SIGNED_OUT"` event. @@ -771,13 +772,14 @@ async def sign_out(self, options: SignOutOptions = {"scope": "global"}) -> None: There is no way to revoke a user's access token jwt until it expires. It is recommended to set a shorter expiry on the jwt for this reason. """ + signout_options = options or {"scope": "global"} with suppress(AuthApiError): session = await self.get_session() access_token = session.access_token if session else None if access_token: - await self.admin.sign_out(access_token, options["scope"]) + await self.admin.sign_out(access_token, signout_options["scope"]) - if options["scope"] != "others": + if signout_options["scope"] != "others": await self._remove_session() self._notify_all_subscribers("SIGNED_OUT", None) @@ -801,31 +803,35 @@ def _unsubscribe() -> None: self._state_change_emitters[unique_id] = subscription return subscription - async def reset_password_for_email(self, email: str, options: Options = {}) -> None: + async def reset_password_for_email( + self, email: str, options: Optional[Options] = None + ) -> None: """ Sends a password reset request to an email address. """ + reset_options = options or {} await self._request( "POST", "recover", body={ "email": email, "gotrue_meta_security": { - "captcha_token": options.get("captcha_token"), + "captcha_token": reset_options.get("captcha_token"), }, }, - redirect_to=options.get("redirect_to"), + redirect_to=reset_options.get("redirect_to"), ) async def reset_password_email( self, email: str, - options: Options = {}, + options: Optional[Options] = None, ) -> None: """ Sends a password reset request to an email address. """ - await self.reset_password_for_email(email, options) + + await self.reset_password_for_email(email, options or {}) # MFA methods diff --git a/src/auth/src/supabase_auth/_sync/gotrue_admin_api.py b/src/auth/src/supabase_auth/_sync/gotrue_admin_api.py index e034b699..85857148 100644 --- a/src/auth/src/supabase_auth/_sync/gotrue_admin_api.py +++ b/src/auth/src/supabase_auth/_sync/gotrue_admin_api.py @@ -41,15 +41,16 @@ def __init__( self, *, url: str = "", - headers: Dict[str, str] = {}, + headers: Optional[Dict[str, str]] = None, http_client: Optional[SyncClient] = None, verify: bool = True, proxy: Optional[str] = None, ) -> None: + http_headers = headers or {} SyncGoTrueBaseAPI.__init__( self, url=url, - headers=headers, + headers=http_headers, http_client=http_client, verify=verify, proxy=proxy, @@ -81,16 +82,17 @@ def sign_out(self, jwt: str, scope: SignOutScope = "global") -> None: def invite_user_by_email( self, email: str, - options: InviteUserByEmailOptions = {}, + options: Optional[InviteUserByEmailOptions] = None, ) -> UserResponse: """ Sends an invite link to an email address. """ + email_options = options or {} response = self._request( "POST", "invite", - body={"email": email, "data": options.get("data")}, - redirect_to=options.get("redirect_to"), + body={"email": email, "data": email_options.get("data")}, + redirect_to=email_options.get("redirect_to"), ) return parse_user_response(response) diff --git a/src/auth/src/supabase_auth/_sync/gotrue_base_api.py b/src/auth/src/supabase_auth/_sync/gotrue_base_api.py index 56859992..6101ec89 100644 --- a/src/auth/src/supabase_auth/_sync/gotrue_base_api.py +++ b/src/auth/src/supabase_auth/_sync/gotrue_base_api.py @@ -74,4 +74,4 @@ def _request( response.raise_for_status() return response except (HTTPStatusError, RuntimeError) as e: - raise handle_exception(e) + raise handle_exception(e) # noqa diff --git a/src/auth/src/supabase_auth/_sync/gotrue_client.py b/src/auth/src/supabase_auth/_sync/gotrue_client.py index 365022b6..92339a41 100644 --- a/src/auth/src/supabase_auth/_sync/gotrue_client.py +++ b/src/auth/src/supabase_auth/_sync/gotrue_client.py @@ -669,7 +669,7 @@ def get_user(self, jwt: Optional[str] = None) -> Optional[UserResponse]: return parse_user_response(self._request("GET", "user", jwt=jwt)) def update_user( - self, attributes: UserAttributes, options: UpdateUserOptions = {} + self, attributes: UserAttributes, options: Optional[UpdateUserOptions] ) -> UserResponse: """ Updates user data, if there is a logged in user. @@ -677,11 +677,12 @@ def update_user( session = self.get_session() if not session: raise AuthSessionMissingError() + update_options = options or {} response = self._request( "PUT", "user", body=attributes, - redirect_to=options.get("email_redirect_to"), + redirect_to=update_options.get("email_redirect_to"), jwt=session.access_token, ) user_response = parse_user_response(response) @@ -755,7 +756,7 @@ def refresh_session(self, refresh_token: Optional[str] = None) -> AuthResponse: session = self._call_refresh_token(refresh_token) return AuthResponse(session=session, user=session.user) - def sign_out(self, options: SignOutOptions = {"scope": "global"}) -> None: + def sign_out(self, options: Optional[SignOutOptions] = None) -> None: """ `sign_out` will remove the logged in user from the current session and log them out - removing all items from storage and then trigger a `"SIGNED_OUT"` event. @@ -765,13 +766,14 @@ def sign_out(self, options: SignOutOptions = {"scope": "global"}) -> None: There is no way to revoke a user's access token jwt until it expires. It is recommended to set a shorter expiry on the jwt for this reason. """ + signout_options = options or {"scope": "global"} with suppress(AuthApiError): session = self.get_session() access_token = session.access_token if session else None if access_token: - self.admin.sign_out(access_token, options["scope"]) + self.admin.sign_out(access_token, signout_options["scope"]) - if options["scope"] != "others": + if signout_options["scope"] != "others": self._remove_session() self._notify_all_subscribers("SIGNED_OUT", None) @@ -795,31 +797,35 @@ def _unsubscribe() -> None: self._state_change_emitters[unique_id] = subscription return subscription - def reset_password_for_email(self, email: str, options: Options = {}) -> None: + def reset_password_for_email( + self, email: str, options: Optional[Options] = None + ) -> None: """ Sends a password reset request to an email address. """ + reset_options = options or {} self._request( "POST", "recover", body={ "email": email, "gotrue_meta_security": { - "captcha_token": options.get("captcha_token"), + "captcha_token": reset_options.get("captcha_token"), }, }, - redirect_to=options.get("redirect_to"), + redirect_to=reset_options.get("redirect_to"), ) def reset_password_email( self, email: str, - options: Options = {}, + options: Optional[Options] = None, ) -> None: """ Sends a password reset request to an email address. """ - self.reset_password_for_email(email, options) + + self.reset_password_for_email(email, options or {}) # MFA methods diff --git a/src/auth/src/supabase_auth/helpers.py b/src/auth/src/supabase_auth/helpers.py index 6d9d5ef7..10cfbf34 100644 --- a/src/auth/src/supabase_auth/helpers.py +++ b/src/auth/src/supabase_auth/helpers.py @@ -226,8 +226,8 @@ def decode_jwt(token: str) -> DecodedJWT: header = base64url_to_bytes(parts[0]) payload = base64url_to_bytes(parts[1]) signature = base64url_to_bytes(parts[2]) - except binascii.Error: - raise AuthInvalidJwtError("Invalid JWT structure") + except binascii.Error as e: + raise AuthInvalidJwtError("Invalid JWT structure") from e return DecodedJWT( header=JWTHeaderParser.validate_json(header), diff --git a/src/auth/tests/_async/clients.py b/src/auth/tests/_async/clients.py index a6ed3f3c..fda5360c 100644 --- a/src/auth/tests/_async/clients.py +++ b/src/auth/tests/_async/clients.py @@ -35,14 +35,15 @@ class Credentials: def mock_user_credentials( - options: OptionalCredentials = {}, + options: Optional[OptionalCredentials] = None, ) -> Credentials: fake = Faker() + user_options = options or {} rand_numbers = str(int(time())) return Credentials( - email=options.get("email") or fake.email(), - phone=options.get("phone") or f"1{rand_numbers[-11:]}", - password=options.get("password") or fake.password(), + email=user_options.get("email") or fake.email(), + phone=user_options.get("phone") or f"1{rand_numbers[-11:]}", + password=user_options.get("password") or fake.password(), ) diff --git a/src/auth/tests/_async/test_gotrue.py b/src/auth/tests/_async/test_gotrue.py index a3aae55d..4258bf71 100644 --- a/src/auth/tests/_async/test_gotrue.py +++ b/src/auth/tests/_async/test_gotrue.py @@ -464,14 +464,16 @@ async def test_sign_in_with_password() -> None: "password": "wrong_password", } ) - assert False, "Expected AuthApiError for wrong password" + raise AssertionError("Expected AuthApiError for wrong password") except AuthApiError: pass # Test error case: missing credentials try: await test_client.sign_in_with_password({}) # type: ignore - assert False, "Expected AuthInvalidCredentialsError for missing credentials" + raise AssertionError( + "Expected AuthInvalidCredentialsError for missing credentials" + ) except AuthInvalidCredentialsError: pass @@ -569,7 +571,7 @@ async def test_sign_in_with_otp() -> None: try: await client.sign_in_with_otp({}) # type: ignore - assert False, "Expected AuthInvalidCredentialsError" + raise AssertionError("Expected AuthInvalidCredentialsError") except AuthInvalidCredentialsError: pass diff --git a/src/auth/tests/_async/test_gotrue_admin_api.py b/src/auth/tests/_async/test_gotrue_admin_api.py index 7dfa691f..de48cfcf 100644 --- a/src/auth/tests/_async/test_gotrue_admin_api.py +++ b/src/auth/tests/_async/test_gotrue_admin_api.py @@ -309,7 +309,7 @@ async def test_sign_out_with_an_valid_access_token() -> None: async def test_sign_out_with_an_invalid_access_token() -> None: try: await service_role_api_client().sign_out("this-is-a-bad-token") - assert False + raise AssertionError() except AuthError: pass @@ -325,7 +325,7 @@ async def test_verify_otp_with_non_existent_phone_number() -> None: "type": "sms", }, ) - assert False + raise AssertionError() except AuthError as e: assert e.message == "Token has expired or is invalid" @@ -341,7 +341,7 @@ async def test_verify_otp_with_invalid_phone_number() -> None: "type": "sms", }, ) - assert False + raise AssertionError() except AuthError as e: assert e.message == "Invalid phone number format (E.164 required)" diff --git a/src/auth/tests/_sync/clients.py b/src/auth/tests/_sync/clients.py index 752e16e8..159b68d7 100644 --- a/src/auth/tests/_sync/clients.py +++ b/src/auth/tests/_sync/clients.py @@ -35,14 +35,15 @@ class Credentials: def mock_user_credentials( - options: OptionalCredentials = {}, + options: Optional[OptionalCredentials] = None, ) -> Credentials: fake = Faker() + user_options = options or {} rand_numbers = str(int(time())) return Credentials( - email=options.get("email") or fake.email(), - phone=options.get("phone") or f"1{rand_numbers[-11:]}", - password=options.get("password") or fake.password(), + email=user_options.get("email") or fake.email(), + phone=user_options.get("phone") or f"1{rand_numbers[-11:]}", + password=user_options.get("password") or fake.password(), ) diff --git a/src/auth/tests/_sync/test_gotrue.py b/src/auth/tests/_sync/test_gotrue.py index ed28f54e..53241056 100644 --- a/src/auth/tests/_sync/test_gotrue.py +++ b/src/auth/tests/_sync/test_gotrue.py @@ -462,14 +462,16 @@ def test_sign_in_with_password() -> None: "password": "wrong_password", } ) - assert False, "Expected AuthApiError for wrong password" + raise AssertionError("Expected AuthApiError for wrong password") except AuthApiError: pass # Test error case: missing credentials try: test_client.sign_in_with_password({}) # type: ignore - assert False, "Expected AuthInvalidCredentialsError for missing credentials" + raise AssertionError( + "Expected AuthInvalidCredentialsError for missing credentials" + ) except AuthInvalidCredentialsError: pass @@ -567,7 +569,7 @@ def test_sign_in_with_otp() -> None: try: client.sign_in_with_otp({}) # type: ignore - assert False, "Expected AuthInvalidCredentialsError" + raise AssertionError("Expected AuthInvalidCredentialsError") except AuthInvalidCredentialsError: pass diff --git a/src/auth/tests/_sync/test_gotrue_admin_api.py b/src/auth/tests/_sync/test_gotrue_admin_api.py index 66a055ac..9bdba6f5 100644 --- a/src/auth/tests/_sync/test_gotrue_admin_api.py +++ b/src/auth/tests/_sync/test_gotrue_admin_api.py @@ -305,7 +305,7 @@ def test_sign_out_with_an_valid_access_token() -> None: def test_sign_out_with_an_invalid_access_token() -> None: try: service_role_api_client().sign_out("this-is-a-bad-token") - assert False + raise AssertionError() except AuthError: pass @@ -321,7 +321,7 @@ def test_verify_otp_with_non_existent_phone_number() -> None: "type": "sms", }, ) - assert False + raise AssertionError() except AuthError as e: assert e.message == "Token has expired or is invalid" @@ -337,7 +337,7 @@ def test_verify_otp_with_invalid_phone_number() -> None: "type": "sms", }, ) - assert False + raise AssertionError() except AuthError as e: assert e.message == "Invalid phone number format (E.164 required)" From 5e971e1aec19cc01f68cd8c0d78424d19d6e4c5f Mon Sep 17 00:00:00 2001 From: Leonardo Santiago Date: Fri, 31 Oct 2025 14:32:49 -0300 Subject: [PATCH 6/6] fix: add missing default value --- src/auth/src/supabase_auth/_async/gotrue_client.py | 2 +- src/auth/src/supabase_auth/_sync/gotrue_client.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/auth/src/supabase_auth/_async/gotrue_client.py b/src/auth/src/supabase_auth/_async/gotrue_client.py index a721c8d5..3fb71baf 100644 --- a/src/auth/src/supabase_auth/_async/gotrue_client.py +++ b/src/auth/src/supabase_auth/_async/gotrue_client.py @@ -673,7 +673,7 @@ async def get_user(self, jwt: Optional[str] = None) -> Optional[UserResponse]: return parse_user_response(await self._request("GET", "user", jwt=jwt)) async def update_user( - self, attributes: UserAttributes, options: Optional[UpdateUserOptions] + self, attributes: UserAttributes, options: Optional[UpdateUserOptions] = None ) -> UserResponse: """ Updates user data, if there is a logged in user. diff --git a/src/auth/src/supabase_auth/_sync/gotrue_client.py b/src/auth/src/supabase_auth/_sync/gotrue_client.py index 92339a41..cb65fe37 100644 --- a/src/auth/src/supabase_auth/_sync/gotrue_client.py +++ b/src/auth/src/supabase_auth/_sync/gotrue_client.py @@ -669,7 +669,7 @@ def get_user(self, jwt: Optional[str] = None) -> Optional[UserResponse]: return parse_user_response(self._request("GET", "user", jwt=jwt)) def update_user( - self, attributes: UserAttributes, options: Optional[UpdateUserOptions] + self, attributes: UserAttributes, options: Optional[UpdateUserOptions] = None ) -> UserResponse: """ Updates user data, if there is a logged in user.