- notify.match_changed получил флаг ratings: фото (игрок и админ) его не шлют, правка
партии — только если изменились входы рейтинга (match_service.rating_inputs до/после).
- PATCH /api/groups/{id} публикует group — переименование и правило 9 раундов видны
остальным участникам сразу.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
141 lines
6.4 KiB
Python
141 lines
6.4 KiB
Python
"""Адресаты событий партии. Рейтинг общий (#80): завершённая партия двигает витрины
|
|
всех игроков, поэтому игроки вне группы получают событие ratings (#88)."""
|
|
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 _recipients(published: list[tuple[list[int], dict]], kind: str) -> set[int]:
|
|
return {uid for ids, e in published if e.get("type") == kind for uid in ids}
|
|
|
|
|
|
def _two_groups(client: TestClient, engine) -> tuple[dict, int, int, int, list[int]]:
|
|
"""Хост и Игрок2 в группе партии, Чужой — только в другой группе хоста."""
|
|
me = login(client, "Хост")
|
|
exps = [e["id"] for e in client.get("/api/expansions").json()]
|
|
|
|
def group(name: str) -> int:
|
|
return client.post(
|
|
"/api/groups", json={"name": name, "expansion_ids": exps}, headers=csrf_headers(client)
|
|
).json()["id"]
|
|
|
|
gid, other_gid = group("Группа"), group("Другая")
|
|
p2 = add_group_member(engine, gid, "Игрок2")
|
|
outsider = add_group_member(engine, other_gid, "Чужой")
|
|
fids = [f["id"] for f in client.get(f"/api/groups/{gid}/factions").json()]
|
|
return me, gid, p2, outsider, fids
|
|
|
|
|
|
def test_finished_match_reaches_players_outside_group(client: TestClient, engine, monkeypatch):
|
|
me, gid, p2, outsider, fids = _two_groups(client, engine)
|
|
|
|
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
|
|
# Незавершённая партия рейтинг не двигает — знать о ней нужно только группе.
|
|
assert _recipients(published, "match") == {me["id"], p2}
|
|
assert _recipients(published, "ratings") == set()
|
|
|
|
published.clear()
|
|
fin = finish_match(
|
|
client, started.json()["id"], [{"user_id": me["id"], "place": 1}, {"user_id": p2, "place": 2}]
|
|
)
|
|
assert fin.status_code == 200, fin.text
|
|
assert _recipients(published, "match") == {me["id"], p2}
|
|
ratings = _recipients(published, "ratings")
|
|
assert outsider in ratings
|
|
assert not ratings & {me["id"], p2} # группа уже получила подробное событие
|
|
|
|
|
|
def test_deleting_finished_match_reaches_players_outside_group(
|
|
client: TestClient, engine, monkeypatch
|
|
):
|
|
"""Удаление завершённой партии пересчитывает рейтинг всех, кто играл после неё."""
|
|
me, gid, p2, outsider, fids = _two_groups(client, engine)
|
|
mid = start_match(
|
|
client, gid,
|
|
[{"user_id": me["id"], "faction_id": fids[0]}, {"user_id": p2, "faction_id": fids[1]}],
|
|
).json()["id"]
|
|
finish_match(client, mid, [{"user_id": me["id"], "place": 1}, {"user_id": p2, "place": 2}])
|
|
|
|
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 _recipients(published, "match") == {me["id"], p2}
|
|
assert outsider in _recipients(published, "ratings")
|
|
|
|
|
|
def _finished_match(client: TestClient, me: dict, p2: int, gid: int, fids: list[int]) -> int:
|
|
mid = start_match(
|
|
client, gid,
|
|
[{"user_id": me["id"], "faction_id": fids[0]}, {"user_id": p2, "faction_id": fids[1]}],
|
|
).json()["id"]
|
|
finish_match(client, mid, [{"user_id": me["id"], "place": 1}, {"user_id": p2, "place": 2}])
|
|
return mid
|
|
|
|
|
|
def _patch_match(client: TestClient, mid: int, me: dict, p2: int, fids: list[int], **changes):
|
|
"""PATCH как у формы правки: состав передаётся целиком, даже если меняется комментарий."""
|
|
places = changes.pop("places", (1, 2))
|
|
body = {
|
|
"expected_version": client.get(f"/api/matches/{mid}").json()["version"],
|
|
"participants": [
|
|
{"user_id": me["id"], "faction_id": fids[0], "place": places[0]},
|
|
{"user_id": p2, "faction_id": fids[1], "place": places[1]},
|
|
],
|
|
**changes,
|
|
}
|
|
r = client.patch(f"/api/matches/{mid}", json=body, headers=csrf_headers(client))
|
|
assert r.status_code == 200, r.text
|
|
|
|
|
|
def test_comment_edit_does_not_broadcast_ratings(client: TestClient, engine, monkeypatch):
|
|
"""Правка без влияния на рейтинг — событие только группе: остальным игрокам незачем
|
|
перезапрашивать все рейтинговые витрины."""
|
|
me, gid, p2, outsider, fids = _two_groups(client, engine)
|
|
mid = _finished_match(client, me, p2, gid, fids)
|
|
|
|
published = _capture_events(monkeypatch)
|
|
_patch_match(client, mid, me, p2, fids, overall_comment="Хорошая партия")
|
|
assert _recipients(published, "match") == {me["id"], p2}
|
|
assert _recipients(published, "ratings") == set()
|
|
|
|
|
|
def test_result_edit_broadcasts_ratings(client: TestClient, engine, monkeypatch):
|
|
me, gid, p2, outsider, fids = _two_groups(client, engine)
|
|
mid = _finished_match(client, me, p2, gid, fids)
|
|
|
|
published = _capture_events(monkeypatch)
|
|
_patch_match(client, mid, me, p2, fids, places=(2, 1))
|
|
assert outsider in _recipients(published, "ratings")
|
|
|
|
|
|
def test_group_settings_change_reaches_members(client: TestClient, engine, monkeypatch):
|
|
"""Переключение домашнего правила видно остальным участникам без перезагрузки."""
|
|
me, gid, p2, outsider, fids = _two_groups(client, engine)
|
|
|
|
published = _capture_events(monkeypatch)
|
|
r = client.patch(
|
|
f"/api/groups/{gid}", json={"nine_rounds_rule": True}, headers=csrf_headers(client)
|
|
)
|
|
assert r.status_code == 200, r.text
|
|
assert _recipients(published, "group") == {me["id"], p2}
|