"""Черновик формы завершения: совместное заполнение результатов партии. Плюс запрет правки незавершённой партии (места без завершения — «результат есть, а игры как бы не было»).""" from __future__ import annotations from fastapi.testclient import TestClient from tests.conftest import add_group_member, csrf_headers, finish_match, login, start_match def _start(client: TestClient, engine) -> tuple[dict, int, int, int]: 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()] 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 return me, gid, p2, started.json()["id"] def _draft(client: TestClient, mid: int, body: dict): return client.put(f"/api/matches/{mid}/finish-draft", json=body, headers=csrf_headers(client)) def test_draft_is_shared_between_participants(client: TestClient, engine): """Второй участник видит раскладку первого, не перезагружая страницу.""" me, gid, p2, mid = _start(client, engine) r = _draft(client, mid, { "blocks": [[me["id"]], [p2]], "eliminated": [], "comments": {str(p2): "почти успел"}, "win_reason": "worlds", }) assert r.status_code == 200, r.text assert r.json()["finish_draft"]["data"]["blocks"] == [[me["id"]], [p2]] login(client, "Игрок2") seen = client.get(f"/api/matches/{mid}").json()["finish_draft"] assert seen["data"]["blocks"] == [[me["id"]], [p2]] assert seen["data"]["comments"][str(p2)] == "почти успел" assert seen["data"]["win_reason"] == "worlds" assert seen["updated_by_nickname"] == "Хост" def test_draft_does_not_move_match_version(client: TestClient, engine): """Черновик не трогает версию партии. Иначе «Завершить» у второго участника ловил бы STALE_WRITE на каждую чужую правку — ровно то, ради чего черновик и делался.""" me, gid, p2, mid = _start(client, engine) v1 = client.get(f"/api/matches/{mid}").json()["version"] assert _draft(client, mid, {"blocks": [[p2], [me["id"]]]}).status_code == 200 assert client.get(f"/api/matches/{mid}").json()["version"] == v1 # И завершение со «старой» (на деле актуальной) версией проходит. fin = finish_match( client, mid, [{"user_id": p2, "place": 1}, {"user_id": me["id"], "place": 2}], ) assert fin.status_code == 200, fin.text def test_draft_cleared_after_finish(client: TestClient, engine): me, gid, p2, mid = _start(client, engine) assert _draft(client, mid, {"blocks": [[me["id"]], [p2]]}).status_code == 200 finish_match(client, mid, [{"user_id": me["id"], "place": 1}, {"user_id": p2, "place": 2}]) assert client.get(f"/api/matches/{mid}").json()["finish_draft"] is None # В завершённую партию черновик не пишется. assert _draft(client, mid, {"blocks": [[me["id"]], [p2]]}).status_code == 409 def test_draft_rejects_outsider_and_foreign_players(client: TestClient, engine): me, gid, p2, mid = _start(client, engine) stranger = login(client, "Чужак") # в группе не состоит assert _draft(client, mid, {"blocks": [[me["id"]], [p2]]}).status_code == 403 login(client, "Хост") bad = _draft(client, mid, {"blocks": [[me["id"]], [stranger["id"]]]}) assert bad.status_code == 422, bad.text def test_in_progress_match_cannot_be_patched(client: TestClient, engine): """Места и причина победы у идущей партии — только через завершение. Иначе партия остаётся in_progress с проставленными местами: висит в «Незавершённых», но в статистику не попадает и очков не приносит.""" me, gid, p2, mid = _start(client, engine) parts = client.get(f"/api/matches/{mid}").json()["participants"] body = { "participants": [ {"user_id": p["user_id"], "faction_id": p["faction_id"], "place": i + 1} for i, p in enumerate(parts) ], "win_reason": "objectives", } r = client.patch(f"/api/matches/{mid}", json=body, headers=csrf_headers(client)) assert r.status_code == 409, r.text assert client.get(f"/api/matches/{mid}").json()["status"] == "in_progress" def test_admin_cannot_patch_in_progress_match(client: TestClient, engine, make_admin): me, gid, p2, mid = _start(client, engine) parts = client.get(f"/api/matches/{mid}").json()["participants"] make_admin("admin", "secret123") assert client.post( "/api/admin/auth/login", json={"username": "admin", "password": "secret123"}, headers=csrf_headers(client), ).status_code == 200 body = { "participants": [ {"user_id": p["user_id"], "faction_id": p["faction_id"], "place": i + 1} for i, p in enumerate(parts) ], "win_reason": "objectives", } r = client.patch(f"/api/admin/matches/{mid}", json=body, headers=csrf_headers(client)) assert r.status_code == 409, r.text