141 lines
5.4 KiB
Python
141 lines
5.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
|
|
|
|
PNG = b"\x89PNG\r\n\x1a\n" + b"\x00" * 64
|
|
|
|
|
|
def _use_tmp_uploads(monkeypatch, tmp_path) -> None:
|
|
from app.core.config import settings
|
|
|
|
monkeypatch.setattr(settings, "dev_upload_dir", str(tmp_path))
|
|
|
|
|
|
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 _upload(client: TestClient, mid: int, name: str = "a.png", data: bytes = PNG, mime: str = "image/png"):
|
|
return client.post(
|
|
f"/api/matches/{mid}/attachments",
|
|
files={"file": (name, data, mime)},
|
|
headers=csrf_headers(client),
|
|
)
|
|
|
|
|
|
def test_upload_view_delete(client: TestClient, engine, monkeypatch, tmp_path):
|
|
_use_tmp_uploads(monkeypatch, tmp_path)
|
|
me, gid, p2, mid = _start(client, engine)
|
|
|
|
r = _upload(client, mid)
|
|
assert r.status_code == 200, r.text
|
|
aid = r.json()["id"]
|
|
assert r.json()["url"] == f"/api/matches/{mid}/attachments/{aid}"
|
|
|
|
detail = client.get(f"/api/matches/{mid}").json()
|
|
assert [a["id"] for a in detail["attachments"]] == [aid]
|
|
|
|
g = client.get(f"/api/matches/{mid}/attachments/{aid}")
|
|
assert g.status_code == 200 and g.content == PNG
|
|
assert g.headers["content-type"] == "image/png"
|
|
|
|
d = client.delete(f"/api/matches/{mid}/attachments/{aid}", headers=csrf_headers(client))
|
|
assert d.status_code == 200, d.text
|
|
assert client.get(f"/api/matches/{mid}").json()["attachments"] == []
|
|
|
|
|
|
def test_reject_non_image_and_limit(client: TestClient, engine, monkeypatch, tmp_path):
|
|
_use_tmp_uploads(monkeypatch, tmp_path)
|
|
_me, _gid, _p2, mid = _start(client, engine)
|
|
|
|
assert _upload(client, mid, "x.txt", b"nope", "text/plain").status_code == 422
|
|
|
|
for i in range(10):
|
|
assert _upload(client, mid, f"{i}.png").status_code == 200
|
|
assert _upload(client, mid, "over.png").status_code == 409
|
|
|
|
|
|
def test_no_changes_after_finish(client: TestClient, engine, monkeypatch, tmp_path):
|
|
_use_tmp_uploads(monkeypatch, tmp_path)
|
|
me, gid, p2, mid = _start(client, engine)
|
|
aid = _upload(client, mid).json()["id"]
|
|
|
|
fin = finish_match(
|
|
client, mid, [{"user_id": me["id"], "place": 1}, {"user_id": p2, "place": 2}],
|
|
win_reason="objectives",
|
|
)
|
|
assert fin.status_code == 200, fin.text
|
|
|
|
# После завершения игрок не может ни добавлять, ни удалять.
|
|
assert _upload(client, mid).status_code == 409
|
|
assert client.delete(
|
|
f"/api/matches/{mid}/attachments/{aid}", headers=csrf_headers(client)
|
|
).status_code == 409
|
|
# Но просмотр сохраняется.
|
|
assert client.get(f"/api/matches/{mid}").json()["attachments"][0]["id"] == aid
|
|
|
|
|
|
def test_admin_manage_attachments_on_finished(
|
|
client: TestClient, engine, make_admin, monkeypatch, tmp_path
|
|
):
|
|
_use_tmp_uploads(monkeypatch, tmp_path)
|
|
me, gid, p2, mid = _start(client, engine)
|
|
fin = finish_match(
|
|
client, mid, [{"user_id": me["id"], "place": 1}, {"user_id": p2, "place": 2}],
|
|
win_reason="objectives",
|
|
)
|
|
assert fin.status_code == 200, fin.text
|
|
|
|
make_admin("admin", "secret123")
|
|
assert client.post(
|
|
"/api/admin/auth/login",
|
|
json={"username": "admin", "password": "secret123"},
|
|
headers=csrf_headers(client),
|
|
).status_code == 200
|
|
|
|
# Админ добавляет медиа к ЗАВЕРШЁННОЙ партии (игроку это запрещено).
|
|
up = client.post(
|
|
f"/api/admin/matches/{mid}/attachments",
|
|
files={"file": ("a.png", PNG, "image/png")},
|
|
headers=csrf_headers(client),
|
|
)
|
|
assert up.status_code == 200, up.text
|
|
aid = up.json()["id"]
|
|
assert up.json()["url"] == f"/api/admin/matches/{mid}/attachments/{aid}"
|
|
|
|
assert any(a["id"] == aid for a in client.get(f"/api/admin/matches/{mid}/attachments").json())
|
|
g = client.get(f"/api/admin/matches/{mid}/attachments/{aid}")
|
|
assert g.status_code == 200 and g.content == PNG
|
|
|
|
d = client.delete(
|
|
f"/api/admin/matches/{mid}/attachments/{aid}", headers=csrf_headers(client)
|
|
)
|
|
assert d.status_code == 200, d.text
|
|
assert client.get(f"/api/admin/matches/{mid}/attachments").json() == []
|
|
|
|
|
|
def test_non_member_cannot_view(client: TestClient, engine, monkeypatch, tmp_path):
|
|
_use_tmp_uploads(monkeypatch, tmp_path)
|
|
_me, _gid, _p2, mid = _start(client, engine)
|
|
aid = _upload(client, mid).json()["id"]
|
|
|
|
login(client, "Чужак") # не состоит в группе
|
|
g = client.get(f"/api/matches/{mid}/attachments/{aid}")
|
|
assert g.status_code in (401, 403)
|