示例#1
0
    def test_num_queries_with_streams(self) -> None:
        main_user = self.example_user('hamlet')
        other_user = self.example_user('cordelia')

        realm_id = main_user.realm_id

        self.login(main_user.email)

        # Try to make page-load do extra work for various subscribed
        # streams.
        for i in range(10):
            stream_name = 'test_stream_' + str(i)
            stream = self.make_stream(stream_name)
            DefaultStream.objects.create(
                realm_id=realm_id,
                stream_id=stream.id
            )
            for user in [main_user, other_user]:
                self.subscribe(user, stream_name)

        # Simulate hitting the page the first time to avoid some noise
        # related to initial logins.
        self._get_home_page()

        # Then for the second page load, measure the number of queries.
        flush_per_request_caches()
        with queries_captured() as queries2:
            result = self._get_home_page()

        self.assert_length(queries2, 35)

        # Do a sanity check that our new streams were in the payload.
        html = result.content.decode('utf-8')
        self.assertIn('test_stream_7', html)
示例#2
0
 def test_num_queries_for_realm_admin(self) -> None:
     # Verify number of queries for Realm admin isn't much higher than for normal users.
     self.login(self.example_email("iago"))
     flush_per_request_caches()
     with queries_captured() as queries:
         with patch('zerver.lib.cache.cache_set') as cache_mock:
             result = self._get_home_page()
             self.assertEqual(result.status_code, 200)
             self.assert_length(cache_mock.call_args_list, 6)
         self.assert_length(queries, 37)
示例#3
0
    def test_multiple_stream_senders(self,
                                     mock_send_future_email: mock.MagicMock,
                                     mock_enough_traffic: mock.MagicMock) -> None:

        client = 'website'  # this makes `sent_by_human` return True

        othello = self.example_user('othello')
        self.subscribe(othello, 'Verona')

        one_day_ago = timezone_now() - datetime.timedelta(days=1)
        Message.objects.all().update(pub_date=one_day_ago)
        one_sec_ago = timezone_now() - datetime.timedelta(seconds=1)

        cutoff = time.mktime(one_sec_ago.timetuple())

        senders = ['hamlet', 'cordelia',  'iago', 'prospero', 'ZOE']
        for sender_name in senders:
            email = self.example_email(sender_name)
            self.login(email)
            content = 'some content for ' + email
            payload = dict(
                type='stream',
                client=client,
                to='Verona',
                topic='lunch',
                content=content,
            )
            result = self.client_post("/json/messages", payload)
            self.assert_json_success(result)

        flush_per_request_caches()
        with queries_captured() as queries:
            handle_digest_email(othello.id, cutoff)

        self.assertTrue(24 <= len(queries) <= 25)

        self.assertEqual(mock_send_future_email.call_count, 1)
        kwargs = mock_send_future_email.call_args[1]
        self.assertEqual(kwargs['to_user_ids'], [othello.id])

        hot_convo = kwargs['context']['hot_conversations'][0]

        expected_participants = {
            self.example_user(sender).full_name
            for sender in senders
        }

        self.assertEqual(set(hot_convo['participants']), expected_participants)
        self.assertEqual(hot_convo['count'], 5 - 2)  # 5 messages, but 2 shown
        teaser_messages = hot_convo['first_few_messages'][0]['senders']
        self.assertIn('some content', teaser_messages[0]['content'][0]['plain'])
        self.assertIn(teaser_messages[0]['sender'], expected_participants)
示例#4
0
    def test_activity(self) -> None:
        self.login(self.example_email("hamlet"))
        client, _ = Client.objects.get_or_create(name='website')
        query = '/json/users/me/pointer'
        last_visit = timezone_now()
        count = 150
        for user_profile in UserProfile.objects.all():
            UserActivity.objects.get_or_create(
                user_profile=user_profile,
                client=client,
                query=query,
                count=count,
                last_visit=last_visit
            )
        flush_per_request_caches()
        with queries_captured() as queries:
            self.client_get('/activity')

        self.assert_length(queries, 4)
示例#5
0
    def test_activity(self) -> None:
        self.login(self.example_email("hamlet"))
        client, _ = Client.objects.get_or_create(name='website')
        query = '/json/users/me/pointer'
        last_visit = timezone_now()
        count = 150
        for activity_user_profile in UserProfile.objects.all():
            UserActivity.objects.get_or_create(
                user_profile=activity_user_profile,
                client=client,
                query=query,
                count=count,
                last_visit=last_visit
            )

        # Fails when not staff
        result = self.client_get('/activity')
        self.assertEqual(result.status_code, 302)

        user_profile = self.example_user("hamlet")
        user_profile.is_staff = True
        user_profile.save()

        flush_per_request_caches()
        with queries_captured() as queries:
            result = self.client_get('/activity')
            self.assertEqual(result.status_code, 200)

        self.assert_length(queries, 13)

        flush_per_request_caches()
        with queries_captured() as queries:
            result = self.client_get('/realm_activity/zulip/')
            self.assertEqual(result.status_code, 200)

        self.assert_length(queries, 9)

        flush_per_request_caches()
        with queries_captured() as queries:
            result = self.client_get('/user_activity/[email protected]/')
            self.assertEqual(result.status_code, 200)

        self.assert_length(queries, 5)
