API: поиск игроков по нику для автокомплита (GET /api/users/search)
До 10 подсказок по подстроке ника среди активных игроков (role=player). Регистронезависимо и для кириллицы: SQLite LIKE/lower сворачивают регистр только у ASCII, поэтому фильтрация/ранжирование в Python (casefold): префиксные совпадения раньше подстрочных, внутри — по алфавиту. Пустой q — пустой список. Тесты + регенерированный schema.d.ts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
"""Поиск игроков для автокомплита по нику: GET /api/users/search."""
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
from sqlmodel import Session
|
||||
|
||||
from app.models import User
|
||||
from tests.conftest import login
|
||||
|
||||
|
||||
def _add_user(engine, nickname: str, *, active: bool = True, role: str = "player") -> None:
|
||||
"""Готовим данные напрямую в БД: нужны выключенные аккаунты и админы."""
|
||||
with Session(engine) as s:
|
||||
s.add(
|
||||
User(
|
||||
nickname=nickname,
|
||||
role=role,
|
||||
auth_provider="stub" if role == "player" else "local",
|
||||
is_active=active,
|
||||
# CHECK ck_users_admin_has_password: админ обязан иметь пароль.
|
||||
password_hash="x" if role == "admin" else None,
|
||||
)
|
||||
)
|
||||
s.commit()
|
||||
|
||||
|
||||
def test_search_prefix_first_cyrillic_case_insensitive(client: TestClient, engine):
|
||||
login(client, "Искатель")
|
||||
for nick in ("Барсик", "барс", "Морбарс", "Zed"):
|
||||
_add_user(engine, nick)
|
||||
|
||||
r = client.get("/api/users/search", params={"q": "БАР"})
|
||||
assert r.status_code == 200, r.text
|
||||
got = r.json()
|
||||
# Префиксные раньше подстрочных, внутри — по алфавиту; регистр кириллицы не важен.
|
||||
assert [u["nickname"] for u in got] == ["барс", "Барсик", "Морбарс"]
|
||||
assert {"user_id", "nickname", "avatar_url"} <= set(got[0].keys())
|
||||
|
||||
|
||||
def test_search_empty_query_returns_empty_list(client: TestClient):
|
||||
login(client, "Искатель")
|
||||
r = client.get("/api/users/search", params={"q": " "})
|
||||
assert r.status_code == 200, r.text
|
||||
assert r.json() == []
|
||||
|
||||
|
||||
def test_search_skips_inactive_and_admins(client: TestClient, engine):
|
||||
login(client, "Искатель")
|
||||
_add_user(engine, "Спящий барсук", active=False)
|
||||
_add_user(engine, "адм барс", role="admin")
|
||||
|
||||
r = client.get("/api/users/search", params={"q": "барс"})
|
||||
assert r.status_code == 200, r.text
|
||||
assert r.json() == []
|
||||
|
||||
|
||||
def test_search_respects_limit(client: TestClient, engine):
|
||||
login(client, "Искатель")
|
||||
for i in range(5):
|
||||
_add_user(engine, f"Игрок {i}")
|
||||
|
||||
assert len(client.get("/api/users/search", params={"q": "игрок"}).json()) == 3
|
||||
assert (
|
||||
len(client.get("/api/users/search", params={"q": "игрок", "limit": 5}).json()) == 5
|
||||
)
|
||||
Reference in New Issue
Block a user