mirror of
https://github.com/zulip/zulip.git
synced 2025-11-02 13:03:29 +00:00
tests: Extract test_message_send.py for message sending tests.
This commit extracts out MessagePOSTTest class from test_messages.py intially. In future commits other related message sending tests will be moved from test_messages.py to test_message_send.py.
This commit is contained in:
918
zerver/tests/test_message_send.py
Normal file
918
zerver/tests/test_message_send.py
Normal file
@@ -0,0 +1,918 @@
|
||||
import datetime
|
||||
from typing import Any, Optional
|
||||
from unittest import mock
|
||||
|
||||
import ujson
|
||||
from django.db import IntegrityError
|
||||
from django.utils.timezone import now as timezone_now
|
||||
|
||||
from zerver.decorator import JsonableError
|
||||
from zerver.lib.actions import (
|
||||
create_mirror_user_if_needed,
|
||||
do_change_stream_post_policy,
|
||||
do_create_user,
|
||||
do_deactivate_user,
|
||||
do_set_realm_property,
|
||||
internal_send_stream_message,
|
||||
)
|
||||
from zerver.lib.create_user import create_user_profile
|
||||
from zerver.lib.message import get_recent_private_conversations
|
||||
from zerver.lib.test_classes import ZulipTestCase
|
||||
from zerver.lib.test_helpers import reset_emails_in_zulip_realm
|
||||
from zerver.lib.timestamp import datetime_to_timestamp
|
||||
from zerver.models import (
|
||||
MAX_MESSAGE_LENGTH,
|
||||
MAX_TOPIC_NAME_LENGTH,
|
||||
Stream,
|
||||
UserProfile,
|
||||
get_huddle_recipient,
|
||||
get_realm,
|
||||
get_stream,
|
||||
get_system_bot,
|
||||
get_user,
|
||||
)
|
||||
from zerver.views.message_send import InvalidMirrorInput
|
||||
|
||||
|
||||
class MessagePOSTTest(ZulipTestCase):
|
||||
|
||||
def _send_and_verify_message(self, user: UserProfile, stream_name: str, error_msg: Optional[str]=None) -> None:
|
||||
if error_msg is None:
|
||||
msg_id = self.send_stream_message(user, stream_name)
|
||||
result = self.api_get(user, '/json/messages/' + str(msg_id))
|
||||
self.assert_json_success(result)
|
||||
else:
|
||||
with self.assertRaisesRegex(JsonableError, error_msg):
|
||||
self.send_stream_message(user, stream_name)
|
||||
|
||||
def test_message_to_self(self) -> None:
|
||||
"""
|
||||
Sending a message to a stream to which you are subscribed is
|
||||
successful.
|
||||
"""
|
||||
self.login('hamlet')
|
||||
result = self.client_post("/json/messages", {"type": "stream",
|
||||
"to": "Verona",
|
||||
"client": "test suite",
|
||||
"content": "Test message",
|
||||
"topic": "Test topic"})
|
||||
self.assert_json_success(result)
|
||||
|
||||
def test_api_message_to_self(self) -> None:
|
||||
"""
|
||||
Same as above, but for the API view
|
||||
"""
|
||||
user = self.example_user('hamlet')
|
||||
result = self.api_post(user, "/api/v1/messages", {"type": "stream",
|
||||
"to": "Verona",
|
||||
"client": "test suite",
|
||||
"content": "Test message",
|
||||
"topic": "Test topic"})
|
||||
self.assert_json_success(result)
|
||||
|
||||
def test_message_to_stream_with_nonexistent_id(self) -> None:
|
||||
cordelia = self.example_user('cordelia')
|
||||
bot = self.create_test_bot(
|
||||
short_name='whatever',
|
||||
user_profile=cordelia,
|
||||
)
|
||||
result = self.api_post(
|
||||
bot, "/api/v1/messages",
|
||||
{
|
||||
"type": "stream",
|
||||
"to": ujson.dumps([99999]),
|
||||
"client": "test suite",
|
||||
"content": "Stream message by ID.",
|
||||
"topic": "Test topic for stream ID message",
|
||||
},
|
||||
)
|
||||
self.assert_json_error(result, "Stream with ID '99999' does not exist")
|
||||
|
||||
msg = self.get_last_message()
|
||||
expected = ("Your bot `whatever-bot@zulip.testserver` tried to send a message to "
|
||||
"stream ID 99999, but there is no stream with that ID.")
|
||||
self.assertEqual(msg.content, expected)
|
||||
|
||||
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.
|
||||
"""
|
||||
self.login('hamlet')
|
||||
realm = get_realm('zulip')
|
||||
stream = get_stream('Verona', realm)
|
||||
result = self.client_post("/json/messages", {"type": "stream",
|
||||
"to": ujson.dumps([stream.id]),
|
||||
"client": "test suite",
|
||||
"content": "Stream message by ID.",
|
||||
"topic": "Test topic for stream ID message"})
|
||||
self.assert_json_success(result)
|
||||
sent_message = self.get_last_message()
|
||||
self.assertEqual(sent_message.content, "Stream message by ID.")
|
||||
|
||||
def test_sending_message_as_stream_post_policy_admins(self) -> None:
|
||||
"""
|
||||
Sending messages to streams which only the admins can create and post to.
|
||||
"""
|
||||
admin_profile = self.example_user("iago")
|
||||
self.login_user(admin_profile)
|
||||
|
||||
stream_name = "Verona"
|
||||
stream = get_stream(stream_name, admin_profile.realm)
|
||||
do_change_stream_post_policy(stream, Stream.STREAM_POST_POLICY_ADMINS)
|
||||
|
||||
# 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(
|
||||
short_name='whatever1',
|
||||
full_name='whatever1',
|
||||
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
|
||||
self._send_and_verify_message(non_admin_profile, stream_name,
|
||||
"Only organization administrators can send to this stream.")
|
||||
non_admin_owned_bot = self.create_test_bot(
|
||||
short_name='whatever2',
|
||||
full_name='whatever2',
|
||||
user_profile=non_admin_profile,
|
||||
)
|
||||
self._send_and_verify_message(non_admin_owned_bot, stream_name,
|
||||
"Only organization administrators can send to this stream.")
|
||||
|
||||
# Bots without owner (except cross realm bot) cannot send to announcement only streams
|
||||
bot_without_owner = do_create_user(
|
||||
email='free-bot@zulip.testserver',
|
||||
password='',
|
||||
realm=non_admin_profile.realm,
|
||||
full_name='freebot',
|
||||
short_name='freebot',
|
||||
bot_type=UserProfile.DEFAULT_BOT,
|
||||
)
|
||||
self._send_and_verify_message(bot_without_owner, stream_name,
|
||||
"Only organization administrators can send to this stream.")
|
||||
|
||||
# Cross realm bots should be allowed
|
||||
notification_bot = get_system_bot("notification-bot@zulip.com")
|
||||
internal_send_stream_message(stream.realm, notification_bot, stream,
|
||||
'Test topic', 'Test message by notification bot')
|
||||
self.assertEqual(self.get_last_message().content, 'Test message by notification bot')
|
||||
|
||||
def test_sending_message_as_stream_post_policy_restrict_new_members(self) -> None:
|
||||
"""
|
||||
Sending messages to streams which new members cannot create and post to.
|
||||
"""
|
||||
admin_profile = self.example_user("iago")
|
||||
self.login_user(admin_profile)
|
||||
|
||||
do_set_realm_property(admin_profile.realm, 'waiting_period_threshold', 10)
|
||||
admin_profile.date_joined = timezone_now() - datetime.timedelta(days=9)
|
||||
admin_profile.save()
|
||||
self.assertTrue(admin_profile.is_new_member)
|
||||
self.assertTrue(admin_profile.is_realm_admin)
|
||||
|
||||
stream_name = "Verona"
|
||||
stream = get_stream(stream_name, admin_profile.realm)
|
||||
do_change_stream_post_policy(stream, Stream.STREAM_POST_POLICY_RESTRICT_NEW_MEMBERS)
|
||||
|
||||
# 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(
|
||||
short_name='whatever1',
|
||||
full_name='whatever1',
|
||||
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_admin_profile.date_joined = timezone_now() - datetime.timedelta(days=9)
|
||||
non_admin_profile.save()
|
||||
self.assertTrue(non_admin_profile.is_new_member)
|
||||
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
|
||||
self._send_and_verify_message(non_admin_profile, stream_name,
|
||||
"New members cannot send to this stream.")
|
||||
non_admin_owned_bot = self.create_test_bot(
|
||||
short_name='whatever2',
|
||||
full_name='whatever2',
|
||||
user_profile=non_admin_profile,
|
||||
)
|
||||
self._send_and_verify_message(non_admin_owned_bot, stream_name,
|
||||
"New members cannot send to this stream.")
|
||||
|
||||
# Bots without owner (except cross realm bot) cannot send to announcement only stream
|
||||
bot_without_owner = do_create_user(
|
||||
email='free-bot@zulip.testserver',
|
||||
password='',
|
||||
realm=non_admin_profile.realm,
|
||||
full_name='freebot',
|
||||
short_name='freebot',
|
||||
bot_type=UserProfile.DEFAULT_BOT,
|
||||
)
|
||||
self._send_and_verify_message(bot_without_owner, stream_name,
|
||||
"New members cannot send to this stream.")
|
||||
|
||||
# Cross realm bots should be allowed
|
||||
notification_bot = get_system_bot("notification-bot@zulip.com")
|
||||
internal_send_stream_message(stream.realm, notification_bot, stream,
|
||||
'Test topic', 'Test message by notification bot')
|
||||
self.assertEqual(self.get_last_message().content, 'Test message by notification bot')
|
||||
|
||||
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.
|
||||
"""
|
||||
user = self.example_user('hamlet')
|
||||
user.default_sending_stream_id = get_stream('Verona', user.realm).id
|
||||
user.save()
|
||||
result = self.api_post(user, "/api/v1/messages", {"type": "stream",
|
||||
"client": "test suite",
|
||||
"content": "Test message no to",
|
||||
"topic": "Test topic"})
|
||||
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.
|
||||
"""
|
||||
self.login('hamlet')
|
||||
self.assertFalse(Stream.objects.filter(name="nonexistent_stream"))
|
||||
result = self.client_post("/json/messages", {"type": "stream",
|
||||
"to": "nonexistent_stream",
|
||||
"client": "test suite",
|
||||
"content": "Test message",
|
||||
"topic": "Test topic"})
|
||||
self.assert_json_error(result, "Stream 'nonexistent_stream' does not exist")
|
||||
|
||||
def test_message_to_nonexistent_stream_with_bad_characters(self) -> None:
|
||||
"""
|
||||
Nonexistent stream name with bad characters should be escaped properly.
|
||||
"""
|
||||
self.login('hamlet')
|
||||
self.assertFalse(Stream.objects.filter(name="""&<"'><non-existent>"""))
|
||||
result = self.client_post("/json/messages", {"type": "stream",
|
||||
"to": """&<"'><non-existent>""",
|
||||
"client": "test suite",
|
||||
"content": "Test message",
|
||||
"topic": "Test topic"})
|
||||
self.assert_json_error(result, "Stream '&<"'><non-existent>' does not exist")
|
||||
|
||||
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)
|
||||
othello = self.example_user('othello')
|
||||
result = self.client_post("/json/messages", {"type": "private",
|
||||
"content": "Test message",
|
||||
"client": "test suite",
|
||||
"to": othello.email})
|
||||
self.assert_json_success(result)
|
||||
message_id = ujson.loads(result.content.decode())['id']
|
||||
|
||||
recent_conversations = get_recent_private_conversations(user_profile)
|
||||
self.assertEqual(len(recent_conversations), 1)
|
||||
recent_conversation = list(recent_conversations.values())[0]
|
||||
recipient_id = list(recent_conversations.keys())[0]
|
||||
self.assertEqual(set(recent_conversation['user_ids']), {othello.id})
|
||||
self.assertEqual(recent_conversation['max_message_id'], message_id)
|
||||
|
||||
# Now send a message to yourself and see how that interacts with the data structure
|
||||
result = self.client_post("/json/messages", {"type": "private",
|
||||
"content": "Test message",
|
||||
"client": "test suite",
|
||||
"to": user_profile.email})
|
||||
self.assert_json_success(result)
|
||||
self_message_id = ujson.loads(result.content.decode())['id']
|
||||
|
||||
recent_conversations = get_recent_private_conversations(user_profile)
|
||||
self.assertEqual(len(recent_conversations), 2)
|
||||
recent_conversation = recent_conversations[recipient_id]
|
||||
self.assertEqual(set(recent_conversation['user_ids']), {othello.id})
|
||||
self.assertEqual(recent_conversation['max_message_id'], message_id)
|
||||
|
||||
# Now verify we have the appropriate self-pm data structure
|
||||
del recent_conversations[recipient_id]
|
||||
recent_conversation = list(recent_conversations.values())[0]
|
||||
recipient_id = list(recent_conversations.keys())[0]
|
||||
self.assertEqual(set(recent_conversation['user_ids']), set())
|
||||
self.assertEqual(recent_conversation['max_message_id'], self_message_id)
|
||||
|
||||
def test_personal_message_by_id(self) -> None:
|
||||
"""
|
||||
Sending a personal message to a valid user ID is successful.
|
||||
"""
|
||||
self.login('hamlet')
|
||||
result = self.client_post(
|
||||
"/json/messages",
|
||||
{
|
||||
"type": "private",
|
||||
"content": "Test message",
|
||||
"client": "test suite",
|
||||
"to": ujson.dumps([self.example_user("othello").id]),
|
||||
},
|
||||
)
|
||||
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").id)
|
||||
|
||||
def test_group_personal_message_by_id(self) -> None:
|
||||
"""
|
||||
Sending a personal message to a valid user ID is successful.
|
||||
"""
|
||||
self.login('hamlet')
|
||||
result = self.client_post(
|
||||
"/json/messages",
|
||||
{
|
||||
"type": "private",
|
||||
"content": "Test message",
|
||||
"client": "test suite",
|
||||
"to": ujson.dumps([self.example_user("othello").id,
|
||||
self.example_user("cordelia").id]),
|
||||
},
|
||||
)
|
||||
self.assert_json_success(result)
|
||||
|
||||
msg = self.get_last_message()
|
||||
self.assertEqual("Test message", msg.content)
|
||||
self.assertEqual(msg.recipient_id, get_huddle_recipient(
|
||||
{self.example_user("hamlet").id,
|
||||
self.example_user("othello").id,
|
||||
self.example_user("cordelia").id}).id,
|
||||
)
|
||||
|
||||
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.
|
||||
"""
|
||||
hamlet = self.example_user('hamlet')
|
||||
othello = self.example_user('othello')
|
||||
self.login_user(hamlet)
|
||||
result = self.client_post("/json/messages", {
|
||||
"type": "private",
|
||||
"content": "Test message",
|
||||
"client": "test suite",
|
||||
"to": ujson.dumps([hamlet.id, othello.id])})
|
||||
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.
|
||||
"""
|
||||
self.login('hamlet')
|
||||
result = self.client_post("/json/messages", {"type": "private",
|
||||
"content": "Test message",
|
||||
"client": "test suite",
|
||||
"to": "nonexistent"})
|
||||
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.
|
||||
"""
|
||||
othello = self.example_user('othello')
|
||||
cordelia = self.example_user('cordelia')
|
||||
do_deactivate_user(othello)
|
||||
self.login('hamlet')
|
||||
|
||||
result = self.client_post("/json/messages", {
|
||||
"type": "private",
|
||||
"content": "Test message",
|
||||
"client": "test suite",
|
||||
"to": ujson.dumps([othello.id])})
|
||||
self.assert_json_error(result, f"'{othello.email}' is no longer using Zulip.")
|
||||
|
||||
result = self.client_post("/json/messages", {
|
||||
"type": "private",
|
||||
"content": "Test message",
|
||||
"client": "test suite",
|
||||
"to": ujson.dumps([othello.id, cordelia.id])})
|
||||
self.assert_json_error(result, f"'{othello.email}' is no longer using Zulip.")
|
||||
|
||||
def test_invalid_type(self) -> None:
|
||||
"""
|
||||
Sending a message of unknown type returns error JSON.
|
||||
"""
|
||||
self.login('hamlet')
|
||||
othello = self.example_user('othello')
|
||||
result = self.client_post("/json/messages", {"type": "invalid type",
|
||||
"content": "Test message",
|
||||
"client": "test suite",
|
||||
"to": othello.email})
|
||||
self.assert_json_error(result, "Invalid message type")
|
||||
|
||||
def test_empty_message(self) -> None:
|
||||
"""
|
||||
Sending a message that is empty or only whitespace should fail
|
||||
"""
|
||||
self.login('hamlet')
|
||||
othello = self.example_user('othello')
|
||||
result = self.client_post("/json/messages", {"type": "private",
|
||||
"content": " ",
|
||||
"client": "test suite",
|
||||
"to": othello.email})
|
||||
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
|
||||
"""
|
||||
self.login('hamlet')
|
||||
result = self.client_post("/json/messages", {"type": "stream",
|
||||
"to": "Verona",
|
||||
"client": "test suite",
|
||||
"content": "Test message",
|
||||
"topic": ""})
|
||||
self.assert_json_error(result, "Topic can't be empty")
|
||||
|
||||
def test_missing_topic(self) -> None:
|
||||
"""
|
||||
Sending a message without topic should fail
|
||||
"""
|
||||
self.login('hamlet')
|
||||
result = self.client_post("/json/messages", {"type": "stream",
|
||||
"to": "Verona",
|
||||
"client": "test suite",
|
||||
"content": "Test message"})
|
||||
self.assert_json_error(result, "Missing topic")
|
||||
|
||||
def test_invalid_message_type(self) -> None:
|
||||
"""
|
||||
Messages other than the type of "private" or "stream" are considered as invalid
|
||||
"""
|
||||
self.login('hamlet')
|
||||
result = self.client_post("/json/messages", {"type": "invalid",
|
||||
"to": "Verona",
|
||||
"client": "test suite",
|
||||
"content": "Test message",
|
||||
"topic": "Test topic"})
|
||||
self.assert_json_error(result, "Invalid message type")
|
||||
|
||||
def test_private_message_without_recipients(self) -> None:
|
||||
"""
|
||||
Sending private message without recipients should fail
|
||||
"""
|
||||
self.login('hamlet')
|
||||
result = self.client_post("/json/messages", {"type": "private",
|
||||
"content": "Test content",
|
||||
"client": "test suite",
|
||||
"to": ""})
|
||||
self.assert_json_error(result, "Message must have recipients")
|
||||
|
||||
def test_mirrored_huddle(self) -> None:
|
||||
"""
|
||||
Sending a mirrored huddle message works
|
||||
"""
|
||||
result = self.api_post(self.mit_user("starnine"),
|
||||
"/json/messages", {"type": "private",
|
||||
"sender": self.mit_email("sipbtest"),
|
||||
"content": "Test message",
|
||||
"client": "zephyr_mirror",
|
||||
"to": ujson.dumps([self.mit_email("starnine"),
|
||||
self.mit_email("espuser")])},
|
||||
subdomain="zephyr")
|
||||
self.assert_json_success(result)
|
||||
|
||||
def test_mirrored_personal(self) -> None:
|
||||
"""
|
||||
Sending a mirrored personal message works
|
||||
"""
|
||||
result = self.api_post(self.mit_user("starnine"),
|
||||
"/json/messages", {"type": "private",
|
||||
"sender": self.mit_email("sipbtest"),
|
||||
"content": "Test message",
|
||||
"client": "zephyr_mirror",
|
||||
"to": self.mit_email("starnine")},
|
||||
subdomain="zephyr")
|
||||
self.assert_json_success(result)
|
||||
|
||||
def test_mirrored_personal_browser(self) -> None:
|
||||
"""
|
||||
Sending a mirrored personal message via the browser should not work.
|
||||
"""
|
||||
user = self.mit_user('starnine')
|
||||
self.login_user(user)
|
||||
result = self.client_post("/json/messages",
|
||||
{"type": "private",
|
||||
"sender": self.mit_email("sipbtest"),
|
||||
"content": "Test message",
|
||||
"client": "zephyr_mirror",
|
||||
"to": self.mit_email("starnine")},
|
||||
subdomain="zephyr")
|
||||
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.
|
||||
"""
|
||||
result = self.api_post(self.mit_user("starnine"), "/api/v1/messages",
|
||||
{"type": "private",
|
||||
"sender": self.mit_email("sipbtest"),
|
||||
"content": "Test message",
|
||||
"client": "zephyr_mirror",
|
||||
"to": self.mit_email("espuser")},
|
||||
subdomain="zephyr")
|
||||
self.assert_json_error(result, "User not authorized for this query")
|
||||
|
||||
def test_duplicated_mirrored_huddle(self) -> None:
|
||||
"""
|
||||
Sending two mirrored huddles in the row return the same ID
|
||||
"""
|
||||
msg = {"type": "private",
|
||||
"sender": self.mit_email("sipbtest"),
|
||||
"content": "Test message",
|
||||
"client": "zephyr_mirror",
|
||||
"to": ujson.dumps([self.mit_email("espuser"),
|
||||
self.mit_email("starnine")])}
|
||||
|
||||
with mock.patch('DNS.dnslookup', return_value=[['starnine:*:84233:101:Athena Consulting Exchange User,,,:/mit/starnine:/bin/bash']]):
|
||||
result1 = self.api_post(self.mit_user("starnine"), "/api/v1/messages", msg,
|
||||
subdomain="zephyr")
|
||||
self.assert_json_success(result1)
|
||||
|
||||
with mock.patch('DNS.dnslookup', return_value=[['espuser:*:95494:101:Esp Classroom,,,:/mit/espuser:/bin/athena/bash']]):
|
||||
result2 = self.api_post(self.mit_user("espuser"), "/api/v1/messages", msg,
|
||||
subdomain="zephyr")
|
||||
self.assert_json_success(result2)
|
||||
|
||||
self.assertEqual(ujson.loads(result1.content)['id'],
|
||||
ujson.loads(result2.content)['id'])
|
||||
|
||||
def test_message_with_null_bytes(self) -> None:
|
||||
"""
|
||||
A message with null bytes in it is handled.
|
||||
"""
|
||||
self.login('hamlet')
|
||||
post_data = {"type": "stream", "to": "Verona", "client": "test suite",
|
||||
"content": " I like null bytes \x00 in my content", "topic": "Test topic"}
|
||||
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.
|
||||
"""
|
||||
self.login('hamlet')
|
||||
post_data = {"type": "stream", "to": "Verona", "client": "test suite",
|
||||
"content": " I like whitespace at the end! \n\n \n", "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, " I like whitespace at the end!")
|
||||
|
||||
def test_long_message(self) -> None:
|
||||
"""
|
||||
Sending a message longer than the maximum message length succeeds but is
|
||||
truncated.
|
||||
"""
|
||||
self.login('hamlet')
|
||||
long_message = "A" * (MAX_MESSAGE_LENGTH + 1)
|
||||
post_data = {"type": "stream", "to": "Verona", "client": "test suite",
|
||||
"content": long_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,
|
||||
"A" * (MAX_MESSAGE_LENGTH - 20) + "\n[message truncated]")
|
||||
|
||||
def test_long_topic(self) -> None:
|
||||
"""
|
||||
Sending a message with a topic longer than the maximum topic length
|
||||
succeeds, but the topic is truncated.
|
||||
"""
|
||||
self.login('hamlet')
|
||||
long_topic = "A" * (MAX_TOPIC_NAME_LENGTH + 1)
|
||||
post_data = {"type": "stream", "to": "Verona", "client": "test suite",
|
||||
"content": "test content", "topic": long_topic}
|
||||
result = self.client_post("/json/messages", post_data)
|
||||
self.assert_json_success(result)
|
||||
|
||||
sent_message = self.get_last_message()
|
||||
self.assertEqual(sent_message.topic_name(),
|
||||
"A" * (MAX_TOPIC_NAME_LENGTH - 3) + "...")
|
||||
|
||||
def test_send_forged_message_as_not_superuser(self) -> None:
|
||||
self.login('hamlet')
|
||||
result = self.client_post("/json/messages", {"type": "stream",
|
||||
"to": "Verona",
|
||||
"client": "test suite",
|
||||
"content": "Test message",
|
||||
"topic": "Test topic",
|
||||
"forged": "true"})
|
||||
self.assert_json_error(result, "User not authorized for this query")
|
||||
|
||||
def test_send_message_as_not_superuser_to_different_domain(self) -> None:
|
||||
self.login('hamlet')
|
||||
result = self.client_post("/json/messages", {"type": "stream",
|
||||
"to": "Verona",
|
||||
"client": "test suite",
|
||||
"content": "Test message",
|
||||
"topic": "Test topic",
|
||||
"realm_str": "mit"})
|
||||
self.assert_json_error(result, "User not authorized for this query")
|
||||
|
||||
def test_send_message_as_superuser_to_domain_that_dont_exist(self) -> None:
|
||||
user = self.example_user("default_bot")
|
||||
password = "test_password"
|
||||
user.set_password(password)
|
||||
user.is_api_super_user = True
|
||||
user.save()
|
||||
result = self.api_post(user,
|
||||
"/api/v1/messages", {"type": "stream",
|
||||
"to": "Verona",
|
||||
"client": "test suite",
|
||||
"content": "Test message",
|
||||
"topic": "Test topic",
|
||||
"realm_str": "non-existing"})
|
||||
user.is_api_super_user = False
|
||||
user.save()
|
||||
self.assert_json_error(result, "Unknown organization 'non-existing'")
|
||||
|
||||
def test_send_message_when_sender_is_not_set(self) -> None:
|
||||
result = self.api_post(self.mit_user("starnine"), "/api/v1/messages",
|
||||
{"type": "private",
|
||||
"content": "Test message",
|
||||
"client": "zephyr_mirror",
|
||||
"to": self.mit_email("starnine")},
|
||||
subdomain="zephyr")
|
||||
self.assert_json_error(result, "Missing sender")
|
||||
|
||||
def test_send_message_as_not_superuser_when_type_is_not_private(self) -> None:
|
||||
result = self.api_post(self.mit_user("starnine"), "/api/v1/messages",
|
||||
{"type": "not-private",
|
||||
"sender": self.mit_email("sipbtest"),
|
||||
"content": "Test message",
|
||||
"client": "zephyr_mirror",
|
||||
"to": self.mit_email("starnine")},
|
||||
subdomain="zephyr")
|
||||
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(
|
||||
self, create_mirrored_message_users_mock: Any) -> None:
|
||||
create_mirrored_message_users_mock.side_effect = InvalidMirrorInput()
|
||||
result = self.api_post(self.mit_user("starnine"), "/api/v1/messages",
|
||||
{"type": "private",
|
||||
"sender": self.mit_email("sipbtest"),
|
||||
"content": "Test message",
|
||||
"client": "zephyr_mirror",
|
||||
"to": self.mit_email("starnine")},
|
||||
subdomain="zephyr")
|
||||
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(
|
||||
self, create_mirrored_message_users_mock: Any) -> None:
|
||||
create_mirrored_message_users_mock.return_value = mock.Mock()
|
||||
user = self.mit_user("starnine")
|
||||
user.realm.string_id = 'notzephyr'
|
||||
user.realm.save()
|
||||
result = self.api_post(user, "/api/v1/messages",
|
||||
{"type": "private",
|
||||
"sender": self.mit_email("sipbtest"),
|
||||
"content": "Test message",
|
||||
"client": "zephyr_mirror",
|
||||
"to": user.email},
|
||||
subdomain="notzephyr")
|
||||
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(
|
||||
self, create_mirrored_message_users_mock: Any) -> None:
|
||||
create_mirrored_message_users_mock.return_value = mock.Mock()
|
||||
user = self.mit_user("starnine")
|
||||
self.login_user(user)
|
||||
result = self.api_post(user, "/api/v1/messages",
|
||||
{"type": "private",
|
||||
"sender": self.mit_email("sipbtest"),
|
||||
"content": "Test message",
|
||||
"client": "zephyr_mirror",
|
||||
"to": ujson.dumps([user.id])},
|
||||
subdomain="zephyr")
|
||||
self.assert_json_error(result, "Mirroring not allowed with recipient user IDs")
|
||||
|
||||
def test_send_message_irc_mirror(self) -> None:
|
||||
reset_emails_in_zulip_realm()
|
||||
self.login('hamlet')
|
||||
bot_info = {
|
||||
'full_name': 'IRC bot',
|
||||
'short_name': 'irc',
|
||||
}
|
||||
result = self.client_post("/json/bots", bot_info)
|
||||
self.assert_json_success(result)
|
||||
|
||||
email = "irc-bot@zulip.testserver"
|
||||
user = get_user(email, get_realm('zulip'))
|
||||
user.is_api_super_user = True
|
||||
user.save()
|
||||
user = get_user(email, get_realm('zulip'))
|
||||
self.subscribe(user, "IRCland")
|
||||
|
||||
# Simulate a mirrored message with a slightly old timestamp.
|
||||
fake_date_sent = timezone_now() - datetime.timedelta(minutes=37)
|
||||
fake_timestamp = datetime_to_timestamp(fake_date_sent)
|
||||
|
||||
result = self.api_post(user, "/api/v1/messages", {"type": "stream",
|
||||
"forged": "true",
|
||||
"time": fake_timestamp,
|
||||
"sender": "irc-user@irc.zulip.com",
|
||||
"content": "Test message",
|
||||
"client": "irc_mirror",
|
||||
"topic": "from irc",
|
||||
"to": "IRCLand"})
|
||||
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
|
||||
fake_date_sent = timezone_now() - datetime.timedelta(minutes=22)
|
||||
fake_timestamp = datetime_to_timestamp(fake_date_sent)
|
||||
|
||||
result = self.api_post(user, "/api/v1/messages", {"type": "stream",
|
||||
"forged": "yes",
|
||||
"time": fake_timestamp,
|
||||
"sender": "irc-user@irc.zulip.com",
|
||||
"content": "Test message",
|
||||
"client": "irc_mirror",
|
||||
"topic": "from irc",
|
||||
"to": "IRCLand"})
|
||||
self.assert_json_success(result)
|
||||
|
||||
msg = self.get_last_message()
|
||||
self.assertEqual(int(datetime_to_timestamp(msg.date_sent)), int(fake_timestamp))
|
||||
|
||||
def test_unsubscribed_api_super_user(self) -> None:
|
||||
reset_emails_in_zulip_realm()
|
||||
|
||||
cordelia = self.example_user('cordelia')
|
||||
stream_name = 'private_stream'
|
||||
self.make_stream(stream_name, invite_only=True)
|
||||
|
||||
self.unsubscribe(cordelia, stream_name)
|
||||
|
||||
# As long as Cordelia is a super_user, she can send messages
|
||||
# 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(
|
||||
type="stream",
|
||||
to=stream_name,
|
||||
client=client,
|
||||
topic='whatever',
|
||||
content='whatever',
|
||||
forged=ujson.dumps(forged),
|
||||
)
|
||||
|
||||
# Only pass the 'sender' property when doing mirroring behavior.
|
||||
if forged:
|
||||
payload['sender'] = sender_email
|
||||
|
||||
cordelia.is_api_super_user = False
|
||||
cordelia.save()
|
||||
|
||||
result = self.api_post(cordelia, "/api/v1/messages", payload)
|
||||
self.assert_json_error_contains(result, 'authorized')
|
||||
|
||||
cordelia.is_api_super_user = True
|
||||
cordelia.save()
|
||||
|
||||
result = self.api_post(cordelia, "/api/v1/messages", payload)
|
||||
self.assert_json_success(result)
|
||||
|
||||
test_with(
|
||||
sender_email=cordelia.email,
|
||||
client='test suite',
|
||||
forged=False,
|
||||
)
|
||||
|
||||
test_with(
|
||||
sender_email='irc_person@zulip.com',
|
||||
client='irc_mirror',
|
||||
forged=True,
|
||||
)
|
||||
|
||||
def test_bot_can_send_to_owner_stream(self) -> None:
|
||||
cordelia = self.example_user('cordelia')
|
||||
bot = self.create_test_bot(
|
||||
short_name='whatever',
|
||||
user_profile=cordelia,
|
||||
)
|
||||
|
||||
stream_name = 'private_stream'
|
||||
self.make_stream(stream_name, invite_only=True)
|
||||
|
||||
payload = dict(
|
||||
type="stream",
|
||||
to=stream_name,
|
||||
client='test suite',
|
||||
topic='whatever',
|
||||
content='whatever',
|
||||
)
|
||||
|
||||
result = self.api_post(bot, "/api/v1/messages", payload)
|
||||
self.assert_json_error_contains(result, 'Not authorized to send')
|
||||
|
||||
# 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:
|
||||
notification_bot = self.notification_bot()
|
||||
stream = self.make_stream("notify_channel", get_realm("zulipinternal"))
|
||||
|
||||
result = self.api_post(notification_bot,
|
||||
"/api/v1/messages",
|
||||
{"type": "stream",
|
||||
"to": "notify_channel",
|
||||
"client": "test suite",
|
||||
"content": "Test message",
|
||||
"topic": "Test topic"},
|
||||
subdomain='zulipinternal')
|
||||
|
||||
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_create_mirror_user_despite_race(self) -> None:
|
||||
realm = get_realm('zulip')
|
||||
|
||||
email = 'fred@example.com'
|
||||
|
||||
email_to_full_name = lambda email: 'fred'
|
||||
|
||||
def create_user(**kwargs: Any) -> UserProfile:
|
||||
self.assertEqual(kwargs['full_name'], 'fred')
|
||||
self.assertEqual(kwargs['email'], email)
|
||||
self.assertEqual(kwargs['active'], False)
|
||||
self.assertEqual(kwargs['is_mirror_dummy'], True)
|
||||
# We create an actual user here to simulate a race.
|
||||
# We use the minimal, un-mocked function.
|
||||
kwargs['bot_type'] = None
|
||||
kwargs['bot_owner'] = None
|
||||
kwargs['tos_version'] = None
|
||||
kwargs['timezone'] = timezone_now()
|
||||
create_user_profile(**kwargs).save()
|
||||
raise IntegrityError()
|
||||
|
||||
with mock.patch('zerver.lib.actions.create_user',
|
||||
side_effect=create_user) as m:
|
||||
mirror_fred_user = create_mirror_user_if_needed(
|
||||
realm,
|
||||
email,
|
||||
email_to_full_name,
|
||||
)
|
||||
|
||||
self.assertEqual(mirror_fred_user.delivery_email, email)
|
||||
m.assert_called()
|
||||
|
||||
def test_guest_user(self) -> None:
|
||||
sender = self.example_user('polonius')
|
||||
|
||||
stream_name = 'public stream'
|
||||
self.make_stream(stream_name, invite_only=False)
|
||||
payload = dict(
|
||||
type="stream",
|
||||
to=stream_name,
|
||||
client='test suite',
|
||||
topic='whatever',
|
||||
content='whatever',
|
||||
)
|
||||
|
||||
# Guest user can't send message to unsubscribed public streams
|
||||
result = self.api_post(sender, "/api/v1/messages", payload)
|
||||
self.assert_json_error(result, "Not authorized to send to stream 'public stream'")
|
||||
|
||||
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)
|
||||
@@ -1,11 +1,10 @@
|
||||
import datetime
|
||||
import time
|
||||
from typing import Any, Dict, List, Optional, Set, Union
|
||||
from typing import Any, Dict, List, Set, Union
|
||||
from unittest import mock
|
||||
|
||||
import ujson
|
||||
from django.conf import settings
|
||||
from django.db import IntegrityError
|
||||
from django.db.models import Q
|
||||
from django.http import HttpResponse
|
||||
from django.test import override_settings
|
||||
@@ -17,14 +16,11 @@ from zerver.decorator import JsonableError
|
||||
from zerver.lib.actions import (
|
||||
check_message,
|
||||
check_send_stream_message,
|
||||
create_mirror_user_if_needed,
|
||||
do_add_alert_words,
|
||||
do_change_is_api_super_user,
|
||||
do_change_stream_invite_only,
|
||||
do_change_stream_post_policy,
|
||||
do_claim_attachments,
|
||||
do_create_user,
|
||||
do_deactivate_user,
|
||||
do_send_messages,
|
||||
do_set_realm_property,
|
||||
do_update_message,
|
||||
@@ -44,7 +40,6 @@ from zerver.lib.actions import (
|
||||
)
|
||||
from zerver.lib.addressee import Addressee
|
||||
from zerver.lib.cache import cache_delete, get_stream_cache_key, to_dict_cache_key_id
|
||||
from zerver.lib.create_user import create_user_profile
|
||||
from zerver.lib.markdown import MentionData
|
||||
from zerver.lib.markdown import version as markdown_version
|
||||
from zerver.lib.message import (
|
||||
@@ -74,17 +69,14 @@ from zerver.lib.test_helpers import (
|
||||
most_recent_message,
|
||||
most_recent_usermessage,
|
||||
queries_captured,
|
||||
reset_emails_in_zulip_realm,
|
||||
)
|
||||
from zerver.lib.timestamp import convert_to_UTC, datetime_to_timestamp
|
||||
from zerver.lib.timestamp import convert_to_UTC
|
||||
from zerver.lib.timezone import get_timezone
|
||||
from zerver.lib.topic import DB_TOPIC_NAME, TOPIC_LINKS
|
||||
from zerver.lib.types import DisplayRecipientT, UserDisplayRecipient
|
||||
from zerver.lib.upload import create_attachment
|
||||
from zerver.lib.url_encoding import near_message_url
|
||||
from zerver.models import (
|
||||
MAX_MESSAGE_LENGTH,
|
||||
MAX_TOPIC_NAME_LENGTH,
|
||||
Attachment,
|
||||
Message,
|
||||
Reaction,
|
||||
@@ -102,14 +94,12 @@ from zerver.models import (
|
||||
bulk_get_huddle_user_ids,
|
||||
flush_per_request_caches,
|
||||
get_display_recipient,
|
||||
get_huddle_recipient,
|
||||
get_huddle_user_ids,
|
||||
get_realm,
|
||||
get_stream,
|
||||
get_system_bot,
|
||||
get_user,
|
||||
)
|
||||
from zerver.views.message_send import InvalidMirrorInput
|
||||
|
||||
|
||||
class MiscMessageTest(ZulipTestCase):
|
||||
@@ -1359,890 +1349,6 @@ class SewMessageAndReactionTest(ZulipTestCase):
|
||||
self.assertTrue(data['id'])
|
||||
self.assertTrue(data['content'])
|
||||
|
||||
|
||||
class MessagePOSTTest(ZulipTestCase):
|
||||
|
||||
def _send_and_verify_message(self, user: UserProfile, stream_name: str, error_msg: Optional[str]=None) -> None:
|
||||
if error_msg is None:
|
||||
msg_id = self.send_stream_message(user, stream_name)
|
||||
result = self.api_get(user, '/json/messages/' + str(msg_id))
|
||||
self.assert_json_success(result)
|
||||
else:
|
||||
with self.assertRaisesRegex(JsonableError, error_msg):
|
||||
self.send_stream_message(user, stream_name)
|
||||
|
||||
def test_message_to_self(self) -> None:
|
||||
"""
|
||||
Sending a message to a stream to which you are subscribed is
|
||||
successful.
|
||||
"""
|
||||
self.login('hamlet')
|
||||
result = self.client_post("/json/messages", {"type": "stream",
|
||||
"to": "Verona",
|
||||
"client": "test suite",
|
||||
"content": "Test message",
|
||||
"topic": "Test topic"})
|
||||
self.assert_json_success(result)
|
||||
|
||||
def test_api_message_to_self(self) -> None:
|
||||
"""
|
||||
Same as above, but for the API view
|
||||
"""
|
||||
user = self.example_user('hamlet')
|
||||
result = self.api_post(user, "/api/v1/messages", {"type": "stream",
|
||||
"to": "Verona",
|
||||
"client": "test suite",
|
||||
"content": "Test message",
|
||||
"topic": "Test topic"})
|
||||
self.assert_json_success(result)
|
||||
|
||||
def test_message_to_stream_with_nonexistent_id(self) -> None:
|
||||
cordelia = self.example_user('cordelia')
|
||||
bot = self.create_test_bot(
|
||||
short_name='whatever',
|
||||
user_profile=cordelia,
|
||||
)
|
||||
result = self.api_post(
|
||||
bot, "/api/v1/messages",
|
||||
{
|
||||
"type": "stream",
|
||||
"to": ujson.dumps([99999]),
|
||||
"client": "test suite",
|
||||
"content": "Stream message by ID.",
|
||||
"topic": "Test topic for stream ID message",
|
||||
},
|
||||
)
|
||||
self.assert_json_error(result, "Stream with ID '99999' does not exist")
|
||||
|
||||
msg = self.get_last_message()
|
||||
expected = ("Your bot `whatever-bot@zulip.testserver` tried to send a message to "
|
||||
"stream ID 99999, but there is no stream with that ID.")
|
||||
self.assertEqual(msg.content, expected)
|
||||
|
||||
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.
|
||||
"""
|
||||
self.login('hamlet')
|
||||
realm = get_realm('zulip')
|
||||
stream = get_stream('Verona', realm)
|
||||
result = self.client_post("/json/messages", {"type": "stream",
|
||||
"to": ujson.dumps([stream.id]),
|
||||
"client": "test suite",
|
||||
"content": "Stream message by ID.",
|
||||
"topic": "Test topic for stream ID message"})
|
||||
self.assert_json_success(result)
|
||||
sent_message = self.get_last_message()
|
||||
self.assertEqual(sent_message.content, "Stream message by ID.")
|
||||
|
||||
def test_sending_message_as_stream_post_policy_admins(self) -> None:
|
||||
"""
|
||||
Sending messages to streams which only the admins can create and post to.
|
||||
"""
|
||||
admin_profile = self.example_user("iago")
|
||||
self.login_user(admin_profile)
|
||||
|
||||
stream_name = "Verona"
|
||||
stream = get_stream(stream_name, admin_profile.realm)
|
||||
do_change_stream_post_policy(stream, Stream.STREAM_POST_POLICY_ADMINS)
|
||||
|
||||
# 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(
|
||||
short_name='whatever1',
|
||||
full_name='whatever1',
|
||||
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
|
||||
self._send_and_verify_message(non_admin_profile, stream_name,
|
||||
"Only organization administrators can send to this stream.")
|
||||
non_admin_owned_bot = self.create_test_bot(
|
||||
short_name='whatever2',
|
||||
full_name='whatever2',
|
||||
user_profile=non_admin_profile,
|
||||
)
|
||||
self._send_and_verify_message(non_admin_owned_bot, stream_name,
|
||||
"Only organization administrators can send to this stream.")
|
||||
|
||||
# Bots without owner (except cross realm bot) cannot send to announcement only streams
|
||||
bot_without_owner = do_create_user(
|
||||
email='free-bot@zulip.testserver',
|
||||
password='',
|
||||
realm=non_admin_profile.realm,
|
||||
full_name='freebot',
|
||||
short_name='freebot',
|
||||
bot_type=UserProfile.DEFAULT_BOT,
|
||||
)
|
||||
self._send_and_verify_message(bot_without_owner, stream_name,
|
||||
"Only organization administrators can send to this stream.")
|
||||
|
||||
# Cross realm bots should be allowed
|
||||
notification_bot = get_system_bot("notification-bot@zulip.com")
|
||||
internal_send_stream_message(stream.realm, notification_bot, stream,
|
||||
'Test topic', 'Test message by notification bot')
|
||||
self.assertEqual(self.get_last_message().content, 'Test message by notification bot')
|
||||
|
||||
def test_sending_message_as_stream_post_policy_restrict_new_members(self) -> None:
|
||||
"""
|
||||
Sending messages to streams which new members cannot create and post to.
|
||||
"""
|
||||
admin_profile = self.example_user("iago")
|
||||
self.login_user(admin_profile)
|
||||
|
||||
do_set_realm_property(admin_profile.realm, 'waiting_period_threshold', 10)
|
||||
admin_profile.date_joined = timezone_now() - datetime.timedelta(days=9)
|
||||
admin_profile.save()
|
||||
self.assertTrue(admin_profile.is_new_member)
|
||||
self.assertTrue(admin_profile.is_realm_admin)
|
||||
|
||||
stream_name = "Verona"
|
||||
stream = get_stream(stream_name, admin_profile.realm)
|
||||
do_change_stream_post_policy(stream, Stream.STREAM_POST_POLICY_RESTRICT_NEW_MEMBERS)
|
||||
|
||||
# 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(
|
||||
short_name='whatever1',
|
||||
full_name='whatever1',
|
||||
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_admin_profile.date_joined = timezone_now() - datetime.timedelta(days=9)
|
||||
non_admin_profile.save()
|
||||
self.assertTrue(non_admin_profile.is_new_member)
|
||||
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
|
||||
self._send_and_verify_message(non_admin_profile, stream_name,
|
||||
"New members cannot send to this stream.")
|
||||
non_admin_owned_bot = self.create_test_bot(
|
||||
short_name='whatever2',
|
||||
full_name='whatever2',
|
||||
user_profile=non_admin_profile,
|
||||
)
|
||||
self._send_and_verify_message(non_admin_owned_bot, stream_name,
|
||||
"New members cannot send to this stream.")
|
||||
|
||||
# Bots without owner (except cross realm bot) cannot send to announcement only stream
|
||||
bot_without_owner = do_create_user(
|
||||
email='free-bot@zulip.testserver',
|
||||
password='',
|
||||
realm=non_admin_profile.realm,
|
||||
full_name='freebot',
|
||||
short_name='freebot',
|
||||
bot_type=UserProfile.DEFAULT_BOT,
|
||||
)
|
||||
self._send_and_verify_message(bot_without_owner, stream_name,
|
||||
"New members cannot send to this stream.")
|
||||
|
||||
# Cross realm bots should be allowed
|
||||
notification_bot = get_system_bot("notification-bot@zulip.com")
|
||||
internal_send_stream_message(stream.realm, notification_bot, stream,
|
||||
'Test topic', 'Test message by notification bot')
|
||||
self.assertEqual(self.get_last_message().content, 'Test message by notification bot')
|
||||
|
||||
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.
|
||||
"""
|
||||
user = self.example_user('hamlet')
|
||||
user.default_sending_stream_id = get_stream('Verona', user.realm).id
|
||||
user.save()
|
||||
result = self.api_post(user, "/api/v1/messages", {"type": "stream",
|
||||
"client": "test suite",
|
||||
"content": "Test message no to",
|
||||
"topic": "Test topic"})
|
||||
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.
|
||||
"""
|
||||
self.login('hamlet')
|
||||
self.assertFalse(Stream.objects.filter(name="nonexistent_stream"))
|
||||
result = self.client_post("/json/messages", {"type": "stream",
|
||||
"to": "nonexistent_stream",
|
||||
"client": "test suite",
|
||||
"content": "Test message",
|
||||
"topic": "Test topic"})
|
||||
self.assert_json_error(result, "Stream 'nonexistent_stream' does not exist")
|
||||
|
||||
def test_message_to_nonexistent_stream_with_bad_characters(self) -> None:
|
||||
"""
|
||||
Nonexistent stream name with bad characters should be escaped properly.
|
||||
"""
|
||||
self.login('hamlet')
|
||||
self.assertFalse(Stream.objects.filter(name="""&<"'><non-existent>"""))
|
||||
result = self.client_post("/json/messages", {"type": "stream",
|
||||
"to": """&<"'><non-existent>""",
|
||||
"client": "test suite",
|
||||
"content": "Test message",
|
||||
"topic": "Test topic"})
|
||||
self.assert_json_error(result, "Stream '&<"'><non-existent>' does not exist")
|
||||
|
||||
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)
|
||||
othello = self.example_user('othello')
|
||||
result = self.client_post("/json/messages", {"type": "private",
|
||||
"content": "Test message",
|
||||
"client": "test suite",
|
||||
"to": othello.email})
|
||||
self.assert_json_success(result)
|
||||
message_id = ujson.loads(result.content.decode())['id']
|
||||
|
||||
recent_conversations = get_recent_private_conversations(user_profile)
|
||||
self.assertEqual(len(recent_conversations), 1)
|
||||
recent_conversation = list(recent_conversations.values())[0]
|
||||
recipient_id = list(recent_conversations.keys())[0]
|
||||
self.assertEqual(set(recent_conversation['user_ids']), {othello.id})
|
||||
self.assertEqual(recent_conversation['max_message_id'], message_id)
|
||||
|
||||
# Now send a message to yourself and see how that interacts with the data structure
|
||||
result = self.client_post("/json/messages", {"type": "private",
|
||||
"content": "Test message",
|
||||
"client": "test suite",
|
||||
"to": user_profile.email})
|
||||
self.assert_json_success(result)
|
||||
self_message_id = ujson.loads(result.content.decode())['id']
|
||||
|
||||
recent_conversations = get_recent_private_conversations(user_profile)
|
||||
self.assertEqual(len(recent_conversations), 2)
|
||||
recent_conversation = recent_conversations[recipient_id]
|
||||
self.assertEqual(set(recent_conversation['user_ids']), {othello.id})
|
||||
self.assertEqual(recent_conversation['max_message_id'], message_id)
|
||||
|
||||
# Now verify we have the appropriate self-pm data structure
|
||||
del recent_conversations[recipient_id]
|
||||
recent_conversation = list(recent_conversations.values())[0]
|
||||
recipient_id = list(recent_conversations.keys())[0]
|
||||
self.assertEqual(set(recent_conversation['user_ids']), set())
|
||||
self.assertEqual(recent_conversation['max_message_id'], self_message_id)
|
||||
|
||||
def test_personal_message_by_id(self) -> None:
|
||||
"""
|
||||
Sending a personal message to a valid user ID is successful.
|
||||
"""
|
||||
self.login('hamlet')
|
||||
result = self.client_post(
|
||||
"/json/messages",
|
||||
{
|
||||
"type": "private",
|
||||
"content": "Test message",
|
||||
"client": "test suite",
|
||||
"to": ujson.dumps([self.example_user("othello").id]),
|
||||
},
|
||||
)
|
||||
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").id)
|
||||
|
||||
def test_group_personal_message_by_id(self) -> None:
|
||||
"""
|
||||
Sending a personal message to a valid user ID is successful.
|
||||
"""
|
||||
self.login('hamlet')
|
||||
result = self.client_post(
|
||||
"/json/messages",
|
||||
{
|
||||
"type": "private",
|
||||
"content": "Test message",
|
||||
"client": "test suite",
|
||||
"to": ujson.dumps([self.example_user("othello").id,
|
||||
self.example_user("cordelia").id]),
|
||||
},
|
||||
)
|
||||
self.assert_json_success(result)
|
||||
|
||||
msg = self.get_last_message()
|
||||
self.assertEqual("Test message", msg.content)
|
||||
self.assertEqual(msg.recipient_id, get_huddle_recipient(
|
||||
{self.example_user("hamlet").id,
|
||||
self.example_user("othello").id,
|
||||
self.example_user("cordelia").id}).id,
|
||||
)
|
||||
|
||||
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.
|
||||
"""
|
||||
hamlet = self.example_user('hamlet')
|
||||
othello = self.example_user('othello')
|
||||
self.login_user(hamlet)
|
||||
result = self.client_post("/json/messages", {
|
||||
"type": "private",
|
||||
"content": "Test message",
|
||||
"client": "test suite",
|
||||
"to": ujson.dumps([hamlet.id, othello.id])})
|
||||
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.
|
||||
"""
|
||||
self.login('hamlet')
|
||||
result = self.client_post("/json/messages", {"type": "private",
|
||||
"content": "Test message",
|
||||
"client": "test suite",
|
||||
"to": "nonexistent"})
|
||||
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.
|
||||
"""
|
||||
othello = self.example_user('othello')
|
||||
cordelia = self.example_user('cordelia')
|
||||
do_deactivate_user(othello)
|
||||
self.login('hamlet')
|
||||
|
||||
result = self.client_post("/json/messages", {
|
||||
"type": "private",
|
||||
"content": "Test message",
|
||||
"client": "test suite",
|
||||
"to": ujson.dumps([othello.id])})
|
||||
self.assert_json_error(result, f"'{othello.email}' is no longer using Zulip.")
|
||||
|
||||
result = self.client_post("/json/messages", {
|
||||
"type": "private",
|
||||
"content": "Test message",
|
||||
"client": "test suite",
|
||||
"to": ujson.dumps([othello.id, cordelia.id])})
|
||||
self.assert_json_error(result, f"'{othello.email}' is no longer using Zulip.")
|
||||
|
||||
def test_invalid_type(self) -> None:
|
||||
"""
|
||||
Sending a message of unknown type returns error JSON.
|
||||
"""
|
||||
self.login('hamlet')
|
||||
othello = self.example_user('othello')
|
||||
result = self.client_post("/json/messages", {"type": "invalid type",
|
||||
"content": "Test message",
|
||||
"client": "test suite",
|
||||
"to": othello.email})
|
||||
self.assert_json_error(result, "Invalid message type")
|
||||
|
||||
def test_empty_message(self) -> None:
|
||||
"""
|
||||
Sending a message that is empty or only whitespace should fail
|
||||
"""
|
||||
self.login('hamlet')
|
||||
othello = self.example_user('othello')
|
||||
result = self.client_post("/json/messages", {"type": "private",
|
||||
"content": " ",
|
||||
"client": "test suite",
|
||||
"to": othello.email})
|
||||
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
|
||||
"""
|
||||
self.login('hamlet')
|
||||
result = self.client_post("/json/messages", {"type": "stream",
|
||||
"to": "Verona",
|
||||
"client": "test suite",
|
||||
"content": "Test message",
|
||||
"topic": ""})
|
||||
self.assert_json_error(result, "Topic can't be empty")
|
||||
|
||||
def test_missing_topic(self) -> None:
|
||||
"""
|
||||
Sending a message without topic should fail
|
||||
"""
|
||||
self.login('hamlet')
|
||||
result = self.client_post("/json/messages", {"type": "stream",
|
||||
"to": "Verona",
|
||||
"client": "test suite",
|
||||
"content": "Test message"})
|
||||
self.assert_json_error(result, "Missing topic")
|
||||
|
||||
def test_invalid_message_type(self) -> None:
|
||||
"""
|
||||
Messages other than the type of "private" or "stream" are considered as invalid
|
||||
"""
|
||||
self.login('hamlet')
|
||||
result = self.client_post("/json/messages", {"type": "invalid",
|
||||
"to": "Verona",
|
||||
"client": "test suite",
|
||||
"content": "Test message",
|
||||
"topic": "Test topic"})
|
||||
self.assert_json_error(result, "Invalid message type")
|
||||
|
||||
def test_private_message_without_recipients(self) -> None:
|
||||
"""
|
||||
Sending private message without recipients should fail
|
||||
"""
|
||||
self.login('hamlet')
|
||||
result = self.client_post("/json/messages", {"type": "private",
|
||||
"content": "Test content",
|
||||
"client": "test suite",
|
||||
"to": ""})
|
||||
self.assert_json_error(result, "Message must have recipients")
|
||||
|
||||
def test_mirrored_huddle(self) -> None:
|
||||
"""
|
||||
Sending a mirrored huddle message works
|
||||
"""
|
||||
result = self.api_post(self.mit_user("starnine"),
|
||||
"/json/messages", {"type": "private",
|
||||
"sender": self.mit_email("sipbtest"),
|
||||
"content": "Test message",
|
||||
"client": "zephyr_mirror",
|
||||
"to": ujson.dumps([self.mit_email("starnine"),
|
||||
self.mit_email("espuser")])},
|
||||
subdomain="zephyr")
|
||||
self.assert_json_success(result)
|
||||
|
||||
def test_mirrored_personal(self) -> None:
|
||||
"""
|
||||
Sending a mirrored personal message works
|
||||
"""
|
||||
result = self.api_post(self.mit_user("starnine"),
|
||||
"/json/messages", {"type": "private",
|
||||
"sender": self.mit_email("sipbtest"),
|
||||
"content": "Test message",
|
||||
"client": "zephyr_mirror",
|
||||
"to": self.mit_email("starnine")},
|
||||
subdomain="zephyr")
|
||||
self.assert_json_success(result)
|
||||
|
||||
def test_mirrored_personal_browser(self) -> None:
|
||||
"""
|
||||
Sending a mirrored personal message via the browser should not work.
|
||||
"""
|
||||
user = self.mit_user('starnine')
|
||||
self.login_user(user)
|
||||
result = self.client_post("/json/messages",
|
||||
{"type": "private",
|
||||
"sender": self.mit_email("sipbtest"),
|
||||
"content": "Test message",
|
||||
"client": "zephyr_mirror",
|
||||
"to": self.mit_email("starnine")},
|
||||
subdomain="zephyr")
|
||||
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.
|
||||
"""
|
||||
result = self.api_post(self.mit_user("starnine"), "/api/v1/messages",
|
||||
{"type": "private",
|
||||
"sender": self.mit_email("sipbtest"),
|
||||
"content": "Test message",
|
||||
"client": "zephyr_mirror",
|
||||
"to": self.mit_email("espuser")},
|
||||
subdomain="zephyr")
|
||||
self.assert_json_error(result, "User not authorized for this query")
|
||||
|
||||
def test_duplicated_mirrored_huddle(self) -> None:
|
||||
"""
|
||||
Sending two mirrored huddles in the row return the same ID
|
||||
"""
|
||||
msg = {"type": "private",
|
||||
"sender": self.mit_email("sipbtest"),
|
||||
"content": "Test message",
|
||||
"client": "zephyr_mirror",
|
||||
"to": ujson.dumps([self.mit_email("espuser"),
|
||||
self.mit_email("starnine")])}
|
||||
|
||||
with mock.patch('DNS.dnslookup', return_value=[['starnine:*:84233:101:Athena Consulting Exchange User,,,:/mit/starnine:/bin/bash']]):
|
||||
result1 = self.api_post(self.mit_user("starnine"), "/api/v1/messages", msg,
|
||||
subdomain="zephyr")
|
||||
self.assert_json_success(result1)
|
||||
|
||||
with mock.patch('DNS.dnslookup', return_value=[['espuser:*:95494:101:Esp Classroom,,,:/mit/espuser:/bin/athena/bash']]):
|
||||
result2 = self.api_post(self.mit_user("espuser"), "/api/v1/messages", msg,
|
||||
subdomain="zephyr")
|
||||
self.assert_json_success(result2)
|
||||
|
||||
self.assertEqual(ujson.loads(result1.content)['id'],
|
||||
ujson.loads(result2.content)['id'])
|
||||
|
||||
def test_message_with_null_bytes(self) -> None:
|
||||
"""
|
||||
A message with null bytes in it is handled.
|
||||
"""
|
||||
self.login('hamlet')
|
||||
post_data = {"type": "stream", "to": "Verona", "client": "test suite",
|
||||
"content": " I like null bytes \x00 in my content", "topic": "Test topic"}
|
||||
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.
|
||||
"""
|
||||
self.login('hamlet')
|
||||
post_data = {"type": "stream", "to": "Verona", "client": "test suite",
|
||||
"content": " I like whitespace at the end! \n\n \n", "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, " I like whitespace at the end!")
|
||||
|
||||
def test_long_message(self) -> None:
|
||||
"""
|
||||
Sending a message longer than the maximum message length succeeds but is
|
||||
truncated.
|
||||
"""
|
||||
self.login('hamlet')
|
||||
long_message = "A" * (MAX_MESSAGE_LENGTH + 1)
|
||||
post_data = {"type": "stream", "to": "Verona", "client": "test suite",
|
||||
"content": long_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,
|
||||
"A" * (MAX_MESSAGE_LENGTH - 20) + "\n[message truncated]")
|
||||
|
||||
def test_long_topic(self) -> None:
|
||||
"""
|
||||
Sending a message with a topic longer than the maximum topic length
|
||||
succeeds, but the topic is truncated.
|
||||
"""
|
||||
self.login('hamlet')
|
||||
long_topic = "A" * (MAX_TOPIC_NAME_LENGTH + 1)
|
||||
post_data = {"type": "stream", "to": "Verona", "client": "test suite",
|
||||
"content": "test content", "topic": long_topic}
|
||||
result = self.client_post("/json/messages", post_data)
|
||||
self.assert_json_success(result)
|
||||
|
||||
sent_message = self.get_last_message()
|
||||
self.assertEqual(sent_message.topic_name(),
|
||||
"A" * (MAX_TOPIC_NAME_LENGTH - 3) + "...")
|
||||
|
||||
def test_send_forged_message_as_not_superuser(self) -> None:
|
||||
self.login('hamlet')
|
||||
result = self.client_post("/json/messages", {"type": "stream",
|
||||
"to": "Verona",
|
||||
"client": "test suite",
|
||||
"content": "Test message",
|
||||
"topic": "Test topic",
|
||||
"forged": "true"})
|
||||
self.assert_json_error(result, "User not authorized for this query")
|
||||
|
||||
def test_send_message_as_not_superuser_to_different_domain(self) -> None:
|
||||
self.login('hamlet')
|
||||
result = self.client_post("/json/messages", {"type": "stream",
|
||||
"to": "Verona",
|
||||
"client": "test suite",
|
||||
"content": "Test message",
|
||||
"topic": "Test topic",
|
||||
"realm_str": "mit"})
|
||||
self.assert_json_error(result, "User not authorized for this query")
|
||||
|
||||
def test_send_message_as_superuser_to_domain_that_dont_exist(self) -> None:
|
||||
user = self.example_user("default_bot")
|
||||
password = "test_password"
|
||||
user.set_password(password)
|
||||
user.is_api_super_user = True
|
||||
user.save()
|
||||
result = self.api_post(user,
|
||||
"/api/v1/messages", {"type": "stream",
|
||||
"to": "Verona",
|
||||
"client": "test suite",
|
||||
"content": "Test message",
|
||||
"topic": "Test topic",
|
||||
"realm_str": "non-existing"})
|
||||
user.is_api_super_user = False
|
||||
user.save()
|
||||
self.assert_json_error(result, "Unknown organization 'non-existing'")
|
||||
|
||||
def test_send_message_when_sender_is_not_set(self) -> None:
|
||||
result = self.api_post(self.mit_user("starnine"), "/api/v1/messages",
|
||||
{"type": "private",
|
||||
"content": "Test message",
|
||||
"client": "zephyr_mirror",
|
||||
"to": self.mit_email("starnine")},
|
||||
subdomain="zephyr")
|
||||
self.assert_json_error(result, "Missing sender")
|
||||
|
||||
def test_send_message_as_not_superuser_when_type_is_not_private(self) -> None:
|
||||
result = self.api_post(self.mit_user("starnine"), "/api/v1/messages",
|
||||
{"type": "not-private",
|
||||
"sender": self.mit_email("sipbtest"),
|
||||
"content": "Test message",
|
||||
"client": "zephyr_mirror",
|
||||
"to": self.mit_email("starnine")},
|
||||
subdomain="zephyr")
|
||||
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(
|
||||
self, create_mirrored_message_users_mock: Any) -> None:
|
||||
create_mirrored_message_users_mock.side_effect = InvalidMirrorInput()
|
||||
result = self.api_post(self.mit_user("starnine"), "/api/v1/messages",
|
||||
{"type": "private",
|
||||
"sender": self.mit_email("sipbtest"),
|
||||
"content": "Test message",
|
||||
"client": "zephyr_mirror",
|
||||
"to": self.mit_email("starnine")},
|
||||
subdomain="zephyr")
|
||||
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(
|
||||
self, create_mirrored_message_users_mock: Any) -> None:
|
||||
create_mirrored_message_users_mock.return_value = mock.Mock()
|
||||
user = self.mit_user("starnine")
|
||||
user.realm.string_id = 'notzephyr'
|
||||
user.realm.save()
|
||||
result = self.api_post(user, "/api/v1/messages",
|
||||
{"type": "private",
|
||||
"sender": self.mit_email("sipbtest"),
|
||||
"content": "Test message",
|
||||
"client": "zephyr_mirror",
|
||||
"to": user.email},
|
||||
subdomain="notzephyr")
|
||||
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(
|
||||
self, create_mirrored_message_users_mock: Any) -> None:
|
||||
create_mirrored_message_users_mock.return_value = mock.Mock()
|
||||
user = self.mit_user("starnine")
|
||||
self.login_user(user)
|
||||
result = self.api_post(user, "/api/v1/messages",
|
||||
{"type": "private",
|
||||
"sender": self.mit_email("sipbtest"),
|
||||
"content": "Test message",
|
||||
"client": "zephyr_mirror",
|
||||
"to": ujson.dumps([user.id])},
|
||||
subdomain="zephyr")
|
||||
self.assert_json_error(result, "Mirroring not allowed with recipient user IDs")
|
||||
|
||||
def test_send_message_irc_mirror(self) -> None:
|
||||
reset_emails_in_zulip_realm()
|
||||
self.login('hamlet')
|
||||
bot_info = {
|
||||
'full_name': 'IRC bot',
|
||||
'short_name': 'irc',
|
||||
}
|
||||
result = self.client_post("/json/bots", bot_info)
|
||||
self.assert_json_success(result)
|
||||
|
||||
email = "irc-bot@zulip.testserver"
|
||||
user = get_user(email, get_realm('zulip'))
|
||||
user.is_api_super_user = True
|
||||
user.save()
|
||||
user = get_user(email, get_realm('zulip'))
|
||||
self.subscribe(user, "IRCland")
|
||||
|
||||
# Simulate a mirrored message with a slightly old timestamp.
|
||||
fake_date_sent = timezone_now() - datetime.timedelta(minutes=37)
|
||||
fake_timestamp = datetime_to_timestamp(fake_date_sent)
|
||||
|
||||
result = self.api_post(user, "/api/v1/messages", {"type": "stream",
|
||||
"forged": "true",
|
||||
"time": fake_timestamp,
|
||||
"sender": "irc-user@irc.zulip.com",
|
||||
"content": "Test message",
|
||||
"client": "irc_mirror",
|
||||
"topic": "from irc",
|
||||
"to": "IRCLand"})
|
||||
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
|
||||
fake_date_sent = timezone_now() - datetime.timedelta(minutes=22)
|
||||
fake_timestamp = datetime_to_timestamp(fake_date_sent)
|
||||
|
||||
result = self.api_post(user, "/api/v1/messages", {"type": "stream",
|
||||
"forged": "yes",
|
||||
"time": fake_timestamp,
|
||||
"sender": "irc-user@irc.zulip.com",
|
||||
"content": "Test message",
|
||||
"client": "irc_mirror",
|
||||
"topic": "from irc",
|
||||
"to": "IRCLand"})
|
||||
self.assert_json_success(result)
|
||||
|
||||
msg = self.get_last_message()
|
||||
self.assertEqual(int(datetime_to_timestamp(msg.date_sent)), int(fake_timestamp))
|
||||
|
||||
def test_unsubscribed_api_super_user(self) -> None:
|
||||
reset_emails_in_zulip_realm()
|
||||
|
||||
cordelia = self.example_user('cordelia')
|
||||
stream_name = 'private_stream'
|
||||
self.make_stream(stream_name, invite_only=True)
|
||||
|
||||
self.unsubscribe(cordelia, stream_name)
|
||||
|
||||
# As long as Cordelia is a super_user, she can send messages
|
||||
# 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(
|
||||
type="stream",
|
||||
to=stream_name,
|
||||
client=client,
|
||||
topic='whatever',
|
||||
content='whatever',
|
||||
forged=ujson.dumps(forged),
|
||||
)
|
||||
|
||||
# Only pass the 'sender' property when doing mirroring behavior.
|
||||
if forged:
|
||||
payload['sender'] = sender_email
|
||||
|
||||
cordelia.is_api_super_user = False
|
||||
cordelia.save()
|
||||
|
||||
result = self.api_post(cordelia, "/api/v1/messages", payload)
|
||||
self.assert_json_error_contains(result, 'authorized')
|
||||
|
||||
cordelia.is_api_super_user = True
|
||||
cordelia.save()
|
||||
|
||||
result = self.api_post(cordelia, "/api/v1/messages", payload)
|
||||
self.assert_json_success(result)
|
||||
|
||||
test_with(
|
||||
sender_email=cordelia.email,
|
||||
client='test suite',
|
||||
forged=False,
|
||||
)
|
||||
|
||||
test_with(
|
||||
sender_email='irc_person@zulip.com',
|
||||
client='irc_mirror',
|
||||
forged=True,
|
||||
)
|
||||
|
||||
def test_bot_can_send_to_owner_stream(self) -> None:
|
||||
cordelia = self.example_user('cordelia')
|
||||
bot = self.create_test_bot(
|
||||
short_name='whatever',
|
||||
user_profile=cordelia,
|
||||
)
|
||||
|
||||
stream_name = 'private_stream'
|
||||
self.make_stream(stream_name, invite_only=True)
|
||||
|
||||
payload = dict(
|
||||
type="stream",
|
||||
to=stream_name,
|
||||
client='test suite',
|
||||
topic='whatever',
|
||||
content='whatever',
|
||||
)
|
||||
|
||||
result = self.api_post(bot, "/api/v1/messages", payload)
|
||||
self.assert_json_error_contains(result, 'Not authorized to send')
|
||||
|
||||
# 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:
|
||||
notification_bot = self.notification_bot()
|
||||
stream = self.make_stream("notify_channel", get_realm("zulipinternal"))
|
||||
|
||||
result = self.api_post(notification_bot,
|
||||
"/api/v1/messages",
|
||||
{"type": "stream",
|
||||
"to": "notify_channel",
|
||||
"client": "test suite",
|
||||
"content": "Test message",
|
||||
"topic": "Test topic"},
|
||||
subdomain='zulipinternal')
|
||||
|
||||
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_create_mirror_user_despite_race(self) -> None:
|
||||
realm = get_realm('zulip')
|
||||
|
||||
email = 'fred@example.com'
|
||||
|
||||
email_to_full_name = lambda email: 'fred'
|
||||
|
||||
def create_user(**kwargs: Any) -> UserProfile:
|
||||
self.assertEqual(kwargs['full_name'], 'fred')
|
||||
self.assertEqual(kwargs['email'], email)
|
||||
self.assertEqual(kwargs['active'], False)
|
||||
self.assertEqual(kwargs['is_mirror_dummy'], True)
|
||||
# We create an actual user here to simulate a race.
|
||||
# We use the minimal, un-mocked function.
|
||||
kwargs['bot_type'] = None
|
||||
kwargs['bot_owner'] = None
|
||||
kwargs['tos_version'] = None
|
||||
kwargs['timezone'] = timezone_now()
|
||||
create_user_profile(**kwargs).save()
|
||||
raise IntegrityError()
|
||||
|
||||
with mock.patch('zerver.lib.actions.create_user',
|
||||
side_effect=create_user) as m:
|
||||
mirror_fred_user = create_mirror_user_if_needed(
|
||||
realm,
|
||||
email,
|
||||
email_to_full_name,
|
||||
)
|
||||
|
||||
self.assertEqual(mirror_fred_user.delivery_email, email)
|
||||
m.assert_called()
|
||||
|
||||
def test_guest_user(self) -> None:
|
||||
sender = self.example_user('polonius')
|
||||
|
||||
stream_name = 'public stream'
|
||||
self.make_stream(stream_name, invite_only=False)
|
||||
payload = dict(
|
||||
type="stream",
|
||||
to=stream_name,
|
||||
client='test suite',
|
||||
topic='whatever',
|
||||
content='whatever',
|
||||
)
|
||||
|
||||
# Guest user can't send message to unsubscribed public streams
|
||||
result = self.api_post(sender, "/api/v1/messages", payload)
|
||||
self.assert_json_error(result, "Not authorized to send to stream 'public stream'")
|
||||
|
||||
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)
|
||||
|
||||
class ScheduledMessageTest(ZulipTestCase):
|
||||
|
||||
def last_scheduled_message(self) -> ScheduledMessage:
|
||||
|
||||
Reference in New Issue
Block a user