mirror of
				https://github.com/zulip/zulip.git
				synced 2025-10-31 12:03:46 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			342 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			342 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| from unittest.mock import MagicMock, patch
 | |
| 
 | |
| import orjson
 | |
| from django.core.exceptions import ValidationError
 | |
| 
 | |
| from zerver.lib.test_classes import ZulipTestCase
 | |
| from zerver.models import Message, Stream
 | |
| from zerver.models.realms import get_realm
 | |
| from zerver.models.users import get_user
 | |
| 
 | |
| 
 | |
| class TestIntegrationsDevPanel(ZulipTestCase):
 | |
|     zulip_realm = get_realm("zulip")
 | |
| 
 | |
|     def test_check_send_webhook_fixture_message_for_error(self) -> None:
 | |
|         bot = get_user("webhook-bot@zulip.com", self.zulip_realm)
 | |
|         url = f"/api/v1/external/airbrake?api_key={bot.api_key}"
 | |
|         target_url = "/devtools/integrations/check_send_webhook_fixture_message"
 | |
|         body = "{}"  # This empty body should generate a ValidationError on the webhook code side.
 | |
| 
 | |
|         data = {
 | |
|             "url": url,
 | |
|             "body": body,
 | |
|             "custom_headers": "{}",
 | |
|             "is_json": "true",
 | |
|         }
 | |
|         with (
 | |
|             self.assertLogs(level="ERROR") as logs,
 | |
|             self.settings(TEST_SUITE=False),
 | |
|             self.assertRaises(ValidationError),
 | |
|         ):
 | |
|             self.client_post(target_url, data)
 | |
| 
 | |
|         # Intention of this test looks like to trigger ValidationError
 | |
|         # so just testing ValidationError is printed along with Traceback in logs
 | |
|         self.assert_length(logs.output, 2)
 | |
|         self.assertTrue(
 | |
|             logs.output[0].startswith(
 | |
|                 "ERROR:django.request:Internal Server Error: /api/v1/external/airbrake\n"
 | |
|                 "Traceback (most recent call last):\n"
 | |
|             )
 | |
|         )
 | |
|         self.assertTrue("ValidationError" in logs.output[0])
 | |
|         self.assertTrue(
 | |
|             logs.output[1].startswith(
 | |
|                 "ERROR:django.request:Internal Server Error: /devtools/integrations/check_send_webhook_fixture_message\n"
 | |
|                 "Traceback (most recent call last):\n"
 | |
|             )
 | |
|         )
 | |
|         self.assertTrue("ValidationError" in logs.output[1])
 | |
| 
 | |
|     def test_check_send_webhook_fixture_message_for_success_without_headers(self) -> None:
 | |
|         bot = get_user("webhook-bot@zulip.com", self.zulip_realm)
 | |
|         url = f"/api/v1/external/airbrake?api_key={bot.api_key}&stream=Denmark&topic=Airbrake notifications"
 | |
|         target_url = "/devtools/integrations/check_send_webhook_fixture_message"
 | |
|         with open("zerver/webhooks/airbrake/fixtures/error_message.json") as f:
 | |
|             body = f.read()
 | |
| 
 | |
|         data = {
 | |
|             "url": url,
 | |
|             "body": body,
 | |
|             "custom_headers": "{}",
 | |
|             "is_json": "true",
 | |
|         }
 | |
| 
 | |
|         response = self.client_post(target_url, data)
 | |
|         expected_response = {
 | |
|             "responses": [{"status_code": 200, "message": {"result": "success", "msg": ""}}],
 | |
|             "result": "success",
 | |
|             "msg": "",
 | |
|         }
 | |
|         response_content = orjson.loads(response.content)
 | |
|         response_content["responses"][0]["message"] = orjson.loads(
 | |
|             response_content["responses"][0]["message"]
 | |
|         )
 | |
|         self.assertEqual(response.status_code, 200)
 | |
|         self.assertEqual(response_content, expected_response)
 | |
| 
 | |
|         latest_msg = Message.objects.latest("id")
 | |
|         expected_message = '[ZeroDivisionError](https://zulip.airbrake.io/projects/125209/groups/1705190192091077626): "Error message from logger" occurred.'
 | |
|         self.assertEqual(latest_msg.content, expected_message)
 | |
|         self.assertEqual(Stream.objects.get(id=latest_msg.recipient.type_id).name, "Denmark")
 | |
|         self.assertEqual(latest_msg.topic_name(), "Airbrake notifications")
 | |
| 
 | |
