from __future__ import annotations import pytest import v1.auth.gateway as auth_gateway_module from v1.auth.schemas import ( AuthTokenResponse, AuthResendCodeResponse, AuthSignupStartResponse, AuthUserByEmailResponse, AuthUser, LoginRequest, RefreshRequest, SignupResendRequest, SignupStartRequest, SignupVerifyRequest, ) from v1.auth.service import AuthService, AuthServiceGateway class FakeGateway(AuthServiceGateway): def __init__(self, response: AuthTokenResponse) -> None: self._response = response async def signup_start( self, request: SignupStartRequest ) -> AuthSignupStartResponse: return AuthSignupStartResponse(email=request.email) async def signup_verify(self, request: SignupVerifyRequest) -> AuthTokenResponse: return self._response async def signup_resend( self, request: SignupResendRequest ) -> AuthResendCodeResponse: return AuthResendCodeResponse() async def login(self, request: LoginRequest) -> AuthTokenResponse: return self._response async def refresh(self, request: RefreshRequest) -> AuthTokenResponse: return self._response async def logout(self, refresh_token: str | None) -> None: return None async def get_user_by_email(self, email: str) -> AuthUserByEmailResponse: return AuthUserByEmailResponse( id="user-1", email=email, created_at="2026-02-24T00:00:00Z", email_confirmed_at=None, ) @pytest.mark.asyncio async def test_signup_maps_response() -> None: user = AuthUser(id="user-1", email="user@example.com") token_response = AuthTokenResponse( access_token="access", refresh_token="refresh", expires_in=3600, token_type="bearer", user=user, ) service = AuthService(gateway=FakeGateway(token_response)) start_result = await service.signup_start( SignupStartRequest( username="demo", email="user@example.com", password="secret123" ) ) assert start_result.status == "pending_verification" assert start_result.email == "user@example.com" result = await service.signup_verify( SignupVerifyRequest(email="user@example.com", token="123456") ) assert result.access_token == "access" assert result.refresh_token == "refresh" assert result.user.id == "user-1" class LogoutAssertingGateway(AuthServiceGateway): def __init__(self, expected_refresh_token: str) -> None: self._expected_refresh_token = expected_refresh_token async def signup_start( self, request: SignupStartRequest ) -> AuthSignupStartResponse: raise NotImplementedError async def signup_verify(self, request: SignupVerifyRequest) -> AuthTokenResponse: raise NotImplementedError async def signup_resend( self, request: SignupResendRequest ) -> AuthResendCodeResponse: raise NotImplementedError async def login(self, request: LoginRequest) -> AuthTokenResponse: raise NotImplementedError async def refresh(self, request: RefreshRequest) -> AuthTokenResponse: raise NotImplementedError async def logout(self, refresh_token: str | None) -> None: assert refresh_token == self._expected_refresh_token async def get_user_by_email(self, email: str) -> AuthUserByEmailResponse: raise NotImplementedError @pytest.mark.asyncio async def test_logout_forwards_refresh_token() -> None: service = AuthService(gateway=LogoutAssertingGateway("refresh-token")) await service.logout("refresh-token") @pytest.mark.asyncio async def test_get_user_by_email_forwards_to_gateway() -> None: user = AuthUser(id="user-1", email="user@example.com") token_response = AuthTokenResponse( access_token="access", refresh_token="refresh", expires_in=3600, token_type="bearer", user=user, ) service = AuthService(gateway=FakeGateway(token_response)) result = await service.get_user_by_email("user@example.com") assert result.email == "user@example.com" @pytest.mark.asyncio async def test_signup_resend_returns_generic_message() -> None: user = AuthUser(id="user-1", email="user@example.com") token_response = AuthTokenResponse( access_token="access", refresh_token="refresh", expires_in=3600, token_type="bearer", user=user, ) service = AuthService(gateway=FakeGateway(token_response)) result = await service.signup_resend(SignupResendRequest(email="user@example.com")) assert result.message == "If the email exists, a verification code has been sent" @pytest.mark.asyncio async def test_supabase_signup_passes_username_in_metadata( monkeypatch: pytest.MonkeyPatch, ) -> None: captured_payload: dict[str, object] = {} class FakeSupabaseAuth: def sign_up(self, payload: dict[str, object]) -> object: captured_payload.update(payload) class _User: id = "user-1" email = "user@example.com" class _Session: access_token = "access" refresh_token = "refresh" expires_in = 3600 token_type = "bearer" class _Response: user = _User() session = None return _Response() class FakeClient: auth = FakeSupabaseAuth() monkeypatch.setattr(auth_gateway_module, "create_client", lambda *_: FakeClient()) gateway = auth_gateway_module.SupabaseAuthGateway() await gateway.signup_start( SignupStartRequest( username="demo", email="user@example.com", password="secret123", ) ) assert captured_payload["data"] == {"username": "demo"}