snapshot of https://github.com/zulip/zulip.git @ 48c69911c7dd4e03b20552eb3bd77ad8151c1357 (2026-09-15)
Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled
Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled
This commit is contained in:
234
zerver/tests/test_submessage.py
Normal file
234
zerver/tests/test_submessage.py
Normal file
@@ -0,0 +1,234 @@
|
||||
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)
|
||||
Reference in New Issue
Block a user