Партия: правка истории и кнопка «Редактировать» у игрока
Правка завершённой партии проверяла состав теми же правилами, что и создание: участник обязан состоять в группе сейчас, фракция — быть доступной сейчас. После отключения дополнения партию, сыгранную на Тау, было уже не исправить, а после удаления игрока из группы — любую партию с ним. Теперь то, что уже записано в партии, проходит всегда, а новые игроки и фракции по-прежнему берутся только из текущего состава: чинить историю можно, занести в неё постороннего — нет. Создание партии не ослабло. Править завершённую партию умел любой участник группы, но только через API — кнопки не было, и на практике это мог сделать лишь админ через админку. Теперь у блока «Результаты» есть «Редактировать», и правка идёт тем же перетаскиванием, что и завершение: раскладка восстанавливается из сохранённых мест (одинаковое место — ничья, выбывшие отдельно), рядом — фракции, причина победы и комментарии. Сохранение шлёт версию партии, так что устаревшая правка отклоняется как раньше. Фракции вынесены отдельным блоком, а не в PlaceEditor: он занят перетаскиванием, и селекты внутри него — лишний риск. В списке доступных фракций к набору группы добавляются те, что уже стоят в партии, — иначе фракцию из отключённого дополнения нельзя было бы даже оставить как есть. #29 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,142 @@
|
||||
"""Правка завершённой партии игроком: история чинится после изменений в группе."""
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from tests.conftest import add_group_member, create_finished_match, csrf_headers, login
|
||||
|
||||
|
||||
def _exp_id(client: TestClient, code: str) -> int:
|
||||
return next(e["id"] for e in client.get("/api/expansions").json() if e["code"] == code)
|
||||
|
||||
|
||||
def _played(client: TestClient, engine) -> tuple[dict, int, int, int, dict]:
|
||||
"""Партия «Аня против Бори» в группе с обоими дополнениями."""
|
||||
me = login(client, "Аня")
|
||||
fw, fv = _exp_id(client, "forgotten_worlds"), _exp_id(client, "forsaken_voids")
|
||||
gid = client.post(
|
||||
"/api/groups", json={"name": "Группа", "expansion_ids": [fw, fv]},
|
||||
headers=csrf_headers(client),
|
||||
).json()["id"]
|
||||
b = add_group_member(engine, gid, "Боря")
|
||||
factions = {f["code"]: f["id"] for f in client.get(f"/api/groups/{gid}/factions").json()}
|
||||
mid = create_finished_match(
|
||||
client, gid,
|
||||
[
|
||||
{"user_id": me["id"], "faction_id": factions["orks"], "place": 1},
|
||||
{"user_id": b, "faction_id": factions["tau"], "place": 2},
|
||||
],
|
||||
)["id"]
|
||||
return me, gid, b, mid, factions
|
||||
|
||||
|
||||
def _swap_places(client: TestClient, mid: int, me_id: int, b: int, factions: dict) -> dict:
|
||||
detail = client.get(f"/api/matches/{mid}").json()
|
||||
fid = {p["user_id"]: p["faction_id"] for p in detail["participants"]}
|
||||
return {
|
||||
"participants": [
|
||||
{"user_id": me_id, "faction_id": fid[me_id], "place": 2},
|
||||
{"user_id": b, "faction_id": fid[b], "place": 1},
|
||||
],
|
||||
"expected_version": detail["version"],
|
||||
}
|
||||
|
||||
|
||||
def test_edit_after_expansion_disabled(client: TestClient, engine):
|
||||
"""Дополнение выключили — партия на Тау всё равно правится.
|
||||
|
||||
Регрессия: правка проверяла фракции по ТЕКУЩЕМУ набору группы, и партия,
|
||||
сыгранная на фракции из отключённого дополнения, становилась неисправимой."""
|
||||
me, gid, b, mid, factions = _played(client, engine)
|
||||
|
||||
off = client.put(
|
||||
f"/api/groups/{gid}/expansions",
|
||||
json={"expansion_ids": [_exp_id(client, "forsaken_voids")]},
|
||||
headers=csrf_headers(client),
|
||||
)
|
||||
assert off.status_code == 200, off.text
|
||||
|
||||
r = client.patch(
|
||||
f"/api/matches/{mid}", json=_swap_places(client, mid, me["id"], b, factions),
|
||||
headers=csrf_headers(client),
|
||||
)
|
||||
assert r.status_code == 200, r.text
|
||||
places = {p["user_id"]: p["place"] for p in client.get(f"/api/matches/{mid}").json()["participants"]}
|
||||
assert places[b] == 1 and places[me["id"]] == 2
|
||||
|
||||
|
||||
def test_edit_after_player_left_group(client: TestClient, engine):
|
||||
"""Игрока убрали из группы — партия с ним всё равно правится."""
|
||||
me, gid, b, mid, factions = _played(client, engine)
|
||||
|
||||
out = client.delete(f"/api/groups/{gid}/members/{b}", headers=csrf_headers(client))
|
||||
assert out.status_code == 200, out.text
|
||||
|
||||
r = client.patch(
|
||||
f"/api/matches/{mid}", json=_swap_places(client, mid, me["id"], b, factions),
|
||||
headers=csrf_headers(client),
|
||||
)
|
||||
assert r.status_code == 200, r.text
|
||||
|
||||
|
||||
def test_edit_rejects_new_outsider_and_unavailable_faction(client: TestClient, engine):
|
||||
"""Послабление — только для того, что уже в партии.
|
||||
|
||||
Вписать нового игрока не из группы или фракцию, которой в партии не было и у
|
||||
группы нет, по-прежнему нельзя: иначе в историю можно занести что угодно."""
|
||||
me, gid, b, mid, factions = _played(client, engine)
|
||||
stranger = client.post(
|
||||
"/api/auth/dev/users", json={"nickname": "Чужак"}, headers=csrf_headers(client)
|
||||
).json()
|
||||
detail = client.get(f"/api/matches/{mid}").json()
|
||||
fid = {p["user_id"]: p["faction_id"] for p in detail["participants"]}
|
||||
|
||||
bad_user = client.patch(
|
||||
f"/api/matches/{mid}",
|
||||
json={
|
||||
"participants": [
|
||||
{"user_id": me["id"], "faction_id": fid[me["id"]], "place": 1},
|
||||
{"user_id": stranger["id"], "faction_id": fid[b], "place": 2},
|
||||
]
|
||||
},
|
||||
headers=csrf_headers(client),
|
||||
)
|
||||
assert bad_user.status_code == 422, bad_user.text
|
||||
|
||||
# Выключаем дополнение и пробуем поставить ЕГО фракцию, которой в партии не было.
|
||||
assert client.put(
|
||||
f"/api/groups/{gid}/expansions",
|
||||
json={"expansion_ids": [_exp_id(client, "forsaken_voids")]},
|
||||
headers=csrf_headers(client),
|
||||
).status_code == 200
|
||||
bad_faction = client.patch(
|
||||
f"/api/matches/{mid}",
|
||||
json={
|
||||
"participants": [
|
||||
{"user_id": me["id"], "faction_id": factions["necrons"], "place": 1},
|
||||
{"user_id": b, "faction_id": fid[b], "place": 2},
|
||||
]
|
||||
},
|
||||
headers=csrf_headers(client),
|
||||
)
|
||||
assert bad_faction.status_code == 422, bad_faction.text
|
||||
|
||||
|
||||
def test_create_match_still_validated(client: TestClient, engine):
|
||||
"""Создание партии не ослабло: посторонний игрок по-прежнему отклоняется."""
|
||||
me, gid, b, mid, factions = _played(client, engine)
|
||||
stranger = client.post(
|
||||
"/api/auth/dev/users", json={"nickname": "Чужак2"}, headers=csrf_headers(client)
|
||||
).json()
|
||||
r = client.post(
|
||||
"/api/matches",
|
||||
json={
|
||||
"group_id": gid,
|
||||
"participants": [
|
||||
{"user_id": me["id"], "faction_id": factions["orks"]},
|
||||
{"user_id": stranger["id"], "faction_id": factions["eldar"]},
|
||||
],
|
||||
},
|
||||
headers=csrf_headers(client),
|
||||
)
|
||||
assert r.status_code == 422, r.text
|
||||
Reference in New Issue
Block a user