from __future__ import annotations from types import SimpleNamespace from unittest.mock import MagicMock import pytest from core.http.errors import ApiProblemError from v1.auth.gateway import SupabaseAuthGateway from v1.auth.schemas import ( OtpSendRequest, PhoneSessionCreateRequest, SessionRefreshRequest, ) class TestSupabaseAuthGateway: @pytest.fixture def gateway( self, monkeypatch: pytest.MonkeyPatch ) -> tuple[SupabaseAuthGateway, MagicMock, MagicMock]: mock_client = MagicMock() mock_admin_client = MagicMock() monkeypatch.setattr( "v1.auth.gateway.supabase_service.get_client", lambda: mock_client ) monkeypatch.setattr( "v1.auth.gateway.supabase_service.get_admin_client", lambda: mock_admin_client, ) return SupabaseAuthGateway(), mock_client, mock_admin_client @pytest.mark.asyncio async def test_send_otp_sets_should_create_user( self, gateway: tuple[SupabaseAuthGateway, MagicMock, MagicMock] ) -> None: sut, mock_client, _ = gateway mock_sign_in_with_otp = MagicMock() mock_client.auth.sign_in_with_otp = mock_sign_in_with_otp await sut.send_otp(OtpSendRequest(phone="+8613812345678")) mock_sign_in_with_otp.assert_called_once_with( { "phone": "+8613812345678", "options": {"should_create_user": True}, } ) @pytest.mark.asyncio async def test_create_phone_session_uses_verify_otp( self, gateway: tuple[SupabaseAuthGateway, MagicMock, MagicMock] ) -> None: sut, mock_client, _ = gateway verify_response = SimpleNamespace( session=SimpleNamespace( access_token="access", refresh_token="refresh", expires_in=3600, token_type="bearer", ), user=SimpleNamespace(id="user-1", phone="+8613812345678"), ) mock_client.auth.verify_otp = MagicMock(return_value=verify_response) response = await sut.create_phone_session( PhoneSessionCreateRequest(phone="+8613812345678", token="123456") ) assert response.user.id == "user-1" assert response.access_token == "access" @pytest.mark.asyncio async def test_create_phone_session_normalizes_phone_without_plus_prefix( self, gateway: tuple[SupabaseAuthGateway, MagicMock, MagicMock] ) -> None: sut, mock_client, _ = gateway verify_response = SimpleNamespace( session=SimpleNamespace( access_token="access", refresh_token="refresh", expires_in=3600, token_type="bearer", ), user=SimpleNamespace(id="user-1", phone="14155552671"), ) mock_client.auth.verify_otp = MagicMock(return_value=verify_response) response = await sut.create_phone_session( PhoneSessionCreateRequest(phone="+14155552671", token="123456") ) assert response.user.phone == "+14155552671" @pytest.mark.asyncio async def test_refresh_session_maps_invalid_token( self, gateway: tuple[SupabaseAuthGateway, MagicMock, MagicMock] ) -> None: sut, mock_client, _ = gateway mock_client.auth.refresh_session = MagicMock( return_value=SimpleNamespace(session=None, user=None) ) with pytest.raises(ApiProblemError) as exc_info: await sut.refresh_session(SessionRefreshRequest(refresh_token="bad")) assert exc_info.value.status_code == 401 @pytest.mark.asyncio async def test_get_user_by_phone_uses_in_memory_cache( self, gateway: tuple[SupabaseAuthGateway, MagicMock, MagicMock], monkeypatch: pytest.MonkeyPatch, ) -> None: sut, _, _ = gateway user = SimpleNamespace( id="user-1", phone="+8613811112222", created_at="2026-03-16T00:00:00Z", phone_confirmed_at=None, ) list_calls = {"count": 0} def _fake_list_auth_users(_client: object) -> list[SimpleNamespace]: list_calls["count"] += 1 return [user] monkeypatch.setattr("v1.auth.gateway._list_auth_users", _fake_list_auth_users) first = await sut.get_user_by_phone("+8613811112222") second = await sut.get_user_by_phone("+8613811112222") assert first.id == "user-1" assert second.phone == "+8613811112222" assert list_calls["count"] == 1 @pytest.mark.asyncio async def test_search_user_ids_by_phone_supports_suffix_query( self, gateway: tuple[SupabaseAuthGateway, MagicMock, MagicMock], monkeypatch: pytest.MonkeyPatch, ) -> None: sut, _, _ = gateway users = [ SimpleNamespace( id="user-cn", phone="+8613811112222", created_at="2026-03-16T00:00:00Z", phone_confirmed_at=None, ), SimpleNamespace( id="user-us", phone="+14155552671", created_at="2026-03-16T00:00:00Z", phone_confirmed_at=None, ), ] monkeypatch.setattr("v1.auth.gateway._list_auth_users", lambda _client: users) matched_cn = await sut.search_user_ids_by_phone("13811112222") matched_us = await sut.search_user_ids_by_phone("4155552671") assert matched_cn == ["user-cn"] assert matched_us == ["user-us"]