示例#6
0
    def test_home(self) -> None:

        # Keep this list sorted!!!
        html_bits = [
            'Compose your message here...',
            'Exclude messages with topic',
            'Keyboard shortcuts',
            'Loading...',
            'Manage streams',
            'Narrow to topic',
            'Next message',
            'Search streams',
            'Welcome to Zulip',
            # Verify that the app styles get included
            'app-stubentry.js',
            'data-params',
        ]

        # Keep this list sorted!!!
        expected_keys = [
            "alert_words",
            "available_notification_sounds",
            "avatar_source",
            "avatar_url",
            "avatar_url_medium",
            "bot_types",
            "can_create_streams",
            "can_subscribe_other_users",
            "cross_realm_bots",
            "custom_profile_field_types",
            "custom_profile_fields",
            "debug_mode",
            "default_language",
            "default_language_name",
            "delivery_email",
            "demote_inactive_streams",
            "dense_mode",
            "desktop_icon_count_display",
            "development_environment",
            "email",
            "emojiset",
            "emojiset_choices",
            "enable_desktop_notifications",
            "enable_digest_emails",
            "enable_login_emails",
            "enable_offline_email_notifications",
            "enable_offline_push_notifications",
            "enable_online_push_notifications",
            "enable_sounds",
            "enable_stream_audible_notifications",
            "enable_stream_desktop_notifications",
            "enable_stream_email_notifications",
            "enable_stream_push_notifications",
            "enter_sends",
            "first_in_realm",
            "fluid_layout_width",
            "full_name",
            "furthest_read_time",
            "has_mobile_devices",
            "have_initial_messages",
            "high_contrast_mode",
            "hotspots",
            "initial_servertime",
            "insecure_desktop_app",
            "is_admin",
            "is_guest",
            "jitsi_server_url",
            "language_list",
            "language_list_dbl_col",
            "last_event_id",
            "left_side_userlist",
            "login_page",
            "max_avatar_file_size",
            "max_file_upload_size",
            "max_icon_file_size",
            "max_logo_file_size",
            "max_message_id",
            "message_content_in_email_notifications",
            "muted_topics",
            "narrow",
            "narrow_stream",
            "needs_tutorial",
            "never_subscribed",
            "night_mode",
            "notification_sound",
            "password_min_guesses",
            "password_min_length",
            "plan_includes_wide_organization_logo",
            "pm_content_in_desktop_notifications",
            "pointer",
            "poll_timeout",
            "presences",
            "prompt_for_invites",
            "queue_id",
            "realm_add_emoji_by_admins_only",
            "realm_allow_community_topic_editing",
            "realm_allow_edit_history",
            "realm_allow_message_deleting",
            "realm_allow_message_editing",
            "realm_authentication_methods",
            "realm_available_video_chat_providers",
            "realm_avatar_changes_disabled",
            "realm_bot_creation_policy",
            "realm_bot_domain",
            "realm_bots",
            "realm_create_stream_policy",
            "realm_default_code_block_language",
            "realm_default_external_accounts",
            "realm_default_language",
            "realm_default_stream_groups",
            "realm_default_streams",
            "realm_default_twenty_four_hour_time",
            "realm_description",
            "realm_digest_emails_enabled",
            "realm_digest_weekday",
            "realm_disallow_disposable_email_addresses",
            "realm_domains",
            "realm_email_address_visibility",
            "realm_email_auth_enabled",
            "realm_email_changes_disabled",
            "realm_emails_restricted_to_domains",
            "realm_embedded_bots",
            "realm_emoji",
            "realm_filters",
            "realm_google_hangouts_domain",
            "realm_icon_source",
            "realm_icon_url",
            "realm_incoming_webhook_bots",
            "realm_inline_image_preview",
            "realm_inline_url_embed_preview",
            "realm_invite_by_admins_only",
            "realm_invite_required",
            "realm_invite_to_stream_policy",
            "realm_is_zephyr_mirror_realm",
            "realm_logo_source",
            "realm_logo_url",
            "realm_mandatory_topics",
            "realm_message_content_allowed_in_email_notifications",
            "realm_message_content_delete_limit_seconds",
            "realm_message_content_edit_limit_seconds",
            "realm_message_retention_days",
            "realm_name",
            "realm_name_changes_disabled",
            "realm_name_in_notifications",
            "realm_night_logo_source",
            "realm_night_logo_url",
            "realm_non_active_users",
            "realm_notifications_stream_id",
            "realm_password_auth_enabled",
            "realm_plan_type",
            "realm_presence_disabled",
            "realm_private_message_policy",
            "realm_push_notifications_enabled",
            "realm_send_welcome_emails",
            "realm_signup_notifications_stream_id",
            "realm_upload_quota",
            "realm_uri",
            "realm_user_group_edit_policy",
            "realm_user_groups",
            "realm_users",
            "realm_video_chat_provider",
            "realm_waiting_period_threshold",
            "realm_zoom_api_key",
            "realm_zoom_api_secret",
            "realm_zoom_user_id",
            "recent_private_conversations",
            "root_domain_uri",
            "save_stacktraces",
            "search_pills_enabled",
            "server_avatar_changes_disabled",
            "server_generation",
            "server_inline_image_preview",
            "server_inline_url_embed_preview",
            "server_name_changes_disabled",
            "settings_send_digest_emails",
            "starred_message_counts",
            "starred_messages",
            "stop_words",
            "stream_description_max_length",
            "stream_name_max_length",
            "subscriptions",
            "test_suite",
            "timezone",
            "translate_emoticons",
            "translation_data",
            "twenty_four_hour_time",
            "two_fa_enabled",
            "two_fa_enabled_user",
            "unread_msgs",
            "unsubscribed",
            "upgrade_text_for_wide_organization_logo",
            "user_id",
            "user_status",
            "warn_no_email",
            "webpack_public_path",
            "wildcard_mentions_notify",
            "zulip_feature_level",
            "zulip_version",
        ]

        # Verify fails if logged-out
        result = self.client_get('/')
        self.assertEqual(result.status_code, 302)

        self.login('hamlet')

        # Create bot for realm_bots testing. Must be done before fetching home_page.
        bot_info = {
            'full_name': 'The Bot of Hamlet',
            'short_name': 'hambot',
        }
        self.client_post("/json/bots", bot_info)

        # Verify succeeds once logged-in
        flush_per_request_caches()
        with queries_captured() as queries:
            with patch('zerver.lib.cache.cache_set') as cache_mock:
                result = self._get_home_page(stream='Denmark')
        self.assertEqual(set(result["Cache-Control"].split(", ")),
                         {"must-revalidate", "no-store", "no-cache"})

        self.assert_length(queries, 43)
        self.assert_length(cache_mock.call_args_list, 5)

        html = result.content.decode('utf-8')

        for html_bit in html_bits:
            if html_bit not in html:
                raise AssertionError('%s not in result' % (html_bit, ))

        page_params = self._get_page_params(result)

        actual_keys = sorted([str(k) for k in page_params.keys()])

        self.assertEqual(actual_keys, expected_keys)

        # TODO: Inspect the page_params data further.
        # print(ujson.dumps(page_params, indent=2))
        realm_bots_expected_keys = [
            'api_key',
            'avatar_url',
            'bot_type',
            'default_all_public_streams',
            'default_events_register_stream',
            'default_sending_stream',
            'email',
            'full_name',
            'is_active',
            'owner',
            'services',
            'user_id',
        ]

        realm_bots_actual_keys = sorted(
            [str(key) for key in page_params['realm_bots'][0].keys()])
        self.assertEqual(realm_bots_actual_keys, realm_bots_expected_keys)
