Групповая цепочка убрана: рейтинг у игрока один, по всем партиям приложения (решение владельца). 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
86 lines
3.5 KiB
Python
86 lines
3.5 KiB
Python
"""Список игроков группы — только текущий состав (#76).
|
|
|
|
Удалённый из группы игрок пропадает из рейтинга группы, но его партии остаются в
|
|
истории: они уже повлияли на (общий) рейтинг оставшихся."""
|
|
from __future__ import annotations
|
|
|
|
from fastapi.testclient import TestClient
|
|
|
|
from tests.conftest import add_group_member, create_finished_match, csrf_headers, login
|
|
|
|
|
|
def _ids(stats: dict) -> dict[str, set[int]]:
|
|
return {
|
|
block: {e["user_id"] for e in stats[block]}
|
|
for block in ("leaderboard", "provisional", "inactive")
|
|
}
|
|
|
|
|
|
def _group(client: TestClient) -> tuple[int, list[int]]:
|
|
gid = client.post(
|
|
"/api/groups", json={"name": "Группа", "expansion_ids": []}, headers=csrf_headers(client)
|
|
).json()["id"]
|
|
fids = [f["id"] for f in client.get(f"/api/groups/{gid}/factions").json()]
|
|
return gid, fids
|
|
|
|
|
|
def _remove(client: TestClient, gid: int, uid: int) -> None:
|
|
r = client.delete(f"/api/groups/{gid}/members/{uid}", headers=csrf_headers(client))
|
|
assert r.status_code == 200, r.text
|
|
|
|
|
|
def test_removed_member_leaves_group_rating_but_not_overall(client: TestClient, engine):
|
|
me = login(client, "Хозяин")
|
|
gid, fids = _group(client)
|
|
b = add_group_member(engine, gid, "Ушедший")
|
|
create_finished_match(
|
|
client, gid,
|
|
[
|
|
{"user_id": me["id"], "faction_id": fids[0], "place": 1},
|
|
{"user_id": b, "faction_id": fids[1], "place": 2},
|
|
],
|
|
)
|
|
before = client.get(f"/api/groups/{gid}/stats").json()
|
|
my_score = next(e["score"] for e in before["provisional"] if e["user_id"] == me["id"])
|
|
|
|
_remove(client, gid, b)
|
|
|
|
stats = client.get(f"/api/groups/{gid}/stats").json()
|
|
ids = _ids(stats)
|
|
assert all(b not in block for block in ids.values())
|
|
assert me["id"] in ids["provisional"]
|
|
# Партия с ушедшим учтена: рейтинг оставшегося не изменился, счётчик партий тоже.
|
|
assert next(e["score"] for e in stats["provisional"] if e["user_id"] == me["id"]) == my_score
|
|
assert stats["total_matches"] == 1
|
|
|
|
board = client.get("/api/stats/leaderboard").json()
|
|
assert b in {e["user_id"] for e in board["entries"] + board["provisional"]}
|
|
|
|
# Вернули в группу — снова в списке со своей историей.
|
|
add_group_member(engine, gid, "Ушедший")
|
|
back = client.get(f"/api/groups/{gid}/stats").json()
|
|
entry = next(e for e in back["provisional"] if e["user_id"] == b)
|
|
assert (entry["games"], entry["score"]) == (1, 1468)
|
|
|
|
|
|
def test_group_ranks_renumbered_after_removal(client: TestClient, engine):
|
|
me = login(client, "Первый")
|
|
gid, fids = _group(client)
|
|
b = add_group_member(engine, gid, "Второй")
|
|
c = add_group_member(engine, gid, "Третий")
|
|
for _ in range(10):
|
|
create_finished_match(
|
|
client, gid,
|
|
[
|
|
{"user_id": me["id"], "faction_id": fids[0], "place": 1},
|
|
{"user_id": b, "faction_id": fids[1], "place": 2},
|
|
{"user_id": c, "faction_id": fids[2], "place": 3},
|
|
],
|
|
)
|
|
assert [e["rank"] for e in client.get(f"/api/groups/{gid}/stats").json()["leaderboard"]] == [1, 2, 3]
|
|
|
|
_remove(client, gid, b)
|
|
|
|
board = client.get(f"/api/groups/{gid}/stats").json()["leaderboard"]
|
|
assert [(e["user_id"], e["rank"]) for e in board] == [(me["id"], 1), (c, 2)]
|