|     def test_check_send_webhook_fixture_message_for_success_with_headers(self) -> None:
 | |
|         bot = get_user("webhook-bot@zulip.com", self.zulip_realm)
 | |
|         url = f"/api/v1/external/github?api_key={bot.api_key}&stream=Denmark&topic=GitHub notifications"
 | |
|         target_url = "/devtools/integrations/check_send_webhook_fixture_message"
 | |
|         with open("zerver/webhooks/github/fixtures/ping__organization.json") as f:
 | |
|             body = f.read()
 | |
| 
 | |
|         data = {
 | |
|             "url": url,
 | |
|             "body": body,
 | |
|             "custom_headers": orjson.dumps({"X-GitHub-Event": "ping"}).decode(),
 | |
|             "is_json": "true",
 | |
|         }
 | |
| 
 | |
|         response = self.client_post(target_url, data)
 | |
|         self.assertEqual(response.status_code, 200)
 | |
| 
 | |
|         latest_msg = Message.objects.latest("id")
 | |
|         expected_message = "GitHub webhook has been successfully configured by eeshangarg."
 | |
|         self.assertEqual(latest_msg.content, expected_message)
 | |
|         self.assertEqual(Stream.objects.get(id=latest_msg.recipient.type_id).name, "Denmark")
 | |
|         self.assertEqual(latest_msg.topic_name(), "GitHub notifications")
 | |
| 
 | |
|     def test_check_send_webhook_fixture_message_for_success_with_headers_and_non_json_fixtures(
 | |
|         self,
 | |
|     ) -> None:
 | |
|         bot = get_user("webhook-bot@zulip.com", self.zulip_realm)
 | |
|         url = f"/api/v1/external/wordpress?api_key={bot.api_key}&stream=Denmark&topic=WordPress notifications"
 | |
|         target_url = "/devtools/integrations/check_send_webhook_fixture_message"
 | |
|         with open("zerver/webhooks/wordpress/fixtures/publish_post_no_data_provided.txt") as f:
 | |
|             body = f.read()
 | |
| 
 | |
|         data = {
 | |
|             "url": url,
 | |
|             "body": body,
 | |
|             "custom_headers": orjson.dumps(
 | |
|                 {"Content-Type": "application/x-www-form-urlencoded"}
 | |
|             ).decode(),
 | |
|             "is_json": "false",
 | |
|         }
 | |
| 
 | |
|         response = self.client_post(target_url, data)
 | |
|         self.assertEqual(response.status_code, 200)
 | |
| 
 | |
|         latest_msg = Message.objects.latest("id")
 | |
|         expected_message = "New post published:\n* [New WordPress post](WordPress post URL)"
 | |
|         self.assertEqual(latest_msg.content, expected_message)
 | |
|         self.assertEqual(Stream.objects.get(id=latest_msg.recipient.type_id).name, "Denmark")
 | |
|         self.assertEqual(latest_msg.topic_name(), "WordPress notifications")
 | |
| 
 | |
|     def test_get_fixtures_for_nonexistent_integration(self) -> None:
 | |
|         target_url = "/devtools/integrations/somerandomnonexistentintegration/fixtures"
 | |
|         response = self.client_get(target_url)
 | |
|         expected_response = {
 | |
|             "code": "BAD_REQUEST",
 | |
|             "msg": '"somerandomnonexistentintegration" is not a valid webhook integration.',
 | |
|             "result": "error",
 | |
|         }
 | |
|         self.assertEqual(response.status_code, 404)
 | |
|         self.assertEqual(orjson.loads(response.content), expected_response)
 | |
| 
 | |
|     @patch("zerver.views.development.integrations.os.path.exists")
 | |
|     def test_get_fixtures_for_integration_without_fixtures(
 | |
|         self, os_path_exists_mock: MagicMock
 | |
|     ) -> None:
 | |
|         os_path_exists_mock.return_value = False
 | |
|         target_url = "/devtools/integrations/airbrake/fixtures"
 | |
|         response = self.client_get(target_url)
 | |
|         expected_response = {
 | |
|             "code": "BAD_REQUEST",
 | |
|             "msg": 'The integration "airbrake" does not have fixtures.',
 | |
|             "result": "error",
 | |
|         }
 | |
|         self.assertEqual(response.status_code, 404)
 | |
|         self.assertEqual(orjson.loads(response.content), expected_response)
 | |
| 
 | |
|     def test_get_fixtures_for_success(self) -> None:
 | |
|         target_url = "/devtools/integrations/airbrake/fixtures"
 | |
|         response = self.client_get(target_url)
 | |