示例#7
0
    def test_queries(self) -> None:
        user = self.example_user("hamlet")

        self.login_user(user)

        flush_per_request_caches()
        with queries_captured() as queries:
            with mock.patch("zerver.lib.events.always_want") as want_mock:
                fetch_initial_state_data(user)

        self.assert_length(queries, 31)

        expected_counts = dict(
            alert_words=1,
            custom_profile_fields=1,
            default_streams=1,
            default_stream_groups=1,
            hotspots=0,
            message=1,
            muted_topics=1,
            muted_users=1,
            presence=1,
            realm=0,
            realm_bot=1,
            realm_domains=1,
            realm_embedded_bots=0,
            realm_incoming_webhook_bots=0,
            realm_emoji=1,
            realm_filters=1,
            realm_linkifiers=1,
            realm_playgrounds=1,
            realm_user=3,
            realm_user_groups=2,
            recent_private_conversations=1,
            starred_messages=1,
            stream=2,
            stop_words=0,
            subscription=4,
            update_display_settings=0,
            update_global_notifications=0,
            update_message_flags=5,
            user_status=1,
            video_calls=0,
            giphy=0,
        )

        wanted_event_types = {item[0][0] for item in want_mock.call_args_list}

        self.assertEqual(wanted_event_types, set(expected_counts))

        for event_type in sorted(wanted_event_types):
            count = expected_counts[event_type]
            flush_per_request_caches()
            with queries_captured() as queries:
                if event_type == "update_message_flags":
                    event_types = ["update_message_flags", "message"]
                else:
                    event_types = [event_type]

                fetch_initial_state_data(user, event_types=event_types)
            self.assert_length(queries, count)
