2021-03-27 12:23:32 +01:00
|
|
|
from datetime import datetime, timezone
|
|
|
|
from unittest import mock
|
|
|
|
|
2021-04-10 19:32:22 +02:00
|
|
|
import orjson
|
|
|
|
|
2022-04-14 23:48:28 +02:00
|
|
|
from zerver.actions.users import do_deactivate_user
|
2021-03-27 13:31:26 +01:00
|
|
|
from zerver.lib.cache import cache_get, get_muting_users_cache_key
|
2021-03-27 12:23:32 +01:00
|
|
|
from zerver.lib.test_classes import ZulipTestCase
|
|
|
|
from zerver.lib.timestamp import datetime_to_timestamp
|
2021-03-27 13:31:26 +01:00
|
|
|
from zerver.lib.user_mutes import get_mute_object, get_muting_users, get_user_mutes
|
2021-03-27 13:45:49 +01:00
|
|
|
from zerver.models import RealmAuditLog, UserMessage, UserProfile
|
2021-03-27 12:23:32 +01:00
|
|
|
|
|
|
|
|
|
|
|
class MutedUsersTests(ZulipTestCase):
|
2021-04-07 19:20:46 +02:00
|
|
|
# Hamlet does the muting/unmuting, and Cordelia gets muted/unmuted.
|
2021-03-27 12:23:32 +01:00
|
|
|
def test_get_user_mutes(self) -> None:
|
2021-04-07 19:20:46 +02:00
|
|
|
hamlet = self.example_user("hamlet")
|
2021-03-27 12:23:32 +01:00
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
|
2021-04-07 19:20:46 +02:00
|
|
|
muted_users = get_user_mutes(hamlet)
|
2021-03-27 12:23:32 +01:00
|
|
|
self.assertEqual(muted_users, [])
|
|
|
|
mute_time = datetime(2021, 1, 1, tzinfo=timezone.utc)
|
|
|
|
|
2021-04-07 19:33:10 +02:00
|
|
|
with mock.patch("zerver.views.muting.timezone_now", return_value=mute_time):
|
2021-08-02 23:36:06 +02:00
|
|
|
url = f"/api/v1/users/me/muted_users/{cordelia.id}"
|
2021-04-07 19:33:10 +02:00
|
|
|
result = self.api_post(hamlet, url)
|
|
|
|
self.assert_json_success(result)
|
2021-03-27 12:23:32 +01:00
|
|
|
|
2021-04-07 19:20:46 +02:00
|
|
|
muted_users = get_user_mutes(hamlet)
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(muted_users, 1)
|
2021-03-27 12:23:32 +01:00
|
|
|
|
|
|
|
self.assertDictEqual(
|
|
|
|
muted_users[0],
|
|
|
|
{
|
|
|
|
"id": cordelia.id,
|
|
|
|
"timestamp": datetime_to_timestamp(mute_time),
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
def test_add_muted_user_mute_self(self) -> None:
|
2021-04-07 19:20:46 +02:00
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
self.login_user(hamlet)
|
2021-03-27 12:23:32 +01:00
|
|
|
|
2021-08-02 23:36:06 +02:00
|
|
|
url = f"/api/v1/users/me/muted_users/{hamlet.id}"
|
2021-04-07 19:20:46 +02:00
|
|
|
result = self.api_post(hamlet, url)
|
2021-03-27 12:23:32 +01:00
|
|
|
self.assert_json_error(result, "Cannot mute self")
|
|
|
|
|
|
|
|
def test_add_muted_user_mute_bot(self) -> None:
|
2021-04-07 19:20:46 +02:00
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
self.login_user(hamlet)
|
2021-03-27 12:23:32 +01:00
|
|
|
|
|
|
|
bot_info = {
|
|
|
|
"full_name": "The Bot of Hamlet",
|
|
|
|
"short_name": "hambot",
|
|
|
|
"bot_type": "1",
|
|
|
|
}
|
|
|
|
result = self.client_post("/json/bots", bot_info)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
muted_id = result.json()["user_id"]
|
|
|
|
|
2021-08-02 23:36:06 +02:00
|
|
|
url = f"/api/v1/users/me/muted_users/{muted_id}"
|
2021-04-07 19:20:46 +02:00
|
|
|
result = self.api_post(hamlet, url)
|
2021-03-27 12:23:32 +01:00
|
|
|
# Currently we do not allow muting bots. This is the error message
|
|
|
|
# from `access_user_by_id`.
|
|
|
|
self.assert_json_error(result, "No such user")
|
|
|
|
|
|
|
|
def test_add_muted_user_mute_twice(self) -> None:
|
2021-04-07 19:20:46 +02:00
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
self.login_user(hamlet)
|
|
|
|
cordelia = self.example_user("cordelia")
|
2021-03-27 12:23:32 +01:00
|
|
|
|
2021-08-02 23:36:06 +02:00
|
|
|
url = f"/api/v1/users/me/muted_users/{cordelia.id}"
|
2021-04-07 19:33:10 +02:00
|
|
|
result = self.api_post(hamlet, url)
|
|
|
|
self.assert_json_success(result)
|
2021-03-27 12:23:32 +01:00
|
|
|
|
2021-08-02 23:36:06 +02:00
|
|
|
url = f"/api/v1/users/me/muted_users/{cordelia.id}"
|
2021-04-07 19:20:46 +02:00
|
|
|
result = self.api_post(hamlet, url)
|
2021-03-27 12:23:32 +01:00
|
|
|
self.assert_json_error(result, "User already muted")
|
|
|
|
|
2022-03-25 00:45:35 +01:00
|
|
|
# Verify the error handling for the database level
|
|
|
|
# IntegrityError we'll get with a race between two processes
|
|
|
|
# trying to mute the user. To do this, we patch the
|
|
|
|
# get_mute_object function to always return None.
|
|
|
|
with mock.patch("zerver.views.muting.get_mute_object", return_value=None):
|
|
|
|
result = self.api_post(hamlet, url)
|
|
|
|
self.assert_json_error(result, "User already muted")
|
|
|
|
|
2021-07-07 16:55:52 +02:00
|
|
|
def _test_add_muted_user_valid_data(self, deactivate_user: bool = False) -> None:
|
2021-04-07 19:20:46 +02:00
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
self.login_user(hamlet)
|
|
|
|
cordelia = self.example_user("cordelia")
|
2021-04-07 19:03:15 +02:00
|
|
|
mute_time = datetime(2021, 1, 1, tzinfo=timezone.utc)
|
2021-03-27 12:23:32 +01:00
|
|
|
|
2021-07-07 16:55:52 +02:00
|
|
|
if deactivate_user:
|
|
|
|
do_deactivate_user(cordelia, acting_user=None)
|
|
|
|
|
2021-04-07 19:03:15 +02:00
|
|
|
with mock.patch("zerver.views.muting.timezone_now", return_value=mute_time):
|
2021-08-02 23:36:06 +02:00
|
|
|
url = f"/api/v1/users/me/muted_users/{cordelia.id}"
|
2021-04-07 19:20:46 +02:00
|
|
|
result = self.api_post(hamlet, url)
|
2021-03-27 12:23:32 +01:00
|
|
|
self.assert_json_success(result)
|
|
|
|
|
2021-04-07 19:03:15 +02:00
|
|
|
self.assertIn(
|
|
|
|
{
|
2021-04-07 19:20:46 +02:00
|
|
|
"id": cordelia.id,
|
2021-04-07 19:03:15 +02:00
|
|
|
"timestamp": datetime_to_timestamp(mute_time),
|
|
|
|
},
|
2021-04-07 19:20:46 +02:00
|
|
|
get_user_mutes(hamlet),
|
2021-04-07 19:03:15 +02:00
|
|
|
)
|
2021-04-08 06:20:43 +02:00
|
|
|
self.assertIsNotNone(get_mute_object(hamlet, cordelia))
|
2021-03-27 12:23:32 +01:00
|
|
|
|
2021-04-10 19:32:22 +02:00
|
|
|
audit_log_entries = list(
|
|
|
|
RealmAuditLog.objects.filter(acting_user=hamlet, modified_user=hamlet).values_list(
|
|
|
|
"event_type", "event_time", "extra_data"
|
|
|
|
)
|
|
|
|
)
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(audit_log_entries, 1)
|
2021-04-10 19:32:22 +02:00
|
|
|
audit_log_entry = audit_log_entries[0]
|
|
|
|
self.assertEqual(
|
|
|
|
audit_log_entry,
|
|
|
|
(
|
|
|
|
RealmAuditLog.USER_MUTED,
|
|
|
|
mute_time,
|
|
|
|
orjson.dumps({"muted_user_id": cordelia.id}).decode(),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
2021-07-07 16:55:52 +02:00
|
|
|
def test_add_muted_user_valid_data(self) -> None:
|
|
|
|
self._test_add_muted_user_valid_data()
|
|
|
|
|
|
|
|
def test_add_muted_user_deactivated_user(self) -> None:
|
|
|
|
self._test_add_muted_user_valid_data(deactivate_user=True)
|
|
|
|
|
2021-03-27 12:23:32 +01:00
|
|
|
def test_remove_muted_user_unmute_before_muting(self) -> None:
|
2021-04-07 19:20:46 +02:00
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
self.login_user(hamlet)
|
|
|
|
cordelia = self.example_user("cordelia")
|
2021-03-27 12:23:32 +01:00
|
|
|
|
2021-08-02 23:36:06 +02:00
|
|
|
url = f"/api/v1/users/me/muted_users/{cordelia.id}"
|
2021-04-07 19:20:46 +02:00
|
|
|
result = self.api_delete(hamlet, url)
|
2021-03-27 12:23:32 +01:00
|
|
|
self.assert_json_error(result, "User is not muted")
|
|
|
|
|
2021-07-07 16:55:52 +02:00
|
|
|
def _test_remove_muted_user_valid_data(self, deactivate_user: bool = False) -> None:
|
2021-04-07 19:20:46 +02:00
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
self.login_user(hamlet)
|
|
|
|
cordelia = self.example_user("cordelia")
|
2021-04-07 19:03:15 +02:00
|
|
|
mute_time = datetime(2021, 1, 1, tzinfo=timezone.utc)
|
2021-03-27 12:23:32 +01:00
|
|
|
|
2021-07-07 16:55:52 +02:00
|
|
|
if deactivate_user:
|
|
|
|
do_deactivate_user(cordelia, acting_user=None)
|
|
|
|
|
2021-04-10 19:32:22 +02:00
|
|
|
with mock.patch("zerver.views.muting.timezone_now", return_value=mute_time):
|
2021-08-02 23:36:06 +02:00
|
|
|
url = f"/api/v1/users/me/muted_users/{cordelia.id}"
|
2021-04-10 19:32:22 +02:00
|
|
|
result = self.api_post(hamlet, url)
|
|
|
|
self.assert_json_success(result)
|
2021-03-27 12:23:32 +01:00
|
|
|
|
2022-04-14 23:55:22 +02:00
|
|
|
with mock.patch("zerver.actions.muted_users.timezone_now", return_value=mute_time):
|
2021-04-10 19:32:22 +02:00
|
|
|
# To test that `RealmAuditLog` entry has correct `event_time`.
|
2021-08-02 23:36:06 +02:00
|
|
|
url = f"/api/v1/users/me/muted_users/{cordelia.id}"
|
2021-04-10 19:32:22 +02:00
|
|
|
result = self.api_delete(hamlet, url)
|
2021-03-27 12:23:32 +01:00
|
|
|
|
|
|
|
self.assert_json_success(result)
|
2021-04-07 19:03:15 +02:00
|
|
|
self.assertNotIn(
|
|
|
|
{
|
2021-04-07 19:20:46 +02:00
|
|
|
"id": cordelia.id,
|
2021-04-07 19:03:15 +02:00
|
|
|
"timestamp": datetime_to_timestamp(mute_time),
|
|
|
|
},
|
2021-04-07 19:20:46 +02:00
|
|
|
get_user_mutes(hamlet),
|
2021-04-07 19:03:15 +02:00
|
|
|
)
|
2021-04-08 06:20:43 +02:00
|
|
|
self.assertIsNone(get_mute_object(hamlet, cordelia))
|
2021-04-10 19:32:22 +02:00
|
|
|
|
|
|
|
audit_log_entries = list(
|
|
|
|
RealmAuditLog.objects.filter(acting_user=hamlet, modified_user=hamlet)
|
|
|
|
.values_list("event_type", "event_time", "extra_data")
|
|
|
|
.order_by("id")
|
|
|
|
)
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(audit_log_entries, 2)
|
2021-04-10 19:32:22 +02:00
|
|
|
audit_log_entry = audit_log_entries[1]
|
|
|
|
self.assertEqual(
|
|
|
|
audit_log_entry,
|
|
|
|
(
|
|
|
|
RealmAuditLog.USER_UNMUTED,
|
|
|
|
mute_time,
|
|
|
|
orjson.dumps({"unmuted_user_id": cordelia.id}).decode(),
|
|
|
|
),
|
|
|
|
)
|
2021-03-27 13:31:26 +01:00
|
|
|
|
2021-07-07 16:55:52 +02:00
|
|
|
def test_remove_muted_user_valid_data(self) -> None:
|
|
|
|
self._test_remove_muted_user_valid_data()
|
|
|
|
|
|
|
|
def test_remove_muted_user_deactivated_user(self) -> None:
|
|
|
|
self._test_remove_muted_user_valid_data(deactivate_user=True)
|
|
|
|
|
2021-03-27 13:31:26 +01:00
|
|
|
def test_get_muting_users(self) -> None:
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
self.login_user(hamlet)
|
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
|
2021-06-06 04:47:36 +02:00
|
|
|
self.assertEqual(None, cache_get(get_muting_users_cache_key(cordelia.id)))
|
|
|
|
self.assertEqual(set(), get_muting_users(cordelia.id))
|
|
|
|
self.assertEqual(set(), cache_get(get_muting_users_cache_key(cordelia.id))[0])
|
2021-03-27 13:31:26 +01:00
|
|
|
|
2021-08-02 23:36:06 +02:00
|
|
|
url = f"/api/v1/users/me/muted_users/{cordelia.id}"
|
2021-03-27 13:31:26 +01:00
|
|
|
result = self.api_post(hamlet, url)
|
|
|
|
self.assert_json_success(result)
|
2021-06-06 04:47:36 +02:00
|
|
|
self.assertEqual(None, cache_get(get_muting_users_cache_key(cordelia.id)))
|
|
|
|
self.assertEqual({hamlet.id}, get_muting_users(cordelia.id))
|
|
|
|
self.assertEqual({hamlet.id}, cache_get(get_muting_users_cache_key(cordelia.id))[0])
|
2021-03-27 13:31:26 +01:00
|
|
|
|
2021-08-02 23:36:06 +02:00
|
|
|
url = f"/api/v1/users/me/muted_users/{cordelia.id}"
|
2021-03-27 13:31:26 +01:00
|
|
|
result = self.api_delete(hamlet, url)
|
|
|
|
self.assert_json_success(result)
|
2021-06-06 04:47:36 +02:00
|
|
|
self.assertEqual(None, cache_get(get_muting_users_cache_key(cordelia.id)))
|
|
|
|
self.assertEqual(set(), get_muting_users(cordelia.id))
|
|
|
|
self.assertEqual(set(), cache_get(get_muting_users_cache_key(cordelia.id))[0])
|
2021-03-27 13:45:49 +01:00
|
|
|
|
|
|
|
def assert_usermessage_read_flag(self, user: UserProfile, message: int, flag: bool) -> None:
|
|
|
|
usermesaage = UserMessage.objects.get(
|
|
|
|
user_profile=user,
|
|
|
|
message=message,
|
|
|
|
)
|
|
|
|
self.assertTrue(usermesaage.flags.read == flag)
|
|
|
|
|
|
|
|
def test_new_messages_from_muted_user_marked_as_read(self) -> None:
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
self.login_user(hamlet)
|
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
othello = self.example_user("othello")
|
|
|
|
|
|
|
|
self.make_stream("general")
|
|
|
|
self.subscribe(hamlet, "general")
|
|
|
|
self.subscribe(cordelia, "general")
|
|
|
|
self.subscribe(othello, "general")
|
|
|
|
|
|
|
|
# Hamlet mutes Cordelia.
|
2021-08-02 23:36:06 +02:00
|
|
|
url = f"/api/v1/users/me/muted_users/{cordelia.id}"
|
2021-03-27 13:45:49 +01:00
|
|
|
result = self.api_post(hamlet, url)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
# Have Cordelia send messages to Hamlet and Othello.
|
|
|
|
stream_message = self.send_stream_message(cordelia, "general", "Spam in stream")
|
|
|
|
huddle_message = self.send_huddle_message(cordelia, [hamlet, othello], "Spam in huddle")
|
|
|
|
pm_to_hamlet = self.send_personal_message(cordelia, hamlet, "Spam in PM")
|
|
|
|
pm_to_othello = self.send_personal_message(cordelia, othello, "Spam in PM")
|
|
|
|
|
|
|
|
# These should be marked as read for Hamlet, since he has muted Cordelia.
|
|
|
|
self.assert_usermessage_read_flag(hamlet, stream_message, True)
|
|
|
|
self.assert_usermessage_read_flag(hamlet, huddle_message, True)
|
|
|
|
self.assert_usermessage_read_flag(hamlet, pm_to_hamlet, True)
|
|
|
|
|
|
|
|
# These messages should be unreads for Othello, since he hasn't muted Cordelia.
|
|
|
|
self.assert_usermessage_read_flag(othello, stream_message, False)
|
|
|
|
self.assert_usermessage_read_flag(othello, huddle_message, False)
|
|
|
|
self.assert_usermessage_read_flag(othello, pm_to_othello, False)
|
2021-03-27 13:52:30 +01:00
|
|
|
|
|
|
|
def test_existing_messages_from_muted_user_marked_as_read(self) -> None:
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
self.login_user(hamlet)
|
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
othello = self.example_user("othello")
|
|
|
|
|
|
|
|
self.make_stream("general")
|
|
|
|
self.subscribe(hamlet, "general")
|
|
|
|
self.subscribe(cordelia, "general")
|
|
|
|
self.subscribe(othello, "general")
|
|
|
|
|
|
|
|
# Have Cordelia send messages to Hamlet and Othello.
|
|
|
|
stream_message = self.send_stream_message(cordelia, "general", "Spam in stream")
|
|
|
|
huddle_message = self.send_huddle_message(cordelia, [hamlet, othello], "Spam in huddle")
|
|
|
|
pm_to_hamlet = self.send_personal_message(cordelia, hamlet, "Spam in PM")
|
|
|
|
pm_to_othello = self.send_personal_message(cordelia, othello, "Spam in PM")
|
|
|
|
|
|
|
|
# These messages are unreads for both Hamlet and Othello right now.
|
|
|
|
self.assert_usermessage_read_flag(hamlet, stream_message, False)
|
|
|
|
self.assert_usermessage_read_flag(hamlet, huddle_message, False)
|
|
|
|
self.assert_usermessage_read_flag(hamlet, pm_to_hamlet, False)
|
|
|
|
|
|
|
|
self.assert_usermessage_read_flag(othello, stream_message, False)
|
|
|
|
self.assert_usermessage_read_flag(othello, huddle_message, False)
|
|
|
|
self.assert_usermessage_read_flag(othello, pm_to_othello, False)
|
|
|
|
|
|
|
|
# Hamlet mutes Cordelia.
|
2021-08-02 23:36:06 +02:00
|
|
|
url = f"/api/v1/users/me/muted_users/{cordelia.id}"
|
2021-03-27 13:52:30 +01:00
|
|
|
result = self.api_post(hamlet, url)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
# The messages sent earlier should be marked as read for Hamlet.
|
|
|
|
self.assert_usermessage_read_flag(hamlet, stream_message, True)
|
|
|
|
self.assert_usermessage_read_flag(hamlet, huddle_message, True)
|
|
|
|
self.assert_usermessage_read_flag(hamlet, pm_to_hamlet, True)
|
|
|
|
|
|
|
|
# These messages are still unreads for Othello, since he did not mute Cordelia.
|
|
|
|
self.assert_usermessage_read_flag(othello, stream_message, False)
|
|
|
|
self.assert_usermessage_read_flag(othello, huddle_message, False)
|
|
|
|
self.assert_usermessage_read_flag(othello, pm_to_othello, False)
|
2021-06-06 09:21:09 +02:00
|
|
|
|
|
|
|
def test_muted_message_send_notifications_not_enqueued(self) -> None:
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
self.login_user(hamlet)
|
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
|
|
|
|
# No muting involved. Notification about to be enqueued for Hamlet.
|
|
|
|
with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as m:
|
|
|
|
self.send_personal_message(cordelia, hamlet)
|
|
|
|
m.assert_called_once()
|
|
|
|
|
|
|
|
# Hamlet mutes Cordelia.
|
2021-08-02 23:36:06 +02:00
|
|
|
url = f"/api/v1/users/me/muted_users/{cordelia.id}"
|
2021-06-06 09:21:09 +02:00
|
|
|
result = self.api_post(hamlet, url)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
# Cordelia has been muted. Notification will not be enqueued for Hamlet.
|
|
|
|
with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as m:
|
|
|
|
self.send_personal_message(cordelia, hamlet)
|
|
|
|
m.assert_not_called()
|
|
|
|
|
|
|
|
def test_muted_message_edit_notifications_not_enqueued(self) -> None:
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
self.login_user(hamlet)
|
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
self.make_stream("general")
|
|
|
|
self.subscribe(hamlet, "general")
|
|
|
|
|
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
|
|
|
def send_stream_message() -> int:
|
|
|
|
# For testing simplicity we allow the somewhat
|
|
|
|
# contrived situation that cordelia can post
|
|
|
|
# to the general stream, even though she is not
|
|
|
|
# subscribed. This prevents some noise when we
|
|
|
|
# look at the mocked calls to maybe_enqueue_notifications.
|
|
|
|
message_id = self.send_stream_message(
|
|
|
|
cordelia, "general", allow_unsubscribed_sender=True
|
|
|
|
)
|
|
|
|
return message_id
|
|
|
|
|
2021-10-18 16:30:46 +02:00
|
|
|
# No muting. Only Hamlet is subscribed to #general, so only he can potentially receive
|
2021-06-06 09:21:09 +02:00
|
|
|
# notifications.
|
|
|
|
with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as m:
|
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
|
|
|
message_id = send_stream_message()
|
2021-06-06 09:21:09 +02:00
|
|
|
# Message does not mention Hamlet, so no notification.
|
|
|
|
m.assert_not_called()
|
|
|
|
|
|
|
|
self.login("cordelia")
|
|
|
|
with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as m:
|
|
|
|
result = self.client_patch(
|
|
|
|
"/json/messages/" + str(message_id),
|
|
|
|
dict(
|
|
|
|
content="@**King Hamlet**",
|
|
|
|
),
|
|
|
|
)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
m.assert_called_once()
|
|
|
|
# `maybe_enqueue_notificaions` was called for Hamlet after message edit mentioned him.
|
2021-06-25 14:08:41 +02:00
|
|
|
self.assertEqual(m.call_args_list[0][1]["user_notifications_data"].user_id, hamlet.id)
|
2021-06-06 09:21:09 +02:00
|
|
|
|
|
|
|
# Hamlet mutes Cordelia.
|
|
|
|
self.login("hamlet")
|
2021-08-02 23:36:06 +02:00
|
|
|
url = f"/api/v1/users/me/muted_users/{cordelia.id}"
|
2021-06-06 09:21:09 +02:00
|
|
|
result = self.api_post(hamlet, url)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as m:
|
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
|
|
|
message_id = send_stream_message()
|
2021-06-06 09:21:09 +02:00
|
|
|
m.assert_not_called()
|
|
|
|
|
|
|
|
self.login("cordelia")
|
|
|
|
with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as m:
|
|
|
|
result = self.client_patch(
|
|
|
|
"/json/messages/" + str(message_id),
|
|
|
|
dict(
|
|
|
|
content="@**King Hamlet**",
|
|
|
|
),
|
|
|
|
)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
# `maybe_enqueue_notificaions` wasn't called for Hamlet after message edit which mentioned him,
|
|
|
|
# because the sender (Cordelia) was muted.
|
|
|
|
m.assert_not_called()
|