2023-02-04 09:16:07 +01:00
|
|
|
from datetime import datetime, timezone
|
2023-04-05 13:36:01 +02:00
|
|
|
from typing import Any, Dict, List
|
2017-03-08 12:46:05 +01:00
|
|
|
|
2023-06-17 17:37:04 +02:00
|
|
|
import orjson
|
2023-04-03 01:16:10 +02:00
|
|
|
import time_machine
|
2020-06-11 00:54:34 +02:00
|
|
|
from django.utils.timezone import now as timezone_now
|
2017-10-23 19:39:12 +02:00
|
|
|
|
2023-06-17 17:37:04 +02:00
|
|
|
from zerver.actions.reactions import check_add_reaction
|
|
|
|
from zerver.actions.user_settings import do_change_user_setting
|
2023-02-03 13:21:25 +01:00
|
|
|
from zerver.actions.user_topics import do_set_user_topic_visibility_policy
|
2020-06-11 00:54:34 +02:00
|
|
|
from zerver.lib.stream_topic import StreamTopicTarget
|
|
|
|
from zerver.lib.test_classes import ZulipTestCase
|
2023-06-17 17:37:04 +02:00
|
|
|
from zerver.lib.test_helpers import get_subscription
|
2023-08-03 02:09:35 +02:00
|
|
|
from zerver.lib.user_topics import get_topic_mutes, topic_has_visibility_policy
|
2021-07-23 15:26:02 +02:00
|
|
|
from zerver.models import UserProfile, UserTopic, get_stream
|
2020-06-11 00:54:34 +02:00
|
|
|
|
2017-03-08 12:46:05 +01:00
|
|
|
|
2023-03-31 21:18:12 +02:00
|
|
|
class MutedTopicsTestsDeprecated(ZulipTestCase):
|
|
|
|
# Tests the deprecated URL: "/api/v1/users/me/subscriptions/muted_topics".
|
|
|
|
# It exists for backward compatibility and should be removed once
|
|
|
|
# we remove the deprecated URL.
|
2021-02-16 02:26:56 +01:00
|
|
|
def test_get_deactivated_muted_topic(self) -> None:
|
|
|
|
user = self.example_user("hamlet")
|
|
|
|
self.login_user(user)
|
|
|
|
|
|
|
|
stream = get_stream("Verona", user.realm)
|
|
|
|
|
|
|
|
mock_date_muted = datetime(2020, 1, 1, tzinfo=timezone.utc).timestamp()
|
|
|
|
|
2023-04-02 22:23:25 +02:00
|
|
|
url = "/api/v1/users/me/subscriptions/muted_topics"
|
|
|
|
data = {"stream_id": stream.id, "topic": "Verona3", "op": "add"}
|
2023-04-03 01:16:10 +02:00
|
|
|
with time_machine.travel(datetime(2020, 1, 1, tzinfo=timezone.utc), tick=False):
|
2023-04-02 22:23:25 +02:00
|
|
|
result = self.api_patch(user, url, data)
|
|
|
|
self.assert_json_success(result)
|
2021-02-16 02:26:56 +01:00
|
|
|
|
|
|
|
stream.deactivated = True
|
|
|
|
stream.save()
|
|
|
|
|
|
|
|
self.assertNotIn((stream.name, "Verona3", mock_date_muted), get_topic_mutes(user))
|
|
|
|
self.assertIn((stream.name, "Verona3", mock_date_muted), get_topic_mutes(user, True))
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_user_ids_muting_topic(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
cordelia = self.example_user("cordelia")
|
2017-10-23 19:39:12 +02:00
|
|
|
realm = hamlet.realm
|
2021-02-12 08:20:45 +01:00
|
|
|
stream = get_stream("Verona", realm)
|
|
|
|
topic_name = "teST topic"
|
2023-02-04 09:16:07 +01:00
|
|
|
date_muted = datetime(2020, 1, 1, tzinfo=timezone.utc)
|
2017-10-23 19:39:12 +02:00
|
|
|
|
|
|
|
stream_topic_target = StreamTopicTarget(
|
|
|
|
stream_id=stream.id,
|
|
|
|
topic_name=topic_name,
|
|
|
|
)
|
|
|
|
|
2023-03-12 16:19:42 +01:00
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.MUTED
|
|
|
|
)
|
2017-10-23 19:39:12 +02:00
|
|
|
self.assertEqual(user_ids, set())
|
|
|
|
|
2023-04-02 22:23:25 +02:00
|
|
|
url = "/api/v1/users/me/subscriptions/muted_topics"
|
|
|
|
data = {"stream_id": stream.id, "topic": "test TOPIC", "op": "add"}
|
|
|
|
|
2021-03-27 11:58:03 +01:00
|
|
|
def mute_topic_for_user(user: UserProfile) -> None:
|
2023-04-03 01:16:10 +02:00
|
|
|
with time_machine.travel(datetime(2020, 1, 1, tzinfo=timezone.utc), tick=False):
|
2023-04-02 22:23:25 +02:00
|
|
|
result = self.api_patch(user, url, data)
|
|
|
|
self.assert_json_success(result)
|
2017-10-23 19:39:12 +02:00
|
|
|
|
2021-03-27 11:58:03 +01:00
|
|
|
mute_topic_for_user(hamlet)
|
2023-03-12 16:19:42 +01:00
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.MUTED
|
|
|
|
)
|
2017-10-23 19:39:12 +02:00
|
|
|
self.assertEqual(user_ids, {hamlet.id})
|
2021-08-02 09:49:56 +02:00
|
|
|
hamlet_date_muted = UserTopic.objects.filter(
|
2023-03-12 16:19:42 +01:00
|
|
|
user_profile=hamlet, visibility_policy=UserTopic.VisibilityPolicy.MUTED
|
2021-08-02 09:49:56 +02:00
|
|
|
)[0].last_updated
|
2023-02-04 09:16:07 +01:00
|
|
|
self.assertEqual(hamlet_date_muted, date_muted)
|
2017-10-23 19:39:12 +02:00
|
|
|
|
2021-03-27 11:58:03 +01:00
|
|
|
mute_topic_for_user(cordelia)
|
2023-03-12 16:19:42 +01:00
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.MUTED
|
|
|
|
)
|
2017-10-23 19:39:12 +02:00
|
|
|
self.assertEqual(user_ids, {hamlet.id, cordelia.id})
|
2021-08-02 09:49:56 +02:00
|
|
|
cordelia_date_muted = UserTopic.objects.filter(
|
2023-03-12 16:19:42 +01:00
|
|
|
user_profile=cordelia, visibility_policy=UserTopic.VisibilityPolicy.MUTED
|
2021-08-02 09:49:56 +02:00
|
|
|
)[0].last_updated
|
2023-02-04 09:16:07 +01:00
|
|
|
self.assertEqual(cordelia_date_muted, date_muted)
|
2017-10-23 19:39:12 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_muted_topic(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
user = self.example_user("hamlet")
|
2020-03-06 18:40:46 +01:00
|
|
|
self.login_user(user)
|
2018-12-24 17:04:27 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
stream = get_stream("Verona", user.realm)
|
2017-03-13 22:07:00 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
url = "/api/v1/users/me/subscriptions/muted_topics"
|
2017-03-13 22:07:00 +01:00
|
|
|
|
2021-12-17 08:14:22 +01:00
|
|
|
payloads: List[Dict[str, object]] = [
|
2021-02-12 08:20:45 +01:00
|
|
|
{"stream": stream.name, "topic": "Verona3", "op": "add"},
|
|
|
|
{"stream_id": stream.id, "topic": "Verona3", "op": "add"},
|
2018-12-24 17:04:27 +01:00
|
|
|
]
|
|
|
|
|
2020-06-04 03:32:59 +02:00
|
|
|
mock_date_muted = datetime(2020, 1, 1, tzinfo=timezone.utc).timestamp()
|
2018-12-24 17:04:27 +01:00
|
|
|
for data in payloads:
|
2023-04-03 01:16:10 +02:00
|
|
|
with time_machine.travel(datetime(2020, 1, 1, tzinfo=timezone.utc), tick=False):
|
2020-02-05 09:03:11 +01:00
|
|
|
result = self.api_patch(user, url, data)
|
|
|
|
self.assert_json_success(result)
|
2018-12-24 17:04:27 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertIn((stream.name, "Verona3", mock_date_muted), get_topic_mutes(user))
|
2023-03-26 15:36:01 +02:00
|
|
|
self.assertTrue(
|
|
|
|
topic_has_visibility_policy(
|
|
|
|
user, stream.id, "verona3", UserTopic.VisibilityPolicy.MUTED
|
|
|
|
)
|
|
|
|
)
|
2018-12-24 17:04:27 +01:00
|
|
|
|
2023-02-03 13:21:25 +01:00
|
|
|
do_set_user_topic_visibility_policy(
|
2023-03-03 18:00:27 +01:00
|
|
|
user,
|
|
|
|
stream,
|
|
|
|
"Verona3",
|
2023-03-12 16:19:42 +01:00
|
|
|
visibility_policy=UserTopic.VisibilityPolicy.INHERIT,
|
2018-12-24 17:04:27 +01:00
|
|
|
)
|
2017-08-30 02:19:34 +02:00
|
|
|
|
2022-05-31 01:27:38 +02:00
|
|
|
assert stream.recipient is not None
|
2023-02-10 20:06:39 +01:00
|
|
|
result = self.api_patch(user, url, data)
|
2022-02-09 16:49:46 +01:00
|
|
|
|
2023-03-14 15:09:12 +01:00
|
|
|
# Now check that no error is raised when attempted to mute
|
|
|
|
# an already muted topic. This should be case-insensitive.
|
|
|
|
user_topic_count = UserTopic.objects.count()
|
2023-02-10 20:06:39 +01:00
|
|
|
data["topic"] = "VERONA3"
|
2023-03-14 15:09:12 +01:00
|
|
|
with self.assertLogs(level="INFO") as info_logs:
|
|
|
|
result = self.api_patch(user, url, data)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
self.assertEqual(
|
|
|
|
info_logs.output[0],
|
|
|
|
f"INFO:root:User {user.id} tried to set visibility_policy to its current value of {UserTopic.VisibilityPolicy.MUTED}",
|
|
|
|
)
|
|
|
|
# Verify that we didn't end up with duplicate UserTopic rows
|
|
|
|
# with the two different cases after the previous API call.
|
|
|
|
self.assertEqual(UserTopic.objects.count() - user_topic_count, 0)
|
2022-02-09 16:49:46 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_remove_muted_topic(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
user = self.example_user("hamlet")
|
2018-12-24 17:04:27 +01:00
|
|
|
realm = user.realm
|
2020-03-06 18:40:46 +01:00
|
|
|
self.login_user(user)
|
2017-03-13 22:07:00 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
stream = get_stream("Verona", realm)
|
2017-08-30 02:19:34 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
url = "/api/v1/users/me/subscriptions/muted_topics"
|
2021-12-17 08:14:22 +01:00
|
|
|
payloads: List[Dict[str, object]] = [
|
2021-02-12 08:20:45 +01:00
|
|
|
{"stream": stream.name, "topic": "vERONA3", "op": "remove"},
|
|
|
|
{"stream_id": stream.id, "topic": "vEroNA3", "op": "remove"},
|
2018-12-24 17:04:27 +01:00
|
|
|
]
|
2020-06-04 03:32:59 +02:00
|
|
|
mock_date_muted = datetime(2020, 1, 1, tzinfo=timezone.utc).timestamp()
|
2017-03-13 22:07:00 +01:00
|
|
|
|
2018-12-24 17:04:27 +01:00
|
|
|
for data in payloads:
|
2023-02-03 12:57:43 +01:00
|
|
|
do_set_user_topic_visibility_policy(
|
2023-03-03 18:00:27 +01:00
|
|
|
user,
|
|
|
|
stream,
|
|
|
|
"Verona3",
|
2023-03-12 16:19:42 +01:00
|
|
|
visibility_policy=UserTopic.VisibilityPolicy.MUTED,
|
2023-02-03 12:57:43 +01:00
|
|
|
last_updated=datetime(2020, 1, 1, tzinfo=timezone.utc),
|
2018-12-24 17:04:27 +01:00
|
|
|
)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertIn((stream.name, "Verona3", mock_date_muted), get_topic_mutes(user))
|
2018-12-24 17:04:27 +01:00
|
|
|
|
2020-03-10 11:48:26 +01:00
|
|
|
result = self.api_patch(user, url, data)
|
2018-12-24 17:04:27 +01:00
|
|
|
|
|
|
|
self.assert_json_success(result)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertNotIn((stream.name, "Verona3", mock_date_muted), get_topic_mutes(user))
|
2023-03-26 15:36:01 +02:00
|
|
|
self.assertFalse(
|
|
|
|
topic_has_visibility_policy(
|
|
|
|
user, stream.id, "verona3", UserTopic.VisibilityPolicy.MUTED
|
|
|
|
)
|
|
|
|
)
|
2017-03-13 22:07:00 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_muted_topic_add_invalid(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
user = self.example_user("hamlet")
|
2018-12-24 17:04:27 +01:00
|
|
|
realm = user.realm
|
2020-03-06 18:40:46 +01:00
|
|
|
self.login_user(user)
|
2017-03-13 22:07:00 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
stream = get_stream("Verona", realm)
|
2023-02-03 12:57:43 +01:00
|
|
|
do_set_user_topic_visibility_policy(
|
2023-03-12 16:19:42 +01:00
|
|
|
user,
|
|
|
|
stream,
|
|
|
|
"Verona3",
|
|
|
|
visibility_policy=UserTopic.VisibilityPolicy.MUTED,
|
|
|
|
last_updated=timezone_now(),
|
2023-02-03 12:57:43 +01:00
|
|
|
)
|
2017-08-30 02:19:34 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
url = "/api/v1/users/me/subscriptions/muted_topics"
|
2018-12-24 17:04:27 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
data = {"stream_id": 999999999, "topic": "Verona3", "op": "add"}
|
2020-03-10 11:48:26 +01:00
|
|
|
result = self.api_patch(user, url, data)
|
2022-05-27 14:03:08 +02:00
|
|
|
self.assert_json_error(result, "Invalid stream ID")
|
2018-12-24 17:04:27 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
data = {"topic": "Verona3", "op": "add"}
|
2020-03-10 11:48:26 +01:00
|
|
|
result = self.api_patch(user, url, data)
|
2018-12-24 17:04:27 +01:00
|
|
|
self.assert_json_error(result, "Please supply 'stream'.")
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
data = {"stream": stream.name, "stream_id": stream.id, "topic": "Verona3", "op": "add"}
|
2020-03-10 11:48:26 +01:00
|
|
|
result = self.api_patch(user, url, data)
|
2018-12-24 17:04:27 +01:00
|
|
|
self.assert_json_error(result, "Please choose one: 'stream' or 'stream_id'.")
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_muted_topic_remove_invalid(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
user = self.example_user("hamlet")
|
2018-12-24 17:04:27 +01:00
|
|
|
realm = user.realm
|
2020-03-06 18:40:46 +01:00
|
|
|
self.login_user(user)
|
2021-02-12 08:20:45 +01:00
|
|
|
stream = get_stream("Verona", realm)
|
2017-03-13 22:07:00 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
url = "/api/v1/users/me/subscriptions/muted_topics"
|
|
|
|
data: Dict[str, Any] = {"stream": "BOGUS", "topic": "Verona3", "op": "remove"}
|
2020-03-10 11:48:26 +01:00
|
|
|
result = self.api_patch(user, url, data)
|
2018-12-24 17:04:27 +01:00
|
|
|
self.assert_json_error(result, "Topic is not muted")
|
|
|
|
|
2023-03-14 15:09:12 +01:00
|
|
|
# Check that removing mute from a topic for which the user
|
|
|
|
# doesn't already have a visibility_policy doesn't cause an error.
|
|
|
|
data = {"stream": stream.name, "topic": "BOGUS", "op": "remove"}
|
|
|
|
with self.assertLogs(level="INFO") as info_logs:
|
2022-09-21 15:51:48 +02:00
|
|
|
result = self.api_patch(user, url, data)
|
2023-03-14 15:09:12 +01:00
|
|
|
self.assert_json_success(result)
|
|
|
|
self.assertEqual(
|
|
|
|
info_logs.output[0],
|
|
|
|
f"INFO:root:User {user.id} tried to remove visibility_policy, which actually doesn't exist",
|
|
|
|
)
|
2017-08-30 02:19:34 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
data = {"stream_id": 999999999, "topic": "BOGUS", "op": "remove"}
|
2020-03-10 11:48:26 +01:00
|
|
|
result = self.api_patch(user, url, data)
|
2018-08-27 22:13:56 +02:00
|
|
|
self.assert_json_error(result, "Topic is not muted")
|
2018-12-24 17:04:27 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
data = {"topic": "Verona3", "op": "remove"}
|
2020-03-10 11:48:26 +01:00
|
|
|
result = self.api_patch(user, url, data)
|
2018-12-24 17:04:27 +01:00
|
|
|
self.assert_json_error(result, "Please supply 'stream'.")
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
data = {"stream": stream.name, "stream_id": stream.id, "topic": "Verona3", "op": "remove"}
|
2020-03-10 11:48:26 +01:00
|
|
|
result = self.api_patch(user, url, data)
|
2018-12-24 17:04:27 +01:00
|
|
|
self.assert_json_error(result, "Please choose one: 'stream' or 'stream_id'.")
|
user_topics: Refactor add_topic_mute.
In order to support different types of topic visibility policies,
this renames 'add_topic_mute' to
'set_user_topic_visibility_policy_in_database'
and refactors it to accept a parameter 'visibility_policy'.
Create a corresponding UserTopic row for any visibility policy,
not just muting topics.
When a UserTopic row for (user_profile, stream, topic, recipient_id)
exists already, it updates the row with the new visibility_policy.
In the event of a duplicate request, raises a JsonableError.
i.e., new_visibility_policy == existing_visibility_policy.
There is an increase in the database query count in the message-edit
code path.
Reason:
Earlier, 'add_topic_mute' used 'bulk_create' which either
creates or raises IntegrityError -- 1 query.
Now, 'set_user_topic_visibility_policy' uses get_or_create
-- 2 queries in the case of creating new row.
We can't use the previous approach, because now we have to
handle the case of updating the visibility_policy too.
Also, using bulk_* for a single row is not the correct way.
Co-authored-by: Kartik Srivastava <kaushiksri0908@gmail.com>
Co-authored-by: Prakhar Pratyush <prakhar841301@gmail.com>
2022-09-12 16:39:53 +02:00
|
|
|
|
|
|
|
|
2023-03-31 21:18:12 +02:00
|
|
|
class MutedTopicsTests(ZulipTestCase):
|
|
|
|
def test_get_deactivated_muted_topic(self) -> None:
|
|
|
|
user = self.example_user("hamlet")
|
|
|
|
self.login_user(user)
|
|
|
|
|
|
|
|
stream = get_stream("Verona", user.realm)
|
|
|
|
|
|
|
|
url = "/api/v1/user_topics"
|
|
|
|
data = {
|
|
|
|
"stream_id": stream.id,
|
|
|
|
"topic": "Verona3",
|
|
|
|
"visibility_policy": UserTopic.VisibilityPolicy.MUTED,
|
|
|
|
}
|
|
|
|
|
|
|
|
mock_date_muted = datetime(2020, 1, 1, tzinfo=timezone.utc).timestamp()
|
|
|
|
with time_machine.travel(datetime(2020, 1, 1, tzinfo=timezone.utc), tick=False):
|
|
|
|
result = self.api_post(user, url, data)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
stream.deactivated = True
|
|
|
|
stream.save()
|
|
|
|
|
|
|
|
self.assertNotIn((stream.name, "Verona3", mock_date_muted), get_topic_mutes(user))
|
|
|
|
self.assertIn((stream.name, "Verona3", mock_date_muted), get_topic_mutes(user, True))
|
|
|
|
|
|
|
|
def test_user_ids_muting_topic(self) -> None:
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
realm = hamlet.realm
|
|
|
|
stream = get_stream("Verona", realm)
|
|
|
|
topic_name = "teST topic"
|
|
|
|
date_muted = datetime(2020, 1, 1, tzinfo=timezone.utc)
|
|
|
|
|
|
|
|
stream_topic_target = StreamTopicTarget(
|
|
|
|
stream_id=stream.id,
|
|
|
|
topic_name=topic_name,
|
|
|
|
)
|
|
|
|
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.MUTED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, set())
|
|
|
|
|
|
|
|
url = "/api/v1/user_topics"
|
|
|
|
|
|
|
|
def set_topic_visibility_for_user(user: UserProfile, visibility_policy: int) -> None:
|
|
|
|
data = {
|
|
|
|
"stream_id": stream.id,
|
|
|
|
"topic": "test TOPIC",
|
|
|
|
"visibility_policy": visibility_policy,
|
|
|
|
}
|
|
|
|
with time_machine.travel(date_muted, tick=False):
|
|
|
|
result = self.api_post(user, url, data)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
set_topic_visibility_for_user(hamlet, UserTopic.VisibilityPolicy.MUTED)
|
|
|
|
set_topic_visibility_for_user(cordelia, UserTopic.VisibilityPolicy.UNMUTED)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.MUTED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, {hamlet.id})
|
|
|
|
hamlet_date_muted = UserTopic.objects.filter(
|
|
|
|
user_profile=hamlet, visibility_policy=UserTopic.VisibilityPolicy.MUTED
|
|
|
|
)[0].last_updated
|
|
|
|
self.assertEqual(hamlet_date_muted, date_muted)
|
|
|
|
|
|
|
|
set_topic_visibility_for_user(cordelia, UserTopic.VisibilityPolicy.MUTED)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.MUTED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, {hamlet.id, cordelia.id})
|
|
|
|
cordelia_date_muted = UserTopic.objects.filter(
|
|
|
|
user_profile=cordelia, visibility_policy=UserTopic.VisibilityPolicy.MUTED
|
|
|
|
)[0].last_updated
|
|
|
|
self.assertEqual(cordelia_date_muted, date_muted)
|
|
|
|
|
|
|
|
def test_add_muted_topic(self) -> None:
|
|
|
|
user = self.example_user("hamlet")
|
|
|
|
self.login_user(user)
|
|
|
|
|
|
|
|
stream = get_stream("Verona", user.realm)
|
|
|
|
|
|
|
|
url = "/api/v1/user_topics"
|
|
|
|
data = {
|
|
|
|
"stream_id": stream.id,
|
|
|
|
"topic": "Verona3",
|
|
|
|
"visibility_policy": UserTopic.VisibilityPolicy.MUTED,
|
|
|
|
}
|
|
|
|
|
|
|
|
mock_date_muted = datetime(2020, 1, 1, tzinfo=timezone.utc).timestamp()
|
|
|
|
|
2023-04-05 13:36:01 +02:00
|
|
|
with self.capture_send_event_calls(expected_num_events=2) as events:
|
2023-03-31 21:18:12 +02:00
|
|
|
with time_machine.travel(datetime(2020, 1, 1, tzinfo=timezone.utc), tick=False):
|
|
|
|
result = self.api_post(user, url, data)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
self.assertTrue(
|
|
|
|
topic_has_visibility_policy(
|
|
|
|
user, stream.id, "verona3", UserTopic.VisibilityPolicy.MUTED
|
|
|
|
)
|
|
|
|
)
|
|
|
|
# Verify if events are sent properly
|
|
|
|
user_topic_event: Dict[str, Any] = {
|
|
|
|
"type": "user_topic",
|
|
|
|
"stream_id": stream.id,
|
|
|
|
"topic_name": "Verona3",
|
|
|
|
"last_updated": mock_date_muted,
|
|
|
|
"visibility_policy": UserTopic.VisibilityPolicy.MUTED,
|
|
|
|
}
|
|
|
|
muted_topics_event = dict(type="muted_topics", muted_topics=get_topic_mutes(user))
|
|
|
|
self.assertEqual(events[0]["event"], muted_topics_event)
|
|
|
|
self.assertEqual(events[1]["event"], user_topic_event)
|
|
|
|
|
|
|
|
# Now check that no error is raised when attempted to mute
|
|
|
|
# an already muted topic. This should be case-insensitive.
|
|
|
|
user_topic_count = UserTopic.objects.count()
|
|
|
|
data["topic"] = "VERONA3"
|
|
|
|
with self.assertLogs(level="INFO") as info_logs:
|
|
|
|
result = self.api_post(user, url, data)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
self.assertEqual(
|
|
|
|
info_logs.output[0],
|
|
|
|
f"INFO:root:User {user.id} tried to set visibility_policy to its current value of {UserTopic.VisibilityPolicy.MUTED}",
|
|
|
|
)
|
|
|
|
# Verify that we didn't end up with duplicate UserTopic rows
|
|
|
|
# with the two different cases after the previous API call.
|
|
|
|
self.assertEqual(UserTopic.objects.count() - user_topic_count, 0)
|
|
|
|
|
|
|
|
def test_remove_muted_topic(self) -> None:
|
|
|
|
user = self.example_user("hamlet")
|
|
|
|
realm = user.realm
|
|
|
|
self.login_user(user)
|
|
|
|
|
|
|
|
stream = get_stream("Verona", realm)
|
|
|
|
|
|
|
|
do_set_user_topic_visibility_policy(
|
|
|
|
user,
|
|
|
|
stream,
|
|
|
|
"Verona3",
|
|
|
|
visibility_policy=UserTopic.VisibilityPolicy.MUTED,
|
|
|
|
last_updated=datetime(2020, 1, 1, tzinfo=timezone.utc),
|
|
|
|
)
|
|
|
|
self.assertTrue(
|
|
|
|
topic_has_visibility_policy(
|
|
|
|
user, stream.id, "verona3", UserTopic.VisibilityPolicy.MUTED
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
url = "/api/v1/user_topics"
|
|
|
|
data = {
|
|
|
|
"stream_id": stream.id,
|
|
|
|
"topic": "Verona3",
|
|
|
|
"visibility_policy": UserTopic.VisibilityPolicy.INHERIT,
|
|
|
|
}
|
|
|
|
|
|
|
|
mock_date_mute_removed = datetime(2020, 1, 1, tzinfo=timezone.utc).timestamp()
|
|
|
|
|
2023-04-05 13:36:01 +02:00
|
|
|
with self.capture_send_event_calls(expected_num_events=2) as events:
|
2023-03-31 21:18:12 +02:00
|
|
|
with time_machine.travel(datetime(2020, 1, 1, tzinfo=timezone.utc), tick=False):
|
|
|
|
result = self.api_post(user, url, data)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
self.assertFalse(
|
|
|
|
topic_has_visibility_policy(
|
|
|
|
user, stream.id, "verona3", UserTopic.VisibilityPolicy.MUTED
|
|
|
|
)
|
|
|
|
)
|
|
|
|
# Verify if events are sent properly
|
|
|
|
user_topic_event: Dict[str, Any] = {
|
|
|
|
"type": "user_topic",
|
|
|
|
"stream_id": stream.id,
|
|
|
|
"topic_name": data["topic"],
|
|
|
|
"last_updated": mock_date_mute_removed,
|
|
|
|
"visibility_policy": UserTopic.VisibilityPolicy.INHERIT,
|
|
|
|
}
|
|
|
|
muted_topics_event = dict(type="muted_topics", muted_topics=get_topic_mutes(user))
|
|
|
|
self.assertEqual(events[0]["event"], muted_topics_event)
|
|
|
|
self.assertEqual(events[1]["event"], user_topic_event)
|
|
|
|
|
|
|
|
# Check that removing mute from a topic for which the user
|
|
|
|
# doesn't already have a visibility_policy doesn't cause an error.
|
|
|
|
with self.assertLogs(level="INFO") as info_logs:
|
|
|
|
result = self.api_post(user, url, data)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
self.assertEqual(
|
|
|
|
info_logs.output[0],
|
|
|
|
f"INFO:root:User {user.id} tried to remove visibility_policy, which actually doesn't exist",
|
|
|
|
)
|
|
|
|
|
|
|
|
def test_muted_topic_add_invalid(self) -> None:
|
|
|
|
user = self.example_user("hamlet")
|
|
|
|
self.login_user(user)
|
|
|
|
|
|
|
|
url = "/api/v1/user_topics"
|
|
|
|
data = {
|
|
|
|
"stream_id": 999999999,
|
|
|
|
"topic": "Verona3",
|
|
|
|
"visibility_policy": UserTopic.VisibilityPolicy.MUTED,
|
|
|
|
}
|
|
|
|
result = self.api_post(user, url, data)
|
|
|
|
self.assert_json_error(result, "Invalid stream ID")
|
|
|
|
|
|
|
|
def test_muted_topic_remove_invalid(self) -> None:
|
|
|
|
user = self.example_user("hamlet")
|
|
|
|
self.login_user(user)
|
|
|
|
|
|
|
|
url = "/api/v1/user_topics"
|
|
|
|
data = {
|
|
|
|
"stream_id": 999999999,
|
|
|
|
"topic": "Verona3",
|
|
|
|
"visibility_policy": UserTopic.VisibilityPolicy.INHERIT,
|
|
|
|
}
|
|
|
|
|
|
|
|
result = self.api_post(user, url, data)
|
|
|
|
self.assert_json_error(result, "Invalid stream ID")
|
|
|
|
|
|
|
|
|
user_topics: Refactor add_topic_mute.
In order to support different types of topic visibility policies,
this renames 'add_topic_mute' to
'set_user_topic_visibility_policy_in_database'
and refactors it to accept a parameter 'visibility_policy'.
Create a corresponding UserTopic row for any visibility policy,
not just muting topics.
When a UserTopic row for (user_profile, stream, topic, recipient_id)
exists already, it updates the row with the new visibility_policy.
In the event of a duplicate request, raises a JsonableError.
i.e., new_visibility_policy == existing_visibility_policy.
There is an increase in the database query count in the message-edit
code path.
Reason:
Earlier, 'add_topic_mute' used 'bulk_create' which either
creates or raises IntegrityError -- 1 query.
Now, 'set_user_topic_visibility_policy' uses get_or_create
-- 2 queries in the case of creating new row.
We can't use the previous approach, because now we have to
handle the case of updating the visibility_policy too.
Also, using bulk_* for a single row is not the correct way.
Co-authored-by: Kartik Srivastava <kaushiksri0908@gmail.com>
Co-authored-by: Prakhar Pratyush <prakhar841301@gmail.com>
2022-09-12 16:39:53 +02:00
|
|
|
class UnmutedTopicsTests(ZulipTestCase):
|
|
|
|
def test_user_ids_unmuting_topic(self) -> None:
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
realm = hamlet.realm
|
|
|
|
stream = get_stream("Verona", realm)
|
|
|
|
topic_name = "teST topic"
|
|
|
|
date_unmuted = datetime(2020, 1, 1, tzinfo=timezone.utc)
|
|
|
|
|
|
|
|
stream_topic_target = StreamTopicTarget(
|
|
|
|
stream_id=stream.id,
|
|
|
|
topic_name=topic_name,
|
|
|
|
)
|
|
|
|
|
2023-03-12 16:19:42 +01:00
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
user_topics: Refactor add_topic_mute.
In order to support different types of topic visibility policies,
this renames 'add_topic_mute' to
'set_user_topic_visibility_policy_in_database'
and refactors it to accept a parameter 'visibility_policy'.
Create a corresponding UserTopic row for any visibility policy,
not just muting topics.
When a UserTopic row for (user_profile, stream, topic, recipient_id)
exists already, it updates the row with the new visibility_policy.
In the event of a duplicate request, raises a JsonableError.
i.e., new_visibility_policy == existing_visibility_policy.
There is an increase in the database query count in the message-edit
code path.
Reason:
Earlier, 'add_topic_mute' used 'bulk_create' which either
creates or raises IntegrityError -- 1 query.
Now, 'set_user_topic_visibility_policy' uses get_or_create
-- 2 queries in the case of creating new row.
We can't use the previous approach, because now we have to
handle the case of updating the visibility_policy too.
Also, using bulk_* for a single row is not the correct way.
Co-authored-by: Kartik Srivastava <kaushiksri0908@gmail.com>
Co-authored-by: Prakhar Pratyush <prakhar841301@gmail.com>
2022-09-12 16:39:53 +02:00
|
|
|
self.assertEqual(user_ids, set())
|
|
|
|
|
2023-04-02 23:36:13 +02:00
|
|
|
url = "/api/v1/user_topics"
|
|
|
|
|
user_topics: Refactor add_topic_mute.
In order to support different types of topic visibility policies,
this renames 'add_topic_mute' to
'set_user_topic_visibility_policy_in_database'
and refactors it to accept a parameter 'visibility_policy'.
Create a corresponding UserTopic row for any visibility policy,
not just muting topics.
When a UserTopic row for (user_profile, stream, topic, recipient_id)
exists already, it updates the row with the new visibility_policy.
In the event of a duplicate request, raises a JsonableError.
i.e., new_visibility_policy == existing_visibility_policy.
There is an increase in the database query count in the message-edit
code path.
Reason:
Earlier, 'add_topic_mute' used 'bulk_create' which either
creates or raises IntegrityError -- 1 query.
Now, 'set_user_topic_visibility_policy' uses get_or_create
-- 2 queries in the case of creating new row.
We can't use the previous approach, because now we have to
handle the case of updating the visibility_policy too.
Also, using bulk_* for a single row is not the correct way.
Co-authored-by: Kartik Srivastava <kaushiksri0908@gmail.com>
Co-authored-by: Prakhar Pratyush <prakhar841301@gmail.com>
2022-09-12 16:39:53 +02:00
|
|
|
def set_topic_visibility_for_user(user: UserProfile, visibility_policy: int) -> None:
|
2023-04-02 23:36:13 +02:00
|
|
|
data = {
|
|
|
|
"stream_id": stream.id,
|
|
|
|
"topic": "test TOPIC",
|
|
|
|
"visibility_policy": visibility_policy,
|
|
|
|
}
|
|
|
|
with time_machine.travel(datetime(2020, 1, 1, tzinfo=timezone.utc), tick=False):
|
|
|
|
result = self.api_post(user, url, data)
|
|
|
|
self.assert_json_success(result)
|
user_topics: Refactor add_topic_mute.
In order to support different types of topic visibility policies,
this renames 'add_topic_mute' to
'set_user_topic_visibility_policy_in_database'
and refactors it to accept a parameter 'visibility_policy'.
Create a corresponding UserTopic row for any visibility policy,
not just muting topics.
When a UserTopic row for (user_profile, stream, topic, recipient_id)
exists already, it updates the row with the new visibility_policy.
In the event of a duplicate request, raises a JsonableError.
i.e., new_visibility_policy == existing_visibility_policy.
There is an increase in the database query count in the message-edit
code path.
Reason:
Earlier, 'add_topic_mute' used 'bulk_create' which either
creates or raises IntegrityError -- 1 query.
Now, 'set_user_topic_visibility_policy' uses get_or_create
-- 2 queries in the case of creating new row.
We can't use the previous approach, because now we have to
handle the case of updating the visibility_policy too.
Also, using bulk_* for a single row is not the correct way.
Co-authored-by: Kartik Srivastava <kaushiksri0908@gmail.com>
Co-authored-by: Prakhar Pratyush <prakhar841301@gmail.com>
2022-09-12 16:39:53 +02:00
|
|
|
|
2023-03-12 16:19:42 +01:00
|
|
|
set_topic_visibility_for_user(hamlet, UserTopic.VisibilityPolicy.UNMUTED)
|
|
|
|
set_topic_visibility_for_user(cordelia, UserTopic.VisibilityPolicy.MUTED)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
user_topics: Refactor add_topic_mute.
In order to support different types of topic visibility policies,
this renames 'add_topic_mute' to
'set_user_topic_visibility_policy_in_database'
and refactors it to accept a parameter 'visibility_policy'.
Create a corresponding UserTopic row for any visibility policy,
not just muting topics.
When a UserTopic row for (user_profile, stream, topic, recipient_id)
exists already, it updates the row with the new visibility_policy.
In the event of a duplicate request, raises a JsonableError.
i.e., new_visibility_policy == existing_visibility_policy.
There is an increase in the database query count in the message-edit
code path.
Reason:
Earlier, 'add_topic_mute' used 'bulk_create' which either
creates or raises IntegrityError -- 1 query.
Now, 'set_user_topic_visibility_policy' uses get_or_create
-- 2 queries in the case of creating new row.
We can't use the previous approach, because now we have to
handle the case of updating the visibility_policy too.
Also, using bulk_* for a single row is not the correct way.
Co-authored-by: Kartik Srivastava <kaushiksri0908@gmail.com>
Co-authored-by: Prakhar Pratyush <prakhar841301@gmail.com>
2022-09-12 16:39:53 +02:00
|
|
|
self.assertEqual(user_ids, {hamlet.id})
|
|
|
|
hamlet_date_unmuted = UserTopic.objects.filter(
|
2023-03-12 16:19:42 +01:00
|
|
|
user_profile=hamlet, visibility_policy=UserTopic.VisibilityPolicy.UNMUTED
|
user_topics: Refactor add_topic_mute.
In order to support different types of topic visibility policies,
this renames 'add_topic_mute' to
'set_user_topic_visibility_policy_in_database'
and refactors it to accept a parameter 'visibility_policy'.
Create a corresponding UserTopic row for any visibility policy,
not just muting topics.
When a UserTopic row for (user_profile, stream, topic, recipient_id)
exists already, it updates the row with the new visibility_policy.
In the event of a duplicate request, raises a JsonableError.
i.e., new_visibility_policy == existing_visibility_policy.
There is an increase in the database query count in the message-edit
code path.
Reason:
Earlier, 'add_topic_mute' used 'bulk_create' which either
creates or raises IntegrityError -- 1 query.
Now, 'set_user_topic_visibility_policy' uses get_or_create
-- 2 queries in the case of creating new row.
We can't use the previous approach, because now we have to
handle the case of updating the visibility_policy too.
Also, using bulk_* for a single row is not the correct way.
Co-authored-by: Kartik Srivastava <kaushiksri0908@gmail.com>
Co-authored-by: Prakhar Pratyush <prakhar841301@gmail.com>
2022-09-12 16:39:53 +02:00
|
|
|
)[0].last_updated
|
|
|
|
self.assertEqual(hamlet_date_unmuted, date_unmuted)
|
|
|
|
|
2023-03-12 16:19:42 +01:00
|
|
|
set_topic_visibility_for_user(cordelia, UserTopic.VisibilityPolicy.UNMUTED)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
user_topics: Refactor add_topic_mute.
In order to support different types of topic visibility policies,
this renames 'add_topic_mute' to
'set_user_topic_visibility_policy_in_database'
and refactors it to accept a parameter 'visibility_policy'.
Create a corresponding UserTopic row for any visibility policy,
not just muting topics.
When a UserTopic row for (user_profile, stream, topic, recipient_id)
exists already, it updates the row with the new visibility_policy.
In the event of a duplicate request, raises a JsonableError.
i.e., new_visibility_policy == existing_visibility_policy.
There is an increase in the database query count in the message-edit
code path.
Reason:
Earlier, 'add_topic_mute' used 'bulk_create' which either
creates or raises IntegrityError -- 1 query.
Now, 'set_user_topic_visibility_policy' uses get_or_create
-- 2 queries in the case of creating new row.
We can't use the previous approach, because now we have to
handle the case of updating the visibility_policy too.
Also, using bulk_* for a single row is not the correct way.
Co-authored-by: Kartik Srivastava <kaushiksri0908@gmail.com>
Co-authored-by: Prakhar Pratyush <prakhar841301@gmail.com>
2022-09-12 16:39:53 +02:00
|
|
|
self.assertEqual(user_ids, {hamlet.id, cordelia.id})
|
|
|
|
cordelia_date_unmuted = UserTopic.objects.filter(
|
2023-03-12 16:19:42 +01:00
|
|
|
user_profile=cordelia, visibility_policy=UserTopic.VisibilityPolicy.UNMUTED
|
user_topics: Refactor add_topic_mute.
In order to support different types of topic visibility policies,
this renames 'add_topic_mute' to
'set_user_topic_visibility_policy_in_database'
and refactors it to accept a parameter 'visibility_policy'.
Create a corresponding UserTopic row for any visibility policy,
not just muting topics.
When a UserTopic row for (user_profile, stream, topic, recipient_id)
exists already, it updates the row with the new visibility_policy.
In the event of a duplicate request, raises a JsonableError.
i.e., new_visibility_policy == existing_visibility_policy.
There is an increase in the database query count in the message-edit
code path.
Reason:
Earlier, 'add_topic_mute' used 'bulk_create' which either
creates or raises IntegrityError -- 1 query.
Now, 'set_user_topic_visibility_policy' uses get_or_create
-- 2 queries in the case of creating new row.
We can't use the previous approach, because now we have to
handle the case of updating the visibility_policy too.
Also, using bulk_* for a single row is not the correct way.
Co-authored-by: Kartik Srivastava <kaushiksri0908@gmail.com>
Co-authored-by: Prakhar Pratyush <prakhar841301@gmail.com>
2022-09-12 16:39:53 +02:00
|
|
|
)[0].last_updated
|
|
|
|
self.assertEqual(cordelia_date_unmuted, date_unmuted)
|
2023-03-31 21:18:12 +02:00
|
|
|
|
|
|
|
def test_add_unmuted_topic(self) -> None:
|
|
|
|
user = self.example_user("hamlet")
|
|
|
|
self.login_user(user)
|
|
|
|
|
|
|
|
stream = get_stream("Verona", user.realm)
|
|
|
|
|
|
|
|
url = "/api/v1/user_topics"
|
|
|
|
data = {
|
|
|
|
"stream_id": stream.id,
|
|
|
|
"topic": "Verona3",
|
|
|
|
"visibility_policy": UserTopic.VisibilityPolicy.UNMUTED,
|
|
|
|
}
|
|
|
|
|
|
|
|
mock_date_unmuted = datetime(2020, 1, 1, tzinfo=timezone.utc).timestamp()
|
|
|
|
|
2023-04-05 13:36:01 +02:00
|
|
|
with self.capture_send_event_calls(expected_num_events=2) as events:
|
2023-03-31 21:18:12 +02:00
|
|
|
with time_machine.travel(datetime(2020, 1, 1, tzinfo=timezone.utc), tick=False):
|
|
|
|
result = self.api_post(user, url, data)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
self.assertTrue(
|
|
|
|
topic_has_visibility_policy(
|
|
|
|
user, stream.id, "verona3", UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
|
|
|
)
|
|
|
|
# Verify if events are sent properly
|
|
|
|
user_topic_event: Dict[str, Any] = {
|
|
|
|
"type": "user_topic",
|
|
|
|
"stream_id": stream.id,
|
|
|
|
"topic_name": "Verona3",
|
|
|
|
"last_updated": mock_date_unmuted,
|
|
|
|
"visibility_policy": UserTopic.VisibilityPolicy.UNMUTED,
|
|
|
|
}
|
|
|
|
muted_topics_event = dict(type="muted_topics", muted_topics=get_topic_mutes(user))
|
|
|
|
self.assertEqual(events[0]["event"], muted_topics_event)
|
|
|
|
self.assertEqual(events[1]["event"], user_topic_event)
|
|
|
|
|
|
|
|
# Now check that no error is raised when attempted to UNMUTE
|
|
|
|
# an already UNMUTED topic. This should be case-insensitive.
|
|
|
|
user_topic_count = UserTopic.objects.count()
|
|
|
|
data["topic"] = "VERONA3"
|
|
|
|
with self.assertLogs(level="INFO") as info_logs:
|
|
|
|
result = self.api_post(user, url, data)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
self.assertEqual(
|
|
|
|
info_logs.output[0],
|
|
|
|
f"INFO:root:User {user.id} tried to set visibility_policy to its current value of {UserTopic.VisibilityPolicy.UNMUTED}",
|
|
|
|
)
|
|
|
|
# Verify that we didn't end up with duplicate UserTopic rows
|
|
|
|
# with the two different cases after the previous API call.
|
|
|
|
self.assertEqual(UserTopic.objects.count() - user_topic_count, 0)
|
|
|
|
|
|
|
|
def test_remove_unmuted_topic(self) -> None:
|
|
|
|
user = self.example_user("hamlet")
|
|
|
|
realm = user.realm
|
|
|
|
self.login_user(user)
|
|
|
|
|
|
|
|
stream = get_stream("Verona", realm)
|
|
|
|
|
|
|
|
do_set_user_topic_visibility_policy(
|
|
|
|
user,
|
|
|
|
stream,
|
|
|
|
"Verona3",
|
|
|
|
visibility_policy=UserTopic.VisibilityPolicy.UNMUTED,
|
|
|
|
last_updated=datetime(2020, 1, 1, tzinfo=timezone.utc),
|
|
|
|
)
|
|
|
|
self.assertTrue(
|
|
|
|
topic_has_visibility_policy(
|
|
|
|
user, stream.id, "verona3", UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
url = "/api/v1/user_topics"
|
|
|
|
data = {
|
|
|
|
"stream_id": stream.id,
|
|
|
|
"topic": "vEroNA3",
|
|
|
|
"visibility_policy": UserTopic.VisibilityPolicy.INHERIT,
|
|
|
|
}
|
|
|
|
|
|
|
|
mock_date_unmute_removed = datetime(2020, 1, 1, tzinfo=timezone.utc).timestamp()
|
|
|
|
|
2023-04-05 13:36:01 +02:00
|
|
|
with self.capture_send_event_calls(expected_num_events=2) as events:
|
2023-03-31 21:18:12 +02:00
|
|
|
with time_machine.travel(datetime(2020, 1, 1, tzinfo=timezone.utc), tick=False):
|
|
|
|
result = self.api_post(user, url, data)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
self.assertFalse(
|
|
|
|
topic_has_visibility_policy(
|
|
|
|
user, stream.id, "verona3", UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
|
|
|
)
|
|
|
|
# Verify if events are sent properly
|
|
|
|
user_topic_event: Dict[str, Any] = {
|
|
|
|
"type": "user_topic",
|
|
|
|
"stream_id": stream.id,
|
|
|
|
"topic_name": data["topic"],
|
|
|
|
"last_updated": mock_date_unmute_removed,
|
|
|
|
"visibility_policy": UserTopic.VisibilityPolicy.INHERIT,
|
|
|
|
}
|
|
|
|
muted_topics_event = dict(type="muted_topics", muted_topics=get_topic_mutes(user))
|
|
|
|
self.assertEqual(events[0]["event"], muted_topics_event)
|
|
|
|
self.assertEqual(events[1]["event"], user_topic_event)
|
|
|
|
|
|
|
|
# Check that removing UNMUTE from a topic for which the user
|
|
|
|
# doesn't already have a visibility_policy doesn't cause an error.
|
|
|
|
with self.assertLogs(level="INFO") as info_logs:
|
|
|
|
result = self.api_post(user, url, data)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
self.assertEqual(
|
|
|
|
info_logs.output[0],
|
|
|
|
f"INFO:root:User {user.id} tried to remove visibility_policy, which actually doesn't exist",
|
|
|
|
)
|
|
|
|
|
|
|
|
def test_unmuted_topic_add_invalid(self) -> None:
|
|
|
|
user = self.example_user("hamlet")
|
|
|
|
self.login_user(user)
|
|
|
|
|
|
|
|
url = "/api/v1/user_topics"
|
|
|
|
data = {
|
|
|
|
"stream_id": 999999999,
|
|
|
|
"topic": "Verona3",
|
|
|
|
"visibility_policy": UserTopic.VisibilityPolicy.UNMUTED,
|
|
|
|
}
|
|
|
|
|
|
|
|
result = self.api_post(user, url, data)
|
|
|
|
self.assert_json_error(result, "Invalid stream ID")
|
|
|
|
|
|
|
|
def test_unmuted_topic_remove_invalid(self) -> None:
|
|
|
|
user = self.example_user("hamlet")
|
|
|
|
self.login_user(user)
|
|
|
|
|
|
|
|
url = "/api/v1/user_topics"
|
|
|
|
data = {
|
|
|
|
"stream_id": 999999999,
|
|
|
|
"topic": "Verona3",
|
|
|
|
"visibility_policy": UserTopic.VisibilityPolicy.INHERIT,
|
|
|
|
}
|
|
|
|
|
|
|
|
result = self.api_post(user, url, data)
|
|
|
|
self.assert_json_error(result, "Invalid stream ID")
|
2023-06-17 17:37:04 +02:00
|
|
|
|
|
|
|
|
|
|
|
class AutomaticallyFollowTopicsTests(ZulipTestCase):
|
|
|
|
def test_automatically_follow_topic_on_initiation(self) -> None:
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
iago = self.example_user("iago")
|
|
|
|
stream = get_stream("Verona", hamlet.realm)
|
|
|
|
topic_name = "teST topic"
|
|
|
|
|
|
|
|
stream_topic_target = StreamTopicTarget(
|
|
|
|
stream_id=stream.id,
|
|
|
|
topic_name=topic_name,
|
|
|
|
)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.FOLLOWED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, set())
|
|
|
|
|
|
|
|
# For hamlet & cordelia,
|
|
|
|
# 'automatically_follow_topics_policy' set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION'.
|
|
|
|
for user in [hamlet, cordelia]:
|
|
|
|
do_change_user_setting(
|
|
|
|
user,
|
|
|
|
"automatically_follow_topics_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
# Hamlet starts a topic. DO automatically follow the topic.
|
|
|
|
self.send_stream_message(hamlet, stream_name=stream.name, topic_name=topic_name)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.FOLLOWED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, {hamlet.id})
|
|
|
|
|
|
|
|
# Cordelia sends a message to the topic which hamlet started. DON'T automatically follow the topic.
|
|
|
|
self.send_stream_message(cordelia, stream_name=stream.name, topic_name=topic_name)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.FOLLOWED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, {hamlet.id})
|
|
|
|
|
|
|
|
# Iago has 'automatically_follow_topics_policy' set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_NEVER'.
|
|
|
|
# DON'T automatically follow the topic, even if he starts the topic.
|
|
|
|
do_change_user_setting(
|
|
|
|
iago,
|
|
|
|
"automatically_follow_topics_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_NEVER,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
self.send_stream_message(iago, stream_name=stream.name, topic_name="New Topic")
|
|
|
|
stream_topic_target = StreamTopicTarget(
|
|
|
|
stream_id=stream.id,
|
|
|
|
topic_name="New Topic",
|
|
|
|
)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.FOLLOWED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, set())
|
|
|
|
|
|
|
|
# When a user sends the first message to a topic with protected history,
|
|
|
|
# the user starts that topic from their perspective. So, the user
|
|
|
|
# should follow the topic if 'automatically_follow_topics_policy' is set
|
|
|
|
# to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION', even if the message
|
|
|
|
# is not the first message in the topic.
|
|
|
|
private_stream = self.make_stream(stream_name="private stream", invite_only=True)
|
|
|
|
self.subscribe(iago, private_stream.name)
|
|
|
|
self.send_stream_message(iago, private_stream.name)
|
|
|
|
|
|
|
|
# Hamlet should automatically follow the topic, even if it already has messages.
|
|
|
|
self.subscribe(hamlet, private_stream.name)
|
|
|
|
self.send_stream_message(hamlet, private_stream.name)
|
|
|
|
stream_topic_target = StreamTopicTarget(
|
|
|
|
stream_id=private_stream.id,
|
|
|
|
topic_name="test",
|
|
|
|
)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.FOLLOWED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, {hamlet.id})
|
|
|
|
|
|
|
|
def test_automatically_follow_topic_on_send(self) -> None:
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
aaron = self.example_user("aaron")
|
|
|
|
stream = get_stream("Verona", hamlet.realm)
|
|
|
|
topic_name = "teST topic"
|
|
|
|
self.send_stream_message(aaron, stream.name, "hello", topic_name)
|
|
|
|
|
|
|
|
# For hamlet, 'automatically_follow_topics_policy' set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_SEND'.
|
|
|
|
do_change_user_setting(
|
|
|
|
hamlet,
|
|
|
|
"automatically_follow_topics_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_SEND,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
# For aaron, 'automatically_follow_topics_policy' NOT set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_SEND'.
|
|
|
|
do_change_user_setting(
|
|
|
|
aaron,
|
|
|
|
"automatically_follow_topics_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
|
|
|
|
stream_topic_target = StreamTopicTarget(
|
|
|
|
stream_id=stream.id,
|
|
|
|
topic_name=topic_name,
|
|
|
|
)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.FOLLOWED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, set())
|
|
|
|
|
|
|
|
# Hamlet sends a message. DO automatically follow the topic.
|
|
|
|
# Aaron sends a message. DON'T automatically follow the topic.
|
|
|
|
self.send_stream_message(hamlet, stream_name=stream.name, topic_name=topic_name)
|
|
|
|
self.send_stream_message(aaron, stream_name=stream.name, topic_name=topic_name)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.FOLLOWED
|
|
|
|
)
|
2023-12-10 14:53:52 +01:00
|
|
|
self.assertEqual(user_ids, {hamlet.id})
|
|
|
|
|
|
|
|
def test_automatically_follow_topic_on_mention(self) -> None:
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
aaron = self.example_user("aaron")
|
|
|
|
stream = get_stream("Verona", hamlet.realm)
|
|
|
|
topic_name = "teST topic"
|
|
|
|
|
|
|
|
do_change_user_setting(
|
|
|
|
hamlet,
|
|
|
|
"automatically_follow_topics_where_mentioned",
|
|
|
|
True,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
|
|
|
|
content = "silently mentioning... @_**" + hamlet.full_name + "**"
|
|
|
|
self.send_stream_message(aaron, stream.name, content, topic_name)
|
|
|
|
|
|
|
|
stream_topic_target = StreamTopicTarget(
|
|
|
|
stream_id=stream.id,
|
|
|
|
topic_name=topic_name,
|
|
|
|
)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.FOLLOWED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, set())
|
|
|
|
|
|
|
|
content = "quoting... \n```quote\n@**" + hamlet.full_name + "**\n```"
|
|
|
|
self.send_stream_message(aaron, stream.name, content, topic_name)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.FOLLOWED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, set())
|
|
|
|
|
|
|
|
content = "mentioning... @**" + hamlet.full_name + "**"
|
|
|
|
self.send_stream_message(aaron, stream.name, content, topic_name)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.FOLLOWED
|
|
|
|
)
|
2023-06-17 17:37:04 +02:00
|
|
|
self.assertEqual(user_ids, {hamlet.id})
|
|
|
|
|
|
|
|
def test_automatically_follow_topic_on_participation_send_message(self) -> None:
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
aaron = self.example_user("aaron")
|
|
|
|
stream = get_stream("Verona", hamlet.realm)
|
|
|
|
topic_name = "teST topic"
|
|
|
|
|
|
|
|
# For hamlet, 'automatically_follow_topics_policy' set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
|
|
|
do_change_user_setting(
|
|
|
|
hamlet,
|
|
|
|
"automatically_follow_topics_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
# For aaron, 'automatically_follow_topics_policy' NOT set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
|
|
|
do_change_user_setting(
|
|
|
|
aaron,
|
|
|
|
"automatically_follow_topics_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
|
|
|
|
stream_topic_target = StreamTopicTarget(
|
|
|
|
stream_id=stream.id,
|
|
|
|
topic_name=topic_name,
|
|
|
|
)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.FOLLOWED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, set())
|
|
|
|
|
|
|
|
# Hamlet sends a message. DO automatically follow the topic.
|
|
|
|
# Aaron sends a message. DON'T automatically follow the topic.
|
|
|
|
self.send_stream_message(hamlet, stream_name=stream.name, topic_name=topic_name)
|
|
|
|
self.send_stream_message(aaron, stream_name=stream.name, topic_name=topic_name)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.FOLLOWED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, {hamlet.id})
|
|
|
|
|
|
|
|
def test_automatically_follow_topic_on_participation_add_reaction(self) -> None:
|
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
aaron = self.example_user("aaron")
|
|
|
|
stream = get_stream("Verona", aaron.realm)
|
|
|
|
topic_name = "teST topic"
|
|
|
|
|
|
|
|
# For cordelia, 'automatically_follow_topics_policy' set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
|
|
|
do_change_user_setting(
|
|
|
|
cordelia,
|
|
|
|
"automatically_follow_topics_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
# For aaron, 'automatically_follow_topics_policy' NOT set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
|
|
|
do_change_user_setting(
|
|
|
|
aaron,
|
|
|
|
"automatically_follow_topics_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
|
|
|
|
stream_topic_target = StreamTopicTarget(
|
|
|
|
stream_id=stream.id,
|
|
|
|
topic_name=topic_name,
|
|
|
|
)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.FOLLOWED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, set())
|
|
|
|
|
|
|
|
message_id = self.send_stream_message(
|
|
|
|
hamlet, stream_name=stream.name, topic_name=topic_name
|
|
|
|
)
|
|
|
|
# Cordelia reacts to a message. DO automatically follow the topic.
|
|
|
|
# Aaron reacts to a message. DON'T automatically follow the topic.
|
|
|
|
check_add_reaction(
|
|
|
|
user_profile=cordelia,
|
|
|
|
message_id=message_id,
|
|
|
|
emoji_name="smile",
|
|
|
|
emoji_code=None,
|
|
|
|
reaction_type=None,
|
|
|
|
)
|
|
|
|
check_add_reaction(
|
|
|
|
user_profile=aaron,
|
|
|
|
message_id=message_id,
|
|
|
|
emoji_name="smile",
|
|
|
|
emoji_code=None,
|
|
|
|
reaction_type=None,
|
|
|
|
)
|
|
|
|
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.FOLLOWED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, {cordelia.id})
|
|
|
|
|
|
|
|
# We don't decrease visibility policy
|
|
|
|
sub = get_subscription(stream.name, cordelia)
|
|
|
|
sub.is_muted = True
|
|
|
|
sub.save()
|
|
|
|
do_change_user_setting(
|
|
|
|
cordelia,
|
|
|
|
"automatically_follow_topics_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_NEVER,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
do_change_user_setting(
|
|
|
|
cordelia,
|
|
|
|
"automatically_unmute_topics_in_muted_streams_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
check_add_reaction(
|
|
|
|
user_profile=cordelia,
|
|
|
|
message_id=message_id,
|
|
|
|
emoji_name="plus",
|
|
|
|
emoji_code=None,
|
|
|
|
reaction_type=None,
|
|
|
|
)
|
|
|
|
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.FOLLOWED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, {cordelia.id})
|
|
|
|
|
|
|
|
# increase visibility policy
|
|
|
|
do_set_user_topic_visibility_policy(
|
|
|
|
cordelia,
|
|
|
|
stream,
|
|
|
|
topic_name,
|
|
|
|
visibility_policy=UserTopic.VisibilityPolicy.MUTED,
|
|
|
|
)
|
|
|
|
check_add_reaction(
|
|
|
|
user_profile=cordelia,
|
|
|
|
message_id=message_id,
|
|
|
|
emoji_name="heart",
|
|
|
|
emoji_code=None,
|
|
|
|
reaction_type=None,
|
|
|
|
)
|
|
|
|
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, {cordelia.id})
|
|
|
|
|
|
|
|
# Add test coverage for 'should_change_visibility_policy' when
|
|
|
|
# new_visibility_policy == current_visibility_policy
|
|
|
|
check_add_reaction(
|
|
|
|
user_profile=cordelia,
|
|
|
|
message_id=message_id,
|
|
|
|
emoji_name="tada",
|
|
|
|
emoji_code=None,
|
|
|
|
reaction_type=None,
|
|
|
|
)
|
|
|
|
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, {cordelia.id})
|
|
|
|
|
|
|
|
def test_automatically_follow_topic_on_participation_participate_in_poll(self) -> None:
|
|
|
|
iago = self.example_user("iago")
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
aaron = self.example_user("aaron")
|
|
|
|
stream = get_stream("Verona", aaron.realm)
|
|
|
|
topic_name = "teST topic"
|
|
|
|
|
|
|
|
# For iago, 'automatically_follow_topics_policy' set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
|
|
|
do_change_user_setting(
|
|
|
|
iago,
|
|
|
|
"automatically_follow_topics_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
# For aaron, 'automatically_follow_topics_policy' NOT set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
|
|
|
do_change_user_setting(
|
|
|
|
aaron,
|
|
|
|
"automatically_follow_topics_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
|
|
|
|
stream_topic_target = StreamTopicTarget(
|
|
|
|
stream_id=stream.id,
|
|
|
|
topic_name=topic_name,
|
|
|
|
)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.FOLLOWED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, set())
|
|
|
|
|
|
|
|
# Hamlet creates a poll.
|
|
|
|
payload = dict(
|
|
|
|
type="stream",
|
|
|
|
to=orjson.dumps(stream.name).decode(),
|
|
|
|
topic=topic_name,
|
|
|
|
content="/poll Preference?\n\nyes\nno",
|
|
|
|
)
|
|
|
|
result = self.api_post(hamlet, "/api/v1/messages", payload)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
# Iago participates in the poll. DO automatically follow the topic.
|
|
|
|
# Aaron participates in the poll. DON'T automatically follow the topic.
|
|
|
|
message = self.get_last_message()
|
|
|
|
|
|
|
|
def participate_in_poll(user: UserProfile, data: Dict[str, object]) -> None:
|
|
|
|
content = orjson.dumps(data).decode()
|
|
|
|
payload = dict(
|
|
|
|
message_id=message.id,
|
|
|
|
msg_type="widget",
|
|
|
|
content=content,
|
|
|
|
)
|
|
|
|
result = self.api_post(user, "/api/v1/submessage", payload)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
participate_in_poll(iago, dict(type="vote", key="1,1", vote=1))
|
|
|
|
participate_in_poll(aaron, dict(type="new_option", idx=7, option="maybe"))
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.FOLLOWED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, {iago.id})
|
|
|
|
|
|
|
|
def test_automatically_follow_topic_on_participation_edit_todo_list(self) -> None:
|
|
|
|
othello = self.example_user("othello")
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
aaron = self.example_user("aaron")
|
|
|
|
stream = get_stream("Verona", aaron.realm)
|
|
|
|
topic_name = "teST topic"
|
|
|
|
|
|
|
|
# For othello, 'automatically_follow_topics_policy' set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
|
|
|
do_change_user_setting(
|
|
|
|
othello,
|
|
|
|
"automatically_follow_topics_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
# For aaron, 'automatically_follow_topics_policy' NOT set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
|
|
|
do_change_user_setting(
|
|
|
|
aaron,
|
|
|
|
"automatically_follow_topics_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
|
|
|
|
stream_topic_target = StreamTopicTarget(
|
|
|
|
stream_id=stream.id,
|
|
|
|
topic_name=topic_name,
|
|
|
|
)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.FOLLOWED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, set())
|
|
|
|
|
|
|
|
# Hamlet creates a todo list.
|
|
|
|
payload = dict(
|
|
|
|
type="stream",
|
|
|
|
to=orjson.dumps(stream.name).decode(),
|
|
|
|
topic=topic_name,
|
|
|
|
content="/todo",
|
|
|
|
)
|
|
|
|
result = self.api_post(hamlet, "/api/v1/messages", payload)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
# Othello edits the todo list. DO automatically follow the topic.
|
|
|
|
# Aaron edits the todo list. DON'T automatically follow the topic.
|
|
|
|
message = self.get_last_message()
|
|
|
|
|
|
|
|
def edit_todo_list(user: UserProfile, data: Dict[str, object]) -> None:
|
|
|
|
content = orjson.dumps(data).decode()
|
|
|
|
payload = dict(
|
|
|
|
message_id=message.id,
|
|
|
|
msg_type="widget",
|
|
|
|
content=content,
|
|
|
|
)
|
|
|
|
result = self.api_post(user, "/api/v1/submessage", payload)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
edit_todo_list(othello, dict(type="new_task", key=7, task="eat", desc="", completed=False))
|
|
|
|
edit_todo_list(aaron, dict(type="strike", key="5,9"))
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.FOLLOWED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, {othello.id})
|
|
|
|
|
|
|
|
|
|
|
|
class AutomaticallyUnmuteTopicsTests(ZulipTestCase):
|
|
|
|
def test_automatically_unmute_topic_on_initiation(self) -> None:
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
iago = self.example_user("iago")
|
|
|
|
stream = get_stream("Verona", hamlet.realm)
|
|
|
|
topic_name = "teST topic"
|
|
|
|
for user in [hamlet, cordelia, iago]:
|
|
|
|
sub = get_subscription(stream.name, user)
|
|
|
|
sub.is_muted = True
|
|
|
|
sub.save()
|
|
|
|
|
|
|
|
stream_topic_target = StreamTopicTarget(
|
|
|
|
stream_id=stream.id,
|
|
|
|
topic_name=topic_name,
|
|
|
|
)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, set())
|
|
|
|
|
|
|
|
# For hamlet & cordelia, 'automatically_unmute_topics_in_muted_streams_policy'
|
|
|
|
# set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION'.
|
|
|
|
for user in [hamlet, cordelia]:
|
|
|
|
do_change_user_setting(
|
|
|
|
user,
|
|
|
|
"automatically_unmute_topics_in_muted_streams_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
# Hamlet starts a topic. DO automatically unmute the topic.
|
|
|
|
self.send_stream_message(hamlet, stream_name=stream.name, topic_name=topic_name)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, {hamlet.id})
|
|
|
|
|
|
|
|
# Cordelia sends a message to the topic which hamlet started. DON'T automatically unmute the topic.
|
|
|
|
self.send_stream_message(cordelia, stream_name=stream.name, topic_name=topic_name)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, {hamlet.id})
|
|
|
|
|
|
|
|
# Iago has 'automatically_unmute_topics_in_muted_streams_policy' set to
|
|
|
|
# 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_NEVER'.
|
|
|
|
# DON'T automatically unmute the topic, even if he starts the topic.
|
|
|
|
do_change_user_setting(
|
|
|
|
iago,
|
|
|
|
"automatically_unmute_topics_in_muted_streams_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_NEVER,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
self.send_stream_message(iago, stream_name=stream.name, topic_name="New Topic")
|
|
|
|
stream_topic_target = StreamTopicTarget(
|
|
|
|
stream_id=stream.id,
|
|
|
|
topic_name="New Topic",
|
|
|
|
)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, set())
|
|
|
|
|
|
|
|
# When a user sends the first message to a topic with protected history,
|
|
|
|
# the user starts that topic from their perspective. So, the user
|
|
|
|
# should unmute the topic if 'automatically_unmute_topics_in_muted_streams_policy'
|
|
|
|
# is set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION', even if
|
|
|
|
# the message is not the first message in the topic.
|
|
|
|
private_stream = self.make_stream(stream_name="private stream", invite_only=True)
|
|
|
|
self.subscribe(iago, private_stream.name)
|
|
|
|
self.send_stream_message(iago, private_stream.name)
|
|
|
|
|
|
|
|
# Hamlet should automatically unmute the topic, even if it already has messages.
|
|
|
|
self.subscribe(hamlet, private_stream.name)
|
|
|
|
sub = get_subscription(private_stream.name, hamlet)
|
|
|
|
sub.is_muted = True
|
|
|
|
sub.save()
|
|
|
|
self.send_stream_message(hamlet, private_stream.name)
|
|
|
|
stream_topic_target = StreamTopicTarget(
|
|
|
|
stream_id=private_stream.id,
|
|
|
|
topic_name="test",
|
|
|
|
)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, {hamlet.id})
|
|
|
|
|
|
|
|
def test_automatically_unmute_topic_on_send(self) -> None:
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
aaron = self.example_user("aaron")
|
|
|
|
stream = get_stream("Verona", hamlet.realm)
|
|
|
|
topic_name = "teST topic"
|
|
|
|
self.send_stream_message(aaron, stream.name, "hello", topic_name)
|
|
|
|
for user in [hamlet, aaron]:
|
|
|
|
sub = get_subscription(stream.name, user)
|
|
|
|
sub.is_muted = True
|
|
|
|
sub.save()
|
|
|
|
|
|
|
|
# For hamlet, 'automatically_unmute_topics_in_muted_streams_policy'
|
|
|
|
# set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_SEND'.
|
|
|
|
do_change_user_setting(
|
|
|
|
hamlet,
|
|
|
|
"automatically_unmute_topics_in_muted_streams_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_SEND,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
# For aaron, 'automatically_unmute_topics_in_muted_streams_policy' NOT
|
|
|
|
# set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_SEND'.
|
|
|
|
do_change_user_setting(
|
|
|
|
aaron,
|
|
|
|
"automatically_unmute_topics_in_muted_streams_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
|
|
|
|
stream_topic_target = StreamTopicTarget(
|
|
|
|
stream_id=stream.id,
|
|
|
|
topic_name=topic_name,
|
|
|
|
)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, set())
|
|
|
|
|
|
|
|
# Hamlet sends a message. DO automatically unmute the topic.
|
|
|
|
# Aaron sends a message. DON'T automatically unmute the topic.
|
|
|
|
self.send_stream_message(hamlet, stream_name=stream.name, topic_name=topic_name)
|
|
|
|
self.send_stream_message(aaron, stream_name=stream.name, topic_name=topic_name)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, {hamlet.id})
|
|
|
|
|
|
|
|
def test_automatically_unmute_topic_on_participation_send_message(self) -> None:
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
aaron = self.example_user("aaron")
|
|
|
|
stream = get_stream("Verona", hamlet.realm)
|
|
|
|
topic_name = "teST topic"
|
|
|
|
for user in [hamlet, aaron]:
|
|
|
|
sub = get_subscription(stream.name, user)
|
|
|
|
sub.is_muted = True
|
|
|
|
sub.save()
|
|
|
|
|
|
|
|
# For hamlet, 'automatically_unmute_topics_in_muted_streams_policy'
|
|
|
|
# set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
|
|
|
do_change_user_setting(
|
|
|
|
hamlet,
|
|
|
|
"automatically_unmute_topics_in_muted_streams_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
# For aaron, 'automatically_unmute_topics_in_muted_streams_policy' NOT
|
|
|
|
# set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
|
|
|
do_change_user_setting(
|
|
|
|
aaron,
|
|
|
|
"automatically_unmute_topics_in_muted_streams_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
|
|
|
|
stream_topic_target = StreamTopicTarget(
|
|
|
|
stream_id=stream.id,
|
|
|
|
topic_name=topic_name,
|
|
|
|
)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, set())
|
|
|
|
|
|
|
|
# Hamlet sends a message. DO automatically unmute the topic.
|
|
|
|
# Aaron sends a message. DON'T automatically unmute the topic.
|
|
|
|
self.send_stream_message(hamlet, stream_name=stream.name, topic_name=topic_name)
|
|
|
|
self.send_stream_message(aaron, stream_name=stream.name, topic_name=topic_name)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, {hamlet.id})
|
|
|
|
|
|
|
|
def test_automatically_unmute_topic_on_participation_add_reaction(self) -> None:
|
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
aaron = self.example_user("aaron")
|
|
|
|
stream = get_stream("Verona", aaron.realm)
|
|
|
|
topic_name = "teST topic"
|
|
|
|
for user in [cordelia, hamlet, aaron]:
|
|
|
|
sub = get_subscription(stream.name, user)
|
|
|
|
sub.is_muted = True
|
|
|
|
sub.save()
|
|
|
|
|
|
|
|
# For cordelia, 'automatically_unmute_topics_in_muted_streams_policy'
|
|
|
|
# set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
|
|
|
do_change_user_setting(
|
|
|
|
cordelia,
|
|
|
|
"automatically_unmute_topics_in_muted_streams_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
# For aaron, 'automatically_unmute_topics_in_muted_streams_policy' NOT
|
|
|
|
# set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
|
|
|
do_change_user_setting(
|
|
|
|
aaron,
|
|
|
|
"automatically_unmute_topics_in_muted_streams_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
|
|
|
|
stream_topic_target = StreamTopicTarget(
|
|
|
|
stream_id=stream.id,
|
|
|
|
topic_name=topic_name,
|
|
|
|
)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, set())
|
|
|
|
|
|
|
|
message_id = self.send_stream_message(
|
|
|
|
hamlet, stream_name=stream.name, topic_name=topic_name
|
|
|
|
)
|
|
|
|
# Cordelia reacts to a message. DO automatically unmute the topic.
|
|
|
|
# Aaron reacts to a message. DON'T automatically unmute the topic.
|
|
|
|
check_add_reaction(
|
|
|
|
user_profile=cordelia,
|
|
|
|
message_id=message_id,
|
|
|
|
emoji_name="smile",
|
|
|
|
emoji_code=None,
|
|
|
|
reaction_type=None,
|
|
|
|
)
|
|
|
|
check_add_reaction(
|
|
|
|
user_profile=aaron,
|
|
|
|
message_id=message_id,
|
|
|
|
emoji_name="smile",
|
|
|
|
emoji_code=None,
|
|
|
|
reaction_type=None,
|
|
|
|
)
|
|
|
|
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, {cordelia.id})
|
|
|
|
|
|
|
|
def test_automatically_unmute_topic_on_participation_participate_in_poll(self) -> None:
|
|
|
|
iago = self.example_user("iago")
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
aaron = self.example_user("aaron")
|
|
|
|
stream = get_stream("Verona", aaron.realm)
|
|
|
|
topic_name = "teST topic"
|
|
|
|
for user in [iago, hamlet, aaron]:
|
|
|
|
sub = get_subscription(stream.name, user)
|
|
|
|
sub.is_muted = True
|
|
|
|
sub.save()
|
|
|
|
|
|
|
|
# For iago, 'automatically_unmute_topics_in_muted_streams_policy'
|
|
|
|
# set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
|
|
|
do_change_user_setting(
|
|
|
|
iago,
|
|
|
|
"automatically_unmute_topics_in_muted_streams_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
# For aaron, 'automatically_unmute_topics_in_muted_streams_policy' NOT
|
|
|
|
# set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
|
|
|
do_change_user_setting(
|
|
|
|
aaron,
|
|
|
|
"automatically_unmute_topics_in_muted_streams_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
|
|
|
|
stream_topic_target = StreamTopicTarget(
|
|
|
|
stream_id=stream.id,
|
|
|
|
topic_name=topic_name,
|
|
|
|
)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, set())
|
|
|
|
|
|
|
|
# Hamlet creates a poll.
|
|
|
|
payload = dict(
|
|
|
|
type="stream",
|
|
|
|
to=orjson.dumps(stream.name).decode(),
|
|
|
|
topic=topic_name,
|
|
|
|
content="/poll Preference?\n\nyes\nno",
|
|
|
|
)
|
|
|
|
result = self.api_post(hamlet, "/api/v1/messages", payload)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
# Iago participates in the poll. DO automatically unmute the topic.
|
|
|
|
# Aaron participates in the poll. DON'T automatically unmute the topic.
|
|
|
|
message = self.get_last_message()
|
|
|
|
|
|
|
|
def participate_in_poll(user: UserProfile, data: Dict[str, object]) -> None:
|
|
|
|
content = orjson.dumps(data).decode()
|
|
|
|
payload = dict(
|
|
|
|
message_id=message.id,
|
|
|
|
msg_type="widget",
|
|
|
|
content=content,
|
|
|
|
)
|
|
|
|
result = self.api_post(user, "/api/v1/submessage", payload)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
participate_in_poll(iago, dict(type="vote", key="1,1", vote=1))
|
|
|
|
participate_in_poll(aaron, dict(type="new_option", idx=7, option="maybe"))
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, {iago.id})
|
|
|
|
|
|
|
|
def test_automatically_unmute_topic_on_participation_edit_todo_list(self) -> None:
|
|
|
|
othello = self.example_user("othello")
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
aaron = self.example_user("aaron")
|
|
|
|
stream = get_stream("Verona", aaron.realm)
|
|
|
|
topic_name = "teST topic"
|
|
|
|
for user in [othello, hamlet, aaron]:
|
|
|
|
sub = get_subscription(stream.name, user)
|
|
|
|
sub.is_muted = True
|
|
|
|
sub.save()
|
|
|
|
# For othello, 'automatically_unmute_topics_in_muted_streams_policy'
|
|
|
|
# set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
|
|
|
do_change_user_setting(
|
|
|
|
othello,
|
|
|
|
"automatically_unmute_topics_in_muted_streams_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
# For aaron, 'automatically_unmute_topics_in_muted_streams_policy' NOT
|
|
|
|
# set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
|
|
|
do_change_user_setting(
|
|
|
|
aaron,
|
|
|
|
"automatically_unmute_topics_in_muted_streams_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
|
|
|
|
stream_topic_target = StreamTopicTarget(
|
|
|
|
stream_id=stream.id,
|
|
|
|
topic_name=topic_name,
|
|
|
|
)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, set())
|
|
|
|
|
|
|
|
# Hamlet creates a todo list.
|
|
|
|
payload = dict(
|
|
|
|
type="stream",
|
|
|
|
to=orjson.dumps(stream.name).decode(),
|
|
|
|
topic=topic_name,
|
|
|
|
content="/todo",
|
|
|
|
)
|
|
|
|
result = self.api_post(hamlet, "/api/v1/messages", payload)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
# Othello edits the todo list. DO automatically unmute the topic.
|
|
|
|
# Aaron edits the todo list. DON'T automatically unmute the topic.
|
|
|
|
message = self.get_last_message()
|
|
|
|
|
|
|
|
def edit_todo_list(user: UserProfile, data: Dict[str, object]) -> None:
|
|
|
|
content = orjson.dumps(data).decode()
|
|
|
|
payload = dict(
|
|
|
|
message_id=message.id,
|
|
|
|
msg_type="widget",
|
|
|
|
content=content,
|
|
|
|
)
|
|
|
|
result = self.api_post(user, "/api/v1/submessage", payload)
|
|
|
|
self.assert_json_success(result)
|
|
|
|
|
|
|
|
edit_todo_list(othello, dict(type="new_task", key=7, task="eat", desc="", completed=False))
|
|
|
|
edit_todo_list(aaron, dict(type="strike", key="5,9"))
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, {othello.id})
|
|
|
|
|
|
|
|
def test_only_automatically_increase_visibility_policy(self) -> None:
|
|
|
|
aaron = self.example_user("aaron")
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
stream = get_stream("Verona", aaron.realm)
|
|
|
|
topic_name = "teST topic"
|
|
|
|
for user in [hamlet, aaron]:
|
|
|
|
sub = get_subscription(stream.name, user)
|
|
|
|
sub.is_muted = True
|
|
|
|
sub.save()
|
|
|
|
|
|
|
|
stream_topic_target = StreamTopicTarget(
|
|
|
|
stream_id=stream.id,
|
|
|
|
topic_name=topic_name,
|
|
|
|
)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, set())
|
|
|
|
# If a topic is already FOLLOWED, we don't change the state to UNMUTED as the
|
|
|
|
# intent of these "automatically follow or unmute" policies is that they can only
|
|
|
|
# increase the user's visibility policy for the topic.
|
|
|
|
do_set_user_topic_visibility_policy(
|
|
|
|
aaron,
|
|
|
|
stream,
|
|
|
|
topic_name,
|
|
|
|
visibility_policy=UserTopic.VisibilityPolicy.FOLLOWED,
|
|
|
|
)
|
|
|
|
do_change_user_setting(
|
|
|
|
aaron,
|
|
|
|
"automatically_unmute_topics_in_muted_streams_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
self.send_stream_message(aaron, stream_name=stream.name, topic_name=topic_name)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, set())
|
|
|
|
|
|
|
|
# increase visibility from MUTED to UNMUTED
|
|
|
|
topic_name = "new Topic"
|
|
|
|
stream_topic_target = StreamTopicTarget(
|
|
|
|
stream_id=stream.id,
|
|
|
|
topic_name=topic_name,
|
|
|
|
)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, set())
|
|
|
|
do_set_user_topic_visibility_policy(
|
|
|
|
hamlet,
|
|
|
|
stream,
|
|
|
|
topic_name,
|
|
|
|
visibility_policy=UserTopic.VisibilityPolicy.MUTED,
|
|
|
|
)
|
|
|
|
do_change_user_setting(
|
|
|
|
hamlet,
|
|
|
|
"automatically_unmute_topics_in_muted_streams_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
self.send_stream_message(hamlet, stream_name=stream.name, topic_name=topic_name)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, {hamlet.id})
|
|
|
|
|
|
|
|
def test_automatically_unmute_policy_unmuted_stream(self) -> None:
|
|
|
|
aaron = self.example_user("aaron")
|
|
|
|
cordelia = self.example_user("cordelia")
|
|
|
|
stream = get_stream("Verona", aaron.realm)
|
|
|
|
topic_name = "teST topic"
|
|
|
|
|
|
|
|
stream_topic_target = StreamTopicTarget(
|
|
|
|
stream_id=stream.id,
|
|
|
|
topic_name=topic_name,
|
|
|
|
)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, set())
|
|
|
|
# The 'automatically_unmute_topics_in_muted_streams_policy' setting has
|
|
|
|
# NO effect in unmuted streams.
|
|
|
|
do_change_user_setting(
|
|
|
|
aaron,
|
|
|
|
"automatically_unmute_topics_in_muted_streams_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
self.send_stream_message(aaron, stream_name=stream.name, topic_name=topic_name)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, set())
|
|
|
|
|
|
|
|
do_set_user_topic_visibility_policy(
|
|
|
|
cordelia,
|
|
|
|
stream,
|
|
|
|
topic_name,
|
|
|
|
visibility_policy=UserTopic.VisibilityPolicy.MUTED,
|
|
|
|
)
|
|
|
|
do_change_user_setting(
|
|
|
|
cordelia,
|
|
|
|
"automatically_unmute_topics_in_muted_streams_policy",
|
|
|
|
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
self.send_stream_message(cordelia, stream_name=stream.name, topic_name=topic_name)
|
|
|
|
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
|
|
|
UserTopic.VisibilityPolicy.UNMUTED
|
|
|
|
)
|
|
|
|
self.assertEqual(user_ids, set())
|