示例#8
0
    def test_soft_deactivated_user_multiple_stream_senders(self) -> None:
        one_day_ago = timezone_now() - datetime.timedelta(days=1)
        Message.objects.all().update(date_sent=one_day_ago)

        digest_users = [
            self.example_user("othello"),
            self.example_user("aaron"),
            self.example_user("desdemona"),
            self.example_user("polonius"),
        ]
        digest_users.sort(key=lambda user: user.id)

        for digest_user in digest_users:
            for stream in ["Verona", "Scotland", "Denmark"]:
                self.subscribe(digest_user, stream)

        RealmAuditLog.objects.all().delete()

        # Send messages to a stream and unsubscribe - subscribe from that stream
        senders = ["hamlet", "cordelia", "iago", "prospero", "ZOE"]
        self.simulate_stream_conversation("Verona", senders)

        for digest_user in digest_users:
            self.unsubscribe(digest_user, "Verona")
            self.subscribe(digest_user, "Verona")

        # Send messages to other streams
        self.simulate_stream_conversation("Scotland", senders)
        self.simulate_stream_conversation("Denmark", senders)

        one_hour_ago = timezone_now() - datetime.timedelta(seconds=3600)
        cutoff = time.mktime(one_hour_ago.timetuple())

        flush_per_request_caches()

        # When this test is run in isolation, one additional query is run which
        # is equivalent to
        # ContentType.objects.get(app_label='zerver', model='userprofile')
        # This code is run when we call `confirmation.models.create_confirmation_link`.
        # To trigger this, we call the one_click_unsubscribe_link function below.
        one_click_unsubscribe_link(digest_users[0], "digest")

        with mock.patch("zerver.lib.digest.send_future_email") as mock_send_future_email:
            digest_user_ids = [user.id for user in digest_users]

            with queries_captured() as queries:
                with cache_tries_captured() as cache_tries:
                    bulk_handle_digest_email(digest_user_ids, cutoff)

            self.assert_length(queries, 12)
            self.assert_length(cache_tries, 0)

        self.assertEqual(mock_send_future_email.call_count, len(digest_users))

        for i, digest_user in enumerate(digest_users):
            kwargs = mock_send_future_email.call_args_list[i][1]
            self.assertEqual(kwargs["to_user_ids"], [digest_user.id])

            hot_conversations = kwargs["context"]["hot_conversations"]
            self.assertEqual(2, len(hot_conversations), [digest_user.id])

            hot_convo = hot_conversations[0]
            expected_participants = {self.example_user(sender).full_name for sender in senders}

            self.assertEqual(set(hot_convo["participants"]), expected_participants)
            self.assertEqual(hot_convo["count"], 5 - 2)  # 5 messages, but 2 shown
            teaser_messages = hot_convo["first_few_messages"][0]["senders"]
            self.assertIn("some content", teaser_messages[0]["content"][0]["plain"])
            self.assertIn(teaser_messages[0]["sender"], expected_participants)

        last_message_id = get_last_message_id()
        for digest_user in digest_users:
            log_rows = RealmAuditLog.objects.filter(
                modified_user_id=digest_user.id,
                event_type=RealmAuditLog.USER_DIGEST_EMAIL_CREATED,
            )
            (log,) = log_rows
            self.assertEqual(log.event_last_message_id, last_message_id)
示例#9
0
    def do_consume(
        self, consume_func: Callable[[List[Dict[str, Any]]], None], events: List[Dict[str, Any]]
    ) -> None:
        consume_time_seconds: Optional[float] = None
        with configure_scope() as scope:
            scope.clear_breadcrumbs()
            add_breadcrumb(
                type="debug",
                category="queue_processor",
                message=f"Consuming {self.queue_name}",
                data={"events": events, "local_queue_size": self.get_remaining_local_queue_size()},
            )
        try:
            if self.idle:
                # We're reactivating after having gone idle due to emptying the queue.
                # We should update the stats file to keep it fresh and to make it clear
                # that the queue started processing, in case the event we're about to process
                # makes us freeze.
                self.idle = False
                self.update_statistics(self.get_remaining_local_queue_size())

            time_start = time.time()
            if self.MAX_CONSUME_SECONDS and self.ENABLE_TIMEOUTS:
                try:
                    signal.signal(
                        signal.SIGALRM,
                        functools.partial(timer_expired, self.MAX_CONSUME_SECONDS, len(events)),
                    )
                    try:
                        signal.alarm(self.MAX_CONSUME_SECONDS * len(events))
                        consume_func(events)
                    finally:
                        signal.alarm(0)
                finally:
                    signal.signal(signal.SIGALRM, signal.SIG_DFL)
            else:
                consume_func(events)
            consume_time_seconds = time.time() - time_start
            self.consumed_since_last_emptied += len(events)
        except Exception as e:
            self._handle_consume_exception(events, e)
        finally:
            flush_per_request_caches()
            reset_queries()

            if consume_time_seconds is not None:
                self.recent_consume_times.append((len(events), consume_time_seconds))

            remaining_local_queue_size = self.get_remaining_local_queue_size()
            if remaining_local_queue_size == 0:
                self.queue_last_emptied_timestamp = time.time()
                self.consumed_since_last_emptied = 0
                # We've cleared all the events from the queue, so we don't
                # need to worry about the small overhead of doing a disk write.
                # We take advantage of this to update the stats file to keep it fresh,
                # especially since the queue might go idle until new events come in.
                self.update_statistics(0)
                self.idle = True
                return

            self.consume_iteration_counter += 1
            if (
                self.consume_iteration_counter >= self.CONSUME_ITERATIONS_BEFORE_UPDATE_STATS_NUM
                or time.time() - self.last_statistics_update_time
                >= self.MAX_SECONDS_BEFORE_UPDATE_STATS
            ):
                self.consume_iteration_counter = 0
                self.update_statistics(remaining_local_queue_size)
示例#10
0
 def process_response(self, request: HttpRequest,
                      response: HttpResponse) -> HttpResponse:
     # We flush the per-request caches after every request, so they
     # are not shared at all between requests.
     flush_per_request_caches()
     return response