|         self.assertEqual(response.status_code, 200)
 | |
|         self.assertIsNotNone(orjson.loads(response.content)["fixtures"])
 | |
| 
 | |
|     def test_get_dev_panel_page(self) -> None:
 | |
|         # Just to satisfy the test suite.
 | |
|         target_url = "/devtools/integrations/"
 | |
|         response = self.client_get(target_url)
 | |
|         self.assertEqual(response.status_code, 200)
 | |
| 
 | |
|     def test_send_all_webhook_fixture_messages_for_success(self) -> None:
 | |
|         bot = get_user("webhook-bot@zulip.com", self.zulip_realm)
 | |
|         url = f"/api/v1/external/appfollow?api_key={bot.api_key}&stream=Denmark&topic=Appfollow bulk notifications"
 | |
|         target_url = "/devtools/integrations/send_all_webhook_fixture_messages"
 | |
| 
 | |
|         data = {
 | |
|             "url": url,
 | |
|             "custom_headers": "{}",
 | |
|             "integration_name": "appfollow",
 | |
|         }
 | |
| 
 | |
|         response = self.client_post(target_url, data)
 | |
|         expected_responses = [
 | |
|             {
 | |
|                 "fixture_name": "sample.json",
 | |
|                 "status_code": 200,
 | |
|                 "message": {"msg": "", "result": "success"},
 | |
|             },
 | |
|             {
 | |
|                 "fixture_name": "review.json",
 | |
|                 "status_code": 200,
 | |
|                 "message": {"msg": "", "result": "success"},
 | |
|             },
 | |
|         ]
 | |
|         responses = orjson.loads(response.content)["responses"]
 | |
|         for r in responses:
 | |
|             r["message"] = orjson.loads(r["message"])
 | |
|         self.assertEqual(response.status_code, 200)
 | |
|         for r in responses:
 | |
|             # We have to use this roundabout manner since the order may vary each time.
 | |
|             # This is not an issue.
 | |
|             self.assertTrue(r in expected_responses)
 | |
|             expected_responses.remove(r)
 | |
| 
 | |
|         new_messages = Message.objects.order_by("-id")[0:2]
 | |
|         expected_messages = [
 | |
|             "Webhook integration was successful.\nTest User / Acme (Google Play)",
 | |
|             "Acme - Group chat\nApp Store, Acme Technologies, Inc.\n★★★★★ United States\n**Great for Information Management**\nAcme enables me to manage the flow of information quite well. I only wish I could create and edit my Acme Post files in the iOS app.\n*by* **Mr RESOLUTIONARY** *for v3.9*\n[Permalink](http://appfollow.io/permalink) · [Add tag](http://watch.appfollow.io/add_tag)",
 | |
|         ]
 | |
|         for msg in new_messages:
 | |
|             # new_messages -> expected_messages or expected_messages -> new_messages shouldn't make
 | |
|             # a difference since equality is commutative.
 | |
|             self.assertTrue(msg.content in expected_messages)
 | |
|             expected_messages.remove(msg.content)
 | |
|             self.assertEqual(Stream.objects.get(id=msg.recipient.type_id).name, "Denmark")
 | |
|             self.assertEqual(msg.topic_name(), "Appfollow bulk notifications")
 | |
| 
 | |
|     def test_send_all_webhook_fixture_messages_for_success_with_non_json_fixtures(self) -> None:
 | |
|         bot = get_user("webhook-bot@zulip.com", self.zulip_realm)
 | |
|         url = f"/api/v1/external/wordpress?api_key={bot.api_key}&stream=Denmark&topic=WordPress bulk notifications"
 | |
|         target_url = "/devtools/integrations/send_all_webhook_fixture_messages"
 | |
| 
 | |
|         data = {
 | |
|             "url": url,
 | |
|             "custom_headers": "{}",
 | |
|             "integration_name": "wordpress",
 | |
|         }
 | |
| 
 | |
|         response = self.client_post(target_url, data)
 | |
