diff --git a/zerver/tests/test_example.py b/zerver/tests/test_example.py index 215a2080f4..a1177bf569 100644 --- a/zerver/tests/test_example.py +++ b/zerver/tests/test_example.py @@ -5,14 +5,15 @@ import orjson import time_machine from django.utils.timezone import now as timezone_now +from zerver.actions.realm_settings import do_set_realm_property from zerver.actions.users import do_change_can_create_users, do_change_user_role -from zerver.lib.exceptions import JsonableError +from zerver.lib.exceptions import JsonableError, StreamWildcardMentionNotAllowedError from zerver.lib.streams import access_stream_for_send_message from zerver.lib.test_classes import ZulipTestCase from zerver.lib.test_helpers import most_recent_message from zerver.lib.users import is_administrator_role -from zerver.models import UserProfile, UserStatus -from zerver.models.realms import get_realm +from zerver.models import Realm, UserProfile, UserStatus +from zerver.models.realms import WildcardMentionPolicyEnum, get_realm from zerver.models.streams import get_stream from zerver.models.users import get_user_by_delivery_email @@ -433,13 +434,75 @@ class TestMocking(ZulipTestCase): # # Mocking is generally used in situations where # we want to avoid running original code for reasons - # like skipping HTTP requests, saving execution time etc. + # like skipping HTTP requests, saving execution time, + # or simulating convenient return values. # # Learn more about mocking in-depth at: # https://zulip.readthedocs.io/en/latest/testing/testing-with-django.html#testing-with-mocks - # - # The following test demonstrates a simple use case - # where mocking is helpful in saving test-run time. + def test_wildcard_mentions(self) -> None: + cordelia = self.example_user("cordelia") + self.login_user(cordelia) + self.subscribe(cordelia, "test_stream") + + # Let's explicitly set the policy for sending wildcard + # mentions to a stream. If a stream has too many + # subscribers, we won't allow any users to spam the stream. + do_set_realm_property( + cordelia.realm, + "wildcard_mention_policy", + WildcardMentionPolicyEnum.NOBODY, + acting_user=None, + ) + + # We will try the same message a couple times. + # Notice the content includes "@**all**". + all_mention = "@**all** test wildcard mention" + + # First, let's test the SAD PATH, where cordelia + # tries a wildcard method and gets rejected. + # + # Here we use both mock.patch to simulate a return value + # and assertRaisesRegex to verify our function raises + # an error. + with ( + mock.patch( + "zerver.lib.message.num_subscribers_for_stream_id", + return_value=Realm.WILDCARD_MENTION_THRESHOLD + 1, + ), + self.assertRaisesRegex( + StreamWildcardMentionNotAllowedError, + "You do not have permission to use channel wildcard mentions in this channel.", + ), + ): + self.send_stream_message( + sender=cordelia, + stream_name="test_stream", + content=all_mention, + ) + + # Verify the message was NOT sent. + message = most_recent_message(cordelia) + self.assertNotEqual(message.content, all_mention) + + # Now for the HAPPY PATH, we still mock the number of + # subscribers, but here we simulate that we are under + # the limit. We expect cordelia's message to go through. + with mock.patch( + "zerver.lib.message.num_subscribers_for_stream_id", + return_value=Realm.WILDCARD_MENTION_THRESHOLD - 1, + ): + self.send_stream_message( + sender=cordelia, + stream_name="test_stream", + content=all_mention, + ) + + # Verify the message WAS sent. + message = most_recent_message(cordelia) + self.assertEqual(message.content, all_mention) + + +class TestTimeTravel(ZulipTestCase): def test_edit_message(self) -> None: """ Verify if the time limit imposed on message editing is working correctly. @@ -484,28 +547,15 @@ class TestMocking(ZulipTestCase): # Now that we tested message editing works within the limit, # we want to verify it doesn't work beyond the limit. # - # To do that we'll have to wait for the time limit to pass which is - # 5 minutes here. Easy, use time.sleep() but mind that it slows down the - # test to a great extent which isn't good. This is when mocking comes to rescue. - # We can check what the original code does to determine whether the time limit - # exceeded and mock that here such that the code runs as if the time limit - # exceeded without actually waiting for that long! - # - # In this case, it is timezone_now, an alias to django.utils.timezone.now, - # to which the difference with message-sent-time is checked. So, we want - # that timezone_now() call to return `datetime` object representing time - # that is beyond the limit. - # - # Notice how mock.patch() is used here to do exactly the above mentioned. - # mock.patch() here makes any calls to `timezone_now` in `zerver.actions.message_edit` - # to return the value passed to `return_value` in the its context. - # You can also use mock.patch() as a decorator depending on the - # requirements. Read more at the documentation link provided above. - + # First, calculate the time we want to travel to, adding + # a little buffer of 100 seconds beyond the limit. time_beyond_edit_limit = message_sent_time + timedelta( seconds=MESSAGE_CONTENT_EDIT_LIMIT + 100 - ) # There's a buffer time applied to the limit, hence the extra 100s. + ) + # Now use time_machine.travel to simulate that we are in the future. + # + # See https://pypi.org/project/time-machine/ for more info. with time_machine.travel(time_beyond_edit_limit, tick=False): result = self.client_patch( f"/json/messages/{sent_message_id}", {"content": "I actually want pizza."}