104 lines
3.1 KiB
Python
104 lines
3.1 KiB
Python
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from unittest.mock import MagicMock
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
|
||
|
|
from app.features.auth import repo
|
||
|
|
from app.features.auth.google import GoogleIdentity
|
||
|
|
from app.features.auth.service import AuthError, AuthService
|
||
|
|
from app.features.user.models import User
|
||
|
|
|
||
|
|
|
||
|
|
def _identity(
|
||
|
|
*,
|
||
|
|
subject: str = "google-sub-1",
|
||
|
|
email: str = "person@example.com",
|
||
|
|
name: str = "Google Person",
|
||
|
|
) -> GoogleIdentity:
|
||
|
|
return GoogleIdentity(
|
||
|
|
subject=subject,
|
||
|
|
email=email,
|
||
|
|
email_verified=True,
|
||
|
|
name=name,
|
||
|
|
picture="https://example.com/avatar.jpg",
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_google_login_creates_user(
|
||
|
|
auth_session_factory,
|
||
|
|
monkeypatch: pytest.MonkeyPatch,
|
||
|
|
) -> None:
|
||
|
|
monkeypatch.setattr(
|
||
|
|
"app.features.auth.service.verify_google_id_token",
|
||
|
|
lambda _token: _identity(),
|
||
|
|
)
|
||
|
|
|
||
|
|
async with auth_session_factory() as db:
|
||
|
|
svc = AuthService(db=db, sms=MagicMock())
|
||
|
|
result = await svc.login_with_google("id-token", language="en")
|
||
|
|
|
||
|
|
assert result["access_token"]
|
||
|
|
assert result["refresh_token"]
|
||
|
|
assert result["is_new_user"] is True
|
||
|
|
|
||
|
|
user = await repo.get_user_by_openid("google:google-sub-1", db)
|
||
|
|
assert user is not None
|
||
|
|
assert user.phone == "google:google-sub-1"
|
||
|
|
assert user.email == "person@example.com"
|
||
|
|
assert user.nickname == "Google Person"
|
||
|
|
assert user.language_preference == "en"
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_google_login_links_existing_verified_email_user(
|
||
|
|
auth_session_factory,
|
||
|
|
make_test_user,
|
||
|
|
monkeypatch: pytest.MonkeyPatch,
|
||
|
|
) -> None:
|
||
|
|
monkeypatch.setattr(
|
||
|
|
"app.features.auth.service.verify_google_id_token",
|
||
|
|
lambda _token: _identity(email="linked@example.com"),
|
||
|
|
)
|
||
|
|
|
||
|
|
async with auth_session_factory() as db:
|
||
|
|
existing: User = make_test_user(nickname="Existing")
|
||
|
|
existing.email = "linked@example.com"
|
||
|
|
db.add(existing)
|
||
|
|
await db.commit()
|
||
|
|
|
||
|
|
svc = AuthService(db=db, sms=MagicMock())
|
||
|
|
result = await svc.login_with_google("id-token", language="en")
|
||
|
|
|
||
|
|
assert result["is_new_user"] is False
|
||
|
|
await db.refresh(existing)
|
||
|
|
assert existing.openid == "google:google-sub-1"
|
||
|
|
assert existing.nickname == "Existing"
|
||
|
|
assert existing.language_preference != "en"
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_google_login_rejects_email_bound_to_other_openid(
|
||
|
|
auth_session_factory,
|
||
|
|
make_test_user,
|
||
|
|
monkeypatch: pytest.MonkeyPatch,
|
||
|
|
) -> None:
|
||
|
|
monkeypatch.setattr(
|
||
|
|
"app.features.auth.service.verify_google_id_token",
|
||
|
|
lambda _token: _identity(email="taken@example.com"),
|
||
|
|
)
|
||
|
|
|
||
|
|
async with auth_session_factory() as db:
|
||
|
|
existing: User = make_test_user(nickname="Existing")
|
||
|
|
existing.email = "taken@example.com"
|
||
|
|
existing.openid = "google:another-sub"
|
||
|
|
db.add(existing)
|
||
|
|
await db.commit()
|
||
|
|
|
||
|
|
svc = AuthService(db=db, sms=MagicMock())
|
||
|
|
with pytest.raises(AuthError) as exc_info:
|
||
|
|
await svc.login_with_google("id-token")
|
||
|
|
|
||
|
|
assert exc_info.value.code == "EMAIL_EXISTS"
|