|         expected_responses = [
 | |
|             {
 | |
|                 "message": {
 | |
|                     "msg": "Unknown WordPress webhook action: WordPress action",
 | |
|                     "result": "error",
 | |
|                     "code": "BAD_REQUEST",
 | |
|                 },
 | |
|                 "fixture_name": "user_register.txt",
 | |
|                 "status_code": 400,
 | |
|             },
 | |
|             {
 | |
|                 "message": {
 | |
|                     "msg": "Unknown WordPress webhook action: WordPress action",
 | |
|                     "result": "error",
 | |
|                     "code": "BAD_REQUEST",
 | |
|                 },
 | |
|                 "fixture_name": "publish_post_no_data_provided.txt",
 | |
|                 "status_code": 400,
 | |
|             },
 | |
|             {
 | |
|                 "message": {
 | |
|                     "msg": "Unknown WordPress webhook action: WordPress action",
 | |
|                     "result": "error",
 | |
|                     "code": "BAD_REQUEST",
 | |
|                 },
 | |
|                 "fixture_name": "unknown_action_no_data.txt",
 | |
|                 "status_code": 400,
 | |
|             },
 | |
|             {
 | |
|                 "message": {
 | |
|                     "msg": "Unknown WordPress webhook action: WordPress action",
 | |
|                     "result": "error",
 | |
|                     "code": "BAD_REQUEST",
 | |
|                 },
 | |
|                 "fixture_name": "publish_page.txt",
 | |
|                 "status_code": 400,
 | |
|             },
 | |
|             {
 | |
|                 "message": {
 | |
|                     "msg": "Unknown WordPress webhook action: WordPress action",
 | |
|                     "result": "error",
 | |
|                     "code": "BAD_REQUEST",
 | |
|                 },
 | |
|                 "fixture_name": "unknown_action_no_hook_provided.txt",
 | |
|                 "status_code": 400,
 | |
|             },
 | |
|             {
 | |
|                 "message": {
 | |
|                     "msg": "Unknown WordPress webhook action: WordPress action",
 | |
|                     "result": "error",
 | |
|                     "code": "BAD_REQUEST",
 | |
|                 },
 | |
|                 "fixture_name": "publish_post_type_not_provided.txt",
 | |
|                 "status_code": 400,
 | |
|             },
 | |
|             {
 | |
|                 "message": {
 | |
|                     "msg": "Unknown WordPress webhook action: WordPress action",
 | |
|                     "result": "error",
 | |
|                     "code": "BAD_REQUEST",
 | |
|                 },
 | |
|                 "fixture_name": "wp_login.txt",
 | |
|                 "status_code": 400,
 | |
|             },
 | |
|             {
 | |
|                 "message": {
 | |
|                     "msg": "Unknown WordPress webhook action: WordPress action",
 | |
|                     "result": "error",
 | |
|                     "code": "BAD_REQUEST",
 | |
|                 },
 | |
|                 "fixture_name": "publish_post.txt",
 | |
|                 "status_code": 400,
 | |
|             },
 | |
|         ]
 | |
|         responses = orjson.loads(response.content)["responses"]
 | |
|         for r in responses:
 | |
|             r["message"] = orjson.loads(r["message"])
 | |
|         self.assertEqual(response.status_code, 200)
 | |
|         for r in responses:
 | |
|             # We have to use this roundabout manner since the order may vary each time. This is not
 | |
|             # an issue. Basically, we're trying to compare 2 lists and since we're not resorting to
 | |
|             # using sets or a sorted order, we're sticking with O(n*m) time complexity for this
 | |
|             # comparison (where n and m are the lengths of the two lists respectively). But since
 | |
|             # this is just a unit test and more importantly n = m = some-low-number we don't really
 | |
|             # care about the time complexity being what it is.
 | |
|             self.assertTrue(r in expected_responses)
 | |
|             expected_responses.remove(r)
 | |
| 
 | |
|     @patch("zerver.views.development.integrations.os.path.exists")
 | |
|     def test_send_all_webhook_fixture_messages_for_missing_fixtures(
 | |
|         self, os_path_exists_mock: MagicMock
 | |
|     ) -> None:
 | |
|         os_path_exists_mock.return_value = False
 | |
|         bot = get_user("webhook-bot@zulip.com", self.zulip_realm)
 | |
|         url = f"/api/v1/external/appfollow?api_key={bot.api_key}&stream=Denmark&topic=Appfollow bulk notifications"
 | |
|         data = {
 | |
|             "url": url,
 | |
|             "custom_headers": "{}",
 | |
|             "integration_name": "appfollow",
 | |
|         }
 | |
|         response = self.client_post(
 | |
|             "/devtools/integrations/send_all_webhook_fixture_messages", data
 | |
|         )
 | |
|         expected_response = {
 | |
|             "code": "BAD_REQUEST",
 | |
|             "msg": 'The integration "appfollow" does not have fixtures.',
 | |
|             "result": "error",
 | |
|         }
 | |
|         self.assertEqual(response.status_code, 404)
 | |
|         self.assertEqual(orjson.loads(response.content), expected_response)
 |