示例#11
0
    def test_home(self) -> None:

        # Keep this list sorted!!!
        html_bits = [
            'Compose your message here...',
            'Exclude messages with topic',
            'Keyboard shortcuts',
            'Loading...',
            'Manage streams',
            'Narrow by topic',
            'Next message',
            'Search streams',
            'Welcome to Zulip',
            'pygments.css',
            'var page_params',
        ]

        # Keep this list sorted!!!
        expected_keys = [
            "alert_words",
            "attachments",
            "autoscroll_forever",
            "avatar_source",
            "avatar_url",
            "avatar_url_medium",
            "can_create_streams",
            "cross_realm_bots",
            "custom_profile_fields",
            "debug_mode",
            "default_desktop_notifications",
            "default_language",
            "default_language_name",
            "development_environment",
            "email",
            "emoji_alt_code",
            "emojiset",
            "emojiset_choices",
            "enable_desktop_notifications",
            "enable_digest_emails",
            "enable_offline_email_notifications",
            "enable_offline_push_notifications",
            "enable_online_push_notifications",
            "enable_sounds",
            "enable_stream_desktop_notifications",
            "enable_stream_email_notifications",
            "enable_stream_push_notifications",
            "enable_stream_sounds",
            "enter_sends",
            "first_in_realm",
            "full_name",
            "furthest_read_time",
            "has_mobile_devices",
            "have_initial_messages",
            "high_contrast_mode",
            "hotspots",
            "initial_servertime",
            "is_admin",
            "language_list",
            "language_list_dbl_col",
            "last_event_id",
            "left_side_userlist",
            "login_page",
            "max_avatar_file_size",
            "max_icon_file_size",
            "max_message_id",
            "maxfilesize",
            "muted_topics",
            "narrow",
            "narrow_stream",
            "needs_tutorial",
            "never_subscribed",
            "night_mode",
            "password_min_guesses",
            "password_min_length",
            "pm_content_in_desktop_notifications",
            "pointer",
            "poll_timeout",
            "presences",
            "prompt_for_invites",
            "queue_id",
            "realm_add_emoji_by_admins_only",
            "realm_allow_edit_history",
            "realm_allow_message_deleting",
            "realm_allow_message_editing",
            "realm_authentication_methods",
            "realm_bot_domain",
            "realm_bots",
            "realm_create_stream_by_admins_only",
            "realm_default_language",
            "realm_default_stream_groups",
            "realm_default_streams",
            "realm_description",
            "realm_domains",
            "realm_email_auth_enabled",
            "realm_email_changes_disabled",
            "realm_embedded_bots",
            "realm_emoji",
            "realm_filters",
            "realm_icon_source",
            "realm_icon_url",
            "realm_inline_image_preview",
            "realm_inline_url_embed_preview",
            "realm_invite_by_admins_only",
            "realm_invite_required",
            "realm_is_zephyr_mirror_realm",
            "realm_mandatory_topics",
            "realm_message_content_edit_limit_seconds",
            "realm_message_retention_days",
            "realm_name",
            "realm_name_changes_disabled",
            "realm_non_active_users",
            "realm_notifications_stream_id",
            "realm_password_auth_enabled",
            "realm_presence_disabled",
            "realm_restricted_to_domain",
            "realm_show_digest_email",
            "realm_signup_notifications_stream_id",
            "realm_uri",
            "realm_user_groups",
            "realm_users",
            "realm_waiting_period_threshold",
            "root_domain_uri",
            "save_stacktraces",
            "server_generation",
            "server_inline_image_preview",
            "server_inline_url_embed_preview",
            "subscriptions",
            "test_suite",
            "timezone",
            "total_uploads_size",
            "twenty_four_hour_time",
            "unread_msgs",
            "unsubscribed",
            "upload_quota",
            "use_websockets",
            "user_id",
            "zulip_version",
        ]

        email = self.example_email("hamlet")

        # Verify fails if logged-out
        result = self.client_get('/')
        self.assertEqual(result.status_code, 302)

        self.login(email)

        # Create bot for realm_bots testing. Must be done before fetching home_page.
        bot_info = {
            'full_name': 'The Bot of Hamlet',
            'short_name': 'hambot',
        }
        self.client_post("/json/bots", bot_info)

        # Verify succeeds once logged-in
        flush_per_request_caches()
        with queries_captured() as queries:
            with patch('zerver.lib.cache.cache_set') as cache_mock:
                result = self._get_home_page(stream='Denmark')

        self.assert_length(queries, 40)
        self.assert_length(cache_mock.call_args_list, 7)

        html = result.content.decode('utf-8')

        for html_bit in html_bits:
            if html_bit not in html:
                raise AssertionError('%s not in result' % (html_bit, ))

        page_params = self._get_page_params(result)

        actual_keys = sorted([str(k) for k in page_params.keys()])

        self.assertEqual(actual_keys, expected_keys)

        # TODO: Inspect the page_params data further.
        # print(ujson.dumps(page_params, indent=2))
        realm_bots_expected_keys = [
            'api_key',
            'avatar_url',
            'bot_type',
            'default_all_public_streams',
            'default_events_register_stream',
            'default_sending_stream',
            'email',
            'full_name',
            'is_active',
            'owner',
            'user_id',
        ]

        realm_bots_actual_keys = sorted(
            [str(key) for key in page_params['realm_bots'][0].keys()])
        self.assertEqual(realm_bots_actual_keys, realm_bots_expected_keys)
