Группы: приглашения вместо авто-добавления (согласиться/отказаться)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-17 20:07:20 +03:00
co-authored by Claude Opus 4.8
parent 66370c7df4
commit a5c54512a5
20 changed files with 742 additions and 98 deletions
+97
View File
@@ -0,0 +1,97 @@
"""Приглашения в группу: создание, просмотр, принятие/отклонение, права участника."""
from __future__ import annotations
from fastapi.testclient import TestClient
from tests.conftest import add_group_member, csrf_headers, login
def _group(client: TestClient, name: str = "Группа") -> int:
return client.post(
"/api/groups", json={"name": name, "expansion_ids": []}, headers=csrf_headers(client)
).json()["id"]
def _dev_user(client: TestClient, nick: str) -> None:
client.post("/api/auth/dev/users", json={"nickname": nick}, headers=csrf_headers(client))
def test_invite_accept_flow(client: TestClient):
login(client, "Овнер")
gid = _group(client)
_dev_user(client, "Гость")
r = client.post(
f"/api/groups/{gid}/invitations", json={"nickname": "Гость"}, headers=csrf_headers(client)
)
assert r.status_code == 200, r.text
assert r.json()["invited_by_nickname"] == "Овнер"
# Повторное приглашение → 409.
dup = client.post(
f"/api/groups/{gid}/invitations", json={"nickname": "Гость"}, headers=csrf_headers(client)
)
assert dup.status_code == 409, dup.text
# Приглашённый видит приглашение и принимает.
login(client, "Гость")
invs = client.get("/api/invitations").json()
assert len(invs) == 1
assert invs[0]["group_id"] == gid and invs[0]["group_name"] == "Группа"
inv_id = invs[0]["id"]
acc = client.post(f"/api/invitations/{inv_id}/accept", headers=csrf_headers(client))
assert acc.status_code == 200, acc.text
# Стал участником; первая группа стала активной; приглашение исчезло.
me = client.get("/api/users/me").json()
assert me["active_group_id"] == gid
assert client.get("/api/invitations").json() == []
# Теперь он участник → приглашать его повторно нельзя.
login(client, "Овнер")
again = client.post(
f"/api/groups/{gid}/invitations", json={"nickname": "Гость"}, headers=csrf_headers(client)
)
assert again.status_code == 409, again.text
members = client.get(f"/api/groups/{gid}/members").json()
assert any(m["nickname"] == "Гость" for m in members)
def test_invite_decline(client: TestClient):
login(client, "Овнер2")
gid = _group(client)
_dev_user(client, "Гость2")
client.post(
f"/api/groups/{gid}/invitations", json={"nickname": "Гость2"}, headers=csrf_headers(client)
)
login(client, "Гость2")
inv_id = client.get("/api/invitations").json()[0]["id"]
dec = client.post(f"/api/invitations/{inv_id}/decline", headers=csrf_headers(client))
assert dec.status_code == 200, dec.text
assert client.get("/api/invitations").json() == []
login(client, "Овнер2")
members = client.get(f"/api/groups/{gid}/members").json()
assert all(m["nickname"] != "Гость2" for m in members)
def test_any_member_can_invite(client: TestClient, engine):
login(client, "Овнер3")
gid = _group(client)
add_group_member(engine, gid, "Участник") # обычный member (не владелец)
_dev_user(client, "Новичок")
# Обычный участник входит и приглашает — должно быть можно (assert_member, не owner).
login(client, "Участник")
r = client.post(
f"/api/groups/{gid}/invitations", json={"nickname": "Новичок"}, headers=csrf_headers(client)
)
assert r.status_code == 200, r.text
def test_invite_unknown_nick_404(client: TestClient):
login(client, "Овнер4")
gid = _group(client)
r = client.post(
f"/api/groups/{gid}/invitations", json={"nickname": "НетТакого"}, headers=csrf_headers(client)
)
assert r.status_code == 404, r.text