Files
NotBigGhostandClaude Opus 5 640f712037 Рейтинг: единый рейтинг вместо группового
Групповая цепочка убрана: рейтинг у игрока один, по всем партиям приложения
(решение владельца). load_history всегда грузит всю историю, одно
проигрывание на запрос. Страница группы: игры, победы, винрейт и среднее
место — по партиям группы, рейтинг — общий; «Новичок» тоже по общему числу
партий (новое поле rating_confirmed), поэтому опытный игрок в новой группе
ранжирован. Участники, не игравшие в группе, показывают общий рейтинг.
Главная и профиль — общие показатели; в блоке активной группы игры по
группе, рейтинг общий. У profile_stats убран неиспользуемый group_id.

Фронт: цвет рейтинга в списках берётся из rating_confirmed; справка —
«Один рейтинг на всё приложение». Документ рейтинга: раздел 8 и решение 7
в разделе 9.

Тесты: рейтинг в группах равен общему при групповых играх и победах,
главная и блок активной группы, ветеран в новой группе ранжирован,
«Ещё не играли» с общим рейтингом. #80

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqSoRj99iwVEH5U5fnZgsd
2026-09-15 00:18:53 +03:00

70 lines
3.3 KiB
Python

"""Статистика профиля: цифры сходятся с лидербордом, а главная не грузит историю лишний раз."""
from __future__ import annotations
from fastapi.testclient import TestClient
from app.services import stats_service
from tests.conftest import add_group_member, create_finished_match, csrf_headers, login
def _group_with_matches(client: TestClient, engine, games: int = 3) -> tuple[dict, int, int]:
me = login(client, "Статист")
exps = [e["id"] for e in client.get("/api/expansions").json()]
gid = client.post(
"/api/groups", json={"name": "Группа", "expansion_ids": exps}, headers=csrf_headers(client)
).json()["id"]
p2 = add_group_member(engine, gid, "Соперник")
fids = [f["id"] for f in client.get(f"/api/groups/{gid}/factions").json()]
for i in range(games):
# Чередуем победителя и берём разные фракции: средние и разбивка по фракциям
# должны получиться нетривиальными, а фракции в партии не повторяться.
winner_first = i % 2 == 0
mine, theirs = fids[(2 * i) % len(fids)], fids[(2 * i + 1) % len(fids)]
create_finished_match(
client, gid,
[
{"user_id": me["id"], "faction_id": mine, "place": 1 if winner_first else 2},
{"user_id": p2, "faction_id": theirs, "place": 2 if winner_first else 1},
],
)
return me, gid, p2
def test_profile_numbers_match_leaderboard(client: TestClient, engine):
"""Профиль и лидерборд собирают итог разными путями: цифры обязаны совпадать."""
me, gid, p2 = _group_with_matches(client, engine, games=4)
profile = client.get("/api/users/me/stats").json()["overall"]
board = client.get("/api/stats/leaderboard").json()
entry = next(
e for e in board["entries"] + board["provisional"] if e["user_id"] == me["id"]
)
for field in ("games", "wins", "win_rate", "avg_place", "score"):
assert profile[field] == entry[field], field
def test_home_loads_history_once(client: TestClient, engine, monkeypatch):
"""Главная грузит историю партий один раз: топ, профиль и итог активной группы
считаются из неё. Без этой проверки лишняя загрузка тихо вернётся при правке витрин."""
me, gid, p2 = _group_with_matches(client, engine, games=2)
client.put(
"/api/users/me/active-group", json={"group_id": gid}, headers=csrf_headers(client)
)
calls: list[int] = []
original = stats_service.load_history
def spy(session):
calls.append(1)
return original(session)
monkeypatch.setattr(stats_service, "load_history", spy)
r = client.get("/api/home")
assert r.status_code == 200, r.text
assert len(calls) == 1
# Главная всё ещё показывает и профиль, и блок активной группы.
body = r.json()
assert body["profile"]["overall"]["games"] == 2
assert body["active_group"]["games"] == 2