示例#12
0
    def test_soft_deactivated_user_multiple_stream_senders(self) -> None:
        one_day_ago = timezone_now() - datetime.timedelta(days=1)
        Message.objects.all().update(date_sent=one_day_ago)

        digest_users = [
            self.example_user('othello'),
            self.example_user('aaron'),
            self.example_user('desdemona'),
            self.example_user('polonius'),
        ]

        for digest_user in digest_users:
            for stream in ['Verona', 'Scotland', 'Denmark']:
                self.subscribe(digest_user, stream)

        RealmAuditLog.objects.all().delete()

        for digest_user in digest_users:
            digest_user.long_term_idle = True
            digest_user.save(update_fields=['long_term_idle'])

        # Send messages to a stream and unsubscribe - subscribe from that stream
        senders = ['hamlet', 'cordelia', 'iago', 'prospero', 'ZOE']
        self.simulate_stream_conversation('Verona', senders)

        for digest_user in digest_users:
            self.unsubscribe(digest_user, 'Verona')
            self.subscribe(digest_user, 'Verona')

        # Send messages to other streams
        self.simulate_stream_conversation('Scotland', senders)
        self.simulate_stream_conversation('Denmark', senders)

        one_hour_ago = timezone_now() - datetime.timedelta(seconds=3600)
        cutoff = time.mktime(one_hour_ago.timetuple())

        flush_per_request_caches()

        # When this test is run in isolation, one additional query is run which
        # is equivalent to
        # ContentType.objects.get(app_label='zerver', model='userprofile')
        # This code is run when we call `confirmation.models.create_confirmation_link`.
        # To trigger this, we call the one_click_unsubscribe_link function below.
        one_click_unsubscribe_link(digest_users[0], 'digest')

        with mock.patch('zerver.lib.digest.send_future_email'
                        ) as mock_send_future_email:
            digest_user_ids = [user.id for user in digest_users]

            with queries_captured() as queries:
                with cache_tries_captured() as cache_tries:
                    bulk_handle_digest_email(digest_user_ids, cutoff)

            self.assert_length(queries, 37)
            self.assert_length(cache_tries, 4)

        self.assertEqual(mock_send_future_email.call_count, len(digest_users))

        for i, digest_user in enumerate(digest_users):
            kwargs = mock_send_future_email.call_args_list[i][1]
            self.assertEqual(kwargs['to_user_ids'], [digest_user.id])

            hot_conversations = kwargs['context']['hot_conversations']
            self.assertEqual(2, len(hot_conversations), [digest_user.id])

            hot_convo = hot_conversations[0]
            expected_participants = {
                self.example_user(sender).full_name
                for sender in senders
            }

            self.assertEqual(set(hot_convo['participants']),
                             expected_participants)
            self.assertEqual(hot_convo['count'],
                             5 - 2)  # 5 messages, but 2 shown
            teaser_messages = hot_convo['first_few_messages'][0]['senders']
            self.assertIn('some content',
                          teaser_messages[0]['content'][0]['plain'])
            self.assertIn(teaser_messages[0]['sender'], expected_participants)
示例#13
0
    def test_soft_deactivated_user_multiple_stream_senders(self,
                                                           mock_send_future_email: mock.MagicMock,
                                                           mock_enough_traffic: mock.MagicMock) -> None:

        one_day_ago = timezone_now() - datetime.timedelta(days=1)
        Message.objects.all().update(pub_date=one_day_ago)

        othello = self.example_user('othello')
        for stream in ['Verona', 'Scotland', 'Denmark']:
            self.subscribe(othello, stream)
        RealmAuditLog.objects.all().delete()

        othello.long_term_idle = True
        othello.save(update_fields=['long_term_idle'])

        # Send messages to a stream and unsubscribe - subscribe from that stream
        senders = ['hamlet', 'cordelia',  'iago', 'prospero', 'ZOE']
        self.simulate_stream_conversation('Verona', senders)
        self.unsubscribe(othello, 'Verona')
        self.subscribe(othello, 'Verona')

        # Send messages to other streams
        self.simulate_stream_conversation('Scotland', senders)
        self.simulate_stream_conversation('Denmark', senders)

        one_hour_ago = timezone_now() - datetime.timedelta(seconds=3600)
        cutoff = time.mktime(one_hour_ago.timetuple())

        flush_per_request_caches()
        # When this test is run in isolation, one additional query is run which
        # is equivalent to
        # ContentType.objects.get(app_label='zerver', model='userprofile')
        # This code is run when we call `confirmation.models.create_confirmation_link`.
        # To trigger this, we call the one_click_unsubscribe_link function below.
        one_click_unsubscribe_link(othello, 'digest')
        with queries_captured() as queries:
            handle_digest_email(othello.id, cutoff)

        # This can definitely be optimized; for both the huddle and
        # stream cases, the get_narrow_url API ends up double-fetching
        # some data because of how the functions are organized.
        self.assert_length(queries, 9)

        self.assertEqual(mock_send_future_email.call_count, 1)
        kwargs = mock_send_future_email.call_args[1]
        self.assertEqual(kwargs['to_user_ids'], [othello.id])

        hot_conversations = kwargs['context']['hot_conversations']
        self.assertEqual(2, len(hot_conversations), [othello.id])

        hot_convo = hot_conversations[0]
        expected_participants = {
            self.example_user(sender).full_name
            for sender in senders
        }

        self.assertEqual(set(hot_convo['participants']), expected_participants)
        self.assertEqual(hot_convo['count'], 5 - 2)  # 5 messages, but 2 shown
        teaser_messages = hot_convo['first_few_messages'][0]['senders']
        self.assertIn('some content', teaser_messages[0]['content'][0]['plain'])
        self.assertIn(teaser_messages[0]['sender'], expected_participants)
