2023-11-19 19:45:19 +01:00
|
|
|
from datetime import timedelta
|
2022-07-27 23:33:49 +02:00
|
|
|
from email.headerregistry import Address
|
2024-07-12 02:30:23 +02:00
|
|
|
from typing import Any
|
2020-07-04 23:20:55 +02:00
|
|
|
from unittest import mock
|
|
|
|
|
2020-08-07 01:09:47 +02:00
|
|
|
import orjson
|
2020-07-06 01:17:57 +02:00
|
|
|
from django.conf import settings
|
|
|
|
from django.db.models import Q
|
2020-07-07 23:34:04 +02:00
|
|
|
from django.test import override_settings
|
2020-07-04 23:20:55 +02:00
|
|
|
from django.utils.timezone import now as timezone_now
|
|
|
|
|
2022-04-14 23:58:15 +02:00
|
|
|
from zerver.actions.create_realm import do_create_realm
|
2022-04-14 23:53:15 +02:00
|
|
|
from zerver.actions.create_user import do_create_user
|
2022-04-14 23:50:10 +02:00
|
|
|
from zerver.actions.message_send import (
|
2020-09-29 20:34:38 +02:00
|
|
|
build_message_send_dict,
|
2020-07-08 01:04:07 +02:00
|
|
|
check_message,
|
2020-07-06 00:15:22 +02:00
|
|
|
check_send_stream_message,
|
|
|
|
do_send_messages,
|
2020-07-06 21:21:15 +02:00
|
|
|
extract_private_recipients,
|
|
|
|
extract_stream_indicator,
|
2020-07-06 22:28:48 +02:00
|
|
|
internal_prep_private_message,
|
|
|
|
internal_prep_stream_message_by_name,
|
2024-07-04 14:05:48 +02:00
|
|
|
internal_send_group_direct_message,
|
2020-07-06 22:28:48 +02:00
|
|
|
internal_send_private_message,
|
2020-07-04 23:20:55 +02:00
|
|
|
internal_send_stream_message,
|
2020-07-06 22:28:48 +02:00
|
|
|
internal_send_stream_message_by_name,
|
2020-07-08 01:04:07 +02:00
|
|
|
send_rate_limited_pm_notification_to_bot_owner,
|
2020-07-04 23:20:55 +02:00
|
|
|
)
|
2024-07-08 19:00:08 +02:00
|
|
|
from zerver.actions.realm_settings import (
|
|
|
|
do_change_realm_permission_group_setting,
|
|
|
|
do_set_realm_property,
|
|
|
|
)
|
2022-04-14 23:51:16 +02:00
|
|
|
from zerver.actions.streams import do_change_stream_post_policy
|
2023-06-13 05:47:02 +02:00
|
|
|
from zerver.actions.user_groups import add_subgroups_to_user_group, check_add_user_group
|
2023-08-16 05:45:05 +02:00
|
|
|
from zerver.actions.user_settings import do_change_user_setting
|
2022-04-14 23:50:10 +02:00
|
|
|
from zerver.actions.users import do_change_can_forge_sender, do_deactivate_user
|
2020-07-07 23:47:32 +02:00
|
|
|
from zerver.lib.addressee import Addressee
|
2024-07-08 19:00:08 +02:00
|
|
|
from zerver.lib.exceptions import (
|
|
|
|
DirectMessageInitiationError,
|
|
|
|
DirectMessagePermissionError,
|
|
|
|
JsonableError,
|
|
|
|
)
|
2023-10-03 03:25:57 +02:00
|
|
|
from zerver.lib.message import get_raw_unread_data, get_recent_private_conversations
|
|
|
|
from zerver.lib.message_cache import MessageDict
|
2023-07-14 19:46:50 +02:00
|
|
|
from zerver.lib.per_request_cache import flush_per_request_caches
|
2023-02-13 15:32:06 +01:00
|
|
|
from zerver.lib.streams import create_stream_if_needed
|
2020-07-04 23:20:55 +02:00
|
|
|
from zerver.lib.test_classes import ZulipTestCase
|
2020-07-06 00:15:22 +02:00
|
|
|
from zerver.lib.test_helpers import (
|
2020-07-07 23:34:04 +02:00
|
|
|
get_user_messages,
|
2020-07-06 00:15:22 +02:00
|
|
|
make_client,
|
|
|
|
message_stream_count,
|
|
|
|
most_recent_message,
|
|
|
|
most_recent_usermessage,
|
2023-03-01 07:34:25 +01:00
|
|
|
reset_email_visibility_to_everyone_in_zulip_realm,
|
2020-07-06 00:15:22 +02:00
|
|
|
)
|
2023-04-17 14:11:08 +02:00
|
|
|
from zerver.lib.timestamp import datetime_to_timestamp
|
2020-07-04 23:20:55 +02:00
|
|
|
from zerver.models import (
|
2020-07-06 00:15:22 +02:00
|
|
|
Message,
|
2024-04-18 12:23:46 +02:00
|
|
|
NamedUserGroup,
|
2020-07-06 01:17:57 +02:00
|
|
|
Realm,
|
2020-07-07 23:34:04 +02:00
|
|
|
RealmDomain,
|
2020-07-06 01:17:57 +02:00
|
|
|
Recipient,
|
2020-07-04 23:20:55 +02:00
|
|
|
Stream,
|
2020-07-06 00:15:22 +02:00
|
|
|
Subscription,
|
|
|
|
UserMessage,
|
2020-07-04 23:20:55 +02:00
|
|
|
UserProfile,
|
|
|
|
)
|
2023-12-05 19:32:36 +01:00
|
|
|
from zerver.models.constants import MAX_TOPIC_NAME_LENGTH
|
2023-12-15 01:55:59 +01:00
|
|
|
from zerver.models.groups import SystemGroups
|
2024-07-08 19:00:08 +02:00
|
|
|
from zerver.models.realms import WildcardMentionPolicyEnum, get_realm
|
2024-07-04 14:05:48 +02:00
|
|
|
from zerver.models.recipients import get_or_create_direct_message_group
|
2023-12-15 03:57:04 +01:00
|
|
|
from zerver.models.streams import get_stream
|
2023-12-15 01:16:00 +01:00
|
|
|
from zerver.models.users import get_system_bot, get_user
|
2022-11-17 09:30:48 +01:00
|
|
|
from zerver.views.message_send import InvalidMirrorInputError
|
2020-07-04 23:20:55 +02:00
|
|
|
|
|
|
|
|
|
|
|
class MessagePOSTTest(ZulipTestCase):
|
2021-02-12 08:19:30 +01:00
|
|
|
def _send_and_verify_message(
|
2024-07-12 02:30:23 +02:00
|
|
|
self, user: UserProfile, stream_name: str, error_msg: str | None = None
|
2021-02-12 08:19:30 +01:00
|
|
|
) -> None:
|
2020-07-04 23:20:55 +02:00
|
|
|
if error_msg is None:
|
|
|
|
msg_id = self.send_stream_message(user, stream_name)
|
2022-11-04 20:26:18 +01:00
|
|
|
result = self.api_get(user, "/api/v1/messages/" + str(msg_id))
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
else:
|
|
|
|
with self.assertRaisesRegex(JsonableError, error_msg):
|
|
|
|
self.send_stream_message(user, stream_name)
|
|
|
|
|
2020-09-23 21:04:36 +02:00
|
|
|
def test_message_to_stream_by_name(self) -> None:
|
2020-07-04 23:20:55 +02:00
|
|
|
"""
|
|
|
|
Sending a message to a stream to which you are subscribed is
|
|
|
|
successful.
|
|
|
|
"""
|
2024-04-10 20:48:10 +02:00
|
|
|
recipient_type_name = ["stream", "channel"]
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2024-04-10 20:48:10 +02:00
|
|
|
|
|
|
|
for recipient_type in recipient_type_name:
|
|
|
|
result = self.client_post(
|
|
|
|
"/json/messages",
|
|
|
|
{
|
|
|
|
"type": recipient_type,
|
|
|
|
"to": orjson.dumps("Verona").decode(),
|
|
|
|
"content": "Test message",
|
|
|
|
"topic": "Test topic",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
self.assert_json_success(result)
|
2020-07-04 23:20:55 +02:00
|
|
|
|
2020-09-23 21:04:36 +02:00
|
|
|
def test_api_message_to_stream_by_name(self) -> None:
|
2020-07-04 23:20:55 +02:00
|
|
|
"""
|
|
|
|
Same as above, but for the API view
|
|
|
|
"""
|
2024-04-10 20:48:10 +02:00
|
|
|
recipient_type_name = ["stream", "channel"]
|
2021-02-12 08:20:45 +01:00
|
|
|
user = self.example_user("hamlet")
|
2024-04-10 20:48:10 +02:00
|
|
|
|
|
|
|
for recipient_type in recipient_type_name:
|
|
|
|
result = self.api_post(
|
|
|
|
user,
|
|
|
|
"/api/v1/messages",
|
|
|
|
{
|
|
|
|
"type": recipient_type,
|
|
|
|
"to": orjson.dumps("Verona").decode(),
|
|
|
|
"content": "Test message",
|
|
|
|
"topic": "Test topic",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
self.assert_json_success(result)
|
2020-07-04 23:20:55 +02:00
|
|
|
|
|
|
|
def test_message_to_stream_with_nonexistent_id(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
cordelia = self.example_user("cordelia")
|
2020-07-04 23:20:55 +02:00
|
|
|
bot = self.create_test_bot(
|
2021-02-12 08:20:45 +01:00
|
|
|
short_name="whatever",
|
2020-07-04 23:20:55 +02:00
|
|
|
user_profile=cordelia,
|
|
|
|
)
|
|
|
|
result = self.api_post(
|
2021-02-12 08:19:30 +01:00
|
|
|
bot,
|
|
|
|
"/api/v1/messages",
|
2020-07-04 23:20:55 +02:00
|
|
|
{
|
2024-04-10 20:48:10 +02:00
|
|
|
"type": "channel",
|
2020-08-07 01:09:47 +02:00
|
|
|
"to": orjson.dumps([99999]).decode(),
|
2020-07-04 23:20:55 +02:00
|
|
|
"content": "Stream message by ID.",
|
|
|
|
"topic": "Test topic for stream ID message",
|
|
|
|
},
|
|
|
|
)
|
2024-04-16 21:27:36 +02:00
|
|
|
self.assert_json_error(result, "Channel with ID '99999' does not exist")
|
2020-07-04 23:20:55 +02:00
|
|
|
|
|
|
|
msg = self.get_last_message()
|
2021-02-12 08:19:30 +01:00
|
|
|
expected = (
|
|
|
|
"Your bot `whatever-bot@zulip.testserver` tried to send a message to "
|
2024-04-16 17:58:08 +02:00
|
|
|
"channel ID 99999, but there is no channel with that ID."
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assertEqual(msg.content, expected)
|
|
|
|
|
2023-02-13 15:32:06 +01:00
|
|
|
def test_message_to_stream_with_no_subscribers(self) -> None:
|
|
|
|
"""
|
|
|
|
Sending a message to an empty stream succeeds, but sends a warning
|
|
|
|
to the owner.
|
|
|
|
"""
|
|
|
|
realm = get_realm("zulip")
|
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
bot = self.create_test_bot(
|
|
|
|
short_name="whatever",
|
|
|
|
user_profile=cordelia,
|
|
|
|
)
|
|
|
|
stream = create_stream_if_needed(realm, "Acropolis")[0]
|
|
|
|
result = self.api_post(
|
|
|
|
bot,
|
|
|
|
"/api/v1/messages",
|
|
|
|
{
|
2024-04-10 20:48:10 +02:00
|
|
|
"type": "channel",
|
2023-02-13 15:32:06 +01:00
|
|
|
"to": orjson.dumps(stream.name).decode(),
|
|
|
|
"content": "Stream message to an empty stream by name.",
|
|
|
|
"topic": "Test topic for empty stream name message",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
msg = self.get_last_message()
|
|
|
|
expected = "Stream message to an empty stream by name."
|
|
|
|
self.assertEqual(msg.content, expected)
|
|
|
|
|
|
|
|
msg = self.get_second_to_last_message()
|
|
|
|
expected = (
|
|
|
|
"Your bot `whatever-bot@zulip.testserver` tried to send a message to "
|
2024-04-16 17:58:08 +02:00
|
|
|
"channel #**Acropolis**. The channel exists but does not have any subscribers."
|
2023-02-13 15:32:06 +01:00
|
|
|
)
|
|
|
|
self.assertEqual(msg.content, expected)
|
|
|
|
|
|
|
|
def test_message_to_stream_with_no_subscribers_by_id(self) -> None:
|
|
|
|
"""
|
|
|
|
Sending a message to an empty stream succeeds, but sends a warning
|
|
|
|
to the owner.
|
|
|
|
"""
|
|
|
|
realm = get_realm("zulip")
|
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
bot = self.create_test_bot(
|
|
|
|
short_name="whatever",
|
|
|
|
user_profile=cordelia,
|
|
|
|
)
|
|
|
|
stream = create_stream_if_needed(realm, "Acropolis")[0]
|
|
|
|
result = self.api_post(
|
|
|
|
bot,
|
|
|
|
"/api/v1/messages",
|
|
|
|
{
|
2024-04-10 20:48:10 +02:00
|
|
|
"type": "channel",
|
2023-02-13 15:32:06 +01:00
|
|
|
"to": orjson.dumps([stream.id]).decode(),
|
|
|
|
"content": "Stream message to an empty stream by id.",
|
|
|
|
"topic": "Test topic for empty stream id message",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
msg = self.get_last_message()
|
|
|
|
expected = "Stream message to an empty stream by id."
|
|
|
|
self.assertEqual(msg.content, expected)
|
|
|
|
|
|
|
|
msg = self.get_second_to_last_message()
|
|
|
|
expected = (
|
|
|
|
"Your bot `whatever-bot@zulip.testserver` tried to send a message to "
|
2024-04-16 17:58:08 +02:00
|
|
|
"channel #**Acropolis**. The channel exists but does not have any subscribers."
|
2023-02-13 15:32:06 +01:00
|
|
|
)
|
|
|
|
self.assertEqual(msg.content, expected)
|
|
|
|
|
2020-07-04 23:20:55 +02:00
|
|
|
def test_message_to_stream_by_id(self) -> None:
|
|
|
|
"""
|
|
|
|
Sending a message to a stream (by stream ID) to which you are
|
|
|
|
subscribed is successful.
|
|
|
|
"""
|
2024-04-10 20:48:10 +02:00
|
|
|
recipient_type_name = ["stream", "channel"]
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
|
|
|
realm = get_realm("zulip")
|
|
|
|
stream = get_stream("Verona", realm)
|
2024-04-10 20:48:10 +02:00
|
|
|
|
|
|
|
for recipient_type in recipient_type_name:
|
|
|
|
content = f"Stream message by ID, type parameter: {recipient_type}."
|
|
|
|
result = self.client_post(
|
|
|
|
"/json/messages",
|
|
|
|
{
|
|
|
|
"type": recipient_type,
|
|
|
|
"to": orjson.dumps([stream.id]).decode(),
|
|
|
|
"content": content,
|
|
|
|
"topic": "Test topic for stream ID message",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
sent_message = self.get_last_message()
|
|
|
|
self.assertEqual(sent_message.content, content)
|
2020-07-04 23:20:55 +02:00
|
|
|
|
|
|
|
def test_sending_message_as_stream_post_policy_admins(self) -> None:
|
|
|
|
"""
|
2021-03-29 17:54:57 +02:00
|
|
|
Sending messages to streams which only the admins can post to.
|
2020-07-04 23:20:55 +02:00
|
|
|
"""
|
|
|
|
admin_profile = self.example_user("iago")
|
|
|
|
self.login_user(admin_profile)
|
|
|
|
|
|
|
|
stream_name = "Verona"
|
|
|
|
stream = get_stream(stream_name, admin_profile.realm)
|
2021-12-15 01:04:35 +01:00
|
|
|
do_change_stream_post_policy(
|
|
|
|
stream, Stream.STREAM_POST_POLICY_ADMINS, acting_user=admin_profile
|
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
|
|
|
|
# Admins and their owned bots can send to STREAM_POST_POLICY_ADMINS streams
|
|
|
|
self._send_and_verify_message(admin_profile, stream_name)
|
|
|
|
admin_owned_bot = self.create_test_bot(
|
2021-02-12 08:20:45 +01:00
|
|
|
short_name="whatever1",
|
|
|
|
full_name="whatever1",
|
2020-07-04 23:20:55 +02:00
|
|
|
user_profile=admin_profile,
|
|
|
|
)
|
|
|
|
self._send_and_verify_message(admin_owned_bot, stream_name)
|
|
|
|
|
|
|
|
non_admin_profile = self.example_user("hamlet")
|
|
|
|
self.login_user(non_admin_profile)
|
|
|
|
|
|
|
|
# Non admins and their owned bots cannot send to STREAM_POST_POLICY_ADMINS streams
|
2021-02-12 08:19:30 +01:00
|
|
|
self._send_and_verify_message(
|
|
|
|
non_admin_profile,
|
|
|
|
stream_name,
|
2024-04-16 20:31:49 +02:00
|
|
|
"Only organization administrators can send to this channel.",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
non_admin_owned_bot = self.create_test_bot(
|
2021-02-12 08:20:45 +01:00
|
|
|
short_name="whatever2",
|
|
|
|
full_name="whatever2",
|
2020-07-04 23:20:55 +02:00
|
|
|
user_profile=non_admin_profile,
|
|
|
|
)
|
2021-02-12 08:19:30 +01:00
|
|
|
self._send_and_verify_message(
|
|
|
|
non_admin_owned_bot,
|
|
|
|
stream_name,
|
2024-04-16 20:31:49 +02:00
|
|
|
"Only organization administrators can send to this channel.",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
|
2021-03-29 18:28:27 +02:00
|
|
|
moderator_profile = self.example_user("shiva")
|
|
|
|
self.login_user(moderator_profile)
|
|
|
|
|
|
|
|
# Moderators and their owned bots cannot send to STREAM_POST_POLICY_ADMINS streams
|
|
|
|
self._send_and_verify_message(
|
|
|
|
moderator_profile,
|
|
|
|
stream_name,
|
2024-04-16 20:31:49 +02:00
|
|
|
"Only organization administrators can send to this channel.",
|
2021-03-29 18:28:27 +02:00
|
|
|
)
|
|
|
|
moderator_owned_bot = self.create_test_bot(
|
|
|
|
short_name="whatever3",
|
|
|
|
full_name="whatever3",
|
|
|
|
user_profile=moderator_profile,
|
|
|
|
)
|
|
|
|
self._send_and_verify_message(
|
|
|
|
moderator_owned_bot,
|
|
|
|
stream_name,
|
2024-04-16 20:31:49 +02:00
|
|
|
"Only organization administrators can send to this channel.",
|
2021-03-29 18:28:27 +02:00
|
|
|
)
|
|
|
|
|
2020-07-04 23:20:55 +02:00
|
|
|
# Bots without owner (except cross realm bot) cannot send to announcement only streams
|
|
|
|
bot_without_owner = do_create_user(
|
2021-02-12 08:20:45 +01:00
|
|
|
email="free-bot@zulip.testserver",
|
|
|
|
password="",
|
2020-07-04 23:20:55 +02:00
|
|
|
realm=non_admin_profile.realm,
|
2021-02-12 08:20:45 +01:00
|
|
|
full_name="freebot",
|
2020-07-04 23:20:55 +02:00
|
|
|
bot_type=UserProfile.DEFAULT_BOT,
|
2021-02-06 14:27:06 +01:00
|
|
|
acting_user=None,
|
2020-07-04 23:20:55 +02:00
|
|
|
)
|
2021-02-12 08:19:30 +01:00
|
|
|
self._send_and_verify_message(
|
|
|
|
bot_without_owner,
|
|
|
|
stream_name,
|
2024-04-16 20:31:49 +02:00
|
|
|
"Only organization administrators can send to this channel.",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
|
|
|
|
# Cross realm bots should be allowed
|
2021-03-08 11:54:39 +01:00
|
|
|
notification_bot = get_system_bot("notification-bot@zulip.com", stream.realm_id)
|
2021-02-12 08:19:30 +01:00
|
|
|
internal_send_stream_message(
|
2021-02-18 20:14:05 +01:00
|
|
|
notification_bot, stream, "Test topic", "Test message by notification bot"
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(self.get_last_message().content, "Test message by notification bot")
|
2020-07-04 23:20:55 +02:00
|
|
|
|
2020-10-08 20:30:11 +02:00
|
|
|
guest_profile = self.example_user("polonius")
|
|
|
|
# Guests cannot send to non-STREAM_POST_POLICY_EVERYONE streams
|
2021-02-12 08:19:30 +01:00
|
|
|
self._send_and_verify_message(
|
2024-04-16 20:31:49 +02:00
|
|
|
guest_profile, stream_name, "Only organization administrators can send to this channel."
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-10-08 20:30:11 +02:00
|
|
|
|
2021-03-29 16:01:39 +02:00
|
|
|
def test_sending_message_as_stream_post_policy_moderators(self) -> None:
|
|
|
|
"""
|
|
|
|
Sending messages to streams which only the moderators can post to.
|
|
|
|
"""
|
|
|
|
admin_profile = self.example_user("iago")
|
|
|
|
self.login_user(admin_profile)
|
|
|
|
|
|
|
|
stream_name = "Verona"
|
|
|
|
stream = get_stream(stream_name, admin_profile.realm)
|
2021-12-15 01:04:35 +01:00
|
|
|
do_change_stream_post_policy(
|
|
|
|
stream, Stream.STREAM_POST_POLICY_MODERATORS, acting_user=admin_profile
|
|
|
|
)
|
2021-03-29 16:01:39 +02:00
|
|
|
|
|
|
|
# Admins and their owned bots can send to STREAM_POST_POLICY_MODERATORS streams
|
|
|
|
self._send_and_verify_message(admin_profile, stream_name)
|
|
|
|
admin_owned_bot = self.create_test_bot(
|
|
|
|
short_name="whatever1",
|
|
|
|
full_name="whatever1",
|
|
|
|
user_profile=admin_profile,
|
|
|
|
)
|
|
|
|
self._send_and_verify_message(admin_owned_bot, stream_name)
|
|
|
|
|
|
|
|
moderator_profile = self.example_user("shiva")
|
|
|
|
self.login_user(moderator_profile)
|
|
|
|
|
|
|
|
# Moderators and their owned bots can send to STREAM_POST_POLICY_MODERATORS streams
|
|
|
|
self._send_and_verify_message(moderator_profile, stream_name)
|
|
|
|
moderator_owned_bot = self.create_test_bot(
|
|
|
|
short_name="whatever2",
|
|
|
|
full_name="whatever2",
|
|
|
|
user_profile=moderator_profile,
|
|
|
|
)
|
|
|
|
self._send_and_verify_message(moderator_owned_bot, stream_name)
|
|
|
|
|
|
|
|
non_admin_profile = self.example_user("hamlet")
|
|
|
|
self.login_user(non_admin_profile)
|
|
|
|
|
|
|
|
# Members and their owned bots cannot send to STREAM_POST_POLICY_MODERATORS streams
|
|
|
|
self._send_and_verify_message(
|
|
|
|
non_admin_profile,
|
|
|
|
stream_name,
|
2024-04-16 20:31:49 +02:00
|
|
|
"Only organization administrators and moderators can send to this channel.",
|
2021-03-29 16:01:39 +02:00
|
|
|
)
|
|
|
|
non_admin_owned_bot = self.create_test_bot(
|
|
|
|
short_name="whatever3",
|
|
|
|
full_name="whatever3",
|
|
|
|
user_profile=non_admin_profile,
|
|
|
|
)
|
|
|
|
self._send_and_verify_message(
|
|
|
|
non_admin_owned_bot,
|
|
|
|
stream_name,
|
2024-04-16 20:31:49 +02:00
|
|
|
"Only organization administrators and moderators can send to this channel.",
|
2021-03-29 16:01:39 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
# Bots without owner (except cross realm bot) cannot send to STREAM_POST_POLICY_MODERATORS streams.
|
|
|
|
bot_without_owner = do_create_user(
|
|
|
|
email="free-bot@zulip.testserver",
|
|
|
|
password="",
|
|
|
|
realm=non_admin_profile.realm,
|
|
|
|
full_name="freebot",
|
|
|
|
bot_type=UserProfile.DEFAULT_BOT,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
self._send_and_verify_message(
|
|
|
|
bot_without_owner,
|
|
|
|
stream_name,
|
2024-04-16 20:31:49 +02:00
|
|
|
"Only organization administrators and moderators can send to this channel.",
|
2021-03-29 16:01:39 +02:00
|
|
|
)
|
|
|
|
|
2021-03-08 11:54:39 +01:00
|
|
|
# System bots should be allowed
|
|
|
|
notification_bot = get_system_bot("notification-bot@zulip.com", stream.realm_id)
|
2021-03-29 16:01:39 +02:00
|
|
|
internal_send_stream_message(
|
|
|
|
notification_bot, stream, "Test topic", "Test message by notification bot"
|
|
|
|
)
|
|
|
|
self.assertEqual(self.get_last_message().content, "Test message by notification bot")
|
|
|
|
|
|
|
|
guest_profile = self.example_user("polonius")
|
|
|
|
# Guests cannot send to non-STREAM_POST_POLICY_EVERYONE streams
|
|
|
|
self._send_and_verify_message(
|
|
|
|
guest_profile,
|
|
|
|
stream_name,
|
2024-04-16 20:31:49 +02:00
|
|
|
"Only organization administrators and moderators can send to this channel.",
|
2021-03-29 16:01:39 +02:00
|
|
|
)
|
|
|
|
|
2020-07-04 23:20:55 +02:00
|
|
|
def test_sending_message_as_stream_post_policy_restrict_new_members(self) -> None:
|
|
|
|
"""
|
2021-03-29 17:54:57 +02:00
|
|
|
Sending messages to streams which new members cannot post to.
|
2020-07-04 23:20:55 +02:00
|
|
|
"""
|
|
|
|
admin_profile = self.example_user("iago")
|
|
|
|
self.login_user(admin_profile)
|
|
|
|
|
2021-03-01 11:33:24 +01:00
|
|
|
do_set_realm_property(admin_profile.realm, "waiting_period_threshold", 10, acting_user=None)
|
2023-11-19 19:45:19 +01:00
|
|
|
admin_profile.date_joined = timezone_now() - timedelta(days=9)
|
2020-07-04 23:20:55 +02:00
|
|
|
admin_profile.save()
|
2021-02-24 20:39:28 +01:00
|
|
|
self.assertTrue(admin_profile.is_provisional_member)
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assertTrue(admin_profile.is_realm_admin)
|
|
|
|
|
|
|
|
stream_name = "Verona"
|
|
|
|
stream = get_stream(stream_name, admin_profile.realm)
|
2021-12-15 01:04:35 +01:00
|
|
|
do_change_stream_post_policy(
|
|
|
|
stream, Stream.STREAM_POST_POLICY_RESTRICT_NEW_MEMBERS, acting_user=admin_profile
|
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
|
|
|
|
# Admins and their owned bots can send to STREAM_POST_POLICY_RESTRICT_NEW_MEMBERS streams,
|
|
|
|
# even if the admin is a new user
|
|
|
|
self._send_and_verify_message(admin_profile, stream_name)
|
|
|
|
admin_owned_bot = self.create_test_bot(
|
2021-02-12 08:20:45 +01:00
|
|
|
short_name="whatever1",
|
|
|
|
full_name="whatever1",
|
2020-07-04 23:20:55 +02:00
|
|
|
user_profile=admin_profile,
|
|
|
|
)
|
|
|
|
self._send_and_verify_message(admin_owned_bot, stream_name)
|
|
|
|
|
|
|
|
non_admin_profile = self.example_user("hamlet")
|
|
|
|
self.login_user(non_admin_profile)
|
|
|
|
|
2023-11-19 19:45:19 +01:00
|
|
|
non_admin_profile.date_joined = timezone_now() - timedelta(days=9)
|
2020-07-04 23:20:55 +02:00
|
|
|
non_admin_profile.save()
|
2021-02-24 20:39:28 +01:00
|
|
|
self.assertTrue(non_admin_profile.is_provisional_member)
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assertFalse(non_admin_profile.is_realm_admin)
|
|
|
|
|
|
|
|
# Non admins and their owned bots can send to STREAM_POST_POLICY_RESTRICT_NEW_MEMBERS streams,
|
|
|
|
# if the user is not a new member
|
2021-02-12 08:19:30 +01:00
|
|
|
self._send_and_verify_message(
|
2024-04-16 20:31:49 +02:00
|
|
|
non_admin_profile, stream_name, "New members cannot send to this channel."
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
non_admin_owned_bot = self.create_test_bot(
|
2021-02-12 08:20:45 +01:00
|
|
|
short_name="whatever2",
|
|
|
|
full_name="whatever2",
|
2020-07-04 23:20:55 +02:00
|
|
|
user_profile=non_admin_profile,
|
|
|
|
)
|
2021-02-12 08:19:30 +01:00
|
|
|
self._send_and_verify_message(
|
2024-04-16 20:31:49 +02:00
|
|
|
non_admin_owned_bot, stream_name, "New members cannot send to this channel."
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
|
2023-11-19 19:45:19 +01:00
|
|
|
non_admin_profile.date_joined = timezone_now() - timedelta(days=11)
|
2021-01-07 20:33:02 +01:00
|
|
|
non_admin_profile.save()
|
2021-02-24 20:39:28 +01:00
|
|
|
self.assertFalse(non_admin_profile.is_provisional_member)
|
2021-01-07 20:33:02 +01:00
|
|
|
|
|
|
|
self._send_and_verify_message(non_admin_profile, stream_name)
|
|
|
|
# We again set bot owner here, as date_joined of non_admin_profile is changed.
|
|
|
|
non_admin_owned_bot.bot_owner = non_admin_profile
|
|
|
|
non_admin_owned_bot.save()
|
|
|
|
self._send_and_verify_message(non_admin_owned_bot, stream_name)
|
|
|
|
|
2021-01-06 20:37:38 +01:00
|
|
|
# Bots without owner (except cross realm bot) cannot send to STREAM_POST_POLICY_ADMINS_ONLY and
|
|
|
|
# STREAM_POST_POLICY_RESTRICT_NEW_MEMBERS streams
|
2020-07-04 23:20:55 +02:00
|
|
|
bot_without_owner = do_create_user(
|
2021-02-12 08:20:45 +01:00
|
|
|
email="free-bot@zulip.testserver",
|
|
|
|
password="",
|
2020-07-04 23:20:55 +02:00
|
|
|
realm=non_admin_profile.realm,
|
2021-02-12 08:20:45 +01:00
|
|
|
full_name="freebot",
|
2020-07-04 23:20:55 +02:00
|
|
|
bot_type=UserProfile.DEFAULT_BOT,
|
2021-02-06 14:27:06 +01:00
|
|
|
acting_user=None,
|
2020-07-04 23:20:55 +02:00
|
|
|
)
|
2021-02-12 08:19:30 +01:00
|
|
|
self._send_and_verify_message(
|
2024-04-16 20:31:49 +02:00
|
|
|
bot_without_owner, stream_name, "New members cannot send to this channel."
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
|
2020-12-28 17:14:26 +01:00
|
|
|
moderator_profile = self.example_user("shiva")
|
2023-11-19 19:45:19 +01:00
|
|
|
moderator_profile.date_joined = timezone_now() - timedelta(days=9)
|
2020-12-28 17:14:26 +01:00
|
|
|
moderator_profile.save()
|
|
|
|
self.assertTrue(moderator_profile.is_moderator)
|
|
|
|
self.assertFalse(moderator_profile.is_provisional_member)
|
|
|
|
|
|
|
|
# Moderators and their owned bots can send to STREAM_POST_POLICY_RESTRICT_NEW_MEMBERS
|
2021-03-29 17:47:13 +02:00
|
|
|
# streams, even if the moderator is a new user
|
2020-12-28 17:14:26 +01:00
|
|
|
self._send_and_verify_message(moderator_profile, stream_name)
|
|
|
|
moderator_owned_bot = self.create_test_bot(
|
|
|
|
short_name="whatever3",
|
|
|
|
full_name="whatever3",
|
|
|
|
user_profile=moderator_profile,
|
|
|
|
)
|
2023-11-19 19:45:19 +01:00
|
|
|
moderator_owned_bot.date_joined = timezone_now() - timedelta(days=11)
|
2020-12-28 17:14:26 +01:00
|
|
|
moderator_owned_bot.save()
|
|
|
|
self._send_and_verify_message(moderator_owned_bot, stream_name)
|
|
|
|
|
2021-03-08 11:54:39 +01:00
|
|
|
# System bots should be allowed
|
|
|
|
notification_bot = get_system_bot("notification-bot@zulip.com", stream.realm_id)
|
2021-02-12 08:19:30 +01:00
|
|
|
internal_send_stream_message(
|
2021-02-18 20:14:05 +01:00
|
|
|
notification_bot, stream, "Test topic", "Test message by notification bot"
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(self.get_last_message().content, "Test message by notification bot")
|
2020-07-04 23:20:55 +02:00
|
|
|
|
2020-10-08 20:30:11 +02:00
|
|
|
guest_profile = self.example_user("polonius")
|
|
|
|
# Guests cannot send to non-STREAM_POST_POLICY_EVERYONE streams
|
2021-02-12 08:19:30 +01:00
|
|
|
self._send_and_verify_message(
|
2024-04-16 20:31:49 +02:00
|
|
|
guest_profile, stream_name, "Guests cannot send to this channel."
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-10-08 20:30:11 +02:00
|
|
|
|
2020-07-04 23:20:55 +02:00
|
|
|
def test_api_message_with_default_to(self) -> None:
|
|
|
|
"""
|
|
|
|
Sending messages without a to field should be sent to the default
|
|
|
|
stream for the user_profile.
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
user = self.example_user("hamlet")
|
|
|
|
user.default_sending_stream_id = get_stream("Verona", user.realm).id
|
2020-07-04 23:20:55 +02:00
|
|
|
user.save()
|
2020-07-25 17:28:10 +02:00
|
|
|
# The `to` field is required according to OpenAPI specification
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.api_post(
|
|
|
|
user,
|
|
|
|
"/api/v1/messages",
|
|
|
|
{
|
2024-04-10 20:48:10 +02:00
|
|
|
"type": "channel",
|
2021-02-12 08:19:30 +01:00
|
|
|
"content": "Test message no to",
|
|
|
|
"topic": "Test topic",
|
|
|
|
},
|
|
|
|
intentionally_undocumented=True,
|
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
sent_message = self.get_last_message()
|
|
|
|
self.assertEqual(sent_message.content, "Test message no to")
|
|
|
|
|
|
|
|
def test_message_to_nonexistent_stream(self) -> None:
|
|
|
|
"""
|
|
|
|
Sending a message to a nonexistent stream fails.
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assertFalse(Stream.objects.filter(name="nonexistent_stream"))
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/json/messages",
|
|
|
|
{
|
2024-04-10 20:48:10 +02:00
|
|
|
"type": "channel",
|
2021-02-12 08:19:30 +01:00
|
|
|
"to": "nonexistent_stream",
|
|
|
|
"content": "Test message",
|
|
|
|
"topic": "Test topic",
|
|
|
|
},
|
|
|
|
)
|
2024-04-16 21:27:36 +02:00
|
|
|
self.assert_json_error(result, "Channel 'nonexistent_stream' does not exist")
|
2020-07-04 23:20:55 +02:00
|
|
|
|
|
|
|
def test_message_to_nonexistent_stream_with_bad_characters(self) -> None:
|
|
|
|
"""
|
|
|
|
Nonexistent stream name with bad characters should be escaped properly.
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assertFalse(Stream.objects.filter(name="""&<"'><non-existent>"""))
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/json/messages",
|
|
|
|
{
|
2024-04-10 20:48:10 +02:00
|
|
|
"type": "channel",
|
2021-02-12 08:19:30 +01:00
|
|
|
"to": """&<"'><non-existent>""",
|
|
|
|
"content": "Test message",
|
|
|
|
"topic": "Test topic",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
self.assert_json_error(
|
2024-04-16 21:27:36 +02:00
|
|
|
result, "Channel '&<"'><non-existent>' does not exist"
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
|
2023-10-09 13:11:32 +02:00
|
|
|
def test_message_to_stream_with_automatically_change_visibility_policy(self) -> None:
|
|
|
|
"""
|
|
|
|
Sending a message to a stream with the automatic follow/unmute policy
|
|
|
|
enabled results in including an extra optional parameter in the response.
|
|
|
|
"""
|
|
|
|
user = self.example_user("hamlet")
|
|
|
|
do_change_user_setting(
|
|
|
|
user,
|
|
|
|
"automatically_follow_topics_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_SEND,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
result = self.api_post(
|
|
|
|
user,
|
|
|
|
"/api/v1/messages",
|
|
|
|
{
|
2024-04-10 20:48:10 +02:00
|
|
|
"type": "channel",
|
2023-10-09 13:11:32 +02:00
|
|
|
"to": orjson.dumps("Verona").decode(),
|
|
|
|
"content": "Test message",
|
|
|
|
"topic": "Test topic",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
content = self.assert_json_success(result)
|
|
|
|
assert "automatic_new_visibility_policy" in content
|
|
|
|
self.assertEqual(content["automatic_new_visibility_policy"], 3)
|
|
|
|
|
|
|
|
# Hamlet sends another message to the same topic. There will be no change in the visibility
|
|
|
|
# policy, so the 'automatic_new_visibility_policy' parameter should be absent in the result.
|
|
|
|
result = self.api_post(
|
|
|
|
user,
|
|
|
|
"/api/v1/messages",
|
|
|
|
{
|
2024-04-10 20:48:10 +02:00
|
|
|
"type": "channel",
|
2023-10-09 13:11:32 +02:00
|
|
|
"to": orjson.dumps("Verona").decode(),
|
|
|
|
"content": "Another Test message",
|
|
|
|
"topic": "Test topic",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
content = self.assert_json_success(result)
|
|
|
|
assert "automatic_new_visibility_policy" not in content
|
|
|
|
|
2020-07-04 23:20:55 +02:00
|
|
|
def test_personal_message(self) -> None:
|
|
|
|
"""
|
|
|
|
Sending a personal message to a valid username is successful.
|
|
|
|
"""
|
|
|
|
user_profile = self.example_user("hamlet")
|
|
|
|
self.login_user(user_profile)
|
2021-02-12 08:20:45 +01:00
|
|
|
othello = self.example_user("othello")
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/json/messages",
|
|
|
|
{
|
2023-04-17 17:02:07 +02:00
|
|
|
"type": "direct",
|
2021-02-12 08:19:30 +01:00
|
|
|
"content": "Test message",
|
2022-09-13 08:39:44 +02:00
|
|
|
"to": orjson.dumps([othello.email]).decode(),
|
2021-02-12 08:19:30 +01:00
|
|
|
},
|
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assert_json_success(result)
|
2021-02-12 08:20:45 +01:00
|
|
|
message_id = orjson.loads(result.content)["id"]
|
2020-07-04 23:20:55 +02:00
|
|
|
|
|
|
|
recent_conversations = get_recent_private_conversations(user_profile)
|
2023-07-22 00:34:11 +02:00
|
|
|
[(recipient_id, recent_conversation)] = recent_conversations.items()
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(set(recent_conversation["user_ids"]), {othello.id})
|
|
|
|
self.assertEqual(recent_conversation["max_message_id"], message_id)
|
2020-07-04 23:20:55 +02:00
|
|
|
|
|
|
|
# Now send a message to yourself and see how that interacts with the data structure
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/json/messages",
|
|
|
|
{
|
2023-04-17 17:02:07 +02:00
|
|
|
"type": "direct",
|
2021-02-12 08:19:30 +01:00
|
|
|
"content": "Test message",
|
2022-09-13 08:39:44 +02:00
|
|
|
"to": orjson.dumps([user_profile.email]).decode(),
|
2021-02-12 08:19:30 +01:00
|
|
|
},
|
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assert_json_success(result)
|
2021-02-12 08:20:45 +01:00
|
|
|
self_message_id = orjson.loads(result.content)["id"]
|
2020-07-04 23:20:55 +02:00
|
|
|
|
|
|
|
recent_conversations = get_recent_private_conversations(user_profile)
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(recent_conversations, 2)
|
2020-07-04 23:20:55 +02:00
|
|
|
recent_conversation = recent_conversations[recipient_id]
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(set(recent_conversation["user_ids"]), {othello.id})
|
|
|
|
self.assertEqual(recent_conversation["max_message_id"], message_id)
|
2020-07-04 23:20:55 +02:00
|
|
|
|
|
|
|
# Now verify we have the appropriate self-pm data structure
|
|
|
|
del recent_conversations[recipient_id]
|
2023-07-22 00:34:11 +02:00
|
|
|
[(recipient_id, recent_conversation)] = recent_conversations.items()
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(set(recent_conversation["user_ids"]), set())
|
|
|
|
self.assertEqual(recent_conversation["max_message_id"], self_message_id)
|
2020-07-04 23:20:55 +02:00
|
|
|
|
|
|
|
def test_personal_message_by_id(self) -> None:
|
|
|
|
"""
|
2023-04-17 17:02:07 +02:00
|
|
|
Sending a personal message to a valid user ID is successful
|
|
|
|
for both valid strings for `type` parameter.
|
2020-07-04 23:20:55 +02:00
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2023-04-17 17:02:07 +02:00
|
|
|
recipient_type_name = ["direct", "private"]
|
2020-07-04 23:20:55 +02:00
|
|
|
|
2023-04-17 17:02:07 +02:00
|
|
|
for type in recipient_type_name:
|
|
|
|
result = self.client_post(
|
|
|
|
"/json/messages",
|
|
|
|
{
|
|
|
|
"type": type,
|
|
|
|
"content": "Test message",
|
|
|
|
"to": orjson.dumps([self.example_user("othello").id]).decode(),
|
|
|
|
},
|
|
|
|
)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
msg = self.get_last_message()
|
|
|
|
self.assertEqual("Test message", msg.content)
|
|
|
|
self.assertEqual(msg.recipient_id, self.example_user("othello").recipient_id)
|
2020-07-04 23:20:55 +02:00
|
|
|
|
|
|
|
def test_group_personal_message_by_id(self) -> None:
|
|
|
|
"""
|
2023-04-17 17:02:07 +02:00
|
|
|
Sending a personal message to a valid user ID is successful
|
|
|
|
for both valid strings for `type` parameter.
|
2020-07-04 23:20:55 +02:00
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2023-04-17 17:02:07 +02:00
|
|
|
recipient_type_name = ["direct", "private"]
|
2020-07-04 23:20:55 +02:00
|
|
|
|
2023-04-17 17:02:07 +02:00
|
|
|
for type in recipient_type_name:
|
|
|
|
result = self.client_post(
|
|
|
|
"/json/messages",
|
2021-02-12 08:19:30 +01:00
|
|
|
{
|
2023-04-17 17:02:07 +02:00
|
|
|
"type": type,
|
|
|
|
"content": "Test message",
|
|
|
|
"to": orjson.dumps(
|
|
|
|
[self.example_user("othello").id, self.example_user("cordelia").id]
|
|
|
|
).decode(),
|
|
|
|
},
|
|
|
|
)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
msg = self.get_last_message()
|
|
|
|
self.assertEqual("Test message", msg.content)
|
2024-07-04 14:05:48 +02:00
|
|
|
direct_message_group = get_or_create_direct_message_group(
|
2023-08-09 16:22:16 +02:00
|
|
|
[
|
|
|
|
self.example_user("hamlet").id,
|
|
|
|
self.example_user("othello").id,
|
|
|
|
self.example_user("cordelia").id,
|
|
|
|
]
|
2023-04-17 17:02:07 +02:00
|
|
|
)
|
2024-07-04 14:05:48 +02:00
|
|
|
self.assertEqual(msg.recipient_id, direct_message_group.recipient_id)
|
2020-07-04 23:20:55 +02:00
|
|
|
|
|
|
|
def test_personal_message_copying_self(self) -> None:
|
|
|
|
"""
|
|
|
|
Sending a personal message to yourself plus another user is successful,
|
|
|
|
and counts as a message just to that user.
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
othello = self.example_user("othello")
|
2020-07-04 23:20:55 +02:00
|
|
|
self.login_user(hamlet)
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/json/messages",
|
|
|
|
{
|
2023-04-17 17:02:07 +02:00
|
|
|
"type": "direct",
|
2021-02-12 08:19:30 +01:00
|
|
|
"content": "Test message",
|
|
|
|
"to": orjson.dumps([hamlet.id, othello.id]).decode(),
|
|
|
|
},
|
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
msg = self.get_last_message()
|
|
|
|
# Verify that we're not actually on the "recipient list"
|
|
|
|
self.assertNotIn("Hamlet", str(msg.recipient))
|
|
|
|
|
|
|
|
def test_personal_message_to_nonexistent_user(self) -> None:
|
|
|
|
"""
|
|
|
|
Sending a personal message to an invalid email returns error JSON.
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/json/messages",
|
|
|
|
{
|
2023-04-17 17:02:07 +02:00
|
|
|
"type": "direct",
|
2021-02-12 08:19:30 +01:00
|
|
|
"content": "Test message",
|
|
|
|
"to": "nonexistent",
|
|
|
|
},
|
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assert_json_error(result, "Invalid email 'nonexistent'")
|
|
|
|
|
|
|
|
def test_personal_message_to_deactivated_user(self) -> None:
|
|
|
|
"""
|
|
|
|
Sending a personal message to a deactivated user returns error JSON.
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
othello = self.example_user("othello")
|
|
|
|
cordelia = self.example_user("cordelia")
|
2021-03-27 06:02:12 +01:00
|
|
|
do_deactivate_user(othello, acting_user=None)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2020-07-04 23:20:55 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/json/messages",
|
|
|
|
{
|
2023-04-17 17:02:07 +02:00
|
|
|
"type": "direct",
|
2021-02-12 08:19:30 +01:00
|
|
|
"content": "Test message",
|
|
|
|
"to": orjson.dumps([othello.id]).decode(),
|
|
|
|
},
|
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assert_json_error(result, f"'{othello.email}' is no longer using Zulip.")
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/json/messages",
|
|
|
|
{
|
2023-04-17 17:02:07 +02:00
|
|
|
"type": "direct",
|
2021-02-12 08:19:30 +01:00
|
|
|
"content": "Test message",
|
|
|
|
"to": orjson.dumps([othello.id, cordelia.id]).decode(),
|
|
|
|
},
|
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assert_json_error(result, f"'{othello.email}' is no longer using Zulip.")
|
|
|
|
|
2023-10-25 09:15:41 +02:00
|
|
|
def test_personal_message_to_inaccessible_users(self) -> None:
|
|
|
|
othello = self.example_user("othello")
|
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
iago = self.example_user("iago")
|
|
|
|
|
|
|
|
self.set_up_db_for_testing_user_access()
|
|
|
|
self.login("polonius")
|
|
|
|
|
|
|
|
result = self.client_post(
|
|
|
|
"/json/messages",
|
|
|
|
{
|
|
|
|
"type": "direct",
|
|
|
|
"content": "Test direct message",
|
|
|
|
"to": orjson.dumps([othello.id]).decode(),
|
|
|
|
},
|
|
|
|
)
|
|
|
|
self.assert_json_error(
|
|
|
|
result, "You do not have permission to access some of the recipients."
|
|
|
|
)
|
|
|
|
|
|
|
|
result = self.client_post(
|
|
|
|
"/json/messages",
|
|
|
|
{
|
|
|
|
"type": "direct",
|
|
|
|
"content": "Test direct message",
|
|
|
|
"to": orjson.dumps([hamlet.id]).decode(),
|
|
|
|
},
|
|
|
|
)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
msg = self.get_last_message()
|
|
|
|
self.assertEqual(msg.content, "Test direct message")
|
|
|
|
|
|
|
|
result = self.client_post(
|
|
|
|
"/json/messages",
|
|
|
|
{
|
|
|
|
"type": "direct",
|
|
|
|
"content": "Test group direct message",
|
|
|
|
"to": orjson.dumps([othello.id, cordelia.id]).decode(),
|
|
|
|
},
|
|
|
|
)
|
|
|
|
self.assert_json_error(
|
|
|
|
result, "You do not have permission to access some of the recipients."
|
|
|
|
)
|
|
|
|
|
|
|
|
result = self.client_post(
|
|
|
|
"/json/messages",
|
|
|
|
{
|
|
|
|
"type": "direct",
|
|
|
|
"content": "Test group direct message",
|
|
|
|
"to": orjson.dumps([hamlet.id, cordelia.id]).decode(),
|
|
|
|
},
|
|
|
|
)
|
|
|
|
self.assert_json_error(
|
|
|
|
result, "You do not have permission to access some of the recipients."
|
|
|
|
)
|
|
|
|
|
|
|
|
result = self.client_post(
|
|
|
|
"/json/messages",
|
|
|
|
{
|
|
|
|
"type": "direct",
|
|
|
|
"content": "Test group direct message",
|
|
|
|
"to": orjson.dumps([hamlet.id, iago.id]).decode(),
|
|
|
|
},
|
|
|
|
)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
msg = self.get_last_message()
|
|
|
|
self.assertEqual(msg.content, "Test group direct message")
|
|
|
|
|
2020-07-04 23:20:55 +02:00
|
|
|
def test_invalid_type(self) -> None:
|
|
|
|
"""
|
|
|
|
Sending a message of unknown type returns error JSON.
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
|
|
|
othello = self.example_user("othello")
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/json/messages",
|
|
|
|
{
|
|
|
|
"type": "invalid type",
|
|
|
|
"content": "Test message",
|
|
|
|
"to": othello.email,
|
|
|
|
},
|
|
|
|
)
|
2023-04-17 17:02:07 +02:00
|
|
|
self.assert_json_error(result, "Invalid type")
|
2020-07-04 23:20:55 +02:00
|
|
|
|
|
|
|
def test_empty_message(self) -> None:
|
|
|
|
"""
|
|
|
|
Sending a message that is empty or only whitespace should fail
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
|
|
|
othello = self.example_user("othello")
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/json/messages",
|
2023-04-17 17:02:07 +02:00
|
|
|
{"type": "direct", "content": " ", "to": othello.email},
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assert_json_error(result, "Message must not be empty")
|
|
|
|
|
|
|
|
def test_empty_string_topic(self) -> None:
|
|
|
|
"""
|
|
|
|
Sending a message that has empty string topic should fail
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/json/messages",
|
|
|
|
{
|
2024-04-10 20:48:10 +02:00
|
|
|
"type": "channel",
|
2021-02-12 08:19:30 +01:00
|
|
|
"to": "Verona",
|
|
|
|
"content": "Test message",
|
|
|
|
"topic": "",
|
|
|
|
},
|
|
|
|
)
|
2022-01-11 22:07:06 +01:00
|
|
|
self.assert_json_error(result, "Topic can't be empty!")
|
2020-07-04 23:20:55 +02:00
|
|
|
|
|
|
|
def test_missing_topic(self) -> None:
|
|
|
|
"""
|
|
|
|
Sending a message without topic should fail
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/json/messages",
|
2024-04-10 20:48:10 +02:00
|
|
|
{"type": "channel", "to": "Verona", "content": "Test message"},
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assert_json_error(result, "Missing topic")
|
|
|
|
|
api: Prevent special characters in topics.
Special characters, including `\r`, `\n`, and more esoteric codepoints
like non-characters, can negatively affect rendering and UI behaviour.
Check for, and prevent making new messages with, characters in the
Unicode categories of `Cc` (control characters), `Cs`, (surrogates),
and `Cn` (unassigned, non-characters).
Fixes #20128.
2021-11-06 16:48:03 +01:00
|
|
|
def test_invalid_topic(self) -> None:
|
|
|
|
"""
|
|
|
|
Sending a message with invalid 'Cc', 'Cs' and 'Cn' category of unicode characters
|
|
|
|
"""
|
|
|
|
# For 'Cc' category
|
|
|
|
self.login("hamlet")
|
|
|
|
result = self.client_post(
|
|
|
|
"/json/messages",
|
|
|
|
{
|
2024-04-10 20:48:10 +02:00
|
|
|
"type": "channel",
|
api: Prevent special characters in topics.
Special characters, including `\r`, `\n`, and more esoteric codepoints
like non-characters, can negatively affect rendering and UI behaviour.
Check for, and prevent making new messages with, characters in the
Unicode categories of `Cc` (control characters), `Cs`, (surrogates),
and `Cn` (unassigned, non-characters).
Fixes #20128.
2021-11-06 16:48:03 +01:00
|
|
|
"to": "Verona",
|
|
|
|
"topic": "Test\n\rTopic",
|
|
|
|
"content": "Test message",
|
|
|
|
},
|
|
|
|
)
|
2022-01-11 22:03:11 +01:00
|
|
|
self.assert_json_error(result, "Invalid character in topic, at position 5!")
|
api: Prevent special characters in topics.
Special characters, including `\r`, `\n`, and more esoteric codepoints
like non-characters, can negatively affect rendering and UI behaviour.
Check for, and prevent making new messages with, characters in the
Unicode categories of `Cc` (control characters), `Cs`, (surrogates),
and `Cn` (unassigned, non-characters).
Fixes #20128.
2021-11-06 16:48:03 +01:00
|
|
|
|
|
|
|
# For 'Cn' category
|
|
|
|
result = self.client_post(
|
|
|
|
"/json/messages",
|
|
|
|
{
|
2024-04-10 20:48:10 +02:00
|
|
|
"type": "channel",
|
api: Prevent special characters in topics.
Special characters, including `\r`, `\n`, and more esoteric codepoints
like non-characters, can negatively affect rendering and UI behaviour.
Check for, and prevent making new messages with, characters in the
Unicode categories of `Cc` (control characters), `Cs`, (surrogates),
and `Cn` (unassigned, non-characters).
Fixes #20128.
2021-11-06 16:48:03 +01:00
|
|
|
"to": "Verona",
|
2023-12-05 18:45:07 +01:00
|
|
|
"topic": "Test\ufffeTopic",
|
api: Prevent special characters in topics.
Special characters, including `\r`, `\n`, and more esoteric codepoints
like non-characters, can negatively affect rendering and UI behaviour.
Check for, and prevent making new messages with, characters in the
Unicode categories of `Cc` (control characters), `Cs`, (surrogates),
and `Cn` (unassigned, non-characters).
Fixes #20128.
2021-11-06 16:48:03 +01:00
|
|
|
"content": "Test message",
|
|
|
|
},
|
|
|
|
)
|
2022-01-11 22:03:11 +01:00
|
|
|
self.assert_json_error(result, "Invalid character in topic, at position 5!")
|
api: Prevent special characters in topics.
Special characters, including `\r`, `\n`, and more esoteric codepoints
like non-characters, can negatively affect rendering and UI behaviour.
Check for, and prevent making new messages with, characters in the
Unicode categories of `Cc` (control characters), `Cs`, (surrogates),
and `Cn` (unassigned, non-characters).
Fixes #20128.
2021-11-06 16:48:03 +01:00
|
|
|
|
2023-04-17 17:02:07 +02:00
|
|
|
def test_invalid_recipient_type(self) -> None:
|
2020-07-04 23:20:55 +02:00
|
|
|
"""
|
2024-04-10 20:48:10 +02:00
|
|
|
Messages other than the type of "direct", "private", "channel" or "stream" are invalid.
|
2020-07-04 23:20:55 +02:00
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/json/messages",
|
|
|
|
{
|
|
|
|
"type": "invalid",
|
|
|
|
"to": "Verona",
|
|
|
|
"content": "Test message",
|
|
|
|
"topic": "Test topic",
|
|
|
|
},
|
|
|
|
)
|
2023-04-17 17:02:07 +02:00
|
|
|
self.assert_json_error(result, "Invalid type")
|
2020-07-04 23:20:55 +02:00
|
|
|
|
|
|
|
def test_private_message_without_recipients(self) -> None:
|
|
|
|
"""
|
2023-06-19 16:26:12 +02:00
|
|
|
Sending a direct message without recipients should fail
|
2020-07-04 23:20:55 +02:00
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/json/messages",
|
2023-04-17 17:02:07 +02:00
|
|
|
{"type": "direct", "content": "Test content", "to": ""},
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assert_json_error(result, "Message must have recipients")
|
|
|
|
|
2024-07-04 14:05:48 +02:00
|
|
|
def test_mirrored_direct_message_group(self) -> None:
|
2020-07-04 23:20:55 +02:00
|
|
|
"""
|
2024-07-04 14:05:48 +02:00
|
|
|
Sending a mirrored group direct message works
|
2020-07-04 23:20:55 +02:00
|
|
|
"""
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.api_post(
|
|
|
|
self.mit_user("starnine"),
|
2022-11-04 20:26:18 +01:00
|
|
|
"/api/v1/messages",
|
2021-02-12 08:19:30 +01:00
|
|
|
{
|
2023-04-17 17:02:07 +02:00
|
|
|
"type": "direct",
|
2021-02-12 08:19:30 +01:00
|
|
|
"sender": self.mit_email("sipbtest"),
|
|
|
|
"content": "Test message",
|
|
|
|
"client": "zephyr_mirror",
|
|
|
|
"to": orjson.dumps(
|
|
|
|
[self.mit_email("starnine"), self.mit_email("espuser")]
|
|
|
|
).decode(),
|
|
|
|
},
|
|
|
|
subdomain="zephyr",
|
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
def test_mirrored_personal(self) -> None:
|
|
|
|
"""
|
|
|
|
Sending a mirrored personal message works
|
|
|
|
"""
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.api_post(
|
|
|
|
self.mit_user("starnine"),
|
2022-11-04 20:26:18 +01:00
|
|
|
"/api/v1/messages",
|
2021-02-12 08:19:30 +01:00
|
|
|
{
|
2023-04-17 17:02:07 +02:00
|
|
|
"type": "direct",
|
2021-02-12 08:19:30 +01:00
|
|
|
"sender": self.mit_email("sipbtest"),
|
|
|
|
"content": "Test message",
|
|
|
|
"client": "zephyr_mirror",
|
2022-09-13 08:39:44 +02:00
|
|
|
"to": orjson.dumps([self.mit_email("starnine")]).decode(),
|
2021-02-12 08:19:30 +01:00
|
|
|
},
|
|
|
|
subdomain="zephyr",
|
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
def test_mirrored_personal_browser(self) -> None:
|
|
|
|
"""
|
|
|
|
Sending a mirrored personal message via the browser should not work.
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
user = self.mit_user("starnine")
|
2020-07-04 23:20:55 +02:00
|
|
|
self.login_user(user)
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/json/messages",
|
|
|
|
{
|
2023-04-17 17:02:07 +02:00
|
|
|
"type": "direct",
|
2021-02-12 08:19:30 +01:00
|
|
|
"sender": self.mit_email("sipbtest"),
|
|
|
|
"content": "Test message",
|
|
|
|
"client": "zephyr_mirror",
|
|
|
|
"to": self.mit_email("starnine"),
|
|
|
|
},
|
|
|
|
subdomain="zephyr",
|
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assert_json_error(result, "Invalid mirrored message")
|
|
|
|
|
|
|
|
def test_mirrored_personal_to_someone_else(self) -> None:
|
|
|
|
"""
|
|
|
|
Sending a mirrored personal message to someone else is not allowed.
|
|
|
|
"""
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.api_post(
|
|
|
|
self.mit_user("starnine"),
|
|
|
|
"/api/v1/messages",
|
|
|
|
{
|
2023-04-17 17:02:07 +02:00
|
|
|
"type": "direct",
|
2021-02-12 08:19:30 +01:00
|
|
|
"sender": self.mit_email("sipbtest"),
|
|
|
|
"content": "Test message",
|
|
|
|
"client": "zephyr_mirror",
|
|
|
|
"to": self.mit_email("espuser"),
|
|
|
|
},
|
|
|
|
subdomain="zephyr",
|
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assert_json_error(result, "User not authorized for this query")
|
|
|
|
|
2024-07-04 14:05:48 +02:00
|
|
|
def test_duplicated_mirrored_direct_message_group(self) -> None:
|
2020-07-04 23:20:55 +02:00
|
|
|
"""
|
2024-07-04 14:05:48 +02:00
|
|
|
Sending two mirrored direct message groups in the row return the same ID
|
2020-07-04 23:20:55 +02:00
|
|
|
"""
|
2021-02-12 08:19:30 +01:00
|
|
|
msg = {
|
2023-04-17 17:02:07 +02:00
|
|
|
"type": "direct",
|
2021-02-12 08:19:30 +01:00
|
|
|
"sender": self.mit_email("sipbtest"),
|
|
|
|
"content": "Test message",
|
|
|
|
"client": "zephyr_mirror",
|
|
|
|
"to": orjson.dumps([self.mit_email("espuser"), self.mit_email("starnine")]).decode(),
|
|
|
|
}
|
2020-07-04 23:20:55 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
with mock.patch(
|
2021-02-12 08:20:45 +01:00
|
|
|
"DNS.dnslookup",
|
2021-02-12 08:19:30 +01:00
|
|
|
return_value=[
|
2021-02-12 08:20:45 +01:00
|
|
|
["starnine:*:84233:101:Athena Consulting Exchange User,,,:/mit/starnine:/bin/bash"]
|
2021-02-12 08:19:30 +01:00
|
|
|
],
|
|
|
|
):
|
|
|
|
result1 = self.api_post(
|
|
|
|
self.mit_user("starnine"), "/api/v1/messages", msg, subdomain="zephyr"
|
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assert_json_success(result1)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
with mock.patch(
|
2021-02-12 08:20:45 +01:00
|
|
|
"DNS.dnslookup",
|
|
|
|
return_value=[["espuser:*:95494:101:Esp Classroom,,,:/mit/espuser:/bin/athena/bash"]],
|
2021-02-12 08:19:30 +01:00
|
|
|
):
|
|
|
|
result2 = self.api_post(
|
|
|
|
self.mit_user("espuser"), "/api/v1/messages", msg, subdomain="zephyr"
|
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assert_json_success(result2)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(orjson.loads(result1.content)["id"], orjson.loads(result2.content)["id"])
|
2020-07-04 23:20:55 +02:00
|
|
|
|
|
|
|
def test_message_with_null_bytes(self) -> None:
|
|
|
|
"""
|
|
|
|
A message with null bytes in it is handled.
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2021-02-12 08:19:30 +01:00
|
|
|
post_data = {
|
2024-04-10 20:48:10 +02:00
|
|
|
"type": "channel",
|
2021-02-12 08:19:30 +01:00
|
|
|
"to": "Verona",
|
|
|
|
"content": " I like null bytes \x00 in my content",
|
|
|
|
"topic": "Test topic",
|
|
|
|
}
|
2020-07-04 23:20:55 +02:00
|
|
|
result = self.client_post("/json/messages", post_data)
|
|
|
|
self.assert_json_error(result, "Message must not contain null bytes")
|
|
|
|
|
|
|
|
def test_strip_message(self) -> None:
|
|
|
|
"""
|
|
|
|
A message with mixed whitespace at the end is cleaned up.
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2021-02-12 08:19:30 +01:00
|
|
|
post_data = {
|
2024-04-10 20:48:10 +02:00
|
|
|
"type": "channel",
|
2022-09-13 08:39:44 +02:00
|
|
|
"to": orjson.dumps("Verona").decode(),
|
2021-02-12 08:19:30 +01:00
|
|
|
"content": " I like whitespace at the end! \n\n \n",
|
|
|
|
"topic": "Test topic",
|
|
|
|
}
|
2020-07-04 23:20:55 +02:00
|
|
|
result = self.client_post("/json/messages", post_data)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
sent_message = self.get_last_message()
|
|
|
|
self.assertEqual(sent_message.content, " I like whitespace at the end!")
|
|
|
|
|
2021-07-05 17:55:02 +02:00
|
|
|
# Test if it removes the new line from the beginning of the message.
|
|
|
|
post_data = {
|
2024-04-10 20:48:10 +02:00
|
|
|
"type": "channel",
|
2022-09-13 08:39:44 +02:00
|
|
|
"to": orjson.dumps("Verona").decode(),
|
2021-07-05 17:55:02 +02:00
|
|
|
"content": "\nAvoid the new line at the beginning of the message.",
|
|
|
|
"topic": "Test topic",
|
|
|
|
}
|
|
|
|
result = self.client_post("/json/messages", post_data)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
sent_message = self.get_last_message()
|
|
|
|
self.assertEqual(
|
|
|
|
sent_message.content, "Avoid the new line at the beginning of the message."
|
|
|
|
)
|
|
|
|
|
2021-06-03 15:04:22 +02:00
|
|
|
@override_settings(MAX_MESSAGE_LENGTH=25)
|
2020-07-04 23:20:55 +02:00
|
|
|
def test_long_message(self) -> None:
|
|
|
|
"""
|
|
|
|
Sending a message longer than the maximum message length succeeds but is
|
|
|
|
truncated.
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2021-06-03 15:04:22 +02:00
|
|
|
MAX_MESSAGE_LENGTH = settings.MAX_MESSAGE_LENGTH
|
2020-07-04 23:20:55 +02:00
|
|
|
long_message = "A" * (MAX_MESSAGE_LENGTH + 1)
|
2021-02-12 08:19:30 +01:00
|
|
|
post_data = {
|
2024-04-10 20:48:10 +02:00
|
|
|
"type": "channel",
|
2022-09-13 08:39:44 +02:00
|
|
|
"to": orjson.dumps("Verona").decode(),
|
2021-02-12 08:19:30 +01:00
|
|
|
"content": long_message,
|
|
|
|
"topic": "Test topic",
|
|
|
|
}
|
2020-07-04 23:20:55 +02:00
|
|
|
result = self.client_post("/json/messages", post_data)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
sent_message = self.get_last_message()
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertEqual(
|
|
|
|
sent_message.content, "A" * (MAX_MESSAGE_LENGTH - 20) + "\n[message truncated]"
|
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
|
|
|
|
def test_long_topic(self) -> None:
|
|
|
|
"""
|
|
|
|
Sending a message with a topic longer than the maximum topic length
|
|
|
|
succeeds, but the topic is truncated.
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2024-01-15 12:17:50 +01:00
|
|
|
long_topic_name = "A" * (MAX_TOPIC_NAME_LENGTH + 1)
|
2021-02-12 08:19:30 +01:00
|
|
|
post_data = {
|
2024-04-10 20:48:10 +02:00
|
|
|
"type": "channel",
|
2022-09-13 08:39:44 +02:00
|
|
|
"to": orjson.dumps("Verona").decode(),
|
2021-02-12 08:19:30 +01:00
|
|
|
"content": "test content",
|
2024-01-15 12:17:50 +01:00
|
|
|
"topic": long_topic_name,
|
2021-02-12 08:19:30 +01:00
|
|
|
}
|
2020-07-04 23:20:55 +02:00
|
|
|
result = self.client_post("/json/messages", post_data)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
sent_message = self.get_last_message()
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertEqual(sent_message.topic_name(), "A" * (MAX_TOPIC_NAME_LENGTH - 3) + "...")
|
2020-07-04 23:20:55 +02:00
|
|
|
|
|
|
|
def test_send_forged_message_as_not_superuser(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
|
|
|
"/json/messages",
|
|
|
|
{
|
2024-04-10 20:48:10 +02:00
|
|
|
"type": "channel",
|
2021-02-12 08:19:30 +01:00
|
|
|
"to": "Verona",
|
|
|
|
"content": "Test message",
|
|
|
|
"topic": "Test topic",
|
|
|
|
"forged": "true",
|
|
|
|
},
|
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assert_json_error(result, "User not authorized for this query")
|
|
|
|
|
|
|
|
def test_send_message_when_sender_is_not_set(self) -> None:
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.api_post(
|
|
|
|
self.mit_user("starnine"),
|
|
|
|
"/api/v1/messages",
|
|
|
|
{
|
2023-04-17 17:02:07 +02:00
|
|
|
"type": "direct",
|
2021-02-12 08:19:30 +01:00
|
|
|
"content": "Test message",
|
|
|
|
"client": "zephyr_mirror",
|
|
|
|
"to": self.mit_email("starnine"),
|
|
|
|
},
|
|
|
|
subdomain="zephyr",
|
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assert_json_error(result, "Missing sender")
|
|
|
|
|
|
|
|
def test_send_message_as_not_superuser_when_type_is_not_private(self) -> None:
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.api_post(
|
|
|
|
self.mit_user("starnine"),
|
|
|
|
"/api/v1/messages",
|
|
|
|
{
|
2024-04-10 20:48:10 +02:00
|
|
|
"type": "channel",
|
2021-02-12 08:19:30 +01:00
|
|
|
"sender": self.mit_email("sipbtest"),
|
|
|
|
"content": "Test message",
|
|
|
|
"client": "zephyr_mirror",
|
|
|
|
"to": self.mit_email("starnine"),
|
|
|
|
},
|
|
|
|
subdomain="zephyr",
|
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assert_json_error(result, "User not authorized for this query")
|
|
|
|
|
|
|
|
@mock.patch("zerver.views.message_send.create_mirrored_message_users")
|
|
|
|
def test_send_message_create_mirrored_message_user_returns_invalid_input(
|
2021-02-12 08:19:30 +01:00
|
|
|
self, create_mirrored_message_users_mock: Any
|
|
|
|
) -> None:
|
2022-11-17 09:30:48 +01:00
|
|
|
create_mirrored_message_users_mock.side_effect = InvalidMirrorInputError()
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.api_post(
|
|
|
|
self.mit_user("starnine"),
|
|
|
|
"/api/v1/messages",
|
|
|
|
{
|
2023-04-17 17:02:07 +02:00
|
|
|
"type": "direct",
|
2021-02-12 08:19:30 +01:00
|
|
|
"sender": self.mit_email("sipbtest"),
|
|
|
|
"content": "Test message",
|
|
|
|
"client": "zephyr_mirror",
|
|
|
|
"to": self.mit_email("starnine"),
|
|
|
|
},
|
|
|
|
subdomain="zephyr",
|
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assert_json_error(result, "Invalid mirrored message")
|
|
|
|
|
|
|
|
@mock.patch("zerver.views.message_send.create_mirrored_message_users")
|
|
|
|
def test_send_message_when_client_is_zephyr_mirror_but_string_id_is_not_zephyr(
|
2021-02-12 08:19:30 +01:00
|
|
|
self, create_mirrored_message_users_mock: Any
|
|
|
|
) -> None:
|
2020-07-04 23:20:55 +02:00
|
|
|
create_mirrored_message_users_mock.return_value = mock.Mock()
|
|
|
|
user = self.mit_user("starnine")
|
2021-02-12 08:20:45 +01:00
|
|
|
user.realm.string_id = "notzephyr"
|
2020-07-04 23:20:55 +02:00
|
|
|
user.realm.save()
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.api_post(
|
|
|
|
user,
|
|
|
|
"/api/v1/messages",
|
|
|
|
{
|
2023-04-17 17:02:07 +02:00
|
|
|
"type": "direct",
|
2021-02-12 08:19:30 +01:00
|
|
|
"sender": self.mit_email("sipbtest"),
|
|
|
|
"content": "Test message",
|
|
|
|
"client": "zephyr_mirror",
|
|
|
|
"to": user.email,
|
|
|
|
},
|
|
|
|
subdomain="notzephyr",
|
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assert_json_error(result, "Zephyr mirroring is not allowed in this organization")
|
|
|
|
|
|
|
|
@mock.patch("zerver.views.message_send.create_mirrored_message_users")
|
|
|
|
def test_send_message_when_client_is_zephyr_mirror_but_recipient_is_user_id(
|
2021-02-12 08:19:30 +01:00
|
|
|
self, create_mirrored_message_users_mock: Any
|
|
|
|
) -> None:
|
2020-07-04 23:20:55 +02:00
|
|
|
create_mirrored_message_users_mock.return_value = mock.Mock()
|
|
|
|
user = self.mit_user("starnine")
|
|
|
|
self.login_user(user)
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.api_post(
|
|
|
|
user,
|
|
|
|
"/api/v1/messages",
|
|
|
|
{
|
2023-04-17 17:02:07 +02:00
|
|
|
"type": "direct",
|
2021-02-12 08:19:30 +01:00
|
|
|
"sender": self.mit_email("sipbtest"),
|
|
|
|
"content": "Test message",
|
|
|
|
"client": "zephyr_mirror",
|
|
|
|
"to": orjson.dumps([user.id]).decode(),
|
|
|
|
},
|
|
|
|
subdomain="zephyr",
|
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assert_json_error(result, "Mirroring not allowed with recipient user IDs")
|
|
|
|
|
|
|
|
def test_send_message_irc_mirror(self) -> None:
|
2023-03-01 07:34:25 +01:00
|
|
|
reset_email_visibility_to_everyone_in_zulip_realm()
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2020-07-04 23:20:55 +02:00
|
|
|
bot_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "IRC bot",
|
|
|
|
"short_name": "irc",
|
2020-07-04 23:20:55 +02:00
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
email = "irc-bot@zulip.testserver"
|
2021-02-12 08:20:45 +01:00
|
|
|
user = get_user(email, get_realm("zulip"))
|
2020-12-20 14:21:42 +01:00
|
|
|
user.can_forge_sender = True
|
2020-07-04 23:20:55 +02:00
|
|
|
user.save()
|
2021-02-12 08:20:45 +01:00
|
|
|
user = get_user(email, get_realm("zulip"))
|
2020-07-04 23:20:55 +02:00
|
|
|
self.subscribe(user, "IRCland")
|
|
|
|
|
|
|
|
# Simulate a mirrored message with a slightly old timestamp.
|
2023-11-19 19:45:19 +01:00
|
|
|
fake_date_sent = timezone_now() - timedelta(minutes=37)
|
2020-07-04 23:20:55 +02:00
|
|
|
fake_timestamp = datetime_to_timestamp(fake_date_sent)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.api_post(
|
|
|
|
user,
|
|
|
|
"/api/v1/messages",
|
|
|
|
{
|
2024-04-10 20:48:10 +02:00
|
|
|
"type": "channel",
|
2021-02-12 08:19:30 +01:00
|
|
|
"forged": "true",
|
|
|
|
"time": fake_timestamp,
|
|
|
|
"sender": "irc-user@irc.zulip.com",
|
|
|
|
"content": "Test message",
|
|
|
|
"client": "irc_mirror",
|
|
|
|
"topic": "from irc",
|
2022-09-13 08:39:44 +02:00
|
|
|
"to": orjson.dumps("IRCLand").decode(),
|
2021-02-12 08:19:30 +01:00
|
|
|
},
|
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
msg = self.get_last_message()
|
|
|
|
self.assertEqual(int(datetime_to_timestamp(msg.date_sent)), int(fake_timestamp))
|
|
|
|
|
|
|
|
# Now test again using forged=yes
|
2023-11-19 19:45:19 +01:00
|
|
|
fake_date_sent = timezone_now() - timedelta(minutes=22)
|
2020-07-04 23:20:55 +02:00
|
|
|
fake_timestamp = datetime_to_timestamp(fake_date_sent)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.api_post(
|
|
|
|
user,
|
|
|
|
"/api/v1/messages",
|
|
|
|
{
|
2024-04-10 20:48:10 +02:00
|
|
|
"type": "channel",
|
2021-02-12 08:19:30 +01:00
|
|
|
"forged": "yes",
|
|
|
|
"time": fake_timestamp,
|
|
|
|
"sender": "irc-user@irc.zulip.com",
|
|
|
|
"content": "Test message",
|
|
|
|
"client": "irc_mirror",
|
|
|
|
"topic": "from irc",
|
2022-09-13 08:39:44 +02:00
|
|
|
"to": orjson.dumps("IRCLand").decode(),
|
2021-02-12 08:19:30 +01:00
|
|
|
},
|
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
msg = self.get_last_message()
|
|
|
|
self.assertEqual(int(datetime_to_timestamp(msg.date_sent)), int(fake_timestamp))
|
|
|
|
|
2020-12-20 14:21:42 +01:00
|
|
|
def test_unsubscribed_can_forge_sender(self) -> None:
|
2023-03-01 07:34:25 +01:00
|
|
|
reset_email_visibility_to_everyone_in_zulip_realm()
|
2020-07-04 23:20:55 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
stream_name = "private_stream"
|
2020-07-04 23:20:55 +02:00
|
|
|
self.make_stream(stream_name, invite_only=True)
|
|
|
|
|
|
|
|
self.unsubscribe(cordelia, stream_name)
|
|
|
|
|
2020-12-20 14:21:42 +01:00
|
|
|
# As long as Cordelia cam_forge_sender, she can send messages
|
2020-07-04 23:20:55 +02:00
|
|
|
# to ANY stream, even one she is not unsubscribed to, and
|
|
|
|
# she can do it for herself or on behalf of a mirrored user.
|
|
|
|
|
|
|
|
def test_with(sender_email: str, client: str, forged: bool) -> None:
|
|
|
|
payload = dict(
|
2024-04-10 20:48:10 +02:00
|
|
|
type="channel",
|
2022-09-13 08:39:44 +02:00
|
|
|
to=orjson.dumps(stream_name).decode(),
|
2020-07-04 23:20:55 +02:00
|
|
|
client=client,
|
2021-02-12 08:20:45 +01:00
|
|
|
topic="whatever",
|
|
|
|
content="whatever",
|
2020-08-07 01:09:47 +02:00
|
|
|
forged=orjson.dumps(forged).decode(),
|
2020-07-04 23:20:55 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
# Only pass the 'sender' property when doing mirroring behavior.
|
|
|
|
if forged:
|
2021-02-12 08:20:45 +01:00
|
|
|
payload["sender"] = sender_email
|
2020-07-04 23:20:55 +02:00
|
|
|
|
2020-12-20 14:21:42 +01:00
|
|
|
cordelia.can_forge_sender = False
|
2020-07-04 23:20:55 +02:00
|
|
|
cordelia.save()
|
|
|
|
|
|
|
|
result = self.api_post(cordelia, "/api/v1/messages", payload)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error_contains(result, "authorized")
|
2020-07-04 23:20:55 +02:00
|
|
|
|
2020-12-20 14:21:42 +01:00
|
|
|
cordelia.can_forge_sender = True
|
2020-07-04 23:20:55 +02:00
|
|
|
cordelia.save()
|
|
|
|
|
|
|
|
result = self.api_post(cordelia, "/api/v1/messages", payload)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
test_with(
|
|
|
|
sender_email=cordelia.email,
|
2021-02-12 08:20:45 +01:00
|
|
|
client="test suite",
|
2020-07-04 23:20:55 +02:00
|
|
|
forged=False,
|
|
|
|
)
|
|
|
|
|
|
|
|
test_with(
|
2021-02-12 08:20:45 +01:00
|
|
|
sender_email="irc_person@zulip.com",
|
|
|
|
client="irc_mirror",
|
2020-07-04 23:20:55 +02:00
|
|
|
forged=True,
|
|
|
|
)
|
|
|
|
|
|
|
|
def test_bot_can_send_to_owner_stream(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
cordelia = self.example_user("cordelia")
|
2020-07-04 23:20:55 +02:00
|
|
|
bot = self.create_test_bot(
|
2021-02-12 08:20:45 +01:00
|
|
|
short_name="whatever",
|
2020-07-04 23:20:55 +02:00
|
|
|
user_profile=cordelia,
|
|
|
|
)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
stream_name = "private_stream"
|
2020-07-04 23:20:55 +02:00
|
|
|
self.make_stream(stream_name, invite_only=True)
|
|
|
|
|
|
|
|
payload = dict(
|
2024-04-10 20:48:10 +02:00
|
|
|
type="channel",
|
2022-09-13 08:39:44 +02:00
|
|
|
to=orjson.dumps(stream_name).decode(),
|
2021-02-12 08:20:45 +01:00
|
|
|
topic="whatever",
|
|
|
|
content="whatever",
|
2020-07-04 23:20:55 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
result = self.api_post(bot, "/api/v1/messages", payload)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error_contains(result, "Not authorized to send")
|
2020-07-04 23:20:55 +02:00
|
|
|
|
|
|
|
# We subscribe the bot owner! (aka cordelia)
|
|
|
|
assert bot.bot_owner is not None
|
|
|
|
self.subscribe(bot.bot_owner, stream_name)
|
|
|
|
|
|
|
|
result = self.api_post(bot, "/api/v1/messages", payload)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
def test_cross_realm_bots_can_use_api_on_own_subdomain(self) -> None:
|
|
|
|
# Cross realm bots should use internal_send_*_message, not the API:
|
2021-03-08 11:39:48 +01:00
|
|
|
internal_realm = get_realm("zulipinternal")
|
|
|
|
notification_bot = self.notification_bot(internal_realm)
|
|
|
|
stream = self.make_stream("notify_channel", internal_realm)
|
2020-07-04 23:20:55 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.api_post(
|
|
|
|
notification_bot,
|
|
|
|
"/api/v1/messages",
|
|
|
|
{
|
2024-04-10 20:48:10 +02:00
|
|
|
"type": "channel",
|
2022-09-13 08:39:44 +02:00
|
|
|
"to": orjson.dumps("notify_channel").decode(),
|
2021-02-12 08:19:30 +01:00
|
|
|
"content": "Test message",
|
|
|
|
"topic": "Test topic",
|
|
|
|
},
|
2021-02-12 08:20:45 +01:00
|
|
|
subdomain="zulipinternal",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-07-04 23:20:55 +02:00
|
|
|
|
|
|
|
self.assert_json_success(result)
|
|
|
|
message = self.get_last_message()
|
|
|
|
|
|
|
|
self.assertEqual(message.content, "Test message")
|
|
|
|
self.assertEqual(message.sender, notification_bot)
|
|
|
|
self.assertEqual(message.recipient.type_id, stream.id)
|
|
|
|
|
|
|
|
def test_guest_user(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
sender = self.example_user("polonius")
|
2020-07-04 23:20:55 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
stream_name = "public stream"
|
2020-07-04 23:20:55 +02:00
|
|
|
self.make_stream(stream_name, invite_only=False)
|
|
|
|
payload = dict(
|
2024-04-10 20:48:10 +02:00
|
|
|
type="channel",
|
2022-09-13 08:39:44 +02:00
|
|
|
to=orjson.dumps(stream_name).decode(),
|
2021-02-12 08:20:45 +01:00
|
|
|
topic="whatever",
|
|
|
|
content="whatever",
|
2020-07-04 23:20:55 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
# Guest user can't send message to unsubscribed public streams
|
|
|
|
result = self.api_post(sender, "/api/v1/messages", payload)
|
2024-04-16 20:31:49 +02:00
|
|
|
self.assert_json_error(result, "Not authorized to send to channel 'public stream'")
|
2020-07-04 23:20:55 +02:00
|
|
|
|
|
|
|
self.subscribe(sender, stream_name)
|
|
|
|
# Guest user can send message to subscribed public streams
|
|
|
|
result = self.api_post(sender, "/api/v1/messages", payload)
|
|
|
|
self.assert_json_success(result)
|
2020-07-05 21:10:27 +02:00
|
|
|
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
class StreamMessagesTest(ZulipTestCase):
|
|
|
|
def assert_stream_message(
|
|
|
|
self, stream_name: str, topic_name: str = "test topic", content: str = "test content"
|
|
|
|
) -> None:
|
2020-07-06 00:15:22 +02:00
|
|
|
"""
|
|
|
|
Check that messages sent to a stream reach all subscribers to that stream.
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
realm = get_realm("zulip")
|
2020-07-06 00:15:22 +02:00
|
|
|
subscribers = self.users_subscribed_to_stream(stream_name, realm)
|
|
|
|
|
|
|
|
# Outgoing webhook bots don't store UserMessage rows; they will be processed later.
|
2021-02-12 08:19:30 +01:00
|
|
|
subscribers = [
|
|
|
|
subscriber
|
|
|
|
for subscriber in subscribers
|
|
|
|
if subscriber.bot_type != UserProfile.OUTGOING_WEBHOOK_BOT
|
|
|
|
]
|
2020-07-06 00:15:22 +02:00
|
|
|
|
2023-07-31 22:52:35 +02:00
|
|
|
old_subscriber_messages = list(map(message_stream_count, subscribers))
|
2020-07-06 00:15:22 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
non_subscribers = [
|
|
|
|
user_profile
|
|
|
|
for user_profile in UserProfile.objects.all()
|
|
|
|
if user_profile not in subscribers
|
|
|
|
]
|
2023-07-31 22:52:35 +02:00
|
|
|
old_non_subscriber_messages = list(map(message_stream_count, non_subscribers))
|
2020-07-06 00:15:22 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
non_bot_subscribers = [
|
|
|
|
user_profile for user_profile in subscribers if not user_profile.is_bot
|
|
|
|
]
|
2020-07-06 00:15:22 +02:00
|
|
|
a_subscriber = non_bot_subscribers[0]
|
|
|
|
self.login_user(a_subscriber)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.send_stream_message(a_subscriber, stream_name, content=content, topic_name=topic_name)
|
2020-07-06 00:15:22 +02:00
|
|
|
|
|
|
|
# Did all of the subscribers get the message?
|
2023-07-31 22:52:35 +02:00
|
|
|
new_subscriber_messages = list(map(message_stream_count, subscribers))
|
2020-07-06 00:15:22 +02:00
|
|
|
|
|
|
|
# Did non-subscribers not get the message?
|
2023-07-31 22:52:35 +02:00
|
|
|
new_non_subscriber_messages = list(map(message_stream_count, non_subscribers))
|
2020-07-06 00:15:22 +02:00
|
|
|
|
|
|
|
self.assertEqual(old_non_subscriber_messages, new_non_subscriber_messages)
|
|
|
|
self.assertEqual(new_subscriber_messages, [elt + 1 for elt in old_subscriber_messages])
|
|
|
|
|
|
|
|
def test_performance(self) -> None:
|
2021-02-12 08:19:30 +01:00
|
|
|
"""
|
2020-07-06 00:15:22 +02:00
|
|
|
This test is part of the automated test suite, but
|
|
|
|
it is more intended as an aid to measuring the
|
|
|
|
performance of do_send_messages() with consistent
|
|
|
|
data setup across different commits. You can modify
|
|
|
|
the values below and run just this test, and then
|
|
|
|
comment out the print statement toward the bottom.
|
2021-02-12 08:19:30 +01:00
|
|
|
"""
|
2020-07-06 00:15:22 +02:00
|
|
|
num_messages = 2
|
|
|
|
num_extra_users = 10
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
sender = self.example_user("cordelia")
|
2020-07-06 00:15:22 +02:00
|
|
|
realm = sender.realm
|
2021-02-12 08:20:45 +01:00
|
|
|
message_content = "whatever"
|
|
|
|
stream = get_stream("Denmark", realm)
|
|
|
|
topic_name = "lunch"
|
2020-07-06 00:15:22 +02:00
|
|
|
recipient = stream.recipient
|
2022-05-29 21:52:25 +02:00
|
|
|
assert recipient is not None
|
2020-07-06 00:15:22 +02:00
|
|
|
sending_client = make_client(name="test suite")
|
|
|
|
|
|
|
|
for i in range(num_extra_users):
|
|
|
|
# Make every other user be idle.
|
|
|
|
long_term_idle = i % 2 > 0
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
email = f"foo{i}@example.com"
|
2020-07-06 00:15:22 +02:00
|
|
|
user = UserProfile.objects.create(
|
|
|
|
realm=realm,
|
|
|
|
email=email,
|
2020-07-15 19:19:41 +02:00
|
|
|
delivery_email=email,
|
2020-07-06 00:15:22 +02:00
|
|
|
long_term_idle=long_term_idle,
|
|
|
|
)
|
|
|
|
Subscription.objects.create(
|
|
|
|
user_profile=user,
|
2021-02-14 00:03:40 +01:00
|
|
|
is_user_active=user.is_active,
|
2020-07-06 00:15:22 +02:00
|
|
|
recipient=recipient,
|
|
|
|
)
|
|
|
|
|
|
|
|
def send_test_message() -> None:
|
|
|
|
message = Message(
|
|
|
|
sender=sender,
|
|
|
|
recipient=recipient,
|
2022-09-27 21:42:31 +02:00
|
|
|
realm=stream.realm,
|
2020-07-06 00:15:22 +02:00
|
|
|
content=message_content,
|
|
|
|
date_sent=timezone_now(),
|
|
|
|
sending_client=sending_client,
|
|
|
|
)
|
|
|
|
message.set_topic_name(topic_name)
|
2021-05-20 17:38:24 +02:00
|
|
|
message_dict = build_message_send_dict(message=message)
|
2020-09-29 20:34:38 +02:00
|
|
|
do_send_messages([message_dict])
|
2020-07-06 00:15:22 +02:00
|
|
|
|
|
|
|
before_um_count = UserMessage.objects.count()
|
|
|
|
|
|
|
|
for i in range(num_messages):
|
|
|
|
send_test_message()
|
|
|
|
|
|
|
|
after_um_count = UserMessage.objects.count()
|
|
|
|
ums_created = after_um_count - before_um_count
|
|
|
|
|
|
|
|
num_active_users = num_extra_users / 2
|
|
|
|
self.assertTrue(ums_created > (num_active_users * num_messages))
|
|
|
|
|
|
|
|
def test_not_too_many_queries(self) -> None:
|
2021-02-12 08:19:30 +01:00
|
|
|
recipient_list = [
|
|
|
|
self.example_user("hamlet"),
|
|
|
|
self.example_user("iago"),
|
|
|
|
self.example_user("cordelia"),
|
|
|
|
self.example_user("othello"),
|
|
|
|
]
|
2020-07-06 00:15:22 +02:00
|
|
|
for user_profile in recipient_list:
|
|
|
|
self.subscribe(user_profile, "Denmark")
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
sender = self.example_user("hamlet")
|
2023-12-10 14:53:52 +01:00
|
|
|
user = self.example_user("othello")
|
2020-07-06 00:15:22 +02:00
|
|
|
sending_client = make_client(name="test suite")
|
2021-02-12 08:20:45 +01:00
|
|
|
stream_name = "Denmark"
|
|
|
|
topic_name = "foo"
|
|
|
|
content = "whatever"
|
2020-07-06 00:15:22 +02:00
|
|
|
|
2023-06-17 17:37:04 +02:00
|
|
|
# Note: We don't need to assert the db query count for each possible
|
|
|
|
# combination of 'automatically_follow_topics_policy' and 'automatically_unmute_topics_in_muted_streams_policy',
|
|
|
|
# as the query count depends only on the actions, i.e., 'ON_INITIATION',
|
|
|
|
# 'ON_PARTICIPATION', and 'NEVER', and is independent of the final visibility_policy set.
|
|
|
|
# Asserting query count using one of the above-mentioned settings fulfils our purpose.
|
|
|
|
|
2020-07-06 00:15:22 +02:00
|
|
|
# To get accurate count of the queries, we should make sure that
|
|
|
|
# caches don't come into play. If we count queries while caches are
|
|
|
|
# filled, we will get a lower count. Caches are not supposed to be
|
|
|
|
# persistent, so our test can also fail if cache is invalidated
|
|
|
|
# during the course of the unit test.
|
|
|
|
flush_per_request_caches()
|
2023-06-17 17:37:04 +02:00
|
|
|
do_change_user_setting(
|
|
|
|
user_profile=sender,
|
|
|
|
setting_name="automatically_follow_topics_policy",
|
|
|
|
setting_value=UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_NEVER,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
2022-10-15 22:47:40 +02:00
|
|
|
with self.assert_database_query_count(13):
|
2020-07-06 00:15:22 +02:00
|
|
|
check_send_stream_message(
|
|
|
|
sender=sender,
|
|
|
|
client=sending_client,
|
|
|
|
stream_name=stream_name,
|
2024-01-13 09:55:16 +01:00
|
|
|
topic_name=topic_name,
|
2020-07-06 00:15:22 +02:00
|
|
|
body=content,
|
|
|
|
)
|
|
|
|
|
2023-06-17 17:37:04 +02:00
|
|
|
do_change_user_setting(
|
|
|
|
user_profile=sender,
|
|
|
|
setting_name="automatically_follow_topics_policy",
|
|
|
|
setting_value=UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
# There will be an increase in the query count of 5 while sending
|
|
|
|
# the first message to a topic.
|
|
|
|
# 5 queries: 1 to check if it is the first message in the topic +
|
|
|
|
# 1 to check if the topic is already followed + 3 to follow the topic.
|
|
|
|
flush_per_request_caches()
|
|
|
|
with self.assert_database_query_count(18):
|
|
|
|
check_send_stream_message(
|
|
|
|
sender=sender,
|
|
|
|
client=sending_client,
|
|
|
|
stream_name=stream_name,
|
2024-01-13 09:55:16 +01:00
|
|
|
topic_name="new topic",
|
2023-06-17 17:37:04 +02:00
|
|
|
body=content,
|
|
|
|
)
|
|
|
|
|
|
|
|
do_change_user_setting(
|
|
|
|
user_profile=sender,
|
|
|
|
setting_name="automatically_follow_topics_policy",
|
|
|
|
setting_value=UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
self.send_stream_message(self.example_user("iago"), stream_name, "Hello", "topic 2")
|
|
|
|
# There will be an increase in the query count of 4 while sending
|
|
|
|
# a message to a topic with visibility policy other than FOLLOWED.
|
|
|
|
# 1 to check if the topic is already followed + 3 queries to follow the topic.
|
|
|
|
flush_per_request_caches()
|
|
|
|
with self.assert_database_query_count(17):
|
|
|
|
check_send_stream_message(
|
|
|
|
sender=sender,
|
|
|
|
client=sending_client,
|
|
|
|
stream_name=stream_name,
|
2024-01-13 09:55:16 +01:00
|
|
|
topic_name="topic 2",
|
2023-06-17 17:37:04 +02:00
|
|
|
body=content,
|
|
|
|
)
|
|
|
|
# If the topic is already FOLLOWED, there will be an increase in the query
|
|
|
|
# count of 1 to check if the topic is already followed.
|
|
|
|
flush_per_request_caches()
|
|
|
|
with self.assert_database_query_count(14):
|
|
|
|
check_send_stream_message(
|
|
|
|
sender=sender,
|
|
|
|
client=sending_client,
|
|
|
|
stream_name=stream_name,
|
2024-01-13 09:55:16 +01:00
|
|
|
topic_name="topic 2",
|
2023-06-17 17:37:04 +02:00
|
|
|
body=content,
|
|
|
|
)
|
|
|
|
|
2023-12-10 14:53:52 +01:00
|
|
|
realm = get_realm("zulip")
|
|
|
|
subscribers = self.users_subscribed_to_stream(stream_name, realm)
|
|
|
|
|
|
|
|
for user in subscribers:
|
|
|
|
do_change_user_setting(
|
|
|
|
user_profile=user,
|
|
|
|
setting_name="automatically_follow_topics_where_mentioned",
|
|
|
|
setting_value=True,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
# There will be an increase in the query count of 5 while sending
|
|
|
|
# a message with a mention to a topic if visibility policy for the
|
|
|
|
# mentioned user is other than FOLLOWED.
|
|
|
|
# 1 to get the user_id of the mentioned user + 1 to check if the topic
|
|
|
|
# is already followed + 3 queries to follow the topic.
|
|
|
|
flush_per_request_caches()
|
|
|
|
with self.assert_database_query_count(22):
|
|
|
|
check_send_stream_message(
|
|
|
|
sender=sender,
|
|
|
|
client=sending_client,
|
|
|
|
stream_name=stream_name,
|
2024-01-13 09:55:16 +01:00
|
|
|
topic_name="topic 2",
|
2023-12-10 14:53:52 +01:00
|
|
|
body="@**" + user.full_name + "**",
|
|
|
|
)
|
|
|
|
# If the topic is already FOLLOWED, there will be an increase in the query
|
|
|
|
# count of 2.
|
|
|
|
# 1 to get the user_id of the mentioned user + 1 to check if the topic is
|
|
|
|
# already followed.
|
|
|
|
flush_per_request_caches()
|
|
|
|
with self.assert_database_query_count(19):
|
|
|
|
check_send_stream_message(
|
|
|
|
sender=sender,
|
|
|
|
client=sending_client,
|
|
|
|
stream_name=stream_name,
|
2024-01-13 09:55:16 +01:00
|
|
|
topic_name="topic 2",
|
2023-12-10 14:53:52 +01:00
|
|
|
body="@**" + user.full_name + "**",
|
|
|
|
)
|
|
|
|
|
|
|
|
flush_per_request_caches()
|
|
|
|
with self.assert_database_query_count(16):
|
|
|
|
check_send_stream_message(
|
|
|
|
sender=sender,
|
|
|
|
client=sending_client,
|
|
|
|
stream_name=stream_name,
|
2024-01-13 09:55:16 +01:00
|
|
|
topic_name="topic 2",
|
2023-12-10 14:53:52 +01:00
|
|
|
body="@**all**",
|
|
|
|
)
|
|
|
|
|
2020-07-06 00:15:22 +02:00
|
|
|
def test_stream_message_dict(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
user_profile = self.example_user("iago")
|
2020-07-06 00:15:22 +02:00
|
|
|
self.subscribe(user_profile, "Denmark")
|
2021-02-12 08:19:30 +01:00
|
|
|
self.send_stream_message(
|
|
|
|
self.example_user("hamlet"), "Denmark", content="whatever", topic_name="my topic"
|
|
|
|
)
|
2020-07-06 00:15:22 +02:00
|
|
|
message = most_recent_message(user_profile)
|
2023-10-13 03:53:42 +02:00
|
|
|
dct = MessageDict.ids_to_dict([message.id])[0]
|
2023-11-08 04:53:05 +01:00
|
|
|
MessageDict.post_process_dicts(
|
|
|
|
[dct],
|
|
|
|
apply_markdown=True,
|
|
|
|
client_gravatar=False,
|
|
|
|
realm=user_profile.realm,
|
|
|
|
)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(dct["display_recipient"], "Denmark")
|
2020-07-06 00:15:22 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
stream = get_stream("Denmark", user_profile.realm)
|
|
|
|
self.assertEqual(dct["stream_id"], stream.id)
|
2020-07-06 00:15:22 +02:00
|
|
|
|
|
|
|
def test_stream_message_unicode(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
receiving_user_profile = self.example_user("iago")
|
|
|
|
sender = self.example_user("hamlet")
|
2020-07-06 00:15:22 +02:00
|
|
|
self.subscribe(receiving_user_profile, "Denmark")
|
2021-02-12 08:19:30 +01:00
|
|
|
self.send_stream_message(sender, "Denmark", content="whatever", topic_name="my topic")
|
2020-07-06 00:15:22 +02:00
|
|
|
message = most_recent_message(receiving_user_profile)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertEqual(
|
2023-03-08 22:18:59 +01:00
|
|
|
repr(message),
|
2023-08-03 00:28:59 +02:00
|
|
|
f"<Message: Denmark / my topic / <UserProfile: {sender.email} {sender.realm!r}>>",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-07-06 00:15:22 +02:00
|
|
|
|
|
|
|
def test_message_mentions(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
user_profile = self.example_user("iago")
|
2020-07-06 00:15:22 +02:00
|
|
|
self.subscribe(user_profile, "Denmark")
|
2021-02-12 08:19:30 +01:00
|
|
|
self.send_stream_message(
|
|
|
|
self.example_user("hamlet"), "Denmark", content="test @**Iago** rules"
|
|
|
|
)
|
2020-07-06 00:15:22 +02:00
|
|
|
message = most_recent_message(user_profile)
|
2021-02-12 08:19:30 +01:00
|
|
|
assert UserMessage.objects.get(
|
|
|
|
user_profile=user_profile, message=message
|
|
|
|
).flags.mentioned.is_set
|
2020-07-06 00:15:22 +02:00
|
|
|
|
|
|
|
def test_is_private_flag(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
user_profile = self.example_user("iago")
|
2020-07-06 00:15:22 +02:00
|
|
|
self.subscribe(user_profile, "Denmark")
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
self.send_stream_message(self.example_user("hamlet"), "Denmark", content="test")
|
2020-07-06 00:15:22 +02:00
|
|
|
message = most_recent_message(user_profile)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertFalse(
|
|
|
|
UserMessage.objects.get(
|
|
|
|
user_profile=user_profile, message=message
|
|
|
|
).flags.is_private.is_set
|
|
|
|
)
|
2020-07-06 00:15:22 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
self.send_personal_message(self.example_user("hamlet"), user_profile, content="test")
|
2020-07-06 00:15:22 +02:00
|
|
|
message = most_recent_message(user_profile)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertTrue(
|
|
|
|
UserMessage.objects.get(
|
|
|
|
user_profile=user_profile, message=message
|
|
|
|
).flags.is_private.is_set
|
|
|
|
)
|
2020-07-06 00:15:22 +02:00
|
|
|
|
2024-07-12 02:30:17 +02:00
|
|
|
def _send_stream_message(self, user: UserProfile, stream_name: str, content: str) -> set[int]:
|
2023-04-05 13:36:01 +02:00
|
|
|
with self.capture_send_event_calls(expected_num_events=1) as events:
|
2020-07-06 00:15:22 +02:00
|
|
|
self.send_stream_message(
|
|
|
|
user,
|
|
|
|
stream_name,
|
|
|
|
content=content,
|
2024-05-15 19:24:37 +02:00
|
|
|
skip_capture_on_commit_callbacks=True,
|
2020-07-06 00:15:22 +02:00
|
|
|
)
|
2021-05-27 16:25:23 +02:00
|
|
|
users = events[0]["users"]
|
2021-02-12 08:20:45 +01:00
|
|
|
user_ids = {u["id"] for u in users}
|
2020-07-06 00:15:22 +02:00
|
|
|
return user_ids
|
|
|
|
|
|
|
|
def test_unsub_mention(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
hamlet = self.example_user("hamlet")
|
2020-07-06 00:15:22 +02:00
|
|
|
|
2021-05-10 07:02:14 +02:00
|
|
|
stream_name = "Test stream"
|
2020-07-06 00:15:22 +02:00
|
|
|
|
|
|
|
self.subscribe(hamlet, stream_name)
|
|
|
|
|
|
|
|
UserMessage.objects.filter(
|
|
|
|
user_profile=cordelia,
|
|
|
|
).delete()
|
|
|
|
|
2024-07-12 02:30:17 +02:00
|
|
|
def mention_cordelia() -> set[int]:
|
2021-04-11 16:26:54 +02:00
|
|
|
content = "test @**Cordelia, Lear's daughter** rules"
|
2020-07-06 00:15:22 +02:00
|
|
|
|
|
|
|
user_ids = self._send_stream_message(
|
|
|
|
user=hamlet,
|
|
|
|
stream_name=stream_name,
|
|
|
|
content=content,
|
|
|
|
)
|
|
|
|
return user_ids
|
|
|
|
|
|
|
|
def num_cordelia_messages() -> int:
|
|
|
|
return UserMessage.objects.filter(
|
|
|
|
user_profile=cordelia,
|
|
|
|
).count()
|
|
|
|
|
|
|
|
user_ids = mention_cordelia()
|
|
|
|
self.assertEqual(0, num_cordelia_messages())
|
|
|
|
self.assertNotIn(cordelia.id, user_ids)
|
|
|
|
|
|
|
|
# Make sure test isn't too brittle-subscribing
|
|
|
|
# Cordelia and mentioning her should give her a
|
|
|
|
# message.
|
|
|
|
self.subscribe(cordelia, stream_name)
|
|
|
|
user_ids = mention_cordelia()
|
|
|
|
self.assertIn(cordelia.id, user_ids)
|
|
|
|
self.assertEqual(1, num_cordelia_messages())
|
|
|
|
|
|
|
|
def test_message_bot_mentions(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
hamlet = self.example_user("hamlet")
|
2020-07-06 00:15:22 +02:00
|
|
|
realm = hamlet.realm
|
|
|
|
|
2021-05-10 07:02:14 +02:00
|
|
|
stream_name = "Test stream"
|
2020-07-06 00:15:22 +02:00
|
|
|
|
|
|
|
self.subscribe(hamlet, stream_name)
|
|
|
|
|
|
|
|
normal_bot = do_create_user(
|
2021-02-12 08:20:45 +01:00
|
|
|
email="normal-bot@zulip.com",
|
|
|
|
password="",
|
2020-07-06 00:15:22 +02:00
|
|
|
realm=realm,
|
2021-02-12 08:20:45 +01:00
|
|
|
full_name="Normal Bot",
|
2020-07-06 00:15:22 +02:00
|
|
|
bot_type=UserProfile.DEFAULT_BOT,
|
|
|
|
bot_owner=cordelia,
|
2021-02-06 14:27:06 +01:00
|
|
|
acting_user=None,
|
2020-07-06 00:15:22 +02:00
|
|
|
)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
content = "test @**Normal Bot** rules"
|
2020-07-06 00:15:22 +02:00
|
|
|
|
|
|
|
user_ids = self._send_stream_message(
|
|
|
|
user=hamlet,
|
|
|
|
stream_name=stream_name,
|
|
|
|
content=content,
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assertIn(normal_bot.id, user_ids)
|
|
|
|
user_message = most_recent_usermessage(normal_bot)
|
|
|
|
self.assertEqual(user_message.message.content, content)
|
|
|
|
self.assertTrue(user_message.flags.mentioned)
|
|
|
|
|
2023-06-07 19:19:33 +02:00
|
|
|
def send_and_verify_topic_wildcard_mention_message(
|
2023-11-21 10:39:13 +01:00
|
|
|
self, sender_name: str, test_fails: bool = False, topic_participant_count: int = 20
|
2023-06-07 19:19:33 +02:00
|
|
|
) -> None:
|
|
|
|
sender = self.example_user(sender_name)
|
|
|
|
content = "@**topic** test topic wildcard mention"
|
2023-11-21 10:39:13 +01:00
|
|
|
participants_user_ids = set(range(topic_participant_count))
|
|
|
|
with mock.patch(
|
|
|
|
"zerver.actions.message_send.participants_for_topic", return_value=participants_user_ids
|
|
|
|
):
|
2023-06-07 19:19:33 +02:00
|
|
|
if not test_fails:
|
|
|
|
msg_id = self.send_stream_message(sender, "test_stream", content)
|
|
|
|
result = self.api_get(sender, "/api/v1/messages/" + str(msg_id))
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
else:
|
|
|
|
with self.assertRaisesRegex(
|
|
|
|
JsonableError,
|
2023-11-21 10:39:13 +01:00
|
|
|
"You do not have permission to use topic wildcard mentions in this topic.",
|
2023-06-07 19:19:33 +02:00
|
|
|
):
|
|
|
|
self.send_stream_message(sender, "test_stream", content)
|
|
|
|
|
|
|
|
def test_topic_wildcard_mention_restrictions(self) -> None:
|
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
iago = self.example_user("iago")
|
|
|
|
polonius = self.example_user("polonius")
|
|
|
|
shiva = self.example_user("shiva")
|
|
|
|
realm = cordelia.realm
|
|
|
|
|
|
|
|
stream_name = "test_stream"
|
|
|
|
self.subscribe(cordelia, stream_name)
|
|
|
|
self.subscribe(iago, stream_name)
|
|
|
|
self.subscribe(polonius, stream_name)
|
|
|
|
self.subscribe(shiva, stream_name)
|
|
|
|
|
|
|
|
do_set_realm_property(
|
|
|
|
realm,
|
|
|
|
"wildcard_mention_policy",
|
2024-05-22 11:43:10 +02:00
|
|
|
WildcardMentionPolicyEnum.EVERYONE,
|
2023-06-07 19:19:33 +02:00
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
self.send_and_verify_topic_wildcard_mention_message("polonius")
|
|
|
|
|
|
|
|
do_set_realm_property(
|
|
|
|
realm,
|
|
|
|
"wildcard_mention_policy",
|
2024-05-22 11:43:10 +02:00
|
|
|
WildcardMentionPolicyEnum.MEMBERS,
|
2023-06-07 19:19:33 +02:00
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
self.send_and_verify_topic_wildcard_mention_message("polonius", test_fails=True)
|
2023-11-21 10:39:13 +01:00
|
|
|
# There is no restriction on topics with less than 'Realm.WILDCARD_MENTION_THRESHOLD' participants.
|
|
|
|
self.send_and_verify_topic_wildcard_mention_message("polonius", topic_participant_count=10)
|
2023-06-07 19:19:33 +02:00
|
|
|
self.send_and_verify_topic_wildcard_mention_message("cordelia")
|
|
|
|
|
|
|
|
do_set_realm_property(
|
|
|
|
realm,
|
|
|
|
"wildcard_mention_policy",
|
2024-05-22 11:43:10 +02:00
|
|
|
WildcardMentionPolicyEnum.FULL_MEMBERS,
|
2023-06-07 19:19:33 +02:00
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
do_set_realm_property(realm, "waiting_period_threshold", 10, acting_user=None)
|
|
|
|
iago.date_joined = timezone_now()
|
|
|
|
iago.save()
|
|
|
|
shiva.date_joined = timezone_now()
|
|
|
|
shiva.save()
|
|
|
|
cordelia.date_joined = timezone_now()
|
|
|
|
cordelia.save()
|
|
|
|
self.send_and_verify_topic_wildcard_mention_message("cordelia", test_fails=True)
|
2023-11-21 10:39:13 +01:00
|
|
|
self.send_and_verify_topic_wildcard_mention_message("cordelia", topic_participant_count=10)
|
2023-06-07 19:19:33 +02:00
|
|
|
# Administrators and moderators can use wildcard mentions even if they are new.
|
|
|
|
self.send_and_verify_topic_wildcard_mention_message("iago")
|
|
|
|
self.send_and_verify_topic_wildcard_mention_message("shiva")
|
|
|
|
|
2023-11-19 19:45:19 +01:00
|
|
|
cordelia.date_joined = timezone_now() - timedelta(days=11)
|
2023-06-07 19:19:33 +02:00
|
|
|
cordelia.save()
|
|
|
|
self.send_and_verify_topic_wildcard_mention_message("cordelia")
|
|
|
|
|
|
|
|
do_set_realm_property(
|
|
|
|
realm,
|
|
|
|
"wildcard_mention_policy",
|
2024-05-22 11:43:10 +02:00
|
|
|
WildcardMentionPolicyEnum.MODERATORS,
|
2023-06-07 19:19:33 +02:00
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
self.send_and_verify_topic_wildcard_mention_message("cordelia", test_fails=True)
|
2023-11-21 10:39:13 +01:00
|
|
|
self.send_and_verify_topic_wildcard_mention_message("cordelia", topic_participant_count=10)
|
2023-06-07 19:19:33 +02:00
|
|
|
self.send_and_verify_topic_wildcard_mention_message("shiva")
|
|
|
|
|
|
|
|
cordelia.date_joined = timezone_now()
|
|
|
|
cordelia.save()
|
|
|
|
do_set_realm_property(
|
2024-05-22 11:43:10 +02:00
|
|
|
realm,
|
|
|
|
"wildcard_mention_policy",
|
|
|
|
WildcardMentionPolicyEnum.ADMINS,
|
|
|
|
acting_user=None,
|
2023-06-07 19:19:33 +02:00
|
|
|
)
|
|
|
|
self.send_and_verify_topic_wildcard_mention_message("shiva", test_fails=True)
|
2023-11-21 10:39:13 +01:00
|
|
|
# There is no restriction on topics with less than 'Realm.WILDCARD_MENTION_THRESHOLD' participants.
|
|
|
|
self.send_and_verify_topic_wildcard_mention_message("shiva", topic_participant_count=10)
|
2023-06-07 19:19:33 +02:00
|
|
|
self.send_and_verify_topic_wildcard_mention_message("iago")
|
|
|
|
|
|
|
|
do_set_realm_property(
|
2024-05-22 11:43:10 +02:00
|
|
|
realm,
|
|
|
|
"wildcard_mention_policy",
|
|
|
|
WildcardMentionPolicyEnum.NOBODY,
|
|
|
|
acting_user=None,
|
2023-06-07 19:19:33 +02:00
|
|
|
)
|
|
|
|
self.send_and_verify_topic_wildcard_mention_message("iago", test_fails=True)
|
2023-11-21 10:39:13 +01:00
|
|
|
self.send_and_verify_topic_wildcard_mention_message("iago", topic_participant_count=10)
|
2023-06-07 19:19:33 +02:00
|
|
|
|
2023-06-03 16:51:38 +02:00
|
|
|
def send_and_verify_stream_wildcard_mention_message(
|
2021-02-12 08:19:30 +01:00
|
|
|
self, sender_name: str, test_fails: bool = False, sub_count: int = 16
|
|
|
|
) -> None:
|
2020-09-11 16:11:06 +02:00
|
|
|
sender = self.example_user(sender_name)
|
2023-06-03 16:51:38 +02:00
|
|
|
content = "@**all** test stream wildcard mention"
|
2021-02-12 08:19:30 +01:00
|
|
|
with mock.patch("zerver.lib.message.num_subscribers_for_stream_id", return_value=sub_count):
|
2020-09-11 16:11:06 +02:00
|
|
|
if not test_fails:
|
|
|
|
msg_id = self.send_stream_message(sender, "test_stream", content)
|
2022-11-04 20:26:18 +01:00
|
|
|
result = self.api_get(sender, "/api/v1/messages/" + str(msg_id))
|
2020-09-11 16:11:06 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
else:
|
2021-02-12 08:19:30 +01:00
|
|
|
with self.assertRaisesRegex(
|
|
|
|
JsonableError,
|
2024-04-16 21:27:36 +02:00
|
|
|
"You do not have permission to use channel wildcard mentions in this channel.",
|
2021-02-12 08:19:30 +01:00
|
|
|
):
|
2020-09-11 16:11:06 +02:00
|
|
|
self.send_stream_message(sender, "test_stream", content)
|
|
|
|
|
2023-06-03 16:51:38 +02:00
|
|
|
def test_stream_wildcard_mention_restrictions(self) -> None:
|
2020-09-11 16:11:06 +02:00
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
iago = self.example_user("iago")
|
|
|
|
polonius = self.example_user("polonius")
|
2020-12-28 17:14:26 +01:00
|
|
|
shiva = self.example_user("shiva")
|
2020-09-11 16:11:06 +02:00
|
|
|
realm = cordelia.realm
|
|
|
|
|
|
|
|
stream_name = "test_stream"
|
|
|
|
self.subscribe(cordelia, stream_name)
|
|
|
|
self.subscribe(iago, stream_name)
|
|
|
|
self.subscribe(polonius, stream_name)
|
2020-12-28 17:14:26 +01:00
|
|
|
self.subscribe(shiva, stream_name)
|
2020-09-11 16:11:06 +02:00
|
|
|
|
|
|
|
do_set_realm_property(
|
2021-03-01 11:33:24 +01:00
|
|
|
realm,
|
|
|
|
"wildcard_mention_policy",
|
2024-05-22 11:43:10 +02:00
|
|
|
WildcardMentionPolicyEnum.EVERYONE,
|
2021-03-01 11:33:24 +01:00
|
|
|
acting_user=None,
|
2020-09-11 16:11:06 +02:00
|
|
|
)
|
2023-06-03 16:51:38 +02:00
|
|
|
self.send_and_verify_stream_wildcard_mention_message("polonius")
|
2020-09-11 16:11:06 +02:00
|
|
|
|
|
|
|
do_set_realm_property(
|
2021-03-01 11:33:24 +01:00
|
|
|
realm,
|
|
|
|
"wildcard_mention_policy",
|
2024-05-22 11:43:10 +02:00
|
|
|
WildcardMentionPolicyEnum.MEMBERS,
|
2021-03-01 11:33:24 +01:00
|
|
|
acting_user=None,
|
2020-09-11 16:11:06 +02:00
|
|
|
)
|
2023-06-03 16:51:38 +02:00
|
|
|
self.send_and_verify_stream_wildcard_mention_message("polonius", test_fails=True)
|
2020-09-11 16:11:06 +02:00
|
|
|
# There is no restriction on small streams.
|
2023-06-03 16:51:38 +02:00
|
|
|
self.send_and_verify_stream_wildcard_mention_message("polonius", sub_count=10)
|
|
|
|
self.send_and_verify_stream_wildcard_mention_message("cordelia")
|
2020-09-11 16:11:06 +02:00
|
|
|
|
|
|
|
do_set_realm_property(
|
2021-03-01 11:33:24 +01:00
|
|
|
realm,
|
|
|
|
"wildcard_mention_policy",
|
2024-05-22 11:43:10 +02:00
|
|
|
WildcardMentionPolicyEnum.FULL_MEMBERS,
|
2021-03-01 11:33:24 +01:00
|
|
|
acting_user=None,
|
2020-09-11 16:11:06 +02:00
|
|
|
)
|
2021-03-01 11:33:24 +01:00
|
|
|
do_set_realm_property(realm, "waiting_period_threshold", 10, acting_user=None)
|
2020-09-11 16:11:06 +02:00
|
|
|
iago.date_joined = timezone_now()
|
|
|
|
iago.save()
|
2020-12-28 17:14:26 +01:00
|
|
|
shiva.date_joined = timezone_now()
|
|
|
|
shiva.save()
|
2020-09-11 16:11:06 +02:00
|
|
|
cordelia.date_joined = timezone_now()
|
|
|
|
cordelia.save()
|
2023-06-03 16:51:38 +02:00
|
|
|
self.send_and_verify_stream_wildcard_mention_message("cordelia", test_fails=True)
|
|
|
|
self.send_and_verify_stream_wildcard_mention_message("cordelia", sub_count=10)
|
2020-12-28 17:14:26 +01:00
|
|
|
# Administrators and moderators can use wildcard mentions even if they are new.
|
2023-06-03 16:51:38 +02:00
|
|
|
self.send_and_verify_stream_wildcard_mention_message("iago")
|
|
|
|
self.send_and_verify_stream_wildcard_mention_message("shiva")
|
2020-09-11 16:11:06 +02:00
|
|
|
|
2023-11-19 19:45:19 +01:00
|
|
|
cordelia.date_joined = timezone_now() - timedelta(days=11)
|
2020-09-11 16:11:06 +02:00
|
|
|
cordelia.save()
|
2023-06-03 16:51:38 +02:00
|
|
|
self.send_and_verify_stream_wildcard_mention_message("cordelia")
|
2020-09-11 16:11:06 +02:00
|
|
|
|
|
|
|
do_set_realm_property(
|
2021-05-02 09:56:58 +02:00
|
|
|
realm,
|
|
|
|
"wildcard_mention_policy",
|
2024-05-22 11:43:10 +02:00
|
|
|
WildcardMentionPolicyEnum.MODERATORS,
|
2021-05-02 09:56:58 +02:00
|
|
|
acting_user=None,
|
|
|
|
)
|
2023-06-03 16:51:38 +02:00
|
|
|
self.send_and_verify_stream_wildcard_mention_message("cordelia", test_fails=True)
|
|
|
|
self.send_and_verify_stream_wildcard_mention_message("cordelia", sub_count=10)
|
|
|
|
self.send_and_verify_stream_wildcard_mention_message("shiva")
|
2021-05-02 09:56:58 +02:00
|
|
|
|
2020-09-11 16:11:06 +02:00
|
|
|
cordelia.date_joined = timezone_now()
|
|
|
|
cordelia.save()
|
|
|
|
do_set_realm_property(
|
2024-05-22 11:43:10 +02:00
|
|
|
realm,
|
|
|
|
"wildcard_mention_policy",
|
|
|
|
WildcardMentionPolicyEnum.ADMINS,
|
|
|
|
acting_user=None,
|
2020-09-11 16:11:06 +02:00
|
|
|
)
|
2023-06-03 16:51:38 +02:00
|
|
|
self.send_and_verify_stream_wildcard_mention_message("shiva", test_fails=True)
|
2020-09-11 16:11:06 +02:00
|
|
|
# There is no restriction on small streams.
|
2023-06-03 16:51:38 +02:00
|
|
|
self.send_and_verify_stream_wildcard_mention_message("shiva", sub_count=10)
|
|
|
|
self.send_and_verify_stream_wildcard_mention_message("iago")
|
2020-09-11 16:11:06 +02:00
|
|
|
|
|
|
|
do_set_realm_property(
|
2024-05-22 11:43:10 +02:00
|
|
|
realm,
|
|
|
|
"wildcard_mention_policy",
|
|
|
|
WildcardMentionPolicyEnum.NOBODY,
|
|
|
|
acting_user=None,
|
2020-09-11 16:11:06 +02:00
|
|
|
)
|
2023-06-03 16:51:38 +02:00
|
|
|
self.send_and_verify_stream_wildcard_mention_message("iago", test_fails=True)
|
|
|
|
self.send_and_verify_stream_wildcard_mention_message("iago", sub_count=10)
|
2020-09-11 16:11:06 +02:00
|
|
|
|
2023-10-19 18:11:53 +02:00
|
|
|
def test_topic_wildcard_mentioned_flag(self) -> None:
|
|
|
|
# For topic wildcard mentions, the 'topic_wildcard_mentioned' flag should be
|
2023-08-16 05:45:05 +02:00
|
|
|
# set for all the user messages for topic participants, irrespective of
|
|
|
|
# their notifications settings.
|
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
iago = self.example_user("iago")
|
|
|
|
|
|
|
|
for user_profile in [cordelia, hamlet, iago]:
|
|
|
|
self.subscribe(user_profile, "Denmark")
|
|
|
|
|
|
|
|
# user | topic participant | wildcard_mentions_notify setting
|
|
|
|
# -------- | ----------------- | ----------------------------------
|
|
|
|
# cordelia | YES | True
|
|
|
|
# hamlet | YES | False
|
|
|
|
# iago | NO | True
|
|
|
|
self.send_stream_message(cordelia, "Denmark", content="test", topic_name="topic-1")
|
|
|
|
do_change_user_setting(cordelia, "wildcard_mentions_notify", True, acting_user=None)
|
|
|
|
self.send_stream_message(hamlet, "Denmark", content="Hi @**topic**", topic_name="topic-1")
|
|
|
|
message = most_recent_message(cordelia)
|
|
|
|
self.assertTrue(
|
|
|
|
UserMessage.objects.get(
|
|
|
|
user_profile=cordelia, message=message
|
2023-10-19 18:11:53 +02:00
|
|
|
).flags.topic_wildcard_mentioned.is_set
|
2023-08-16 05:45:05 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
self.send_stream_message(hamlet, "Denmark", content="test", topic_name="topic-2")
|
|
|
|
do_change_user_setting(hamlet, "wildcard_mentions_notify", False, acting_user=None)
|
|
|
|
self.send_stream_message(cordelia, "Denmark", content="Hi @**topic**", topic_name="topic-2")
|
|
|
|
message = most_recent_message(hamlet)
|
|
|
|
self.assertTrue(
|
|
|
|
UserMessage.objects.get(
|
|
|
|
user_profile=hamlet, message=message
|
2023-10-19 18:11:53 +02:00
|
|
|
).flags.topic_wildcard_mentioned.is_set
|
2023-08-16 05:45:05 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
do_change_user_setting(iago, "wildcard_mentions_notify", True, acting_user=None)
|
|
|
|
self.send_stream_message(hamlet, "Denmark", content="Hi @**topic**", topic_name="topic-3")
|
|
|
|
message = most_recent_message(iago)
|
|
|
|
self.assertFalse(
|
|
|
|
UserMessage.objects.get(
|
|
|
|
user_profile=iago, message=message
|
2023-10-19 18:11:53 +02:00
|
|
|
).flags.topic_wildcard_mentioned.is_set
|
2023-08-16 05:45:05 +02:00
|
|
|
)
|
|
|
|
|
2020-09-11 16:11:06 +02:00
|
|
|
def test_invalid_wildcard_mention_policy(self) -> None:
|
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
self.login_user(cordelia)
|
|
|
|
|
|
|
|
self.subscribe(cordelia, "test_stream")
|
2021-03-01 11:33:24 +01:00
|
|
|
do_set_realm_property(cordelia.realm, "wildcard_mention_policy", 10, acting_user=None)
|
2020-09-11 16:11:06 +02:00
|
|
|
content = "@**all** test wildcard mention"
|
2021-02-12 08:19:30 +01:00
|
|
|
with mock.patch("zerver.lib.message.num_subscribers_for_stream_id", return_value=16):
|
2020-09-11 16:11:06 +02:00
|
|
|
with self.assertRaisesRegex(AssertionError, "Invalid wildcard mention policy"):
|
|
|
|
self.send_stream_message(cordelia, "test_stream", content)
|
|
|
|
|
2023-06-13 05:47:02 +02:00
|
|
|
def test_user_group_mention_restrictions(self) -> None:
|
|
|
|
iago = self.example_user("iago")
|
|
|
|
shiva = self.example_user("shiva")
|
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
othello = self.example_user("othello")
|
|
|
|
self.subscribe(iago, "test_stream")
|
|
|
|
self.subscribe(shiva, "test_stream")
|
|
|
|
self.subscribe(othello, "test_stream")
|
|
|
|
self.subscribe(cordelia, "test_stream")
|
|
|
|
|
|
|
|
leadership = check_add_user_group(othello.realm, "leadership", [othello], acting_user=None)
|
|
|
|
support = check_add_user_group(othello.realm, "support", [othello], acting_user=None)
|
|
|
|
|
2024-04-18 12:23:46 +02:00
|
|
|
moderators_system_group = NamedUserGroup.objects.get(
|
2023-09-21 13:06:39 +02:00
|
|
|
realm=iago.realm, name=SystemGroups.MODERATORS, is_system_group=True
|
2023-06-13 05:47:02 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
content = "Test mentioning user group @*leadership*"
|
|
|
|
msg_id = self.send_stream_message(cordelia, "test_stream", content)
|
|
|
|
result = self.api_get(cordelia, "/api/v1/messages/" + str(msg_id))
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
leadership.can_mention_group = moderators_system_group
|
|
|
|
leadership.save()
|
|
|
|
with self.assertRaisesRegex(
|
|
|
|
JsonableError,
|
2024-05-18 09:48:42 +02:00
|
|
|
f"You are not allowed to mention user group '{leadership.name}'.",
|
2023-06-13 05:47:02 +02:00
|
|
|
):
|
|
|
|
self.send_stream_message(cordelia, "test_stream", content)
|
|
|
|
|
|
|
|
# The restriction does not apply on silent mention.
|
|
|
|
content = "Test mentioning user group @_*leadership*"
|
|
|
|
msg_id = self.send_stream_message(cordelia, "test_stream", content)
|
|
|
|
result = self.api_get(cordelia, "/api/v1/messages/" + str(msg_id))
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
content = "Test mentioning user group @*leadership*"
|
|
|
|
msg_id = self.send_stream_message(shiva, "test_stream", content)
|
|
|
|
result = self.api_get(shiva, "/api/v1/messages/" + str(msg_id))
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
msg_id = self.send_stream_message(iago, "test_stream", content)
|
|
|
|
result = self.api_get(iago, "/api/v1/messages/" + str(msg_id))
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
test = check_add_user_group(shiva.realm, "test", [shiva], acting_user=None)
|
|
|
|
add_subgroups_to_user_group(leadership, [test], acting_user=None)
|
|
|
|
support.can_mention_group = leadership
|
|
|
|
support.save()
|
|
|
|
|
|
|
|
content = "Test mentioning user group @*support*"
|
|
|
|
with self.assertRaisesRegex(
|
|
|
|
JsonableError,
|
2024-05-18 09:48:42 +02:00
|
|
|
f"You are not allowed to mention user group '{support.name}'.",
|
2023-06-13 05:47:02 +02:00
|
|
|
):
|
|
|
|
self.send_stream_message(iago, "test_stream", content)
|
|
|
|
|
|
|
|
msg_id = self.send_stream_message(othello, "test_stream", content)
|
|
|
|
result = self.api_get(othello, "/api/v1/messages/" + str(msg_id))
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
msg_id = self.send_stream_message(shiva, "test_stream", content)
|
|
|
|
result = self.api_get(shiva, "/api/v1/messages/" + str(msg_id))
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
content = "Test mentioning user group @*support* @*leadership*"
|
|
|
|
with self.assertRaisesRegex(
|
|
|
|
JsonableError,
|
2024-05-18 09:48:42 +02:00
|
|
|
f"You are not allowed to mention user group '{support.name}'.",
|
2023-06-13 05:47:02 +02:00
|
|
|
):
|
|
|
|
self.send_stream_message(iago, "test_stream", content)
|
|
|
|
|
|
|
|
with self.assertRaisesRegex(
|
|
|
|
JsonableError,
|
2024-05-18 09:48:42 +02:00
|
|
|
f"You are not allowed to mention user group '{leadership.name}'.",
|
2023-06-13 05:47:02 +02:00
|
|
|
):
|
|
|
|
self.send_stream_message(othello, "test_stream", content)
|
|
|
|
|
|
|
|
msg_id = self.send_stream_message(shiva, "test_stream", content)
|
|
|
|
result = self.api_get(shiva, "/api/v1/messages/" + str(msg_id))
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2024-01-10 07:58:52 +01:00
|
|
|
# Test system bots.
|
|
|
|
content = "Test mentioning user group @*support*"
|
2024-04-18 12:23:46 +02:00
|
|
|
members_group = NamedUserGroup.objects.get(
|
2024-01-10 07:58:52 +01:00
|
|
|
name=SystemGroups.MEMBERS, realm=iago.realm, is_system_group=True
|
|
|
|
)
|
|
|
|
support.can_mention_group = members_group
|
|
|
|
support.save()
|
|
|
|
|
|
|
|
internal_realm = get_realm(settings.SYSTEM_BOT_REALM)
|
|
|
|
system_bot = get_system_bot(settings.EMAIL_GATEWAY_BOT, internal_realm.id)
|
|
|
|
with self.assertRaisesRegex(
|
|
|
|
JsonableError,
|
2024-05-18 09:48:42 +02:00
|
|
|
f"You are not allowed to mention user group '{support.name}'.",
|
2024-01-10 07:58:52 +01:00
|
|
|
):
|
|
|
|
self.send_stream_message(system_bot, "test_stream", content, recipient_realm=iago.realm)
|
|
|
|
|
2024-04-18 12:23:46 +02:00
|
|
|
everyone_group = NamedUserGroup.objects.get(
|
2024-01-10 07:58:52 +01:00
|
|
|
name=SystemGroups.EVERYONE, realm=iago.realm, is_system_group=True
|
|
|
|
)
|
|
|
|
support.can_mention_group = everyone_group
|
|
|
|
support.save()
|
|
|
|
|
|
|
|
msg_id = self.send_stream_message(
|
|
|
|
system_bot, "test_stream", content, recipient_realm=iago.realm
|
|
|
|
)
|
|
|
|
result = self.api_get(shiva, "/api/v1/messages/" + str(msg_id))
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2024-05-18 09:48:42 +02:00
|
|
|
# Test all the cases when can_mention_group is not a named user group.
|
|
|
|
content = "Test mentioning user group @*leadership*"
|
2024-05-28 09:25:40 +02:00
|
|
|
user_group = self.create_or_update_anonymous_group_for_setting(
|
|
|
|
[othello], [moderators_system_group]
|
|
|
|
)
|
2024-05-18 09:48:42 +02:00
|
|
|
leadership.can_mention_group = user_group
|
|
|
|
leadership.save()
|
|
|
|
|
|
|
|
msg_id = self.send_stream_message(othello, "test_stream", content)
|
|
|
|
result = self.api_get(cordelia, "/api/v1/messages/" + str(msg_id))
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
msg_id = self.send_stream_message(shiva, "test_stream", content)
|
|
|
|
result = self.api_get(cordelia, "/api/v1/messages/" + str(msg_id))
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
msg_id = self.send_stream_message(iago, "test_stream", content)
|
|
|
|
result = self.api_get(cordelia, "/api/v1/messages/" + str(msg_id))
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
with self.assertRaisesRegex(
|
|
|
|
JsonableError,
|
|
|
|
f"You are not allowed to mention user group '{leadership.name}'.",
|
|
|
|
):
|
|
|
|
self.send_stream_message(cordelia, "test_stream", content)
|
|
|
|
|
|
|
|
with self.assertRaisesRegex(
|
|
|
|
JsonableError,
|
|
|
|
f"You are not allowed to mention user group '{leadership.name}'.",
|
|
|
|
):
|
|
|
|
self.send_stream_message(system_bot, "test_stream", content, recipient_realm=iago.realm)
|
|
|
|
|
|
|
|
content = "Test mentioning user group @_*leadership*"
|
|
|
|
msg_id = self.send_stream_message(shiva, "test_stream", content)
|
|
|
|
result = self.api_get(cordelia, "/api/v1/messages/" + str(msg_id))
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2020-07-06 00:15:22 +02:00
|
|
|
def test_stream_message_mirroring(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
user = self.mit_user("starnine")
|
|
|
|
self.subscribe(user, "Verona")
|
2020-07-06 00:15:22 +02:00
|
|
|
|
2020-12-20 14:21:42 +01:00
|
|
|
do_change_can_forge_sender(user, True)
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.api_post(
|
|
|
|
user,
|
|
|
|
"/api/v1/messages",
|
|
|
|
{
|
2024-04-10 20:48:10 +02:00
|
|
|
"type": "channel",
|
2022-09-13 08:39:44 +02:00
|
|
|
"to": orjson.dumps("Verona").decode(),
|
2021-02-12 08:19:30 +01:00
|
|
|
"sender": self.mit_email("sipbtest"),
|
|
|
|
"client": "zephyr_mirror",
|
|
|
|
"topic": "announcement",
|
|
|
|
"content": "Everyone knows Iago rules",
|
|
|
|
"forged": "true",
|
|
|
|
},
|
|
|
|
subdomain="zephyr",
|
|
|
|
)
|
2020-07-06 00:15:22 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2020-12-20 14:21:42 +01:00
|
|
|
do_change_can_forge_sender(user, False)
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.api_post(
|
|
|
|
user,
|
|
|
|
"/api/v1/messages",
|
|
|
|
{
|
2024-04-10 20:48:10 +02:00
|
|
|
"type": "channel",
|
2021-02-12 08:19:30 +01:00
|
|
|
"to": "Verona",
|
|
|
|
"sender": self.mit_email("sipbtest"),
|
|
|
|
"client": "zephyr_mirror",
|
|
|
|
"topic": "announcement",
|
|
|
|
"content": "Everyone knows Iago rules",
|
|
|
|
"forged": "true",
|
|
|
|
},
|
|
|
|
subdomain="zephyr",
|
|
|
|
)
|
2020-07-06 00:15:22 +02:00
|
|
|
self.assert_json_error(result, "User not authorized for this query")
|
|
|
|
|
|
|
|
def test_message_to_stream(self) -> None:
|
|
|
|
"""
|
|
|
|
If you send a message to a stream, everyone subscribed to the stream
|
|
|
|
receives the messages.
|
|
|
|
"""
|
|
|
|
self.assert_stream_message("Scotland")
|
|
|
|
|
|
|
|
def test_non_ascii_stream_message(self) -> None:
|
|
|
|
"""
|
|
|
|
Sending a stream message containing non-ASCII characters in the stream
|
|
|
|
name, topic, or message body succeeds.
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2020-07-06 00:15:22 +02:00
|
|
|
|
|
|
|
# Subscribe everyone to a stream with non-ASCII characters.
|
|
|
|
non_ascii_stream_name = "hümbüǵ"
|
|
|
|
realm = get_realm("zulip")
|
|
|
|
stream = self.make_stream(non_ascii_stream_name)
|
2021-02-12 08:19:30 +01:00
|
|
|
for user_profile in UserProfile.objects.filter(is_active=True, is_bot=False, realm=realm)[
|
|
|
|
0:3
|
|
|
|
]:
|
2020-07-06 00:15:22 +02:00
|
|
|
self.subscribe(user_profile, stream.name)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assert_stream_message(non_ascii_stream_name, topic_name="hümbüǵ", content="hümbüǵ")
|
2020-07-06 00:15:22 +02:00
|
|
|
|
2024-07-04 14:05:48 +02:00
|
|
|
def test_get_raw_unread_data_for_group_direct_messages(self) -> None:
|
2020-07-06 00:15:22 +02:00
|
|
|
users = [
|
2021-02-12 08:20:45 +01:00
|
|
|
self.example_user("hamlet"),
|
|
|
|
self.example_user("cordelia"),
|
|
|
|
self.example_user("iago"),
|
|
|
|
self.example_user("prospero"),
|
|
|
|
self.example_user("othello"),
|
2020-07-06 00:15:22 +02:00
|
|
|
]
|
|
|
|
|
2024-07-04 14:05:48 +02:00
|
|
|
message1_id = self.send_group_direct_message(users[0], users, "test content 1")
|
|
|
|
message2_id = self.send_group_direct_message(users[0], users, "test content 2")
|
2020-07-06 00:15:22 +02:00
|
|
|
|
|
|
|
msg_data = get_raw_unread_data(users[1])
|
|
|
|
|
|
|
|
# both the messages are present in msg_data
|
|
|
|
self.assertIn(message1_id, msg_data["huddle_dict"].keys())
|
|
|
|
self.assertIn(message2_id, msg_data["huddle_dict"].keys())
|
|
|
|
|
|
|
|
# only these two messages are present in msg_data
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(msg_data["huddle_dict"].keys(), 2)
|
2020-07-06 00:15:22 +02:00
|
|
|
|
|
|
|
recent_conversations = get_recent_private_conversations(users[1])
|
2023-07-22 00:34:11 +02:00
|
|
|
[recent_conversation] = recent_conversations.values()
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertEqual(
|
2021-02-12 08:20:45 +01:00
|
|
|
set(recent_conversation["user_ids"]), {user.id for user in users if user != users[1]}
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(recent_conversation["max_message_id"], message2_id)
|
2020-07-06 01:17:57 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2020-07-06 01:17:57 +02:00
|
|
|
class PersonalMessageSendTest(ZulipTestCase):
|
|
|
|
def test_personal_to_self(self) -> None:
|
|
|
|
"""
|
|
|
|
If you send a personal to yourself, only you see it.
|
|
|
|
"""
|
|
|
|
old_user_profiles = list(UserProfile.objects.all())
|
2021-02-12 08:20:45 +01:00
|
|
|
test_email = self.nonreg_email("test1")
|
2020-07-06 01:17:57 +02:00
|
|
|
self.register(test_email, "test1")
|
|
|
|
|
2023-07-31 22:52:35 +02:00
|
|
|
old_messages = list(map(message_stream_count, old_user_profiles))
|
2020-07-06 01:17:57 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
user_profile = self.nonreg_user("test1")
|
2020-07-06 01:17:57 +02:00
|
|
|
self.send_personal_message(user_profile, user_profile)
|
|
|
|
|
2023-07-31 22:52:35 +02:00
|
|
|
new_messages = list(map(message_stream_count, old_user_profiles))
|
2020-07-06 01:17:57 +02:00
|
|
|
self.assertEqual(old_messages, new_messages)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
user_profile = self.nonreg_user("test1")
|
2020-07-06 01:17:57 +02:00
|
|
|
recipient = Recipient.objects.get(type_id=user_profile.id, type=Recipient.PERSONAL)
|
|
|
|
self.assertEqual(most_recent_message(user_profile).recipient, recipient)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
def assert_personal(
|
|
|
|
self, sender: UserProfile, receiver: UserProfile, content: str = "testcontent"
|
|
|
|
) -> None:
|
2020-07-06 01:17:57 +02:00
|
|
|
"""
|
2023-06-19 16:26:12 +02:00
|
|
|
Send a direct message from `sender_email` to `receiver_email` and check
|
2020-07-06 01:17:57 +02:00
|
|
|
that only those two parties actually received the message.
|
|
|
|
"""
|
|
|
|
sender_messages = message_stream_count(sender)
|
|
|
|
receiver_messages = message_stream_count(receiver)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
other_user_profiles = UserProfile.objects.filter(~Q(id=sender.id) & ~Q(id=receiver.id))
|
2023-07-31 22:52:35 +02:00
|
|
|
old_other_messages = list(map(message_stream_count, other_user_profiles))
|
2020-07-06 01:17:57 +02:00
|
|
|
|
|
|
|
self.send_personal_message(sender, receiver, content)
|
|
|
|
|
|
|
|
# Users outside the conversation don't get the message.
|
2023-07-31 22:52:35 +02:00
|
|
|
new_other_messages = list(map(message_stream_count, other_user_profiles))
|
2020-07-06 01:17:57 +02:00
|
|
|
self.assertEqual(old_other_messages, new_other_messages)
|
|
|
|
|
|
|
|
# The personal message is in the streams of both the sender and receiver.
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertEqual(message_stream_count(sender), sender_messages + 1)
|
|
|
|
self.assertEqual(message_stream_count(receiver), receiver_messages + 1)
|
2020-07-06 01:17:57 +02:00
|
|
|
|
|
|
|
recipient = Recipient.objects.get(type_id=receiver.id, type=Recipient.PERSONAL)
|
|
|
|
self.assertEqual(most_recent_message(sender).recipient, recipient)
|
|
|
|
self.assertEqual(most_recent_message(receiver).recipient, recipient)
|
|
|
|
|
|
|
|
def test_personal(self) -> None:
|
|
|
|
"""
|
|
|
|
If you send a personal, only you and the recipient see it.
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2020-07-06 01:17:57 +02:00
|
|
|
self.assert_personal(
|
|
|
|
sender=self.example_user("hamlet"),
|
|
|
|
receiver=self.example_user("othello"),
|
|
|
|
)
|
|
|
|
|
2024-07-08 19:00:08 +02:00
|
|
|
def test_direct_message_initiator_group_setting(self) -> None:
|
2020-07-06 01:17:57 +02:00
|
|
|
"""
|
2024-07-08 19:00:08 +02:00
|
|
|
Tests that direct_message_initiator_group_setting works correctly.
|
2020-07-06 01:17:57 +02:00
|
|
|
"""
|
|
|
|
user_profile = self.example_user("hamlet")
|
2024-07-08 19:00:08 +02:00
|
|
|
polonius = self.example_user("polonius")
|
|
|
|
admin = self.example_user("iago")
|
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
realm = user_profile.realm
|
|
|
|
direct_message_group_1 = [user_profile, admin, polonius]
|
|
|
|
direct_message_group_2 = [user_profile, admin, polonius, cordelia]
|
|
|
|
administrators_system_group = NamedUserGroup.objects.get(
|
|
|
|
name=SystemGroups.ADMINISTRATORS, realm=realm, is_system_group=True
|
|
|
|
)
|
2020-07-06 01:17:57 +02:00
|
|
|
self.login_user(user_profile)
|
2024-07-08 19:00:08 +02:00
|
|
|
self.send_personal_message(user_profile, polonius)
|
|
|
|
do_change_realm_permission_group_setting(
|
|
|
|
realm,
|
|
|
|
"direct_message_initiator_group",
|
|
|
|
administrators_system_group,
|
2021-03-01 11:33:24 +01:00
|
|
|
acting_user=None,
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2024-07-08 19:00:08 +02:00
|
|
|
|
|
|
|
# We can send to Polonius because we'd previously messaged him.
|
|
|
|
self.send_personal_message(user_profile, polonius)
|
|
|
|
# Tests if we can send messages to self irrespective of the value of the setting.
|
|
|
|
self.send_personal_message(user_profile, user_profile)
|
|
|
|
|
|
|
|
# We cannot send to users with whom we does not have any direct message conversation.
|
|
|
|
with self.assertRaises(DirectMessageInitiationError) as direct_message_initiation_error:
|
|
|
|
self.send_personal_message(user_profile, cordelia)
|
|
|
|
self.assertEqual(
|
|
|
|
str(direct_message_initiation_error.exception),
|
|
|
|
"You do not have permission to initiate direct message conversations.",
|
|
|
|
)
|
|
|
|
with self.assertRaises(DirectMessageInitiationError):
|
|
|
|
self.send_personal_message(user_profile, admin)
|
|
|
|
|
|
|
|
# Have the administrator send a message, and verify that allows the user to reply.
|
|
|
|
self.send_personal_message(admin, user_profile)
|
|
|
|
with self.assert_database_query_count(16):
|
|
|
|
self.send_personal_message(user_profile, admin)
|
|
|
|
|
|
|
|
# Tests that user cannot initiate direct message thread in groups.
|
|
|
|
with self.assertRaises(DirectMessageInitiationError):
|
|
|
|
self.send_group_direct_message(user_profile, direct_message_group_1)
|
|
|
|
|
|
|
|
# Have the administrator send a message to the direct message group, and verify
|
|
|
|
# that allows the user to reply.
|
|
|
|
self.send_group_direct_message(admin, direct_message_group_1)
|
|
|
|
with self.assert_database_query_count(20):
|
|
|
|
self.send_group_direct_message(user_profile, direct_message_group_1)
|
|
|
|
|
|
|
|
# We cannot sent to `direct_message_group_2` as no message has been sent to this group yet.
|
|
|
|
with self.assertRaises(DirectMessageInitiationError):
|
|
|
|
self.send_group_direct_message(user_profile, direct_message_group_2)
|
2020-07-06 01:17:57 +02:00
|
|
|
|
|
|
|
bot_profile = self.create_test_bot("testbot", user_profile)
|
2021-03-08 11:54:39 +01:00
|
|
|
notification_bot = get_system_bot("notification-bot@zulip.com", user_profile.realm_id)
|
2024-07-08 19:00:08 +02:00
|
|
|
# Tests if messages to and from bots are allowed irrespective of the value of the setting.
|
2021-03-08 11:54:39 +01:00
|
|
|
self.send_personal_message(user_profile, notification_bot)
|
2020-07-06 01:17:57 +02:00
|
|
|
self.send_personal_message(user_profile, bot_profile)
|
|
|
|
self.send_personal_message(bot_profile, user_profile)
|
|
|
|
|
2024-07-08 19:00:08 +02:00
|
|
|
# Tests if the permission works when the setting is set to a combination of
|
|
|
|
# groups and users.
|
|
|
|
user_group = self.create_or_update_anonymous_group_for_setting(
|
|
|
|
[user_profile],
|
|
|
|
[administrators_system_group],
|
|
|
|
)
|
|
|
|
do_change_realm_permission_group_setting(
|
|
|
|
realm,
|
|
|
|
"direct_message_initiator_group",
|
|
|
|
user_group,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
self.send_personal_message(user_profile, cordelia)
|
|
|
|
|
|
|
|
def test_direct_message_permission_group_setting(self) -> None:
|
|
|
|
"""
|
|
|
|
Tests that direct_message_permission_group_setting works correctly.
|
|
|
|
"""
|
|
|
|
user_profile = self.example_user("hamlet")
|
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
polonius = self.example_user("polonius")
|
|
|
|
admin = self.example_user("iago")
|
|
|
|
realm = user_profile.realm
|
|
|
|
direct_message_group = [user_profile, cordelia, admin]
|
|
|
|
direct_message_group_without_admin = [user_profile, cordelia, polonius]
|
|
|
|
administrators_system_group = NamedUserGroup.objects.get(
|
|
|
|
name=SystemGroups.ADMINISTRATORS, realm=realm, is_system_group=True
|
|
|
|
)
|
|
|
|
nobody_system_group = NamedUserGroup.objects.get(
|
|
|
|
name=SystemGroups.NOBODY, realm=realm, is_system_group=True
|
|
|
|
)
|
|
|
|
self.login_user(user_profile)
|
|
|
|
do_change_realm_permission_group_setting(
|
|
|
|
realm,
|
|
|
|
"direct_message_permission_group",
|
|
|
|
administrators_system_group,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
# Tests if the user is allowed to send to administrators.
|
|
|
|
with self.assert_database_query_count(16):
|
|
|
|
self.send_personal_message(user_profile, admin)
|
|
|
|
self.send_personal_message(admin, user_profile)
|
|
|
|
# Tests if we can send messages to self irrespective of the value of the setting.
|
|
|
|
self.send_personal_message(user_profile, user_profile)
|
|
|
|
|
|
|
|
# We cannot send direct messages unless one of the recipient is in the
|
|
|
|
# `direct_message_permission_group` (in this case, the
|
|
|
|
# `administrators_system_group`).
|
|
|
|
with self.assertRaises(DirectMessagePermissionError) as direct_message_permission_error:
|
|
|
|
self.send_personal_message(user_profile, cordelia)
|
|
|
|
self.assertEqual(
|
|
|
|
str(direct_message_permission_error.exception),
|
2024-07-11 03:15:46 +02:00
|
|
|
"This conversation does not include any users who can authorize it.",
|
2024-07-08 19:00:08 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
# We can send to this direct message group as it has administrator as one of the
|
|
|
|
# recipient.
|
|
|
|
with self.assert_database_query_count(24):
|
|
|
|
self.send_group_direct_message(user_profile, direct_message_group)
|
|
|
|
self.send_group_direct_message(admin, direct_message_group)
|
|
|
|
|
|
|
|
# But this one does not have an administrator. So, it should throw an error.
|
|
|
|
with self.assertRaises(DirectMessagePermissionError):
|
|
|
|
self.send_group_direct_message(user_profile, direct_message_group_without_admin)
|
|
|
|
|
|
|
|
bot_profile = self.create_test_bot("testbot", user_profile)
|
|
|
|
notification_bot = get_system_bot("notification-bot@zulip.com", user_profile.realm_id)
|
|
|
|
# Tests if messages to and from bots are allowed irrespective of the value of the setting.
|
|
|
|
self.send_personal_message(user_profile, notification_bot)
|
|
|
|
self.send_personal_message(user_profile, bot_profile)
|
|
|
|
self.send_personal_message(bot_profile, user_profile)
|
|
|
|
|
|
|
|
# Tests if the permission works when the setting is set to a combination of
|
|
|
|
# groups and users.
|
|
|
|
user_group = self.create_or_update_anonymous_group_for_setting(
|
|
|
|
[user_profile],
|
|
|
|
[administrators_system_group],
|
|
|
|
)
|
|
|
|
do_change_realm_permission_group_setting(
|
|
|
|
realm,
|
|
|
|
"direct_message_permission_group",
|
|
|
|
user_group,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
self.send_personal_message(user_profile, cordelia)
|
|
|
|
|
|
|
|
do_change_realm_permission_group_setting(
|
|
|
|
realm,
|
|
|
|
"direct_message_permission_group",
|
|
|
|
nobody_system_group,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
with self.assertRaises(DirectMessagePermissionError) as direct_message_permission_error:
|
|
|
|
self.send_personal_message(user_profile, cordelia)
|
|
|
|
self.assertEqual(
|
|
|
|
str(direct_message_permission_error.exception),
|
|
|
|
"Direct messages are disabled in this organization.",
|
|
|
|
)
|
|
|
|
|
2020-07-06 01:17:57 +02:00
|
|
|
def test_non_ascii_personal(self) -> None:
|
|
|
|
"""
|
2023-06-19 16:26:12 +02:00
|
|
|
Sending a direct message containing non-ASCII characters succeeds.
|
2020-07-06 01:17:57 +02:00
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
2020-07-06 01:17:57 +02:00
|
|
|
self.assert_personal(
|
|
|
|
sender=self.example_user("hamlet"),
|
|
|
|
receiver=self.example_user("othello"),
|
|
|
|
content="hümbüǵ",
|
|
|
|
)
|
2020-07-06 21:21:15 +02:00
|
|
|
|
|
|
|
|
|
|
|
class ExtractTest(ZulipTestCase):
|
|
|
|
def test_extract_stream_indicator(self) -> None:
|
|
|
|
self.assertEqual(
|
2021-02-12 08:20:45 +01:00
|
|
|
extract_stream_indicator("development"),
|
2020-07-06 21:21:15 +02:00
|
|
|
"development",
|
|
|
|
)
|
|
|
|
self.assertEqual(
|
2021-02-12 08:20:45 +01:00
|
|
|
extract_stream_indicator("commas,are,fine"),
|
2020-07-06 21:21:15 +02:00
|
|
|
"commas,are,fine",
|
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
extract_stream_indicator('"Who hasn\'t done this?"'),
|
|
|
|
"Who hasn't done this?",
|
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
extract_stream_indicator("999"),
|
|
|
|
999,
|
|
|
|
)
|
|
|
|
|
|
|
|
# For legacy reasons it's plausible that users will
|
|
|
|
# put a single stream into an array and then encode it
|
|
|
|
# as JSON. We can probably eliminate this support
|
|
|
|
# by mid 2020 at the latest.
|
|
|
|
self.assertEqual(
|
|
|
|
extract_stream_indicator('["social"]'),
|
2021-02-12 08:20:45 +01:00
|
|
|
"social",
|
2020-07-06 21:21:15 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
self.assertEqual(
|
|
|
|
extract_stream_indicator("[123]"),
|
|
|
|
123,
|
|
|
|
)
|
|
|
|
|
2024-04-16 17:58:08 +02:00
|
|
|
with self.assertRaisesRegex(JsonableError, "Invalid data type for channel"):
|
2021-02-12 08:20:45 +01:00
|
|
|
extract_stream_indicator("{}")
|
2020-07-06 21:21:15 +02:00
|
|
|
|
2024-04-16 17:58:08 +02:00
|
|
|
with self.assertRaisesRegex(JsonableError, "Invalid data type for channel"):
|
2021-02-12 08:20:45 +01:00
|
|
|
extract_stream_indicator("[{}]")
|
2020-07-06 21:21:15 +02:00
|
|
|
|
2024-04-16 17:58:08 +02:00
|
|
|
with self.assertRaisesRegex(JsonableError, "Expected exactly one channel"):
|
2020-07-06 21:21:15 +02:00
|
|
|
extract_stream_indicator('[1,2,"general"]')
|
|
|
|
|
|
|
|
def test_extract_private_recipients_emails(self) -> None:
|
|
|
|
# JSON list w/dups, empties, and trailing whitespace
|
2021-02-12 08:20:45 +01:00
|
|
|
s = orjson.dumps([" alice@zulip.com ", " bob@zulip.com ", " ", "bob@zulip.com"]).decode()
|
2020-07-06 21:21:15 +02:00
|
|
|
# sorted() gets confused by extract_private_recipients' return type
|
|
|
|
# For testing, ignorance here is better than manual casting
|
|
|
|
result = sorted(extract_private_recipients(s))
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(result, ["alice@zulip.com", "bob@zulip.com"])
|
2020-07-06 21:21:15 +02:00
|
|
|
|
|
|
|
# simple string with one name
|
2021-02-12 08:20:45 +01:00
|
|
|
s = "alice@zulip.com "
|
|
|
|
self.assertEqual(extract_private_recipients(s), ["alice@zulip.com"])
|
2020-07-06 21:21:15 +02:00
|
|
|
|
|
|
|
# JSON-encoded string
|
|
|
|
s = '"alice@zulip.com"'
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(extract_private_recipients(s), ["alice@zulip.com"])
|
2020-07-06 21:21:15 +02:00
|
|
|
|
|
|
|
# bare comma-delimited string
|
2021-02-12 08:20:45 +01:00
|
|
|
s = "bob@zulip.com, alice@zulip.com"
|
2020-07-06 21:21:15 +02:00
|
|
|
result = sorted(extract_private_recipients(s))
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(result, ["alice@zulip.com", "bob@zulip.com"])
|
2020-07-06 21:21:15 +02:00
|
|
|
|
|
|
|
# JSON-encoded, comma-delimited string
|
|
|
|
s = '"bob@zulip.com,alice@zulip.com"'
|
|
|
|
result = sorted(extract_private_recipients(s))
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(result, ["alice@zulip.com", "bob@zulip.com"])
|
2020-07-06 21:21:15 +02:00
|
|
|
|
|
|
|
# Invalid data
|
2021-02-12 08:20:45 +01:00
|
|
|
s = orjson.dumps(dict(color="red")).decode()
|
|
|
|
with self.assertRaisesRegex(JsonableError, "Invalid data type for recipients"):
|
2020-07-06 21:21:15 +02:00
|
|
|
extract_private_recipients(s)
|
|
|
|
|
2020-08-07 01:09:47 +02:00
|
|
|
s = orjson.dumps([{}]).decode()
|
2021-02-12 08:20:45 +01:00
|
|
|
with self.assertRaisesRegex(JsonableError, "Invalid data type for recipients"):
|
2020-07-06 21:21:15 +02:00
|
|
|
extract_private_recipients(s)
|
|
|
|
|
|
|
|
# Empty list
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(extract_private_recipients("[]"), [])
|
2020-07-06 21:21:15 +02:00
|
|
|
|
|
|
|
# Heterogeneous lists are not supported
|
2021-02-12 08:20:45 +01:00
|
|
|
mixed = orjson.dumps(["eeshan@example.com", 3, 4]).decode()
|
2021-02-12 08:19:30 +01:00
|
|
|
with self.assertRaisesRegex(
|
2021-02-12 08:20:45 +01:00
|
|
|
JsonableError, "Recipient lists may contain emails or user IDs, but not both."
|
2021-02-12 08:19:30 +01:00
|
|
|
):
|
2020-07-06 21:21:15 +02:00
|
|
|
extract_private_recipients(mixed)
|
|
|
|
|
|
|
|
def test_extract_recipient_ids(self) -> None:
|
|
|
|
# JSON list w/dups
|
2020-08-07 01:09:47 +02:00
|
|
|
s = orjson.dumps([3, 3, 12]).decode()
|
2020-07-06 21:21:15 +02:00
|
|
|
result = sorted(extract_private_recipients(s))
|
|
|
|
self.assertEqual(result, [3, 12])
|
|
|
|
|
|
|
|
# Invalid data
|
2020-08-07 01:09:47 +02:00
|
|
|
ids = orjson.dumps(dict(recipient=12)).decode()
|
2021-02-12 08:20:45 +01:00
|
|
|
with self.assertRaisesRegex(JsonableError, "Invalid data type for recipients"):
|
2020-07-06 21:21:15 +02:00
|
|
|
extract_private_recipients(ids)
|
|
|
|
|
|
|
|
# Heterogeneous lists are not supported
|
2021-02-12 08:20:45 +01:00
|
|
|
mixed = orjson.dumps([3, 4, "eeshan@example.com"]).decode()
|
2021-02-12 08:19:30 +01:00
|
|
|
with self.assertRaisesRegex(
|
2021-02-12 08:20:45 +01:00
|
|
|
JsonableError, "Recipient lists may contain emails or user IDs, but not both."
|
2021-02-12 08:19:30 +01:00
|
|
|
):
|
2020-07-06 21:21:15 +02:00
|
|
|
extract_private_recipients(mixed)
|
2020-07-06 22:28:48 +02:00
|
|
|
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
class InternalPrepTest(ZulipTestCase):
|
2020-07-06 22:28:48 +02:00
|
|
|
def test_returns_for_internal_sends(self) -> None:
|
|
|
|
# For our internal_send_* functions we return
|
|
|
|
# if the prep stages fail. This is mostly defensive
|
|
|
|
# code, since we are generally creating the messages
|
|
|
|
# ourselves, but we want to make sure that the functions
|
|
|
|
# won't actually explode if we give them bad content.
|
2021-02-12 08:20:45 +01:00
|
|
|
bad_content = ""
|
|
|
|
realm = get_realm("zulip")
|
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
othello = self.example_user("othello")
|
|
|
|
stream = get_stream("Verona", realm)
|
2020-07-06 22:28:48 +02:00
|
|
|
|
2020-07-27 20:26:15 +02:00
|
|
|
with self.assertLogs(level="ERROR") as m:
|
2020-07-06 22:28:48 +02:00
|
|
|
internal_send_private_message(
|
|
|
|
sender=cordelia,
|
|
|
|
recipient_user=hamlet,
|
|
|
|
content=bad_content,
|
|
|
|
)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertEqual(
|
2021-02-12 08:20:45 +01:00
|
|
|
m.output[0].split("\n")[0],
|
2021-02-12 08:19:30 +01:00
|
|
|
"ERROR:root:Error queueing internal message by {}: {}".format(
|
|
|
|
"cordelia@zulip.com", "Message must not be empty"
|
|
|
|
),
|
|
|
|
)
|
2020-07-06 22:28:48 +02:00
|
|
|
|
2020-07-27 20:26:15 +02:00
|
|
|
with self.assertLogs(level="ERROR") as m:
|
2024-07-04 14:05:48 +02:00
|
|
|
internal_send_group_direct_message(
|
2020-07-06 22:28:48 +02:00
|
|
|
realm=realm,
|
|
|
|
sender=cordelia,
|
|
|
|
emails=[hamlet.email, othello.email],
|
|
|
|
content=bad_content,
|
|
|
|
)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertEqual(
|
2021-02-12 08:20:45 +01:00
|
|
|
m.output[0].split("\n")[0],
|
2021-02-12 08:19:30 +01:00
|
|
|
"ERROR:root:Error queueing internal message by {}: {}".format(
|
|
|
|
"cordelia@zulip.com", "Message must not be empty"
|
|
|
|
),
|
|
|
|
)
|
2020-07-06 22:28:48 +02:00
|
|
|
|
2020-07-27 20:26:15 +02:00
|
|
|
with self.assertLogs(level="ERROR") as m:
|
2020-07-06 22:28:48 +02:00
|
|
|
internal_send_stream_message(
|
|
|
|
sender=cordelia,
|
2024-01-13 09:55:16 +01:00
|
|
|
topic_name="whatever",
|
2020-07-06 22:28:48 +02:00
|
|
|
content=bad_content,
|
|
|
|
stream=stream,
|
|
|
|
)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertEqual(
|
2021-02-12 08:20:45 +01:00
|
|
|
m.output[0].split("\n")[0],
|
2021-02-12 08:19:30 +01:00
|
|
|
"ERROR:root:Error queueing internal message by {}: {}".format(
|
|
|
|
"cordelia@zulip.com", "Message must not be empty"
|
|
|
|
),
|
|
|
|
)
|
2020-07-06 22:28:48 +02:00
|
|
|
|
2020-07-27 20:26:15 +02:00
|
|
|
with self.assertLogs(level="ERROR") as m:
|
2020-07-06 22:28:48 +02:00
|
|
|
internal_send_stream_message_by_name(
|
|
|
|
realm=realm,
|
|
|
|
sender=cordelia,
|
|
|
|
stream_name=stream.name,
|
2024-01-13 09:55:16 +01:00
|
|
|
topic_name="whatever",
|
2020-07-06 22:28:48 +02:00
|
|
|
content=bad_content,
|
|
|
|
)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertEqual(
|
2021-02-12 08:20:45 +01:00
|
|
|
m.output[0].split("\n")[0],
|
2021-02-12 08:19:30 +01:00
|
|
|
"ERROR:root:Error queueing internal message by {}: {}".format(
|
|
|
|
"cordelia@zulip.com", "Message must not be empty"
|
|
|
|
),
|
|
|
|
)
|
2020-07-06 22:28:48 +02:00
|
|
|
|
|
|
|
def test_error_handling(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
sender = self.example_user("cordelia")
|
|
|
|
recipient_user = self.example_user("hamlet")
|
message-send: Deduplicate check of `settings.MAX_MESSAGE_LENGTH`.
Removes the initial check in `_internal_prep_message` of the length
of the message content because the `check_message` in the try block
will call `normalize_body` on the message content string, which
does a more robust check of the message content (empty string, null
bytes, length). If the message content length exceeds the value of
`settings.MAX_MESSAGE_LENGTH`, then it is truncated based on that
value. Updates associated backend test for these changes.
The removed length check would truncate the message content with a
hard coded value instead of using the value for
`settings.MAX_MESSAGE_LENGTH`.
Also, removes an extraneous comment about removing null bytes. If
there are null bytes in the message content, then `normalize_body`
will raise an error.
Note that the previous check had intentionally reduced any message over
the 10000 character limit to 3900 characters, with the code in
question dating to 2012's 100df7e349d44fbaccee95a1105f41d7e0cdb529.
The 3900 character truncating rule was implemented for incoming emails
with the email gateway, and predated other features to help with
overly long messages (better stripping of email footers via Talon,
introduced in f1f48f305e38c7cdab471fbd6d62c9a06b5f664b, and
condensing, introduced in c92d664b4450a76efb2ccdb6f56dee8c14ea08af).
While we could preserve that logic if desired, it likely is no longer
a necessary or useful variation from our usual truncation rules.
2023-02-15 16:27:13 +01:00
|
|
|
MAX_MESSAGE_LENGTH = settings.MAX_MESSAGE_LENGTH
|
|
|
|
content = "x" * (MAX_MESSAGE_LENGTH + 10)
|
2020-07-06 22:28:48 +02:00
|
|
|
|
|
|
|
result = internal_prep_private_message(
|
2022-09-04 15:57:56 +02:00
|
|
|
sender=sender, recipient_user=recipient_user, content=content
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-07-06 22:28:48 +02:00
|
|
|
assert result is not None
|
2020-11-24 12:31:28 +01:00
|
|
|
message = result.message
|
message-send: Deduplicate check of `settings.MAX_MESSAGE_LENGTH`.
Removes the initial check in `_internal_prep_message` of the length
of the message content because the `check_message` in the try block
will call `normalize_body` on the message content string, which
does a more robust check of the message content (empty string, null
bytes, length). If the message content length exceeds the value of
`settings.MAX_MESSAGE_LENGTH`, then it is truncated based on that
value. Updates associated backend test for these changes.
The removed length check would truncate the message content with a
hard coded value instead of using the value for
`settings.MAX_MESSAGE_LENGTH`.
Also, removes an extraneous comment about removing null bytes. If
there are null bytes in the message content, then `normalize_body`
will raise an error.
Note that the previous check had intentionally reduced any message over
the 10000 character limit to 3900 characters, with the code in
question dating to 2012's 100df7e349d44fbaccee95a1105f41d7e0cdb529.
The 3900 character truncating rule was implemented for incoming emails
with the email gateway, and predated other features to help with
overly long messages (better stripping of email footers via Talon,
introduced in f1f48f305e38c7cdab471fbd6d62c9a06b5f664b, and
condensing, introduced in c92d664b4450a76efb2ccdb6f56dee8c14ea08af).
While we could preserve that logic if desired, it likely is no longer
a necessary or useful variation from our usual truncation rules.
2023-02-15 16:27:13 +01:00
|
|
|
self.assertIn("message truncated", message.content)
|
2020-07-06 22:28:48 +02:00
|
|
|
|
|
|
|
# Simulate sending a message to somebody not in the
|
|
|
|
# realm of the sender.
|
2021-02-12 08:20:45 +01:00
|
|
|
recipient_user = self.mit_user("starnine")
|
2020-07-27 20:26:15 +02:00
|
|
|
with self.assertLogs(level="ERROR") as m:
|
2020-07-06 22:28:48 +02:00
|
|
|
result = internal_prep_private_message(
|
2022-09-04 15:57:56 +02:00
|
|
|
sender=sender, recipient_user=recipient_user, content=content
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-07-27 20:26:15 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertEqual(
|
2021-02-12 08:20:45 +01:00
|
|
|
m.output[0].split("\n")[0],
|
2021-02-12 08:19:30 +01:00
|
|
|
"ERROR:root:Error queueing internal message by {}: {}".format(
|
|
|
|
"cordelia@zulip.com",
|
2023-01-24 15:36:03 +01:00
|
|
|
"You can't send direct messages outside of your organization.",
|
2021-02-12 08:19:30 +01:00
|
|
|
),
|
|
|
|
)
|
2020-07-06 22:28:48 +02:00
|
|
|
|
|
|
|
def test_ensure_stream_gets_called(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
realm = get_realm("zulip")
|
|
|
|
sender = self.example_user("cordelia")
|
|
|
|
stream_name = "test_stream"
|
2024-01-13 09:55:16 +01:00
|
|
|
topic_name = "whatever"
|
2021-02-12 08:20:45 +01:00
|
|
|
content = "hello"
|
2020-07-06 22:28:48 +02:00
|
|
|
|
|
|
|
internal_prep_stream_message_by_name(
|
2024-01-13 09:55:16 +01:00
|
|
|
realm=realm,
|
|
|
|
sender=sender,
|
|
|
|
stream_name=stream_name,
|
|
|
|
topic_name=topic_name,
|
|
|
|
content=content,
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-07-06 22:28:48 +02:00
|
|
|
|
|
|
|
# This would throw an error if the stream
|
|
|
|
# wasn't automatically created.
|
|
|
|
Stream.objects.get(name=stream_name, realm_id=realm.id)
|
2020-07-07 23:34:04 +02:00
|
|
|
|
2024-01-07 04:18:20 +01:00
|
|
|
def test_direct_message_to_self_and_bot_in_dm_disabled_org(self) -> None:
|
|
|
|
"""
|
|
|
|
Test that a user can send a direct message to themselves and to a bot in a DM disabled organization
|
|
|
|
"""
|
|
|
|
sender = self.example_user("hamlet")
|
2024-07-08 19:00:08 +02:00
|
|
|
nobody_system_group = NamedUserGroup.objects.get(
|
|
|
|
name=SystemGroups.NOBODY, realm=sender.realm, is_system_group=True
|
|
|
|
)
|
|
|
|
do_change_realm_permission_group_setting(
|
|
|
|
sender.realm,
|
|
|
|
"direct_message_permission_group",
|
|
|
|
nobody_system_group,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
2024-01-07 04:18:20 +01:00
|
|
|
sender.realm.save()
|
|
|
|
|
|
|
|
# Create a non-bot user
|
|
|
|
recipient_user = self.example_user("othello")
|
|
|
|
recipient_user.realm = sender.realm
|
|
|
|
|
|
|
|
# Create a new bot user
|
|
|
|
bot = do_create_user(
|
|
|
|
email="test-bot@zulip.com",
|
|
|
|
password="",
|
|
|
|
realm=sender.realm,
|
|
|
|
full_name="Test Bot",
|
|
|
|
bot_type=UserProfile.DEFAULT_BOT,
|
|
|
|
bot_owner=sender,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
|
|
|
|
# Test sending a message to self
|
|
|
|
result = self.api_post(
|
|
|
|
sender,
|
|
|
|
"/api/v1/messages",
|
|
|
|
{
|
|
|
|
"type": "private",
|
|
|
|
"to": orjson.dumps([sender.id]).decode(),
|
|
|
|
"content": "Test message to self",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
msg = self.get_last_message()
|
|
|
|
expected = "Test message to self"
|
|
|
|
self.assertEqual(msg.content, expected)
|
|
|
|
|
|
|
|
# Test sending a message to non-bot user
|
|
|
|
result = self.api_post(
|
|
|
|
sender,
|
|
|
|
"/api/v1/messages",
|
|
|
|
{
|
|
|
|
"type": "private",
|
|
|
|
"to": orjson.dumps([recipient_user.id]).decode(),
|
|
|
|
"content": "Test message",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
self.assert_json_error(result, "Direct messages are disabled in this organization.")
|
|
|
|
|
|
|
|
# Test sending a message to the bot
|
|
|
|
result = self.api_post(
|
|
|
|
sender,
|
|
|
|
"/api/v1/messages",
|
|
|
|
{
|
|
|
|
"type": "private",
|
|
|
|
"to": orjson.dumps([bot.id]).decode(),
|
|
|
|
"content": "Test message to bot",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
msg = self.get_last_message()
|
|
|
|
expected = "Test message to bot"
|
|
|
|
self.assertEqual(msg.content, expected)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2020-07-07 23:34:04 +02:00
|
|
|
class TestCrossRealmPMs(ZulipTestCase):
|
|
|
|
def make_realm(self, domain: str) -> Realm:
|
2021-03-08 13:22:43 +01:00
|
|
|
realm = do_create_realm(string_id=domain, name=domain)
|
2021-03-01 11:33:24 +01:00
|
|
|
do_set_realm_property(realm, "invite_required", False, acting_user=None)
|
2020-07-07 23:34:04 +02:00
|
|
|
RealmDomain.objects.create(realm=realm, domain=domain)
|
|
|
|
return realm
|
|
|
|
|
|
|
|
def create_user(self, email: str) -> UserProfile:
|
2022-07-27 23:33:49 +02:00
|
|
|
subdomain = Address(addr_spec=email).domain
|
2021-02-12 08:20:45 +01:00
|
|
|
self.register(email, "test", subdomain=subdomain)
|
2022-01-23 20:30:46 +01:00
|
|
|
# self.register has the side-effect of ending up with a logged in session
|
|
|
|
# for the new user. We don't want that in these tests.
|
|
|
|
self.logout()
|
|
|
|
|
2020-07-07 23:34:04 +02:00
|
|
|
return get_user(email, get_realm(subdomain))
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
@override_settings(
|
|
|
|
CROSS_REALM_BOT_EMAILS=[
|
2021-02-12 08:20:45 +01:00
|
|
|
"notification-bot@zulip.com",
|
|
|
|
"welcome-bot@zulip.com",
|
|
|
|
"support@3.example.com",
|
2021-02-12 08:19:30 +01:00
|
|
|
]
|
|
|
|
)
|
2020-07-07 23:34:04 +02:00
|
|
|
def test_realm_scenarios(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.make_realm("1.example.com")
|
|
|
|
r2 = self.make_realm("2.example.com")
|
|
|
|
self.make_realm("3.example.com")
|
2020-07-07 23:34:04 +02:00
|
|
|
|
|
|
|
def assert_message_received(to_user: UserProfile, from_user: UserProfile) -> None:
|
|
|
|
messages = get_user_messages(to_user)
|
|
|
|
self.assertEqual(messages[-1].sender.id, from_user.id)
|
|
|
|
|
|
|
|
def assert_invalid_user() -> Any:
|
2021-02-12 08:20:45 +01:00
|
|
|
return self.assertRaisesRegex(JsonableError, "Invalid user ID ")
|
2020-07-07 23:34:04 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
user1_email = "user1@1.example.com"
|
|
|
|
user1a_email = "user1a@1.example.com"
|
|
|
|
user2_email = "user2@2.example.com"
|
|
|
|
user3_email = "user3@3.example.com"
|
|
|
|
notification_bot_email = "notification-bot@zulip.com"
|
|
|
|
support_email = "support@3.example.com" # note: not zulip.com
|
2020-07-07 23:34:04 +02:00
|
|
|
|
|
|
|
user1 = self.create_user(user1_email)
|
|
|
|
user1a = self.create_user(user1a_email)
|
|
|
|
user2 = self.create_user(user2_email)
|
|
|
|
user3 = self.create_user(user3_email)
|
2021-03-08 11:54:39 +01:00
|
|
|
|
|
|
|
internal_realm = get_realm(settings.SYSTEM_BOT_REALM)
|
|
|
|
notification_bot = get_system_bot(notification_bot_email, internal_realm.id)
|
2021-02-12 08:19:30 +01:00
|
|
|
with self.settings(
|
2021-02-12 08:20:45 +01:00
|
|
|
CROSS_REALM_BOT_EMAILS=["notification-bot@zulip.com", "welcome-bot@zulip.com"]
|
2021-02-12 08:19:30 +01:00
|
|
|
):
|
2020-07-07 23:34:04 +02:00
|
|
|
# HACK: We should probably be creating this "bot" user another
|
|
|
|
# way, but since you can't register a user with a
|
|
|
|
# cross-realm email, we need to hide this for now.
|
|
|
|
support_bot = self.create_user(support_email)
|
|
|
|
|
2023-06-19 16:26:12 +02:00
|
|
|
# Users can send a direct message to themselves.
|
2020-07-07 23:34:04 +02:00
|
|
|
self.send_personal_message(user1, user1)
|
|
|
|
assert_message_received(user1, user1)
|
|
|
|
|
2023-06-19 16:26:12 +02:00
|
|
|
# Users on the same realm can send direct messages to each other.
|
2020-07-07 23:34:04 +02:00
|
|
|
self.send_personal_message(user1, user1a)
|
|
|
|
assert_message_received(user1a, user1)
|
|
|
|
|
2023-06-19 16:26:12 +02:00
|
|
|
# Cross-realm bots in the zulip.com realm can send a direct message
|
|
|
|
# in any realm.
|
2020-07-07 23:34:04 +02:00
|
|
|
# (They need lower level APIs to do this.)
|
|
|
|
internal_send_private_message(
|
2021-03-08 11:54:39 +01:00
|
|
|
sender=notification_bot,
|
2020-07-07 23:34:04 +02:00
|
|
|
recipient_user=get_user(user2_email, r2),
|
2021-02-12 08:20:45 +01:00
|
|
|
content="bla",
|
2020-07-07 23:34:04 +02:00
|
|
|
)
|
|
|
|
assert_message_received(user2, notification_bot)
|
|
|
|
|
2023-06-19 16:26:12 +02:00
|
|
|
# All users can send a direct message to cross-realm bots in the
|
|
|
|
# zulip.com realm.
|
2020-07-07 23:34:04 +02:00
|
|
|
self.send_personal_message(user1, notification_bot)
|
|
|
|
assert_message_received(notification_bot, user1)
|
2021-02-19 16:27:11 +01:00
|
|
|
# Verify that internal_send_private_message can also successfully
|
|
|
|
# be used.
|
|
|
|
internal_send_private_message(
|
|
|
|
sender=user2,
|
2021-03-08 11:54:39 +01:00
|
|
|
recipient_user=notification_bot,
|
2021-02-19 16:27:11 +01:00
|
|
|
content="blabla",
|
|
|
|
)
|
|
|
|
assert_message_received(notification_bot, user2)
|
2023-06-19 16:26:12 +02:00
|
|
|
# Users can send a direct message to cross-realm bots on non-zulip
|
|
|
|
# realms.
|
2020-07-07 23:34:04 +02:00
|
|
|
# (The support bot represents some theoretical bot that we may
|
|
|
|
# create in the future that does not have zulip.com as its realm.)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.send_personal_message(user1, support_bot)
|
2020-07-07 23:34:04 +02:00
|
|
|
assert_message_received(support_bot, user1)
|
|
|
|
|
2023-06-19 16:26:12 +02:00
|
|
|
# Allow sending direct messages to two different cross-realm bots
|
|
|
|
# simultaneously.
|
2020-07-07 23:34:04 +02:00
|
|
|
# (We don't particularly need this feature, but since users can
|
2023-06-19 16:26:12 +02:00
|
|
|
# already individually send direct messages to cross-realm bots,
|
|
|
|
# we shouldn't prevent them from sending multiple bots at once.
|
2024-07-04 14:05:48 +02:00
|
|
|
# We may revisit this if it's a nuisance for direct message
|
|
|
|
# groups.)
|
|
|
|
self.send_group_direct_message(user1, [notification_bot, support_bot])
|
2020-07-07 23:34:04 +02:00
|
|
|
assert_message_received(notification_bot, user1)
|
|
|
|
assert_message_received(support_bot, user1)
|
|
|
|
|
2023-06-19 16:26:12 +02:00
|
|
|
# Prevent old loophole where I could send direct messages to other
|
|
|
|
# users as long as I copied a cross-realm bot from the same realm.
|
2020-07-07 23:34:04 +02:00
|
|
|
with assert_invalid_user():
|
2024-07-04 14:05:48 +02:00
|
|
|
self.send_group_direct_message(user1, [user3, support_bot])
|
2020-07-07 23:34:04 +02:00
|
|
|
|
2023-06-19 16:26:12 +02:00
|
|
|
# Users on three different realms can't send direct messages to
|
|
|
|
# each other, even if one of the users is a cross-realm bot.
|
2020-07-07 23:34:04 +02:00
|
|
|
with assert_invalid_user():
|
2024-07-04 14:05:48 +02:00
|
|
|
self.send_group_direct_message(user1, [user2, notification_bot])
|
2020-07-07 23:34:04 +02:00
|
|
|
|
|
|
|
with assert_invalid_user():
|
2024-07-04 14:05:48 +02:00
|
|
|
self.send_group_direct_message(notification_bot, [user1, user2])
|
2020-07-07 23:34:04 +02:00
|
|
|
|
2023-06-19 16:26:12 +02:00
|
|
|
# Users on the different realms cannot send direct messages to
|
|
|
|
# each other.
|
2020-07-07 23:34:04 +02:00
|
|
|
with assert_invalid_user():
|
|
|
|
self.send_personal_message(user1, user2)
|
|
|
|
|
2023-06-19 16:26:12 +02:00
|
|
|
# Users on non-zulip realms can't send direct messages to
|
|
|
|
# "ordinary" Zulip users.
|
2020-07-07 23:34:04 +02:00
|
|
|
with assert_invalid_user():
|
2021-02-12 08:20:45 +01:00
|
|
|
self.send_personal_message(user1, self.example_user("hamlet"))
|
2020-07-07 23:34:04 +02:00
|
|
|
|
2023-06-19 16:26:12 +02:00
|
|
|
# Users on three different realms cannot send direct messages
|
|
|
|
# to each other.
|
2020-07-07 23:34:04 +02:00
|
|
|
with assert_invalid_user():
|
2024-07-04 14:05:48 +02:00
|
|
|
self.send_group_direct_message(user1, [user2, user3])
|
2020-07-07 23:47:32 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2020-07-07 23:47:32 +02:00
|
|
|
class TestAddressee(ZulipTestCase):
|
|
|
|
def test_addressee_for_user_ids(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
realm = get_realm("zulip")
|
2021-02-12 08:19:30 +01:00
|
|
|
user_ids = [
|
2021-02-12 08:20:45 +01:00
|
|
|
self.example_user("cordelia").id,
|
|
|
|
self.example_user("hamlet").id,
|
|
|
|
self.example_user("othello").id,
|
2021-02-12 08:19:30 +01:00
|
|
|
]
|
2020-07-07 23:47:32 +02:00
|
|
|
|
|
|
|
result = Addressee.for_user_ids(user_ids=user_ids, realm=realm)
|
|
|
|
user_profiles = result.user_profiles()
|
2021-02-12 08:19:30 +01:00
|
|
|
result_user_ids = [user_profiles[0].id, user_profiles[1].id, user_profiles[2].id]
|
2020-07-07 23:47:32 +02:00
|
|
|
|
|
|
|
self.assertEqual(set(result_user_ids), set(user_ids))
|
|
|
|
|
|
|
|
def test_addressee_for_user_ids_nonexistent_id(self) -> None:
|
|
|
|
def assert_invalid_user_id() -> Any:
|
2021-02-12 08:20:45 +01:00
|
|
|
return self.assertRaisesRegex(JsonableError, "Invalid user ID ")
|
2020-07-07 23:47:32 +02:00
|
|
|
|
|
|
|
with assert_invalid_user_id():
|
2021-02-12 08:20:45 +01:00
|
|
|
Addressee.for_user_ids(user_ids=[779], realm=get_realm("zulip"))
|
2020-07-07 23:47:32 +02:00
|
|
|
|
|
|
|
def test_addressee_legacy_build_for_user_ids(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
realm = get_realm("zulip")
|
|
|
|
self.login("hamlet")
|
|
|
|
user_ids = [self.example_user("cordelia").id, self.example_user("othello").id]
|
2020-07-07 23:47:32 +02:00
|
|
|
|
|
|
|
result = Addressee.legacy_build(
|
2021-02-12 08:20:45 +01:00
|
|
|
sender=self.example_user("hamlet"),
|
2023-04-18 17:23:58 +02:00
|
|
|
recipient_type_name="private",
|
2021-02-12 08:19:30 +01:00
|
|
|
message_to=user_ids,
|
2021-02-12 08:20:45 +01:00
|
|
|
topic_name="random_topic",
|
2020-07-07 23:47:32 +02:00
|
|
|
realm=realm,
|
|
|
|
)
|
|
|
|
user_profiles = result.user_profiles()
|
|
|
|
result_user_ids = [user_profiles[0].id, user_profiles[1].id]
|
|
|
|
|
|
|
|
self.assertEqual(set(result_user_ids), set(user_ids))
|
|
|
|
|
|
|
|
def test_addressee_legacy_build_for_stream_id(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
realm = get_realm("zulip")
|
|
|
|
self.login("iago")
|
|
|
|
sender = self.example_user("iago")
|
2020-07-07 23:47:32 +02:00
|
|
|
self.subscribe(sender, "Denmark")
|
2021-02-12 08:20:45 +01:00
|
|
|
stream = get_stream("Denmark", realm)
|
2020-07-07 23:47:32 +02:00
|
|
|
|
|
|
|
result = Addressee.legacy_build(
|
2021-02-12 08:19:30 +01:00
|
|
|
sender=sender,
|
2023-04-18 17:23:58 +02:00
|
|
|
recipient_type_name="stream",
|
2021-02-12 08:19:30 +01:00
|
|
|
message_to=[stream.id],
|
2021-02-12 08:20:45 +01:00
|
|
|
topic_name="random_topic",
|
2020-07-07 23:47:32 +02:00
|
|
|
realm=realm,
|
|
|
|
)
|
|
|
|
|
|
|
|
stream_id = result.stream_id()
|
|
|
|
self.assertEqual(stream.id, stream_id)
|
2020-07-08 01:04:07 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2020-07-08 01:04:07 +02:00
|
|
|
class CheckMessageTest(ZulipTestCase):
|
|
|
|
def test_basic_check_message_call(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
sender = self.example_user("othello")
|
2020-07-08 01:04:07 +02:00
|
|
|
client = make_client(name="test suite")
|
2021-02-12 08:20:45 +01:00
|
|
|
stream_name = "España y Francia"
|
2020-07-08 01:04:07 +02:00
|
|
|
self.make_stream(stream_name)
|
2021-02-12 08:20:45 +01:00
|
|
|
topic_name = "issue"
|
|
|
|
message_content = "whatever"
|
2020-07-08 01:04:07 +02:00
|
|
|
addressee = Addressee.for_stream_name(stream_name, topic_name)
|
|
|
|
ret = check_message(sender, client, addressee, message_content)
|
2020-11-24 12:31:28 +01:00
|
|
|
self.assertEqual(ret.message.sender.id, sender.id)
|
2020-07-08 01:04:07 +02:00
|
|
|
|
2021-03-25 15:42:51 +01:00
|
|
|
def test_check_message_normal_user_cant_send_to_stream_in_another_realm(self) -> None:
|
|
|
|
mit_user = self.mit_user("sipbtest")
|
|
|
|
|
|
|
|
client = make_client(name="test suite")
|
|
|
|
stream = get_stream("Denmark", get_realm("zulip"))
|
|
|
|
topic_name = "issue"
|
|
|
|
message_content = "whatever"
|
|
|
|
addressee = Addressee.for_stream(stream, topic_name)
|
|
|
|
|
|
|
|
with self.assertRaisesRegex(JsonableError, "User not authorized for this query"):
|
|
|
|
check_message(
|
|
|
|
mit_user,
|
|
|
|
client,
|
|
|
|
addressee,
|
|
|
|
message_content,
|
|
|
|
)
|
|
|
|
|
|
|
|
def test_check_message_cant_forge_message_as_other_realm_user(self) -> None:
|
|
|
|
"""
|
|
|
|
Verifies that the .can_forge_sender permission doesn't allow
|
|
|
|
forging another realm's user as sender of a message to a stream
|
|
|
|
in the forwarder's realm.
|
|
|
|
"""
|
|
|
|
forwarder_user_profile = self.example_user("othello")
|
|
|
|
do_change_can_forge_sender(forwarder_user_profile, True)
|
|
|
|
|
|
|
|
mit_user = self.mit_user("sipbtest")
|
2021-03-08 11:39:48 +01:00
|
|
|
internal_realm = get_realm("zulipinternal")
|
|
|
|
notification_bot = self.notification_bot(internal_realm)
|
2021-03-25 15:42:51 +01:00
|
|
|
|
|
|
|
client = make_client(name="test suite")
|
|
|
|
stream = get_stream("Denmark", forwarder_user_profile.realm)
|
|
|
|
topic_name = "issue"
|
|
|
|
message_content = "whatever"
|
|
|
|
addressee = Addressee.for_stream(stream, topic_name)
|
|
|
|
|
|
|
|
with self.assertRaisesRegex(JsonableError, "User not authorized for this query"):
|
|
|
|
check_message(
|
|
|
|
mit_user,
|
|
|
|
client,
|
|
|
|
addressee,
|
|
|
|
message_content,
|
|
|
|
forged=True,
|
|
|
|
forwarder_user_profile=forwarder_user_profile,
|
|
|
|
)
|
|
|
|
with self.assertRaisesRegex(JsonableError, "User not authorized for this query"):
|
|
|
|
check_message(
|
|
|
|
notification_bot,
|
|
|
|
client,
|
|
|
|
addressee,
|
|
|
|
message_content,
|
|
|
|
forged=True,
|
|
|
|
forwarder_user_profile=forwarder_user_profile,
|
|
|
|
)
|
|
|
|
|
|
|
|
def test_check_message_cant_forge_message_to_stream_in_different_realm(self) -> None:
|
|
|
|
"""
|
|
|
|
Verifies that the .can_forge_sender permission doesn't allow
|
|
|
|
forging another realm's user as sender of a message to a stream
|
|
|
|
in the forged user's realm..
|
|
|
|
"""
|
|
|
|
forwarder_user_profile = self.example_user("othello")
|
|
|
|
do_change_can_forge_sender(forwarder_user_profile, True)
|
|
|
|
|
|
|
|
mit_user = self.mit_user("sipbtest")
|
2021-03-08 11:39:48 +01:00
|
|
|
internal_realm = get_realm("zulipinternal")
|
|
|
|
notification_bot = self.notification_bot(internal_realm)
|
2021-03-25 15:42:51 +01:00
|
|
|
|
|
|
|
client = make_client(name="test suite")
|
|
|
|
stream_name = "España y Francia"
|
|
|
|
stream = self.make_stream(stream_name, realm=mit_user.realm)
|
|
|
|
self.subscribe(mit_user, stream_name)
|
|
|
|
topic_name = "issue"
|
|
|
|
message_content = "whatever"
|
|
|
|
addressee = Addressee.for_stream(stream, topic_name)
|
|
|
|
|
|
|
|
with self.assertRaisesRegex(JsonableError, "User not authorized for this query"):
|
|
|
|
check_message(
|
|
|
|
mit_user,
|
|
|
|
client,
|
|
|
|
addressee,
|
|
|
|
message_content,
|
|
|
|
forged=True,
|
|
|
|
forwarder_user_profile=forwarder_user_profile,
|
|
|
|
)
|
|
|
|
with self.assertRaisesRegex(JsonableError, "User not authorized for this query"):
|
|
|
|
check_message(
|
|
|
|
notification_bot,
|
|
|
|
client,
|
|
|
|
addressee,
|
|
|
|
message_content,
|
|
|
|
forged=True,
|
|
|
|
forwarder_user_profile=forwarder_user_profile,
|
|
|
|
)
|
|
|
|
|
|
|
|
# Make sure the special case of sending a message forged as cross-realm bot
|
|
|
|
# to a stream in the bot's realm isn't allowed either.
|
|
|
|
stream = self.make_stream(stream_name, realm=notification_bot.realm)
|
|
|
|
self.subscribe(notification_bot, stream_name)
|
|
|
|
addressee = Addressee.for_stream(stream, topic_name)
|
|
|
|
with self.assertRaisesRegex(JsonableError, "User not authorized for this query"):
|
|
|
|
check_message(
|
|
|
|
notification_bot,
|
|
|
|
client,
|
|
|
|
addressee,
|
|
|
|
message_content,
|
|
|
|
forged=True,
|
|
|
|
forwarder_user_profile=forwarder_user_profile,
|
|
|
|
)
|
|
|
|
|
2020-07-24 04:56:12 +02:00
|
|
|
def test_guest_user_can_send_message(self) -> None:
|
|
|
|
# Guest users can write to web_public streams.
|
|
|
|
sender = self.example_user("polonius")
|
|
|
|
client = make_client(name="test suite")
|
|
|
|
rome_stream = get_stream("Rome", sender.realm)
|
|
|
|
|
|
|
|
is_sender_subscriber = Subscription.objects.filter(
|
|
|
|
user_profile=sender,
|
|
|
|
recipient__type_id=rome_stream.id,
|
|
|
|
).exists()
|
|
|
|
self.assertFalse(is_sender_subscriber)
|
|
|
|
self.assertTrue(rome_stream.is_web_public)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
topic_name = "issue"
|
|
|
|
message_content = "whatever"
|
2020-07-24 04:56:12 +02:00
|
|
|
addressee = Addressee.for_stream_name(rome_stream.name, topic_name)
|
|
|
|
ret = check_message(sender, client, addressee, message_content)
|
2020-11-24 12:31:28 +01:00
|
|
|
self.assertEqual(ret.message.sender.id, sender.id)
|
2020-07-24 04:56:12 +02:00
|
|
|
|
2020-07-08 01:04:07 +02:00
|
|
|
def test_bot_pm_feature(self) -> None:
|
2023-06-19 16:26:12 +02:00
|
|
|
"""We send a direct message to a bot's owner if their bot sends a
|
|
|
|
message to an unsubscribed stream"""
|
2021-02-12 08:20:45 +01:00
|
|
|
parent = self.example_user("othello")
|
2020-07-08 01:04:07 +02:00
|
|
|
bot = do_create_user(
|
2021-02-12 08:20:45 +01:00
|
|
|
email="othello-bot@zulip.com",
|
|
|
|
password="",
|
2020-07-08 01:04:07 +02:00
|
|
|
realm=parent.realm,
|
2021-02-12 08:20:45 +01:00
|
|
|
full_name="",
|
2020-07-08 01:04:07 +02:00
|
|
|
bot_type=UserProfile.DEFAULT_BOT,
|
|
|
|
bot_owner=parent,
|
2021-02-06 14:27:06 +01:00
|
|
|
acting_user=None,
|
2020-07-08 01:04:07 +02:00
|
|
|
)
|
|
|
|
bot.last_reminder = None
|
|
|
|
|
|
|
|
sender = bot
|
|
|
|
client = make_client(name="test suite")
|
2021-02-12 08:20:45 +01:00
|
|
|
stream_name = "Россия"
|
|
|
|
topic_name = "issue"
|
2020-07-08 01:04:07 +02:00
|
|
|
addressee = Addressee.for_stream_name(stream_name, topic_name)
|
2021-02-12 08:20:45 +01:00
|
|
|
message_content = "whatever"
|
2020-07-08 01:04:07 +02:00
|
|
|
old_count = message_stream_count(parent)
|
|
|
|
|
|
|
|
# Try sending to stream that doesn't exist sends a reminder to
|
|
|
|
# the sender
|
|
|
|
with self.assertRaises(JsonableError):
|
|
|
|
check_message(sender, client, addressee, message_content)
|
|
|
|
|
|
|
|
new_count = message_stream_count(parent)
|
|
|
|
self.assertEqual(new_count, old_count + 1)
|
2024-04-16 17:58:08 +02:00
|
|
|
self.assertIn("that channel does not exist.", most_recent_message(parent).content)
|
2020-07-08 01:04:07 +02:00
|
|
|
|
|
|
|
# Try sending to stream that exists with no subscribers soon
|
|
|
|
# after; due to rate-limiting, this should send nothing.
|
|
|
|
self.make_stream(stream_name)
|
|
|
|
ret = check_message(sender, client, addressee, message_content)
|
|
|
|
new_count = message_stream_count(parent)
|
|
|
|
self.assertEqual(new_count, old_count + 1)
|
|
|
|
|
|
|
|
# Try sending to stream that exists with no subscribers longer
|
|
|
|
# after; this should send an error to the bot owner that the
|
|
|
|
# stream doesn't exist
|
2021-02-12 08:19:30 +01:00
|
|
|
assert sender.last_reminder is not None
|
2023-11-19 19:45:19 +01:00
|
|
|
sender.last_reminder = sender.last_reminder - timedelta(hours=1)
|
2020-07-08 01:04:07 +02:00
|
|
|
sender.save(update_fields=["last_reminder"])
|
|
|
|
ret = check_message(sender, client, addressee, message_content)
|
|
|
|
|
|
|
|
new_count = message_stream_count(parent)
|
|
|
|
self.assertEqual(new_count, old_count + 2)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(ret.message.sender.email, "othello-bot@zulip.com")
|
2020-07-08 01:04:07 +02:00
|
|
|
self.assertIn("does not have any subscribers", most_recent_message(parent).content)
|
|
|
|
|
|
|
|
def test_bot_pm_error_handling(self) -> None:
|
|
|
|
# This just test some defensive code.
|
2021-02-12 08:20:45 +01:00
|
|
|
cordelia = self.example_user("cordelia")
|
2020-07-08 01:04:07 +02:00
|
|
|
test_bot = self.create_test_bot(
|
2021-02-12 08:20:45 +01:00
|
|
|
short_name="test",
|
2020-07-08 01:04:07 +02:00
|
|
|
user_profile=cordelia,
|
|
|
|
)
|
2021-02-12 08:20:45 +01:00
|
|
|
content = "whatever"
|
2020-07-08 01:04:07 +02:00
|
|
|
good_realm = test_bot.realm
|
|
|
|
wrong_realm = get_realm("zephyr")
|
|
|
|
wrong_sender = cordelia
|
|
|
|
|
|
|
|
send_rate_limited_pm_notification_to_bot_owner(test_bot, wrong_realm, content)
|
|
|
|
self.assertEqual(test_bot.last_reminder, None)
|
|
|
|
|
|
|
|
send_rate_limited_pm_notification_to_bot_owner(wrong_sender, good_realm, content)
|
|
|
|
self.assertEqual(test_bot.last_reminder, None)
|
|
|
|
|
|
|
|
test_bot.realm.deactivated = True
|
|
|
|
send_rate_limited_pm_notification_to_bot_owner(test_bot, good_realm, content)
|
|
|
|
self.assertEqual(test_bot.last_reminder, None)
|
2021-10-18 08:44:29 +02:00
|
|
|
|
|
|
|
def test_no_topic_message(self) -> None:
|
|
|
|
realm = get_realm("zulip")
|
|
|
|
sender = self.example_user("iago")
|
|
|
|
client = make_client(name="test suite")
|
|
|
|
stream = get_stream("Denmark", realm)
|
|
|
|
topic_name = "(no topic)"
|
|
|
|
message_content = "whatever"
|
|
|
|
addressee = Addressee.for_stream(stream, topic_name)
|
|
|
|
|
|
|
|
do_set_realm_property(realm, "mandatory_topics", True, acting_user=None)
|
|
|
|
realm.refresh_from_db()
|
|
|
|
|
|
|
|
with self.assertRaisesRegex(JsonableError, "Topics are required in this organization"):
|
|
|
|
check_message(sender, client, addressee, message_content, realm)
|
|
|
|
|
|
|
|
do_set_realm_property(realm, "mandatory_topics", False, acting_user=None)
|
|
|
|
realm.refresh_from_db()
|
|
|
|
ret = check_message(sender, client, addressee, message_content, realm)
|
|
|
|
self.assertEqual(ret.message.sender.id, sender.id)
|