Files
zulip/zerver/tests/test_submessage.py
Prakhar Pratyush 3bad36ef8c queue: Rename queue_json_publish to queue_json_publish_rollback_unsafe.
This commit renames the 'queue_json_publish' function to
'queue_json_publish_rollback_unsafe' to reflect the fact that it doesn't
wait for the db transaction (within which it gets called, if any)
to commit and sends event irrespective of commit or rollback.

In most of the cases we don't want to send event in the case of
rollbacks, so the caller should be aware that calling the function
directly is rollback unsafe.

Fixes part of #30489.
2024-12-06 09:23:02 -08:00

235 lines
7.7 KiB
Python

from typing import Any
from unittest import mock
from zerver.actions.submessage import do_add_submessage
from zerver.lib.message_cache import MessageDict
from zerver.lib.test_classes import ZulipTestCase
from zerver.models import Message, SubMessage
class TestBasics(ZulipTestCase):
def test_get_raw_db_rows(self) -> None:
cordelia = self.example_user("cordelia")
hamlet = self.example_user("hamlet")
stream_name = "Verona"
message_id = self.send_stream_message(
sender=cordelia,
stream_name=stream_name,
)
def get_raw_rows() -> list[dict[str, Any]]:
query = SubMessage.get_raw_db_rows([message_id])
rows = list(query)
return rows
rows = get_raw_rows()
self.assertEqual(rows, [])
sm1 = SubMessage.objects.create(
msg_type="whatever",
content="stuff1",
message_id=message_id,
sender=cordelia,
)
sm2 = SubMessage.objects.create(
msg_type="whatever",
content="stuff2",
message_id=message_id,
sender=hamlet,
)
expected_data = [
dict(
id=sm1.id,
message_id=message_id,
sender_id=cordelia.id,
msg_type="whatever",
content="stuff1",
),
dict(
id=sm2.id,
message_id=message_id,
sender_id=hamlet.id,
msg_type="whatever",
content="stuff2",
),
]
self.assertEqual(get_raw_rows(), expected_data)
message = Message.objects.get(id=message_id)
message_json = MessageDict.wide_dict(message)
rows = message_json["submessages"]
rows.sort(key=lambda r: r["id"])
self.assertEqual(rows, expected_data)
msg_rows = MessageDict.ids_to_dict([message_id])
rows = msg_rows[0]["submessages"]
rows.sort(key=lambda r: r["id"])
self.assertEqual(rows, expected_data)
def test_endpoint_errors(self) -> None:
cordelia = self.example_user("cordelia")
stream_name = "Verona"
message_id = self.send_stream_message(
sender=cordelia,
stream_name=stream_name,
)
self.login_user(cordelia)
payload = dict(
message_id=message_id,
msg_type="whatever",
content="not json",
)
result = self.client_post("/json/submessage", payload)
self.assert_json_error(result, "Invalid json for submessage")
hamlet = self.example_user("hamlet")
bad_message_id = self.send_personal_message(
from_user=hamlet,
to_user=hamlet,
)
payload = dict(
message_id=bad_message_id,
msg_type="whatever",
content="does not matter",
)
result = self.client_post("/json/submessage", payload)
self.assert_json_error(result, "Invalid message(s)")
def test_original_sender_enforced(self) -> None:
cordelia = self.example_user("cordelia")
hamlet = self.example_user("hamlet")
stream_name = "Verona"
message_id = self.send_stream_message(
sender=cordelia,
stream_name=stream_name,
)
self.login_user(hamlet)
payload = dict(
message_id=message_id,
msg_type="whatever",
content="{}",
)
# Hamlet can't just go attaching submessages to Cordelia's
# message, even though he does have read access here to the
# message itself.
result = self.client_post("/json/submessage", payload)
self.assert_json_error(result, "You cannot attach a submessage to this message.")
# Since Hamlet is actually subscribed to the stream, he is welcome
# to send submessages to Cordelia once she initiates the "subconversation".
do_add_submessage(
realm=cordelia.realm,
sender_id=cordelia.id,
message_id=message_id,
msg_type="whatever",
content="whatever",
)
result = self.client_post("/json/submessage", payload)
self.assert_json_success(result)
def test_endpoint_success(self) -> None:
cordelia = self.example_user("cordelia")
hamlet = self.example_user("hamlet")
stream_name = "Verona"
message_id = self.send_stream_message(
sender=cordelia,
stream_name=stream_name,
)
self.login_user(cordelia)
payload = dict(
message_id=message_id,
msg_type="whatever",
content='{"name": "alice", "salary": 20}',
)
with self.capture_send_event_calls(expected_num_events=1) as events:
result = self.client_post("/json/submessage", payload)
self.assert_json_success(result)
submessage = SubMessage.objects.get(message_id=message_id)
expected_data = dict(
message_id=message_id,
submessage_id=submessage.id,
content=payload["content"],
msg_type="whatever",
sender_id=cordelia.id,
type="submessage",
)
data = events[0]["event"]
self.assertEqual(data, expected_data)
users = events[0]["users"]
self.assertIn(cordelia.id, users)
self.assertIn(hamlet.id, users)
rows = SubMessage.get_raw_db_rows([message_id])
self.assert_length(rows, 1)
row = rows[0]
expected_data = dict(
id=row["id"],
message_id=message_id,
content='{"name": "alice", "salary": 20}',
msg_type="whatever",
sender_id=cordelia.id,
)
self.assertEqual(row, expected_data)
def test_submessage_event_sent_after_transaction_commits(self) -> None:
"""
Tests that `send_event_rollback_unsafe` is hooked to `transaction.on_commit`.
This is important, because we don't want to end up holding locks on message rows
for too long if the event queue runs into a problem.
"""
hamlet = self.example_user("hamlet")
message_id = self.send_stream_message(hamlet, "Denmark")
with (
self.capture_send_event_calls(expected_num_events=1),
mock.patch("zerver.tornado.django_api.queue_json_publish_rollback_unsafe") as m,
):
m.side_effect = AssertionError(
"Events should be sent only after the transaction commits."
)
do_add_submessage(hamlet.realm, hamlet.id, message_id, "whatever", "whatever")
def test_fetch_message_containing_submessages(self) -> None:
cordelia = self.example_user("cordelia")
stream_name = "Verona"
message_id = self.send_stream_message(
sender=cordelia,
stream_name=stream_name,
)
self.login_user(cordelia)
payload = dict(
message_id=message_id,
msg_type="whatever",
content='{"name": "alice", "salary": 20}',
)
self.assert_json_success(self.client_post("/json/submessage", payload))
result = self.client_get(f"/json/messages/{message_id}")
response_dict = self.assert_json_success(result)
self.assert_length(response_dict["message"]["submessages"], 1)
submessage = response_dict["message"]["submessages"][0]
expected_data = dict(
id=submessage["id"],
message_id=message_id,
content='{"name": "alice", "salary": 20}',
msg_type="whatever",
sender_id=cordelia.id,
)
self.assertEqual(submessage, expected_data)