2017-11-09 18:53:02 +01:00
|
|
|
import time
|
2019-02-11 07:03:20 +01:00
|
|
|
from typing import List
|
2020-06-11 00:54:34 +02:00
|
|
|
from unittest.mock import patch
|
2017-03-08 12:36:07 +01:00
|
|
|
|
2019-04-24 03:18:00 +02:00
|
|
|
from bs4 import BeautifulSoup
|
2022-07-14 17:24:04 +02:00
|
|
|
from django.http import HttpResponse
|
2020-06-11 00:54:34 +02:00
|
|
|
|
2019-04-26 15:02:16 +02:00
|
|
|
from zerver.lib.realm_icon import get_realm_icon_url
|
2022-07-14 17:24:04 +02:00
|
|
|
from zerver.lib.request import RequestNotes
|
2017-11-09 18:53:02 +01:00
|
|
|
from zerver.lib.test_classes import ZulipTestCase
|
2022-07-14 17:24:04 +02:00
|
|
|
from zerver.lib.test_helpers import HostRequestMock
|
2022-01-22 07:31:33 +01:00
|
|
|
from zerver.lib.utils import assert_is_not_none
|
2022-07-14 17:24:04 +02:00
|
|
|
from zerver.middleware import LogRequests, is_slow_query, write_log_line
|
2020-05-08 16:37:58 +02:00
|
|
|
from zerver.models import get_realm
|
2022-07-14 17:24:04 +02:00
|
|
|
from zilencer.models import RemoteZulipServer
|
2017-03-08 12:36:07 +01:00
|
|
|
|
2020-06-11 00:54:34 +02:00
|
|
|
|
2017-11-09 18:53:02 +01:00
|
|
|
class SlowQueryTest(ZulipTestCase):
|
2017-11-10 11:57:02 +01:00
|
|
|
SLOW_QUERY_TIME = 10
|
2021-02-12 08:19:30 +01:00
|
|
|
log_data = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"extra": "[transport=websocket]",
|
|
|
|
"time_started": 0,
|
|
|
|
"markdown_requests_start": 0,
|
|
|
|
"markdown_time_start": 0,
|
|
|
|
"remote_cache_time_start": 0,
|
|
|
|
"remote_cache_requests_start": 0,
|
2021-02-12 08:19:30 +01:00
|
|
|
}
|
2017-11-10 11:57:02 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_is_slow_query(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertFalse(is_slow_query(1.1, "/some/random/url"))
|
|
|
|
self.assertTrue(is_slow_query(2, "/some/random/url"))
|
|
|
|
self.assertTrue(is_slow_query(5.1, "/activity"))
|
|
|
|
self.assertFalse(is_slow_query(2, "/activity"))
|
|
|
|
self.assertFalse(is_slow_query(2, "/realm_activity/whatever"))
|
|
|
|
self.assertFalse(is_slow_query(2, "/user_activity/whatever"))
|
|
|
|
self.assertFalse(is_slow_query(9, "/accounts/webathena_kerberos_login/"))
|
|
|
|
self.assertTrue(is_slow_query(11, "/accounts/webathena_kerberos_login/"))
|
2017-11-09 18:53:02 +01:00
|
|
|
|
2020-05-08 16:37:58 +02:00
|
|
|
def test_slow_query_log(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.log_data["time_started"] = time.time() - self.SLOW_QUERY_TIME
|
2021-07-26 19:43:57 +02:00
|
|
|
with self.assertLogs(
|
|
|
|
"zulip.slow_queries", level="INFO"
|
|
|
|
) as slow_query_logger, self.assertLogs(
|
|
|
|
"zulip.requests", level="INFO"
|
|
|
|
) as middleware_normal_logger:
|
2021-02-12 08:19:30 +01:00
|
|
|
write_log_line(
|
|
|
|
self.log_data,
|
2021-02-12 08:20:45 +01:00
|
|
|
path="/some/endpoint/",
|
|
|
|
method="GET",
|
|
|
|
remote_ip="123.456.789.012",
|
|
|
|
requestor_for_logs="unknown",
|
|
|
|
client_name="?",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2021-07-26 19:43:57 +02:00
|
|
|
self.assert_length(middleware_normal_logger.output, 1)
|
|
|
|
self.assert_length(slow_query_logger.output, 1)
|
2020-05-08 16:37:58 +02:00
|
|
|
|
2020-06-04 03:04:07 +02:00
|
|
|
self.assertRegex(
|
2021-07-26 19:43:57 +02:00
|
|
|
slow_query_logger.output[0],
|
python: Use trailing commas consistently.
Automatically generated by the following script, based on the output
of lint with flake8-comma:
import re
import sys
last_filename = None
last_row = None
lines = []
for msg in sys.stdin:
m = re.match(
r"\x1b\[35mflake8 \|\x1b\[0m \x1b\[1;31m(.+):(\d+):(\d+): (\w+)", msg
)
if m:
filename, row_str, col_str, err = m.groups()
row, col = int(row_str), int(col_str)
if filename == last_filename:
assert last_row != row
else:
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
with open(filename) as f:
lines = f.readlines()
last_filename = filename
last_row = row
line = lines[row - 1]
if err in ["C812", "C815"]:
lines[row - 1] = line[: col - 1] + "," + line[col - 1 :]
elif err in ["C819"]:
assert line[col - 2] == ","
lines[row - 1] = line[: col - 2] + line[col - 1 :].lstrip(" ")
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-10 05:23:40 +02:00
|
|
|
r"123\.456\.789\.012 GET 200 10\.\ds .* \(unknown via \?\)",
|
2020-05-08 16:37:58 +02:00
|
|
|
)
|
2018-12-14 23:28:00 +01:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2018-12-14 23:28:00 +01:00
|
|
|
class OpenGraphTest(ZulipTestCase):
|
2021-02-12 08:19:30 +01:00
|
|
|
def check_title_and_description(
|
|
|
|
self,
|
|
|
|
path: str,
|
|
|
|
title: str,
|
|
|
|
in_description: List[str],
|
|
|
|
not_in_description: List[str],
|
|
|
|
status_code: int = 200,
|
|
|
|
) -> None:
|
2018-12-14 23:28:00 +01:00
|
|
|
response = self.client_get(path)
|
2019-04-24 03:33:40 +02:00
|
|
|
self.assertEqual(response.status_code, status_code)
|
2021-02-12 08:20:45 +01:00
|
|
|
bs = BeautifulSoup(response.content, features="lxml")
|
2022-01-22 07:31:33 +01:00
|
|
|
open_graph_title = assert_is_not_none(bs.select_one('meta[property="og:title"]')).get(
|
|
|
|
"content"
|
|
|
|
)
|
2019-04-24 03:18:00 +02:00
|
|
|
self.assertEqual(open_graph_title, title)
|
2019-02-11 07:03:20 +01:00
|
|
|
|
2022-01-22 07:31:33 +01:00
|
|
|
open_graph_description = assert_is_not_none(
|
|
|
|
bs.select_one('meta[property="og:description"]')
|
|
|
|
).get("content")
|
2022-06-23 16:47:06 +02:00
|
|
|
assert isinstance(open_graph_description, str)
|
2019-02-11 07:03:20 +01:00
|
|
|
for substring in in_description:
|
|
|
|
self.assertIn(substring, open_graph_description)
|
|
|
|
for substring in not_in_description:
|
|
|
|
self.assertNotIn(substring, open_graph_description)
|
2018-12-14 23:28:00 +01:00
|
|
|
|
|
|
|
def test_admonition_and_link(self) -> None:
|
|
|
|
# disable-message-edit-history starts with an {!admin-only.md!}, and has a link
|
|
|
|
# in the first paragraph.
|
|
|
|
self.check_title_and_description(
|
2021-02-12 08:20:45 +01:00
|
|
|
"/help/disable-message-edit-history",
|
2022-09-08 13:36:05 +02:00
|
|
|
"Disable message edit history | Zulip help center",
|
2021-02-12 08:19:30 +01:00
|
|
|
[
|
2022-03-23 14:05:07 +01:00
|
|
|
"In Zulip, users can view the edit history of a message. | To remove the",
|
2021-02-12 08:19:30 +01:00
|
|
|
"best to delete the message entirely. ",
|
|
|
|
],
|
|
|
|
[
|
|
|
|
"Disable message edit history",
|
|
|
|
"feature is only available",
|
|
|
|
"Related articles",
|
|
|
|
"Restrict message editing",
|
|
|
|
],
|
2019-02-11 07:03:20 +01:00
|
|
|
)
|
2018-12-14 23:28:00 +01:00
|
|
|
|
|
|
|
def test_settings_tab(self) -> None:
|
2021-06-28 19:23:45 +02:00
|
|
|
# deactivate-your-account starts with {settings_tab|account-and-privacy}
|
2018-12-14 23:28:00 +01:00
|
|
|
self.check_title_and_description(
|
2021-02-12 08:20:45 +01:00
|
|
|
"/help/deactivate-your-account",
|
2022-09-08 13:36:05 +02:00
|
|
|
"Deactivate your account | Zulip help center",
|
2023-03-15 19:20:18 +01:00
|
|
|
[
|
|
|
|
"Deactivating your Zulip account in one organization will have no effect "
|
|
|
|
"on any other Zulip accounts you may have. | Once you deactivate your account"
|
|
|
|
],
|
|
|
|
["Approve by clicking", " ", "\n"],
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2018-12-14 23:28:00 +01:00
|
|
|
|
|
|
|
def test_tabs(self) -> None:
|
|
|
|
# logging-out starts with {start_tabs}
|
|
|
|
self.check_title_and_description(
|
2021-02-12 08:20:45 +01:00
|
|
|
"/help/logging-out",
|
2022-09-08 13:36:05 +02:00
|
|
|
"Logging out | Zulip help center",
|
2018-12-14 23:28:00 +01:00
|
|
|
# Ideally we'd do something better here
|
2021-02-12 08:19:30 +01:00
|
|
|
[
|
2023-01-03 02:16:53 +01:00
|
|
|
"Your feedback helps us make Zulip better for everyone! Please contact us with"
|
|
|
|
" questions, suggestions, and feature requests."
|
2021-02-12 08:19:30 +01:00
|
|
|
],
|
|
|
|
["Click on the gear"],
|
|
|
|
)
|
2018-12-14 23:28:00 +01:00
|
|
|
|
|
|
|
def test_index_pages(self) -> None:
|
|
|
|
self.check_title_and_description(
|
2021-02-12 08:20:45 +01:00
|
|
|
"/help/",
|
2022-09-08 13:36:05 +02:00
|
|
|
"Zulip help center",
|
2021-12-01 19:47:44 +01:00
|
|
|
[("Welcome to the Zulip")],
|
2021-02-12 08:19:30 +01:00
|
|
|
[],
|
|
|
|
)
|
2018-12-14 23:28:00 +01:00
|
|
|
|
|
|
|
self.check_title_and_description(
|
2021-02-12 08:20:45 +01:00
|
|
|
"/api/",
|
2020-10-23 02:43:28 +02:00
|
|
|
"Zulip API documentation",
|
2021-02-12 08:19:30 +01:00
|
|
|
[
|
|
|
|
(
|
|
|
|
"Zulip's APIs allow you to integrate other services with Zulip. This "
|
|
|
|
"guide should help you find the API you need:"
|
|
|
|
)
|
|
|
|
],
|
|
|
|
[],
|
|
|
|
)
|
2018-12-14 23:28:00 +01:00
|
|
|
|
|
|
|
def test_nonexistent_page(self) -> None:
|
2019-04-24 03:33:40 +02:00
|
|
|
self.check_title_and_description(
|
2021-02-12 08:20:45 +01:00
|
|
|
"/help/not-a-real-page",
|
2018-12-14 23:28:00 +01:00
|
|
|
# Probably we should make this "Zulip Help Center"
|
2022-09-08 13:36:05 +02:00
|
|
|
"No such article. | Zulip help center",
|
2021-02-12 08:19:30 +01:00
|
|
|
[
|
2022-09-27 13:18:33 +02:00
|
|
|
"No such article.",
|
|
|
|
"Your feedback helps us make Zulip better for everyone! Please contact us",
|
2021-02-12 08:19:30 +01:00
|
|
|
],
|
2019-04-24 03:33:40 +02:00
|
|
|
[],
|
|
|
|
# Test that our open graph logic doesn't throw a 500
|
2021-02-12 08:19:30 +01:00
|
|
|
404,
|
|
|
|
)
|
2019-04-24 04:30:15 +02:00
|
|
|
|
|
|
|
def test_login_page_simple_description(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
name = "Zulip Dev"
|
2021-02-12 08:19:30 +01:00
|
|
|
description = (
|
|
|
|
"The Zulip development environment default organization. It's great for testing!"
|
|
|
|
)
|
2019-04-24 04:30:15 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
self.check_title_and_description("/login/", name, [description], [])
|
2019-04-24 04:10:56 +02:00
|
|
|
|
|
|
|
def test_login_page_markdown_description(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
realm = get_realm("zulip")
|
2021-02-12 08:19:30 +01:00
|
|
|
description = (
|
|
|
|
"Welcome to **Clojurians Zulip** - the place where the Clojure community meets.\n\n"
|
|
|
|
"Before you signup/login:\n\n"
|
|
|
|
"* note-1\n"
|
|
|
|
"* note-2\n"
|
|
|
|
"* note-3\n\n"
|
|
|
|
"Enjoy!"
|
|
|
|
)
|
2019-04-24 04:10:56 +02:00
|
|
|
realm.description = description
|
2021-02-12 08:20:45 +01:00
|
|
|
realm.save(update_fields=["description"])
|
2019-04-24 04:10:56 +02:00
|
|
|
|
|
|
|
self.check_title_and_description(
|
2021-02-12 08:20:45 +01:00
|
|
|
"/login/",
|
|
|
|
"Zulip Dev",
|
2021-02-12 08:19:30 +01:00
|
|
|
[
|
2021-02-12 08:20:45 +01:00
|
|
|
"Welcome to Clojurians Zulip - the place where the Clojure community meets",
|
|
|
|
"* note-1 * note-2 * note-3 | Enjoy!",
|
2021-02-12 08:19:30 +01:00
|
|
|
],
|
|
|
|
[],
|
|
|
|
)
|
2019-04-26 15:02:16 +02:00
|
|
|
|
|
|
|
def test_login_page_realm_icon(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
realm = get_realm("zulip")
|
|
|
|
realm.icon_source = "U"
|
|
|
|
realm.save(update_fields=["icon_source"])
|
2019-04-26 15:02:16 +02:00
|
|
|
realm_icon = get_realm_icon_url(realm)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
response = self.client_get("/login/")
|
2019-04-26 15:02:16 +02:00
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
bs = BeautifulSoup(response.content, features="lxml")
|
2022-01-22 07:31:33 +01:00
|
|
|
open_graph_image = assert_is_not_none(bs.select_one('meta[property="og:image"]')).get(
|
|
|
|
"content"
|
|
|
|
)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(open_graph_image, f"{realm.uri}{realm_icon}")
|
2019-05-16 14:07:12 +02:00
|
|
|
|
|
|
|
def test_login_page_realm_icon_absolute_url(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
realm = get_realm("zulip")
|
|
|
|
realm.icon_source = "U"
|
|
|
|
realm.save(update_fields=["icon_source"])
|
2020-06-10 06:41:04 +02:00
|
|
|
icon_url = f"https://foo.s3.amazonaws.com/{realm.id}/realm/icon.png?version={1}"
|
2021-02-12 08:19:30 +01:00
|
|
|
with patch(
|
2021-02-12 08:20:45 +01:00
|
|
|
"zerver.lib.realm_icon.upload_backend.get_realm_icon_url", return_value=icon_url
|
2021-02-12 08:19:30 +01:00
|
|
|
):
|
2021-02-12 08:20:45 +01:00
|
|
|
response = self.client_get("/login/")
|
2019-05-16 14:07:12 +02:00
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
bs = BeautifulSoup(response.content, features="lxml")
|
2022-01-22 07:31:33 +01:00
|
|
|
open_graph_image = assert_is_not_none(bs.select_one('meta[property="og:image"]')).get(
|
|
|
|
"content"
|
|
|
|
)
|
2019-05-16 14:07:12 +02:00
|
|
|
self.assertEqual(open_graph_image, icon_url)
|
2019-04-29 14:02:16 +02:00
|
|
|
|
|
|
|
def test_no_realm_api_page_og_url(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
response = self.client_get("/api/", subdomain="")
|
2019-04-29 14:02:16 +02:00
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
bs = BeautifulSoup(response.content, features="lxml")
|
2022-01-22 07:31:33 +01:00
|
|
|
open_graph_url = assert_is_not_none(bs.select_one('meta[property="og:url"]')).get("content")
|
2019-04-29 14:02:16 +02:00
|
|
|
|
2022-01-22 07:31:33 +01:00
|
|
|
assert isinstance(open_graph_url, str)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertTrue(open_graph_url.endswith("/api/"))
|
2022-07-14 17:24:04 +02:00
|
|
|
|
|
|
|
|
|
|
|
class LogRequestsTest(ZulipTestCase):
|
|
|
|
meta_data = {"REMOTE_ADDR": "127.0.0.1"}
|
|
|
|
|
|
|
|
def test_requestor_for_logs_as_user(self) -> None:
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
request = HostRequestMock(user_profile=hamlet, meta_data=self.meta_data)
|
|
|
|
RequestNotes.get_notes(request).log_data = None
|
|
|
|
|
|
|
|
with self.assertLogs("zulip.requests", level="INFO") as m:
|
|
|
|
LogRequests(lambda _: HttpResponse())(request)
|
|
|
|
self.assertIn(hamlet.format_requestor_for_logs(), m.output[0])
|
|
|
|
|
|
|
|
def test_requestor_for_logs_as_remote_server(self) -> None:
|
|
|
|
remote_server = RemoteZulipServer()
|
2022-06-12 21:33:20 +02:00
|
|
|
request = HostRequestMock(remote_server=remote_server, meta_data=self.meta_data)
|
2022-07-14 17:24:04 +02:00
|
|
|
RequestNotes.get_notes(request).log_data = None
|
|
|
|
|
|
|
|
with self.assertLogs("zulip.requests", level="INFO") as m:
|
|
|
|
LogRequests(lambda _: HttpResponse())(request)
|
|
|
|
self.assertIn(remote_server.format_requestor_for_logs(), m.output[0])
|
|
|
|
|
|
|
|
def test_requestor_for_logs_unauthenticated(self) -> None:
|
|
|
|
request = HostRequestMock(meta_data=self.meta_data)
|
|
|
|
RequestNotes.get_notes(request).log_data = None
|
|
|
|
|
|
|
|
expected_requestor = "unauth@root"
|
|
|
|
with self.assertLogs("zulip.requests", level="INFO") as m:
|
|
|
|
LogRequests(lambda _: HttpResponse())(request)
|
|
|
|
self.assertIn(expected_requestor, m.output[0])
|