# -*- coding: utf-8 -*- from __future__ import absolute_import from __future__ import print_function from typing import Any, Callable, Dict, Iterable, List, Tuple from django.test import override_settings from zerver.lib.test_classes import ( ZulipTestCase, ) from zerver.lib.utils import statsd import mock import ujson def fix_params(raw_params): # type: (Dict[str, Any]) -> Dict[str, str] # A few of our few legacy endpoints need their # individual parameters serialized as JSON. return {k: ujson.dumps(v) for k, v in raw_params.items()} class StatsMock(object): def __init__(self, settings): # type: (Callable) -> None self.settings = settings self.real_impl = statsd self.func_calls = [] # type: List[Tuple[str, Iterable[Any]]] def __getattr__(self, name): # type: (str) -> Callable def f(*args): # type: (*Any) -> None with self.settings(STATSD_HOST=''): getattr(self.real_impl, name)(*args) self.func_calls.append((name, args)) return f class TestReport(ZulipTestCase): def test_send_time(self): # type: () -> None email = 'hamlet@zulip.com' self.login(email) params = dict( time=5, received=6, displayed=7, locally_echoed='true', rendered_content_disparity='true', ) stats_mock = StatsMock(self.settings) with mock.patch('zerver.views.report.statsd', wraps=stats_mock): result = self.client_post("/json/report_send_time", params) self.assert_json_success(result) expected_calls = [ ('timing', ('endtoend.send_time.zulip_com', 5)), ('timing', ('endtoend.receive_time.zulip_com', 6)), ('timing', ('endtoend.displayed_time.zulip_com', 7)), ('incr', ('locally_echoed',)), ('incr', ('render_disparity',)), ] self.assertEqual(stats_mock.func_calls, expected_calls) def test_narrow_time(self): # type: () -> None email = 'hamlet@zulip.com' self.login(email) params = dict( initial_core=5, initial_free=6, network=7, ) stats_mock = StatsMock(self.settings) with mock.patch('zerver.views.report.statsd', wraps=stats_mock): result = self.client_post("/json/report_narrow_time", params) self.assert_json_success(result) expected_calls = [ ('timing', ('narrow.initial_core.zulip_com', 5)), ('timing', ('narrow.initial_free.zulip_com', 6)), ('timing', ('narrow.network.zulip_com', 7)), ] self.assertEqual(stats_mock.func_calls, expected_calls) def test_unnarrow_time(self): # type: () -> None email = 'hamlet@zulip.com' self.login(email) params = dict( initial_core=5, initial_free=6, ) stats_mock = StatsMock(self.settings) with mock.patch('zerver.views.report.statsd', wraps=stats_mock): result = self.client_post("/json/report_unnarrow_time", params) self.assert_json_success(result) expected_calls = [ ('timing', ('unnarrow.initial_core.zulip_com', 5)), ('timing', ('unnarrow.initial_free.zulip_com', 6)), ] self.assertEqual(stats_mock.func_calls, expected_calls) @override_settings(BROWSER_ERROR_REPORTING=True) def test_report_error(self): # type: () -> None email = 'hamlet@zulip.com' self.login(email) params = fix_params(dict( message='hello', stacktrace='trace', ui_message=True, user_agent='agent', href='href', log='log', more_info=dict(foo='bar'), )) publish_mock = mock.patch('zerver.views.report.queue_json_publish') subprocess_mock = mock.patch( 'zerver.views.report.subprocess.check_output', side_effect=KeyError('foo') ) with publish_mock as m, subprocess_mock: result = self.client_post("/json/report_error", params) self.assert_json_success(result) report = m.call_args[0][1]['report'] for k in set(params) - set(['ui_message', 'more_info']): self.assertEqual(report[k], params[k]) self.assertEqual(report['more_info'], dict(foo='bar')) self.assertEqual(report['user_email'], email) with self.settings(BROWSER_ERROR_REPORTING=False): result = self.client_post("/json/report_error", params) self.assert_json_success(result) # If js_source_map is present, then the stack trace should be annotated. # DEVELOPMENT=False and TEST_SUITE=False are necessary to ensure that # js_source_map actually gets instantiated. with \ self.settings(DEVELOPMENT=False, TEST_SUITE=False), \ mock.patch('zerver.lib.unminify.SourceMap.annotate_stacktrace') as annotate: result = self.client_post("/json/report_error", params) self.assert_json_success(result) # fix_params (see above) adds quotes when JSON encoding. annotate.assert_called_once_with('"trace"')