2022-06-08 04:52:09 +02:00
|
|
|
from typing import TYPE_CHECKING, Any, Dict, List, Mapping
|
2017-10-08 15:42:41 +02:00
|
|
|
from unittest import mock
|
2016-11-03 18:49:00 +01:00
|
|
|
|
2020-08-07 01:09:47 +02:00
|
|
|
import orjson
|
2020-06-11 00:54:34 +02:00
|
|
|
|
2022-04-14 23:54:01 +02:00
|
|
|
from zerver.actions.reactions import notify_reaction_update
|
2022-04-14 23:51:16 +02:00
|
|
|
from zerver.actions.streams import do_change_stream_permission
|
2020-06-11 00:54:34 +02:00
|
|
|
from zerver.lib.cache import cache_get, to_dict_cache_key_id
|
2017-05-01 07:29:56 +02:00
|
|
|
from zerver.lib.emoji import emoji_name_to_emoji_code
|
2021-07-16 22:11:10 +02:00
|
|
|
from zerver.lib.exceptions import JsonableError
|
2020-05-23 12:58:18 +02:00
|
|
|
from zerver.lib.message import extract_message_dict
|
2016-11-03 18:49:00 +01:00
|
|
|
from zerver.lib.test_classes import ZulipTestCase
|
2021-05-08 08:25:06 +02:00
|
|
|
from zerver.lib.test_helpers import zulip_reaction_info
|
2020-06-11 00:54:34 +02:00
|
|
|
from zerver.models import Message, Reaction, RealmEmoji, UserMessage, get_realm
|
|
|
|
|
2022-06-08 04:52:09 +02:00
|
|
|
if TYPE_CHECKING:
|
|
|
|
from django.test.client import _MonkeyPatchedWSGIResponse as TestHttpResponse
|
|
|
|
|
2016-11-03 18:49:00 +01:00
|
|
|
|
|
|
|
class ReactionEmojiTest(ZulipTestCase):
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_missing_emoji(self) -> None:
|
2016-11-03 18:49:00 +01:00
|
|
|
"""
|
|
|
|
Sending reaction without emoji fails
|
|
|
|
"""
|
2020-03-10 11:48:26 +01:00
|
|
|
sender = self.example_user("hamlet")
|
2019-10-10 19:03:09 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"emoji_name": "",
|
2019-10-10 19:03:09 +02:00
|
|
|
}
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.api_post(sender, "/api/v1/messages/1/reactions", reaction_info)
|
2017-01-17 08:54:50 +01:00
|
|
|
self.assertEqual(result.status_code, 400)
|
2016-11-03 18:49:00 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_invalid_emoji(self) -> None:
|
2016-11-03 18:49:00 +01:00
|
|
|
"""
|
2016-12-04 10:50:32 +01:00
|
|
|
Sending invalid emoji fails
|
2016-11-03 18:49:00 +01:00
|
|
|
"""
|
2020-03-10 11:48:26 +01:00
|
|
|
sender = self.example_user("hamlet")
|
2019-10-10 19:03:09 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"emoji_name": "foo",
|
2019-10-10 19:03:09 +02:00
|
|
|
}
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.api_post(sender, "/api/v1/messages/1/reactions", reaction_info)
|
2016-12-04 10:50:32 +01:00
|
|
|
self.assert_json_error(result, "Emoji 'foo' does not exist")
|
2016-11-03 18:49:00 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_deactivated_realm_emoji(self) -> None:
|
2017-05-23 08:41:30 +02:00
|
|
|
"""
|
|
|
|
Sending deactivated realm emoji fails.
|
|
|
|
"""
|
2017-07-22 21:05:13 +02:00
|
|
|
emoji = RealmEmoji.objects.get(name="green_tick")
|
2017-05-23 08:41:30 +02:00
|
|
|
emoji.deactivated = True
|
2021-02-12 08:20:45 +01:00
|
|
|
emoji.save(update_fields=["deactivated"])
|
2020-03-10 11:48:26 +01:00
|
|
|
sender = self.example_user("hamlet")
|
2019-10-10 19:03:09 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"emoji_name": "green_tick",
|
|
|
|
"reaction_type": "realm_emoji",
|
2019-10-10 19:03:09 +02:00
|
|
|
}
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.api_post(sender, "/api/v1/messages/1/reactions", reaction_info)
|
2017-07-22 21:05:13 +02:00
|
|
|
self.assert_json_error(result, "Emoji 'green_tick' does not exist")
|
2017-05-23 08:41:30 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_valid_emoji(self) -> None:
|
2016-11-03 18:49:00 +01:00
|
|
|
"""
|
|
|
|
Reacting with valid emoji succeeds
|
|
|
|
"""
|
2020-03-10 11:48:26 +01:00
|
|
|
sender = self.example_user("hamlet")
|
2019-10-10 19:03:09 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"emoji_name": "smile",
|
2019-10-10 19:03:09 +02:00
|
|
|
}
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
base_query = Reaction.objects.filter(
|
|
|
|
user_profile=sender,
|
|
|
|
message=Message.objects.get(id=1),
|
|
|
|
)
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.api_post(sender, "/api/v1/messages/1/reactions", reaction_info)
|
2016-11-03 18:49:00 +01:00
|
|
|
self.assert_json_success(result)
|
|
|
|
self.assertEqual(200, result.status_code)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertTrue(base_query.filter(emoji_name=reaction_info["emoji_name"]).exists())
|
2020-03-22 17:44:27 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
reaction_info["emoji_name"] = "green_tick"
|
|
|
|
result = self.api_post(sender, "/api/v1/messages/1/reactions", reaction_info)
|
2020-03-22 17:44:27 +01:00
|
|
|
self.assert_json_success(result)
|
|
|
|
self.assertEqual(200, result.status_code)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertTrue(base_query.filter(emoji_name=reaction_info["emoji_name"]).exists())
|
2016-11-03 18:49:00 +01:00
|
|
|
|
2020-05-23 12:58:18 +02:00
|
|
|
def test_cached_reaction_data(self) -> None:
|
|
|
|
"""
|
|
|
|
Formatted reactions data is saved in cache.
|
|
|
|
"""
|
2021-11-30 01:47:09 +01:00
|
|
|
senders = [self.example_user("hamlet"), self.example_user("cordelia")]
|
|
|
|
emojis = ["smile", "tada"]
|
|
|
|
expected_emoji_codes = ["1f642", "1f389"]
|
|
|
|
|
|
|
|
for sender, emoji in zip(senders, emojis):
|
|
|
|
reaction_info = {
|
|
|
|
"emoji_name": emoji,
|
|
|
|
}
|
|
|
|
result = self.api_post(sender, "/api/v1/messages/1/reactions", reaction_info)
|
|
|
|
|
|
|
|
self.assert_json_success(result)
|
|
|
|
self.assertEqual(200, result.status_code)
|
2020-05-23 12:58:18 +02:00
|
|
|
|
|
|
|
key = to_dict_cache_key_id(1)
|
|
|
|
message = extract_message_dict(cache_get(key)[0])
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
expected_reaction_data = [
|
|
|
|
{
|
2021-11-30 01:47:09 +01:00
|
|
|
"emoji_name": emoji,
|
|
|
|
"emoji_code": emoji_code,
|
2021-02-12 08:20:45 +01:00
|
|
|
"reaction_type": "unicode_emoji",
|
|
|
|
"user": {
|
2021-07-09 20:54:04 +02:00
|
|
|
"email": f"user{sender.id}@zulip.testserver",
|
|
|
|
"id": sender.id,
|
2021-11-30 01:47:09 +01:00
|
|
|
"full_name": sender.full_name,
|
2021-02-12 08:19:30 +01:00
|
|
|
},
|
2021-07-09 20:54:04 +02:00
|
|
|
"user_id": sender.id,
|
2021-02-12 08:19:30 +01:00
|
|
|
}
|
2021-11-30 01:47:09 +01:00
|
|
|
# It's important that we preserve the loop order in this
|
|
|
|
# test, since this is our test to verify that we're
|
|
|
|
# returning reactions in chronological order.
|
|
|
|
for sender, emoji, emoji_code in zip(senders, emojis, expected_emoji_codes)
|
2021-02-12 08:19:30 +01:00
|
|
|
]
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(expected_reaction_data, message["reactions"])
|
2020-05-23 12:58:18 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_zulip_emoji(self) -> None:
|
2017-05-01 05:16:39 +02:00
|
|
|
"""
|
|
|
|
Reacting with zulip emoji succeeds
|
|
|
|
"""
|
2020-03-10 11:48:26 +01:00
|
|
|
sender = self.example_user("hamlet")
|
2019-10-10 19:03:09 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"emoji_name": "zulip",
|
|
|
|
"reaction_type": "zulip_extra_emoji",
|
2019-10-10 19:03:09 +02:00
|
|
|
}
|
2021-02-12 08:19:30 +01:00
|
|
|
base_query = Reaction.objects.filter(
|
2021-02-12 08:20:45 +01:00
|
|
|
user_profile=sender, emoji_name=reaction_info["emoji_name"]
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2019-10-10 19:03:09 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.api_post(sender, "/api/v1/messages/1/reactions", reaction_info)
|
2017-05-01 05:16:39 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
self.assertEqual(200, result.status_code)
|
2020-03-22 17:44:27 +01:00
|
|
|
self.assertTrue(base_query.filter(message=Message.objects.get(id=1)).exists())
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
reaction_info.pop("reaction_type")
|
|
|
|
result = self.api_post(sender, "/api/v1/messages/2/reactions", reaction_info)
|
2020-03-22 17:44:27 +01:00
|
|
|
self.assert_json_success(result)
|
|
|
|
self.assertEqual(200, result.status_code)
|
|
|
|
self.assertTrue(base_query.filter(message=Message.objects.get(id=2)).exists())
|
2017-05-01 05:16:39 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_valid_emoji_react_historical(self) -> None:
|
2017-03-23 04:15:32 +01:00
|
|
|
"""
|
|
|
|
Reacting with valid emoji on a historical message succeeds
|
|
|
|
"""
|
|
|
|
stream_name = "Saxony"
|
2017-08-25 06:01:29 +02:00
|
|
|
self.subscribe(self.example_user("cordelia"), stream_name)
|
2020-03-07 11:43:05 +01:00
|
|
|
message_id = self.send_stream_message(self.example_user("cordelia"), stream_name)
|
2017-03-23 04:15:32 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
user_profile = self.example_user("hamlet")
|
2020-03-10 11:48:26 +01:00
|
|
|
sender = user_profile
|
2017-03-23 04:15:32 +01:00
|
|
|
|
|
|
|
# Verify that hamlet did not receive the message.
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertFalse(
|
|
|
|
UserMessage.objects.filter(user_profile=user_profile, message_id=message_id).exists()
|
|
|
|
)
|
2017-03-23 04:15:32 +01:00
|
|
|
|
|
|
|
# Have hamlet react to the message
|
2019-10-10 19:03:09 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"emoji_name": "smile",
|
2019-10-10 19:03:09 +02:00
|
|
|
}
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.api_post(sender, f"/api/v1/messages/{message_id}/reactions", reaction_info)
|
2017-03-23 04:15:32 +01:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
# Fetch the now-created UserMessage object to confirm it exists and is historical
|
|
|
|
user_message = UserMessage.objects.get(user_profile=user_profile, message_id=message_id)
|
|
|
|
self.assertTrue(user_message.flags.historical)
|
|
|
|
self.assertTrue(user_message.flags.read)
|
|
|
|
self.assertFalse(user_message.flags.starred)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_valid_realm_emoji(self) -> None:
|
2016-11-03 18:49:00 +01:00
|
|
|
"""
|
|
|
|
Reacting with valid realm emoji succeeds
|
|
|
|
"""
|
2020-03-10 11:48:26 +01:00
|
|
|
sender = self.example_user("hamlet")
|
2016-11-03 18:49:00 +01:00
|
|
|
|
2019-10-10 19:03:09 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"emoji_name": "green_tick",
|
|
|
|
"reaction_type": "realm_emoji",
|
2019-10-10 19:03:09 +02:00
|
|
|
}
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.api_post(sender, "/api/v1/messages/1/reactions", reaction_info)
|
2016-11-03 18:49:00 +01:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_emoji_name_to_emoji_code(self) -> None:
|
2017-05-01 07:29:56 +02:00
|
|
|
"""
|
|
|
|
An emoji name is mapped canonically to emoji code.
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
realm = get_realm("zulip")
|
2017-12-15 16:54:07 +01:00
|
|
|
realm_emoji = RealmEmoji.objects.get(name="green_tick")
|
2017-05-01 07:29:56 +02:00
|
|
|
|
|
|
|
# Test active realm emoji.
|
2021-02-12 08:20:45 +01:00
|
|
|
emoji_code, reaction_type = emoji_name_to_emoji_code(realm, "green_tick")
|
2017-12-15 16:54:07 +01:00
|
|
|
self.assertEqual(emoji_code, str(realm_emoji.id))
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(reaction_type, "realm_emoji")
|
2017-05-01 07:29:56 +02:00
|
|
|
|
|
|
|
# Test deactivated realm emoji.
|
2017-12-15 16:54:07 +01:00
|
|
|
realm_emoji.deactivated = True
|
2021-02-12 08:20:45 +01:00
|
|
|
realm_emoji.save(update_fields=["deactivated"])
|
2017-05-01 07:29:56 +02:00
|
|
|
with self.assertRaises(JsonableError) as exc:
|
2021-02-12 08:20:45 +01:00
|
|
|
emoji_name_to_emoji_code(realm, "green_tick")
|
2017-05-01 07:29:56 +02:00
|
|
|
self.assertEqual(str(exc.exception), "Emoji 'green_tick' does not exist")
|
|
|
|
|
|
|
|
# Test ':zulip:' emoji.
|
2021-02-12 08:20:45 +01:00
|
|
|
emoji_code, reaction_type = emoji_name_to_emoji_code(realm, "zulip")
|
|
|
|
self.assertEqual(emoji_code, "zulip")
|
|
|
|
self.assertEqual(reaction_type, "zulip_extra_emoji")
|
2017-05-01 07:29:56 +02:00
|
|
|
|
2020-10-23 02:43:28 +02:00
|
|
|
# Test Unicode emoji.
|
2021-02-12 08:20:45 +01:00
|
|
|
emoji_code, reaction_type = emoji_name_to_emoji_code(realm, "astonished")
|
|
|
|
self.assertEqual(emoji_code, "1f632")
|
|
|
|
self.assertEqual(reaction_type, "unicode_emoji")
|
2017-05-01 07:29:56 +02:00
|
|
|
|
2020-10-23 02:43:28 +02:00
|
|
|
# Test override Unicode emoji.
|
2017-05-01 07:29:56 +02:00
|
|
|
overriding_emoji = RealmEmoji.objects.create(
|
2021-02-12 08:20:45 +01:00
|
|
|
name="astonished", realm=realm, file_name="astonished"
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2021-02-12 08:20:45 +01:00
|
|
|
emoji_code, reaction_type = emoji_name_to_emoji_code(realm, "astonished")
|
2017-12-15 16:54:07 +01:00
|
|
|
self.assertEqual(emoji_code, str(overriding_emoji.id))
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(reaction_type, "realm_emoji")
|
2017-05-01 07:29:56 +02:00
|
|
|
|
|
|
|
# Test deactivate over-ridding realm emoji.
|
|
|
|
overriding_emoji.deactivated = True
|
2021-02-12 08:20:45 +01:00
|
|
|
overriding_emoji.save(update_fields=["deactivated"])
|
|
|
|
emoji_code, reaction_type = emoji_name_to_emoji_code(realm, "astonished")
|
|
|
|
self.assertEqual(emoji_code, "1f632")
|
|
|
|
self.assertEqual(reaction_type, "unicode_emoji")
|
2017-05-01 07:29:56 +02:00
|
|
|
|
|
|
|
# Test override `:zulip:` emoji.
|
2021-02-12 08:20:45 +01:00
|
|
|
overriding_emoji = RealmEmoji.objects.create(name="zulip", realm=realm, file_name="zulip")
|
|
|
|
emoji_code, reaction_type = emoji_name_to_emoji_code(realm, "zulip")
|
2017-12-15 16:54:07 +01:00
|
|
|
self.assertEqual(emoji_code, str(overriding_emoji.id))
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(reaction_type, "realm_emoji")
|
2017-05-01 07:29:56 +02:00
|
|
|
|
|
|
|
# Test non-existent emoji.
|
|
|
|
with self.assertRaises(JsonableError) as exc:
|
2021-02-12 08:20:45 +01:00
|
|
|
emoji_name_to_emoji_code(realm, "invalid_emoji")
|
2017-05-01 07:29:56 +02:00
|
|
|
self.assertEqual(str(exc.exception), "Emoji 'invalid_emoji' does not exist")
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2016-11-03 18:49:00 +01:00
|
|
|
class ReactionMessageIDTest(ZulipTestCase):
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_missing_message_id(self) -> None:
|
2016-11-03 18:49:00 +01:00
|
|
|
"""
|
|
|
|
Reacting without a message_id fails
|
|
|
|
"""
|
2020-03-10 11:48:26 +01:00
|
|
|
sender = self.example_user("hamlet")
|
2019-10-10 19:03:09 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"emoji_name": "smile",
|
2019-10-10 19:03:09 +02:00
|
|
|
}
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.api_post(sender, "/api/v1/messages//reactions", reaction_info)
|
2016-12-16 02:01:34 +01:00
|
|
|
self.assertEqual(result.status_code, 404)
|
2016-11-03 18:49:00 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_invalid_message_id(self) -> None:
|
2016-11-03 18:49:00 +01:00
|
|
|
"""
|
|
|
|
Reacting to an invalid message id fails
|
|
|
|
"""
|
2020-03-10 11:48:26 +01:00
|
|
|
sender = self.example_user("hamlet")
|
2019-10-10 19:03:09 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"emoji_name": "smile",
|
2019-10-10 19:03:09 +02:00
|
|
|
}
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.api_post(sender, "/api/v1/messages/-1/reactions", reaction_info)
|
2016-12-16 02:01:34 +01:00
|
|
|
self.assertEqual(result.status_code, 404)
|
2016-11-03 18:49:00 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_inaccessible_message_id(self) -> None:
|
2016-11-03 18:49:00 +01:00
|
|
|
"""
|
|
|
|
Reacting to a inaccessible (for instance, private) message fails
|
|
|
|
"""
|
2020-03-10 11:48:26 +01:00
|
|
|
pm_sender = self.example_user("hamlet")
|
2020-03-12 14:17:25 +01:00
|
|
|
pm_recipient = self.example_user("othello")
|
2020-03-10 11:48:26 +01:00
|
|
|
reaction_sender = self.example_user("iago")
|
2016-11-03 18:49:00 +01:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.api_post(
|
|
|
|
pm_sender,
|
|
|
|
"/api/v1/messages",
|
2022-09-13 08:39:44 +02:00
|
|
|
{
|
|
|
|
"type": "private",
|
|
|
|
"content": "Test message",
|
|
|
|
"to": orjson.dumps([pm_recipient.email]).decode(),
|
|
|
|
},
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2022-06-07 01:37:01 +02:00
|
|
|
pm_id = self.assert_json_success(result)["id"]
|
2019-10-10 19:03:09 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"emoji_name": "smile",
|
2019-10-10 19:03:09 +02:00
|
|
|
}
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.api_post(
|
2021-02-12 08:20:45 +01:00
|
|
|
reaction_sender, f"/api/v1/messages/{pm_id}/reactions", reaction_info
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2016-11-03 18:49:00 +01:00
|
|
|
self.assert_json_error(result, "Invalid message(s)")
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2016-11-03 18:49:00 +01:00
|
|
|
class ReactionTest(ZulipTestCase):
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_existing_reaction(self) -> None:
|
2016-11-03 18:49:00 +01:00
|
|
|
"""
|
|
|
|
Creating the same reaction twice fails
|
|
|
|
"""
|
2020-03-10 11:48:26 +01:00
|
|
|
pm_sender = self.example_user("hamlet")
|
|
|
|
pm_recipient = self.example_user("othello")
|
2016-11-03 18:49:00 +01:00
|
|
|
reaction_sender = pm_recipient
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
pm = self.api_post(
|
|
|
|
pm_sender,
|
|
|
|
"/api/v1/messages",
|
2022-09-13 08:39:44 +02:00
|
|
|
{
|
|
|
|
"type": "private",
|
|
|
|
"content": "Test message",
|
|
|
|
"to": orjson.dumps([pm_recipient.email]).decode(),
|
|
|
|
},
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2016-11-03 18:49:00 +01:00
|
|
|
self.assert_json_success(pm)
|
2020-08-07 01:09:47 +02:00
|
|
|
content = orjson.loads(pm.content)
|
2016-12-04 10:50:32 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
pm_id = content["id"]
|
2019-10-10 19:03:09 +02:00
|
|
|
|
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"emoji_name": "smile",
|
2019-10-10 19:03:09 +02:00
|
|
|
}
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
first = self.api_post(reaction_sender, f"/api/v1/messages/{pm_id}/reactions", reaction_info)
|
2016-11-03 18:49:00 +01:00
|
|
|
self.assert_json_success(first)
|
2019-10-10 19:03:09 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
second = self.api_post(
|
2021-02-12 08:20:45 +01:00
|
|
|
reaction_sender, f"/api/v1/messages/{pm_id}/reactions", reaction_info
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2019-10-10 19:03:09 +02:00
|
|
|
self.assert_json_error(second, "Reaction already exists.")
|
2016-11-03 18:49:00 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_remove_nonexisting_reaction(self) -> None:
|
2016-11-30 08:14:46 +01:00
|
|
|
"""
|
|
|
|
Removing a reaction twice fails
|
|
|
|
"""
|
2020-03-10 11:48:26 +01:00
|
|
|
pm_sender = self.example_user("hamlet")
|
|
|
|
pm_recipient = self.example_user("othello")
|
2016-11-30 08:14:46 +01:00
|
|
|
reaction_sender = pm_recipient
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
pm = self.api_post(
|
|
|
|
pm_sender,
|
|
|
|
"/api/v1/messages",
|
2022-09-13 08:39:44 +02:00
|
|
|
{
|
|
|
|
"type": "private",
|
|
|
|
"content": "Test message",
|
|
|
|
"to": orjson.dumps([pm_recipient.email]).decode(),
|
|
|
|
},
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2016-11-30 08:14:46 +01:00
|
|
|
self.assert_json_success(pm)
|
2019-10-10 19:03:09 +02:00
|
|
|
|
2020-08-07 01:09:47 +02:00
|
|
|
content = orjson.loads(pm.content)
|
2021-02-12 08:20:45 +01:00
|
|
|
pm_id = content["id"]
|
2019-10-10 19:03:09 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"emoji_name": "smile",
|
2019-10-10 19:03:09 +02:00
|
|
|
}
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
add = self.api_post(reaction_sender, f"/api/v1/messages/{pm_id}/reactions", reaction_info)
|
2016-11-30 08:14:46 +01:00
|
|
|
self.assert_json_success(add)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
first = self.api_delete(
|
2021-02-12 08:20:45 +01:00
|
|
|
reaction_sender, f"/api/v1/messages/{pm_id}/reactions", reaction_info
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2016-11-30 08:14:46 +01:00
|
|
|
self.assert_json_success(first)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
second = self.api_delete(
|
2021-02-12 08:20:45 +01:00
|
|
|
reaction_sender, f"/api/v1/messages/{pm_id}/reactions", reaction_info
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2019-10-10 19:03:09 +02:00
|
|
|
self.assert_json_error(second, "Reaction doesn't exist.")
|
2016-11-30 08:14:46 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_remove_existing_reaction_with_renamed_emoji(self) -> None:
|
2017-10-08 15:42:41 +02:00
|
|
|
"""
|
|
|
|
Removes an old existing reaction but the name of emoji got changed during
|
|
|
|
various emoji infra changes.
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
realm = get_realm("zulip")
|
2020-03-10 11:48:26 +01:00
|
|
|
sender = self.example_user("hamlet")
|
2021-02-12 08:20:45 +01:00
|
|
|
emoji_code, reaction_type = emoji_name_to_emoji_code(realm, "smile")
|
2019-10-10 19:03:09 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"emoji_name": "smile",
|
|
|
|
"emoji_code": emoji_code,
|
|
|
|
"reaction_type": reaction_type,
|
2019-10-10 19:03:09 +02:00
|
|
|
}
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.api_post(sender, "/api/v1/messages/1/reactions", reaction_info)
|
2017-10-08 15:42:41 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
with mock.patch("zerver.lib.emoji.name_to_codepoint", name_to_codepoint={}):
|
|
|
|
result = self.api_delete(sender, "/api/v1/messages/1/reactions", reaction_info)
|
2017-10-08 15:42:41 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_remove_existing_reaction_with_deactivated_realm_emoji(self) -> None:
|
2017-10-08 15:42:41 +02:00
|
|
|
"""
|
|
|
|
Removes an old existing reaction but the realm emoji used there has been deactivated.
|
|
|
|
"""
|
2020-03-10 11:48:26 +01:00
|
|
|
sender = self.example_user("hamlet")
|
2019-10-10 19:03:09 +02:00
|
|
|
|
|
|
|
emoji = RealmEmoji.objects.get(name="green_tick")
|
|
|
|
|
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"emoji_name": "green_tick",
|
|
|
|
"emoji_code": str(emoji.id),
|
|
|
|
"reaction_type": "realm_emoji",
|
2019-10-10 19:03:09 +02:00
|
|
|
}
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.api_post(sender, "/api/v1/messages/1/reactions", reaction_info)
|
2017-10-08 15:42:41 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
# Deactivate realm emoji.
|
|
|
|
emoji.deactivated = True
|
2021-02-12 08:20:45 +01:00
|
|
|
emoji.save(update_fields=["deactivated"])
|
|
|
|
result = self.api_delete(sender, "/api/v1/messages/1/reactions", reaction_info)
|
2017-10-08 15:42:41 +02:00
|
|
|
self.assert_json_success(result)
|
2016-11-30 08:14:46 +01:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2016-11-03 18:49:00 +01:00
|
|
|
class ReactionEventTest(ZulipTestCase):
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_event(self) -> None:
|
2016-11-03 18:49:00 +01:00
|
|
|
"""
|
|
|
|
Recipients of the message receive the reaction event
|
|
|
|
and event contains relevant data
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
pm_sender = self.example_user("hamlet")
|
|
|
|
pm_recipient = self.example_user("othello")
|
2016-11-03 18:49:00 +01:00
|
|
|
reaction_sender = pm_recipient
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.api_post(
|
|
|
|
pm_sender,
|
|
|
|
"/api/v1/messages",
|
2022-09-13 08:39:44 +02:00
|
|
|
{
|
|
|
|
"type": "private",
|
|
|
|
"content": "Test message",
|
|
|
|
"to": orjson.dumps([pm_recipient.email]).decode(),
|
|
|
|
},
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2022-06-07 01:37:01 +02:00
|
|
|
pm_id = self.assert_json_success(result)["id"]
|
2016-11-03 18:49:00 +01:00
|
|
|
|
2020-04-09 21:51:58 +02:00
|
|
|
expected_recipient_ids = {pm_sender.id, pm_recipient.id}
|
2016-11-03 18:49:00 +01:00
|
|
|
|
2019-10-10 19:03:09 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"emoji_name": "smile",
|
2019-10-10 19:03:09 +02:00
|
|
|
}
|
|
|
|
|
python: Convert assignment type annotations to Python 3.6 style.
This commit was split by tabbott; this piece covers the vast majority
of files in Zulip, but excludes scripts/, tools/, and puppet/ to help
ensure we at least show the right error messages for Xenial systems.
We can likely further refine the remaining pieces with some testing.
Generated by com2ann, with whitespace fixes and various manual fixes
for runtime issues:
- invoiced_through: Optional[LicenseLedger] = models.ForeignKey(
+ invoiced_through: Optional["LicenseLedger"] = models.ForeignKey(
-_apns_client: Optional[APNsClient] = None
+_apns_client: Optional["APNsClient"] = None
- notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- signup_notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ signup_notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- author: Optional[UserProfile] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
+ author: Optional["UserProfile"] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
- bot_owner: Optional[UserProfile] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
+ bot_owner: Optional["UserProfile"] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
- default_sending_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
- default_events_register_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_sending_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_events_register_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
-descriptors_by_handler_id: Dict[int, ClientDescriptor] = {}
+descriptors_by_handler_id: Dict[int, "ClientDescriptor"] = {}
-worker_classes: Dict[str, Type[QueueProcessingWorker]] = {}
-queues: Dict[str, Dict[str, Type[QueueProcessingWorker]]] = {}
+worker_classes: Dict[str, Type["QueueProcessingWorker"]] = {}
+queues: Dict[str, Dict[str, Type["QueueProcessingWorker"]]] = {}
-AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional[LDAPSearch] = None
+AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional["LDAPSearch"] = None
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-22 01:09:50 +02:00
|
|
|
events: List[Mapping[str, Any]] = []
|
2021-05-28 07:27:50 +02:00
|
|
|
with self.tornado_redirected_to_list(events, expected_num_events=1):
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.api_post(
|
2021-02-12 08:20:45 +01:00
|
|
|
reaction_sender, f"/api/v1/messages/{pm_id}/reactions", reaction_info
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2016-11-03 18:49:00 +01:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
event = events[0]["event"]
|
|
|
|
event_user_ids = set(events[0]["users"])
|
2016-11-03 18:49:00 +01:00
|
|
|
|
|
|
|
self.assertEqual(expected_recipient_ids, event_user_ids)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(event["user"]["email"], reaction_sender.email)
|
|
|
|
self.assertEqual(event["type"], "reaction")
|
|
|
|
self.assertEqual(event["op"], "add")
|
|
|
|
self.assertEqual(event["emoji_name"], "smile")
|
|
|
|
self.assertEqual(event["message_id"], pm_id)
|
2016-11-30 08:14:46 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_remove_event(self) -> None:
|
2016-11-30 08:14:46 +01:00
|
|
|
"""
|
|
|
|
Recipients of the message receive the reaction event
|
|
|
|
and event contains relevant data
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
pm_sender = self.example_user("hamlet")
|
|
|
|
pm_recipient = self.example_user("othello")
|
2016-11-30 08:14:46 +01:00
|
|
|
reaction_sender = pm_recipient
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.api_post(
|
|
|
|
pm_sender,
|
|
|
|
"/api/v1/messages",
|
2022-09-13 08:39:44 +02:00
|
|
|
{
|
|
|
|
"type": "private",
|
|
|
|
"content": "Test message",
|
|
|
|
"to": orjson.dumps([pm_recipient.email]).decode(),
|
|
|
|
},
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2022-06-07 01:37:01 +02:00
|
|
|
content = self.assert_json_success(result)
|
2021-02-12 08:20:45 +01:00
|
|
|
pm_id = content["id"]
|
2016-11-30 08:14:46 +01:00
|
|
|
|
2020-04-09 21:51:58 +02:00
|
|
|
expected_recipient_ids = {pm_sender.id, pm_recipient.id}
|
2016-11-30 08:14:46 +01:00
|
|
|
|
2019-10-10 19:03:09 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"emoji_name": "smile",
|
2019-10-10 19:03:09 +02:00
|
|
|
}
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
add = self.api_post(reaction_sender, f"/api/v1/messages/{pm_id}/reactions", reaction_info)
|
2016-11-30 08:14:46 +01:00
|
|
|
self.assert_json_success(add)
|
|
|
|
|
python: Convert assignment type annotations to Python 3.6 style.
This commit was split by tabbott; this piece covers the vast majority
of files in Zulip, but excludes scripts/, tools/, and puppet/ to help
ensure we at least show the right error messages for Xenial systems.
We can likely further refine the remaining pieces with some testing.
Generated by com2ann, with whitespace fixes and various manual fixes
for runtime issues:
- invoiced_through: Optional[LicenseLedger] = models.ForeignKey(
+ invoiced_through: Optional["LicenseLedger"] = models.ForeignKey(
-_apns_client: Optional[APNsClient] = None
+_apns_client: Optional["APNsClient"] = None
- notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- signup_notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ signup_notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- author: Optional[UserProfile] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
+ author: Optional["UserProfile"] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
- bot_owner: Optional[UserProfile] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
+ bot_owner: Optional["UserProfile"] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
- default_sending_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
- default_events_register_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_sending_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_events_register_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
-descriptors_by_handler_id: Dict[int, ClientDescriptor] = {}
+descriptors_by_handler_id: Dict[int, "ClientDescriptor"] = {}
-worker_classes: Dict[str, Type[QueueProcessingWorker]] = {}
-queues: Dict[str, Dict[str, Type[QueueProcessingWorker]]] = {}
+worker_classes: Dict[str, Type["QueueProcessingWorker"]] = {}
+queues: Dict[str, Dict[str, Type["QueueProcessingWorker"]]] = {}
-AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional[LDAPSearch] = None
+AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional["LDAPSearch"] = None
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-22 01:09:50 +02:00
|
|
|
events: List[Mapping[str, Any]] = []
|
2021-05-28 07:27:50 +02:00
|
|
|
with self.tornado_redirected_to_list(events, expected_num_events=1):
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.api_delete(
|
2021-02-12 08:20:45 +01:00
|
|
|
reaction_sender, f"/api/v1/messages/{pm_id}/reactions", reaction_info
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2016-11-30 08:14:46 +01:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
event = events[0]["event"]
|
|
|
|
event_user_ids = set(events[0]["users"])
|
2016-11-30 08:14:46 +01:00
|
|
|
|
|
|
|
self.assertEqual(expected_recipient_ids, event_user_ids)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(event["user"]["email"], reaction_sender.email)
|
|
|
|
self.assertEqual(event["type"], "reaction")
|
|
|
|
self.assertEqual(event["op"], "remove")
|
|
|
|
self.assertEqual(event["emoji_name"], "smile")
|
|
|
|
self.assertEqual(event["message_id"], pm_id)
|
2017-10-08 09:34:59 +02:00
|
|
|
|
2021-03-27 03:05:56 +01:00
|
|
|
def test_reaction_event_scope(self) -> None:
|
|
|
|
iago = self.example_user("iago")
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
polonius = self.example_user("polonius")
|
|
|
|
reaction_info = {
|
|
|
|
"emoji_name": "smile",
|
|
|
|
}
|
|
|
|
|
|
|
|
# Test `invite_only` streams with `!history_public_to_subscribers` and `!is_web_public`
|
|
|
|
stream = self.make_stream(
|
|
|
|
"test_reactions_stream", invite_only=True, history_public_to_subscribers=False
|
|
|
|
)
|
|
|
|
self.subscribe(iago, stream.name)
|
|
|
|
message_before_id = self.send_stream_message(
|
|
|
|
iago, "test_reactions_stream", "before subscription history private"
|
|
|
|
)
|
|
|
|
self.subscribe(hamlet, stream.name)
|
|
|
|
self.subscribe(polonius, stream.name)
|
|
|
|
|
|
|
|
# Hamlet and Polonius joined after the message was sent, and
|
|
|
|
# so only Iago should receive the event.
|
|
|
|
events: List[Mapping[str, Any]] = []
|
2021-05-28 07:27:50 +02:00
|
|
|
with self.tornado_redirected_to_list(events, expected_num_events=1):
|
2021-03-27 03:05:56 +01:00
|
|
|
result = self.api_post(
|
|
|
|
iago, f"/api/v1/messages/{message_before_id}/reactions", reaction_info
|
|
|
|
)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
event = events[0]["event"]
|
|
|
|
self.assertEqual(event["type"], "reaction")
|
|
|
|
event_user_ids = set(events[0]["users"])
|
|
|
|
self.assertEqual(event_user_ids, {iago.id})
|
|
|
|
remove = self.api_delete(
|
|
|
|
iago, f"/api/v1/messages/{message_before_id}/reactions", reaction_info
|
|
|
|
)
|
|
|
|
self.assert_json_success(remove)
|
|
|
|
|
|
|
|
# Reaction to a Message sent after subscription, should
|
|
|
|
# trigger events for all subscribers (Iago, Hamlet and Polonius).
|
|
|
|
message_after_id = self.send_stream_message(
|
|
|
|
iago, "test_reactions_stream", "after subscription history private"
|
|
|
|
)
|
2021-05-28 07:27:50 +02:00
|
|
|
with self.tornado_redirected_to_list(events, expected_num_events=1):
|
2021-03-27 03:05:56 +01:00
|
|
|
result = self.api_post(
|
|
|
|
iago, f"/api/v1/messages/{message_after_id}/reactions", reaction_info
|
|
|
|
)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
event = events[0]["event"]
|
|
|
|
self.assertEqual(event["type"], "reaction")
|
|
|
|
event_user_ids = set(events[0]["users"])
|
|
|
|
self.assertEqual(event_user_ids, {iago.id, hamlet.id, polonius.id})
|
|
|
|
remove = self.api_delete(
|
|
|
|
iago, f"/api/v1/messages/{message_after_id}/reactions", reaction_info
|
|
|
|
)
|
|
|
|
self.assert_json_success(remove)
|
|
|
|
|
|
|
|
# Make stream history public to subscribers
|
2021-12-11 00:41:25 +01:00
|
|
|
do_change_stream_permission(
|
2022-08-01 12:54:46 +02:00
|
|
|
stream,
|
|
|
|
invite_only=False,
|
|
|
|
history_public_to_subscribers=True,
|
|
|
|
is_web_public=False,
|
|
|
|
acting_user=iago,
|
2021-12-11 00:41:25 +01:00
|
|
|
)
|
2021-03-27 03:05:56 +01:00
|
|
|
# Since stream history is public to subscribers, reacting to
|
2021-04-17 18:24:02 +02:00
|
|
|
# message_before_id should notify all subscribers:
|
2021-03-27 03:05:56 +01:00
|
|
|
# Iago and Hamlet.
|
2021-05-28 07:27:50 +02:00
|
|
|
with self.tornado_redirected_to_list(events, expected_num_events=1):
|
2021-03-27 03:05:56 +01:00
|
|
|
result = self.api_post(
|
|
|
|
iago, f"/api/v1/messages/{message_before_id}/reactions", reaction_info
|
|
|
|
)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
event = events[0]["event"]
|
|
|
|
self.assertEqual(event["type"], "reaction")
|
|
|
|
event_user_ids = set(events[0]["users"])
|
2021-04-17 18:24:02 +02:00
|
|
|
self.assertEqual(event_user_ids, {iago.id, hamlet.id, polonius.id})
|
2021-03-27 03:05:56 +01:00
|
|
|
remove = self.api_delete(
|
|
|
|
iago, f"/api/v1/messages/{message_before_id}/reactions", reaction_info
|
|
|
|
)
|
|
|
|
self.assert_json_success(remove)
|
|
|
|
|
|
|
|
# Make stream web_public as well.
|
2022-08-01 12:54:46 +02:00
|
|
|
do_change_stream_permission(
|
|
|
|
stream,
|
|
|
|
invite_only=False,
|
|
|
|
history_public_to_subscribers=True,
|
|
|
|
is_web_public=True,
|
|
|
|
acting_user=iago,
|
|
|
|
)
|
2021-03-27 03:05:56 +01:00
|
|
|
# For is_web_public streams, events even on old messages
|
|
|
|
# should go to all subscribers, including guests like polonius.
|
2021-05-28 07:27:50 +02:00
|
|
|
with self.tornado_redirected_to_list(events, expected_num_events=1):
|
2021-03-27 03:05:56 +01:00
|
|
|
result = self.api_post(
|
|
|
|
iago, f"/api/v1/messages/{message_before_id}/reactions", reaction_info
|
|
|
|
)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
event = events[0]["event"]
|
|
|
|
self.assertEqual(event["type"], "reaction")
|
|
|
|
event_user_ids = set(events[0]["users"])
|
|
|
|
self.assertEqual(event_user_ids, {iago.id, hamlet.id, polonius.id})
|
|
|
|
remove = self.api_delete(
|
|
|
|
iago, f"/api/v1/messages/{message_before_id}/reactions", reaction_info
|
|
|
|
)
|
|
|
|
self.assert_json_success(remove)
|
|
|
|
|
|
|
|
# Private message, event should go to both participants.
|
|
|
|
private_message_id = self.send_personal_message(
|
|
|
|
iago,
|
|
|
|
hamlet,
|
2021-04-25 22:54:23 +02:00
|
|
|
"hello to single receiver",
|
2021-03-27 03:05:56 +01:00
|
|
|
)
|
2021-05-28 07:27:50 +02:00
|
|
|
with self.tornado_redirected_to_list(events, expected_num_events=1):
|
2021-03-27 03:05:56 +01:00
|
|
|
result = self.api_post(
|
|
|
|
hamlet, f"/api/v1/messages/{private_message_id}/reactions", reaction_info
|
|
|
|
)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
event = events[0]["event"]
|
|
|
|
self.assertEqual(event["type"], "reaction")
|
|
|
|
event_user_ids = set(events[0]["users"])
|
|
|
|
self.assertEqual(event_user_ids, {iago.id, hamlet.id})
|
|
|
|
|
|
|
|
# Group private message; event should go to all participants.
|
|
|
|
huddle_message_id = self.send_huddle_message(
|
|
|
|
hamlet,
|
|
|
|
[polonius, iago],
|
2022-02-08 00:13:33 +01:00
|
|
|
"hello message to multiple receiver",
|
2021-03-27 03:05:56 +01:00
|
|
|
)
|
2021-05-28 07:27:50 +02:00
|
|
|
with self.tornado_redirected_to_list(events, expected_num_events=1):
|
2021-03-27 03:05:56 +01:00
|
|
|
result = self.api_post(
|
|
|
|
polonius, f"/api/v1/messages/{huddle_message_id}/reactions", reaction_info
|
|
|
|
)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
event = events[0]["event"]
|
|
|
|
self.assertEqual(event["type"], "reaction")
|
|
|
|
event_user_ids = set(events[0]["users"])
|
|
|
|
self.assertEqual(event_user_ids, {iago.id, hamlet.id, polonius.id})
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2017-11-18 20:52:22 +01:00
|
|
|
class EmojiReactionBase(ZulipTestCase):
|
2019-05-30 13:01:06 +02:00
|
|
|
"""Reusable testing functions for emoji reactions tests. Be careful when
|
|
|
|
changing this: It's used in test_retention.py as well."""
|
2020-04-22 01:45:30 +02:00
|
|
|
|
2017-11-18 20:52:22 +01:00
|
|
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
2022-06-08 04:52:09 +02:00
|
|
|
def post_reaction(self, reaction_info: Dict[str, str]) -> "TestHttpResponse":
|
2020-07-16 17:06:39 +02:00
|
|
|
message_id = 1
|
|
|
|
|
|
|
|
result = self.api_post(
|
2021-02-12 08:20:45 +01:00
|
|
|
self.example_user("hamlet"), f"/api/v1/messages/{message_id}/reactions", reaction_info
|
2020-07-16 17:06:39 +02:00
|
|
|
)
|
2017-10-08 09:34:59 +02:00
|
|
|
return result
|
|
|
|
|
2022-06-08 04:52:09 +02:00
|
|
|
def post_other_reaction(self, reaction_info: Dict[str, str]) -> "TestHttpResponse":
|
2020-07-16 17:06:39 +02:00
|
|
|
message_id = 1
|
2017-11-18 20:52:22 +01:00
|
|
|
|
2020-07-16 17:06:39 +02:00
|
|
|
result = self.api_post(
|
2021-02-12 08:20:45 +01:00
|
|
|
self.example_user("AARON"), f"/api/v1/messages/{message_id}/reactions", reaction_info
|
2020-07-16 17:06:39 +02:00
|
|
|
)
|
2017-11-18 20:52:22 +01:00
|
|
|
return result
|
|
|
|
|
2022-06-08 04:52:09 +02:00
|
|
|
def delete_reaction(self, reaction_info: Dict[str, str]) -> "TestHttpResponse":
|
2020-07-16 17:06:39 +02:00
|
|
|
message_id = 1
|
2017-10-08 09:34:59 +02:00
|
|
|
|
2020-07-16 17:06:39 +02:00
|
|
|
result = self.api_delete(
|
2021-02-12 08:20:45 +01:00
|
|
|
self.example_user("hamlet"), f"/api/v1/messages/{message_id}/reactions", reaction_info
|
2020-07-16 17:06:39 +02:00
|
|
|
)
|
2017-11-18 20:52:22 +01:00
|
|
|
return result
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
def get_message_reactions(
|
|
|
|
self, message_id: int, emoji_code: str, reaction_type: str
|
|
|
|
) -> List[Reaction]:
|
2017-10-08 09:34:59 +02:00
|
|
|
message = Message.objects.get(id=message_id)
|
2021-02-12 08:19:30 +01:00
|
|
|
reactions = Reaction.objects.filter(
|
|
|
|
message=message, emoji_code=emoji_code, reaction_type=reaction_type
|
|
|
|
)
|
2017-10-08 09:34:59 +02:00
|
|
|
return list(reactions)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2017-11-18 20:52:22 +01:00
|
|
|
class DefaultEmojiReactionTests(EmojiReactionBase):
|
2017-11-05 10:51:25 +01:00
|
|
|
def setUp(self) -> None:
|
2019-10-19 20:47:00 +02:00
|
|
|
super().setUp()
|
2017-10-08 09:34:59 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"reaction_type": "unicode_emoji",
|
|
|
|
"emoji_name": "hamburger",
|
|
|
|
"emoji_code": "1f354",
|
2017-10-08 09:34:59 +02:00
|
|
|
}
|
|
|
|
result = self.post_reaction(reaction_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_default_emoji_reaction(self) -> None:
|
2017-10-08 09:34:59 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"reaction_type": "unicode_emoji",
|
|
|
|
"emoji_name": "thumbs_up",
|
|
|
|
"emoji_code": "1f44d",
|
2017-10-08 09:34:59 +02:00
|
|
|
}
|
|
|
|
result = self.post_reaction(reaction_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_default_emoji_invalid_code(self) -> None:
|
2017-10-08 09:34:59 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"reaction_type": "unicode_emoji",
|
|
|
|
"emoji_name": "hamburger",
|
|
|
|
"emoji_code": "TBD",
|
2017-10-08 09:34:59 +02:00
|
|
|
}
|
|
|
|
result = self.post_reaction(reaction_info)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "Invalid emoji code.")
|
2017-10-08 09:34:59 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_default_emoji_invalid_name(self) -> None:
|
2017-10-08 09:34:59 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"reaction_type": "unicode_emoji",
|
|
|
|
"emoji_name": "non-existent",
|
|
|
|
"emoji_code": "1f44d",
|
2017-10-08 09:34:59 +02:00
|
|
|
}
|
|
|
|
result = self.post_reaction(reaction_info)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "Invalid emoji name.")
|
2017-10-08 09:34:59 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_to_existing_renamed_default_emoji_reaction(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
hamlet = self.example_user("hamlet")
|
2017-10-08 09:34:59 +02:00
|
|
|
message = Message.objects.get(id=1)
|
2021-02-12 08:19:30 +01:00
|
|
|
reaction = Reaction.objects.create(
|
|
|
|
user_profile=hamlet,
|
|
|
|
message=message,
|
2021-02-12 08:20:45 +01:00
|
|
|
emoji_name="old_name",
|
|
|
|
emoji_code="1f603",
|
|
|
|
reaction_type="unicode_emoji",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2017-10-08 09:34:59 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"reaction_type": "unicode_emoji",
|
|
|
|
"emoji_name": "smiley",
|
|
|
|
"emoji_code": "1f603",
|
2017-10-08 09:34:59 +02:00
|
|
|
}
|
2020-07-16 17:06:39 +02:00
|
|
|
result = self.post_other_reaction(reaction_info)
|
2017-10-08 09:34:59 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
reactions = self.get_message_reactions(1, "1f603", "unicode_emoji")
|
2017-10-08 09:34:59 +02:00
|
|
|
for reaction in reactions:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(reaction.emoji_name, "old_name")
|
2017-10-08 09:34:59 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_duplicate_reaction(self) -> None:
|
2017-10-08 09:34:59 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"reaction_type": "unicode_emoji",
|
|
|
|
"emoji_name": "non-existent",
|
|
|
|
"emoji_code": "1f354",
|
2017-10-08 09:34:59 +02:00
|
|
|
}
|
|
|
|
result = self.post_reaction(reaction_info)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "Reaction already exists.")
|
2017-10-08 09:34:59 +02:00
|
|
|
|
2018-07-09 08:45:56 +02:00
|
|
|
def test_add_reaction_by_name(self) -> None:
|
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"reaction_type": "unicode_emoji",
|
|
|
|
"emoji_name": "+1",
|
2018-07-09 08:45:56 +02:00
|
|
|
}
|
|
|
|
result = self.post_reaction(reaction_info)
|
|
|
|
self.assert_json_success(result)
|
2021-02-12 08:20:45 +01:00
|
|
|
hamlet = self.example_user("hamlet")
|
2018-07-09 08:45:56 +02:00
|
|
|
message = Message.objects.get(id=1)
|
|
|
|
self.assertTrue(
|
2021-02-12 08:19:30 +01:00
|
|
|
Reaction.objects.filter(
|
|
|
|
user_profile=hamlet,
|
|
|
|
message=message,
|
2021-02-12 08:20:45 +01:00
|
|
|
emoji_name=reaction_info["emoji_name"],
|
|
|
|
emoji_code="1f44d",
|
|
|
|
reaction_type="unicode_emoji",
|
2021-02-12 08:19:30 +01:00
|
|
|
).exists(),
|
2018-07-09 08:45:56 +02:00
|
|
|
)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_preserve_non_canonical_name(self) -> None:
|
2017-10-08 09:34:59 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"reaction_type": "unicode_emoji",
|
|
|
|
"emoji_name": "+1",
|
|
|
|
"emoji_code": "1f44d",
|
2017-10-08 09:34:59 +02:00
|
|
|
}
|
|
|
|
result = self.post_reaction(reaction_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
reactions = self.get_message_reactions(1, "1f44d", "unicode_emoji")
|
2017-10-08 09:34:59 +02:00
|
|
|
for reaction in reactions:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(reaction.emoji_name, "+1")
|
2017-10-08 09:34:59 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_reaction_name_collapse(self) -> None:
|
2017-10-08 09:34:59 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"reaction_type": "unicode_emoji",
|
|
|
|
"emoji_name": "+1",
|
|
|
|
"emoji_code": "1f44d",
|
2017-10-08 09:34:59 +02:00
|
|
|
}
|
|
|
|
result = self.post_reaction(reaction_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
reaction_info["emoji_name"] = "thumbs_up"
|
2020-07-16 17:06:39 +02:00
|
|
|
result = self.post_other_reaction(reaction_info)
|
2017-10-08 09:34:59 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
reactions = self.get_message_reactions(1, "1f44d", "unicode_emoji")
|
2017-10-08 09:34:59 +02:00
|
|
|
for reaction in reactions:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(reaction.emoji_name, "+1")
|
2017-10-08 09:34:59 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_delete_default_emoji_reaction(self) -> None:
|
2017-10-08 09:34:59 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"reaction_type": "unicode_emoji",
|
|
|
|
"emoji_name": "hamburger",
|
|
|
|
"emoji_code": "1f354",
|
2017-10-08 09:34:59 +02:00
|
|
|
}
|
|
|
|
result = self.delete_reaction(reaction_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2018-07-09 10:48:42 +02:00
|
|
|
def test_delete_insufficient_arguments_reaction(self) -> None:
|
|
|
|
result = self.delete_reaction({})
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assert_json_error(
|
|
|
|
result,
|
2021-02-12 08:20:45 +01:00
|
|
|
"At least one of the following arguments must be present: emoji_name, emoji_code",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2018-07-09 10:48:42 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_delete_non_existing_emoji_reaction(self) -> None:
|
2017-10-08 09:34:59 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"reaction_type": "unicode_emoji",
|
|
|
|
"emoji_name": "thumbs_up",
|
|
|
|
"emoji_code": "1f44d",
|
2017-10-08 09:34:59 +02:00
|
|
|
}
|
|
|
|
result = self.delete_reaction(reaction_info)
|
|
|
|
self.assert_json_error(result, "Reaction doesn't exist.")
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_delete_renamed_default_emoji(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
hamlet = self.example_user("hamlet")
|
2017-10-08 09:34:59 +02:00
|
|
|
message = Message.objects.get(id=1)
|
2021-02-12 08:19:30 +01:00
|
|
|
Reaction.objects.create(
|
|
|
|
user_profile=hamlet,
|
|
|
|
message=message,
|
2021-02-12 08:20:45 +01:00
|
|
|
emoji_name="old_name",
|
|
|
|
emoji_code="1f44f",
|
|
|
|
reaction_type="unicode_emoji",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2017-10-08 09:34:59 +02:00
|
|
|
|
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"reaction_type": "unicode_emoji",
|
|
|
|
"emoji_name": "new_name",
|
|
|
|
"emoji_code": "1f44f",
|
2017-10-08 09:34:59 +02:00
|
|
|
}
|
|
|
|
result = self.delete_reaction(reaction_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2018-07-09 10:48:42 +02:00
|
|
|
def test_delete_reaction_by_name(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
hamlet = self.example_user("hamlet")
|
2018-07-09 10:48:42 +02:00
|
|
|
message = Message.objects.get(id=1)
|
2021-02-12 08:19:30 +01:00
|
|
|
Reaction.objects.create(
|
|
|
|
user_profile=hamlet,
|
|
|
|
message=message,
|
2021-02-12 08:20:45 +01:00
|
|
|
emoji_name="+1",
|
|
|
|
emoji_code="1f44d",
|
|
|
|
reaction_type="unicode_emoji",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2018-07-09 10:48:42 +02:00
|
|
|
|
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"reaction_type": "unicode_emoji",
|
|
|
|
"emoji_name": "+1",
|
2018-07-09 10:48:42 +02:00
|
|
|
}
|
|
|
|
result = self.delete_reaction(reaction_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
self.assertFalse(
|
2021-02-12 08:19:30 +01:00
|
|
|
Reaction.objects.filter(
|
|
|
|
user_profile=hamlet,
|
|
|
|
message=message,
|
2021-02-12 08:20:45 +01:00
|
|
|
emoji_name=reaction_info["emoji_name"],
|
|
|
|
emoji_code="1f44d",
|
|
|
|
reaction_type="unicode_emoji",
|
2021-02-12 08:19:30 +01:00
|
|
|
).exists(),
|
2018-07-09 10:48:42 +02:00
|
|
|
)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_react_historical(self) -> None:
|
2017-10-08 09:34:59 +02:00
|
|
|
"""
|
|
|
|
Reacting with valid emoji on a historical message succeeds.
|
|
|
|
"""
|
|
|
|
stream_name = "Saxony"
|
|
|
|
self.subscribe(self.example_user("cordelia"), stream_name)
|
2020-03-07 11:43:05 +01:00
|
|
|
message_id = self.send_stream_message(self.example_user("cordelia"), stream_name)
|
2017-10-08 09:34:59 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
user_profile = self.example_user("hamlet")
|
2017-10-08 09:34:59 +02:00
|
|
|
|
|
|
|
# Verify that hamlet did not receive the message.
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertFalse(
|
|
|
|
UserMessage.objects.filter(user_profile=user_profile, message_id=message_id).exists()
|
|
|
|
)
|
2017-10-08 09:34:59 +02:00
|
|
|
|
|
|
|
# Have hamlet react to the message
|
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"reaction_type": "unicode_emoji",
|
|
|
|
"emoji_name": "hamburger",
|
|
|
|
"emoji_code": "1f354",
|
2017-10-08 09:34:59 +02:00
|
|
|
}
|
2020-07-16 17:06:39 +02:00
|
|
|
|
|
|
|
result = self.api_post(
|
2021-02-12 08:20:45 +01:00
|
|
|
user_profile, f"/api/v1/messages/{message_id}/reactions", reaction_info
|
2020-07-16 17:06:39 +02:00
|
|
|
)
|
2017-10-08 09:34:59 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
# Fetch the now-created UserMessage object to confirm it exists and is historical
|
|
|
|
user_message = UserMessage.objects.get(user_profile=user_profile, message_id=message_id)
|
|
|
|
self.assertTrue(user_message.flags.historical)
|
|
|
|
self.assertTrue(user_message.flags.read)
|
|
|
|
self.assertFalse(user_message.flags.starred)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2017-11-18 20:52:22 +01:00
|
|
|
class ZulipExtraEmojiReactionTest(EmojiReactionBase):
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_zulip_emoji_reaction(self) -> None:
|
2020-07-16 17:06:39 +02:00
|
|
|
result = self.post_reaction(zulip_reaction_info())
|
2017-10-08 09:34:59 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_duplicate_zulip_reaction(self) -> None:
|
2020-07-16 17:06:39 +02:00
|
|
|
result = self.post_reaction(zulip_reaction_info())
|
2017-10-08 09:34:59 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2020-07-16 17:06:39 +02:00
|
|
|
result = self.post_reaction(zulip_reaction_info())
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "Reaction already exists.")
|
2017-10-08 09:34:59 +02:00
|
|
|
|
2017-11-19 07:07:29 +01:00
|
|
|
def test_add_invalid_extra_emoji(self) -> None:
|
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"emoji_name": "extra_emoji",
|
|
|
|
"emoji_code": "extra_emoji",
|
|
|
|
"reaction_type": "zulip_extra_emoji",
|
2017-11-19 07:07:29 +01:00
|
|
|
}
|
|
|
|
result = self.post_reaction(reaction_info)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "Invalid emoji code.")
|
2017-11-19 07:07:29 +01:00
|
|
|
|
2017-11-21 00:25:40 +01:00
|
|
|
def test_add_invalid_emoji_name(self) -> None:
|
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"emoji_name": "zulip_invalid",
|
|
|
|
"emoji_code": "zulip",
|
|
|
|
"reaction_type": "zulip_extra_emoji",
|
2017-11-21 00:25:40 +01:00
|
|
|
}
|
|
|
|
result = self.post_reaction(reaction_info)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "Invalid emoji name.")
|
2017-11-21 00:25:40 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_delete_zulip_emoji(self) -> None:
|
2020-07-16 17:06:39 +02:00
|
|
|
result = self.post_reaction(zulip_reaction_info())
|
2017-10-08 09:34:59 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2020-07-16 17:06:39 +02:00
|
|
|
result = self.delete_reaction(zulip_reaction_info())
|
2017-10-08 09:34:59 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_delete_non_existent_zulip_reaction(self) -> None:
|
2020-07-16 17:06:39 +02:00
|
|
|
result = self.delete_reaction(zulip_reaction_info())
|
2017-10-08 09:34:59 +02:00
|
|
|
self.assert_json_error(result, "Reaction doesn't exist.")
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2017-11-18 20:52:22 +01:00
|
|
|
class RealmEmojiReactionTests(EmojiReactionBase):
|
2017-12-15 16:54:07 +01:00
|
|
|
def setUp(self) -> None:
|
2019-10-19 20:47:00 +02:00
|
|
|
super().setUp()
|
2017-12-15 16:54:07 +01:00
|
|
|
green_tick_emoji = RealmEmoji.objects.get(name="green_tick")
|
|
|
|
self.default_reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"reaction_type": "realm_emoji",
|
|
|
|
"emoji_name": "green_tick",
|
|
|
|
"emoji_code": str(green_tick_emoji.id),
|
2017-10-08 09:34:59 +02:00
|
|
|
}
|
2017-12-15 16:54:07 +01:00
|
|
|
|
|
|
|
def test_add_realm_emoji(self) -> None:
|
|
|
|
result = self.post_reaction(self.default_reaction_info)
|
2017-10-08 09:34:59 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_realm_emoji_invalid_code(self) -> None:
|
2017-10-08 09:34:59 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"reaction_type": "realm_emoji",
|
|
|
|
"emoji_name": "green_tick",
|
|
|
|
"emoji_code": "9999",
|
2017-10-08 09:34:59 +02:00
|
|
|
}
|
|
|
|
result = self.post_reaction(reaction_info)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "Invalid custom emoji.")
|
2017-10-08 09:34:59 +02:00
|
|
|
|
2017-11-21 00:25:40 +01:00
|
|
|
def test_add_realm_emoji_invalid_name(self) -> None:
|
2018-03-11 18:55:20 +01:00
|
|
|
green_tick_emoji = RealmEmoji.objects.get(name="green_tick")
|
2017-11-21 00:25:40 +01:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"reaction_type": "realm_emoji",
|
|
|
|
"emoji_name": "bogus_name",
|
|
|
|
"emoji_code": str(green_tick_emoji.id),
|
2017-11-21 00:25:40 +01:00
|
|
|
}
|
|
|
|
result = self.post_reaction(reaction_info)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "Invalid custom emoji name.")
|
2017-11-21 00:25:40 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_deactivated_realm_emoji(self) -> None:
|
2017-10-08 09:34:59 +02:00
|
|
|
emoji = RealmEmoji.objects.get(name="green_tick")
|
|
|
|
emoji.deactivated = True
|
2021-02-12 08:20:45 +01:00
|
|
|
emoji.save(update_fields=["deactivated"])
|
2017-10-08 09:34:59 +02:00
|
|
|
|
2017-12-15 16:54:07 +01:00
|
|
|
result = self.post_reaction(self.default_reaction_info)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_json_error(result, "This custom emoji has been deactivated.")
|
2017-10-08 09:34:59 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_to_existing_deactivated_realm_emoji_reaction(self) -> None:
|
2017-12-15 16:54:07 +01:00
|
|
|
result = self.post_reaction(self.default_reaction_info)
|
2017-10-08 09:34:59 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
emoji = RealmEmoji.objects.get(name="green_tick")
|
|
|
|
emoji.deactivated = True
|
2021-02-12 08:20:45 +01:00
|
|
|
emoji.save(update_fields=["deactivated"])
|
2017-10-08 09:34:59 +02:00
|
|
|
|
2020-07-16 17:06:39 +02:00
|
|
|
result = self.post_other_reaction(self.default_reaction_info)
|
2017-10-08 09:34:59 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
reactions = self.get_message_reactions(
|
2021-02-12 08:20:45 +01:00
|
|
|
1, self.default_reaction_info["emoji_code"], "realm_emoji"
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(reactions, 2)
|
2017-10-08 09:34:59 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_remove_realm_emoji_reaction(self) -> None:
|
2017-12-15 16:54:07 +01:00
|
|
|
result = self.post_reaction(self.default_reaction_info)
|
2017-10-08 09:34:59 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2017-12-15 16:54:07 +01:00
|
|
|
result = self.delete_reaction(self.default_reaction_info)
|
2017-10-08 09:34:59 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_remove_deactivated_realm_emoji_reaction(self) -> None:
|
2017-12-15 16:54:07 +01:00
|
|
|
result = self.post_reaction(self.default_reaction_info)
|
2017-10-08 09:34:59 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
emoji = RealmEmoji.objects.get(name="green_tick")
|
|
|
|
emoji.deactivated = True
|
2021-02-12 08:20:45 +01:00
|
|
|
emoji.save(update_fields=["deactivated"])
|
2017-10-08 09:34:59 +02:00
|
|
|
|
2017-12-15 16:54:07 +01:00
|
|
|
result = self.delete_reaction(self.default_reaction_info)
|
2017-10-08 09:34:59 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_remove_non_existent_realm_emoji_reaction(self) -> None:
|
2017-10-08 09:34:59 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"reaction_type": "realm_emoji",
|
|
|
|
"emoji_name": "non_existent",
|
|
|
|
"emoji_code": "TBD",
|
2017-10-08 09:34:59 +02:00
|
|
|
}
|
|
|
|
result = self.delete_reaction(reaction_info)
|
|
|
|
self.assert_json_error(result, "Reaction doesn't exist.")
|
|
|
|
|
|
|
|
def test_invalid_reaction_type(self) -> None:
|
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"emoji_name": "zulip",
|
|
|
|
"emoji_code": "zulip",
|
|
|
|
"reaction_type": "nonexistent_emoji_type",
|
2017-10-08 09:34:59 +02:00
|
|
|
}
|
2020-03-10 11:48:26 +01:00
|
|
|
sender = self.example_user("hamlet")
|
2017-10-08 09:34:59 +02:00
|
|
|
message_id = 1
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.api_post(sender, f"/api/v1/messages/{message_id}/reactions", reaction_info)
|
2017-11-19 07:07:29 +01:00
|
|
|
self.assert_json_error(result, "Invalid emoji type.")
|
2017-10-08 09:34:59 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2017-11-18 20:52:22 +01:00
|
|
|
class ReactionAPIEventTest(EmojiReactionBase):
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_event(self) -> None:
|
2017-10-08 09:34:59 +02:00
|
|
|
"""
|
|
|
|
Recipients of the message receive the reaction event
|
|
|
|
and event contains relevant data
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
pm_sender = self.example_user("hamlet")
|
|
|
|
pm_recipient = self.example_user("othello")
|
2017-10-08 09:34:59 +02:00
|
|
|
reaction_sender = pm_recipient
|
2020-03-07 11:43:05 +01:00
|
|
|
pm_id = self.send_personal_message(pm_sender, pm_recipient)
|
2020-04-09 21:51:58 +02:00
|
|
|
expected_recipient_ids = {pm_sender.id, pm_recipient.id}
|
2017-10-08 09:34:59 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"emoji_name": "hamburger",
|
|
|
|
"emoji_code": "1f354",
|
|
|
|
"reaction_type": "unicode_emoji",
|
2017-10-08 09:34:59 +02:00
|
|
|
}
|
python: Convert assignment type annotations to Python 3.6 style.
This commit was split by tabbott; this piece covers the vast majority
of files in Zulip, but excludes scripts/, tools/, and puppet/ to help
ensure we at least show the right error messages for Xenial systems.
We can likely further refine the remaining pieces with some testing.
Generated by com2ann, with whitespace fixes and various manual fixes
for runtime issues:
- invoiced_through: Optional[LicenseLedger] = models.ForeignKey(
+ invoiced_through: Optional["LicenseLedger"] = models.ForeignKey(
-_apns_client: Optional[APNsClient] = None
+_apns_client: Optional["APNsClient"] = None
- notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- signup_notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ signup_notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- author: Optional[UserProfile] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
+ author: Optional["UserProfile"] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
- bot_owner: Optional[UserProfile] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
+ bot_owner: Optional["UserProfile"] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
- default_sending_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
- default_events_register_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_sending_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_events_register_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
-descriptors_by_handler_id: Dict[int, ClientDescriptor] = {}
+descriptors_by_handler_id: Dict[int, "ClientDescriptor"] = {}
-worker_classes: Dict[str, Type[QueueProcessingWorker]] = {}
-queues: Dict[str, Dict[str, Type[QueueProcessingWorker]]] = {}
+worker_classes: Dict[str, Type["QueueProcessingWorker"]] = {}
+queues: Dict[str, Dict[str, Type["QueueProcessingWorker"]]] = {}
-AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional[LDAPSearch] = None
+AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional["LDAPSearch"] = None
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-22 01:09:50 +02:00
|
|
|
events: List[Mapping[str, Any]] = []
|
2021-05-28 07:27:50 +02:00
|
|
|
with self.tornado_redirected_to_list(events, expected_num_events=1):
|
2022-04-14 23:54:01 +02:00
|
|
|
with mock.patch("zerver.actions.reactions.send_event") as m:
|
2020-11-14 08:55:38 +01:00
|
|
|
m.side_effect = AssertionError(
|
|
|
|
"Events should be sent only after the transaction commits!"
|
|
|
|
)
|
2021-07-16 02:03:20 +02:00
|
|
|
self.api_post(reaction_sender, f"/api/v1/messages/{pm_id}/reactions", reaction_info)
|
2020-07-16 17:06:39 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
event = events[0]["event"]
|
|
|
|
event_user_ids = set(events[0]["users"])
|
2017-10-08 09:34:59 +02:00
|
|
|
|
|
|
|
self.assertEqual(expected_recipient_ids, event_user_ids)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(event["user"]["user_id"], reaction_sender.id)
|
|
|
|
self.assertEqual(event["user"]["email"], reaction_sender.email)
|
|
|
|
self.assertEqual(event["user"]["full_name"], reaction_sender.full_name)
|
|
|
|
self.assertEqual(event["type"], "reaction")
|
|
|
|
self.assertEqual(event["op"], "add")
|
|
|
|
self.assertEqual(event["message_id"], pm_id)
|
|
|
|
self.assertEqual(event["emoji_name"], reaction_info["emoji_name"])
|
|
|
|
self.assertEqual(event["emoji_code"], reaction_info["emoji_code"])
|
|
|
|
self.assertEqual(event["reaction_type"], reaction_info["reaction_type"])
|
2017-10-08 09:34:59 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_remove_event(self) -> None:
|
2017-10-08 09:34:59 +02:00
|
|
|
"""
|
|
|
|
Recipients of the message receive the reaction event
|
|
|
|
and event contains relevant data
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
pm_sender = self.example_user("hamlet")
|
|
|
|
pm_recipient = self.example_user("othello")
|
2017-10-08 09:34:59 +02:00
|
|
|
reaction_sender = pm_recipient
|
2020-03-07 11:43:05 +01:00
|
|
|
pm_id = self.send_personal_message(pm_sender, pm_recipient)
|
2020-04-09 21:51:58 +02:00
|
|
|
expected_recipient_ids = {pm_sender.id, pm_recipient.id}
|
2017-10-08 09:34:59 +02:00
|
|
|
reaction_info = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"emoji_name": "hamburger",
|
|
|
|
"emoji_code": "1f354",
|
|
|
|
"reaction_type": "unicode_emoji",
|
2017-10-08 09:34:59 +02:00
|
|
|
}
|
2020-07-16 17:06:39 +02:00
|
|
|
add = self.api_post(
|
|
|
|
reaction_sender,
|
2021-02-12 08:20:45 +01:00
|
|
|
f"/api/v1/messages/{pm_id}/reactions",
|
2020-07-16 17:06:39 +02:00
|
|
|
reaction_info,
|
|
|
|
)
|
2017-10-08 09:34:59 +02:00
|
|
|
self.assert_json_success(add)
|
|
|
|
|
python: Convert assignment type annotations to Python 3.6 style.
This commit was split by tabbott; this piece covers the vast majority
of files in Zulip, but excludes scripts/, tools/, and puppet/ to help
ensure we at least show the right error messages for Xenial systems.
We can likely further refine the remaining pieces with some testing.
Generated by com2ann, with whitespace fixes and various manual fixes
for runtime issues:
- invoiced_through: Optional[LicenseLedger] = models.ForeignKey(
+ invoiced_through: Optional["LicenseLedger"] = models.ForeignKey(
-_apns_client: Optional[APNsClient] = None
+_apns_client: Optional["APNsClient"] = None
- notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- signup_notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ signup_notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- author: Optional[UserProfile] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
+ author: Optional["UserProfile"] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
- bot_owner: Optional[UserProfile] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
+ bot_owner: Optional["UserProfile"] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
- default_sending_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
- default_events_register_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_sending_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_events_register_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
-descriptors_by_handler_id: Dict[int, ClientDescriptor] = {}
+descriptors_by_handler_id: Dict[int, "ClientDescriptor"] = {}
-worker_classes: Dict[str, Type[QueueProcessingWorker]] = {}
-queues: Dict[str, Dict[str, Type[QueueProcessingWorker]]] = {}
+worker_classes: Dict[str, Type["QueueProcessingWorker"]] = {}
+queues: Dict[str, Dict[str, Type["QueueProcessingWorker"]]] = {}
-AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional[LDAPSearch] = None
+AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional["LDAPSearch"] = None
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-22 01:09:50 +02:00
|
|
|
events: List[Mapping[str, Any]] = []
|
2021-05-28 07:27:50 +02:00
|
|
|
with self.tornado_redirected_to_list(events, expected_num_events=1):
|
2020-07-16 17:06:39 +02:00
|
|
|
result = self.api_delete(
|
|
|
|
reaction_sender,
|
2021-02-12 08:20:45 +01:00
|
|
|
f"/api/v1/messages/{pm_id}/reactions",
|
2020-07-16 17:06:39 +02:00
|
|
|
reaction_info,
|
|
|
|
)
|
|
|
|
|
2017-10-08 09:34:59 +02:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
event = events[0]["event"]
|
|
|
|
event_user_ids = set(events[0]["users"])
|
2017-10-08 09:34:59 +02:00
|
|
|
|
|
|
|
self.assertEqual(expected_recipient_ids, event_user_ids)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(event["user"]["user_id"], reaction_sender.id)
|
|
|
|
self.assertEqual(event["user"]["email"], reaction_sender.email)
|
|
|
|
self.assertEqual(event["user"]["full_name"], reaction_sender.full_name)
|
|
|
|
self.assertEqual(event["type"], "reaction")
|
|
|
|
self.assertEqual(event["op"], "remove")
|
|
|
|
self.assertEqual(event["message_id"], pm_id)
|
|
|
|
self.assertEqual(event["emoji_name"], reaction_info["emoji_name"])
|
|
|
|
self.assertEqual(event["emoji_code"], reaction_info["emoji_code"])
|
|
|
|
self.assertEqual(event["reaction_type"], reaction_info["reaction_type"])
|
2020-11-14 08:55:38 +01:00
|
|
|
|
|
|
|
def test_events_sent_after_transaction_commits(self) -> None:
|
|
|
|
"""
|
|
|
|
Tests that `send_event` is hooked to `transaction.on_commit`. This is important, because
|
|
|
|
we don't want to end up holding locks on message rows for too long if the event queue runs
|
|
|
|
into a problem.
|
|
|
|
"""
|
|
|
|
hamlet = self.example_user("hamlet")
|
tests: Ensure stream senders get a UserMessage row.
We now complain if a test author sends a stream message
that does not result in the sender getting a
UserMessage row for the message.
This is basically 100% equivalent to complaining that
the author failed to subscribe the sender to the stream
as part of the test setup, as far as I can tell, so the
AssertionError instructs the author to subscribe the
sender to the stream.
We exempt bots from this check, although it is
plausible we should only exempt the system bots like
the notification bot.
I considered auto-subscribing the sender to the stream,
but that can be a little more expensive than the
current check, and we generally want test setup to be
explicit.
If there is some legitimate way than a subscribed human
sender can't get a UserMessage, then we probably want
an explicit test for that, or we may want to change the
backend to just write a UserMessage row in that
hypothetical situation.
For most tests, including almost all the ones fixed
here, the author just wants their test setup to
realistically reflect normal operation, and often devs
may not realize that Cordelia is not subscribed to
Denmark or not realize that Hamlet is not subscribed to
Scotland.
Some of us don't remember our Shakespeare from high
school, and our stream subscriptions don't even
necessarily reflect which countries the Bard placed his
characters in.
There may also be some legitimate use case where an
author wants to simulate sending a message to an
unsubscribed stream, but for those edge cases, they can
always set allow_unsubscribed_sender to True.
2021-12-10 13:55:48 +01:00
|
|
|
self.send_stream_message(hamlet, "Denmark")
|
2020-11-14 08:55:38 +01:00
|
|
|
message = self.get_last_message()
|
|
|
|
reaction = Reaction(
|
|
|
|
user_profile=hamlet,
|
|
|
|
message=message,
|
|
|
|
emoji_name="whatever",
|
|
|
|
emoji_code="whatever",
|
|
|
|
reaction_type="whatever",
|
|
|
|
)
|
|
|
|
|
|
|
|
with self.tornado_redirected_to_list([], expected_num_events=1):
|
2022-04-14 23:54:01 +02:00
|
|
|
with mock.patch("zerver.actions.reactions.send_event") as m:
|
2020-11-14 08:55:38 +01:00
|
|
|
m.side_effect = AssertionError(
|
|
|
|
"Events should be sent only after the transaction commits."
|
|
|
|
)
|
|
|
|
notify_reaction_update(hamlet, message, reaction, "stuff")
|