teecup/tests/test_tournament_round_messages.py

317 lines
13 KiB
Python
Raw Normal View History

"""
Rundespesifikk kommentartråd for org-turneringer (ADR-102). Ekte parallell
til round_messages.py sin RoundMessages, men org-scopet (RLS) i stedet for
plain_connection() -- skriverett = leserett = org-medlemskap
(get_authorized_org), moderering = forfatter ELLER org-admin (is_org_admin),
samme regel som Banter Board (ADR-025). Én tråd PER TURNERINGSRUNDE (hele
feltet deler tråd, ikke per flight).
"""
import uuid
import pytest
from fastapi import HTTPException
from app.auth import CurrentUser
from app.routers.tournament_round_messages import (
CommentIn,
ReactionIn,
delete_tournament_round_message,
delete_tournament_round_message_comment,
list_tournament_round_message_comments,
list_tournament_round_messages,
post_tournament_round_message,
post_tournament_round_message_comment,
remove_tournament_round_message_reaction,
set_tournament_round_message_reaction,
)
from tests.conftest import (
add_membership,
create_org,
create_tournament,
create_tournament_round,
create_course,
create_user,
)
async def _setup(org_id):
tournament_id = await create_tournament(org_id, name="Kommentar-turnering")
course_id = await create_course(org_id, name="Kommentarbanen")
round_id = await create_tournament_round(org_id, tournament_id, course_id, sequence=1)
return tournament_id, round_id
async def test_post_and_list_message(pool):
org_id = await create_org()
tournament_id, round_id = await _setup(org_id)
author_id = await create_user(display_name="Forfatter Testesen")
await add_membership(org_id, author_id)
created = await post_tournament_round_message(
tournament_id, round_id, body="Fin runde i dag!", image=None, client_message_id=None,
organization_id=org_id, user=CurrentUser(user_id=author_id),
)
assert created.body == "Fin runde i dag!"
assert created.tournament_round_id == round_id
assert created.author_display_name == "Forfatter Testesen"
listed = await list_tournament_round_messages(
tournament_id, round_id, organization_id=org_id, user=CurrentUser(user_id=author_id),
)
assert [m.id for m in listed] == [created.id]
async def test_client_message_id_idempotent(pool) -> None:
org_id = await create_org()
tournament_id, round_id = await _setup(org_id)
author_id = await create_user()
await add_membership(org_id, author_id)
client_message_id = str(uuid.uuid4())
first = await post_tournament_round_message(
tournament_id, round_id, body="Duplikat-test", image=None, client_message_id=client_message_id,
organization_id=org_id, user=CurrentUser(user_id=author_id),
)
second = await post_tournament_round_message(
tournament_id, round_id, body="Duplikat-test", image=None, client_message_id=client_message_id,
organization_id=org_id, user=CurrentUser(user_id=author_id),
)
assert first.id == second.id
listed = await list_tournament_round_messages(
tournament_id, round_id, organization_id=org_id, user=CurrentUser(user_id=author_id),
)
assert len(listed) == 1
async def test_body_or_image_required(pool) -> None:
org_id = await create_org()
tournament_id, round_id = await _setup(org_id)
author_id = await create_user()
await add_membership(org_id, author_id)
with pytest.raises(HTTPException) as exc_info:
await post_tournament_round_message(
tournament_id, round_id, body=None, image=None, client_message_id=None,
organization_id=org_id, user=CurrentUser(user_id=author_id),
)
assert exc_info.value.status_code == 400
async def test_wrong_tournament_id_404(pool) -> None:
org_id = await create_org()
tournament_id, round_id = await _setup(org_id)
other_tournament_id = await create_tournament(org_id, name="En annen turnering")
author_id = await create_user()
await add_membership(org_id, author_id)
with pytest.raises(HTTPException) as exc_info:
await post_tournament_round_message(
other_tournament_id, round_id, body="Feil turnering", image=None, client_message_id=None,
organization_id=org_id, user=CurrentUser(user_id=author_id),
)
assert exc_info.value.status_code == 404
async def test_rls_blocks_cross_org_access(pool) -> None:
org_a = await create_org()
org_b = await create_org()
tournament_id, round_id = await _setup(org_a)
author_id = await create_user()
await add_membership(org_a, author_id)
await add_membership(org_b, author_id)
await post_tournament_round_message(
tournament_id, round_id, body="Kun org A", image=None, client_message_id=None,
organization_id=org_a, user=CurrentUser(user_id=author_id),
)
# Samme (tournament_id, round_id) forsøkt lest med FEIL org_id satt --
# RLS filtrerer bort tournament_round-raden, _get_tournament_round_or_404
# ser den derfor ikke og gir 404 (ikke et 0-treffs, "tom liste"-svar).
with pytest.raises(HTTPException) as exc_info:
await list_tournament_round_messages(
tournament_id, round_id, organization_id=org_b, user=CurrentUser(user_id=author_id),
)
assert exc_info.value.status_code == 404
async def test_delete_by_author(pool) -> None:
org_id = await create_org()
tournament_id, round_id = await _setup(org_id)
author_id = await create_user()
await add_membership(org_id, author_id)
created = await post_tournament_round_message(
tournament_id, round_id, body="Slett meg", image=None, client_message_id=None,
organization_id=org_id, user=CurrentUser(user_id=author_id),
)
await delete_tournament_round_message(
tournament_id, round_id, created.id, organization_id=org_id, user=CurrentUser(user_id=author_id),
)
listed = await list_tournament_round_messages(
tournament_id, round_id, organization_id=org_id, user=CurrentUser(user_id=author_id),
)
assert listed == []
async def test_delete_by_org_admin_not_author(pool) -> None:
org_id = await create_org()
tournament_id, round_id = await _setup(org_id)
author_id = await create_user()
admin_id = await create_user()
await add_membership(org_id, author_id, role="member")
await add_membership(org_id, admin_id, role="admin")
created = await post_tournament_round_message(
tournament_id, round_id, body="Slett meg som admin", image=None, client_message_id=None,
organization_id=org_id, user=CurrentUser(user_id=author_id),
)
await delete_tournament_round_message(
tournament_id, round_id, created.id, organization_id=org_id, user=CurrentUser(user_id=admin_id),
)
listed = await list_tournament_round_messages(
tournament_id, round_id, organization_id=org_id, user=CurrentUser(user_id=author_id),
)
assert listed == []
async def test_delete_rejected_for_unrelated_member(pool) -> None:
org_id = await create_org()
tournament_id, round_id = await _setup(org_id)
author_id = await create_user()
other_member_id = await create_user()
await add_membership(org_id, author_id, role="member")
await add_membership(org_id, other_member_id, role="member")
created = await post_tournament_round_message(
tournament_id, round_id, body="Ikke slett meg", image=None, client_message_id=None,
organization_id=org_id, user=CurrentUser(user_id=author_id),
)
with pytest.raises(HTTPException) as exc_info:
await delete_tournament_round_message(
tournament_id, round_id, created.id, organization_id=org_id, user=CurrentUser(user_id=other_member_id),
)
assert exc_info.value.status_code == 403
async def test_ordinary_member_can_post(pool) -> None:
"""Skriverett = org-medlemskap, ikke deltaker-i-akkurat-denne-turneringen
eller admin -- bekrefter at et VANLIG medlem (ikke admin/deltaker) kan
poste, samme "vanlig org-medlemsnivå"-presedens som resten av
individual_tournaments.py sitt oppsett (ADR-102)."""
org_id = await create_org()
tournament_id, round_id = await _setup(org_id)
member_id = await create_user()
await add_membership(org_id, member_id, role="member")
created = await post_tournament_round_message(
tournament_id, round_id, body="Jeg er bare medlem", image=None, client_message_id=None,
organization_id=org_id, user=CurrentUser(user_id=member_id),
)
assert created.body == "Jeg er bare medlem"
async def test_reaction_upsert_and_remove(pool) -> None:
org_id = await create_org()
tournament_id, round_id = await _setup(org_id)
author_id = await create_user()
reactor_id = await create_user()
await add_membership(org_id, author_id)
await add_membership(org_id, reactor_id)
created = await post_tournament_round_message(
tournament_id, round_id, body="Reager på meg", image=None, client_message_id=None,
organization_id=org_id, user=CurrentUser(user_id=author_id),
)
summaries = await set_tournament_round_message_reaction(
tournament_id, round_id, created.id, ReactionIn(emoji="👍"),
organization_id=org_id, user=CurrentUser(user_id=reactor_id),
)
assert len(summaries) == 1
assert summaries[0].emoji == "👍"
assert summaries[0].count == 1
assert summaries[0].reacted_by_me is True
# Bytte av emoji erstatter, stables ikke.
summaries = await set_tournament_round_message_reaction(
tournament_id, round_id, created.id, ReactionIn(emoji="❤️"),
organization_id=org_id, user=CurrentUser(user_id=reactor_id),
)
assert len(summaries) == 1
assert summaries[0].emoji == "❤️"
summaries = await remove_tournament_round_message_reaction(
tournament_id, round_id, created.id, organization_id=org_id, user=CurrentUser(user_id=reactor_id),
)
assert summaries == []
async def test_invalid_emoji_rejected(pool) -> None:
org_id = await create_org()
tournament_id, round_id = await _setup(org_id)
author_id = await create_user()
await add_membership(org_id, author_id)
created = await post_tournament_round_message(
tournament_id, round_id, body="Test", image=None, client_message_id=None,
organization_id=org_id, user=CurrentUser(user_id=author_id),
)
with pytest.raises(HTTPException) as exc_info:
await set_tournament_round_message_reaction(
tournament_id, round_id, created.id, ReactionIn(emoji="🐸"),
organization_id=org_id, user=CurrentUser(user_id=author_id),
)
assert exc_info.value.status_code == 400
async def test_threaded_comment_create_list_delete(pool) -> None:
org_id = await create_org()
tournament_id, round_id = await _setup(org_id)
author_id = await create_user()
commenter_id = await create_user()
admin_id = await create_user()
await add_membership(org_id, author_id, role="member")
await add_membership(org_id, commenter_id, role="member")
await add_membership(org_id, admin_id, role="admin")
message = await post_tournament_round_message(
tournament_id, round_id, body="Kommenter meg", image=None, client_message_id=None,
organization_id=org_id, user=CurrentUser(user_id=author_id),
)
top_comment = await post_tournament_round_message_comment(
tournament_id, round_id, message.id, CommentIn(body="Bra spilt!"),
organization_id=org_id, user=CurrentUser(user_id=commenter_id),
)
reply = await post_tournament_round_message_comment(
tournament_id, round_id, message.id, CommentIn(body="Enig!", parent_comment_id=top_comment.id),
organization_id=org_id, user=CurrentUser(user_id=author_id),
)
comments = await list_tournament_round_message_comments(
tournament_id, round_id, message.id, organization_id=org_id,
)
assert {c.id for c in comments} == {top_comment.id, reply.id}
assert next(c for c in comments if c.id == reply.id).parent_comment_id == top_comment.id
# Ikke-forfatter-ikke-admin nektes.
with pytest.raises(HTTPException) as exc_info:
await delete_tournament_round_message_comment(
tournament_id, round_id, message.id, top_comment.id,
organization_id=org_id, user=CurrentUser(user_id=author_id),
)
assert exc_info.value.status_code == 403
# Org-admin kan slette andres kommentar -- kaskade-sletter svaret under.
await delete_tournament_round_message_comment(
tournament_id, round_id, message.id, top_comment.id,
organization_id=org_id, user=CurrentUser(user_id=admin_id),
)
remaining = await list_tournament_round_message_comments(
tournament_id, round_id, message.id, organization_id=org_id,
)
assert remaining == []