示例#14
0
    def test_home(self) -> None:
        # Keep this list sorted!!!
        html_bits = [
            'Compose your message here...',
            'Exclude messages with topic',
            'Keyboard shortcuts',
            'Loading...',
            'Manage streams',
            'Narrow to topic',
            'Next message',
            'Search streams',
            # Verify that the app styles get included
            'app-stubentry.js',
            'data-params',
        ]

        # Verify fails if logged-out
        result = self.client_get('/')
        self.assertEqual(result.status_code, 302)

        self.login('hamlet')

        # Create bot for realm_bots testing. Must be done before fetching home_page.
        bot_info = {
            'full_name': 'The Bot of Hamlet',
            'short_name': 'hambot',
        }
        self.client_post("/json/bots", bot_info)

        # Verify succeeds once logged-in
        flush_per_request_caches()
        with queries_captured() as queries:
            with patch('zerver.lib.cache.cache_set') as cache_mock:
                result = self._get_home_page(stream='Denmark')
                self.check_rendered_logged_in_app(result)
        self.assertEqual(set(result["Cache-Control"].split(", ")),
                         {"must-revalidate", "no-store", "no-cache"})

        self.assert_length(queries, 42)
        self.assert_length(cache_mock.call_args_list, 5)

        html = result.content.decode('utf-8')

        for html_bit in html_bits:
            if html_bit not in html:
                raise AssertionError(f'{html_bit} not in result')

        page_params = self._get_page_params(result)

        actual_keys = sorted(str(k) for k in page_params.keys())

        self.assertEqual(actual_keys, self.expected_page_params_keys)

        # TODO: Inspect the page_params data further.
        # print(orjson.dumps(page_params, option=orjson.OPT_INDENT_2).decode())
        realm_bots_expected_keys = [
            'api_key',
            'avatar_url',
            'bot_type',
            'default_all_public_streams',
            'default_events_register_stream',
            'default_sending_stream',
            'email',
            'full_name',
            'is_active',
            'owner_id',
            'services',
            'user_id',
        ]

        realm_bots_actual_keys = sorted(str(key) for key in page_params['realm_bots'][0].keys())
        self.assertEqual(realm_bots_actual_keys, realm_bots_expected_keys)
示例#15
0
    def test_home(self) -> None:

        # Keep this list sorted!!!
        html_bits = [
            'Compose your message here...',
            'Exclude messages with topic',
            'Keyboard shortcuts',
            'Loading...',
            'Manage streams',
            'Narrow by topic',
            'Next message',
            'Search streams',
            'Welcome to Zulip',
            # Verify that the app styles get included
            'app-styles-stubentry.js',
            'var page_params',
        ]

        # Keep this list sorted!!!
        expected_keys = [
            "alert_words",
            "avatar_source",
            "avatar_url",
            "avatar_url_medium",
            "bot_types",
            "can_create_streams",
            "cross_realm_bots",
            "custom_profile_field_types",
            "custom_profile_fields",
            "debug_mode",
            "default_language",
            "default_language_name",
            "development_environment",
            "email",
            "emojiset",
            "emojiset_choices",
            "enable_desktop_notifications",
            "enable_digest_emails",
            "enable_offline_email_notifications",
            "enable_offline_push_notifications",
            "enable_online_push_notifications",
            "enable_sounds",
            "enable_stream_desktop_notifications",
            "enable_stream_email_notifications",
            "enable_stream_push_notifications",
            "enable_stream_sounds",
            "enter_sends",
            "first_in_realm",
            "full_name",
            "furthest_read_time",
            "has_mobile_devices",
            "have_initial_messages",
            "high_contrast_mode",
            "hotspots",
            "initial_servertime",
            "is_admin",
            "jitsi_server_url",
            "language_list",
            "language_list_dbl_col",
            "last_event_id",
            "left_side_userlist",
            "login_page",
            "max_avatar_file_size",
            "max_icon_file_size",
            "max_message_id",
            "maxfilesize",
            "message_content_in_email_notifications",
            "muted_topics",
            "narrow",
            "narrow_stream",
            "needs_tutorial",
            "never_subscribed",
            "night_mode",
            "password_min_guesses",
            "password_min_length",
            "pm_content_in_desktop_notifications",
            "pointer",
            "poll_timeout",
            "presences",
            "prompt_for_invites",
            "queue_id",
            "realm_add_emoji_by_admins_only",
            "realm_allow_community_topic_editing",
            "realm_allow_edit_history",
            "realm_allow_message_deleting",
            "realm_allow_message_editing",
            "realm_authentication_methods",
            "realm_available_video_chat_providers",
            "realm_bot_creation_policy",
            "realm_bot_domain",
            "realm_bots",
            "realm_create_stream_by_admins_only",
            "realm_default_language",
            "realm_default_stream_groups",
            "realm_default_streams",
            "realm_default_twenty_four_hour_time",
            "realm_description",
            "realm_disallow_disposable_email_addresses",
            "realm_domains",
            "realm_email_auth_enabled",
            "realm_email_changes_disabled",
            "realm_embedded_bots",
            "realm_emoji",
            "realm_filters",
            "realm_google_hangouts_domain",
            "realm_icon_source",
            "realm_icon_url",
            "realm_inline_image_preview",
            "realm_inline_url_embed_preview",
            "realm_invite_by_admins_only",
            "realm_invite_required",
            "realm_is_zephyr_mirror_realm",
            "realm_mandatory_topics",
            "realm_message_content_delete_limit_seconds",
            "realm_message_content_edit_limit_seconds",
            "realm_message_retention_days",
            "realm_name",
            "realm_name_changes_disabled",
            "realm_name_in_notifications",
            "realm_non_active_users",
            "realm_notifications_stream_id",
            "realm_password_auth_enabled",
            "realm_presence_disabled",
            "realm_push_notifications_enabled",
            "realm_restricted_to_domain",
            "realm_send_welcome_emails",
            "realm_show_digest_email",
            "realm_signup_notifications_stream_id",
            "realm_uri",
            "realm_user_groups",
            "realm_users",
            "realm_video_chat_provider",
            "realm_waiting_period_threshold",
            "root_domain_uri",
            "save_stacktraces",
            "server_generation",
            "server_inline_image_preview",
            "server_inline_url_embed_preview",
            "stream_description_max_length",
            "stream_name_max_length",
            "subscriptions",
            "test_suite",
            "timezone",
            "translate_emoticons",
            "twenty_four_hour_time",
            "unread_msgs",
            "unsubscribed",
            "use_websockets",
            "user_id",
            "warn_no_email",
            "zulip_version",
        ]

        email = self.example_email("hamlet")

        # Verify fails if logged-out
        result = self.client_get('/')
        self.assertEqual(result.status_code, 302)

        self.login(email)

        # Create bot for realm_bots testing. Must be done before fetching home_page.
        bot_info = {
            'full_name': 'The Bot of Hamlet',
            'short_name': 'hambot',
        }
        self.client_post("/json/bots", bot_info)

        # Verify succeeds once logged-in
        flush_per_request_caches()
        with queries_captured() as queries:
            with patch('zerver.lib.cache.cache_set') as cache_mock:
                result = self._get_home_page(stream='Denmark')

        self.assert_length(queries, 41)
        self.assert_length(cache_mock.call_args_list, 7)

        html = result.content.decode('utf-8')

        for html_bit in html_bits:
            if html_bit not in html:
                raise AssertionError('%s not in result' % (html_bit,))

        page_params = self._get_page_params(result)

        actual_keys = sorted([str(k) for k in page_params.keys()])

        self.assertEqual(actual_keys, expected_keys)

        # TODO: Inspect the page_params data further.
        # print(ujson.dumps(page_params, indent=2))
        realm_bots_expected_keys = [
            'api_key',
            'avatar_url',
            'bot_type',
            'default_all_public_streams',
            'default_events_register_stream',
            'default_sending_stream',
            'email',
            'full_name',
            'is_active',
            'owner',
            'services',
            'user_id',
        ]

        realm_bots_actual_keys = sorted([str(key) for key in page_params['realm_bots'][0].keys()])
        self.assertEqual(realm_bots_actual_keys, realm_bots_expected_keys)
