def test_cache_functions_raise_exception(self) -> None: invalid_key = "invalid_character:\n" good_key = "good_key" with self.assertRaises(InvalidCacheKeyException): cache_get(invalid_key) with self.assertRaises(InvalidCacheKeyException): cache_set(invalid_key, 0) with self.assertRaises(InvalidCacheKeyException): cache_delete(invalid_key) with self.assertRaises(InvalidCacheKeyException): cache_get_many([good_key, invalid_key]) with self.assertRaises(InvalidCacheKeyException): cache_set_many({good_key: 0, invalid_key: 1}) with self.assertRaises(InvalidCacheKeyException): cache_delete_many([good_key, invalid_key])
def test_cached_reaction_data(self) -> None: """ Formatted reactions data is saved in cache. """ sender = self.example_user("hamlet") reaction_info = { "emoji_name": "smile", } result = self.api_post(sender, "/api/v1/messages/1/reactions", reaction_info) self.assert_json_success(result) self.assertEqual(200, result.status_code) key = to_dict_cache_key_id(1) message = extract_message_dict(cache_get(key)[0]) expected_reaction_data = [{ "emoji_name": "smile", "emoji_code": "1f642", "reaction_type": "unicode_emoji", "user": { "email": "*****@*****.**", "id": 10, "full_name": "King Hamlet", }, "user_id": 10, }] self.assertEqual(expected_reaction_data, message["reactions"])
def test_cached_reaction_data(self) -> None: """ Formatted reactions data is saved in cache. """ sender = self.example_user("hamlet") reaction_info = { 'emoji_name': 'smile' } result = self.api_post(sender, '/api/v1/messages/1/reactions', reaction_info) self.assert_json_success(result) self.assertEqual(200, result.status_code) key = to_dict_cache_key_id(1) message = extract_message_dict(cache_get(key)[0]) expected_reaction_data = [{ 'emoji_name': 'smile', 'emoji_code': '263a', 'reaction_type': 'unicode_emoji', 'user': { 'email': '*****@*****.**', 'id': 10, 'full_name': 'King Hamlet' }, 'user_id': 10 }] self.assertEqual(expected_reaction_data, message['reactions'])
def test_invalid_url(self) -> None: url = "http://test.org/" error_url = "http://test.org/x" with mock_queue_publish( "zerver.actions.message_send.queue_json_publish"): msg_id = self.send_personal_message( self.example_user("hamlet"), self.example_user("cordelia"), content=error_url, ) msg = Message.objects.select_related("sender").get(id=msg_id) event = { "message_id": msg_id, "urls": [error_url], "message_realm_id": msg.sender.realm_id, "message_content": error_url, } self.create_mock_response(error_url, status=404) with self.settings(TEST_SUITE=False): with self.assertLogs(level="INFO") as info_logs: FetchLinksEmbedData().consume(event) self.assertTrue( "INFO:root:Time spent on get_link_embed_data for http://test.org/x: " in info_logs.output[0]) # FIXME: Should we really cache this, especially without cache invalidation? cached_data = cache_get(preview_url_cache_key(error_url))[0] self.assertIsNone(cached_data) msg.refresh_from_db() self.assertEqual( '<p><a href="http://test.org/x">http://test.org/x</a></p>', msg.rendered_content) self.assertTrue(responses.assert_call_count(url, 0))
def test_link_preview_no_content_type_header(self) -> None: user = self.example_user("hamlet") self.login_user(user) url = "http://test.org/" with mock_queue_publish( "zerver.actions.message_send.queue_json_publish") as patched: msg_id = self.send_stream_message(user, "Denmark", topic_name="foo", content=url) patched.assert_called_once() queue = patched.call_args[0][0] self.assertEqual(queue, "embed_links") event = patched.call_args[0][1] self.create_mock_response(url) with self.settings(TEST_SUITE=False): with self.assertLogs(level="INFO") as info_logs: FetchLinksEmbedData().consume(event) cached_data = cache_get(preview_url_cache_key(url))[0] self.assertTrue( "INFO:root:Time spent on get_link_embed_data for http://test.org/: " in info_logs.output[0]) assert cached_data is not None msg = Message.objects.select_related("sender").get(id=msg_id) self.assertIn(cached_data.title, msg.rendered_content) assert cached_data.image is not None self.assertIn(re.sub(r"([^\w-])", r"\\\1", cached_data.image), msg.rendered_content)
def test_link_preview_non_html_data(self) -> None: user = self.example_user("hamlet") self.login_user(user) url = "http://test.org/audio.mp3" with mock_queue_publish( "zerver.actions.message_send.queue_json_publish") as patched: msg_id = self.send_stream_message(user, "Denmark", topic_name="foo", content=url) patched.assert_called_once() queue = patched.call_args[0][0] self.assertEqual(queue, "embed_links") event = patched.call_args[0][1] content_type = "application/octet-stream" self.create_mock_response(url, content_type=content_type) with self.settings(TEST_SUITE=False): with self.assertLogs(level="INFO") as info_logs: FetchLinksEmbedData().consume(event) cached_data = cache_get(preview_url_cache_key(url))[0] self.assertTrue( "INFO:root:Time spent on get_link_embed_data for http://test.org/audio.mp3: " in info_logs.output[0]) self.assertIsNone(cached_data) msg = Message.objects.select_related("sender").get(id=msg_id) self.assertEqual( ('<p><a href="http://test.org/audio.mp3">' "http://test.org/audio.mp3</a></p>"), msg.rendered_content, )
def test_link_preview_open_graph_image_missing_content(self) -> None: user = self.example_user("hamlet") self.login_user(user) url = "http://test.org/foo.html" with mock_queue_publish("zerver.actions.message_send.queue_json_publish") as patched: msg_id = self.send_stream_message(user, "Denmark", topic_name="foo", content=url) patched.assert_called_once() queue = patched.call_args[0][0] self.assertEqual(queue, "embed_links") event = patched.call_args[0][1] # HTML without the og:image metadata html = "\n".join( line if "og:image" not in line else '<meta property="og:image"/>' for line in self.open_graph_html.splitlines() ) self.create_mock_response(url, body=html) with self.settings(TEST_SUITE=False): with self.assertLogs(level="INFO") as info_logs: FetchLinksEmbedData().consume(event) cached_data = cache_get(preview_url_cache_key(url))[0] self.assertTrue( "INFO:root:Time spent on get_link_embed_data for http://test.org/foo.html: " in info_logs.output[0] ) assert cached_data is not None self.assertIsNotNone(cached_data.title) self.assertIsNone(cached_data.image) msg = Message.objects.select_related("sender").get(id=msg_id) self.assertEqual( ('<p><a href="http://test.org/foo.html">' "http://test.org/foo.html</a></p>"), msg.rendered_content, )
def maybe_update_first_visible_message_id(realm: Realm, lookback_hours: int) -> None: cache_empty = cache_get( realm_first_visible_message_id_cache_key(realm)) is None recent_messages_count = estimate_recent_messages(realm, lookback_hours) if realm.message_visibility_limit is not None and ( recent_messages_count > 0 or cache_empty): update_first_visible_message_id(realm)
def test_get_muting_users(self) -> None: hamlet = self.example_user("hamlet") self.login_user(hamlet) cordelia = self.example_user("cordelia") self.assertEqual(None, cache_get(get_muting_users_cache_key(cordelia.id))) self.assertEqual(set(), get_muting_users(cordelia.id)) self.assertEqual(set(), cache_get(get_muting_users_cache_key(cordelia.id))[0]) url = f"/api/v1/users/me/muted_users/{cordelia.id}" result = self.api_post(hamlet, url) self.assert_json_success(result) self.assertEqual(None, cache_get(get_muting_users_cache_key(cordelia.id))) self.assertEqual({hamlet.id}, get_muting_users(cordelia.id)) self.assertEqual({hamlet.id}, cache_get(get_muting_users_cache_key(cordelia.id))[0]) url = f"/api/v1/users/me/muted_users/{cordelia.id}" result = self.api_delete(hamlet, url) self.assert_json_success(result) self.assertEqual(None, cache_get(get_muting_users_cache_key(cordelia.id))) self.assertEqual(set(), get_muting_users(cordelia.id)) self.assertEqual(set(), cache_get(get_muting_users_cache_key(cordelia.id))[0])
def test_valid_content_type_error_get_data(self) -> None: url = "http://test.org/" with mock_queue_publish( "zerver.actions.message_send.queue_json_publish"): msg_id = self.send_personal_message( self.example_user("hamlet"), self.example_user("cordelia"), content=url, ) msg = Message.objects.select_related("sender").get(id=msg_id) event = { "message_id": msg_id, "urls": [url], "message_realm_id": msg.sender.realm_id, "message_content": url, } self.create_mock_response(url, body=ConnectionError()) with mock.patch( "zerver.lib.url_preview.preview.get_oembed_data", side_effect=lambda *args, **kwargs: None, ): with mock.patch( "zerver.lib.url_preview.preview.valid_content_type", side_effect=lambda k: True): with self.settings(TEST_SUITE=False): with self.assertLogs(level="INFO") as info_logs: FetchLinksEmbedData().consume(event) self.assertTrue( "INFO:root:Time spent on get_link_embed_data for http://test.org/: " in info_logs.output[0]) # This did not get cached -- hence the lack of [0] on the cache_get cached_data = cache_get(preview_url_cache_key(url)) self.assertIsNone(cached_data) msg.refresh_from_db() self.assertEqual( '<p><a href="http://test.org/">http://test.org/</a></p>', msg.rendered_content)
def test_cached_reaction_data(self) -> None: """ Formatted reactions data is saved in cache. """ senders = [self.example_user("hamlet"), self.example_user("cordelia")] emojis = ["smile", "tada"] expected_emoji_codes = ["1f642", "1f389"] for sender, emoji in zip(senders, emojis): reaction_info = { "emoji_name": emoji, } result = self.api_post(sender, "/api/v1/messages/1/reactions", reaction_info) self.assert_json_success(result) self.assertEqual(200, result.status_code) key = to_dict_cache_key_id(1) message = extract_message_dict(cache_get(key)[0]) expected_reaction_data = [ { "emoji_name": emoji, "emoji_code": emoji_code, "reaction_type": "unicode_emoji", "user": { "email": f"user{sender.id}@zulip.testserver", "id": sender.id, "full_name": sender.full_name, }, "user_id": sender.id, } # It's important that we preserve the loop order in this # test, since this is our test to verify that we're # returning reactions in chronological order. for sender, emoji, emoji_code in zip(senders, emojis, expected_emoji_codes) ] self.assertEqual(expected_reaction_data, message["reactions"])
def test_safe_oembed_html_url(self) -> None: url = "http://test.org/" with mock_queue_publish( "zerver.actions.message_send.queue_json_publish"): msg_id = self.send_personal_message( self.example_user("hamlet"), self.example_user("cordelia"), content=url, ) msg = Message.objects.select_related("sender").get(id=msg_id) event = { "message_id": msg_id, "urls": [url], "message_realm_id": msg.sender.realm_id, "message_content": url, } mocked_data = UrlOEmbedData( html=f'<iframe src="{url}"></iframe>', type="video", image=f"{url}/image.png", ) self.create_mock_response(url) with self.settings(TEST_SUITE=False): with self.assertLogs(level="INFO") as info_logs: with mock.patch( "zerver.lib.url_preview.preview.get_oembed_data", lambda *args, **kwargs: mocked_data, ): FetchLinksEmbedData().consume(event) cached_data = cache_get(preview_url_cache_key(url))[0] self.assertTrue( "INFO:root:Time spent on get_link_embed_data for http://test.org/: " in info_logs.output[0]) self.assertEqual(cached_data, mocked_data) msg.refresh_from_db() self.assertIn(f'a data-id="{escape(mocked_data.html)}"', msg.rendered_content)
def get_first_visible_message_id(realm: Realm) -> int: val = cache_get(realm_first_visible_message_id_cache_key(realm)) if val is not None: return val[0] return 0
def maybe_update_first_visible_message_id(realm: Realm, lookback_hours: int) -> None: cache_empty = cache_get(realm_first_visible_message_id_cache_key(realm)) is None recent_messages_count = estimate_recent_messages(realm, lookback_hours) if realm.message_visibility_limit is not None and (recent_messages_count > 0 or cache_empty): update_first_visible_message_id(realm)