2017-07-22 12:49:20 +02:00
|
|
|
import json
|
2024-07-12 02:30:17 +02:00
|
|
|
from typing import Any
|
2020-06-11 00:54:34 +02:00
|
|
|
from unittest import mock
|
|
|
|
|
2018-10-10 15:11:50 +02:00
|
|
|
import requests
|
2023-10-12 19:43:45 +02:00
|
|
|
from typing_extensions import override
|
2017-07-22 12:49:20 +02:00
|
|
|
|
2020-03-28 11:05:26 +01:00
|
|
|
from zerver.lib.avatar import get_gravatar_url
|
2021-04-25 21:23:52 +02:00
|
|
|
from zerver.lib.exceptions import JsonableError
|
2023-10-03 03:25:57 +02:00
|
|
|
from zerver.lib.message_cache import MessageDict
|
2020-06-11 00:54:34 +02:00
|
|
|
from zerver.lib.outgoing_webhook import get_service_interface_class, process_success_response
|
2017-07-22 12:49:20 +02:00
|
|
|
from zerver.lib.test_classes import ZulipTestCase
|
2020-03-28 11:05:26 +01:00
|
|
|
from zerver.lib.timestamp import datetime_to_timestamp
|
2018-11-10 16:43:59 +01:00
|
|
|
from zerver.lib.topic import TOPIC_NAME
|
2023-12-15 21:00:29 +01:00
|
|
|
from zerver.models import Message
|
|
|
|
from zerver.models.bots import SLACK_INTERFACE
|
2023-12-15 02:14:24 +01:00
|
|
|
from zerver.models.realms import get_realm
|
2023-12-15 20:21:59 +01:00
|
|
|
from zerver.models.scheduled_jobs import NotificationTriggers
|
2023-12-15 03:57:04 +01:00
|
|
|
from zerver.models.streams import get_stream
|
2023-12-15 01:16:00 +01:00
|
|
|
from zerver.models.users import get_user
|
2020-08-27 22:54:56 +02:00
|
|
|
from zerver.openapi.openapi import validate_against_openapi_schema
|
2020-03-28 11:05:26 +01:00
|
|
|
|
2017-07-22 12:49:20 +02:00
|
|
|
|
2017-07-24 08:42:22 +02:00
|
|
|
class TestGenericOutgoingWebhookService(ZulipTestCase):
|
2023-10-12 19:43:45 +02:00
|
|
|
@override
|
2017-11-05 10:51:25 +01:00
|
|
|
def setUp(self) -> None:
|
2019-10-19 20:47:00 +02:00
|
|
|
super().setUp()
|
2019-11-05 20:47:50 +01:00
|
|
|
|
2021-04-06 21:24:57 +02:00
|
|
|
self.bot_user = get_user("outgoing-webhook@zulip.com", get_realm("zulip"))
|
2021-02-12 08:20:45 +01:00
|
|
|
service_class = get_service_interface_class("whatever") # GenericOutgoingWebhookService
|
2021-02-12 08:19:30 +01:00
|
|
|
self.handler = service_class(
|
2021-04-06 21:24:57 +02:00
|
|
|
service_name="test-service", token="abcdef", user_profile=self.bot_user
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2017-07-22 12:49:20 +02:00
|
|
|
|
2018-10-10 15:11:50 +02:00
|
|
|
def test_process_success_response(self) -> None:
|
|
|
|
event = dict(
|
|
|
|
user_profile_id=99,
|
2021-02-12 08:20:45 +01:00
|
|
|
message=dict(type="private"),
|
2018-10-10 15:11:50 +02:00
|
|
|
)
|
|
|
|
service_handler = self.handler
|
|
|
|
|
2021-03-27 05:28:33 +01:00
|
|
|
response = mock.Mock(spec=requests.Response)
|
|
|
|
response.status_code = 200
|
|
|
|
response.text = json.dumps(dict(content="whatever"))
|
2018-10-10 15:11:50 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
with mock.patch("zerver.lib.outgoing_webhook.send_response_message") as m:
|
2018-10-10 15:11:50 +02:00
|
|
|
process_success_response(
|
|
|
|
event=event,
|
|
|
|
service_handler=service_handler,
|
|
|
|
response=response,
|
|
|
|
)
|
|
|
|
self.assertTrue(m.called)
|
|
|
|
|
2021-03-27 05:28:33 +01:00
|
|
|
response = mock.Mock(spec=requests.Response)
|
|
|
|
response.status_code = 200
|
|
|
|
response.text = "unparsable text"
|
2018-10-10 15:11:50 +02:00
|
|
|
|
2021-04-25 21:23:52 +02:00
|
|
|
with self.assertRaisesRegex(JsonableError, "Invalid JSON in response"):
|
2018-10-10 15:11:50 +02:00
|
|
|
process_success_response(
|
|
|
|
event=event,
|
|
|
|
service_handler=service_handler,
|
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
|
|
|
response=response,
|
2018-10-10 15:11:50 +02:00
|
|
|
)
|
|
|
|
|
2021-03-27 03:11:40 +01:00
|
|
|
def test_make_request(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
othello = self.example_user("othello")
|
|
|
|
stream = get_stream("Denmark", othello.realm)
|
2020-03-28 11:05:26 +01:00
|
|
|
message_id = self.send_stream_message(
|
|
|
|
othello,
|
|
|
|
stream.name,
|
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
|
|
|
content="@**test**",
|
2020-03-28 11:05:26 +01:00
|
|
|
)
|
|
|
|
|
2020-03-28 01:16:02 +01:00
|
|
|
message = Message.objects.get(id=message_id)
|
2020-03-28 11:05:26 +01:00
|
|
|
|
|
|
|
gravatar_url = get_gravatar_url(
|
|
|
|
othello.delivery_email,
|
|
|
|
othello.avatar_version,
|
|
|
|
)
|
|
|
|
|
|
|
|
expected_message_data = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"avatar_url": gravatar_url,
|
|
|
|
"client": "test suite",
|
|
|
|
"content": "@**test**",
|
|
|
|
"content_type": "text/x-markdown",
|
|
|
|
"display_recipient": "Denmark",
|
|
|
|
"id": message.id,
|
|
|
|
"is_me_message": False,
|
|
|
|
"reactions": [],
|
|
|
|
"recipient_id": message.recipient_id,
|
|
|
|
"rendered_content": "<p>@<strong>test</strong></p>",
|
|
|
|
"sender_email": othello.email,
|
|
|
|
"sender_full_name": "Othello, the Moor of Venice",
|
|
|
|
"sender_id": othello.id,
|
|
|
|
"sender_realm_str": "zulip",
|
|
|
|
"stream_id": stream.id,
|
|
|
|
TOPIC_NAME: "test",
|
|
|
|
"submessages": [],
|
|
|
|
"timestamp": datetime_to_timestamp(message.date_sent),
|
|
|
|
"topic_links": [],
|
|
|
|
"type": "stream",
|
2020-03-28 11:05:26 +01:00
|
|
|
}
|
|
|
|
|
2020-03-28 01:16:02 +01:00
|
|
|
wide_message_dict = MessageDict.wide_dict(message)
|
|
|
|
|
|
|
|
event = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"command": "@**test**",
|
|
|
|
"message": wide_message_dict,
|
|
|
|
"trigger": "mention",
|
2020-03-28 01:16:02 +01:00
|
|
|
}
|
2020-03-28 11:05:26 +01:00
|
|
|
|
2021-03-27 03:11:40 +01:00
|
|
|
test_url = "https://example.com/example"
|
2021-03-27 04:59:27 +01:00
|
|
|
with mock.patch.object(self.handler, "session") as session:
|
2021-03-27 03:11:40 +01:00
|
|
|
self.handler.make_request(
|
|
|
|
test_url,
|
|
|
|
event,
|
2021-09-17 21:22:23 +02:00
|
|
|
othello.realm,
|
2021-03-27 03:11:40 +01:00
|
|
|
)
|
2021-03-27 04:59:27 +01:00
|
|
|
session.post.assert_called_once()
|
|
|
|
self.assertEqual(session.post.call_args[0], (test_url,))
|
|
|
|
request_data = session.post.call_args[1]["json"]
|
2021-03-27 03:11:40 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
validate_against_openapi_schema(request_data, "/zulip-outgoing-webhook", "post", "200")
|
2021-04-06 21:24:57 +02:00
|
|
|
self.assertEqual(request_data["bot_full_name"], self.bot_user.full_name)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(request_data["data"], "@**test**")
|
|
|
|
self.assertEqual(request_data["token"], "abcdef")
|
|
|
|
self.assertEqual(request_data["message"], expected_message_data)
|
2017-07-22 12:49:20 +02:00
|
|
|
|
2020-03-26 23:16:23 +01:00
|
|
|
# Make sure we didn't accidentally mutate wide_message_dict.
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(wide_message_dict["sender_realm_id"], othello.realm_id)
|
2020-03-26 23:16:23 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_process_success(self) -> None:
|
2024-07-12 02:30:17 +02:00
|
|
|
response: dict[str, Any] = dict(response_not_required=True)
|
2020-03-27 22:55:51 +01:00
|
|
|
success_response = self.handler.process_success(response)
|
2017-07-22 12:49:20 +02:00
|
|
|
self.assertEqual(success_response, None)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
response = dict(response_string="test_content")
|
2020-03-27 22:55:51 +01:00
|
|
|
success_response = self.handler.process_success(response)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(success_response, dict(content="test_content"))
|
2017-07-22 12:49:20 +02:00
|
|
|
|
2018-10-26 18:08:08 +02:00
|
|
|
response = dict(
|
2021-02-12 08:20:45 +01:00
|
|
|
content="test_content",
|
|
|
|
widget_content="test_widget_content",
|
|
|
|
red_herring="whatever",
|
2018-10-26 18:08:08 +02:00
|
|
|
)
|
2020-03-27 22:55:51 +01:00
|
|
|
success_response = self.handler.process_success(response)
|
2018-10-26 18:08:08 +02:00
|
|
|
expected_response = dict(
|
2021-02-12 08:20:45 +01:00
|
|
|
content="test_content",
|
|
|
|
widget_content="test_widget_content",
|
2018-10-26 18:08:08 +02:00
|
|
|
)
|
|
|
|
self.assertEqual(success_response, expected_response)
|
|
|
|
|
2020-09-02 08:14:51 +02:00
|
|
|
response = {}
|
2020-03-27 22:55:51 +01:00
|
|
|
success_response = self.handler.process_success(response)
|
2017-07-24 09:02:29 +02:00
|
|
|
self.assertEqual(success_response, None)
|
2017-07-22 12:49:20 +02:00
|
|
|
|
2017-07-01 14:42:34 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
class TestSlackOutgoingWebhookService(ZulipTestCase):
|
2023-10-12 19:43:45 +02:00
|
|
|
@override
|
2017-11-05 10:51:25 +01:00
|
|
|
def setUp(self) -> None:
|
2019-10-19 20:47:00 +02:00
|
|
|
super().setUp()
|
2021-09-17 21:22:23 +02:00
|
|
|
self.bot_user = get_user("outgoing-webhook@zulip.com", get_realm("zulip"))
|
2018-06-25 17:27:06 +02:00
|
|
|
self.stream_message_event = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"command": "@**test**",
|
|
|
|
"user_profile_id": 12,
|
|
|
|
"service_name": "test-service",
|
|
|
|
"trigger": "mention",
|
|
|
|
"message": {
|
|
|
|
"content": "test_content",
|
|
|
|
"type": "stream",
|
|
|
|
"sender_realm_str": "zulip",
|
|
|
|
"sender_email": "sampleuser@zulip.com",
|
|
|
|
"stream_id": "123",
|
|
|
|
"display_recipient": "integrations",
|
|
|
|
"timestamp": 123456,
|
|
|
|
"sender_id": 21,
|
|
|
|
"sender_full_name": "Sample User",
|
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
|
|
|
},
|
2017-07-01 14:42:34 +02:00
|
|
|
}
|
2018-06-24 08:51:47 +02:00
|
|
|
|
|
|
|
self.private_message_event = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"user_profile_id": 24,
|
|
|
|
"service_name": "test-service",
|
|
|
|
"command": "test content",
|
2023-08-04 19:54:41 +02:00
|
|
|
"trigger": NotificationTriggers.DIRECT_MESSAGE,
|
2021-02-12 08:20:45 +01:00
|
|
|
"message": {
|
|
|
|
"sender_id": 3,
|
|
|
|
"sender_realm_str": "zulip",
|
|
|
|
"timestamp": 1529821610,
|
|
|
|
"sender_email": "cordelia@zulip.com",
|
|
|
|
"type": "private",
|
|
|
|
"sender_realm_id": 1,
|
|
|
|
"id": 219,
|
|
|
|
TOPIC_NAME: "test",
|
|
|
|
"content": "test content",
|
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
|
|
|
},
|
2018-06-24 08:51:47 +02:00
|
|
|
}
|
|
|
|
|
2018-10-11 19:07:09 +02:00
|
|
|
service_class = get_service_interface_class(SLACK_INTERFACE)
|
2021-09-17 21:22:23 +02:00
|
|
|
self.handler = service_class(
|
|
|
|
token="abcdef", user_profile=self.bot_user, service_name="test-service"
|
|
|
|
)
|
2017-07-01 14:42:34 +02:00
|
|
|
|
2021-03-27 03:11:40 +01:00
|
|
|
def test_make_request_stream_message(self) -> None:
|
|
|
|
test_url = "https://example.com/example"
|
2021-03-27 04:59:27 +01:00
|
|
|
with mock.patch.object(self.handler, "session") as session:
|
2021-03-27 03:11:40 +01:00
|
|
|
self.handler.make_request(
|
|
|
|
test_url,
|
|
|
|
self.stream_message_event,
|
2021-09-17 21:22:23 +02:00
|
|
|
self.bot_user.realm,
|
2021-03-27 03:11:40 +01:00
|
|
|
)
|
2021-03-27 04:59:27 +01:00
|
|
|
session.post.assert_called_once()
|
|
|
|
self.assertEqual(session.post.call_args[0], (test_url,))
|
|
|
|
request_data = session.post.call_args[1]["data"]
|
2017-07-01 14:42:34 +02:00
|
|
|
|
|
|
|
self.assertEqual(request_data[0][1], "abcdef") # token
|
2021-09-17 21:22:23 +02:00
|
|
|
self.assertEqual(request_data[1][1], "T2") # team_id
|
|
|
|
self.assertEqual(request_data[2][1], "zulip.testserver") # team_domain
|
|
|
|
self.assertEqual(request_data[3][1], "C123") # channel_id
|
2017-07-01 14:42:34 +02:00
|
|
|
self.assertEqual(request_data[4][1], "integrations") # channel_name
|
2021-09-17 21:22:23 +02:00
|
|
|
self.assertEqual(request_data[5][1], 123456) # thread_id
|
|
|
|
self.assertEqual(request_data[6][1], 123456) # timestamp
|
|
|
|
self.assertEqual(request_data[7][1], "U21") # user_id
|
|
|
|
self.assertEqual(request_data[8][1], "Sample User") # user_name
|
|
|
|
self.assertEqual(request_data[9][1], "@**test**") # text
|
|
|
|
self.assertEqual(request_data[10][1], "mention") # trigger_word
|
|
|
|
self.assertEqual(request_data[11][1], 12) # user_profile_id
|
2017-07-01 14:42:34 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
@mock.patch("zerver.lib.outgoing_webhook.fail_with_message")
|
2021-03-27 03:11:40 +01:00
|
|
|
def test_make_request_private_message(self, mock_fail_with_message: mock.Mock) -> None:
|
|
|
|
test_url = "https://example.com/example"
|
2021-03-27 04:59:27 +01:00
|
|
|
with mock.patch.object(self.handler, "session") as session:
|
2021-03-27 03:11:40 +01:00
|
|
|
response = self.handler.make_request(
|
|
|
|
test_url,
|
|
|
|
self.private_message_event,
|
2021-09-17 21:22:23 +02:00
|
|
|
self.bot_user.realm,
|
2021-03-27 03:11:40 +01:00
|
|
|
)
|
2021-03-27 04:59:27 +01:00
|
|
|
session.post.assert_not_called()
|
2021-03-27 03:11:40 +01:00
|
|
|
self.assertIsNone(response)
|
2018-06-24 08:51:47 +02:00
|
|
|
self.assertTrue(mock_fail_with_message.called)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_process_success(self) -> None:
|
2024-07-12 02:30:17 +02:00
|
|
|
response: dict[str, Any] = dict(response_not_required=True)
|
2020-03-27 22:55:51 +01:00
|
|
|
success_response = self.handler.process_success(response)
|
2017-07-24 09:02:29 +02:00
|
|
|
self.assertEqual(success_response, None)
|
2017-07-01 14:42:34 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
response = dict(text="test_content")
|
2020-03-27 22:55:51 +01:00
|
|
|
success_response = self.handler.process_success(response)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(success_response, dict(content="test_content"))
|