diff --git a/backend/app/routers/dev_auth.py b/backend/app/routers/dev_auth.py index 0a32fcb..7f36f2f 100644 --- a/backend/app/routers/dev_auth.py +++ b/backend/app/routers/dev_auth.py @@ -10,7 +10,7 @@ from fastapi import APIRouter, Depends, Request, Response from sqlmodel import Session, select from app.auth.dev_stub import DevStubProvider -from app.auth.login import login_with_identity +from app.auth.login import establish_session, login_with_identity from app.auth.provider import ExternalIdentity from app.db.session import get_session from app.models import User @@ -28,7 +28,19 @@ def dev_login( response: Response, session: Session = Depends(get_session), ) -> s.MeRead: - identity = DevStubProvider().authenticate({"nickname": body.nickname}) + # Dev-вход «стать игроком с этим ником». Если такой игрок уже есть (любого провайдера — + # Telegram, или stub с изменённым позже ником), входим именно в него, а не плодим дубли. + # Только если игрока с таким ником нет — создаём нового stub-пользователя. + nickname = (body.nickname or "").strip() + existing = ( + session.exec(select(User).where(User.nickname == nickname)).first() + if nickname + else None + ) + if existing is not None: + user = establish_session(session, response, request, existing, existing.auth_provider) + return build_me(session, user) + identity = DevStubProvider().authenticate({"nickname": nickname}) user = login_with_identity(session, response, request, identity) return build_me(session, user) diff --git a/backend/tests/test_auth.py b/backend/tests/test_auth.py index 810ad4f..184be38 100644 --- a/backend/tests/test_auth.py +++ b/backend/tests/test_auth.py @@ -58,6 +58,47 @@ def _telegram_payload(token: str, **fields) -> dict: return {**data, "hash": sig} +def test_dev_login_into_telegram_user_no_duplicate(client: TestClient, engine, monkeypatch): + """Dev-вход по нику Telegram-аккаунта входит в него, а не создаёт дубль «ник 2».""" + from sqlmodel import Session, select + + from app.core.config import settings + from app.models import User + + monkeypatch.setattr(settings, "telegram_bot_token", "TEST_BOT_TOKEN") + r = client.post("/api/auth/telegram", json=_telegram_payload("TEST_BOT_TOKEN")) + assert r.status_code == 200, r.text + tg_id, nick = r.json()["id"], r.json()["nickname"] + + client.cookies.clear() # «другой вход» — без сессии + r2 = client.post("/api/auth/dev/login", json={"nickname": nick}) + assert r2.status_code == 200, r2.text + assert r2.json()["id"] == tg_id # тот же аккаунт, не новый + + with Session(engine) as s: + users = s.exec(select(User).where(User.nickname == nick)).all() + assert len(users) == 1 # дубликат не создан + + +def test_dev_login_after_rename_no_duplicate(client: TestClient, engine): + """После смены ника dev-вход по новому нику входит в того же игрока (без дубля).""" + from sqlmodel import Session, select + + from app.models import User + + uid = client.post("/api/auth/dev/login", json={"nickname": "Старый"}).json()["id"] + pr = client.patch("/api/users/me", json={"nickname": "Новый"}, headers=csrf_headers(client)) + assert pr.status_code == 200, pr.text + + client.cookies.clear() + r2 = client.post("/api/auth/dev/login", json={"nickname": "Новый"}) + assert r2.status_code == 200, r2.text + assert r2.json()["id"] == uid + + with Session(engine) as s: + assert len(s.exec(select(User).where(User.nickname == "Новый")).all()) == 1 + + def test_telegram_login_valid_signature(client: TestClient, monkeypatch): from app.core.config import settings