示例#16
0
文件: test_home.py 项目: dcfix/zulip
    def test_home(self) -> None:
        # Keep this list sorted!!!
        html_bits = [
            "start the conversation",
            "Keyboard shortcuts",
            "Loading...",
            "Filter streams",
            # Verify that the app styles get included
            "app-stubentry.js",
            "data-params",
        ]

        self.login("hamlet")

        # Create bot for realm_bots testing. Must be done before fetching home_page.
        bot_info = {
            "full_name": "The Bot of Hamlet",
            "short_name": "hambot",
        }
        self.client_post("/json/bots", bot_info)

        # Verify succeeds once logged-in
        flush_per_request_caches()
        with queries_captured() as queries:
            with patch("zerver.lib.cache.cache_set") as cache_mock:
                result = self._get_home_page(stream="Denmark")
                self.check_rendered_logged_in_app(result)
        self.assertEqual(set(result["Cache-Control"].split(", ")),
                         {"must-revalidate", "no-store", "no-cache"})

        self.assert_length(queries, 40)
        self.assert_length(cache_mock.call_args_list, 5)

        html = result.content.decode("utf-8")

        for html_bit in html_bits:
            if html_bit not in html:
                raise AssertionError(f"{html_bit} not in result")

        page_params = self._get_page_params(result)

        actual_keys = sorted(str(k) for k in page_params.keys())

        self.assertEqual(actual_keys, self.expected_page_params_keys)

        # TODO: Inspect the page_params data further.
        # print(orjson.dumps(page_params, option=orjson.OPT_INDENT_2).decode())
        realm_bots_expected_keys = [
            "api_key",
            "avatar_url",
            "bot_type",
            "default_all_public_streams",
            "default_events_register_stream",
            "default_sending_stream",
            "email",
            "full_name",
            "is_active",
            "owner_id",
            "services",
            "user_id",
        ]

        realm_bots_actual_keys = sorted(
            str(key) for key in page_params["realm_bots"][0].keys())
        self.assertEqual(realm_bots_actual_keys, realm_bots_expected_keys)
示例#17
0
 def process_response(self, request, response):
     # type: (HttpRequest, HttpResponse) -> HttpResponse
     # We flush the per-request caches after every request, so they
     # are not shared at all between requests.
     flush_per_request_caches()
     return response
示例#18
0
 def tearDown(self) -> None:
     super().tearDown()
     # Important: we need to clear event queues to avoid leaking data to future tests.
     clear_client_event_queues_for_testing()
     clear_supported_auth_backends_cache()
     flush_per_request_caches()