Событие о партии рассылается всей группе, и клиент по нему инвалидировал историю игр, публичные профили и личную статистику получателя. В группе из шести человек любая партия двоих заставляла остальные четыре вкладки перезапрашивать свою историю и открытый профиль, хотя у них ничего не изменилось. Теперь событие несёт participant_ids. Общие витрины (карточка партии, списки и статистика группы, топ и главная) обновляются у всех — рейтинг глобальный, чужая партия действительно двигает топ. История, публичный профиль и личная статистика обновляются только у тех, кто играл, и у зрителей их профилей. При удалении партии участники собираются ДО удаления: каскад уносит их строки вместе с партией, и собранный после список всегда был бы пустым. Это же поведение закреплено тестом. Событие без participant_ids (вкладка открыта до обновления сервера) обрабатывается по-старому, широко: обновление бэкенда не ломает уже открытые страницы. #33 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BoiJK9ux8peeyjLb8TYjFf
78 lines
3.4 KiB
Python
78 lines
3.4 KiB
Python
"""Событие партии несёт список участников: по нему клиент решает, чьи витрины протухли."""
|
|
from __future__ import annotations
|
|
|
|
from fastapi.testclient import TestClient
|
|
|
|
from tests.conftest import add_group_member, csrf_headers, finish_match, login, start_match
|
|
|
|
|
|
def _capture_events(monkeypatch) -> list[tuple[list[int], dict]]:
|
|
from app.core import events
|
|
|
|
published: list[tuple[list[int], dict]] = []
|
|
monkeypatch.setattr(
|
|
events.hub, "publish", lambda ids, event: published.append((list(ids), event))
|
|
)
|
|
return published
|
|
|
|
|
|
def _match_events(published: list[tuple[list[int], dict]]) -> list[dict]:
|
|
return [e for _ids, e in published if e.get("type") == "match"]
|
|
|
|
|
|
def test_match_event_carries_participants(client: TestClient, engine, monkeypatch):
|
|
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, "Игрок2")
|
|
# Третий в группе, но НЕ в партии: его история от этой партии не меняется.
|
|
p3 = add_group_member(engine, gid, "Зритель")
|
|
fids = [f["id"] for f in client.get(f"/api/groups/{gid}/factions").json()]
|
|
|
|
published = _capture_events(monkeypatch)
|
|
started = start_match(
|
|
client, gid,
|
|
[{"user_id": me["id"], "faction_id": fids[0]}, {"user_id": p2, "faction_id": fids[1]}],
|
|
)
|
|
assert started.status_code == 200, started.text
|
|
mid = started.json()["id"]
|
|
|
|
ev = _match_events(published)[-1]
|
|
assert sorted(ev["participant_ids"]) == sorted([me["id"], p2])
|
|
assert p3 not in ev["participant_ids"]
|
|
|
|
published.clear()
|
|
fin = finish_match(
|
|
client, mid, [{"user_id": me["id"], "place": 1}, {"user_id": p2, "place": 2}]
|
|
)
|
|
assert fin.status_code == 200, fin.text
|
|
assert sorted(_match_events(published)[-1]["participant_ids"]) == sorted([me["id"], p2])
|
|
|
|
|
|
def test_delete_event_carries_participants(client: TestClient, engine, monkeypatch):
|
|
"""Удаление — главный случай: строки участников уже уничтожены каскадом.
|
|
|
|
Если собирать их после удаления, список всегда окажется пустым, и клиент не
|
|
обновит историю тем, кто в этой партии играл."""
|
|
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, "Игрок2")
|
|
fids = [f["id"] for f in client.get(f"/api/groups/{gid}/factions").json()]
|
|
mid = start_match(
|
|
client, gid,
|
|
[{"user_id": me["id"], "faction_id": fids[0]}, {"user_id": p2, "faction_id": fids[1]}],
|
|
).json()["id"]
|
|
|
|
published = _capture_events(monkeypatch)
|
|
version = client.get(f"/api/matches/{mid}").json()["version"]
|
|
r = client.delete(
|
|
f"/api/matches/{mid}", params={"expected_version": version}, headers=csrf_headers(client)
|
|
)
|
|
assert r.status_code == 200, r.text
|
|
assert sorted(_match_events(published)[-1]["participant_ids"]) == sorted([me["id"], p2])
|