SSE: событие партии несёт участников, клиент не дёргает лишних
Событие о партии рассылается всей группе, и клиент по нему инвалидировал историю игр, публичные профили и личную статистику получателя. В группе из шести человек любая партия двоих заставляла остальные четыре вкладки перезапрашивать свою историю и открытый профиль, хотя у них ничего не изменилось. Теперь событие несёт participant_ids. Общие витрины (карточка партии, списки и статистика группы, топ и главная) обновляются у всех — рейтинг глобальный, чужая партия действительно двигает топ. История, публичный профиль и личная статистика обновляются только у тех, кто играл, и у зрителей их профилей. При удалении партии участники собираются ДО удаления: каскад уносит их строки вместе с партией, и собранный после список всегда был бы пустым. Это же поведение закреплено тестом. Событие без participant_ids (вкладка открыта до обновления сервера) обрабатывается по-старому, широко: обновление бэкенда не ломает уже открытые страницы. #33 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BoiJK9ux8peeyjLb8TYjFf
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
"""Событие партии несёт список участников: по нему клиент решает, чьи витрины протухли."""
|
||||
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])
|
||||
Reference in New Issue
Block a user