mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	zerver/webhooks: Use python 3 syntax for typing.
Tweaked by tabbott to fix various line-wrapping issues.
This commit is contained in:
		@@ -6,8 +6,7 @@ class AirbrakeHookTests(WebhookTestCase):
 | 
			
		||||
    URL_TEMPLATE = u"/api/v1/external/airbrake?stream={stream}&api_key={api_key}"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'airbrake'
 | 
			
		||||
 | 
			
		||||
    def test_airbrake_error_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_airbrake_error_message(self) -> None:
 | 
			
		||||
        expected_subject = u"ZulipIntegrationTest"
 | 
			
		||||
        expected_message = u"[ZeroDivisionError](https://zulip.airbrake.io/projects/125209/groups/1705190192091077626): \"Error message from logger\" occurred."
 | 
			
		||||
        self.send_and_test_stream_message('error_message', expected_subject, expected_message)
 | 
			
		||||
 
 | 
			
		||||
@@ -23,12 +23,10 @@ def api_airbrake_webhook(request, user_profile,
 | 
			
		||||
                              stream, subject, body)
 | 
			
		||||
    return json_success()
 | 
			
		||||
 | 
			
		||||
def get_subject(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> str
 | 
			
		||||
def get_subject(payload: Dict[str, Any]) -> str:
 | 
			
		||||
    return AIRBRAKE_SUBJECT_TEMPLATE.format(project_name=payload['error']['project']['name'])
 | 
			
		||||
 | 
			
		||||
def get_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> str
 | 
			
		||||
def get_body(payload: Dict[str, Any]) -> str:
 | 
			
		||||
    data = {
 | 
			
		||||
        'error_url': payload['airbrake_error_url'],
 | 
			
		||||
        'error_class': payload['error']['error_class'],
 | 
			
		||||
 
 | 
			
		||||
@@ -9,16 +9,14 @@ class AppFollowHookTests(WebhookTestCase):
 | 
			
		||||
    STREAM_NAME = 'appfollow'
 | 
			
		||||
    URL_TEMPLATE = u"/api/v1/external/appfollow?stream={stream}&api_key={api_key}"
 | 
			
		||||
 | 
			
		||||
    def test_sample(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_sample(self) -> None:
 | 
			
		||||
        expected_subject = "Webhook integration was successful."
 | 
			
		||||
        expected_message = u"""Webhook integration was successful.
 | 
			
		||||
Test User / Acme (Google Play)"""
 | 
			
		||||
        self.send_and_test_stream_message('sample', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_reviews(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_reviews(self) -> None:
 | 
			
		||||
        expected_subject = "Acme - Group chat"
 | 
			
		||||
        expected_message = u"""Acme - Group chat
 | 
			
		||||
App Store, Acme Technologies, Inc.
 | 
			
		||||
@@ -30,20 +28,16 @@ Acme enables me to manage the flow of information quite well. I only wish I coul
 | 
			
		||||
        self.send_and_test_stream_message('review', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Text
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Text:
 | 
			
		||||
        return self.fixture_data("appfollow", fixture_name, file_type="json")
 | 
			
		||||
 | 
			
		||||
class ConvertMarkdownTest(TestCase):
 | 
			
		||||
    def test_convert_bold(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_convert_bold(self) -> None:
 | 
			
		||||
        self.assertEqual(convert_markdown("*test message*"), "**test message**")
 | 
			
		||||
 | 
			
		||||
    def test_convert_italics(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_convert_italics(self) -> None:
 | 
			
		||||
        self.assertEqual(convert_markdown("_test message_"), "*test message*")
 | 
			
		||||
        self.assertEqual(convert_markdown("_  spaced message _"), "  *spaced message* ")
 | 
			
		||||
 | 
			
		||||
    def test_convert_strikethrough(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_convert_strikethrough(self) -> None:
 | 
			
		||||
        self.assertEqual(convert_markdown("~test message~"), "~~test message~~")
 | 
			
		||||
 
 | 
			
		||||
@@ -24,8 +24,7 @@ def api_appfollow_webhook(request, user_profile, stream=REQ(default="appfollow")
 | 
			
		||||
                              app_name, convert_markdown(message))
 | 
			
		||||
    return json_success()
 | 
			
		||||
 | 
			
		||||
def convert_markdown(text):
 | 
			
		||||
    # type: (Text) -> Text
 | 
			
		||||
def convert_markdown(text: Text) -> Text:
 | 
			
		||||
    # Converts Slack-style markdown to Zulip format
 | 
			
		||||
    # Implemented mainly for AppFollow messages
 | 
			
		||||
    # Not ready for general use as some edge-cases not handled
 | 
			
		||||
 
 | 
			
		||||
@@ -8,156 +8,125 @@ class BasecampHookTests(WebhookTestCase):
 | 
			
		||||
    FIXTURE_DIR_NAME = 'basecamp'
 | 
			
		||||
    EXPECTED_SUBJECT = "Zulip HQ"
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_doc_active(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_doc_active(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz activated the document [New doc](https://3.basecamp.com/3688623/buckets/2957043/documents/432522214)"
 | 
			
		||||
        self._send_and_test_message('doc_active', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_doc_archived(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_doc_archived(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz archived the document [new doc](https://3.basecamp.com/3688623/buckets/2957043/documents/434455988)"
 | 
			
		||||
        self._send_and_test_message('doc_archived', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_doc_changed_content(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_doc_changed_content(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz changed content of the document [New doc edit](https://3.basecamp.com/3688623/buckets/2957043/documents/432522214)"
 | 
			
		||||
        self._send_and_test_message('doc_content_changed', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_doc_changed_title(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_doc_changed_title(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz changed title of the document [New doc edit](https://3.basecamp.com/3688623/buckets/2957043/documents/432522214)"
 | 
			
		||||
        self._send_and_test_message('doc_title_changed', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_doc_publicized(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_doc_publicized(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz publicized the document [new doc](https://3.basecamp.com/3688623/buckets/2957043/documents/434455988)"
 | 
			
		||||
        self._send_and_test_message('doc_publicized', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_doc_created(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_doc_created(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz created the document [new doc](https://3.basecamp.com/3688623/buckets/2957043/documents/434455988)"
 | 
			
		||||
        self._send_and_test_message('doc_created', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_doc_trashed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_doc_trashed(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz trashed the document [new doc](https://3.basecamp.com/3688623/buckets/2957043/documents/434455988)"
 | 
			
		||||
        self._send_and_test_message('doc_trashed', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_doc_unarchived(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_doc_unarchived(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz unarchived the document [new doc](https://3.basecamp.com/3688623/buckets/2957043/documents/434455988)"
 | 
			
		||||
        self._send_and_test_message('doc_unarchive', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_questions_answer_archived(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_questions_answer_archived(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz archived the [answer](https://3.basecamp.com/3688623/buckets/2957043/questions/432527747/answers/2017-03-16#__recording_432529636) of the question [Question](https://3.basecamp.com/3688623/buckets/2957043/questions/432527747)"
 | 
			
		||||
        self._send_and_test_message('questions_answer_archived', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_questions_answer_content_changed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_questions_answer_content_changed(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz changed content of the [answer](https://3.basecamp.com/3688623/buckets/2957043/questions/432527747/answers/2017-03-16#__recording_432529636) of the question [Question](https://3.basecamp.com/3688623/buckets/2957043/questions/432527747)"
 | 
			
		||||
        self._send_and_test_message('questions_answer_content_changed', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_questions_answer_created(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_questions_answer_created(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz created the [answer](https://3.basecamp.com/3688623/buckets/2957043/questions/432527747/answers/2017-03-16#__recording_432529636) of the question [Question](https://3.basecamp.com/3688623/buckets/2957043/questions/432527747)"
 | 
			
		||||
        self._send_and_test_message('questions_answer_created', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_questions_answer_trashed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_questions_answer_trashed(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz trashed the [answer](https://3.basecamp.com/3688623/buckets/2957043/question_answers/432529636) of the question [Question](https://3.basecamp.com/3688623/buckets/2957043/questions/432527747)"
 | 
			
		||||
        self._send_and_test_message('questions_answer_trashed', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_questions_answer_unarchived(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_questions_answer_unarchived(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz unarchived the [answer](https://3.basecamp.com/3688623/buckets/2957043/questions/432527747/answers/2017-03-16#__recording_432529636) of the question [Question](https://3.basecamp.com/3688623/buckets/2957043/questions/432527747)"
 | 
			
		||||
        self._send_and_test_message('questions_answer_unarchived', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_question_archived(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_question_archived(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz archived the question [Question](https://3.basecamp.com/3688623/buckets/2957043/questions/432527747)"
 | 
			
		||||
        self._send_and_test_message('question_archived', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_question_created(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_question_created(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz created the question [Question](https://3.basecamp.com/3688623/buckets/2957043/questions/432527747)"
 | 
			
		||||
        self._send_and_test_message('question_created', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_question_trashed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_question_trashed(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz trashed the question [Question](https://3.basecamp.com/3688623/buckets/2957043/questions/432527747)"
 | 
			
		||||
        self._send_and_test_message('question_trashed', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_question_unarchived(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_question_unarchived(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz unarchived the question [Question](https://3.basecamp.com/3688623/buckets/2957043/questions/432527747)"
 | 
			
		||||
        self._send_and_test_message('question_unarchived', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_message_archived(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_message_archived(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz archived the message [Message Title new](https://3.basecamp.com/3688623/buckets/2957043/messages/430680605)"
 | 
			
		||||
        self._send_and_test_message('message_archived', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_message_content_change(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_message_content_change(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz changed content of the message [Message Title new](https://3.basecamp.com/3688623/buckets/2957043/messages/430680605)"
 | 
			
		||||
        self._send_and_test_message('message_content_changed', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_message_created(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_message_created(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz created the message [Message Title](https://3.basecamp.com/3688623/buckets/2957043/messages/430680605)"
 | 
			
		||||
        self._send_and_test_message('message_created', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_message_title_change(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_message_title_change(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz changed subject of the message [Message Title new](https://3.basecamp.com/3688623/buckets/2957043/messages/430680605)"
 | 
			
		||||
        self._send_and_test_message('message_title_changed', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_message_trashed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_message_trashed(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz trashed the message [Message Title new](https://3.basecamp.com/3688623/buckets/2957043/messages/430680605)"
 | 
			
		||||
        self._send_and_test_message('message_trashed', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_message_unarchived(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_message_unarchived(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz unarchived the message [Message Title new](https://3.basecamp.com/3688623/buckets/2957043/messages/430680605)"
 | 
			
		||||
        self._send_and_test_message('message_unarchived', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_todo_list_created(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_todo_list_created(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz created the todo list [NEW TO DO LIST](https://3.basecamp.com/3688623/buckets/2957043/todolists/427050190)"
 | 
			
		||||
        self._send_and_test_message('todo_list_created', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_todo_list_description_changed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_todo_list_description_changed(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz changed description of the todo list [NEW TO DO LIST](https://3.basecamp.com/3688623/buckets/2957043/todolists/427050190)"
 | 
			
		||||
        self._send_and_test_message('todo_list_description_changed', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_todo_list_modified(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_todo_list_modified(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz changed name of the todo list [NEW Name TO DO LIST](https://3.basecamp.com/3688623/buckets/2957043/todolists/427050190)"
 | 
			
		||||
        self._send_and_test_message('todo_list_name_changed', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_todo_assignment_changed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_todo_assignment_changed(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz changed assignment of the todo task [New task](https://3.basecamp.com/3688623/buckets/2957043/todos/427055624)"
 | 
			
		||||
        self._send_and_test_message('todo_assignment_changed', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_todo_completed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_todo_completed(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz completed the todo task [New task](https://3.basecamp.com/3688623/buckets/2957043/todos/427055624)"
 | 
			
		||||
        self._send_and_test_message('todo_completed', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_todo_created(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_todo_created(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz created the todo task [New task](https://3.basecamp.com/3688623/buckets/2957043/todos/427055624)"
 | 
			
		||||
        self._send_and_test_message('todo_created', expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_basecamp_makes_comment_created(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_basecamp_makes_comment_created(self) -> None:
 | 
			
		||||
        expected_message = u"Tomasz created the [comment](https://3.basecamp.com/3688623/buckets/2957043/todos/427055624#__recording_427058780) of the task [New task](https://3.basecamp.com/3688623/buckets/2957043/todos/427055624)"
 | 
			
		||||
        self._send_and_test_message('comment_created', expected_message)
 | 
			
		||||
 | 
			
		||||
    def _send_and_test_message(self, fixture_name, expected_message):
 | 
			
		||||
        # type: (Text, Text) -> None
 | 
			
		||||
    def _send_and_test_message(self, fixture_name: Text, expected_message: Text) -> None:
 | 
			
		||||
        self.send_and_test_stream_message(fixture_name, self.EXPECTED_SUBJECT, expected_message)
 | 
			
		||||
 
 | 
			
		||||
@@ -55,28 +55,22 @@ def api_basecamp_webhook(request, user_profile, payload=REQ(argument_type='body'
 | 
			
		||||
                              stream, subject, body)
 | 
			
		||||
    return json_success()
 | 
			
		||||
 | 
			
		||||
def get_project_name(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_project_name(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return payload['recording']['bucket']['name']
 | 
			
		||||
 | 
			
		||||
def get_event_type(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_event_type(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return payload['kind']
 | 
			
		||||
 | 
			
		||||
def get_event_creator(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_event_creator(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return payload['creator']['name']
 | 
			
		||||
 | 
			
		||||
def get_subject_url(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_subject_url(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return payload['recording']['app_url']
 | 
			
		||||
 | 
			
		||||
def get_subject_title(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_subject_title(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return payload['recording']['title']
 | 
			
		||||
 | 
			
		||||
def get_verb(event, prefix):
 | 
			
		||||
    # type: (Text, Text) -> Text
 | 
			
		||||
def get_verb(event: Text, prefix: Text) -> Text:
 | 
			
		||||
    verb = event.replace(prefix, '')
 | 
			
		||||
    if verb == 'active':
 | 
			
		||||
        return 'activated'
 | 
			
		||||
@@ -86,12 +80,10 @@ def get_verb(event, prefix):
 | 
			
		||||
        return "changed {} of".format(matched.group('subject'))
 | 
			
		||||
    return verb
 | 
			
		||||
 | 
			
		||||
def get_document_body(event, payload):
 | 
			
		||||
    # type: (Text, Dict[str, Any]) -> Text
 | 
			
		||||
def get_document_body(event: Text, payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return get_generic_body(event, payload, 'document_', DOCUMENT_TEMPLATE)
 | 
			
		||||
 | 
			
		||||
def get_questions_answer_body(event, payload):
 | 
			
		||||
    # type: (Text, Dict[str, Any]) -> Text
 | 
			
		||||
def get_questions_answer_body(event: Text, payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    verb = get_verb(event, 'question_answer_')
 | 
			
		||||
    question = payload['recording']['parent']
 | 
			
		||||
 | 
			
		||||
@@ -103,8 +95,7 @@ def get_questions_answer_body(event, payload):
 | 
			
		||||
        question_url=question['app_url']
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_comment_body(event, payload):
 | 
			
		||||
    # type: (Text, Dict[str, Any]) -> Text
 | 
			
		||||
def get_comment_body(event: Text, payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    verb = get_verb(event, 'comment_')
 | 
			
		||||
    task = payload['recording']['parent']
 | 
			
		||||
 | 
			
		||||
@@ -116,24 +107,19 @@ def get_comment_body(event, payload):
 | 
			
		||||
        task_url=task['app_url']
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_questions_body(event, payload):
 | 
			
		||||
    # type: (Text, Dict[str, Any]) -> Text
 | 
			
		||||
def get_questions_body(event: Text, payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return get_generic_body(event, payload, 'question_', QUESTION_TEMPLATE)
 | 
			
		||||
 | 
			
		||||
def get_message_body(event, payload):
 | 
			
		||||
    # type: (Text, Dict[str, Any]) -> Text
 | 
			
		||||
def get_message_body(event: Text, payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return get_generic_body(event, payload, 'message_', MESSAGE_TEMPLATE)
 | 
			
		||||
 | 
			
		||||
def get_todo_list_body(event, payload):
 | 
			
		||||
    # type: (Text, Dict[str, Any]) -> Text
 | 
			
		||||
def get_todo_list_body(event: Text, payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return get_generic_body(event, payload, 'todolist_', TODO_LIST_TEMPLATE)
 | 
			
		||||
 | 
			
		||||
def get_todo_body(event, payload):
 | 
			
		||||
    # type: (Text, Dict[str, Any]) -> Text
 | 
			
		||||
def get_todo_body(event: Text, payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return get_generic_body(event, payload, 'todo_', TODO_TEMPLATE)
 | 
			
		||||
 | 
			
		||||
def get_generic_body(event, payload, prefix, template):
 | 
			
		||||
    # type: (Text, Dict[str, Any], Text, Text) -> Text
 | 
			
		||||
def get_generic_body(event: Text, payload: Dict[str, Any], prefix: Text, template: Text) -> Text:
 | 
			
		||||
    verb = get_verb(event, prefix)
 | 
			
		||||
 | 
			
		||||
    return template.format(
 | 
			
		||||
 
 | 
			
		||||
@@ -9,8 +9,7 @@ class BeanstalkHookTests(WebhookTestCase):
 | 
			
		||||
    STREAM_NAME = 'commits'
 | 
			
		||||
    URL_TEMPLATE = u"/api/v1/external/beanstalk"
 | 
			
		||||
 | 
			
		||||
    def test_git_single(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_git_single(self) -> None:
 | 
			
		||||
        expected_subject = "work-test / master"
 | 
			
		||||
        expected_message = """Leo Franchi [pushed](http://lfranchi-svn.beanstalkapp.com/work-test) 1 commit to branch master.
 | 
			
		||||
 | 
			
		||||
@@ -19,8 +18,7 @@ class BeanstalkHookTests(WebhookTestCase):
 | 
			
		||||
                                          content_type=None,
 | 
			
		||||
                                          **self.api_auth(self.TEST_USER_EMAIL))
 | 
			
		||||
 | 
			
		||||
    def test_git_single_filtered_by_branches(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_git_single_filtered_by_branches(self) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url(branches='master,development')
 | 
			
		||||
        expected_subject = "work-test / master"
 | 
			
		||||
        expected_message = """Leo Franchi [pushed](http://lfranchi-svn.beanstalkapp.com/work-test) 1 commit to branch master.
 | 
			
		||||
@@ -30,8 +28,7 @@ class BeanstalkHookTests(WebhookTestCase):
 | 
			
		||||
                                          content_type=None,
 | 
			
		||||
                                          **self.api_auth(self.TEST_USER_EMAIL))
 | 
			
		||||
 | 
			
		||||
    def test_git_multiple_committers(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_git_multiple_committers(self) -> None:
 | 
			
		||||
        expected_subject = "work-test / master"
 | 
			
		||||
        expected_message = """Leo Franchi [pushed](http://lfranchi-svn.beanstalkapp.com/work-test) 3 commits to branch master. Commits by Leo Franchi (2) and Tomasz Kolek (1).
 | 
			
		||||
 | 
			
		||||
@@ -42,8 +39,7 @@ class BeanstalkHookTests(WebhookTestCase):
 | 
			
		||||
                                          content_type=None,
 | 
			
		||||
                                          **self.api_auth(self.TEST_USER_EMAIL))
 | 
			
		||||
 | 
			
		||||
    def test_git_multiple_committers_filtered_by_branches(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_git_multiple_committers_filtered_by_branches(self) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url(branches='master,development')
 | 
			
		||||
        expected_subject = "work-test / master"
 | 
			
		||||
        expected_message = """Leo Franchi [pushed](http://lfranchi-svn.beanstalkapp.com/work-test) 3 commits to branch master. Commits by Leo Franchi (2) and Tomasz Kolek (1).
 | 
			
		||||
@@ -55,8 +51,7 @@ class BeanstalkHookTests(WebhookTestCase):
 | 
			
		||||
                                          content_type=None,
 | 
			
		||||
                                          **self.api_auth(self.TEST_USER_EMAIL))
 | 
			
		||||
 | 
			
		||||
    def test_git_multiple(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_git_multiple(self) -> None:
 | 
			
		||||
        expected_subject = "work-test / master"
 | 
			
		||||
        expected_message = """Leo Franchi [pushed](http://lfranchi-svn.beanstalkapp.com/work-test) 3 commits to branch master.
 | 
			
		||||
 | 
			
		||||
@@ -67,8 +62,7 @@ class BeanstalkHookTests(WebhookTestCase):
 | 
			
		||||
                                          content_type=None,
 | 
			
		||||
                                          **self.api_auth(self.TEST_USER_EMAIL))
 | 
			
		||||
 | 
			
		||||
    def test_git_multiple_filtered_by_branches(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_git_multiple_filtered_by_branches(self) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url(branches='master,development')
 | 
			
		||||
        expected_subject = "work-test / master"
 | 
			
		||||
        expected_message = """Leo Franchi [pushed](http://lfranchi-svn.beanstalkapp.com/work-test) 3 commits to branch master.
 | 
			
		||||
@@ -80,8 +74,7 @@ class BeanstalkHookTests(WebhookTestCase):
 | 
			
		||||
                                          content_type=None,
 | 
			
		||||
                                          **self.api_auth(self.TEST_USER_EMAIL))
 | 
			
		||||
 | 
			
		||||
    def test_git_more_than_limit(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_git_more_than_limit(self) -> None:
 | 
			
		||||
        commits_info = "* add some stuff ([e50508d](http://lfranchi-svn.beanstalkapp.com/work-test/changesets/e50508df))\n"
 | 
			
		||||
        expected_subject = "work-test / master"
 | 
			
		||||
        expected_message = """Leo Franchi [pushed](http://lfranchi-svn.beanstalkapp.com/work-test) 50 commits to branch master.
 | 
			
		||||
@@ -91,8 +84,7 @@ class BeanstalkHookTests(WebhookTestCase):
 | 
			
		||||
                                          content_type=None,
 | 
			
		||||
                                          **self.api_auth(self.TEST_USER_EMAIL))
 | 
			
		||||
 | 
			
		||||
    def test_git_more_than_limit_filtered_by_branches(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_git_more_than_limit_filtered_by_branches(self) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url(branches='master,development')
 | 
			
		||||
        commits_info = "* add some stuff ([e50508d](http://lfranchi-svn.beanstalkapp.com/work-test/changesets/e50508df))\n"
 | 
			
		||||
        expected_subject = "work-test / master"
 | 
			
		||||
@@ -104,8 +96,7 @@ class BeanstalkHookTests(WebhookTestCase):
 | 
			
		||||
                                          **self.api_auth(self.TEST_USER_EMAIL))
 | 
			
		||||
 | 
			
		||||
    @patch('zerver.webhooks.beanstalk.view.check_send_stream_message')
 | 
			
		||||
    def test_git_single_filtered_by_branches_ignore(self, check_send_stream_message_mock):
 | 
			
		||||
        # type: (MagicMock) -> None
 | 
			
		||||
    def test_git_single_filtered_by_branches_ignore(self, check_send_stream_message_mock: MagicMock) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url(branches='changes,development')
 | 
			
		||||
        payload = self.get_body('git_singlecommit')
 | 
			
		||||
        result = self.client_post(self.url, payload,
 | 
			
		||||
@@ -146,8 +137,7 @@ class BeanstalkHookTests(WebhookTestCase):
 | 
			
		||||
        self.assertFalse(check_send_stream_message_mock.called)
 | 
			
		||||
        self.assert_json_success(result)
 | 
			
		||||
 | 
			
		||||
    def test_svn_addremove(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_svn_addremove(self) -> None:
 | 
			
		||||
        expected_subject = "svn r3"
 | 
			
		||||
        expected_message = """Leo Franchi pushed [revision 3](http://lfranchi-svn.beanstalkapp.com/work-test/changesets/3):
 | 
			
		||||
 | 
			
		||||
@@ -156,8 +146,7 @@ class BeanstalkHookTests(WebhookTestCase):
 | 
			
		||||
                                          content_type=None,
 | 
			
		||||
                                          **self.api_auth(self.TEST_USER_EMAIL))
 | 
			
		||||
 | 
			
		||||
    def test_svn_changefile(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_svn_changefile(self) -> None:
 | 
			
		||||
        expected_subject = "svn r2"
 | 
			
		||||
        expected_message = """Leo Franchi pushed [revision 2](http://lfranchi-svn.beanstalkapp.com/work-test/changesets/2):
 | 
			
		||||
 | 
			
		||||
@@ -166,6 +155,5 @@ class BeanstalkHookTests(WebhookTestCase):
 | 
			
		||||
                                          content_type=None,
 | 
			
		||||
                                          **self.api_auth(self.TEST_USER_EMAIL))
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Dict[str, Text]
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Dict[str, Text]:
 | 
			
		||||
        return {'payload': self.fixture_data('beanstalk', fixture_name)}
 | 
			
		||||
 
 | 
			
		||||
@@ -21,11 +21,9 @@ ViewFuncT = TypeVar('ViewFuncT', bound=Callable[..., HttpResponse])
 | 
			
		||||
# Beanstalk's web hook UI rejects url with a @ in the username section of a url
 | 
			
		||||
# So we ask the user to replace them with %40
 | 
			
		||||
# We manually fix the username here before passing it along to @authenticated_rest_api_view
 | 
			
		||||
def beanstalk_decoder(view_func):
 | 
			
		||||
    # type: (ViewFuncT) -> ViewFuncT
 | 
			
		||||
def beanstalk_decoder(view_func: ViewFuncT) -> ViewFuncT:
 | 
			
		||||
    @wraps(view_func)
 | 
			
		||||
    def _wrapped_view_func(request, *args, **kwargs):
 | 
			
		||||
        # type: (HttpRequest, *Any, **Any) -> HttpResponse
 | 
			
		||||
    def _wrapped_view_func(request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
 | 
			
		||||
        try:
 | 
			
		||||
            auth_type, encoded_value = request.META['HTTP_AUTHORIZATION'].split()  # type: str, str
 | 
			
		||||
            if auth_type.lower() == "basic":
 | 
			
		||||
 
 | 
			
		||||
@@ -11,16 +11,14 @@ class BitbucketHookTests(WebhookTestCase):
 | 
			
		||||
    EXPECTED_SUBJECT = u"Repository name"
 | 
			
		||||
    EXPECTED_SUBJECT_BRANCH_EVENTS = u"Repository name / master"
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket_on_push_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket_on_push_event(self) -> None:
 | 
			
		||||
        fixture_name = 'push'
 | 
			
		||||
        self.url = self.build_webhook_url(payload=self.get_body(fixture_name))
 | 
			
		||||
        commit_info = u'* c ([25f93d2](https://bitbucket.org/kolaszek/repository-name/commits/25f93d22b719e2d678a7ad5ee0ef0d1fcdf39c12))'
 | 
			
		||||
        expected_message = u"kolaszek pushed 1 commit to branch master.\n\n{}".format(commit_info)
 | 
			
		||||
        self.send_and_test_stream_message(fixture_name, self.EXPECTED_SUBJECT_BRANCH_EVENTS, expected_message, **self.api_auth(self.TEST_USER_EMAIL))
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket_on_push_event_filtered_by_branches(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket_on_push_event_filtered_by_branches(self) -> None:
 | 
			
		||||
        fixture_name = 'push'
 | 
			
		||||
        self.url = self.build_webhook_url(payload=self.get_body(fixture_name),
 | 
			
		||||
                                          branches='master,development')
 | 
			
		||||
@@ -28,16 +26,14 @@ class BitbucketHookTests(WebhookTestCase):
 | 
			
		||||
        expected_message = u"kolaszek pushed 1 commit to branch master.\n\n{}".format(commit_info)
 | 
			
		||||
        self.send_and_test_stream_message(fixture_name, self.EXPECTED_SUBJECT_BRANCH_EVENTS, expected_message, **self.api_auth(self.TEST_USER_EMAIL))
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket_on_push_commits_above_limit_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket_on_push_commits_above_limit_event(self) -> None:
 | 
			
		||||
        fixture_name = 'push_commits_above_limit'
 | 
			
		||||
        self.url = self.build_webhook_url(payload=self.get_body(fixture_name))
 | 
			
		||||
        commit_info = u'* c ([25f93d2](https://bitbucket.org/kolaszek/repository-name/commits/25f93d22b719e2d678a7ad5ee0ef0d1fcdf39c12))\n'
 | 
			
		||||
        expected_message = u"kolaszek pushed 50 commits to branch master.\n\n{}[and 30 more commit(s)]".format(commit_info * 20)
 | 
			
		||||
        self.send_and_test_stream_message(fixture_name, self.EXPECTED_SUBJECT_BRANCH_EVENTS, expected_message, **self.api_auth(self.TEST_USER_EMAIL))
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket_on_push_commits_above_limit_event_filtered_by_branches(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket_on_push_commits_above_limit_event_filtered_by_branches(self) -> None:
 | 
			
		||||
        fixture_name = 'push_commits_above_limit'
 | 
			
		||||
        self.url = self.build_webhook_url(payload=self.get_body(fixture_name),
 | 
			
		||||
                                          branches='master,development')
 | 
			
		||||
@@ -45,16 +41,14 @@ class BitbucketHookTests(WebhookTestCase):
 | 
			
		||||
        expected_message = u"kolaszek pushed 50 commits to branch master.\n\n{}[and 30 more commit(s)]".format(commit_info * 20)
 | 
			
		||||
        self.send_and_test_stream_message(fixture_name, self.EXPECTED_SUBJECT_BRANCH_EVENTS, expected_message, **self.api_auth(self.TEST_USER_EMAIL))
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket_on_force_push_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket_on_force_push_event(self) -> None:
 | 
			
		||||
        fixture_name = 'force_push'
 | 
			
		||||
        self.url = self.build_webhook_url(payload=self.get_body(fixture_name))
 | 
			
		||||
        expected_message = u"kolaszek [force pushed](https://bitbucket.org/kolaszek/repository-name)"
 | 
			
		||||
        self.send_and_test_stream_message(fixture_name, self.EXPECTED_SUBJECT, expected_message, **self.api_auth(self.TEST_USER_EMAIL))
 | 
			
		||||
 | 
			
		||||
    @patch('zerver.webhooks.bitbucket.view.check_send_stream_message')
 | 
			
		||||
    def test_bitbucket_on_push_event_filtered_by_branches_ignore(self, check_send_stream_message_mock):
 | 
			
		||||
        # type: (MagicMock) -> None
 | 
			
		||||
    def test_bitbucket_on_push_event_filtered_by_branches_ignore(self, check_send_stream_message_mock: MagicMock) -> None:
 | 
			
		||||
        fixture_name = 'push'
 | 
			
		||||
        payload = self.get_body(fixture_name)
 | 
			
		||||
        self.url = self.build_webhook_url(payload=payload,
 | 
			
		||||
@@ -79,6 +73,5 @@ class BitbucketHookTests(WebhookTestCase):
 | 
			
		||||
        self.assertFalse(check_send_stream_message_mock.called)
 | 
			
		||||
        self.assert_json_success(result)
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Union[Text, Dict[str, Text]]
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Union[Text, Dict[str, Text]]:
 | 
			
		||||
        return self.fixture_data(self.FIXTURE_DIR_NAME, fixture_name)
 | 
			
		||||
 
 | 
			
		||||
@@ -13,55 +13,47 @@ class Bitbucket2HookTests(WebhookTestCase):
 | 
			
		||||
    EXPECTED_SUBJECT_ISSUE_EVENTS = u"Repository name / Issue #1 Bug"
 | 
			
		||||
    EXPECTED_SUBJECT_BRANCH_EVENTS = u"Repository name / master"
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_push_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_push_event(self) -> None:
 | 
			
		||||
        commit_info = u'* first commit ([84b96ad](https://bitbucket.org/kolaszek/repository-name/commits/84b96adc644a30fd6465b3d196369d880762afed))'
 | 
			
		||||
        expected_message = u"kolaszek [pushed](https://bitbucket.org/kolaszek/repository-name/branch/master) 1 commit to branch master.\n\n{}".format(commit_info)
 | 
			
		||||
        self.send_and_test_stream_message('push', self.EXPECTED_SUBJECT_BRANCH_EVENTS, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_push_commits_multiple_committers(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_push_commits_multiple_committers(self) -> None:
 | 
			
		||||
        commit_info = u'* first commit ([84b96ad](https://bitbucket.org/kolaszek/repository-name/commits/84b96adc644a30fd6465b3d196369d880762afed))\n'
 | 
			
		||||
        expected_message = u"""kolaszek [pushed](https://bitbucket.org/kolaszek/repository-name/branch/master) 3 commits to branch master. Commits by zbenjamin (2) and kolaszek (1).\n\n{}* first commit ([84b96ad](https://bitbucket.org/kolaszek/repository-name/commits/84b96adc644a30fd6465b3d196369d880762afed))""".format(commit_info*2)
 | 
			
		||||
        self.send_and_test_stream_message('push_multiple_committers', self.EXPECTED_SUBJECT_BRANCH_EVENTS, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_push_commits_multiple_committers_with_others(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_push_commits_multiple_committers_with_others(self) -> None:
 | 
			
		||||
        commit_info = u'* first commit ([84b96ad](https://bitbucket.org/kolaszek/repository-name/commits/84b96adc644a30fd6465b3d196369d880762afed))\n'
 | 
			
		||||
        expected_message = u"""kolaszek [pushed](https://bitbucket.org/kolaszek/repository-name/branch/master) 10 commits to branch master. Commits by james (3), Brendon (2), Tomasz (2) and others (3).\n\n{}* first commit ([84b96ad](https://bitbucket.org/kolaszek/repository-name/commits/84b96adc644a30fd6465b3d196369d880762afed))""".format(commit_info*9)
 | 
			
		||||
        self.send_and_test_stream_message('push_multiple_committers_with_others', self.EXPECTED_SUBJECT_BRANCH_EVENTS, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_push_commits_multiple_committers_filtered_by_branches(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_push_commits_multiple_committers_filtered_by_branches(self) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url(branches='master,development')
 | 
			
		||||
        commit_info = u'* first commit ([84b96ad](https://bitbucket.org/kolaszek/repository-name/commits/84b96adc644a30fd6465b3d196369d880762afed))\n'
 | 
			
		||||
        expected_message = u"""kolaszek [pushed](https://bitbucket.org/kolaszek/repository-name/branch/master) 3 commits to branch master. Commits by zbenjamin (2) and kolaszek (1).\n\n{}* first commit ([84b96ad](https://bitbucket.org/kolaszek/repository-name/commits/84b96adc644a30fd6465b3d196369d880762afed))""".format(commit_info*2)
 | 
			
		||||
        self.send_and_test_stream_message('push_multiple_committers', self.EXPECTED_SUBJECT_BRANCH_EVENTS, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_push_commits_multiple_committers_with_others_filtered_by_branches(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_push_commits_multiple_committers_with_others_filtered_by_branches(self) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url(branches='master,development')
 | 
			
		||||
        commit_info = u'* first commit ([84b96ad](https://bitbucket.org/kolaszek/repository-name/commits/84b96adc644a30fd6465b3d196369d880762afed))\n'
 | 
			
		||||
        expected_message = u"""kolaszek [pushed](https://bitbucket.org/kolaszek/repository-name/branch/master) 10 commits to branch master. Commits by james (3), Brendon (2), Tomasz (2) and others (3).\n\n{}* first commit ([84b96ad](https://bitbucket.org/kolaszek/repository-name/commits/84b96adc644a30fd6465b3d196369d880762afed))""".format(commit_info*9)
 | 
			
		||||
        self.send_and_test_stream_message('push_multiple_committers_with_others', self.EXPECTED_SUBJECT_BRANCH_EVENTS, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_push_event_filtered_by_branches(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_push_event_filtered_by_branches(self) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url(branches='master,development')
 | 
			
		||||
        commit_info = u'* first commit ([84b96ad](https://bitbucket.org/kolaszek/repository-name/commits/84b96adc644a30fd6465b3d196369d880762afed))'
 | 
			
		||||
        expected_message = u"kolaszek [pushed](https://bitbucket.org/kolaszek/repository-name/branch/master) 1 commit to branch master.\n\n{}".format(commit_info)
 | 
			
		||||
        self.send_and_test_stream_message('push', self.EXPECTED_SUBJECT_BRANCH_EVENTS, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_push_commits_above_limit_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_push_commits_above_limit_event(self) -> None:
 | 
			
		||||
        commit_info = '* a ([6f161a7](https://bitbucket.org/kolaszek/repository-name/commits/6f161a7bced94430ac8947d87dbf45c6deee3fb0))\n'
 | 
			
		||||
        expected_message = u"kolaszek [pushed](https://bitbucket.org/kolaszek/repository-name/branches/compare/6f161a7bced94430ac8947d87dbf45c6deee3fb0..1221f2fda6f1e3654b09f1f3a08390e4cb25bb48) 5 commits to branch master. Commits by Tomasz (5).\n\n{}[and more commit(s)]".format(
 | 
			
		||||
            (commit_info * 5),
 | 
			
		||||
        )
 | 
			
		||||
        self.send_and_test_stream_message('push_commits_above_limit', self.EXPECTED_SUBJECT_BRANCH_EVENTS, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_push_commits_above_limit_filtered_by_branches(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_push_commits_above_limit_filtered_by_branches(self) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url(branches='master,development')
 | 
			
		||||
        commit_info = '* a ([6f161a7](https://bitbucket.org/kolaszek/repository-name/commits/6f161a7bced94430ac8947d87dbf45c6deee3fb0))\n'
 | 
			
		||||
        expected_message = u"kolaszek [pushed](https://bitbucket.org/kolaszek/repository-name/branches/compare/6f161a7bced94430ac8947d87dbf45c6deee3fb0..1221f2fda6f1e3654b09f1f3a08390e4cb25bb48) 5 commits to branch master. Commits by Tomasz (5).\n\n{}[and more commit(s)]".format(
 | 
			
		||||
@@ -69,142 +61,121 @@ class Bitbucket2HookTests(WebhookTestCase):
 | 
			
		||||
        )
 | 
			
		||||
        self.send_and_test_stream_message('push_commits_above_limit', self.EXPECTED_SUBJECT_BRANCH_EVENTS, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_force_push_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_force_push_event(self) -> None:
 | 
			
		||||
        expected_message = u"kolaszek [force pushed](https://bitbucket.org/kolaszek/repository-name/branch/master) to branch master. Head is now 25f93d22b719e2d678a7ad5ee0ef0d1fcdf39c12"
 | 
			
		||||
        self.send_and_test_stream_message('force_push', self.EXPECTED_SUBJECT_BRANCH_EVENTS, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_force_push_event_filtered_by_branches(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_force_push_event_filtered_by_branches(self) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url(branches='master,development')
 | 
			
		||||
        expected_message = u"kolaszek [force pushed](https://bitbucket.org/kolaszek/repository-name/branch/master) to branch master. Head is now 25f93d22b719e2d678a7ad5ee0ef0d1fcdf39c12"
 | 
			
		||||
        self.send_and_test_stream_message('force_push', self.EXPECTED_SUBJECT_BRANCH_EVENTS, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_remove_branch_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_remove_branch_event(self) -> None:
 | 
			
		||||
        expected_message = u"kolaszek deleted branch master"
 | 
			
		||||
        self.send_and_test_stream_message('remove_branch', self.EXPECTED_SUBJECT_BRANCH_EVENTS, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_fork_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_fork_event(self) -> None:
 | 
			
		||||
        expected_message = u"User Tomasz(login: kolaszek) forked the repository into [kolaszek/repository-name2](https://bitbucket.org/kolaszek/repository-name2)."
 | 
			
		||||
        self.send_and_test_stream_message('fork', self.EXPECTED_SUBJECT, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_commit_comment_created_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_commit_comment_created_event(self) -> None:
 | 
			
		||||
        expected_message = u"kolaszek [commented](https://bitbucket.org/kolaszek/repository-name/commits/32c4ea19aa3af10acd08e419e2c354941a365d74#comment-3354963) on [32c4ea1](https://bitbucket.org/kolaszek/repository-name/commits/32c4ea19aa3af10acd08e419e2c354941a365d74)\n~~~ quote\nNice fix!\n~~~"
 | 
			
		||||
        self.send_and_test_stream_message('commit_comment_created', self.EXPECTED_SUBJECT, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_commit_status_changed_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_commit_status_changed_event(self) -> None:
 | 
			
		||||
        expected_message = u"[System mybuildtool](https://my-build-tool.com/builds/MY-PROJECT/BUILD-777) changed status of https://bitbucket.org/kolaszek/repository-name/9fec847784abb10b2fa567ee63b85bd238955d0e to SUCCESSFUL."
 | 
			
		||||
        self.send_and_test_stream_message('commit_status_changed', self.EXPECTED_SUBJECT, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_issue_created_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_issue_created_event(self) -> None:
 | 
			
		||||
        expected_message = u"kolaszek created [Issue #1](https://bitbucket.org/kolaszek/repository-name/issues/2/bug)(assigned to kolaszek)\n\n~~~ quote\nSuch a bug\n~~~"
 | 
			
		||||
        self.send_and_test_stream_message('issue_created', self.EXPECTED_SUBJECT_ISSUE_EVENTS, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_issue_updated_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_issue_updated_event(self) -> None:
 | 
			
		||||
        expected_message = u"kolaszek updated [Issue #1](https://bitbucket.org/kolaszek/repository-name/issues/2/bug)"
 | 
			
		||||
        self.send_and_test_stream_message('issue_updated', self.EXPECTED_SUBJECT_ISSUE_EVENTS, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_issue_commented_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_issue_commented_event(self) -> None:
 | 
			
		||||
        expected_message = u"kolaszek [commented](https://bitbucket.org/kolaszek/repository-name/issues/2#comment-28973596) on [Issue #1](https://bitbucket.org/kolaszek/repository-name/issues/2/bug)"
 | 
			
		||||
        self.send_and_test_stream_message('issue_commented', self.EXPECTED_SUBJECT_ISSUE_EVENTS, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_pull_request_created_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_pull_request_created_event(self) -> None:
 | 
			
		||||
        expected_message = u"kolaszek created [PR #1](https://bitbucket.org/kolaszek/repository-name/pull-requests/1)(assigned to tkolek)\nfrom `new-branch` to `master`\n\n~~~ quote\ndescription\n~~~"
 | 
			
		||||
        kwargs = {
 | 
			
		||||
            "HTTP_X_EVENT_KEY": 'pullrequest:created'
 | 
			
		||||
        }
 | 
			
		||||
        self.send_and_test_stream_message('pull_request_created_or_updated', self.EXPECTED_SUBJECT_PR_EVENTS, expected_message, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_pull_request_updated_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_pull_request_updated_event(self) -> None:
 | 
			
		||||
        expected_message = u"kolaszek updated [PR #1](https://bitbucket.org/kolaszek/repository-name/pull-requests/1)(assigned to tkolek)\nfrom `new-branch` to `master`\n\n~~~ quote\ndescription\n~~~"
 | 
			
		||||
        kwargs = {
 | 
			
		||||
            "HTTP_X_EVENT_KEY": 'pullrequest:updated'
 | 
			
		||||
        }
 | 
			
		||||
        self.send_and_test_stream_message('pull_request_created_or_updated', self.EXPECTED_SUBJECT_PR_EVENTS, expected_message, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_pull_request_approved_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_pull_request_approved_event(self) -> None:
 | 
			
		||||
        expected_message = u"kolaszek approved [PR #1](https://bitbucket.org/kolaszek/repository-name/pull-requests/1)"
 | 
			
		||||
        kwargs = {
 | 
			
		||||
            "HTTP_X_EVENT_KEY": 'pullrequest:approved'
 | 
			
		||||
        }
 | 
			
		||||
        self.send_and_test_stream_message('pull_request_approved_or_unapproved', self.EXPECTED_SUBJECT_PR_EVENTS, expected_message, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_pull_request_unapproved_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_pull_request_unapproved_event(self) -> None:
 | 
			
		||||
        expected_message = u"kolaszek unapproved [PR #1](https://bitbucket.org/kolaszek/repository-name/pull-requests/1)"
 | 
			
		||||
        kwargs = {
 | 
			
		||||
            "HTTP_X_EVENT_KEY": 'pullrequest:unapproved'
 | 
			
		||||
        }
 | 
			
		||||
        self.send_and_test_stream_message('pull_request_approved_or_unapproved', self.EXPECTED_SUBJECT_PR_EVENTS, expected_message, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_pull_request_declined_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_pull_request_declined_event(self) -> None:
 | 
			
		||||
        expected_message = u"kolaszek rejected [PR #1](https://bitbucket.org/kolaszek/repository-name/pull-requests/1)"
 | 
			
		||||
        kwargs = {
 | 
			
		||||
            "HTTP_X_EVENT_KEY": 'pullrequest:rejected'
 | 
			
		||||
        }
 | 
			
		||||
        self.send_and_test_stream_message('pull_request_fulfilled_or_rejected', self.EXPECTED_SUBJECT_PR_EVENTS, expected_message, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_pull_request_fulfilled_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_pull_request_fulfilled_event(self) -> None:
 | 
			
		||||
        expected_message = u"kolaszek merged [PR #1](https://bitbucket.org/kolaszek/repository-name/pull-requests/1)"
 | 
			
		||||
        kwargs = {
 | 
			
		||||
            "HTTP_X_EVENT_KEY": 'pullrequest:fulfilled'
 | 
			
		||||
        }
 | 
			
		||||
        self.send_and_test_stream_message('pull_request_fulfilled_or_rejected', self.EXPECTED_SUBJECT_PR_EVENTS, expected_message, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_pull_request_comment_created_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_pull_request_comment_created_event(self) -> None:
 | 
			
		||||
        expected_message = u"kolaszek [commented](https://bitbucket.org/kolaszek/repository-name/pull-requests/3/_/diff#comment-20576503) on [PR #1](https://bitbucket.org/kolaszek/repository-name/pull-requests/3)\n\n~~~ quote\nComment1\n~~~"
 | 
			
		||||
        kwargs = {
 | 
			
		||||
            "HTTP_X_EVENT_KEY": 'pullrequest:comment_created'
 | 
			
		||||
        }
 | 
			
		||||
        self.send_and_test_stream_message('pull_request_comment_action', self.EXPECTED_SUBJECT_PR_EVENTS, expected_message, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_pull_request_comment_updated_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_pull_request_comment_updated_event(self) -> None:
 | 
			
		||||
        expected_message = u"kolaszek updated a [comment](https://bitbucket.org/kolaszek/repository-name/pull-requests/3/_/diff#comment-20576503) on [PR #1](https://bitbucket.org/kolaszek/repository-name/pull-requests/3)\n\n~~~ quote\nComment1\n~~~"
 | 
			
		||||
        kwargs = {
 | 
			
		||||
            "HTTP_X_EVENT_KEY": 'pullrequest:comment_updated'
 | 
			
		||||
        }
 | 
			
		||||
        self.send_and_test_stream_message('pull_request_comment_action', self.EXPECTED_SUBJECT_PR_EVENTS, expected_message, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_pull_request_comment_deleted_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_pull_request_comment_deleted_event(self) -> None:
 | 
			
		||||
        expected_message = u"kolaszek deleted a [comment](https://bitbucket.org/kolaszek/repository-name/pull-requests/3/_/diff#comment-20576503) on [PR #1](https://bitbucket.org/kolaszek/repository-name/pull-requests/3)\n\n~~~ quote\nComment1\n~~~"
 | 
			
		||||
        kwargs = {
 | 
			
		||||
            "HTTP_X_EVENT_KEY": 'pullrequest:comment_deleted'
 | 
			
		||||
        }
 | 
			
		||||
        self.send_and_test_stream_message('pull_request_comment_action', self.EXPECTED_SUBJECT_PR_EVENTS, expected_message, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_push_one_tag_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_push_one_tag_event(self) -> None:
 | 
			
		||||
        expected_message = u"kolaszek pushed tag [a](https://bitbucket.org/kolaszek/repository-name/commits/tag/a)"
 | 
			
		||||
        kwargs = {
 | 
			
		||||
            "HTTP_X_EVENT_KEY": 'pullrequest:push'
 | 
			
		||||
        }
 | 
			
		||||
        self.send_and_test_stream_message('push_one_tag', self.EXPECTED_SUBJECT, expected_message, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_push_remove_tag_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_push_remove_tag_event(self) -> None:
 | 
			
		||||
        expected_message = u"kolaszek removed tag [a](https://bitbucket.org/kolaszek/repository-name/commits/tag/a)"
 | 
			
		||||
        kwargs = {
 | 
			
		||||
            "HTTP_X_EVENT_KEY": 'pullrequest:push'
 | 
			
		||||
        }
 | 
			
		||||
        self.send_and_test_stream_message('push_remove_tag', self.EXPECTED_SUBJECT, expected_message, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_push_more_than_one_tag_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_push_more_than_one_tag_event(self) -> None:
 | 
			
		||||
        expected_message = u"kolaszek pushed tag [{name}](https://bitbucket.org/kolaszek/repository-name/commits/tag/{name})"
 | 
			
		||||
        kwargs = {
 | 
			
		||||
            "HTTP_X_EVENT_KEY": 'pullrequest:push'
 | 
			
		||||
@@ -217,8 +188,7 @@ class Bitbucket2HookTests(WebhookTestCase):
 | 
			
		||||
        self.do_test_subject(msg, self.EXPECTED_SUBJECT)
 | 
			
		||||
        self.do_test_message(msg, expected_message.format(name='a'))
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_more_than_one_push_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_more_than_one_push_event(self) -> None:
 | 
			
		||||
        kwargs = {
 | 
			
		||||
            "HTTP_X_EVENT_KEY": 'pullrequest:push'
 | 
			
		||||
        }
 | 
			
		||||
@@ -230,8 +200,7 @@ class Bitbucket2HookTests(WebhookTestCase):
 | 
			
		||||
        self.do_test_message(msg, 'kolaszek pushed tag [a](https://bitbucket.org/kolaszek/repository-name/commits/tag/a)')
 | 
			
		||||
        self.do_test_subject(msg, self.EXPECTED_SUBJECT)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_more_than_one_push_event_filtered_by_branches(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_more_than_one_push_event_filtered_by_branches(self) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url(branches='master,development')
 | 
			
		||||
        kwargs = {
 | 
			
		||||
            "HTTP_X_EVENT_KEY": 'pullrequest:push'
 | 
			
		||||
@@ -244,8 +213,7 @@ class Bitbucket2HookTests(WebhookTestCase):
 | 
			
		||||
        self.do_test_message(msg, 'kolaszek pushed tag [a](https://bitbucket.org/kolaszek/repository-name/commits/tag/a)')
 | 
			
		||||
        self.do_test_subject(msg, self.EXPECTED_SUBJECT)
 | 
			
		||||
 | 
			
		||||
    def test_bitbucket2_on_more_than_one_push_event_filtered_by_branches_ignore(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bitbucket2_on_more_than_one_push_event_filtered_by_branches_ignore(self) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url(branches='changes,development')
 | 
			
		||||
        kwargs = {
 | 
			
		||||
            "HTTP_X_EVENT_KEY": 'pullrequest:push'
 | 
			
		||||
 
 | 
			
		||||
@@ -62,15 +62,13 @@ def api_bitbucket2_webhook(request, user_profile, payload=REQ(argument_type='bod
 | 
			
		||||
                                      stream, subject, body)
 | 
			
		||||
    return json_success()
 | 
			
		||||
 | 
			
		||||
def get_subject_for_branch_specified_events(payload, branch_name=None):
 | 
			
		||||
    # type: (Dict[str, Any], Optional[Text]) -> Text
 | 
			
		||||
def get_subject_for_branch_specified_events(payload: Dict[str, Any], branch_name: Optional[Text]=None) -> Text:
 | 
			
		||||
    return SUBJECT_WITH_BRANCH_TEMPLATE.format(
 | 
			
		||||
        repo=get_repository_name(payload['repository']),
 | 
			
		||||
        branch=get_branch_name_for_push_event(payload) if branch_name is None else branch_name
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_push_subjects(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> List[str]
 | 
			
		||||
def get_push_subjects(payload: Dict[str, Any]) -> List[str]:
 | 
			
		||||
    subjects_list = []
 | 
			
		||||
    for change in payload['push']['changes']:
 | 
			
		||||
        potential_tag = (change['new'] or change['old'] or {}).get('type')
 | 
			
		||||
@@ -84,13 +82,11 @@ def get_push_subjects(payload):
 | 
			
		||||
            subjects_list.append(str(get_subject_for_branch_specified_events(payload, branch_name)))
 | 
			
		||||
    return subjects_list
 | 
			
		||||
 | 
			
		||||
def get_subject(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> str
 | 
			
		||||
def get_subject(payload: Dict[str, Any]) -> str:
 | 
			
		||||
    assert(payload['repository'] is not None)
 | 
			
		||||
    return BITBUCKET_SUBJECT_TEMPLATE.format(repository_name=get_repository_name(payload['repository']))
 | 
			
		||||
 | 
			
		||||
def get_subject_based_on_type(payload, type):
 | 
			
		||||
    # type: (Dict[str, Any], str) -> Text
 | 
			
		||||
def get_subject_based_on_type(payload: Dict[str, Any], type: str) -> Text:
 | 
			
		||||
    if type.startswith('pull_request'):
 | 
			
		||||
        return SUBJECT_WITH_PR_OR_ISSUE_INFO_TEMPLATE.format(
 | 
			
		||||
            repo=get_repository_name(payload['repository']),
 | 
			
		||||
@@ -107,8 +103,7 @@ def get_subject_based_on_type(payload, type):
 | 
			
		||||
        )
 | 
			
		||||
    return get_subject(payload)
 | 
			
		||||
 | 
			
		||||
def get_type(request, payload):
 | 
			
		||||
    # type: (HttpRequest, Dict[str, Any]) -> str
 | 
			
		||||
def get_type(request: HttpRequest, payload: Dict[str, Any]) -> str:
 | 
			
		||||
    event_key = request.META.get("HTTP_X_EVENT_KEY")
 | 
			
		||||
    if payload.get('push'):
 | 
			
		||||
        return 'push'
 | 
			
		||||
@@ -133,14 +128,12 @@ def get_type(request, payload):
 | 
			
		||||
                return pull_request_template.format(action)
 | 
			
		||||
    raise UnknownTriggerType("We don't support {} event type".format(event_key))
 | 
			
		||||
 | 
			
		||||
def get_body_based_on_type(type):
 | 
			
		||||
    # type: (str) -> Callable[[Dict[str, Any]], Text]
 | 
			
		||||
def get_body_based_on_type(type: str) -> Callable[[Dict[str, Any]], Text]:
 | 
			
		||||
    fn = GET_SINGLE_MESSAGE_BODY_DEPENDING_ON_TYPE_MAPPER.get(type)
 | 
			
		||||
    assert callable(fn)  # type parameter should be pre-checked, so not None
 | 
			
		||||
    return fn
 | 
			
		||||
 | 
			
		||||
def get_push_bodies(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> List[Text]
 | 
			
		||||
def get_push_bodies(payload: Dict[str, Any]) -> List[Text]:
 | 
			
		||||
    messages_list = []
 | 
			
		||||
    for change in payload['push']['changes']:
 | 
			
		||||
        potential_tag = (change['new'] or change['old'] or {}).get('type')
 | 
			
		||||
@@ -154,15 +147,13 @@ def get_push_bodies(payload):
 | 
			
		||||
            messages_list.append(get_normal_push_body(payload, change))
 | 
			
		||||
    return messages_list
 | 
			
		||||
 | 
			
		||||
def get_remove_branch_push_body(payload, change):
 | 
			
		||||
    # type: (Dict[str, Any], Dict[str, Any]) -> Text
 | 
			
		||||
def get_remove_branch_push_body(payload: Dict[str, Any], change: Dict[str, Any]) -> Text:
 | 
			
		||||
    return get_remove_branch_event_message(
 | 
			
		||||
        get_user_username(payload),
 | 
			
		||||
        change['old']['name'],
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_force_push_body(payload, change):
 | 
			
		||||
    # type: (Dict[str, Any], Dict[str, Any]) -> Text
 | 
			
		||||
def get_force_push_body(payload: Dict[str, Any], change: Dict[str, Any]) -> Text:
 | 
			
		||||
    return get_force_push_commits_event_message(
 | 
			
		||||
        get_user_username(payload),
 | 
			
		||||
        change['links']['html']['href'],
 | 
			
		||||
@@ -170,14 +161,12 @@ def get_force_push_body(payload, change):
 | 
			
		||||
        change['new']['target']['hash']
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_commit_author_name(commit):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_commit_author_name(commit: Dict[str, Any]) -> Text:
 | 
			
		||||
    if commit['author'].get('user'):
 | 
			
		||||
        return commit['author']['user'].get('username')
 | 
			
		||||
    return commit['author']['raw'].split()[0]
 | 
			
		||||
 | 
			
		||||
def get_normal_push_body(payload, change):
 | 
			
		||||
    # type: (Dict[str, Any], Dict[str, Any]) -> Text
 | 
			
		||||
def get_normal_push_body(payload: Dict[str, Any], change: Dict[str, Any]) -> Text:
 | 
			
		||||
    commits_data = [{
 | 
			
		||||
        'name': get_commit_author_name(commit),
 | 
			
		||||
        'sha': commit.get('hash'),
 | 
			
		||||
@@ -193,8 +182,7 @@ def get_normal_push_body(payload, change):
 | 
			
		||||
        is_truncated=change['truncated']
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_fork_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> str
 | 
			
		||||
def get_fork_body(payload: Dict[str, Any]) -> str:
 | 
			
		||||
    return BITBUCKET_FORK_BODY.format(
 | 
			
		||||
        display_name=get_user_display_name(payload),
 | 
			
		||||
        username=get_user_username(payload),
 | 
			
		||||
@@ -202,8 +190,7 @@ def get_fork_body(payload):
 | 
			
		||||
        fork_url=get_repository_url(payload['fork'])
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_commit_comment_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_commit_comment_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    comment = payload['comment']
 | 
			
		||||
    action = u'[commented]({})'.format(comment['links']['html']['href'])
 | 
			
		||||
    return get_commits_comment_action_message(
 | 
			
		||||
@@ -214,8 +201,7 @@ def get_commit_comment_body(payload):
 | 
			
		||||
        comment['content']['raw'],
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_commit_status_changed_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> str
 | 
			
		||||
def get_commit_status_changed_body(payload: Dict[str, Any]) -> str:
 | 
			
		||||
    commit_id = re.match('.*/commit/(?P<commit_id>[A-Za-z0-9]*$)', payload['commit_status']['links']['commit']['href'])
 | 
			
		||||
    if commit_id:
 | 
			
		||||
        commit_info = "{}/{}".format(get_repository_url(payload['repository']), commit_id.group('commit_id'))
 | 
			
		||||
@@ -229,13 +215,11 @@ def get_commit_status_changed_body(payload):
 | 
			
		||||
        status=payload['commit_status']['state']
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_issue_commented_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_issue_commented_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    action = '[commented]({}) on'.format(payload['comment']['links']['html']['href'])
 | 
			
		||||
    return get_issue_action_body(payload, action)
 | 
			
		||||
 | 
			
		||||
def get_issue_action_body(payload, action):
 | 
			
		||||
    # type: (Dict[str, Any], str) -> Text
 | 
			
		||||
def get_issue_action_body(payload: Dict[str, Any], action: str) -> Text:
 | 
			
		||||
    issue = payload['issue']
 | 
			
		||||
    assignee = None
 | 
			
		||||
    message = None
 | 
			
		||||
@@ -253,8 +237,7 @@ def get_issue_action_body(payload, action):
 | 
			
		||||
        assignee
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_pull_request_action_body(payload, action):
 | 
			
		||||
    # type: (Dict[str, Any], str) -> Text
 | 
			
		||||
def get_pull_request_action_body(payload: Dict[str, Any], action: str) -> Text:
 | 
			
		||||
    pull_request = payload['pullrequest']
 | 
			
		||||
    return get_pull_request_event_message(
 | 
			
		||||
        get_user_username(payload),
 | 
			
		||||
@@ -263,8 +246,7 @@ def get_pull_request_action_body(payload, action):
 | 
			
		||||
        pull_request.get('id')
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_pull_request_created_or_updated_body(payload, action):
 | 
			
		||||
    # type: (Dict[str, Any], str) -> Text
 | 
			
		||||
def get_pull_request_created_or_updated_body(payload: Dict[str, Any], action: str) -> Text:
 | 
			
		||||
    pull_request = payload['pullrequest']
 | 
			
		||||
    assignee = None
 | 
			
		||||
    if pull_request.get('reviewers'):
 | 
			
		||||
@@ -281,18 +263,15 @@ def get_pull_request_created_or_updated_body(payload, action):
 | 
			
		||||
        assignee=assignee
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_pull_request_comment_created_action_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_pull_request_comment_created_action_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    action = '[commented]({})'.format(payload['comment']['links']['html']['href'])
 | 
			
		||||
    return get_pull_request_comment_action_body(payload, action)
 | 
			
		||||
 | 
			
		||||
def get_pull_request_deleted_or_updated_comment_action_body(payload, action):
 | 
			
		||||
    # type: (Dict[str, Any], Text) -> Text
 | 
			
		||||
def get_pull_request_deleted_or_updated_comment_action_body(payload: Dict[str, Any], action: Text) -> Text:
 | 
			
		||||
    action = "{} a [comment]({})".format(action, payload['comment']['links']['html']['href'])
 | 
			
		||||
    return get_pull_request_comment_action_body(payload, action)
 | 
			
		||||
 | 
			
		||||
def get_pull_request_comment_action_body(payload, action):
 | 
			
		||||
    # type: (Dict[str, Any], str) -> Text
 | 
			
		||||
def get_pull_request_comment_action_body(payload: Dict[str, Any], action: str) -> Text:
 | 
			
		||||
    action += ' on'
 | 
			
		||||
    return get_pull_request_event_message(
 | 
			
		||||
        get_user_username(payload),
 | 
			
		||||
@@ -302,8 +281,7 @@ def get_pull_request_comment_action_body(payload, action):
 | 
			
		||||
        message=payload['comment']['content']['raw']
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_push_tag_body(payload, change):
 | 
			
		||||
    # type: (Dict[str, Any], Dict[str, Any]) -> Text
 | 
			
		||||
def get_push_tag_body(payload: Dict[str, Any], change: Dict[str, Any]) -> Text:
 | 
			
		||||
    if change.get('created'):
 | 
			
		||||
        tag = change['new']
 | 
			
		||||
        action = 'pushed'  # type: Optional[Text]
 | 
			
		||||
@@ -320,36 +298,28 @@ def get_push_tag_body(payload, change):
 | 
			
		||||
        action=action
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_pull_request_title(pullrequest_payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> str
 | 
			
		||||
def get_pull_request_title(pullrequest_payload: Dict[str, Any]) -> str:
 | 
			
		||||
    return pullrequest_payload['title']
 | 
			
		||||
 | 
			
		||||
def get_pull_request_url(pullrequest_payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> str
 | 
			
		||||
def get_pull_request_url(pullrequest_payload: Dict[str, Any]) -> str:
 | 
			
		||||
    return pullrequest_payload['links']['html']['href']
 | 
			
		||||
 | 
			
		||||
def get_repository_url(repository_payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> str
 | 
			
		||||
def get_repository_url(repository_payload: Dict[str, Any]) -> str:
 | 
			
		||||
    return repository_payload['links']['html']['href']
 | 
			
		||||
 | 
			
		||||
def get_repository_name(repository_payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> str
 | 
			
		||||
def get_repository_name(repository_payload: Dict[str, Any]) -> str:
 | 
			
		||||
    return repository_payload['name']
 | 
			
		||||
 | 
			
		||||
def get_repository_full_name(repository_payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> str
 | 
			
		||||
def get_repository_full_name(repository_payload: Dict[str, Any]) -> str:
 | 
			
		||||
    return repository_payload['full_name']
 | 
			
		||||
 | 
			
		||||
def get_user_display_name(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> str
 | 
			
		||||
def get_user_display_name(payload: Dict[str, Any]) -> str:
 | 
			
		||||
    return payload['actor']['display_name']
 | 
			
		||||
 | 
			
		||||
def get_user_username(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> str
 | 
			
		||||
def get_user_username(payload: Dict[str, Any]) -> str:
 | 
			
		||||
    return payload['actor']['username']
 | 
			
		||||
 | 
			
		||||
def get_branch_name_for_push_event(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Optional[str]
 | 
			
		||||
def get_branch_name_for_push_event(payload: Dict[str, Any]) -> Optional[str]:
 | 
			
		||||
    change = payload['push']['changes'][-1]
 | 
			
		||||
    potential_tag = (change['new'] or change['old'] or {}).get('type')
 | 
			
		||||
    if potential_tag == 'tag':
 | 
			
		||||
 
 | 
			
		||||
@@ -6,26 +6,22 @@ class CircleCiHookTests(WebhookTestCase):
 | 
			
		||||
    URL_TEMPLATE = u"/api/v1/external/circleci?stream={stream}&api_key={api_key}"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'circleci'
 | 
			
		||||
 | 
			
		||||
    def test_circleci_build_in_success_status(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_circleci_build_in_success_status(self) -> None:
 | 
			
		||||
        expected_subject = u"RepoName"
 | 
			
		||||
        expected_message = u"[Build](https://circleci.com/gh/username/project/build_number) triggered by username on master branch succeeded."
 | 
			
		||||
        self.send_and_test_stream_message('build_passed', expected_subject, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_circleci_build_in_failed_status(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_circleci_build_in_failed_status(self) -> None:
 | 
			
		||||
        expected_subject = u"RepoName"
 | 
			
		||||
        expected_message = u"[Build](https://circleci.com/gh/username/project/build_number) triggered by username on master branch failed."
 | 
			
		||||
        self.send_and_test_stream_message('build_failed', expected_subject, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_circleci_build_in_failed_status_when_previous_build_failed_too(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_circleci_build_in_failed_status_when_previous_build_failed_too(self) -> None:
 | 
			
		||||
        expected_subject = u"RepoName"
 | 
			
		||||
        expected_message = u"[Build](https://circleci.com/gh/username/project/build_number) triggered by username on master branch is still failing."
 | 
			
		||||
        self.send_and_test_stream_message('build_failed_when_previous_build_failed', expected_subject, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_circleci_build_in_success_status_when_previous_build_failed_too(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_circleci_build_in_success_status_when_previous_build_failed_too(self) -> None:
 | 
			
		||||
        expected_subject = u"RepoName"
 | 
			
		||||
        expected_message = u"[Build](https://circleci.com/gh/username/project/build_number) triggered by username on master branch fixed."
 | 
			
		||||
        self.send_and_test_stream_message('build_passed_when_previous_build_failed', expected_subject, expected_message)
 | 
			
		||||
 
 | 
			
		||||
@@ -29,12 +29,10 @@ def api_circleci_webhook(request, user_profile, payload=REQ(argument_type='body'
 | 
			
		||||
    check_send_stream_message(user_profile, request.client, stream, subject, body)
 | 
			
		||||
    return json_success()
 | 
			
		||||
 | 
			
		||||
def get_subject(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_subject(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return CIRCLECI_SUBJECT_TEMPLATE.format(repository_name=payload['reponame'])
 | 
			
		||||
 | 
			
		||||
def get_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    data = {
 | 
			
		||||
        'build_url': payload['build_url'],
 | 
			
		||||
        'username': payload['username'],
 | 
			
		||||
@@ -43,8 +41,7 @@ def get_body(payload):
 | 
			
		||||
    }
 | 
			
		||||
    return CIRCLECI_MESSAGE_TEMPLATE.format(**data)
 | 
			
		||||
 | 
			
		||||
def get_status(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_status(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    status = payload['status']
 | 
			
		||||
    if payload['previous'] and payload['previous']['status'] == FAILED_STATUS and status == FAILED_STATUS:
 | 
			
		||||
        return u'is still failing'
 | 
			
		||||
 
 | 
			
		||||
@@ -7,32 +7,28 @@ class CodeshipHookTests(WebhookTestCase):
 | 
			
		||||
    SUBJECT = u"codeship/docs"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'codeship'
 | 
			
		||||
 | 
			
		||||
    def test_codeship_build_in_testing_status_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_codeship_build_in_testing_status_message(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Tests if codeship testing status is mapped correctly
 | 
			
		||||
        """
 | 
			
		||||
        expected_message = u"[Build](https://www.codeship.com/projects/10213/builds/973711) triggered by beanieboi on master branch started."
 | 
			
		||||
        self.send_and_test_stream_message('testing_build', self.SUBJECT, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_codeship_build_in_error_status_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_codeship_build_in_error_status_message(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Tests if codeship error status is mapped correctly
 | 
			
		||||
        """
 | 
			
		||||
        expected_message = u"[Build](https://www.codeship.com/projects/10213/builds/973711) triggered by beanieboi on master branch failed."
 | 
			
		||||
        self.send_and_test_stream_message('error_build', self.SUBJECT, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_codeship_build_in_success_status_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_codeship_build_in_success_status_message(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Tests if codeship success status is mapped correctly
 | 
			
		||||
        """
 | 
			
		||||
        expected_message = u"[Build](https://www.codeship.com/projects/10213/builds/973711) triggered by beanieboi on master branch succeeded."
 | 
			
		||||
        self.send_and_test_stream_message('success_build', self.SUBJECT, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_codeship_build_in_other_status_status_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_codeship_build_in_other_status_status_message(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Tests if codeship other status is mapped correctly
 | 
			
		||||
        """
 | 
			
		||||
 
 | 
			
		||||
@@ -37,13 +37,11 @@ def api_codeship_webhook(request, user_profile, payload=REQ(argument_type='body'
 | 
			
		||||
    return json_success()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_subject_for_http_request(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> str
 | 
			
		||||
def get_subject_for_http_request(payload: Dict[str, Any]) -> str:
 | 
			
		||||
    return CODESHIP_SUBJECT_TEMPLATE.format(project_name=payload['project_name'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_body_for_http_request(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> str
 | 
			
		||||
def get_body_for_http_request(payload: Dict[str, Any]) -> str:
 | 
			
		||||
    return CODESHIP_MESSAGE_TEMPLATE.format(
 | 
			
		||||
        build_url=payload['build_url'],
 | 
			
		||||
        committer=payload['committer'],
 | 
			
		||||
@@ -52,7 +50,6 @@ def get_body_for_http_request(payload):
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_status_message(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> str
 | 
			
		||||
def get_status_message(payload: Dict[str, Any]) -> str:
 | 
			
		||||
    build_status = payload['status']
 | 
			
		||||
    return CODESHIP_STATUS_MAPPER.get(build_status, CODESHIP_DEFAULT_STATUS.format(status=build_status))
 | 
			
		||||
 
 | 
			
		||||
@@ -6,14 +6,12 @@ class CrashlyticsHookTests(WebhookTestCase):
 | 
			
		||||
    URL_TEMPLATE = u"/api/v1/external/crashlytics?stream={stream}&api_key={api_key}"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'crashlytics'
 | 
			
		||||
 | 
			
		||||
    def test_crashlytics_verification_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_crashlytics_verification_message(self) -> None:
 | 
			
		||||
        expected_subject = u"Setup"
 | 
			
		||||
        expected_message = u"Webhook has been successfully configured."
 | 
			
		||||
        self.send_and_test_stream_message('verification', expected_subject, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_crashlytics_build_in_success_status(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_crashlytics_build_in_success_status(self) -> None:
 | 
			
		||||
        expected_subject = u"123: Issue Title"
 | 
			
		||||
        expected_message = u"[Issue](http://crashlytics.com/full/url/to/issue) impacts at least 16 device(s)."
 | 
			
		||||
        self.send_and_test_stream_message('issue_message', expected_subject, expected_message)
 | 
			
		||||
 
 | 
			
		||||
@@ -7,8 +7,7 @@ class DelightedHookTests(WebhookTestCase):
 | 
			
		||||
    URL_TEMPLATE = "/api/v1/external/delighted?stream={stream}&api_key={api_key}"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'delighted'
 | 
			
		||||
 | 
			
		||||
    def test_feedback_message_promoter(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_feedback_message_promoter(self) -> None:
 | 
			
		||||
        expected_subject = "Survey Response"
 | 
			
		||||
        expected_message = ("Kudos! You have a new promoter.\n"
 | 
			
		||||
                            ">Score of 9/10 from charlie_gravis@example.com"
 | 
			
		||||
@@ -19,8 +18,7 @@ class DelightedHookTests(WebhookTestCase):
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_feedback_message_non_promoter(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_feedback_message_non_promoter(self) -> None:
 | 
			
		||||
        expected_subject = "Survey Response"
 | 
			
		||||
        expected_message = ("Great! You have new feedback.\n"
 | 
			
		||||
                            ">Score of 5/10 from paul_gravis@example.com"
 | 
			
		||||
@@ -32,6 +30,5 @@ class DelightedHookTests(WebhookTestCase):
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Text
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Text:
 | 
			
		||||
        return self.fixture_data("delighted", fixture_name, file_type="json")
 | 
			
		||||
 
 | 
			
		||||
@@ -9,8 +9,7 @@ from django.http import HttpRequest, HttpResponse
 | 
			
		||||
from six import text_type
 | 
			
		||||
from typing import Dict, Any, Optional
 | 
			
		||||
 | 
			
		||||
def body_template(score):
 | 
			
		||||
    # type: (int) -> str
 | 
			
		||||
def body_template(score: int) -> str:
 | 
			
		||||
    if score >= 7:
 | 
			
		||||
        return 'Kudos! You have a new promoter.\n>Score of {score}/10 from {email}\n>{comment}'
 | 
			
		||||
    else:
 | 
			
		||||
 
 | 
			
		||||
@@ -17,8 +17,7 @@ class DeskDotComHookTests(WebhookTestCase):
 | 
			
		||||
    URL_TEMPLATE = "/api/v1/external/deskdotcom"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'deskdotcom'
 | 
			
		||||
 | 
			
		||||
    def test_static_text_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_static_text_message(self) -> None:
 | 
			
		||||
 | 
			
		||||
        expected_subject = u"static text notification"
 | 
			
		||||
        expected_message = u"This is a custom action."
 | 
			
		||||
@@ -27,8 +26,7 @@ class DeskDotComHookTests(WebhookTestCase):
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded",
 | 
			
		||||
                                          **self.api_auth(self.TEST_USER_EMAIL))
 | 
			
		||||
 | 
			
		||||
    def test_case_updated_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_case_updated_message(self) -> None:
 | 
			
		||||
        expected_subject = u"case updated notification"
 | 
			
		||||
        expected_message = (u"Case 2 updated. "
 | 
			
		||||
                            u"Link: <a href='https://deskdotcomtest.desk.com/web/agent/case/2'>"
 | 
			
		||||
@@ -38,8 +36,7 @@ class DeskDotComHookTests(WebhookTestCase):
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded",
 | 
			
		||||
                                          **self.api_auth(self.TEST_USER_EMAIL))
 | 
			
		||||
 | 
			
		||||
    def test_unicode_text_italian(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_unicode_text_italian(self) -> None:
 | 
			
		||||
 | 
			
		||||
        expected_subject = u"case updated notification"
 | 
			
		||||
        expected_message = (u"Case 2 updated. "
 | 
			
		||||
@@ -50,8 +47,7 @@ class DeskDotComHookTests(WebhookTestCase):
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded",
 | 
			
		||||
                                          **self.api_auth(self.TEST_USER_EMAIL))
 | 
			
		||||
 | 
			
		||||
    def test_unicode_text_japanese(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_unicode_text_japanese(self) -> None:
 | 
			
		||||
 | 
			
		||||
        expected_subject = u"case updated notification"
 | 
			
		||||
        expected_message = (u"Case 2 updated. "
 | 
			
		||||
@@ -62,6 +58,5 @@ class DeskDotComHookTests(WebhookTestCase):
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded",
 | 
			
		||||
                                          **self.api_auth(self.TEST_USER_EMAIL))
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Text
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Text:
 | 
			
		||||
        return self.fixture_data("deskdotcom", fixture_name, file_type="txt")
 | 
			
		||||
 
 | 
			
		||||
@@ -6,8 +6,7 @@ class FreshdeskHookTests(WebhookTestCase):
 | 
			
		||||
    STREAM_NAME = 'freshdesk'
 | 
			
		||||
    URL_TEMPLATE = u"/api/v1/external/freshdesk?stream={stream}"
 | 
			
		||||
 | 
			
		||||
    def test_ticket_creation(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_ticket_creation(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Messages are generated on ticket creation through Freshdesk's
 | 
			
		||||
        "Dispatch'r" service.
 | 
			
		||||
@@ -24,8 +23,7 @@ Priority: **High**
 | 
			
		||||
Status: **Pending**"""
 | 
			
		||||
        self.send_and_test_stream_message('ticket_created', expected_subject, expected_message, content_type="application/x-www-form-urlencoded", **self.api_auth(self.TEST_USER_EMAIL))
 | 
			
		||||
 | 
			
		||||
    def test_status_change(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_status_change(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Messages are generated when a ticket's status changes through
 | 
			
		||||
        Freshdesk's "Observer" service.
 | 
			
		||||
@@ -38,8 +36,7 @@ Status: **Resolved** => **Waiting on Customer**"""
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded",
 | 
			
		||||
                                          **self.api_auth(self.TEST_USER_EMAIL))
 | 
			
		||||
 | 
			
		||||
    def test_priority_change(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_priority_change(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Messages are generated when a ticket's priority changes through
 | 
			
		||||
        Freshdesk's "Observer" service.
 | 
			
		||||
@@ -52,8 +49,7 @@ Priority: **High** => **Low**"""
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded",
 | 
			
		||||
                                          **self.api_auth(self.TEST_USER_EMAIL))
 | 
			
		||||
 | 
			
		||||
    def note_change(self, fixture, note_type):
 | 
			
		||||
        # type: (Text, Text) -> None
 | 
			
		||||
    def note_change(self, fixture: Text, note_type: Text) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Messages are generated when a note gets added to a ticket through
 | 
			
		||||
        Freshdesk's "Observer" service.
 | 
			
		||||
@@ -64,16 +60,13 @@ Priority: **High** => **Low**"""
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded",
 | 
			
		||||
                                          **self.api_auth(self.TEST_USER_EMAIL))
 | 
			
		||||
 | 
			
		||||
    def test_private_note_change(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_private_note_change(self) -> None:
 | 
			
		||||
        self.note_change("private_note", "private")
 | 
			
		||||
 | 
			
		||||
    def test_public_note_change(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_public_note_change(self) -> None:
 | 
			
		||||
        self.note_change("public_note", "public")
 | 
			
		||||
 | 
			
		||||
    def test_inline_image(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_inline_image(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Freshdesk sends us descriptions as HTML, so we have to make the
 | 
			
		||||
        descriptions Zulip markdown-friendly while still doing our best to
 | 
			
		||||
@@ -85,6 +78,5 @@ Priority: **High** => **Low**"""
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded",
 | 
			
		||||
                                          **self.api_auth(self.TEST_USER_EMAIL))
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Text
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Text:
 | 
			
		||||
        return self.fixture_data("freshdesk", fixture_name, file_type="json")
 | 
			
		||||
 
 | 
			
		||||
@@ -22,16 +22,14 @@ class TicketDict(dict):
 | 
			
		||||
    an object where each of the keys is an attribute for easy access.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __getattr__(self, field):
 | 
			
		||||
        # type: (str) -> Any
 | 
			
		||||
    def __getattr__(self, field: str) -> Any:
 | 
			
		||||
        if "_" in field:
 | 
			
		||||
            return self.get(field)
 | 
			
		||||
        else:
 | 
			
		||||
            return self.get("ticket_" + field)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def property_name(property, index):
 | 
			
		||||
    # type: (str, int) -> str
 | 
			
		||||
def property_name(property: str, index: int) -> str:
 | 
			
		||||
    """The Freshdesk API is currently pretty broken: statuses are customizable
 | 
			
		||||
    but the API will only tell you the number associated with the status, not
 | 
			
		||||
    the name. While we engage the Freshdesk developers about exposing this
 | 
			
		||||
@@ -50,8 +48,7 @@ def property_name(property, index):
 | 
			
		||||
        raise ValueError("Unknown property")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def parse_freshdesk_event(event_string):
 | 
			
		||||
    # type: (str) -> List[str]
 | 
			
		||||
def parse_freshdesk_event(event_string: str) -> List[str]:
 | 
			
		||||
    """These are always of the form "{ticket_action:created}" or
 | 
			
		||||
    "{status:{from:4,to:6}}". Note the lack of string quoting: this isn't
 | 
			
		||||
    valid JSON so we have to parse it ourselves.
 | 
			
		||||
@@ -70,8 +67,7 @@ def parse_freshdesk_event(event_string):
 | 
			
		||||
                property_name(property, int(to_state))]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def format_freshdesk_note_message(ticket, event_info):
 | 
			
		||||
    # type: (TicketDict, List[str]) -> str
 | 
			
		||||
def format_freshdesk_note_message(ticket: TicketDict, event_info: List[str]) -> str:
 | 
			
		||||
    """There are public (visible to customers) and private note types."""
 | 
			
		||||
    note_type = event_info[1]
 | 
			
		||||
    content = "%s <%s> added a %s note to [ticket #%s](%s)." % (
 | 
			
		||||
@@ -81,8 +77,7 @@ def format_freshdesk_note_message(ticket, event_info):
 | 
			
		||||
    return content
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def format_freshdesk_property_change_message(ticket, event_info):
 | 
			
		||||
    # type: (TicketDict, List[str]) -> str
 | 
			
		||||
def format_freshdesk_property_change_message(ticket: TicketDict, event_info: List[str]) -> str:
 | 
			
		||||
    """Freshdesk will only tell us the first event to match our webhook
 | 
			
		||||
    configuration, so if we change multiple properties, we only get the before
 | 
			
		||||
    and after data for the first one.
 | 
			
		||||
@@ -96,8 +91,7 @@ def format_freshdesk_property_change_message(ticket, event_info):
 | 
			
		||||
    return content
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def format_freshdesk_ticket_creation_message(ticket):
 | 
			
		||||
    # type: (TicketDict) -> str
 | 
			
		||||
def format_freshdesk_ticket_creation_message(ticket: TicketDict) -> str:
 | 
			
		||||
    """They send us the description as HTML."""
 | 
			
		||||
    cleaned_description = convert_html_to_markdown(ticket.description)
 | 
			
		||||
    content = "%s <%s> created [ticket #%s](%s):\n\n" % (
 | 
			
		||||
 
 | 
			
		||||
@@ -9,43 +9,37 @@ class GoogleCodeInTests(WebhookTestCase):
 | 
			
		||||
    URL_TEMPLATE = "/api/v1/external/gci?&api_key={api_key}&stream={stream}"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'gci'
 | 
			
		||||
 | 
			
		||||
    def test_abandon_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_abandon_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u'Task: Sails unspread it stopped at kearney'
 | 
			
		||||
        expected_message = u'**student-yqqtag** abandoned the task [Sails unspread it stopped at kearney](https://0.0.0.0:8000/dashboard/tasks/6694926301528064/).'
 | 
			
		||||
        self.send_and_test_stream_message('task_abandoned_by_student',
 | 
			
		||||
                                          expected_subject, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_comment_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_comment_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u'Task: Sails unspread it stopped at kearney'
 | 
			
		||||
        expected_message = u'**student-yqqtag** commented on the task [Sails unspread it stopped at kearney](https://0.0.0.0:8000/dashboard/tasks/6694926301528064/).'
 | 
			
		||||
        self.send_and_test_stream_message('student_commented_on_task',
 | 
			
		||||
                                          expected_subject, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_submit_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_submit_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u'Task: Sails unspread it stopped at kearney'
 | 
			
		||||
        expected_message = u'**student-yqqtag** submitted the task [Sails unspread it stopped at kearney](https://0.0.0.0:8000/dashboard/tasks/6694926301528064/).'
 | 
			
		||||
        self.send_and_test_stream_message('task_submitted_by_student',
 | 
			
		||||
                                          expected_subject, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_claim_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_claim_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u'Task: Sails unspread it stopped at kearney'
 | 
			
		||||
        expected_message = u'**student-yqqtag** claimed the task [Sails unspread it stopped at kearney](https://0.0.0.0:8000/dashboard/tasks/6694926301528064/).'
 | 
			
		||||
        self.send_and_test_stream_message('task_claimed_by_student',
 | 
			
		||||
                                          expected_subject, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_approve_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_approve_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u'Task: Sails unspread it stopped at kearney'
 | 
			
		||||
        expected_message = u'**eeshangarg** approved the task [Sails unspread it stopped at kearney](https://0.0.0.0:8000/dashboard/tasks/6694926301528064/).'
 | 
			
		||||
        self.send_and_test_stream_message('task_approved_by_mentor',
 | 
			
		||||
                                          expected_subject, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_needswork_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_needswork_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u'Task: Sails unspread it stopped at kearney'
 | 
			
		||||
        expected_message = u'**eeshangarg** submitted the task [Sails unspread it stopped at kearney](http://localhost:8080/dashboard/tasks/6051711999279104/) for more work.'
 | 
			
		||||
        self.send_and_test_stream_message('task_submitted_by_mentor_for_more_work',
 | 
			
		||||
 
 | 
			
		||||
@@ -15,8 +15,7 @@ GCI_SUBJECT_TEMPLATE = u'Task: {task_name}'
 | 
			
		||||
class UnknownEventType(Exception):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
def get_abandon_event_body(payload):
 | 
			
		||||
    # type: (Dict[Text, Any]) -> Text
 | 
			
		||||
def get_abandon_event_body(payload: Dict[Text, Any]) -> Text:
 | 
			
		||||
    return GCI_MESSAGE_TEMPLATE.format(
 | 
			
		||||
        actor=payload['task_claimed_by'],
 | 
			
		||||
        action='{}ed'.format(payload['event_type']),
 | 
			
		||||
@@ -24,8 +23,7 @@ def get_abandon_event_body(payload):
 | 
			
		||||
        task_url=payload['task_definition_url'],
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_submit_event_body(payload):
 | 
			
		||||
    # type: (Dict[Text, Any]) -> Text
 | 
			
		||||
def get_submit_event_body(payload: Dict[Text, Any]) -> Text:
 | 
			
		||||
    return GCI_MESSAGE_TEMPLATE.format(
 | 
			
		||||
        actor=payload['task_claimed_by'],
 | 
			
		||||
        action='{}ted'.format(payload['event_type']),
 | 
			
		||||
@@ -33,8 +31,7 @@ def get_submit_event_body(payload):
 | 
			
		||||
        task_url=payload['task_definition_url'],
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_comment_event_body(payload):
 | 
			
		||||
    # type: (Dict[Text, Any]) -> Text
 | 
			
		||||
def get_comment_event_body(payload: Dict[Text, Any]) -> Text:
 | 
			
		||||
    return GCI_MESSAGE_TEMPLATE.format(
 | 
			
		||||
        actor=payload['author'],
 | 
			
		||||
        action='{}ed on'.format(payload['event_type']),
 | 
			
		||||
@@ -42,8 +39,7 @@ def get_comment_event_body(payload):
 | 
			
		||||
        task_url=payload['task_definition_url'],
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_claim_event_body(payload):
 | 
			
		||||
    # type: (Dict[Text, Any]) -> Text
 | 
			
		||||
def get_claim_event_body(payload: Dict[Text, Any]) -> Text:
 | 
			
		||||
    return GCI_MESSAGE_TEMPLATE.format(
 | 
			
		||||
        actor=payload['task_claimed_by'],
 | 
			
		||||
        action='{}ed'.format(payload['event_type']),
 | 
			
		||||
@@ -51,8 +47,7 @@ def get_claim_event_body(payload):
 | 
			
		||||
        task_url=payload['task_definition_url'],
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_approve_event_body(payload):
 | 
			
		||||
    # type: (Dict[Text, Any]) -> Text
 | 
			
		||||
def get_approve_event_body(payload: Dict[Text, Any]) -> Text:
 | 
			
		||||
    return GCI_MESSAGE_TEMPLATE.format(
 | 
			
		||||
        actor=payload['author'],
 | 
			
		||||
        action='{}d'.format(payload['event_type']),
 | 
			
		||||
@@ -60,8 +55,7 @@ def get_approve_event_body(payload):
 | 
			
		||||
        task_url=payload['task_definition_url'],
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_needswork_event_body(payload):
 | 
			
		||||
    # type: (Dict[Text, Any]) -> Text
 | 
			
		||||
def get_needswork_event_body(payload: Dict[Text, Any]) -> Text:
 | 
			
		||||
    template = "{} for more work.".format(GCI_MESSAGE_TEMPLATE.rstrip('.'))
 | 
			
		||||
    return template.format(
 | 
			
		||||
        actor=payload['author'],
 | 
			
		||||
@@ -95,14 +89,12 @@ EVENTS_FUNCTION_MAPPER = {
 | 
			
		||||
    'submit': get_submit_event_body,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
def get_event(payload):
 | 
			
		||||
    # type: (Dict[Text, Any]) -> Optional[Text]
 | 
			
		||||
def get_event(payload: Dict[Text, Any]) -> Optional[Text]:
 | 
			
		||||
    event = payload['event_type']
 | 
			
		||||
    if event in EVENTS_FUNCTION_MAPPER:
 | 
			
		||||
        return event
 | 
			
		||||
 | 
			
		||||
    raise UnknownEventType(u"Event '{}' is unknown and cannot be handled".format(event))  # nocoverage
 | 
			
		||||
 | 
			
		||||
def get_body_based_on_event(event):
 | 
			
		||||
    # type: (Text) -> Any
 | 
			
		||||
def get_body_based_on_event(event: Text) -> Any:
 | 
			
		||||
    return EVENTS_FUNCTION_MAPPER[event]
 | 
			
		||||
 
 | 
			
		||||
@@ -18,8 +18,7 @@ class GithubV1HookTests(WebhookTestCase):
 | 
			
		||||
* Baz needs to be longer ([06ebe5f](https://github.com/zbenjamin/zulip-test/commit/06ebe5f472a32f6f31fd2a665f0c7442b69cce72))
 | 
			
		||||
* Final edit to baz, I swear ([b954491](https://github.com/zbenjamin/zulip-test/commit/b95449196980507f08209bdfdc4f1d611689b7a8))"""
 | 
			
		||||
 | 
			
		||||
    def test_spam_branch_is_ignored(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_spam_branch_is_ignored(self) -> None:
 | 
			
		||||
        self.SEND_STREAM = True
 | 
			
		||||
        self.STREAM_NAME = 'commits'
 | 
			
		||||
        self.BRANCHES = 'dev,staging'
 | 
			
		||||
@@ -38,8 +37,7 @@ class GithubV1HookTests(WebhookTestCase):
 | 
			
		||||
        after_count = Message.objects.count()
 | 
			
		||||
        self.assertEqual(prior_count, after_count)
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Dict[str, Text]
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Dict[str, Text]:
 | 
			
		||||
        api_key = self.test_user.api_key
 | 
			
		||||
        data = ujson.loads(self.fixture_data(self.FIXTURE_DIR_NAME, 'v1_' + fixture_name))
 | 
			
		||||
        data.update({'email': self.TEST_USER_EMAIL,
 | 
			
		||||
@@ -52,31 +50,28 @@ class GithubV1HookTests(WebhookTestCase):
 | 
			
		||||
            data['branches'] = self.BRANCHES
 | 
			
		||||
        return data
 | 
			
		||||
 | 
			
		||||
    def basic_test(self, fixture_name, stream_name, expected_subject, expected_content, send_stream=False, branches=None):
 | 
			
		||||
        # type: (Text, Text, Text, Text, bool, Optional[Text]) -> None
 | 
			
		||||
    def basic_test(self, fixture_name: Text, stream_name: Text,
 | 
			
		||||
                   expected_subject: Text, expected_content: Text,
 | 
			
		||||
                   send_stream: bool=False, branches: Optional[Text]=None) -> None:
 | 
			
		||||
        self.STREAM_NAME = stream_name
 | 
			
		||||
        self.SEND_STREAM = send_stream
 | 
			
		||||
        self.BRANCHES = branches
 | 
			
		||||
        self.send_and_test_stream_message(fixture_name, expected_subject, expected_content, content_type=None)
 | 
			
		||||
 | 
			
		||||
    def test_user_specified_branches(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_user_specified_branches(self) -> None:
 | 
			
		||||
        self.basic_test('push', 'my_commits', 'zulip-test / master', self.push_content,
 | 
			
		||||
                        send_stream=True, branches="master,staging")
 | 
			
		||||
 | 
			
		||||
    def test_user_specified_stream(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_user_specified_stream(self) -> None:
 | 
			
		||||
        """Around May 2013 the github webhook started to specify the stream.
 | 
			
		||||
        Before then, the stream was hard coded to "commits"."""
 | 
			
		||||
        self.basic_test('push', 'my_commits', 'zulip-test / master', self.push_content,
 | 
			
		||||
                        send_stream=True)
 | 
			
		||||
 | 
			
		||||
    def test_legacy_hook(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_legacy_hook(self) -> None:
 | 
			
		||||
        self.basic_test('push', 'commits', 'zulip-test / master', self.push_content)
 | 
			
		||||
 | 
			
		||||
    def test_push_multiple_commits(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_multiple_commits(self) -> None:
 | 
			
		||||
        commit_info = "* Add baz ([48c329a](https://github.com/zbenjamin/zulip-test/commit/48c329a0b68a9a379ff195ee3f1c1f4ab0b2a89e))\n"
 | 
			
		||||
        expected_subject = "zbenjamin [pushed](https://github.com/zbenjamin/zulip-test/compare/4f9adc4777d5...b95449196980) 50 commits to branch master.\n\n{}[and {} more commit(s)]".format(
 | 
			
		||||
            commit_info * COMMITS_LIMIT,
 | 
			
		||||
@@ -84,63 +79,53 @@ class GithubV1HookTests(WebhookTestCase):
 | 
			
		||||
        )
 | 
			
		||||
        self.basic_test('push_commits_more_than_limit', 'commits', 'zulip-test / master', expected_subject)
 | 
			
		||||
 | 
			
		||||
    def test_issues_opened(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_issues_opened(self) -> None:
 | 
			
		||||
        self.basic_test('issues_opened', 'issues',
 | 
			
		||||
                        "zulip-test / Issue #5 The frobnicator doesn't work",
 | 
			
		||||
                        "zbenjamin opened [Issue #5](https://github.com/zbenjamin/zulip-test/issues/5)\n\n~~~ quote\nI tried changing the widgets, but I got:\r\n\r\nPermission denied: widgets are immutable\n~~~")
 | 
			
		||||
 | 
			
		||||
    def test_issue_comment(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_issue_comment(self) -> None:
 | 
			
		||||
        self.basic_test('issue_comment', 'issues',
 | 
			
		||||
                        "zulip-test / Issue #5 The frobnicator doesn't work",
 | 
			
		||||
                        "zbenjamin [commented](https://github.com/zbenjamin/zulip-test/issues/5#issuecomment-23374280) on [Issue #5](https://github.com/zbenjamin/zulip-test/issues/5)\n\n~~~ quote\nWhoops, I did something wrong.\r\n\r\nI'm sorry.\n~~~")
 | 
			
		||||
 | 
			
		||||
    def test_issues_closed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_issues_closed(self) -> None:
 | 
			
		||||
        self.basic_test('issues_closed', 'issues',
 | 
			
		||||
                        "zulip-test / Issue #5 The frobnicator doesn't work",
 | 
			
		||||
                        "zbenjamin closed [Issue #5](https://github.com/zbenjamin/zulip-test/issues/5)")
 | 
			
		||||
 | 
			
		||||
    def test_pull_request_opened(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pull_request_opened(self) -> None:
 | 
			
		||||
        self.basic_test('pull_request_opened', 'commits',
 | 
			
		||||
                        "zulip-test / PR #7 Counting is hard.",
 | 
			
		||||
                        "lfaraone opened [PR #7](https://github.com/zbenjamin/zulip-test/pull/7)(assigned to lfaraone)\nfrom `patch-2` to `master`\n\n~~~ quote\nOmitted something I think?\n~~~")
 | 
			
		||||
 | 
			
		||||
    def test_pull_request_closed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pull_request_closed(self) -> None:
 | 
			
		||||
        self.basic_test('pull_request_closed', 'commits',
 | 
			
		||||
                        "zulip-test / PR #7 Counting is hard.",
 | 
			
		||||
                        "zbenjamin closed [PR #7](https://github.com/zbenjamin/zulip-test/pull/7)")
 | 
			
		||||
 | 
			
		||||
    def test_pull_request_synchronize(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pull_request_synchronize(self) -> None:
 | 
			
		||||
        self.basic_test('pull_request_synchronize', 'commits',
 | 
			
		||||
                        "zulip-test / PR #13 Even more cowbell.",
 | 
			
		||||
                        "zbenjamin synchronized [PR #13](https://github.com/zbenjamin/zulip-test/pull/13)")
 | 
			
		||||
 | 
			
		||||
    def test_pull_request_comment(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pull_request_comment(self) -> None:
 | 
			
		||||
        self.basic_test('pull_request_comment', 'commits',
 | 
			
		||||
                        "zulip-test / PR #9 Less cowbell.",
 | 
			
		||||
                        "zbenjamin [commented](https://github.com/zbenjamin/zulip-test/pull/9#issuecomment-24771110) on [PR #9](https://github.com/zbenjamin/zulip-test/pull/9)\n\n~~~ quote\nYeah, who really needs more cowbell than we already have?\n~~~")
 | 
			
		||||
 | 
			
		||||
    def test_pull_request_comment_user_specified_stream(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pull_request_comment_user_specified_stream(self) -> None:
 | 
			
		||||
        self.basic_test('pull_request_comment', 'my_commits',
 | 
			
		||||
                        "zulip-test / PR #9 Less cowbell.",
 | 
			
		||||
                        "zbenjamin [commented](https://github.com/zbenjamin/zulip-test/pull/9#issuecomment-24771110) on [PR #9](https://github.com/zbenjamin/zulip-test/pull/9)\n\n~~~ quote\nYeah, who really needs more cowbell than we already have?\n~~~",
 | 
			
		||||
                        send_stream=True)
 | 
			
		||||
 | 
			
		||||
    def test_commit_comment(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_commit_comment(self) -> None:
 | 
			
		||||
        self.basic_test('commit_comment', 'commits',
 | 
			
		||||
                        "zulip-test",
 | 
			
		||||
                        "zbenjamin [commented](https://github.com/zbenjamin/zulip-test/commit/7c994678d2f98797d299abed852d3ff9d0834533#commitcomment-4252302) on [7c99467](https://github.com/zbenjamin/zulip-test/commit/7c994678d2f98797d299abed852d3ff9d0834533)\n~~~ quote\nAre we sure this is enough cowbell?\n~~~")
 | 
			
		||||
 | 
			
		||||
    def test_commit_comment_line(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_commit_comment_line(self) -> None:
 | 
			
		||||
        self.basic_test('commit_comment_line', 'commits',
 | 
			
		||||
                        "zulip-test",
 | 
			
		||||
                        "zbenjamin [commented](https://github.com/zbenjamin/zulip-test/commit/7c994678d2f98797d299abed852d3ff9d0834533#commitcomment-4252307) on [7c99467](https://github.com/zbenjamin/zulip-test/commit/7c994678d2f98797d299abed852d3ff9d0834533)\n~~~ quote\nThis line adds /unlucky/ cowbell (because of its line number).  We should remove it.\n~~~")
 | 
			
		||||
@@ -158,8 +143,7 @@ class GithubV2HookTests(WebhookTestCase):
 | 
			
		||||
* Baz needs to be longer ([06ebe5f](https://github.com/zbenjamin/zulip-test/commit/06ebe5f472a32f6f31fd2a665f0c7442b69cce72))
 | 
			
		||||
* Final edit to baz, I swear ([b954491](https://github.com/zbenjamin/zulip-test/commit/b95449196980507f08209bdfdc4f1d611689b7a8))"""
 | 
			
		||||
 | 
			
		||||
    def test_spam_branch_is_ignored(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_spam_branch_is_ignored(self) -> None:
 | 
			
		||||
        self.SEND_STREAM = True
 | 
			
		||||
        self.STREAM_NAME = 'commits'
 | 
			
		||||
        self.BRANCHES = 'dev,staging'
 | 
			
		||||
@@ -178,8 +162,7 @@ class GithubV2HookTests(WebhookTestCase):
 | 
			
		||||
        after_count = Message.objects.count()
 | 
			
		||||
        self.assertEqual(prior_count, after_count)
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Dict[str, Text]
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Dict[str, Text]:
 | 
			
		||||
        api_key = self.test_user.api_key
 | 
			
		||||
        data = ujson.loads(self.fixture_data(self.FIXTURE_DIR_NAME, 'v2_' + fixture_name))
 | 
			
		||||
        data.update({'email': self.TEST_USER_EMAIL,
 | 
			
		||||
@@ -192,27 +175,25 @@ class GithubV2HookTests(WebhookTestCase):
 | 
			
		||||
            data['branches'] = self.BRANCHES
 | 
			
		||||
        return data
 | 
			
		||||
 | 
			
		||||
    def basic_test(self, fixture_name, stream_name, expected_subject, expected_content, send_stream=False, branches=None):
 | 
			
		||||
        # type: (Text, Text, Text, Text, bool, Optional[Text]) -> None
 | 
			
		||||
    def basic_test(self, fixture_name: Text, stream_name: Text,
 | 
			
		||||
                   expected_subject: Text, expected_content: Text,
 | 
			
		||||
                   send_stream: bool=False, branches: Optional[Text]=None) -> None:
 | 
			
		||||
        self.STREAM_NAME = stream_name
 | 
			
		||||
        self.SEND_STREAM = send_stream
 | 
			
		||||
        self.BRANCHES = branches
 | 
			
		||||
        self.send_and_test_stream_message(fixture_name, expected_subject, expected_content, content_type=None)
 | 
			
		||||
 | 
			
		||||
    def test_user_specified_branches(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_user_specified_branches(self) -> None:
 | 
			
		||||
        self.basic_test('push', 'my_commits', 'zulip-test / master', self.push_content,
 | 
			
		||||
                        send_stream=True, branches="master,staging")
 | 
			
		||||
 | 
			
		||||
    def test_user_specified_stream(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_user_specified_stream(self) -> None:
 | 
			
		||||
        """Around May 2013 the github webhook started to specify the stream.
 | 
			
		||||
        Before then, the stream was hard coded to "commits"."""
 | 
			
		||||
        self.basic_test('push', 'my_commits', 'zulip-test / master', self.push_content,
 | 
			
		||||
                        send_stream=True)
 | 
			
		||||
 | 
			
		||||
    def test_push_multiple_commits(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_multiple_commits(self) -> None:
 | 
			
		||||
        commit_info = "* Add baz ([48c329a](https://github.com/zbenjamin/zulip-test/commit/48c329a0b68a9a379ff195ee3f1c1f4ab0b2a89e))\n"
 | 
			
		||||
        expected_subject = "zbenjamin [pushed](https://github.com/zbenjamin/zulip-test/compare/4f9adc4777d5...b95449196980) 50 commits to branch master.\n\n{}[and {} more commit(s)]".format(
 | 
			
		||||
            commit_info * COMMITS_LIMIT,
 | 
			
		||||
@@ -220,80 +201,67 @@ class GithubV2HookTests(WebhookTestCase):
 | 
			
		||||
        )
 | 
			
		||||
        self.basic_test('push_commits_more_than_limit', 'commits', 'zulip-test / master', expected_subject)
 | 
			
		||||
 | 
			
		||||
    def test_push_multiple_committers(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_multiple_committers(self) -> None:
 | 
			
		||||
        commit_info = "* Add baz ([48c329a](https://github.com/zbenjamin/zulip-test/commit/48c329a0b68a9a379ff195ee3f1c1f4ab0b2a89e))\n"
 | 
			
		||||
        expected_subject = "zbenjamin [pushed](https://github.com/zbenjamin/zulip-test/compare/4f9adc4777d5...b95449196980) 6 commits to branch master. Commits by tomasz (3), baxthehacker (2) and zbenjamin (1).\n\n{}* Add baz ([48c329a](https://github.com/zbenjamin/zulip-test/commit/48c329a0b68a9a379ff195ee3f1c1f4ab0b2a89e))".format(commit_info * 5)
 | 
			
		||||
        self.basic_test('push_multiple_committers', 'commits', 'zulip-test / master', expected_subject)
 | 
			
		||||
 | 
			
		||||
    def test_push_multiple_committers_with_others(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_multiple_committers_with_others(self) -> None:
 | 
			
		||||
        commit_info = "* Final edit to baz, I swear ([b954491](https://github.com/zbenjamin/zulip-test/commit/b95449196980507f08209bdfdc4f1d611689b7a8))\n"
 | 
			
		||||
        expected_subject = "zbenjamin [pushed](https://github.com/zbenjamin/zulip-test/compare/4f9adc4777d5...b95449196980) 10 commits to branch master. Commits by baxthehacker (4), James (3), Tomasz (2) and others (1).\n\n{}* Final edit to baz, I swear ([b954491](https://github.com/zbenjamin/zulip-test/commit/b95449196980507f08209bdfdc4f1d611689b7a8))".format(commit_info * 9)
 | 
			
		||||
        self.basic_test('push_multiple_committers_with_others', 'commits', 'zulip-test / master', expected_subject)
 | 
			
		||||
 | 
			
		||||
    def test_legacy_hook(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_legacy_hook(self) -> None:
 | 
			
		||||
        self.basic_test('push', 'commits', 'zulip-test / master', self.push_content)
 | 
			
		||||
 | 
			
		||||
    def test_issues_opened(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_issues_opened(self) -> None:
 | 
			
		||||
        self.basic_test('issues_opened', 'issues',
 | 
			
		||||
                        "zulip-test / Issue #5 The frobnicator doesn't work",
 | 
			
		||||
                        "zbenjamin opened [Issue #5](https://github.com/zbenjamin/zulip-test/issues/5)\n\n~~~ quote\nI tried changing the widgets, but I got:\r\n\r\nPermission denied: widgets are immutable\n~~~")
 | 
			
		||||
 | 
			
		||||
    def test_issue_comment(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_issue_comment(self) -> None:
 | 
			
		||||
        self.basic_test('issue_comment', 'issues',
 | 
			
		||||
                        "zulip-test / Issue #5 The frobnicator doesn't work",
 | 
			
		||||
                        "zbenjamin [commented](https://github.com/zbenjamin/zulip-test/issues/5#issuecomment-23374280) on [Issue #5](https://github.com/zbenjamin/zulip-test/issues/5)\n\n~~~ quote\nWhoops, I did something wrong.\r\n\r\nI'm sorry.\n~~~")
 | 
			
		||||
 | 
			
		||||
    def test_issues_closed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_issues_closed(self) -> None:
 | 
			
		||||
        self.basic_test('issues_closed', 'issues',
 | 
			
		||||
                        "zulip-test / Issue #5 The frobnicator doesn't work",
 | 
			
		||||
                        "zbenjamin closed [Issue #5](https://github.com/zbenjamin/zulip-test/issues/5)")
 | 
			
		||||
 | 
			
		||||
    def test_pull_request_opened(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pull_request_opened(self) -> None:
 | 
			
		||||
        self.basic_test('pull_request_opened', 'commits',
 | 
			
		||||
                        "zulip-test / PR #7 Counting is hard.",
 | 
			
		||||
                        "lfaraone opened [PR #7](https://github.com/zbenjamin/zulip-test/pull/7)(assigned to lfaraone)\nfrom `patch-2` to `master`\n\n~~~ quote\nOmitted something I think?\n~~~")
 | 
			
		||||
 | 
			
		||||
    def test_pull_request_closed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pull_request_closed(self) -> None:
 | 
			
		||||
        self.basic_test('pull_request_closed', 'commits',
 | 
			
		||||
                        "zulip-test / PR #7 Counting is hard.",
 | 
			
		||||
                        "zbenjamin closed [PR #7](https://github.com/zbenjamin/zulip-test/pull/7)")
 | 
			
		||||
 | 
			
		||||
    def test_pull_request_synchronize(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pull_request_synchronize(self) -> None:
 | 
			
		||||
        self.basic_test('pull_request_synchronize', 'commits',
 | 
			
		||||
                        "zulip-test / PR #13 Even more cowbell.",
 | 
			
		||||
 | 
			
		||||
                        "zbenjamin synchronized [PR #13](https://github.com/zbenjamin/zulip-test/pull/13)")
 | 
			
		||||
 | 
			
		||||
    def test_pull_request_comment(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pull_request_comment(self) -> None:
 | 
			
		||||
        self.basic_test('pull_request_comment', 'commits',
 | 
			
		||||
                        "zulip-test / PR #9 Less cowbell.",
 | 
			
		||||
                        "zbenjamin [commented](https://github.com/zbenjamin/zulip-test/pull/9#issuecomment-24771110) on [PR #9](https://github.com/zbenjamin/zulip-test/pull/9)\n\n~~~ quote\nYeah, who really needs more cowbell than we already have?\n~~~")
 | 
			
		||||
 | 
			
		||||
    def test_pull_request_comment_user_specified_stream(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pull_request_comment_user_specified_stream(self) -> None:
 | 
			
		||||
        self.basic_test('pull_request_comment', 'my_commits',
 | 
			
		||||
                        "zulip-test / PR #9 Less cowbell.",
 | 
			
		||||
                        "zbenjamin [commented](https://github.com/zbenjamin/zulip-test/pull/9#issuecomment-24771110) on [PR #9](https://github.com/zbenjamin/zulip-test/pull/9)\n\n~~~ quote\nYeah, who really needs more cowbell than we already have?\n~~~",
 | 
			
		||||
                        send_stream=True)
 | 
			
		||||
 | 
			
		||||
    def test_commit_comment(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_commit_comment(self) -> None:
 | 
			
		||||
        self.basic_test('commit_comment', 'commits',
 | 
			
		||||
                        "zulip-test",
 | 
			
		||||
                        "zbenjamin [commented](https://github.com/zbenjamin/zulip-test/commit/7c994678d2f98797d299abed852d3ff9d0834533#commitcomment-4252302) on [7c99467](https://github.com/zbenjamin/zulip-test/commit/7c994678d2f98797d299abed852d3ff9d0834533)\n~~~ quote\nAre we sure this is enough cowbell?\n~~~")
 | 
			
		||||
 | 
			
		||||
    def test_commit_comment_line(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_commit_comment_line(self) -> None:
 | 
			
		||||
        self.basic_test('commit_comment_line', 'commits',
 | 
			
		||||
                        "zulip-test",
 | 
			
		||||
                        "zbenjamin [commented](https://github.com/zbenjamin/zulip-test/commit/7c994678d2f98797d299abed852d3ff9d0834533#commitcomment-4252307) on [7c99467](https://github.com/zbenjamin/zulip-test/commit/7c994678d2f98797d299abed852d3ff9d0834533)\n~~~ quote\nThis line adds /unlucky/ cowbell (because of its line number).  We should remove it.\n~~~")
 | 
			
		||||
 
 | 
			
		||||
@@ -22,15 +22,13 @@ from django.http import HttpRequest, HttpResponse
 | 
			
		||||
ZULIP_TEST_REPO_NAME = 'zulip-test'
 | 
			
		||||
ZULIP_TEST_REPO_ID = 6893087
 | 
			
		||||
 | 
			
		||||
def is_test_repository(repository):
 | 
			
		||||
    # type: (Mapping[Text, Any]) -> bool
 | 
			
		||||
def is_test_repository(repository: Mapping[Text, Any]) -> bool:
 | 
			
		||||
    return repository['name'] == ZULIP_TEST_REPO_NAME and repository['id'] == ZULIP_TEST_REPO_ID
 | 
			
		||||
 | 
			
		||||
class UnknownEventType(Exception):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
def github_pull_request_content(payload):
 | 
			
		||||
    # type: (Mapping[Text, Any]) -> Text
 | 
			
		||||
def github_pull_request_content(payload: Mapping[Text, Any]) -> Text:
 | 
			
		||||
    pull_request = payload['pull_request']
 | 
			
		||||
    action = get_pull_request_or_issue_action(payload)
 | 
			
		||||
 | 
			
		||||
@@ -52,8 +50,7 @@ def github_pull_request_content(payload):
 | 
			
		||||
        pull_request['number']
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def github_issues_content(payload):
 | 
			
		||||
    # type: (Mapping[Text, Any]) -> Text
 | 
			
		||||
def github_issues_content(payload: Mapping[Text, Any]) -> Text:
 | 
			
		||||
    issue = payload['issue']
 | 
			
		||||
    action = get_pull_request_or_issue_action(payload)
 | 
			
		||||
 | 
			
		||||
@@ -73,8 +70,7 @@ def github_issues_content(payload):
 | 
			
		||||
        issue['number'],
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def github_object_commented_content(payload, type):
 | 
			
		||||
    # type: (Mapping[Text, Any], Text) -> Text
 | 
			
		||||
def github_object_commented_content(payload: Mapping[Text, Any], type: Text) -> Text:
 | 
			
		||||
    comment = payload['comment']
 | 
			
		||||
    issue = payload['issue']
 | 
			
		||||
    action = u'[commented]({}) on'.format(comment['html_url'])
 | 
			
		||||
@@ -88,19 +84,18 @@ def github_object_commented_content(payload, type):
 | 
			
		||||
        type=type
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_pull_request_or_issue_action(payload):
 | 
			
		||||
    # type: (Mapping[Text, Any]) -> Text
 | 
			
		||||
def get_pull_request_or_issue_action(payload: Mapping[Text, Any]) -> Text:
 | 
			
		||||
    return 'synchronized' if payload['action'] == 'synchronize' else payload['action']
 | 
			
		||||
 | 
			
		||||
def get_pull_request_or_issue_assignee(object_payload):
 | 
			
		||||
    # type: (Mapping[Text, Any]) -> Optional[Text]
 | 
			
		||||
def get_pull_request_or_issue_assignee(object_payload: Mapping[Text, Any]) -> Optional[Text]:
 | 
			
		||||
    assignee_dict = object_payload.get('assignee')
 | 
			
		||||
    if assignee_dict:
 | 
			
		||||
        return assignee_dict.get('login')
 | 
			
		||||
    return None
 | 
			
		||||
 | 
			
		||||
def get_pull_request_or_issue_subject(repository, payload_object, type):
 | 
			
		||||
    # type: (Mapping[Text, Any], Mapping[Text, Any], Text) -> Text
 | 
			
		||||
def get_pull_request_or_issue_subject(repository: Mapping[Text, Any],
 | 
			
		||||
                                      payload_object: Mapping[Text, Any],
 | 
			
		||||
                                      type: Text) -> Text:
 | 
			
		||||
    return SUBJECT_WITH_PR_OR_ISSUE_INFO_TEMPLATE.format(
 | 
			
		||||
        repo=repository['name'],
 | 
			
		||||
        type=type,
 | 
			
		||||
@@ -108,13 +103,16 @@ def get_pull_request_or_issue_subject(repository, payload_object, type):
 | 
			
		||||
        title=payload_object['title']
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def github_generic_subject(noun, topic_focus, blob):
 | 
			
		||||
    # type: (Text, Text, Mapping[Text, Any]) -> Text
 | 
			
		||||
def github_generic_subject(noun: Text, topic_focus: Text, blob: Mapping[Text, Any]) -> Text:
 | 
			
		||||
    # issue and pull_request objects have the same fields we're interested in
 | 
			
		||||
    return u'%s: %s %d: %s' % (topic_focus, noun, blob['number'], blob['title'])
 | 
			
		||||
 | 
			
		||||
def api_github_v1(user_profile, event, payload, branches, stream, **kwargs):
 | 
			
		||||
    # type: (UserProfile, Text, Mapping[Text, Any], Text, Text, **Any) -> Tuple[Text, Text, Text]
 | 
			
		||||
def api_github_v1(user_profile: UserProfile,
 | 
			
		||||
                  event: Text,
 | 
			
		||||
                  payload: Mapping[Text, Any],
 | 
			
		||||
                  branches: Text,
 | 
			
		||||
                  stream: Text,
 | 
			
		||||
                  **kwargs: Any) -> Tuple[Text, Text, Text]:
 | 
			
		||||
    """
 | 
			
		||||
    processes github payload with version 1 field specification
 | 
			
		||||
    `payload` comes in unmodified from github
 | 
			
		||||
@@ -298,8 +296,7 @@ def build_message_from_gitlog(user_profile, name, ref, commits, before, after,
 | 
			
		||||
 | 
			
		||||
    return subject, content
 | 
			
		||||
 | 
			
		||||
def _transform_commits_list_to_common_format(commits):
 | 
			
		||||
    # type: (List[Dict[str, Any]]) -> List[Dict[str, str]]
 | 
			
		||||
def _transform_commits_list_to_common_format(commits: List[Dict[str, Any]]) -> List[Dict[str, str]]:
 | 
			
		||||
    new_commits_list = []
 | 
			
		||||
    for commit in commits:
 | 
			
		||||
        new_commits_list.append({
 | 
			
		||||
 
 | 
			
		||||
@@ -6,8 +6,7 @@ from .github.view import api_github_landing
 | 
			
		||||
# Since this dispatcher is an API-style endpoint, it needs to be
 | 
			
		||||
# explicitly marked as CSRF-exempt
 | 
			
		||||
@csrf_exempt
 | 
			
		||||
def api_github_webhook_dispatch(request):
 | 
			
		||||
    # type: (HttpRequest) -> HttpResponse
 | 
			
		||||
def api_github_webhook_dispatch(request: HttpRequest) -> HttpResponse:
 | 
			
		||||
    if request.META.get('HTTP_X_GITHUB_EVENT'):
 | 
			
		||||
        return api_github_webhook(request)
 | 
			
		||||
    else:
 | 
			
		||||
 
 | 
			
		||||
@@ -18,82 +18,69 @@ class GithubWebhookTest(WebhookTestCase):
 | 
			
		||||
    EXPECTED_SUBJECT_BRANCH_EVENTS = u"public-repo / changes"
 | 
			
		||||
    EXPECTED_SUBJECT_WIKI_EVENTS = u"public-repo / Wiki Pages"
 | 
			
		||||
 | 
			
		||||
    def test_ping_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_ping_event(self) -> None:
 | 
			
		||||
        expected_message = u"GitHub webhook has been successfully configured by TomaszKolek"
 | 
			
		||||
        self.send_and_test_stream_message('ping', self.EXPECTED_SUBJECT_REPO_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='ping')
 | 
			
		||||
 | 
			
		||||
    def test_ping_organization_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_ping_organization_event(self) -> None:
 | 
			
		||||
        expected_message = u"GitHub webhook has been successfully configured by eeshangarg"
 | 
			
		||||
        self.send_and_test_stream_message('ping_organization', 'zulip-test-org', expected_message, HTTP_X_GITHUB_EVENT='ping')
 | 
			
		||||
 | 
			
		||||
    def test_push_delete_branch(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_delete_branch(self) -> None:
 | 
			
		||||
        expected_message = u"eeshangarg [deleted](https://github.com/eeshangarg/public-repo/compare/2e8cf535fb38...000000000000) the branch feature."
 | 
			
		||||
        self.send_and_test_stream_message('push_delete_branch', u"public-repo / feature", expected_message, HTTP_X_GITHUB_EVENT='push')
 | 
			
		||||
 | 
			
		||||
    def test_push_local_branch_without_commits(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_local_branch_without_commits(self) -> None:
 | 
			
		||||
        expected_message = u"eeshangarg [pushed](https://github.com/eeshangarg/public-repo/compare/feature) the branch feature."
 | 
			
		||||
        self.send_and_test_stream_message('push_local_branch_without_commits', u"public-repo / feature", expected_message, HTTP_X_GITHUB_EVENT='push')
 | 
			
		||||
 | 
			
		||||
    def test_push_1_commit(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_1_commit(self) -> None:
 | 
			
		||||
        expected_message = u"baxterthehacker [pushed](https://github.com/baxterthehacker/public-repo/compare/9049f1265b7d...0d1a26e67d8f) 1 commit to branch changes.\n\n* Update README.md ([0d1a26e](https://github.com/baxterthehacker/public-repo/commit/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c))"
 | 
			
		||||
        self.send_and_test_stream_message('push_1_commit', self.EXPECTED_SUBJECT_BRANCH_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='push')
 | 
			
		||||
 | 
			
		||||
    def test_push_1_commit_without_username(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_1_commit_without_username(self) -> None:
 | 
			
		||||
        expected_message = u"eeshangarg [pushed](https://github.com/eeshangarg/public-repo/compare/0383613da871...2e8cf535fb38) 1 commit to branch changes. Commits by John Snow (1).\n\n* Update the README ([2e8cf53](https://github.com/eeshangarg/public-repo/commit/2e8cf535fb38a3dab2476cdf856efda904ad4c94))"
 | 
			
		||||
        self.send_and_test_stream_message('push_1_commit_without_username', self.EXPECTED_SUBJECT_BRANCH_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='push')
 | 
			
		||||
 | 
			
		||||
    def test_push_1_commit_filtered_by_branches(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_1_commit_filtered_by_branches(self) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url('master,changes')
 | 
			
		||||
        expected_message = u"baxterthehacker [pushed](https://github.com/baxterthehacker/public-repo/compare/9049f1265b7d...0d1a26e67d8f) 1 commit to branch changes.\n\n* Update README.md ([0d1a26e](https://github.com/baxterthehacker/public-repo/commit/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c))"
 | 
			
		||||
        self.send_and_test_stream_message('push_1_commit', self.EXPECTED_SUBJECT_BRANCH_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='push')
 | 
			
		||||
 | 
			
		||||
    def test_push_multiple_comitters(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_multiple_comitters(self) -> None:
 | 
			
		||||
        commits_info = u'* Update README.md ([0d1a26e](https://github.com/baxterthehacker/public-repo/commit/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c))\n'
 | 
			
		||||
        expected_message = u"""baxterthehacker [pushed](https://github.com/baxterthehacker/public-repo/compare/9049f1265b7d...0d1a26e67d8f) 6 commits to branch changes. Commits by Tomasz (3), Ben (2) and baxterthehacker (1).\n\n{}* Update README.md ([0d1a26e](https://github.com/baxterthehacker/public-repo/commit/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c))""".format(commits_info * 5)
 | 
			
		||||
 | 
			
		||||
        self.send_and_test_stream_message('push_multiple_committers', self.EXPECTED_SUBJECT_BRANCH_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='push')
 | 
			
		||||
 | 
			
		||||
    def test_push_multiple_comitters_with_others(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_multiple_comitters_with_others(self) -> None:
 | 
			
		||||
        commits_info = u'* Update README.md ([0d1a26e](https://github.com/baxterthehacker/public-repo/commit/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c))\n'
 | 
			
		||||
        expected_message = u"""baxterthehacker [pushed](https://github.com/baxterthehacker/public-repo/compare/9049f1265b7d...0d1a26e67d8f) 10 commits to branch changes. Commits by Tomasz (4), Ben (3), James (2) and others (1).\n\n{}* Update README.md ([0d1a26e](https://github.com/baxterthehacker/public-repo/commit/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c))""".format(commits_info * 9)
 | 
			
		||||
 | 
			
		||||
        self.send_and_test_stream_message('push_multiple_committers_with_others', self.EXPECTED_SUBJECT_BRANCH_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='push')
 | 
			
		||||
 | 
			
		||||
    def test_push_multiple_comitters_filtered_by_branches(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_multiple_comitters_filtered_by_branches(self) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url('master,changes')
 | 
			
		||||
        commits_info = u'* Update README.md ([0d1a26e](https://github.com/baxterthehacker/public-repo/commit/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c))\n'
 | 
			
		||||
        expected_message = u"""baxterthehacker [pushed](https://github.com/baxterthehacker/public-repo/compare/9049f1265b7d...0d1a26e67d8f) 6 commits to branch changes. Commits by Tomasz (3), Ben (2) and baxterthehacker (1).\n\n{}* Update README.md ([0d1a26e](https://github.com/baxterthehacker/public-repo/commit/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c))""".format(commits_info * 5)
 | 
			
		||||
 | 
			
		||||
        self.send_and_test_stream_message('push_multiple_committers', self.EXPECTED_SUBJECT_BRANCH_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='push')
 | 
			
		||||
 | 
			
		||||
    def test_push_multiple_comitters_with_others_filtered_by_branches(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_multiple_comitters_with_others_filtered_by_branches(self) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url('master,changes')
 | 
			
		||||
        commits_info = u'* Update README.md ([0d1a26e](https://github.com/baxterthehacker/public-repo/commit/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c))\n'
 | 
			
		||||
        expected_message = u"""baxterthehacker [pushed](https://github.com/baxterthehacker/public-repo/compare/9049f1265b7d...0d1a26e67d8f) 10 commits to branch changes. Commits by Tomasz (4), Ben (3), James (2) and others (1).\n\n{}* Update README.md ([0d1a26e](https://github.com/baxterthehacker/public-repo/commit/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c))""".format(commits_info * 9)
 | 
			
		||||
 | 
			
		||||
        self.send_and_test_stream_message('push_multiple_committers_with_others', self.EXPECTED_SUBJECT_BRANCH_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='push')
 | 
			
		||||
 | 
			
		||||
    def test_push_50_commits(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_50_commits(self) -> None:
 | 
			
		||||
        commit_info = "* Update README.md ([0d1a26e](https://github.com/baxterthehacker/public-repo/commit/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c))\n"
 | 
			
		||||
        expected_message = u"baxterthehacker [pushed](https://github.com/baxterthehacker/public-repo/compare/9049f1265b7d...0d1a26e67d8f) 50 commits to branch changes.\n\n{}[and 30 more commit(s)]".format(
 | 
			
		||||
            commit_info * COMMITS_LIMIT
 | 
			
		||||
        )
 | 
			
		||||
        self.send_and_test_stream_message('push_50_commits', self.EXPECTED_SUBJECT_BRANCH_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='push')
 | 
			
		||||
 | 
			
		||||
    def test_push_50_commits_filtered_by_branches(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_50_commits_filtered_by_branches(self) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url(branches='master,changes')
 | 
			
		||||
        commit_info = "* Update README.md ([0d1a26e](https://github.com/baxterthehacker/public-repo/commit/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c))\n"
 | 
			
		||||
        expected_message = u"baxterthehacker [pushed](https://github.com/baxterthehacker/public-repo/compare/9049f1265b7d...0d1a26e67d8f) 50 commits to branch changes.\n\n{}[and 30 more commit(s)]".format(
 | 
			
		||||
@@ -101,145 +88,117 @@ class GithubWebhookTest(WebhookTestCase):
 | 
			
		||||
        )
 | 
			
		||||
        self.send_and_test_stream_message('push_50_commits', self.EXPECTED_SUBJECT_BRANCH_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='push')
 | 
			
		||||
 | 
			
		||||
    def test_commit_comment_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_commit_comment_msg(self) -> None:
 | 
			
		||||
        expected_message = u"baxterthehacker [commented](https://github.com/baxterthehacker/public-repo/commit/9049f1265b7d61be4a8904a9a27120d2064dab3b#commitcomment-11056394) on [9049f12](https://github.com/baxterthehacker/public-repo/commit/9049f1265b7d61be4a8904a9a27120d2064dab3b)\n~~~ quote\nThis is a really good change! :+1:\n~~~"
 | 
			
		||||
        self.send_and_test_stream_message('commit_comment', self.EXPECTED_SUBJECT_REPO_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='commit_comment')
 | 
			
		||||
 | 
			
		||||
    def test_create_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_create_msg(self) -> None:
 | 
			
		||||
        expected_message = u"baxterthehacker created tag 0.0.1"
 | 
			
		||||
        self.send_and_test_stream_message('create', self.EXPECTED_SUBJECT_REPO_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='create')
 | 
			
		||||
 | 
			
		||||
    def test_delete_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_delete_msg(self) -> None:
 | 
			
		||||
        expected_message = u"baxterthehacker deleted tag simple-tag"
 | 
			
		||||
        self.send_and_test_stream_message('delete', self.EXPECTED_SUBJECT_REPO_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='delete')
 | 
			
		||||
 | 
			
		||||
    def test_deployment_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_deployment_msg(self) -> None:
 | 
			
		||||
        expected_message = u"baxterthehacker created new deployment"
 | 
			
		||||
        self.send_and_test_stream_message('deployment', self.EXPECTED_SUBJECT_DEPLOYMENT_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='deployment')
 | 
			
		||||
 | 
			
		||||
    def test_deployment_status_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_deployment_status_msg(self) -> None:
 | 
			
		||||
        expected_message = u"Deployment changed status to success"
 | 
			
		||||
        self.send_and_test_stream_message('deployment_status', self.EXPECTED_SUBJECT_DEPLOYMENT_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='deployment_status')
 | 
			
		||||
 | 
			
		||||
    def test_fork_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_fork_msg(self) -> None:
 | 
			
		||||
        expected_message = u"baxterandthehackers forked [public-repo](https://github.com/baxterandthehackers/public-repo)"
 | 
			
		||||
        self.send_and_test_stream_message('fork', self.EXPECTED_SUBJECT_REPO_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='fork')
 | 
			
		||||
 | 
			
		||||
    def test_issue_comment_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_issue_comment_msg(self) -> None:
 | 
			
		||||
        expected_message = u"baxterthehacker [commented](https://github.com/baxterthehacker/public-repo/issues/2#issuecomment-99262140) on [Issue #2](https://github.com/baxterthehacker/public-repo/issues/2)\n\n~~~ quote\nYou are totally right! I'll get this fixed right away.\n~~~"
 | 
			
		||||
        self.send_and_test_stream_message('issue_comment', self.EXPECTED_SUBJECT_ISSUE_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='issue_comment')
 | 
			
		||||
 | 
			
		||||
    def test_issue_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_issue_msg(self) -> None:
 | 
			
		||||
        expected_message = u"baxterthehacker opened [Issue #2](https://github.com/baxterthehacker/public-repo/issues/2)\n\n~~~ quote\nIt looks like you accidently spelled 'commit' with two 't's.\n~~~"
 | 
			
		||||
        self.send_and_test_stream_message('issue', self.EXPECTED_SUBJECT_ISSUE_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='issues')
 | 
			
		||||
 | 
			
		||||
    def test_membership_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_membership_msg(self) -> None:
 | 
			
		||||
        expected_message = u"baxterthehacker added [kdaigle](https://github.com/kdaigle) to Contractors team"
 | 
			
		||||
        self.send_and_test_stream_message('membership', self.EXPECTED_SUBJECT_ORGANIZATION_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='membership')
 | 
			
		||||
 | 
			
		||||
    def test_member_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_member_msg(self) -> None:
 | 
			
		||||
        expected_message = u"baxterthehacker added [octocat](https://github.com/octocat) to [public-repo](https://github.com/baxterthehacker/public-repo)"
 | 
			
		||||
        self.send_and_test_stream_message('member', self.EXPECTED_SUBJECT_REPO_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='member')
 | 
			
		||||
 | 
			
		||||
    def test_pull_request_opened_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pull_request_opened_msg(self) -> None:
 | 
			
		||||
        expected_message = u"baxterthehacker opened [PR](https://github.com/baxterthehacker/public-repo/pull/1)\nfrom `changes` to `master`\n\n~~~ quote\nThis is a pretty simple change that we need to pull into master.\n~~~"
 | 
			
		||||
        self.send_and_test_stream_message('opened_pull_request', self.EXPECTED_SUBJECT_PR_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='pull_request')
 | 
			
		||||
 | 
			
		||||
    def test_pull_request_synchronized_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pull_request_synchronized_msg(self) -> None:
 | 
			
		||||
        expected_message = u"baxterthehacker updated [PR](https://github.com/baxterthehacker/public-repo/pull/1)\nfrom `changes` to `master`"
 | 
			
		||||
        self.send_and_test_stream_message('synchronized_pull_request', self.EXPECTED_SUBJECT_PR_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='pull_request')
 | 
			
		||||
 | 
			
		||||
    def test_pull_request_closed_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pull_request_closed_msg(self) -> None:
 | 
			
		||||
        expected_message = u"baxterthehacker closed without merge [PR](https://github.com/baxterthehacker/public-repo/pull/1)"
 | 
			
		||||
        self.send_and_test_stream_message('closed_pull_request', self.EXPECTED_SUBJECT_PR_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='pull_request')
 | 
			
		||||
 | 
			
		||||
    def test_pull_request_merged_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pull_request_merged_msg(self) -> None:
 | 
			
		||||
        expected_message = u"baxterthehacker merged [PR](https://github.com/baxterthehacker/public-repo/pull/1)"
 | 
			
		||||
        self.send_and_test_stream_message('merged_pull_request', self.EXPECTED_SUBJECT_PR_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='pull_request')
 | 
			
		||||
 | 
			
		||||
    def test_public_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_public_msg(self) -> None:
 | 
			
		||||
        expected_message = u"baxterthehacker made [the repository](https://github.com/baxterthehacker/public-repo) public"
 | 
			
		||||
        self.send_and_test_stream_message('public', self.EXPECTED_SUBJECT_REPO_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='public')
 | 
			
		||||
 | 
			
		||||
    def test_wiki_pages_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_wiki_pages_msg(self) -> None:
 | 
			
		||||
        expected_message = u"jasonrudolph:\n* created [Home](https://github.com/baxterthehacker/public-repo/wiki/Home)\n* created [Home](https://github.com/baxterthehacker/public-repo/wiki/Home)"
 | 
			
		||||
        self.send_and_test_stream_message('wiki_pages', self.EXPECTED_SUBJECT_WIKI_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='gollum')
 | 
			
		||||
 | 
			
		||||
    def test_watch_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_watch_msg(self) -> None:
 | 
			
		||||
        expected_message = u"baxterthehacker starred [the repository](https://github.com/baxterthehacker/public-repo)"
 | 
			
		||||
        self.send_and_test_stream_message('watch_repository', self.EXPECTED_SUBJECT_REPO_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='watch')
 | 
			
		||||
 | 
			
		||||
    def test_repository_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_repository_msg(self) -> None:
 | 
			
		||||
        expected_message = u"baxterthehacker created [the repository](https://github.com/baxterandthehackers/public-repo)"
 | 
			
		||||
        self.send_and_test_stream_message('repository', self.EXPECTED_SUBJECT_REPO_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='repository')
 | 
			
		||||
 | 
			
		||||
    def test_team_add_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_team_add_msg(self) -> None:
 | 
			
		||||
        expected_message = u"[The repository](https://github.com/baxterandthehackers/public-repo) was added to team github"
 | 
			
		||||
        self.send_and_test_stream_message('team_add', self.EXPECTED_SUBJECT_REPO_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='team_add')
 | 
			
		||||
 | 
			
		||||
    def test_release_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_release_msg(self) -> None:
 | 
			
		||||
        expected_message = u"baxterthehacker published [the release](https://github.com/baxterthehacker/public-repo/releases/tag/0.0.1)"
 | 
			
		||||
        self.send_and_test_stream_message('release', self.EXPECTED_SUBJECT_REPO_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='release')
 | 
			
		||||
 | 
			
		||||
    def test_page_build_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_page_build_msg(self) -> None:
 | 
			
		||||
        expected_message = u"Github Pages build, trigerred by baxterthehacker, is built"
 | 
			
		||||
        self.send_and_test_stream_message('page_build', self.EXPECTED_SUBJECT_REPO_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='page_build')
 | 
			
		||||
 | 
			
		||||
    def test_status_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_status_msg(self) -> None:
 | 
			
		||||
        expected_message = u"[9049f12](https://github.com/baxterthehacker/public-repo/commit/9049f1265b7d61be4a8904a9a27120d2064dab3b) changed its status to success"
 | 
			
		||||
        self.send_and_test_stream_message('status', self.EXPECTED_SUBJECT_REPO_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='status')
 | 
			
		||||
 | 
			
		||||
    def test_pull_request_review_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pull_request_review_msg(self) -> None:
 | 
			
		||||
        expected_message = u"baxterthehacker submitted [PR Review](https://github.com/baxterthehacker/public-repo/pull/1#pullrequestreview-2626884)"
 | 
			
		||||
        self.send_and_test_stream_message('pull_request_review', self.EXPECTED_SUBJECT_PR_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='pull_request_review')
 | 
			
		||||
 | 
			
		||||
    def test_pull_request_review_comment_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pull_request_review_comment_msg(self) -> None:
 | 
			
		||||
        expected_message = u"baxterthehacker created [PR Review Comment](https://github.com/baxterthehacker/public-repo/pull/1#discussion_r29724692)\n\n~~~ quote\nMaybe you should use more emojji on this line.\n~~~"
 | 
			
		||||
        self.send_and_test_stream_message('pull_request_review_comment', self.EXPECTED_SUBJECT_PR_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='pull_request_review_comment')
 | 
			
		||||
 | 
			
		||||
    def test_push_tag_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_tag_msg(self) -> None:
 | 
			
		||||
        expected_message = u"baxterthehacker pushed tag abc"
 | 
			
		||||
        self.send_and_test_stream_message('push_tag', self.EXPECTED_SUBJECT_REPO_EVENTS, expected_message, HTTP_X_GITHUB_EVENT='push')
 | 
			
		||||
 | 
			
		||||
    def test_pull_request_edited_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pull_request_edited_msg(self) -> None:
 | 
			
		||||
        expected_message = u"baxterthehacker edited [PR](https://github.com/baxterthehacker/public-repo/pull/1)\nfrom `changes` to `master`"
 | 
			
		||||
        self.send_and_test_stream_message('edited_pull_request', self.EXPECTED_SUBJECT_PR_EVENTS, expected_message,
 | 
			
		||||
                                          HTTP_X_GITHUB_EVENT='pull_request')
 | 
			
		||||
 | 
			
		||||
    def test_pull_request_assigned_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pull_request_assigned_msg(self) -> None:
 | 
			
		||||
        expected_message = u"baxterthehacker assigned [PR](https://github.com/baxterthehacker/public-repo/pull/1) to baxterthehacker"
 | 
			
		||||
        self.send_and_test_stream_message('assigned_pull_request', self.EXPECTED_SUBJECT_PR_EVENTS, expected_message,
 | 
			
		||||
                                          HTTP_X_GITHUB_EVENT='pull_request')
 | 
			
		||||
 | 
			
		||||
    def test_pull_request_unassigned_msg(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pull_request_unassigned_msg(self) -> None:
 | 
			
		||||
        expected_message = u"eeshangarg unassigned [PR](https://github.com/zulip-test-org/helloworld/pull/1)"
 | 
			
		||||
        self.send_and_test_stream_message(
 | 
			
		||||
            'unassigned_pull_request',
 | 
			
		||||
@@ -249,40 +208,40 @@ class GithubWebhookTest(WebhookTestCase):
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    @patch('zerver.webhooks.github_webhook.view.check_send_stream_message')
 | 
			
		||||
    def test_pull_request_labeled_ignore(self, check_send_stream_message_mock):
 | 
			
		||||
        # type: (MagicMock) -> None
 | 
			
		||||
    def test_pull_request_labeled_ignore(
 | 
			
		||||
            self, check_send_stream_message_mock: MagicMock) -> None:
 | 
			
		||||
        payload = self.get_body('labeled_pull_request')
 | 
			
		||||
        result = self.client_post(self.url, payload, HTTP_X_GITHUB_EVENT='pull_request', content_type="application/json")
 | 
			
		||||
        self.assertFalse(check_send_stream_message_mock.called)
 | 
			
		||||
        self.assert_json_success(result)
 | 
			
		||||
 | 
			
		||||
    @patch('zerver.webhooks.github_webhook.view.check_send_stream_message')
 | 
			
		||||
    def test_pull_request_unlabeled_ignore(self, check_send_stream_message_mock):
 | 
			
		||||
        # type: (MagicMock) -> None
 | 
			
		||||
    def test_pull_request_unlabeled_ignore(
 | 
			
		||||
            self, check_send_stream_message_mock: MagicMock) -> None:
 | 
			
		||||
        payload = self.get_body('unlabeled_pull_request')
 | 
			
		||||
        result = self.client_post(self.url, payload, HTTP_X_GITHUB_EVENT='pull_request', content_type="application/json")
 | 
			
		||||
        self.assertFalse(check_send_stream_message_mock.called)
 | 
			
		||||
        self.assert_json_success(result)
 | 
			
		||||
 | 
			
		||||
    @patch('zerver.webhooks.github_webhook.view.check_send_stream_message')
 | 
			
		||||
    def test_pull_request_request_review_ignore(self, check_send_stream_message_mock):
 | 
			
		||||
        # type: (MagicMock) -> None
 | 
			
		||||
    def test_pull_request_request_review_ignore(
 | 
			
		||||
            self, check_send_stream_message_mock: MagicMock) -> None:
 | 
			
		||||
        payload = self.get_body('request_review_pull_request')
 | 
			
		||||
        result = self.client_post(self.url, payload, HTTP_X_GITHUB_EVENT='pull_request', content_type="application/json")
 | 
			
		||||
        self.assertFalse(check_send_stream_message_mock.called)
 | 
			
		||||
        self.assert_json_success(result)
 | 
			
		||||
 | 
			
		||||
    @patch('zerver.webhooks.github_webhook.view.check_send_stream_message')
 | 
			
		||||
    def test_pull_request_request_review_remove_ignore(self, check_send_stream_message_mock):
 | 
			
		||||
        # type: (MagicMock) -> None
 | 
			
		||||
    def test_pull_request_request_review_remove_ignore(
 | 
			
		||||
            self, check_send_stream_message_mock: MagicMock) -> None:
 | 
			
		||||
        payload = self.get_body('request_review_removed_pull_request')
 | 
			
		||||
        result = self.client_post(self.url, payload, HTTP_X_GITHUB_EVENT='pull_request', content_type="application/json")
 | 
			
		||||
        self.assertFalse(check_send_stream_message_mock.called)
 | 
			
		||||
        self.assert_json_success(result)
 | 
			
		||||
 | 
			
		||||
    @patch('zerver.webhooks.github_webhook.view.check_send_stream_message')
 | 
			
		||||
    def test_push_1_commit_filtered_by_branches_ignore(self, check_send_stream_message_mock):
 | 
			
		||||
        # type: (MagicMock) -> None
 | 
			
		||||
    def test_push_1_commit_filtered_by_branches_ignore(
 | 
			
		||||
            self, check_send_stream_message_mock: MagicMock) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url(branches='master,development')
 | 
			
		||||
        payload = self.get_body('push_1_commit')
 | 
			
		||||
        result = self.client_post(self.url, payload, HTTP_X_GITHUB_EVENT='push', content_type="application/json")
 | 
			
		||||
@@ -290,8 +249,8 @@ class GithubWebhookTest(WebhookTestCase):
 | 
			
		||||
        self.assert_json_success(result)
 | 
			
		||||
 | 
			
		||||
    @patch('zerver.webhooks.github_webhook.view.check_send_stream_message')
 | 
			
		||||
    def test_push_50_commits_filtered_by_branches_ignore(self, check_send_stream_message_mock):
 | 
			
		||||
        # type: (MagicMock) -> None
 | 
			
		||||
    def test_push_50_commits_filtered_by_branches_ignore(
 | 
			
		||||
            self, check_send_stream_message_mock: MagicMock) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url(branches='master,development')
 | 
			
		||||
        payload = self.get_body('push_50_commits')
 | 
			
		||||
        result = self.client_post(self.url, payload, HTTP_X_GITHUB_EVENT='push', content_type="application/json")
 | 
			
		||||
@@ -299,8 +258,8 @@ class GithubWebhookTest(WebhookTestCase):
 | 
			
		||||
        self.assert_json_success(result)
 | 
			
		||||
 | 
			
		||||
    @patch('zerver.webhooks.github_webhook.view.check_send_stream_message')
 | 
			
		||||
    def test_push_multiple_comitters_filtered_by_branches_ignore(self, check_send_stream_message_mock):
 | 
			
		||||
        # type: (MagicMock) -> None
 | 
			
		||||
    def test_push_multiple_comitters_filtered_by_branches_ignore(
 | 
			
		||||
            self, check_send_stream_message_mock: MagicMock) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url(branches='master,development')
 | 
			
		||||
        payload = self.get_body('push_multiple_committers')
 | 
			
		||||
        result = self.client_post(self.url, payload, HTTP_X_GITHUB_EVENT='push', content_type="application/json")
 | 
			
		||||
@@ -308,8 +267,8 @@ class GithubWebhookTest(WebhookTestCase):
 | 
			
		||||
        self.assert_json_success(result)
 | 
			
		||||
 | 
			
		||||
    @patch('zerver.webhooks.github_webhook.view.check_send_stream_message')
 | 
			
		||||
    def test_push_multiple_comitters_with_others_filtered_by_branches_ignore(self, check_send_stream_message_mock):
 | 
			
		||||
        # type: (MagicMock) -> None
 | 
			
		||||
    def test_push_multiple_comitters_with_others_filtered_by_branches_ignore(
 | 
			
		||||
            self, check_send_stream_message_mock: MagicMock) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url(branches='master,development')
 | 
			
		||||
        payload = self.get_body('push_multiple_committers_with_others')
 | 
			
		||||
        result = self.client_post(self.url, payload, HTTP_X_GITHUB_EVENT='push', content_type="application/json")
 | 
			
		||||
 
 | 
			
		||||
@@ -18,8 +18,7 @@ from zerver.lib.webhooks.git import get_issue_event_message, SUBJECT_WITH_PR_OR_
 | 
			
		||||
class UnknownEventType(Exception):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
def get_opened_or_update_pull_request_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_opened_or_update_pull_request_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    pull_request = payload['pull_request']
 | 
			
		||||
    action = payload['action']
 | 
			
		||||
    if action == 'synchronize':
 | 
			
		||||
@@ -38,8 +37,7 @@ def get_opened_or_update_pull_request_body(payload):
 | 
			
		||||
        assignee=assignee
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_assigned_or_unassigned_pull_request_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_assigned_or_unassigned_pull_request_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    pull_request = payload['pull_request']
 | 
			
		||||
    assignee = pull_request.get('assignee')
 | 
			
		||||
    if assignee is not None:
 | 
			
		||||
@@ -54,8 +52,7 @@ def get_assigned_or_unassigned_pull_request_body(payload):
 | 
			
		||||
        return "{} to {}".format(base_message, assignee)
 | 
			
		||||
    return base_message
 | 
			
		||||
 | 
			
		||||
def get_closed_pull_request_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_closed_pull_request_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    pull_request = payload['pull_request']
 | 
			
		||||
    action = 'merged' if pull_request['merged'] else 'closed without merge'
 | 
			
		||||
    return get_pull_request_event_message(
 | 
			
		||||
@@ -64,8 +61,7 @@ def get_closed_pull_request_body(payload):
 | 
			
		||||
        pull_request['html_url'],
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_membership_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_membership_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    action = payload['action']
 | 
			
		||||
    member = payload['member']
 | 
			
		||||
    scope = payload['scope']
 | 
			
		||||
@@ -80,8 +76,7 @@ def get_membership_body(payload):
 | 
			
		||||
        scope
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_member_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_member_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return u"{} {} [{}]({}) to [{}]({})".format(
 | 
			
		||||
        get_sender_name(payload),
 | 
			
		||||
        payload['action'],
 | 
			
		||||
@@ -91,8 +86,7 @@ def get_member_body(payload):
 | 
			
		||||
        payload['repository']['html_url']
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_issue_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_issue_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    action = payload['action']
 | 
			
		||||
    issue = payload['issue']
 | 
			
		||||
    assignee = issue['assignee']
 | 
			
		||||
@@ -105,8 +99,7 @@ def get_issue_body(payload):
 | 
			
		||||
        assignee=assignee['login'] if assignee else None
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_issue_comment_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_issue_comment_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    action = payload['action']
 | 
			
		||||
    comment = payload['comment']
 | 
			
		||||
    issue = payload['issue']
 | 
			
		||||
@@ -125,8 +118,7 @@ def get_issue_comment_body(payload):
 | 
			
		||||
        comment['body'],
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_fork_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_fork_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    forkee = payload['forkee']
 | 
			
		||||
    return u"{} forked [{}]({})".format(
 | 
			
		||||
        get_sender_name(payload),
 | 
			
		||||
@@ -134,20 +126,17 @@ def get_fork_body(payload):
 | 
			
		||||
        forkee['html_url']
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_deployment_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_deployment_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return u'{} created new deployment'.format(
 | 
			
		||||
        get_sender_name(payload),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_change_deployment_status_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_change_deployment_status_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return u'Deployment changed status to {}'.format(
 | 
			
		||||
        payload['deployment_status']['state'],
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_create_or_delete_body(payload, action):
 | 
			
		||||
    # type: (Dict[str, Any], Text) -> Text
 | 
			
		||||
def get_create_or_delete_body(payload: Dict[str, Any], action: Text) -> Text:
 | 
			
		||||
    ref_type = payload['ref_type']
 | 
			
		||||
    return u'{} {} {} {}'.format(
 | 
			
		||||
        get_sender_name(payload),
 | 
			
		||||
@@ -156,8 +145,7 @@ def get_create_or_delete_body(payload, action):
 | 
			
		||||
        payload['ref']
 | 
			
		||||
    ).rstrip()
 | 
			
		||||
 | 
			
		||||
def get_commit_comment_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_commit_comment_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    comment = payload['comment']
 | 
			
		||||
    comment_url = comment['html_url']
 | 
			
		||||
    commit_url = comment_url.split('#', 1)[0]
 | 
			
		||||
@@ -170,16 +158,14 @@ def get_commit_comment_body(payload):
 | 
			
		||||
        comment['body'],
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_push_tags_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_push_tags_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return get_push_tag_event_message(
 | 
			
		||||
        get_sender_name(payload),
 | 
			
		||||
        get_tag_name_from_ref(payload['ref']),
 | 
			
		||||
        action='pushed' if payload.get('created') else 'removed'
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_push_commits_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_push_commits_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    commits_data = [{
 | 
			
		||||
        'name': (commit.get('author').get('username') or
 | 
			
		||||
                 commit.get('author').get('name')),
 | 
			
		||||
@@ -195,15 +181,13 @@ def get_push_commits_body(payload):
 | 
			
		||||
        deleted=payload['deleted']
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_public_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_public_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return u"{} made [the repository]({}) public".format(
 | 
			
		||||
        get_sender_name(payload),
 | 
			
		||||
        payload['repository']['html_url'],
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_wiki_pages_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_wiki_pages_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    wiki_page_info_template = u"* {action} [{title}]({url})\n"
 | 
			
		||||
    wiki_info = u''
 | 
			
		||||
    for page in payload['pages']:
 | 
			
		||||
@@ -214,37 +198,32 @@ def get_wiki_pages_body(payload):
 | 
			
		||||
        )
 | 
			
		||||
    return u"{}:\n{}".format(get_sender_name(payload), wiki_info.rstrip())
 | 
			
		||||
 | 
			
		||||
def get_watch_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_watch_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return u"{} starred [the repository]({})".format(
 | 
			
		||||
        get_sender_name(payload),
 | 
			
		||||
        payload['repository']['html_url']
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_repository_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_repository_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return u"{} {} [the repository]({})".format(
 | 
			
		||||
        get_sender_name(payload),
 | 
			
		||||
        payload.get('action'),
 | 
			
		||||
        payload['repository']['html_url']
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_add_team_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_add_team_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return u"[The repository]({}) was added to team {}".format(
 | 
			
		||||
        payload['repository']['html_url'],
 | 
			
		||||
        payload['team']['name']
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_release_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_release_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return u"{} published [the release]({})".format(
 | 
			
		||||
        get_sender_name(payload),
 | 
			
		||||
        payload['release']['html_url'],
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_page_build_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_page_build_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    build = payload['build']
 | 
			
		||||
    action = build['status']
 | 
			
		||||
    if action == 'null':
 | 
			
		||||
@@ -262,8 +241,7 @@ def get_page_build_body(payload):
 | 
			
		||||
        action
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_status_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_status_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    if payload['target_url']:
 | 
			
		||||
        status = '[{}]({})'.format(
 | 
			
		||||
            payload['state'],
 | 
			
		||||
@@ -277,8 +255,7 @@ def get_status_body(payload):
 | 
			
		||||
        status
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_pull_request_review_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_pull_request_review_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return get_pull_request_event_message(
 | 
			
		||||
        get_sender_name(payload),
 | 
			
		||||
        'submitted',
 | 
			
		||||
@@ -286,8 +263,7 @@ def get_pull_request_review_body(payload):
 | 
			
		||||
        type='PR Review'
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_pull_request_review_comment_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_pull_request_review_comment_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    action = payload['action']
 | 
			
		||||
    message = None
 | 
			
		||||
    if action == 'created':
 | 
			
		||||
@@ -301,36 +277,28 @@ def get_pull_request_review_comment_body(payload):
 | 
			
		||||
        type='PR Review Comment'
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_ping_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_ping_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return get_setup_webhook_message('GitHub', get_sender_name(payload))
 | 
			
		||||
 | 
			
		||||
def get_repository_name(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_repository_name(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return payload['repository']['name']
 | 
			
		||||
 | 
			
		||||
def get_organization_name(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_organization_name(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return payload['organization']['login']
 | 
			
		||||
 | 
			
		||||
def get_sender_name(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_sender_name(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return payload['sender']['login']
 | 
			
		||||
 | 
			
		||||
def get_branch_name_from_ref(ref_string):
 | 
			
		||||
    # type: (Text) -> Text
 | 
			
		||||
def get_branch_name_from_ref(ref_string: Text) -> Text:
 | 
			
		||||
    return re.sub(r'^refs/heads/', '', ref_string)
 | 
			
		||||
 | 
			
		||||
def get_tag_name_from_ref(ref_string):
 | 
			
		||||
    # type: (Text) -> Text
 | 
			
		||||
def get_tag_name_from_ref(ref_string: Text) -> Text:
 | 
			
		||||
    return re.sub(r'^refs/tags/', '', ref_string)
 | 
			
		||||
 | 
			
		||||
def is_commit_push_event(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> bool
 | 
			
		||||
def is_commit_push_event(payload: Dict[str, Any]) -> bool:
 | 
			
		||||
    return bool(re.match(r'^refs/heads/', payload['ref']))
 | 
			
		||||
 | 
			
		||||
def get_subject_based_on_type(payload, event):
 | 
			
		||||
    # type: (Dict[str, Any], Text) -> Text
 | 
			
		||||
def get_subject_based_on_type(payload: Dict[str, Any], event: Text) -> Text:
 | 
			
		||||
    if 'pull_request' in event:
 | 
			
		||||
        return SUBJECT_WITH_PR_OR_ISSUE_INFO_TEMPLATE.format(
 | 
			
		||||
            repo=get_repository_name(payload),
 | 
			
		||||
@@ -409,8 +377,7 @@ def api_github_webhook(
 | 
			
		||||
        check_send_stream_message(user_profile, request.client, stream, subject, body)
 | 
			
		||||
    return json_success()
 | 
			
		||||
 | 
			
		||||
def get_event(request, payload, branches):
 | 
			
		||||
    # type: (HttpRequest, Dict[str, Any], Text) -> Optional[str]
 | 
			
		||||
def get_event(request: HttpRequest, payload: Dict[str, Any], branches: Text) -> Optional[str]:
 | 
			
		||||
    event = request.META['HTTP_X_GITHUB_EVENT']
 | 
			
		||||
    if event == 'pull_request':
 | 
			
		||||
        action = payload['action']
 | 
			
		||||
@@ -436,6 +403,5 @@ def get_event(request, payload, branches):
 | 
			
		||||
    logging.warning(u'Event {} is unknown and cannot be handled'.format(event))
 | 
			
		||||
    return None
 | 
			
		||||
 | 
			
		||||
def get_body_function_based_on_type(type):
 | 
			
		||||
    # type: (str) -> Any
 | 
			
		||||
def get_body_function_based_on_type(type: str) -> Any:
 | 
			
		||||
    return EVENT_FUNCTION_MAPPER.get(type)
 | 
			
		||||
 
 | 
			
		||||
@@ -10,40 +10,34 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
    URL_TEMPLATE = "/api/v1/external/gitlab?&api_key={api_key}&stream={stream}"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'gitlab'
 | 
			
		||||
 | 
			
		||||
    def test_push_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project / tomek"
 | 
			
		||||
        expected_message = u"Tomasz Kolek [pushed](https://gitlab.com/tomaszkolek0/my-awesome-project/compare/5fcdd5551fc3085df79bece2c32b1400802ac407...eb6ae1e591e0819dc5bf187c6bfe18ec065a80e9) 2 commits to branch tomek.\n\n* b ([66abd2d](https://gitlab.com/tomaszkolek0/my-awesome-project/commit/66abd2da28809ffa128ed0447965cf11d7f863a7))\n* c ([eb6ae1e](https://gitlab.com/tomaszkolek0/my-awesome-project/commit/eb6ae1e591e0819dc5bf187c6bfe18ec065a80e9))"
 | 
			
		||||
        self.send_and_test_stream_message('push', expected_subject, expected_message, HTTP_X_GITLAB_EVENT="Push Hook")
 | 
			
		||||
 | 
			
		||||
    def test_push_local_branch_without_commits(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_local_branch_without_commits(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project / changes"
 | 
			
		||||
        expected_message = u"Eeshan Garg [pushed](https://gitlab.com/eeshangarg/my-awesome-project/compare/0000000000000000000000000000000000000000...68d7a5528cf423dfaac37dd62a56ac9cc8a884e3) the branch changes."
 | 
			
		||||
        self.send_and_test_stream_message('push_local_branch_without_commits', expected_subject, expected_message, HTTP_X_GITLAB_EVENT="Push Hook")
 | 
			
		||||
 | 
			
		||||
    def test_push_event_message_filtered_by_branches(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_event_message_filtered_by_branches(self) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url(branches='master,tomek')
 | 
			
		||||
        expected_subject = u"my-awesome-project / tomek"
 | 
			
		||||
        expected_message = u"Tomasz Kolek [pushed](https://gitlab.com/tomaszkolek0/my-awesome-project/compare/5fcdd5551fc3085df79bece2c32b1400802ac407...eb6ae1e591e0819dc5bf187c6bfe18ec065a80e9) 2 commits to branch tomek.\n\n* b ([66abd2d](https://gitlab.com/tomaszkolek0/my-awesome-project/commit/66abd2da28809ffa128ed0447965cf11d7f863a7))\n* c ([eb6ae1e](https://gitlab.com/tomaszkolek0/my-awesome-project/commit/eb6ae1e591e0819dc5bf187c6bfe18ec065a80e9))"
 | 
			
		||||
        self.send_and_test_stream_message('push', expected_subject, expected_message, HTTP_X_GITLAB_EVENT="Push Hook")
 | 
			
		||||
 | 
			
		||||
    def test_push_multiple_committers(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_multiple_committers(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project / tomek"
 | 
			
		||||
        expected_message = u"Tomasz Kolek [pushed](https://gitlab.com/tomaszkolek0/my-awesome-project/compare/5fcdd5551fc3085df79bece2c32b1400802ac407...eb6ae1e591e0819dc5bf187c6bfe18ec065a80e9) 2 commits to branch tomek. Commits by Ben (1) and Tomasz Kolek (1).\n\n* b ([66abd2d](https://gitlab.com/tomaszkolek0/my-awesome-project/commit/66abd2da28809ffa128ed0447965cf11d7f863a7))\n* c ([eb6ae1e](https://gitlab.com/tomaszkolek0/my-awesome-project/commit/eb6ae1e591e0819dc5bf187c6bfe18ec065a80e9))"
 | 
			
		||||
        self.send_and_test_stream_message('push_multiple_committers', expected_subject, expected_message, HTTP_X_GITLAB_EVENT="Push Hook")
 | 
			
		||||
 | 
			
		||||
    def test_push_multiple_committers_with_others(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_multiple_committers_with_others(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project / tomek"
 | 
			
		||||
        commit_info = u"* b ([eb6ae1e](https://gitlab.com/tomaszkolek0/my-awesome-project/commit/eb6ae1e591e0819dc5bf187c6bfe18ec065a80e9))\n"
 | 
			
		||||
        expected_message = u"Tomasz Kolek [pushed](https://gitlab.com/tomaszkolek0/my-awesome-project/compare/5fcdd5551fc3085df79bece2c32b1400802ac407...eb6ae1e591e0819dc5bf187c6bfe18ec065a80e9) 7 commits to branch tomek. Commits by Ben (3), baxterthehacker (2), James (1) and others (1).\n\n{}* b ([eb6ae1e](https://gitlab.com/tomaszkolek0/my-awesome-project/commit/eb6ae1e591e0819dc5bf187c6bfe18ec065a80e9))".format(commit_info * 6)
 | 
			
		||||
        self.send_and_test_stream_message('push_multiple_committers_with_others', expected_subject, expected_message, HTTP_X_GITLAB_EVENT="Push Hook")
 | 
			
		||||
 | 
			
		||||
    def test_push_commits_more_than_limit_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_commits_more_than_limit_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project / tomek"
 | 
			
		||||
        commits_info = u'* b ([66abd2d](https://gitlab.com/tomaszkolek0/my-awesome-project/commit/66abd2da28809ffa128ed0447965cf11d7f863a7))\n'
 | 
			
		||||
        expected_message = u"Tomasz Kolek [pushed](https://gitlab.com/tomaszkolek0/my-awesome-project/compare/5fcdd5551fc3085df79bece2c32b1400802ac407...eb6ae1e591e0819dc5bf187c6bfe18ec065a80e9) 50 commits to branch tomek.\n\n{}[and {} more commit(s)]".format(
 | 
			
		||||
@@ -52,8 +46,7 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
        )
 | 
			
		||||
        self.send_and_test_stream_message('push_commits_more_than_limit', expected_subject, expected_message, HTTP_X_GITLAB_EVENT="Push Hook")
 | 
			
		||||
 | 
			
		||||
    def test_push_commits_more_than_limit_message_filtered_by_branches(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_commits_more_than_limit_message_filtered_by_branches(self) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url(branches='master,tomek')
 | 
			
		||||
        expected_subject = u"my-awesome-project / tomek"
 | 
			
		||||
        commits_info = u'* b ([66abd2d](https://gitlab.com/tomaszkolek0/my-awesome-project/commit/66abd2da28809ffa128ed0447965cf11d7f863a7))\n'
 | 
			
		||||
@@ -63,15 +56,13 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
        )
 | 
			
		||||
        self.send_and_test_stream_message('push_commits_more_than_limit', expected_subject, expected_message, HTTP_X_GITLAB_EVENT="Push Hook")
 | 
			
		||||
 | 
			
		||||
    def test_remove_branch_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_remove_branch_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project / tomek"
 | 
			
		||||
        expected_message = u"Tomasz Kolek deleted branch tomek"
 | 
			
		||||
 | 
			
		||||
        self.send_and_test_stream_message('remove_branch', expected_subject, expected_message, HTTP_X_GITLAB_EVENT="Push Hook")
 | 
			
		||||
 | 
			
		||||
    def test_add_tag_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_add_tag_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project"
 | 
			
		||||
        expected_message = u"Tomasz Kolek pushed tag xyz"
 | 
			
		||||
 | 
			
		||||
@@ -82,8 +73,7 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
            HTTP_X_GITLAB_EVENT="Tag Push Hook",
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_remove_tag_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_remove_tag_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project"
 | 
			
		||||
        expected_message = u"Tomasz Kolek removed tag xyz"
 | 
			
		||||
 | 
			
		||||
@@ -94,8 +84,7 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
            HTTP_X_GITLAB_EVENT="Tag Push Hook"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_create_issue_without_assignee_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_create_issue_without_assignee_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project / Issue #1 Issue title"
 | 
			
		||||
        expected_message = u"Tomasz Kolek created [Issue #1](https://gitlab.com/tomaszkolek0/my-awesome-project/issues/1)\n\n~~~ quote\nIssue description\n~~~"
 | 
			
		||||
 | 
			
		||||
@@ -106,8 +95,7 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
            HTTP_X_GITLAB_EVENT="Issue Hook"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_create_issue_with_assignee_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_create_issue_with_assignee_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project / Issue #1 Issue title"
 | 
			
		||||
        expected_message = u"Tomasz Kolek created [Issue #1](https://gitlab.com/tomaszkolek0/my-awesome-project/issues/1)(assigned to Tomasz Kolek)\n\n~~~ quote\nIssue description\n~~~"
 | 
			
		||||
 | 
			
		||||
@@ -118,8 +106,7 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
            HTTP_X_GITLAB_EVENT="Issue Hook"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_update_issue_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_update_issue_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project / Issue #1 Issue title_new"
 | 
			
		||||
        expected_message = u"Tomasz Kolek updated [Issue #1](https://gitlab.com/tomaszkolek0/my-awesome-project/issues/1)"
 | 
			
		||||
 | 
			
		||||
@@ -130,8 +117,7 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
            HTTP_X_GITLAB_EVENT="Issue Hook"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_close_issue_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_close_issue_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project / Issue #1 Issue title_new"
 | 
			
		||||
        expected_message = u"Tomasz Kolek closed [Issue #1](https://gitlab.com/tomaszkolek0/my-awesome-project/issues/1)"
 | 
			
		||||
 | 
			
		||||
@@ -142,8 +128,7 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
            HTTP_X_GITLAB_EVENT="Issue Hook"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_reopen_issue_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_reopen_issue_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project / Issue #1 Issue title_new"
 | 
			
		||||
        expected_message = u"Tomasz Kolek reopened [Issue #1](https://gitlab.com/tomaszkolek0/my-awesome-project/issues/1)"
 | 
			
		||||
 | 
			
		||||
@@ -154,8 +139,7 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
            HTTP_X_GITLAB_EVENT="Issue Hook"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_note_commit_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_note_commit_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project"
 | 
			
		||||
        expected_message = u"Tomasz Kolek [commented](https://gitlab.com/tomaszkolek0/my-awesome-project/commit/66abd2da28809ffa128ed0447965cf11d7f863a7#note_14169211) on [66abd2d](https://gitlab.com/tomaszkolek0/my-awesome-project/commit/66abd2da28809ffa128ed0447965cf11d7f863a7)\n~~~ quote\nnice commit\n~~~"
 | 
			
		||||
 | 
			
		||||
@@ -166,8 +150,7 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
            HTTP_X_GITLAB_EVENT="Note Hook"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_note_merge_request_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_note_merge_request_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project / MR #1 Tomek"
 | 
			
		||||
        expected_message = u"Tomasz Kolek [commented](https://gitlab.com/tomaszkolek0/my-awesome-project/merge_requests/1#note_14171860) on [MR #1](https://gitlab.com/tomaszkolek0/my-awesome-project/merge_requests/1)\n\n~~~ quote\nNice merge request!\n~~~"
 | 
			
		||||
 | 
			
		||||
@@ -178,8 +161,7 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
            HTTP_X_GITLAB_EVENT="Note Hook"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_note_issue_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_note_issue_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project / Issue #2 abc"
 | 
			
		||||
        expected_message = u"Tomasz Kolek [commented](https://gitlab.com/tomaszkolek0/my-awesome-project/issues/2#note_14172057) on [Issue #2](https://gitlab.com/tomaszkolek0/my-awesome-project/issues/2)\n\n~~~ quote\nNice issue\n~~~"
 | 
			
		||||
 | 
			
		||||
@@ -190,8 +172,7 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
            HTTP_X_GITLAB_EVENT="Note Hook"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_note_snippet_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_note_snippet_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project / Snippet #2 test"
 | 
			
		||||
        expected_message = u"Tomasz Kolek [commented](https://gitlab.com/tomaszkolek0/my-awesome-project/snippets/2#note_14172058) on [Snippet #2](https://gitlab.com/tomaszkolek0/my-awesome-project/snippets/2)\n\n~~~ quote\nNice snippet\n~~~"
 | 
			
		||||
 | 
			
		||||
@@ -202,8 +183,7 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
            HTTP_X_GITLAB_EVENT="Note Hook"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_merge_request_created_without_assignee_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_merge_request_created_without_assignee_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project / MR #2 NEW MR"
 | 
			
		||||
        expected_message = u"Tomasz Kolek created [MR #2](https://gitlab.com/tomaszkolek0/my-awesome-project/merge_requests/2)\nfrom `tomek` to `master`\n\n~~~ quote\ndescription of merge request\n~~~"
 | 
			
		||||
 | 
			
		||||
@@ -214,8 +194,7 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
            HTTP_X_GITLAB_EVENT="Merge Request Hook"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_merge_request_created_with_assignee_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_merge_request_created_with_assignee_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project / MR #3 New Merge Request"
 | 
			
		||||
        expected_message = u"Tomasz Kolek created [MR #3](https://gitlab.com/tomaszkolek0/my-awesome-project/merge_requests/3)(assigned to Tomasz Kolek)\nfrom `tomek` to `master`\n\n~~~ quote\ndescription of merge request\n~~~"
 | 
			
		||||
        self.send_and_test_stream_message(
 | 
			
		||||
@@ -225,8 +204,7 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
            HTTP_X_GITLAB_EVENT="Merge Request Hook"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_merge_request_closed_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_merge_request_closed_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project / MR #2 NEW MR"
 | 
			
		||||
        expected_message = u"Tomasz Kolek closed [MR #2](https://gitlab.com/tomaszkolek0/my-awesome-project/merge_requests/2)"
 | 
			
		||||
 | 
			
		||||
@@ -237,8 +215,7 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
            HTTP_X_GITLAB_EVENT="Merge Request Hook"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_merge_request_reopened_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_merge_request_reopened_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project / MR #1 Update the README with author ..."
 | 
			
		||||
        expected_message = u"Eeshan Garg reopened [MR #1](https://gitlab.com/eeshangarg/my-awesome-project/merge_requests/1)"
 | 
			
		||||
 | 
			
		||||
@@ -249,8 +226,7 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
            HTTP_X_GITLAB_EVENT="Merge Request Hook"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_merge_request_updated_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_merge_request_updated_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project / MR #3 New Merge Request"
 | 
			
		||||
        expected_message = u"Tomasz Kolek updated [MR #3](https://gitlab.com/tomaszkolek0/my-awesome-project/merge_requests/3)(assigned to Tomasz Kolek)\nfrom `tomek` to `master`\n\n~~~ quote\nupdated desc\n~~~"
 | 
			
		||||
        self.send_and_test_stream_message(
 | 
			
		||||
@@ -260,8 +236,7 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
            HTTP_X_GITLAB_EVENT="Merge Request Hook"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_merge_request_added_commit_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_merge_request_added_commit_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project / MR #3 New Merge Request"
 | 
			
		||||
        expected_message = u"Tomasz Kolek added commit(s) to [MR #3](https://gitlab.com/tomaszkolek0/my-awesome-project/merge_requests/3)"
 | 
			
		||||
        self.send_and_test_stream_message(
 | 
			
		||||
@@ -271,8 +246,7 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
            HTTP_X_GITLAB_EVENT="Merge Request Hook"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_merge_request_merged_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_merge_request_merged_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project / MR #3 New Merge Request"
 | 
			
		||||
        expected_message = u"Tomasz Kolek merged [MR #3](https://gitlab.com/tomaszkolek0/my-awesome-project/merge_requests/3)"
 | 
			
		||||
 | 
			
		||||
@@ -283,8 +257,7 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
            HTTP_X_GITLAB_EVENT="Merge Request Hook"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_wiki_page_opened_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_wiki_page_opened_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project"
 | 
			
		||||
        expected_message = u"Tomasz Kolek created [Wiki Page \"how to\"](https://gitlab.com/tomaszkolek0/my-awesome-project/wikis/how-to)."
 | 
			
		||||
 | 
			
		||||
@@ -295,8 +268,7 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
            HTTP_X_GITLAB_EVENT="Wiki Page Hook"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_wiki_page_edited_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_wiki_page_edited_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project"
 | 
			
		||||
        expected_message = u"Tomasz Kolek updated [Wiki Page \"how to\"](https://gitlab.com/tomaszkolek0/my-awesome-project/wikis/how-to)."
 | 
			
		||||
 | 
			
		||||
@@ -307,8 +279,7 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
            HTTP_X_GITLAB_EVENT="Wiki Page Hook"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_build_created_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_build_created_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project / master"
 | 
			
		||||
        expected_message = u"Build job_name from test stage was created."
 | 
			
		||||
 | 
			
		||||
@@ -319,8 +290,7 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
            HTTP_X_GITLAB_EVENT="Build Hook"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_build_started_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_build_started_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project / master"
 | 
			
		||||
        expected_message = u"Build job_name from test stage started."
 | 
			
		||||
 | 
			
		||||
@@ -331,8 +301,7 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
            HTTP_X_GITLAB_EVENT="Build Hook"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_build_succeeded_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_build_succeeded_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project / master"
 | 
			
		||||
        expected_message = u"Build job_name from test stage changed status to success."
 | 
			
		||||
 | 
			
		||||
@@ -343,8 +312,7 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
            HTTP_X_GITLAB_EVENT="Build Hook"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_pipeline_succeeded_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pipeline_succeeded_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project / master"
 | 
			
		||||
        expected_message = u"Pipeline changed status to success with build(s):\n* job_name2 - success\n* job_name - success."
 | 
			
		||||
 | 
			
		||||
@@ -355,8 +323,7 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
            HTTP_X_GITLAB_EVENT="Pipeline Hook"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_pipeline_started_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pipeline_started_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project / master"
 | 
			
		||||
        expected_message = u"Pipeline started with build(s):\n* job_name - running\n* job_name2 - pending."
 | 
			
		||||
 | 
			
		||||
@@ -367,8 +334,7 @@ class GitlabHookTests(WebhookTestCase):
 | 
			
		||||
            HTTP_X_GITLAB_EVENT="Pipeline Hook"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_pipeline_pending_event_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pipeline_pending_event_message(self) -> None:
 | 
			
		||||
        expected_subject = u"my-awesome-project / master"
 | 
			
		||||
        expected_message = u"Pipeline was created with build(s):\n* job_name2 - pending\n* job_name - created."
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -17,14 +17,12 @@ class UnknownEventType(Exception):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_push_event_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_push_event_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    if payload.get('after') == EMPTY_SHA:
 | 
			
		||||
        return get_remove_branch_event_body(payload)
 | 
			
		||||
    return get_normal_push_event_body(payload)
 | 
			
		||||
 | 
			
		||||
def get_normal_push_event_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_normal_push_event_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    compare_url = u'{}/compare/{}...{}'.format(
 | 
			
		||||
        get_repository_homepage(payload),
 | 
			
		||||
        payload['before'],
 | 
			
		||||
@@ -48,23 +46,20 @@ def get_normal_push_event_body(payload):
 | 
			
		||||
        commits
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_remove_branch_event_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_remove_branch_event_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return get_remove_branch_event_message(
 | 
			
		||||
        get_user_name(payload),
 | 
			
		||||
        get_branch_name(payload)
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_tag_push_event_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_tag_push_event_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return get_push_tag_event_message(
 | 
			
		||||
        get_user_name(payload),
 | 
			
		||||
        get_tag_name(payload),
 | 
			
		||||
        action="pushed" if payload.get('checkout_sha') else "removed"
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_issue_created_event_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_issue_created_event_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return get_issue_event_message(
 | 
			
		||||
        get_issue_user_name(payload),
 | 
			
		||||
        'created',
 | 
			
		||||
@@ -74,8 +69,7 @@ def get_issue_created_event_body(payload):
 | 
			
		||||
        get_objects_assignee(payload)
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_issue_event_body(payload, action):
 | 
			
		||||
    # type: (Dict[str, Any], Text) -> Text
 | 
			
		||||
def get_issue_event_body(payload: Dict[str, Any], action: Text) -> Text:
 | 
			
		||||
    return get_issue_event_message(
 | 
			
		||||
        get_issue_user_name(payload),
 | 
			
		||||
        action,
 | 
			
		||||
@@ -83,14 +77,12 @@ def get_issue_event_body(payload, action):
 | 
			
		||||
        payload['object_attributes'].get('iid'),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_merge_request_updated_event_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_merge_request_updated_event_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    if payload['object_attributes'].get('oldrev'):
 | 
			
		||||
        return get_merge_request_event_body(payload, "added commit(s) to")
 | 
			
		||||
    return get_merge_request_open_or_updated_body(payload, "updated")
 | 
			
		||||
 | 
			
		||||
def get_merge_request_event_body(payload, action):
 | 
			
		||||
    # type: (Dict[str, Any], Text) -> Text
 | 
			
		||||
def get_merge_request_event_body(payload: Dict[str, Any], action: Text) -> Text:
 | 
			
		||||
    pull_request = payload['object_attributes']
 | 
			
		||||
    return get_pull_request_event_message(
 | 
			
		||||
        get_issue_user_name(payload),
 | 
			
		||||
@@ -100,8 +92,7 @@ def get_merge_request_event_body(payload, action):
 | 
			
		||||
        type='MR',
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_merge_request_open_or_updated_body(payload, action):
 | 
			
		||||
    # type: (Dict[str, Any], Text) -> Text
 | 
			
		||||
def get_merge_request_open_or_updated_body(payload: Dict[str, Any], action: Text) -> Text:
 | 
			
		||||
    pull_request = payload['object_attributes']
 | 
			
		||||
    return get_pull_request_event_message(
 | 
			
		||||
        get_issue_user_name(payload),
 | 
			
		||||
@@ -115,15 +106,13 @@ def get_merge_request_open_or_updated_body(payload, action):
 | 
			
		||||
        type='MR',
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_objects_assignee(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Optional[Text]
 | 
			
		||||
def get_objects_assignee(payload: Dict[str, Any]) -> Optional[Text]:
 | 
			
		||||
    assignee_object = payload.get('assignee')
 | 
			
		||||
    if assignee_object:
 | 
			
		||||
        return assignee_object.get('name')
 | 
			
		||||
    return None
 | 
			
		||||
 | 
			
		||||
def get_commented_commit_event_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_commented_commit_event_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    comment = payload['object_attributes']
 | 
			
		||||
    action = u'[commented]({})'.format(comment['url'])
 | 
			
		||||
    return get_commits_comment_action_message(
 | 
			
		||||
@@ -134,8 +123,7 @@ def get_commented_commit_event_body(payload):
 | 
			
		||||
        comment['note'],
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_commented_merge_request_event_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_commented_merge_request_event_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    comment = payload['object_attributes']
 | 
			
		||||
    action = u'[commented]({}) on'.format(comment['url'])
 | 
			
		||||
    url = u'{}/merge_requests/{}'.format(
 | 
			
		||||
@@ -151,8 +139,7 @@ def get_commented_merge_request_event_body(payload):
 | 
			
		||||
        type='MR'
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_commented_issue_event_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_commented_issue_event_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    comment = payload['object_attributes']
 | 
			
		||||
    action = u'[commented]({}) on'.format(comment['url'])
 | 
			
		||||
    url = u'{}/issues/{}'.format(
 | 
			
		||||
@@ -168,8 +155,7 @@ def get_commented_issue_event_body(payload):
 | 
			
		||||
        type='Issue'
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_commented_snippet_event_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_commented_snippet_event_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    comment = payload['object_attributes']
 | 
			
		||||
    action = u'[commented]({}) on'.format(comment['url'])
 | 
			
		||||
    url = u'{}/snippets/{}'.format(
 | 
			
		||||
@@ -185,8 +171,7 @@ def get_commented_snippet_event_body(payload):
 | 
			
		||||
        type='Snippet'
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_wiki_page_event_body(payload, action):
 | 
			
		||||
    # type: (Dict[str, Any], Text) -> Text
 | 
			
		||||
def get_wiki_page_event_body(payload: Dict[str, Any], action: Text) -> Text:
 | 
			
		||||
    return u"{} {} [Wiki Page \"{}\"]({}).".format(
 | 
			
		||||
        get_issue_user_name(payload),
 | 
			
		||||
        action,
 | 
			
		||||
@@ -194,8 +179,7 @@ def get_wiki_page_event_body(payload, action):
 | 
			
		||||
        payload['object_attributes'].get('url'),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_build_hook_event_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_build_hook_event_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    build_status = payload.get('build_status')
 | 
			
		||||
    if build_status == 'created':
 | 
			
		||||
        action = 'was created'
 | 
			
		||||
@@ -209,8 +193,7 @@ def get_build_hook_event_body(payload):
 | 
			
		||||
        action
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def get_pipeline_event_body(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_pipeline_event_body(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    pipeline_status = payload['object_attributes'].get('status')
 | 
			
		||||
    if pipeline_status == 'pending':
 | 
			
		||||
        action = 'was created'
 | 
			
		||||
@@ -224,36 +207,28 @@ def get_pipeline_event_body(payload):
 | 
			
		||||
        builds_status += u"* {} - {}\n".format(build.get('name'), build.get('status'))
 | 
			
		||||
    return u"Pipeline {} with build(s):\n{}.".format(action, builds_status[:-1])
 | 
			
		||||
 | 
			
		||||
def get_repo_name(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_repo_name(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return payload['project']['name']
 | 
			
		||||
 | 
			
		||||
def get_user_name(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_user_name(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return payload['user_name']
 | 
			
		||||
 | 
			
		||||
def get_issue_user_name(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_issue_user_name(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return payload['user']['name']
 | 
			
		||||
 | 
			
		||||
def get_repository_homepage(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_repository_homepage(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return payload['repository']['homepage']
 | 
			
		||||
 | 
			
		||||
def get_branch_name(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_branch_name(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return payload['ref'].replace('refs/heads/', '')
 | 
			
		||||
 | 
			
		||||
def get_tag_name(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_tag_name(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return payload['ref'].replace('refs/tags/', '')
 | 
			
		||||
 | 
			
		||||
def get_object_iid(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_object_iid(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return payload['object_attributes']['iid']
 | 
			
		||||
 | 
			
		||||
def get_object_url(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_object_url(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return payload['object_attributes']['url']
 | 
			
		||||
 | 
			
		||||
EVENT_FUNCTION_MAPPER = {
 | 
			
		||||
@@ -292,12 +267,10 @@ def api_gitlab_webhook(request, user_profile,
 | 
			
		||||
        check_send_stream_message(user_profile, request.client, stream, subject, body)
 | 
			
		||||
    return json_success()
 | 
			
		||||
 | 
			
		||||
def get_body_based_on_event(event):
 | 
			
		||||
    # type: (str) -> Any
 | 
			
		||||
def get_body_based_on_event(event: str) -> Any:
 | 
			
		||||
    return EVENT_FUNCTION_MAPPER[event]
 | 
			
		||||
 | 
			
		||||
def get_subject_based_on_event(event, payload):
 | 
			
		||||
    # type: (str, Dict[str, Any]) -> Text
 | 
			
		||||
def get_subject_based_on_event(event: str, payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    if event == 'Push Hook':
 | 
			
		||||
        return u"{} / {}".format(get_repo_name(payload), get_branch_name(payload))
 | 
			
		||||
    elif event == 'Build Hook':
 | 
			
		||||
@@ -344,8 +317,7 @@ def get_subject_based_on_event(event, payload):
 | 
			
		||||
        )
 | 
			
		||||
    return get_repo_name(payload)
 | 
			
		||||
 | 
			
		||||
def get_event(request, payload, branches):
 | 
			
		||||
    # type: (HttpRequest, Dict[str, Any], Optional[Text]) -> Optional[str]
 | 
			
		||||
def get_event(request: HttpRequest, payload: Dict[str, Any], branches: Optional[Text]) -> Optional[str]:
 | 
			
		||||
    event = request.META['HTTP_X_GITLAB_EVENT']
 | 
			
		||||
    if event == 'Issue Hook':
 | 
			
		||||
        action = payload['object_attributes'].get('action')
 | 
			
		||||
 
 | 
			
		||||
@@ -11,31 +11,27 @@ class GogsHookTests(WebhookTestCase):
 | 
			
		||||
    URL_TEMPLATE = "/api/v1/external/gogs?&api_key={api_key}"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'gogs'
 | 
			
		||||
 | 
			
		||||
    def test_push(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push(self) -> None:
 | 
			
		||||
        expected_subject = u"try-git / master"
 | 
			
		||||
        expected_message = u"""john [pushed](http://localhost:3000/john/try-git/compare/479e6b772b7fba19412457483f50b201286d0103...d8fce16c72a2ff56a5afc8a08645a6ce45491794) 1 commit to branch master. Commits by John (1).
 | 
			
		||||
 | 
			
		||||
* Webhook Test ([d8fce16](http://localhost:3000/john/try-git/commit/d8fce16c72a2ff56a5afc8a08645a6ce45491794))"""
 | 
			
		||||
        self.send_and_test_stream_message('push', expected_subject, expected_message, HTTP_X_GOGS_EVENT='push')
 | 
			
		||||
 | 
			
		||||
    def test_push_multiple_committers(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_multiple_committers(self) -> None:
 | 
			
		||||
        commit_info = u'* Webhook Test ([d8fce16](http://localhost:3000/john/try-git/commit/d8fce16c72a2ff56a5afc8a08645a6ce45491794))\n'
 | 
			
		||||
        expected_subject = u"try-git / master"
 | 
			
		||||
        expected_message = u"""john [pushed](http://localhost:3000/john/try-git/compare/479e6b772b7fba19412457483f50b201286d0103...d8fce16c72a2ff56a5afc8a08645a6ce45491794) 2 commits to branch master. Commits by Benjamin (1) and John (1).\n\n{}* Webhook Test ([d8fce16](http://localhost:3000/john/try-git/commit/d8fce16c72a2ff56a5afc8a08645a6ce45491794))""".format(commit_info)
 | 
			
		||||
        self.send_and_test_stream_message('push_commits_multiple_committers', expected_subject, expected_message, HTTP_X_GOGS_EVENT='push')
 | 
			
		||||
 | 
			
		||||
    def test_push_multiple_committers_filtered_by_branches(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_multiple_committers_filtered_by_branches(self) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url(branches='master,development')
 | 
			
		||||
        commit_info = u'* Webhook Test ([d8fce16](http://localhost:3000/john/try-git/commit/d8fce16c72a2ff56a5afc8a08645a6ce45491794))\n'
 | 
			
		||||
        expected_subject = u"try-git / master"
 | 
			
		||||
        expected_message = u"""john [pushed](http://localhost:3000/john/try-git/compare/479e6b772b7fba19412457483f50b201286d0103...d8fce16c72a2ff56a5afc8a08645a6ce45491794) 2 commits to branch master. Commits by Benjamin (1) and John (1).\n\n{}* Webhook Test ([d8fce16](http://localhost:3000/john/try-git/commit/d8fce16c72a2ff56a5afc8a08645a6ce45491794))""".format(commit_info)
 | 
			
		||||
        self.send_and_test_stream_message('push_commits_multiple_committers', expected_subject, expected_message, HTTP_X_GOGS_EVENT='push')
 | 
			
		||||
 | 
			
		||||
    def test_push_filtered_by_branches(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_filtered_by_branches(self) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url(branches='master,development')
 | 
			
		||||
        expected_subject = u"try-git / master"
 | 
			
		||||
        expected_message = u"""john [pushed](http://localhost:3000/john/try-git/compare/479e6b772b7fba19412457483f50b201286d0103...d8fce16c72a2ff56a5afc8a08645a6ce45491794) 1 commit to branch master. Commits by John (1).
 | 
			
		||||
@@ -43,8 +39,7 @@ class GogsHookTests(WebhookTestCase):
 | 
			
		||||
* Webhook Test ([d8fce16](http://localhost:3000/john/try-git/commit/d8fce16c72a2ff56a5afc8a08645a6ce45491794))"""
 | 
			
		||||
        self.send_and_test_stream_message('push', expected_subject, expected_message, HTTP_X_GOGS_EVENT='push')
 | 
			
		||||
 | 
			
		||||
    def test_push_commits_more_than_limits(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_commits_more_than_limits(self) -> None:
 | 
			
		||||
        expected_subject = u"try-git / master"
 | 
			
		||||
        commits_info = "* Webhook Test ([d8fce16](http://localhost:3000/john/try-git/commit/d8fce16c72a2ff56a5afc8a08645a6ce45491794))\n"
 | 
			
		||||
        expected_message = u"john [pushed](http://localhost:3000/john/try-git/compare/479e6b772b7fba19412457483f50b201286d0103...d8fce16c72a2ff56a5afc8a08645a6ce45491794) 30 commits to branch master. Commits by John (30).\n\n{}[and {} more commit(s)]".format(
 | 
			
		||||
@@ -53,8 +48,7 @@ class GogsHookTests(WebhookTestCase):
 | 
			
		||||
        )
 | 
			
		||||
        self.send_and_test_stream_message('push_commits_more_than_limits', expected_subject, expected_message, HTTP_X_GOGS_EVENT='push')
 | 
			
		||||
 | 
			
		||||
    def test_push_commits_more_than_limits_filtered_by_branches(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_push_commits_more_than_limits_filtered_by_branches(self) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url(branches='master,development')
 | 
			
		||||
        expected_subject = u"try-git / master"
 | 
			
		||||
        commits_info = "* Webhook Test ([d8fce16](http://localhost:3000/john/try-git/commit/d8fce16c72a2ff56a5afc8a08645a6ce45491794))\n"
 | 
			
		||||
@@ -64,36 +58,31 @@ class GogsHookTests(WebhookTestCase):
 | 
			
		||||
        )
 | 
			
		||||
        self.send_and_test_stream_message('push_commits_more_than_limits', expected_subject, expected_message, HTTP_X_GOGS_EVENT='push')
 | 
			
		||||
 | 
			
		||||
    def test_new_branch(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_new_branch(self) -> None:
 | 
			
		||||
        expected_subject = u"try-git / my_feature"
 | 
			
		||||
        expected_message = u"john created [my_feature](http://localhost:3000/john/try-git/src/my_feature) branch"
 | 
			
		||||
        self.send_and_test_stream_message('branch', expected_subject, expected_message, HTTP_X_GOGS_EVENT='create')
 | 
			
		||||
 | 
			
		||||
    def test_pull_request_opened(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pull_request_opened(self) -> None:
 | 
			
		||||
        expected_subject = u"try-git / PR #1 Title Text for Pull Request"
 | 
			
		||||
        expected_message = u"""john opened [PR #1](http://localhost:3000/john/try-git/pulls/1)
 | 
			
		||||
from `feature` to `master`"""
 | 
			
		||||
        self.send_and_test_stream_message('pull_request_opened', expected_subject, expected_message, HTTP_X_GOGS_EVENT='pull_request')
 | 
			
		||||
 | 
			
		||||
    def test_pull_request_closed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pull_request_closed(self) -> None:
 | 
			
		||||
        expected_subject = u"try-git / PR #1 Title Text for Pull Request"
 | 
			
		||||
        expected_message = u"""john closed [PR #1](http://localhost:3000/john/try-git/pulls/1)
 | 
			
		||||
from `feature` to `master`"""
 | 
			
		||||
        self.send_and_test_stream_message('pull_request_closed', expected_subject, expected_message, HTTP_X_GOGS_EVENT='pull_request')
 | 
			
		||||
 | 
			
		||||
    def test_pull_request_merged(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pull_request_merged(self) -> None:
 | 
			
		||||
        expected_subject = u"try-git / PR #2 Title Text for Pull Request"
 | 
			
		||||
        expected_message = u"""john merged [PR #2](http://localhost:3000/john/try-git/pulls/2)
 | 
			
		||||
from `feature` to `master`"""
 | 
			
		||||
        self.send_and_test_stream_message('pull_request_merged', expected_subject, expected_message, HTTP_X_GOGS_EVENT='pull_request')
 | 
			
		||||
 | 
			
		||||
    @patch('zerver.webhooks.gogs.view.check_send_stream_message')
 | 
			
		||||
    def test_push_filtered_by_branches_ignore(self, check_send_stream_message_mock):
 | 
			
		||||
        # type: (MagicMock) -> None
 | 
			
		||||
    def test_push_filtered_by_branches_ignore(self, check_send_stream_message_mock: MagicMock) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url(branches='changes,development')
 | 
			
		||||
        payload = self.get_body('push')
 | 
			
		||||
        result = self.client_post(self.url, payload, HTTP_X_GOGS_EVENT='push',
 | 
			
		||||
 
 | 
			
		||||
@@ -13,8 +13,7 @@ from zerver.models import UserProfile
 | 
			
		||||
from django.http import HttpRequest, HttpResponse
 | 
			
		||||
from typing import Dict, Any, Iterable, Optional, Text
 | 
			
		||||
 | 
			
		||||
def format_push_event(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def format_push_event(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
 | 
			
		||||
    for commit in payload['commits']:
 | 
			
		||||
        commit['sha'] = commit['id']
 | 
			
		||||
@@ -30,8 +29,7 @@ def format_push_event(payload):
 | 
			
		||||
 | 
			
		||||
    return get_push_commits_event_message(**data)
 | 
			
		||||
 | 
			
		||||
def format_new_branch_event(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def format_new_branch_event(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
 | 
			
		||||
    branch_name = payload['ref']
 | 
			
		||||
    url = '{}/src/{}'.format(payload['repository']['html_url'], branch_name)
 | 
			
		||||
@@ -43,8 +41,7 @@ def format_new_branch_event(payload):
 | 
			
		||||
    }
 | 
			
		||||
    return get_create_branch_event_message(**data)
 | 
			
		||||
 | 
			
		||||
def format_pull_request_event(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def format_pull_request_event(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
 | 
			
		||||
    data = {
 | 
			
		||||
        'user_name': payload['pull_request']['user']['username'],
 | 
			
		||||
 
 | 
			
		||||
@@ -8,14 +8,12 @@ class GoSquaredHookTests(WebhookTestCase):
 | 
			
		||||
    FIXTURE_DIR_NAME = 'gosquared'
 | 
			
		||||
 | 
			
		||||
    # Note: Include a test function per each distinct message condition your integration supports
 | 
			
		||||
    def test_traffic_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_traffic_message(self) -> None:
 | 
			
		||||
        expected_subject = "GoSquared - requestb.in"
 | 
			
		||||
        expected_message = u"[requestb.in](https://www.gosquared.com/now/GSN-595854-T) has 33 visitors online."
 | 
			
		||||
 | 
			
		||||
        self.send_and_test_stream_message('traffic_spike', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Text
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Text:
 | 
			
		||||
        return self.fixture_data("gosquared", fixture_name, file_type="json")
 | 
			
		||||
 
 | 
			
		||||
@@ -8,8 +8,7 @@ class GreenhouseHookTests(WebhookTestCase):
 | 
			
		||||
    FIXTURE_DIR_NAME = 'greenhouse'
 | 
			
		||||
    CONTENT_TYPE = "application/x-www-form-urlencoded"
 | 
			
		||||
 | 
			
		||||
    def test_message_candidate_hired(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_message_candidate_hired(self) -> None:
 | 
			
		||||
        expected_subject = "Hire Candidate - 19"
 | 
			
		||||
        expected_message = ("Hire Candidate\n>Johnny Smith\nID: 19"
 | 
			
		||||
                            "\nApplying for role:\nDeveloper\n**Emails:**"
 | 
			
		||||
@@ -21,8 +20,7 @@ class GreenhouseHookTests(WebhookTestCase):
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type=self.CONTENT_TYPE)
 | 
			
		||||
 | 
			
		||||
    def test_message_candidate_rejected(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_message_candidate_rejected(self) -> None:
 | 
			
		||||
        expected_subject = "Reject Candidate - 265788"
 | 
			
		||||
        expected_message = ("Reject Candidate\n>Hector Porter\nID: "
 | 
			
		||||
                            "265788\nApplying for role:\nDesigner"
 | 
			
		||||
@@ -35,8 +33,7 @@ class GreenhouseHookTests(WebhookTestCase):
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type=self.CONTENT_TYPE)
 | 
			
		||||
 | 
			
		||||
    def test_message_candidate_stage_change(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_message_candidate_stage_change(self) -> None:
 | 
			
		||||
        expected_subject = "Candidate Stage Change - 265772"
 | 
			
		||||
        expected_message = ("Candidate Stage Change\n>Giuseppe Hurley"
 | 
			
		||||
                            "\nID: 265772\nApplying for role:\n"
 | 
			
		||||
@@ -51,8 +48,7 @@ class GreenhouseHookTests(WebhookTestCase):
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type=self.CONTENT_TYPE)
 | 
			
		||||
 | 
			
		||||
    def test_message_prospect_created(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_message_prospect_created(self) -> None:
 | 
			
		||||
        expected_subject = "New Prospect Application - 968190"
 | 
			
		||||
        expected_message = ("New Prospect Application\n>Trisha Troy"
 | 
			
		||||
                            "\nID: 968190\nApplying for role:\n"
 | 
			
		||||
@@ -65,6 +61,5 @@ class GreenhouseHookTests(WebhookTestCase):
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type=self.CONTENT_TYPE)
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Text
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Text:
 | 
			
		||||
        return self.fixture_data("greenhouse", fixture_name, file_type="json")
 | 
			
		||||
 
 | 
			
		||||
@@ -12,8 +12,7 @@ import ujson
 | 
			
		||||
 | 
			
		||||
MESSAGE_TEMPLATE = "Applying for role:\n{}\n**Emails:**\n{}\n\n>**Attachments:**\n{}"
 | 
			
		||||
 | 
			
		||||
def dict_list_to_string(some_list):
 | 
			
		||||
    # type: (List[Any]) -> str
 | 
			
		||||
def dict_list_to_string(some_list: List[Any]) -> str:
 | 
			
		||||
    internal_template = ''
 | 
			
		||||
    for item in some_list:
 | 
			
		||||
        item_type = item.get('type', '').title()
 | 
			
		||||
@@ -25,8 +24,7 @@ def dict_list_to_string(some_list):
 | 
			
		||||
            internal_template += "[{}]({})\n".format(item_type, item_url)
 | 
			
		||||
    return internal_template
 | 
			
		||||
 | 
			
		||||
def message_creator(action, application):
 | 
			
		||||
    # type: (str, Dict[str, Any]) -> str
 | 
			
		||||
def message_creator(action: str, application: Dict[str, Any]) -> str:
 | 
			
		||||
    message = MESSAGE_TEMPLATE.format(
 | 
			
		||||
        application['jobs'][0]['name'],
 | 
			
		||||
        dict_list_to_string(application['candidate']['email_addresses']),
 | 
			
		||||
 
 | 
			
		||||
@@ -7,16 +7,14 @@ class HelloSignHookTests(WebhookTestCase):
 | 
			
		||||
    URL_TEMPLATE = "/api/v1/external/hellosign?stream={stream}&api_key={api_key}"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'hellosign'
 | 
			
		||||
 | 
			
		||||
    def test_signatures_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_signatures_message(self) -> None:
 | 
			
		||||
        expected_subject = "NDA with Acme Co."
 | 
			
		||||
        expected_message = ("The NDA with Acme Co. is awaiting the signature of "
 | 
			
		||||
                            "Jack and was just signed by Jill.")
 | 
			
		||||
        self.send_and_test_stream_message('signatures', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_signatures_message_with_own_subject(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_signatures_message_with_own_subject(self) -> None:
 | 
			
		||||
        expected_subject = "Our own subject."
 | 
			
		||||
        self.url = self.build_webhook_url(topic=expected_subject)
 | 
			
		||||
        expected_message = ("The NDA with Acme Co. is awaiting the signature of "
 | 
			
		||||
@@ -24,6 +22,5 @@ class HelloSignHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('signatures_with_own_subject', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded", topic=expected_subject)
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Text
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Text:
 | 
			
		||||
        return self.fixture_data("hellosign", fixture_name, file_type="json")
 | 
			
		||||
 
 | 
			
		||||
@@ -10,10 +10,8 @@ from django.http import HttpRequest, HttpResponse
 | 
			
		||||
from six import text_type
 | 
			
		||||
from typing import Any, Dict, List
 | 
			
		||||
 | 
			
		||||
def format_body(signatories, model_payload):
 | 
			
		||||
    # type: (List[Dict[str, Any]], Dict[str, Any]) -> str
 | 
			
		||||
    def append_separator(i):
 | 
			
		||||
        # type: (int) -> None
 | 
			
		||||
def format_body(signatories: List[Dict[str, Any]], model_payload: Dict[str, Any]) -> str:
 | 
			
		||||
    def append_separator(i: int) -> None:
 | 
			
		||||
        if i + 1 == len(signatories):
 | 
			
		||||
            result.append('.')
 | 
			
		||||
        elif i + 2 == len(signatories):
 | 
			
		||||
@@ -33,8 +31,8 @@ def format_body(signatories, model_payload):
 | 
			
		||||
        append_separator(i)
 | 
			
		||||
    return ''.join(result)
 | 
			
		||||
 | 
			
		||||
def ready_payload(signatories, payload):
 | 
			
		||||
    # type: (List[Dict[str, Any]], Dict[str, Dict[str, Any]]) -> Dict[str, Any]
 | 
			
		||||
def ready_payload(signatories: List[Dict[str, Any]],
 | 
			
		||||
                  payload: Dict[str, Dict[str, Any]]) -> Dict[str, Any]:
 | 
			
		||||
    model_payload = {'contract_title': payload['signature_request']['title']}
 | 
			
		||||
    for i, signatory in enumerate(signatories):
 | 
			
		||||
        model_payload['name_{}'.format(i)] = signatory['signer_name']
 | 
			
		||||
 
 | 
			
		||||
@@ -8,8 +8,7 @@ class HelloWorldHookTests(WebhookTestCase):
 | 
			
		||||
    FIXTURE_DIR_NAME = 'hello'
 | 
			
		||||
 | 
			
		||||
    # Note: Include a test function per each distinct message condition your integration supports
 | 
			
		||||
    def test_hello_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_hello_message(self) -> None:
 | 
			
		||||
        expected_subject = u"Hello World"
 | 
			
		||||
        expected_message = u"Hello! I am happy to be here! :smile:\nThe Wikipedia featured article for today is **[Marilyn Monroe](https://en.wikipedia.org/wiki/Marilyn_Monroe)**"
 | 
			
		||||
 | 
			
		||||
@@ -17,8 +16,7 @@ class HelloWorldHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('hello', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_goodbye_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_goodbye_message(self) -> None:
 | 
			
		||||
        expected_subject = u"Hello World"
 | 
			
		||||
        expected_message = u"Hello! I am happy to be here! :smile:\nThe Wikipedia featured article for today is **[Goodbye](https://en.wikipedia.org/wiki/Goodbye)**"
 | 
			
		||||
 | 
			
		||||
@@ -26,6 +24,5 @@ class HelloWorldHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('goodbye', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Text
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Text:
 | 
			
		||||
        return self.fixture_data("helloworld", fixture_name, file_type="json")
 | 
			
		||||
 
 | 
			
		||||
@@ -6,8 +6,7 @@ class HerokuHookTests(WebhookTestCase):
 | 
			
		||||
    STREAM_NAME = 'heroku'
 | 
			
		||||
    URL_TEMPLATE = u"/api/v1/external/heroku?stream={stream}&api_key={api_key}"
 | 
			
		||||
 | 
			
		||||
    def test_deployment(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_deployment(self) -> None:
 | 
			
		||||
        expected_subject = "sample-project"
 | 
			
		||||
        expected_message = u"""user@example.com deployed version 3eb5f44 of \
 | 
			
		||||
[sample-project](http://sample-project.herokuapp.com)
 | 
			
		||||
@@ -15,6 +14,5 @@ class HerokuHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('deploy', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Text
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Text:
 | 
			
		||||
        return self.fixture_data("heroku", fixture_name, file_type="txt")
 | 
			
		||||
 
 | 
			
		||||
@@ -6,22 +6,19 @@ class HomeAssistantHookTests(WebhookTestCase):
 | 
			
		||||
    URL_TEMPLATE = "/api/v1/external/homeassistant?&api_key={api_key}"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'homeassistant'
 | 
			
		||||
 | 
			
		||||
    def test_simplereq(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_simplereq(self) -> None:
 | 
			
		||||
        expected_subject = "homeassistant"
 | 
			
		||||
        expected_message = "The sun will be shining today!"
 | 
			
		||||
 | 
			
		||||
        self.send_and_test_stream_message('simplereq', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_req_with_title(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_req_with_title(self) -> None:
 | 
			
		||||
        expected_subject = "Weather forecast"
 | 
			
		||||
        expected_message = "It will be 30 degrees Celsius out there today!"
 | 
			
		||||
 | 
			
		||||
        self.send_and_test_stream_message('reqwithtitle', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Text
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Text:
 | 
			
		||||
        return self.fixture_data("homeassistant", fixture_name, file_type="json")
 | 
			
		||||
 
 | 
			
		||||
@@ -6,8 +6,7 @@ class IFTTTHookTests(WebhookTestCase):
 | 
			
		||||
    URL_TEMPLATE = "/api/v1/external/ifttt?stream={stream}&api_key={api_key}"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'ifttt'
 | 
			
		||||
 | 
			
		||||
    def test_ifttt_when_subject_and_body_are_correct(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_ifttt_when_subject_and_body_are_correct(self) -> None:
 | 
			
		||||
        expected_subject = u"Email sent from email@email.com"
 | 
			
		||||
        expected_message = u"Email subject: Subject"
 | 
			
		||||
        self.send_and_test_stream_message('correct_subject_and_body', expected_subject, expected_message)
 | 
			
		||||
 
 | 
			
		||||
@@ -6,8 +6,7 @@ class JiraHookTests(WebhookTestCase):
 | 
			
		||||
    STREAM_NAME = 'jira'
 | 
			
		||||
    URL_TEMPLATE = u"/api/v1/external/jira?api_key={api_key}"
 | 
			
		||||
 | 
			
		||||
    def test_unknown(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_unknown(self) -> None:
 | 
			
		||||
        url = self.build_webhook_url()
 | 
			
		||||
 | 
			
		||||
        result = self.client_post(url,
 | 
			
		||||
@@ -24,8 +23,7 @@ class JiraHookTests(WebhookTestCase):
 | 
			
		||||
 | 
			
		||||
        self.assert_json_success(result)
 | 
			
		||||
 | 
			
		||||
    def test_custom_stream(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_custom_stream(self) -> None:
 | 
			
		||||
        api_key = self.test_user.api_key
 | 
			
		||||
        url = "/api/v1/external/jira?api_key=%s&stream=jira_custom" % (api_key,)
 | 
			
		||||
        msg = self.send_json_payload(self.test_user,
 | 
			
		||||
@@ -38,8 +36,7 @@ class JiraHookTests(WebhookTestCase):
 | 
			
		||||
 | 
			
		||||
> New bug with hook""")
 | 
			
		||||
 | 
			
		||||
    def test_created(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_created(self) -> None:
 | 
			
		||||
        expected_subject = "BUG-15: New bug with hook"
 | 
			
		||||
        expected_message = """Leo Franchi **created** [BUG-15](http://lfranchi.com:8080/browse/BUG-15) priority Major, assigned to **no one**:
 | 
			
		||||
 | 
			
		||||
@@ -47,8 +44,7 @@ class JiraHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('created_v1', expected_subject, expected_message)
 | 
			
		||||
        self.send_and_test_stream_message('created_v2', expected_subject, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_created_with_unicode(self):
 | 
			
		||||
            # type: () -> None
 | 
			
		||||
    def test_created_with_unicode(self) -> None:
 | 
			
		||||
            expected_subject = u"BUG-15: New bug with à hook"
 | 
			
		||||
            expected_message = u"""Leo Franchià **created** [BUG-15](http://lfranchi.com:8080/browse/BUG-15) priority Major, assigned to **no one**:
 | 
			
		||||
 | 
			
		||||
@@ -56,8 +52,7 @@ class JiraHookTests(WebhookTestCase):
 | 
			
		||||
            self.send_and_test_stream_message('created_with_unicode_v1', expected_subject, expected_message)
 | 
			
		||||
            self.send_and_test_stream_message('created_with_unicode_v2', expected_subject, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_created_assignee(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_created_assignee(self) -> None:
 | 
			
		||||
        expected_subject = "TEST-4: Test Created Assignee"
 | 
			
		||||
        expected_message = """Leonardo Franchi [Administrator] **created** [TEST-4](https://zulipp.atlassian.net/browse/TEST-4) priority Major, assigned to **Leonardo Franchi [Administrator]**:
 | 
			
		||||
 | 
			
		||||
@@ -65,8 +60,7 @@ class JiraHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('created_assignee_v1', expected_subject, expected_message)
 | 
			
		||||
        self.send_and_test_stream_message('created_assignee_v2', expected_subject, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_commented(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_commented(self) -> None:
 | 
			
		||||
        expected_subject = "BUG-15: New bug with hook"
 | 
			
		||||
        expected_message = """Leo Franchi **added comment to** [BUG-15](http://lfranchi.com:8080/browse/BUG-15) (assigned to **Othello, the Moor of Venice**):
 | 
			
		||||
 | 
			
		||||
@@ -75,8 +69,7 @@ Adding a comment. Oh, what a comment it is!"""
 | 
			
		||||
        self.send_and_test_stream_message('commented_v1', expected_subject, expected_message)
 | 
			
		||||
        self.send_and_test_stream_message('commented_v2', expected_subject, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_comment_edited(self):
 | 
			
		||||
            # type: () -> None
 | 
			
		||||
    def test_comment_edited(self) -> None:
 | 
			
		||||
            expected_subject = "BUG-15: New bug with hook"
 | 
			
		||||
            expected_message = """Leo Franchi **edited comment on** [BUG-15](http://lfranchi.com:8080/browse/BUG-15) (assigned to **Othello, the Moor of Venice**):
 | 
			
		||||
 | 
			
		||||
@@ -84,28 +77,24 @@ Adding a comment. Oh, what a comment it is!"""
 | 
			
		||||
Adding a comment. Oh, what a comment it is!"""
 | 
			
		||||
            self.send_and_test_stream_message('comment_edited_v2', expected_subject, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_comment_deleted(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_comment_deleted(self) -> None:
 | 
			
		||||
        expected_subject = "TOM-1: New Issue"
 | 
			
		||||
        expected_message = "Tomasz Kolek **deleted comment from** [TOM-1](https://zuliptomek.atlassian.net/browse/TOM-1) (assigned to **kolaszek@go2.pl**)"
 | 
			
		||||
        self.send_and_test_stream_message('comment_deleted_v2', expected_subject, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_commented_markup(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_commented_markup(self) -> None:
 | 
			
		||||
        expected_subject = "TEST-7: Testing of rich text"
 | 
			
		||||
        expected_message = """Leonardo Franchi [Administrator] **added comment to** [TEST-7](https://zulipp.atlassian.net/browse/TEST-7):\n\n\nThis is a comment that likes to **exercise** a lot of _different_ `conventions` that `jira uses`.\r\n\r\n~~~\n\r\nthis code is not highlighted, but monospaced\r\n\n~~~\r\n\r\n~~~\n\r\ndef python():\r\n    print "likes to be formatted"\r\n\n~~~\r\n\r\n[http://www.google.com](http://www.google.com) is a bare link, and [Google](http://www.google.com) is given a title.\r\n\r\nThanks!\r\n\r\n~~~ quote\n\r\nSomeone said somewhere\r\n\n~~~"""
 | 
			
		||||
        self.send_and_test_stream_message('commented_markup_v1', expected_subject, expected_message)
 | 
			
		||||
        self.send_and_test_stream_message('commented_markup_v2', expected_subject, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_deleted(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_deleted(self) -> None:
 | 
			
		||||
        expected_subject = "BUG-15: New bug with hook"
 | 
			
		||||
        expected_message = "Leo Franchi **deleted** [BUG-15](http://lfranchi.com:8080/browse/BUG-15)!"
 | 
			
		||||
        self.send_and_test_stream_message('deleted_v1', expected_subject, expected_message)
 | 
			
		||||
        self.send_and_test_stream_message('deleted_v2', expected_subject, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_reassigned(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_reassigned(self) -> None:
 | 
			
		||||
        expected_subject = "BUG-15: New bug with hook"
 | 
			
		||||
        expected_message = """Leo Franchi **updated** [BUG-15](http://lfranchi.com:8080/browse/BUG-15) (assigned to **Othello, the Moor of Venice**):
 | 
			
		||||
 | 
			
		||||
@@ -113,8 +102,7 @@ Adding a comment. Oh, what a comment it is!"""
 | 
			
		||||
        self.send_and_test_stream_message('reassigned_v1', expected_subject, expected_message)
 | 
			
		||||
        self.send_and_test_stream_message('reassigned_v2', expected_subject, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_priority_updated(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_priority_updated(self) -> None:
 | 
			
		||||
        expected_subject = "TEST-1: Fix That"
 | 
			
		||||
        expected_message = """Leonardo Franchi [Administrator] **updated** [TEST-1](https://zulipp.atlassian.net/browse/TEST-1) (assigned to **leo@zulip.com**):
 | 
			
		||||
 | 
			
		||||
@@ -122,8 +110,7 @@ Adding a comment. Oh, what a comment it is!"""
 | 
			
		||||
        self.send_and_test_stream_message('updated_priority_v1', expected_subject, expected_message)
 | 
			
		||||
        self.send_and_test_stream_message('updated_priority_v2', expected_subject, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_status_changed(self):
 | 
			
		||||
            # type: () -> None
 | 
			
		||||
    def test_status_changed(self) -> None:
 | 
			
		||||
            expected_subject = "TEST-1: Fix That"
 | 
			
		||||
            expected_message = """Leonardo Franchi [Administrator] **updated** [TEST-1](https://zulipp.atlassian.net/browse/TEST-1):
 | 
			
		||||
 | 
			
		||||
@@ -131,6 +118,5 @@ Adding a comment. Oh, what a comment it is!"""
 | 
			
		||||
            self.send_and_test_stream_message('change_status_v1', expected_subject, expected_message)
 | 
			
		||||
            self.send_and_test_stream_message('change_status_v2', expected_subject, expected_message)
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Text
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Text:
 | 
			
		||||
        return self.fixture_data('jira', fixture_name)
 | 
			
		||||
 
 | 
			
		||||
@@ -23,8 +23,7 @@ IGNORED_EVENTS = [
 | 
			
		||||
    'comment_deleted',  # we handle issue_update event instead
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
def guess_zulip_user_from_jira(jira_username, realm):
 | 
			
		||||
    # type: (Text, Realm) -> Optional[UserProfile]
 | 
			
		||||
def guess_zulip_user_from_jira(jira_username: Text, realm: Realm) -> Optional[UserProfile]:
 | 
			
		||||
    try:
 | 
			
		||||
        # Try to find a matching user in Zulip
 | 
			
		||||
        # We search a user's full name, short name,
 | 
			
		||||
@@ -39,8 +38,7 @@ def guess_zulip_user_from_jira(jira_username, realm):
 | 
			
		||||
    except IndexError:
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
def convert_jira_markup(content, realm):
 | 
			
		||||
    # type: (Text, Realm) -> Text
 | 
			
		||||
def convert_jira_markup(content: Text, realm: Realm) -> Text:
 | 
			
		||||
    # Attempt to do some simplistic conversion of JIRA
 | 
			
		||||
    # formatting to Markdown, for consumption in Zulip
 | 
			
		||||
 | 
			
		||||
@@ -91,8 +89,7 @@ def convert_jira_markup(content, realm):
 | 
			
		||||
 | 
			
		||||
    return content
 | 
			
		||||
 | 
			
		||||
def get_in(payload, keys, default=''):
 | 
			
		||||
    # type: (Dict[str, Any], List[str], Text) -> Any
 | 
			
		||||
def get_in(payload: Dict[str, Any], keys: List[str], default: Text='') -> Any:
 | 
			
		||||
    try:
 | 
			
		||||
        for key in keys:
 | 
			
		||||
            payload = payload[key]
 | 
			
		||||
@@ -100,8 +97,7 @@ def get_in(payload, keys, default=''):
 | 
			
		||||
        return default
 | 
			
		||||
    return payload
 | 
			
		||||
 | 
			
		||||
def get_issue_string(payload, issue_id=None):
 | 
			
		||||
    # type: (Dict[str, Any], Text) -> Text
 | 
			
		||||
def get_issue_string(payload: Dict[str, Any], issue_id: Text=None) -> Text:
 | 
			
		||||
    # Guess the URL as it is not specified in the payload
 | 
			
		||||
    # We assume that there is a /browse/BUG-### page
 | 
			
		||||
    # from the REST url of the issue itself
 | 
			
		||||
@@ -114,8 +110,7 @@ def get_issue_string(payload, issue_id=None):
 | 
			
		||||
    else:
 | 
			
		||||
        return issue_id
 | 
			
		||||
 | 
			
		||||
def get_assignee_mention(assignee_email, realm):
 | 
			
		||||
    # type: (Text, Realm) -> Text
 | 
			
		||||
def get_assignee_mention(assignee_email: Text, realm: Realm) -> Text:
 | 
			
		||||
    if assignee_email != '':
 | 
			
		||||
        try:
 | 
			
		||||
            assignee_name = get_user(assignee_email, realm).full_name
 | 
			
		||||
@@ -124,24 +119,19 @@ def get_assignee_mention(assignee_email, realm):
 | 
			
		||||
        return u"**{}**".format(assignee_name)
 | 
			
		||||
    return ''
 | 
			
		||||
 | 
			
		||||
def get_issue_author(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_issue_author(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return get_in(payload, ['user', 'displayName'])
 | 
			
		||||
 | 
			
		||||
def get_issue_id(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_issue_id(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return get_in(payload, ['issue', 'key'])
 | 
			
		||||
 | 
			
		||||
def get_issue_title(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_issue_title(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return get_in(payload, ['issue', 'fields', 'summary'])
 | 
			
		||||
 | 
			
		||||
def get_issue_subject(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_issue_subject(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return u"{}: {}".format(get_issue_id(payload), get_issue_title(payload))
 | 
			
		||||
 | 
			
		||||
def get_sub_event_for_update_issue(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_sub_event_for_update_issue(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    sub_event = payload.get('issue_event_type_name', '')
 | 
			
		||||
    if sub_event == '':
 | 
			
		||||
        if payload.get('comment'):
 | 
			
		||||
@@ -150,15 +140,13 @@ def get_sub_event_for_update_issue(payload):
 | 
			
		||||
            return 'issue_transited'
 | 
			
		||||
    return sub_event
 | 
			
		||||
 | 
			
		||||
def get_event_type(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Optional[Text]
 | 
			
		||||
def get_event_type(payload: Dict[str, Any]) -> Optional[Text]:
 | 
			
		||||
    event = payload.get('webhookEvent')
 | 
			
		||||
    if event is None and payload.get('transition'):
 | 
			
		||||
        event = 'jira:issue_updated'
 | 
			
		||||
    return event
 | 
			
		||||
 | 
			
		||||
def add_change_info(content, field, from_field, to_field):
 | 
			
		||||
    # type: (Text, Text, Text, Text) -> Text
 | 
			
		||||
def add_change_info(content: Text, field: Text, from_field: Text, to_field: Text) -> Text:
 | 
			
		||||
    content += u"* Changed {}".format(field)
 | 
			
		||||
    if from_field:
 | 
			
		||||
        content += u" from **{}**".format(from_field)
 | 
			
		||||
@@ -166,8 +154,7 @@ def add_change_info(content, field, from_field, to_field):
 | 
			
		||||
        content += u" to {}\n".format(to_field)
 | 
			
		||||
    return content
 | 
			
		||||
 | 
			
		||||
def handle_updated_issue_event(payload, user_profile):
 | 
			
		||||
    # type: (Dict[str, Any], UserProfile) -> Text
 | 
			
		||||
def handle_updated_issue_event(payload: Dict[str, Any], user_profile: UserProfile) -> Text:
 | 
			
		||||
    # Reassigned, commented, reopened, and resolved events are all bundled
 | 
			
		||||
    # into this one 'updated' event type, so we try to extract the meaningful
 | 
			
		||||
    # event that happened
 | 
			
		||||
@@ -223,8 +210,7 @@ def handle_updated_issue_event(payload, user_profile):
 | 
			
		||||
 | 
			
		||||
    return content
 | 
			
		||||
 | 
			
		||||
def handle_created_issue_event(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def handle_created_issue_event(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return u"{} **created** {} priority {}, assigned to **{}**:\n\n> {}".format(
 | 
			
		||||
        get_issue_author(payload),
 | 
			
		||||
        get_issue_string(payload),
 | 
			
		||||
@@ -233,8 +219,7 @@ def handle_created_issue_event(payload):
 | 
			
		||||
        get_issue_title(payload)
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def handle_deleted_issue_event(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def handle_deleted_issue_event(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return u"{} **deleted** {}!".format(get_issue_author(payload), get_issue_string(payload))
 | 
			
		||||
 | 
			
		||||
@api_key_only_webhook_view("JIRA")
 | 
			
		||||
 
 | 
			
		||||
@@ -9,39 +9,33 @@ class LibratoHookTests(WebhookTestCase):
 | 
			
		||||
    FIXTURE_DIR_NAME = 'librato'
 | 
			
		||||
    IS_ATTACHMENT = False
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Text
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Text:
 | 
			
		||||
        if self.IS_ATTACHMENT:
 | 
			
		||||
            return self.fixture_data("librato", fixture_name, file_type='json')
 | 
			
		||||
        return urllib.parse.urlencode({'payload': self.fixture_data("librato", fixture_name, file_type='json')})
 | 
			
		||||
 | 
			
		||||
    def test_alert_message_with_default_topic(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_alert_message_with_default_topic(self) -> None:
 | 
			
		||||
        expected_subject = 'Alert alert.name'
 | 
			
		||||
        expected_message = "Alert [alert_name](https://metrics.librato.com/alerts#/6294535) has triggered! [Reaction steps](http://www.google.pl)\n>Metric `librato.cpu.percent.idle`, sum was below 44 by 300s, recorded at 2016-03-31 09:11:42 UTC\n>Metric `librato.swap.swap.cached`, average was absent  by 300s, recorded at 2016-03-31 09:11:42 UTC\n>Metric `librato.swap.swap.cached`, derivative was above 9 by 300s, recorded at 2016-03-31 09:11:42 UTC"
 | 
			
		||||
        self.send_and_test_stream_message('alert', expected_subject, expected_message, content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_alert_message_with_custom_topic(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_alert_message_with_custom_topic(self) -> None:
 | 
			
		||||
        custom_topic = 'custom_name'
 | 
			
		||||
        self.url = self.build_webhook_url(topic=custom_topic)
 | 
			
		||||
        expected_message = "Alert [alert_name](https://metrics.librato.com/alerts#/6294535) has triggered! [Reaction steps](http://www.google.pl)\n>Metric `librato.cpu.percent.idle`, sum was below 44 by 300s, recorded at 2016-03-31 09:11:42 UTC\n>Metric `librato.swap.swap.cached`, average was absent  by 300s, recorded at 2016-03-31 09:11:42 UTC\n>Metric `librato.swap.swap.cached`, derivative was above 9 by 300s, recorded at 2016-03-31 09:11:42 UTC"
 | 
			
		||||
        self.send_and_test_stream_message('alert', custom_topic, expected_message, content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_three_conditions_alert_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_three_conditions_alert_message(self) -> None:
 | 
			
		||||
        expected_message = "Alert [alert_name](https://metrics.librato.com/alerts#/6294535) has triggered! [Reaction steps](http://www.use.water.pl)\n>Metric `collectd.interface.eth0.if_octets.tx`, absolute_value was above 4 by 300s, recorded at 2016-04-11 20:40:14 UTC\n>Metric `collectd.load.load.longterm`, max was above 99, recorded at 2016-04-11 20:40:14 UTC\n>Metric `librato.swap.swap.cached`, average was absent  by 60s, recorded at 2016-04-11 20:40:14 UTC"
 | 
			
		||||
        expected_subject = 'Alert ToHighTemeprature'
 | 
			
		||||
        self.send_and_test_stream_message('three_conditions_alert', expected_subject, expected_message, content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_alert_clear(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_alert_clear(self) -> None:
 | 
			
		||||
        expected_subject = 'Alert Alert_name'
 | 
			
		||||
        expected_message = "Alert [alert_name](https://metrics.librato.com/alerts#/6309313) has cleared at 2016-04-12 13:11:44 UTC!"
 | 
			
		||||
        self.send_and_test_stream_message('alert_cleared', expected_subject, expected_message, content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_snapshot(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_snapshot(self) -> None:
 | 
			
		||||
        self.IS_ATTACHMENT = True
 | 
			
		||||
        expected_subject = 'Snapshots'
 | 
			
		||||
        expected_message = "**Hamlet** sent a [snapshot](http://snapshots.librato.com/chart/nr5l3n0c-82162.png) of [metric](https://metrics.librato.com/s/spaces/167315/explore/1731491?duration=72039&end_time=1460569409)"
 | 
			
		||||
 
 | 
			
		||||
@@ -21,54 +21,45 @@ SNAPSHOT = 'image_url'
 | 
			
		||||
class LibratoWebhookParser(object):
 | 
			
		||||
    ALERT_URL_TEMPLATE = "https://metrics.librato.com/alerts#/{alert_id}"
 | 
			
		||||
 | 
			
		||||
    def __init__(self, payload, attachments):
 | 
			
		||||
        # type: (Dict[str, Any], List[Dict[str, Any]]) -> None
 | 
			
		||||
    def __init__(self, payload: Dict[str, Any], attachments: List[Dict[str, Any]]) -> None:
 | 
			
		||||
        self.payload = payload
 | 
			
		||||
        self.attachments = attachments
 | 
			
		||||
 | 
			
		||||
    def generate_alert_url(self, alert_id):
 | 
			
		||||
        # type: (int) -> Text
 | 
			
		||||
    def generate_alert_url(self, alert_id: int) -> Text:
 | 
			
		||||
        return self.ALERT_URL_TEMPLATE.format(alert_id=alert_id)
 | 
			
		||||
 | 
			
		||||
    def parse_alert(self):
 | 
			
		||||
        # type: () -> Tuple[int, Text, Text, Text]
 | 
			
		||||
    def parse_alert(self) -> Tuple[int, Text, Text, Text]:
 | 
			
		||||
        alert = self.payload['alert']
 | 
			
		||||
        alert_id = alert['id']
 | 
			
		||||
        return alert_id, alert['name'], self.generate_alert_url(alert_id), alert['runbook_url']
 | 
			
		||||
 | 
			
		||||
    def parse_condition(self, condition):
 | 
			
		||||
        # type: (Dict[str, Any]) -> Tuple[Text, Text, Text, Text]
 | 
			
		||||
    def parse_condition(self, condition: Dict[str, Any]) -> Tuple[Text, Text, Text, Text]:
 | 
			
		||||
        summary_function = condition['summary_function']
 | 
			
		||||
        threshold = condition.get('threshold', '')
 | 
			
		||||
        condition_type = condition['type']
 | 
			
		||||
        duration = condition.get('duration', '')
 | 
			
		||||
        return summary_function, threshold, condition_type, duration
 | 
			
		||||
 | 
			
		||||
    def parse_violation(self, violation):
 | 
			
		||||
        # type: (Dict[str, Any]) -> Tuple[Text, Text]
 | 
			
		||||
    def parse_violation(self, violation: Dict[str, Any]) -> Tuple[Text, Text]:
 | 
			
		||||
        metric_name = violation['metric']
 | 
			
		||||
        recorded_at = datetime.fromtimestamp((violation['recorded_at']),
 | 
			
		||||
                                             tz=timezone_utc).strftime('%Y-%m-%d %H:%M:%S')
 | 
			
		||||
        return metric_name, recorded_at
 | 
			
		||||
 | 
			
		||||
    def parse_conditions(self):
 | 
			
		||||
        # type: () -> List[Dict[str, Any]]
 | 
			
		||||
    def parse_conditions(self) -> List[Dict[str, Any]]:
 | 
			
		||||
        conditions = self.payload['conditions']
 | 
			
		||||
        return conditions
 | 
			
		||||
 | 
			
		||||
    def parse_violations(self):
 | 
			
		||||
        # type: () -> List[Dict[str, Any]]
 | 
			
		||||
    def parse_violations(self) -> List[Dict[str, Any]]:
 | 
			
		||||
        violations = self.payload['violations']['test-source']
 | 
			
		||||
        return violations
 | 
			
		||||
 | 
			
		||||
    def parse_snapshot(self, snapshot):
 | 
			
		||||
        # type: (Dict[str, Any]) -> Tuple[Text, Text, Text]
 | 
			
		||||
    def parse_snapshot(self, snapshot: Dict[str, Any]) -> Tuple[Text, Text, Text]:
 | 
			
		||||
        author_name, image_url, title = snapshot['author_name'], snapshot['image_url'], snapshot['title']
 | 
			
		||||
        return author_name, image_url, title
 | 
			
		||||
 | 
			
		||||
class LibratoWebhookHandler(LibratoWebhookParser):
 | 
			
		||||
    def __init__(self, payload, attachments):
 | 
			
		||||
        # type: (Dict[str, Any], List[Dict[str, Any]]) -> None
 | 
			
		||||
    def __init__(self, payload: Dict[str, Any], attachments: List[Dict[str, Any]]) -> None:
 | 
			
		||||
        super().__init__(payload, attachments)
 | 
			
		||||
        self.payload_available_types = {
 | 
			
		||||
            ALERT_CLEAR: self.handle_alert_clear_message,
 | 
			
		||||
@@ -79,8 +70,7 @@ class LibratoWebhookHandler(LibratoWebhookParser):
 | 
			
		||||
            SNAPSHOT: self.handle_snapshots
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    def find_handle_method(self):
 | 
			
		||||
        # type: () -> Callable[[], Text]
 | 
			
		||||
    def find_handle_method(self) -> Callable[[], Text]:
 | 
			
		||||
        for available_type in self.payload_available_types:
 | 
			
		||||
            if self.payload.get(available_type):
 | 
			
		||||
                return self.payload_available_types[available_type]
 | 
			
		||||
@@ -89,20 +79,17 @@ class LibratoWebhookHandler(LibratoWebhookParser):
 | 
			
		||||
                return self.attachments_available_types[available_type]
 | 
			
		||||
        raise Exception("Unexcepted message type")
 | 
			
		||||
 | 
			
		||||
    def handle(self):
 | 
			
		||||
        # type: () -> Text
 | 
			
		||||
    def handle(self) -> Text:
 | 
			
		||||
        return self.find_handle_method()()
 | 
			
		||||
 | 
			
		||||
    def generate_topic(self):
 | 
			
		||||
        # type: () -> Text
 | 
			
		||||
    def generate_topic(self) -> Text:
 | 
			
		||||
        if self.attachments:
 | 
			
		||||
            return "Snapshots"
 | 
			
		||||
        topic_template = "Alert {alert_name}"
 | 
			
		||||
        alert_id, alert_name, alert_url, alert_runbook_url = self.parse_alert()
 | 
			
		||||
        return topic_template.format(alert_name=alert_name)
 | 
			
		||||
 | 
			
		||||
    def handle_alert_clear_message(self):
 | 
			
		||||
        # type: () -> Text
 | 
			
		||||
    def handle_alert_clear_message(self) -> Text:
 | 
			
		||||
        alert_clear_template = "Alert [alert_name]({alert_url}) has cleared at {trigger_time} UTC!"
 | 
			
		||||
        trigger_time = datetime.fromtimestamp((self.payload['trigger_time']),
 | 
			
		||||
                                              tz=timezone_utc).strftime('%Y-%m-%d %H:%M:%S')
 | 
			
		||||
@@ -110,22 +97,19 @@ class LibratoWebhookHandler(LibratoWebhookParser):
 | 
			
		||||
        content = alert_clear_template.format(alert_name=alert_name, alert_url=alert_url, trigger_time=trigger_time)
 | 
			
		||||
        return content
 | 
			
		||||
 | 
			
		||||
    def handle_snapshots(self):
 | 
			
		||||
        # type: () -> Text
 | 
			
		||||
    def handle_snapshots(self) -> Text:
 | 
			
		||||
        content = u''
 | 
			
		||||
        for attachment in self.attachments:
 | 
			
		||||
            content += self.handle_snapshot(attachment)
 | 
			
		||||
        return content
 | 
			
		||||
 | 
			
		||||
    def handle_snapshot(self, snapshot):
 | 
			
		||||
        # type: (Dict[str, Any]) -> Text
 | 
			
		||||
    def handle_snapshot(self, snapshot: Dict[str, Any]) -> Text:
 | 
			
		||||
        snapshot_template = u"**{author_name}** sent a [snapshot]({image_url}) of [metric]({title})"
 | 
			
		||||
        author_name, image_url, title = self.parse_snapshot(snapshot)
 | 
			
		||||
        content = snapshot_template.format(author_name=author_name, image_url=image_url, title=title)
 | 
			
		||||
        return content
 | 
			
		||||
 | 
			
		||||
    def handle_alert_violation_message(self):
 | 
			
		||||
        # type: () -> Text
 | 
			
		||||
    def handle_alert_violation_message(self) -> Text:
 | 
			
		||||
        alert_violation_template = u"Alert [alert_name]({alert_url}) has triggered! "
 | 
			
		||||
        alert_id, alert_name, alert_url, alert_runbook_url = self.parse_alert()
 | 
			
		||||
        content = alert_violation_template.format(alert_name=alert_name, alert_url=alert_url)
 | 
			
		||||
@@ -135,8 +119,7 @@ class LibratoWebhookHandler(LibratoWebhookParser):
 | 
			
		||||
        content += self.generate_conditions_and_violations()
 | 
			
		||||
        return content
 | 
			
		||||
 | 
			
		||||
    def generate_conditions_and_violations(self):
 | 
			
		||||
        # type: () -> Text
 | 
			
		||||
    def generate_conditions_and_violations(self) -> Text:
 | 
			
		||||
        conditions = self.parse_conditions()
 | 
			
		||||
        violations = self.parse_violations()
 | 
			
		||||
        content = u""
 | 
			
		||||
@@ -144,8 +127,7 @@ class LibratoWebhookHandler(LibratoWebhookParser):
 | 
			
		||||
            content += self.generate_violated_metric_condition(violation, condition)
 | 
			
		||||
        return content
 | 
			
		||||
 | 
			
		||||
    def generate_violated_metric_condition(self, violation, condition):
 | 
			
		||||
        # type: (Dict[str, Any], Dict[str, Any]) -> Text
 | 
			
		||||
    def generate_violated_metric_condition(self, violation: Dict[str, Any], condition: Dict[str, Any]) -> Text:
 | 
			
		||||
        summary_function, threshold, condition_type, duration = self.parse_condition(condition)
 | 
			
		||||
        metric_name, recorded_at = self.parse_violation(violation)
 | 
			
		||||
        metric_condition_template = u"\n>Metric `{metric_name}`, {summary_function} was {condition_type} {threshold}"
 | 
			
		||||
 
 | 
			
		||||
@@ -7,8 +7,7 @@ class MentionHookTests(WebhookTestCase):
 | 
			
		||||
    URL_TEMPLATE = "/api/v1/external/mention?api_key={api_key}&stream={stream}"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'mention'
 | 
			
		||||
 | 
			
		||||
    def test_mention_webfeed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_mention_webfeed(self) -> None:
 | 
			
		||||
        expected_topic = u"news"
 | 
			
		||||
        expected_message = (u"**[Historical Sexual Abuse (Football): 29 Nov 2016: House of Commons debates - TheyWorkForYou]"
 | 
			
		||||
                            u"(https://www.theyworkforyou.com/debates/?id=2016-11-29b.1398.7&p=24887)**:\n"
 | 
			
		||||
@@ -21,6 +20,5 @@ class MentionHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('webfeeds', expected_topic, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Text
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Text:
 | 
			
		||||
        return self.fixture_data("mention", fixture_name, file_type="json")
 | 
			
		||||
 
 | 
			
		||||
@@ -6,8 +6,7 @@ class NewRelicHookTests(WebhookTestCase):
 | 
			
		||||
    STREAM_NAME = 'newrelic'
 | 
			
		||||
    URL_TEMPLATE = u"/api/v1/external/newrelic?stream={stream}&api_key={api_key}"
 | 
			
		||||
 | 
			
		||||
    def test_alert(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_alert(self) -> None:
 | 
			
		||||
        expected_subject = "Apdex score fell below critical level of 0.90"
 | 
			
		||||
        expected_message = 'Alert opened on [application name]: \
 | 
			
		||||
Apdex score fell below critical level of 0.90\n\
 | 
			
		||||
@@ -15,14 +14,12 @@ Apdex score fell below critical level of 0.90\n\
 | 
			
		||||
        self.send_and_test_stream_message('alert', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_deployment(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_deployment(self) -> None:
 | 
			
		||||
        expected_subject = 'Test App deploy'
 | 
			
		||||
        expected_message = '`1242` deployed by **Zulip Test**\n\
 | 
			
		||||
Description sent via curl\n\nChangelog string'
 | 
			
		||||
        self.send_and_test_stream_message('deployment', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Text
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Text:
 | 
			
		||||
        return self.fixture_data("newrelic", fixture_name, file_type="txt")
 | 
			
		||||
 
 | 
			
		||||
@@ -7,8 +7,7 @@ class OpsGenieHookTests(WebhookTestCase):
 | 
			
		||||
    URL_TEMPLATE = "/api/v1/external/opsgenie?&api_key={api_key}"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'opsgenie'
 | 
			
		||||
 | 
			
		||||
    def test_acknowledge_alert(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_acknowledge_alert(self) -> None:
 | 
			
		||||
        expected_subject = u"Integration1"
 | 
			
		||||
        expected_message = (u"**OpsGenie: [Alert for Integration1.](https://app.opsgenie.com/alert/V2#/show/052652ac-5d1c-464a-812a-7dd18bbfba8c)**\n"
 | 
			
		||||
                            u"Type: *Acknowledge*\n"
 | 
			
		||||
@@ -18,8 +17,7 @@ class OpsGenieHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('acknowledge', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_addnote_alert(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_addnote_alert(self) -> None:
 | 
			
		||||
        expected_subject = u"Integration1"
 | 
			
		||||
        expected_message = (u"**OpsGenie: [Alert for Integration1.](https://app.opsgenie.com/alert/V2#/show/052652ac-5d1c-464a-812a-7dd18bbfba8c)**\n"
 | 
			
		||||
                            u"Type: *AddNote*\n"
 | 
			
		||||
@@ -30,8 +28,7 @@ class OpsGenieHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('addnote', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_addrecipient_alert(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_addrecipient_alert(self) -> None:
 | 
			
		||||
        expected_subject = u"Integration1"
 | 
			
		||||
        expected_message = (u"**OpsGenie: [Alert for Integration1.](https://app.opsgenie.com/alert/V2#/show/052652ac-5d1c-464a-812a-7dd18bbfba8c)**\n"
 | 
			
		||||
                            u"Type: *AddRecipient*\n"
 | 
			
		||||
@@ -43,8 +40,7 @@ class OpsGenieHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('addrecipient', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_addtags_alert(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_addtags_alert(self) -> None:
 | 
			
		||||
        expected_subject = u"Integration1"
 | 
			
		||||
        expected_message = (u"**OpsGenie: [Alert for Integration1.](https://app.opsgenie.com/alert/V2#/show/052652ac-5d1c-464a-812a-7dd18bbfba8c)**\n"
 | 
			
		||||
                            u"Type: *AddTags*\n"
 | 
			
		||||
@@ -55,8 +51,7 @@ class OpsGenieHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('addtags', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_addteam_alert(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_addteam_alert(self) -> None:
 | 
			
		||||
        expected_subject = u"Integration1"
 | 
			
		||||
        expected_message = (u"**OpsGenie: [Alert for Integration1.](https://app.opsgenie.com/alert/V2#/show/052652ac-5d1c-464a-812a-7dd18bbfba8c)**\n"
 | 
			
		||||
                            u"Type: *AddTeam*\n"
 | 
			
		||||
@@ -67,8 +62,7 @@ class OpsGenieHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('addteam', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_assignownership_alert(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_assignownership_alert(self) -> None:
 | 
			
		||||
        expected_subject = u"Integration1"
 | 
			
		||||
        expected_message = (u"**OpsGenie: [Alert for Integration1.](https://app.opsgenie.com/alert/V2#/show/052652ac-5d1c-464a-812a-7dd18bbfba8c)**\n"
 | 
			
		||||
                            u"Type: *AssignOwnership*\n"
 | 
			
		||||
@@ -79,8 +73,7 @@ class OpsGenieHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('assignownership', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_close_alert(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_close_alert(self) -> None:
 | 
			
		||||
        expected_subject = u"Integration1"
 | 
			
		||||
        expected_message = (u"**OpsGenie: [Alert for Integration1.](https://app.opsgenie.com/alert/V2#/show/052652ac-5d1c-464a-812a-7dd18bbfba8c)**\n"
 | 
			
		||||
                            u"Type: *Close*\n"
 | 
			
		||||
@@ -89,8 +82,7 @@ class OpsGenieHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('close', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_create_alert(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_create_alert(self) -> None:
 | 
			
		||||
        expected_subject = u"Webhook"
 | 
			
		||||
        expected_message = (u"**OpsGenie: [Alert for Webhook.](https://app.opsgenie.com/alert/V2#/show/ec03dad6-62c8-4c94-b38b-d88f398e900f)**\n"
 | 
			
		||||
                            u"Type: *Create*\n"
 | 
			
		||||
@@ -100,8 +92,7 @@ class OpsGenieHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('create', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_customaction_alert(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_customaction_alert(self) -> None:
 | 
			
		||||
        expected_subject = u"Integration1"
 | 
			
		||||
        expected_message = (u"**OpsGenie: [Alert for Integration1.](https://app.opsgenie.com/alert/V2#/show/052652ac-5d1c-464a-812a-7dd18bbfba8c)**\n"
 | 
			
		||||
                            u"Type: *TestAction*\n"
 | 
			
		||||
@@ -111,8 +102,7 @@ class OpsGenieHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('customaction', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_delete_alert(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_delete_alert(self) -> None:
 | 
			
		||||
        expected_subject = u"Integration1"
 | 
			
		||||
        expected_message = (u"**OpsGenie: [Alert for Integration1.](https://app.opsgenie.com/alert/V2#/show/052652ac-5d1c-464a-812a-7dd18bbfba8c)**\n"
 | 
			
		||||
                            u"Type: *Delete*\n"
 | 
			
		||||
@@ -121,8 +111,7 @@ class OpsGenieHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('delete', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_escalate_alert(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_escalate_alert(self) -> None:
 | 
			
		||||
        expected_subject = u"Webhook_Test"
 | 
			
		||||
        expected_message = (u"**OpsGenie: [Alert for Webhook_Test.](https://app.opsgenie.com/alert/V2#/show/7ba97e3a-d328-4b5e-8f9a-39e945a3869a)**\n"
 | 
			
		||||
                            u"Type: *Escalate*\n"
 | 
			
		||||
@@ -131,8 +120,7 @@ class OpsGenieHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('escalate', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_removetags_alert(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_removetags_alert(self) -> None:
 | 
			
		||||
        expected_subject = u"Integration1"
 | 
			
		||||
        expected_message = (u"**OpsGenie: [Alert for Integration1.](https://app.opsgenie.com/alert/V2#/show/052652ac-5d1c-464a-812a-7dd18bbfba8c)**\n"
 | 
			
		||||
                            u"Type: *RemoveTags*\n"
 | 
			
		||||
@@ -143,8 +131,7 @@ class OpsGenieHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('removetags', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_takeownership_alert(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_takeownership_alert(self) -> None:
 | 
			
		||||
        expected_subject = u"Webhook"
 | 
			
		||||
        expected_message = (u"**OpsGenie: [Alert for Webhook.](https://app.opsgenie.com/alert/V2#/show/8a745a79-3ed3-4044-8427-98e067c0623c)**\n"
 | 
			
		||||
                            u"Type: *TakeOwnership*\n"
 | 
			
		||||
@@ -154,8 +141,7 @@ class OpsGenieHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('takeownership', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_unacknowledge_alert(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_unacknowledge_alert(self) -> None:
 | 
			
		||||
        expected_subject = u"Integration1"
 | 
			
		||||
        expected_message = (u"**OpsGenie: [Alert for Integration1.](https://app.opsgenie.com/alert/V2#/show/052652ac-5d1c-464a-812a-7dd18bbfba8c)**\n"
 | 
			
		||||
                            u"Type: *UnAcknowledge*\n"
 | 
			
		||||
@@ -165,6 +151,5 @@ class OpsGenieHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('unacknowledge', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Text
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Text:
 | 
			
		||||
        return self.fixture_data("opsgenie", fixture_name, file_type="json")
 | 
			
		||||
 
 | 
			
		||||
@@ -7,42 +7,34 @@ class PagerDutyHookTests(WebhookTestCase):
 | 
			
		||||
    URL_TEMPLATE = u"/api/v1/external/pagerduty?api_key={api_key}&stream={stream}"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'pagerduty'
 | 
			
		||||
 | 
			
		||||
    def test_trigger(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_trigger(self) -> None:
 | 
			
		||||
        expected_message = ':imp: Incident [3](https://zulip-test.pagerduty.com/incidents/P140S4Y) triggered by [Test service](https://zulip-test.pagerduty.com/services/PIL5CUQ) and assigned to [armooo@](https://zulip-test.pagerduty.com/users/POBCFRJ)\n\n>foo'
 | 
			
		||||
        self.send_and_test_stream_message('trigger', u"incident 3", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_unacknowledge(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_unacknowledge(self) -> None:
 | 
			
		||||
        expected_message = ':imp: Incident [3](https://zulip-test.pagerduty.com/incidents/P140S4Y) unacknowledged by [Test service](https://zulip-test.pagerduty.com/services/PIL5CUQ) and assigned to [armooo@](https://zulip-test.pagerduty.com/users/POBCFRJ)\n\n>foo'
 | 
			
		||||
        self.send_and_test_stream_message('unacknowledge', u"incident 3", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_resolved(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_resolved(self) -> None:
 | 
			
		||||
        expected_message = ':grinning: Incident [1](https://zulip-test.pagerduty.com/incidents/PO1XIJ5) resolved by [armooo@](https://zulip-test.pagerduty.com/users/POBCFRJ)\n\n>It is on fire'
 | 
			
		||||
        self.send_and_test_stream_message('resolved', u"incident 1", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_auto_resolved(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_auto_resolved(self) -> None:
 | 
			
		||||
        expected_message = ':grinning: Incident [2](https://zulip-test.pagerduty.com/incidents/PX7K9J2) resolved\n\n>new'
 | 
			
		||||
        self.send_and_test_stream_message('auto_resolved', u"incident 2", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_acknowledge(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_acknowledge(self) -> None:
 | 
			
		||||
        expected_message = ':no_good: Incident [1](https://zulip-test.pagerduty.com/incidents/PO1XIJ5) acknowledged by [armooo@](https://zulip-test.pagerduty.com/users/POBCFRJ)\n\n>It is on fire'
 | 
			
		||||
        self.send_and_test_stream_message('acknowledge', u"incident 1", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_no_subject(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_no_subject(self) -> None:
 | 
			
		||||
        expected_message = u':grinning: Incident [48219](https://dropbox.pagerduty.com/incidents/PJKGZF9) resolved\n\n>mp_error_block_down_critical\u2119\u01b4'
 | 
			
		||||
        self.send_and_test_stream_message('mp_fail', u"incident 48219", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_bad_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_bad_message(self) -> None:
 | 
			
		||||
        expected_message = 'Unknown pagerduty message\n```\n{\n  "type":"incident.triggered"\n}\n```'
 | 
			
		||||
        self.send_and_test_stream_message('bad_message_type', u"pagerduty", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_unknown_message_type(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_unknown_message_type(self) -> None:
 | 
			
		||||
        expected_message = 'Unknown pagerduty message\n```\n{\n  "type":"foo"\n}\n```'
 | 
			
		||||
        self.send_and_test_stream_message('unknown_message_type', u"pagerduty", expected_message)
 | 
			
		||||
 
 | 
			
		||||
@@ -23,8 +23,7 @@ PAGER_DUTY_EVENT_NAMES = {
 | 
			
		||||
    'incident.delegate': 'delineated',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
def build_pagerduty_formatdict(message):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Dict[str, Any]
 | 
			
		||||
def build_pagerduty_formatdict(message: Dict[str, Any]) -> Dict[str, Any]:
 | 
			
		||||
    # Normalize the message dict, after this all keys will exist. I would
 | 
			
		||||
    # rather some strange looking messages than dropping pages.
 | 
			
		||||
 | 
			
		||||
@@ -69,8 +68,11 @@ def build_pagerduty_formatdict(message):
 | 
			
		||||
    return format_dict
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def send_raw_pagerduty_json(user_profile, client, stream, message, topic):
 | 
			
		||||
    # type: (UserProfile, Client, Text, Dict[str, Any], Optional[Text]) -> None
 | 
			
		||||
def send_raw_pagerduty_json(user_profile: UserProfile,
 | 
			
		||||
                            client: Client,
 | 
			
		||||
                            stream: Text,
 | 
			
		||||
                            message: Dict[str, Any],
 | 
			
		||||
                            topic: Optional[Text]) -> None:
 | 
			
		||||
    subject = topic or 'pagerduty'
 | 
			
		||||
    body = (
 | 
			
		||||
        u'Unknown pagerduty message\n'
 | 
			
		||||
@@ -80,8 +82,12 @@ def send_raw_pagerduty_json(user_profile, client, stream, message, topic):
 | 
			
		||||
    check_send_stream_message(user_profile, client, stream, subject, body)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def send_formated_pagerduty(user_profile, client, stream, message_type, format_dict, topic):
 | 
			
		||||
    # type: (UserProfile, Client, Text, Text, Dict[str, Any], Optional[Text]) -> None
 | 
			
		||||
def send_formated_pagerduty(user_profile: UserProfile,
 | 
			
		||||
                            client: Client,
 | 
			
		||||
                            stream: Text,
 | 
			
		||||
                            message_type: Text,
 | 
			
		||||
                            format_dict: Dict[str, Any],
 | 
			
		||||
                            topic: Optional[Text]) -> None:
 | 
			
		||||
    if message_type in ('incident.trigger', 'incident.unacknowledge'):
 | 
			
		||||
        template = (u':imp: Incident '
 | 
			
		||||
                    u'[{incident_num}]({incident_url}) {action} by '
 | 
			
		||||
 
 | 
			
		||||
@@ -7,8 +7,7 @@ class PapertrailHookTests(WebhookTestCase):
 | 
			
		||||
    FIXTURE_DIR_NAME = 'papertrail'
 | 
			
		||||
 | 
			
		||||
    # Note: Include a test function per each distinct message condition your integration supports
 | 
			
		||||
    def test_short_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_short_message(self) -> None:
 | 
			
		||||
        expected_subject = u"logs"
 | 
			
		||||
        expected_message = u'''**"Important stuff"** search found **2** matches - https://papertrailapp.com/searches/42
 | 
			
		||||
```
 | 
			
		||||
@@ -22,8 +21,7 @@ May 18 20:30:02 server1 cron OR server1:
 | 
			
		||||
        self.send_and_test_stream_message('short_post', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_long_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_long_message(self) -> None:
 | 
			
		||||
        expected_subject = u"logs"
 | 
			
		||||
        expected_message = u'''**"Important stuff"** search found **5** matches - https://papertrailapp.com/searches/42
 | 
			
		||||
```
 | 
			
		||||
@@ -41,6 +39,5 @@ May 18 20:30:02 abc cron OR server1:
 | 
			
		||||
        self.send_and_test_stream_message('long_post', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Text
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Text:
 | 
			
		||||
        return self.fixture_data("papertrail", fixture_name, file_type="json")
 | 
			
		||||
 
 | 
			
		||||
@@ -6,32 +6,28 @@ class PingdomHookTests(WebhookTestCase):
 | 
			
		||||
    URL_TEMPLATE = u"/api/v1/external/pingdom?stream={stream}&api_key={api_key}"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'pingdom'
 | 
			
		||||
 | 
			
		||||
    def test_pingdom_from_up_to_down_http_check_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pingdom_from_up_to_down_http_check_message(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Tests if pingdom http check from up to down is handled correctly
 | 
			
		||||
        """
 | 
			
		||||
        expected_message = u"Service someurl.com changed its HTTP status from UP to DOWN.\nDescription: Non-recoverable failure in name resolution."
 | 
			
		||||
        self.send_and_test_stream_message('http_up_to_down', u"Test check status.", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_pingdom_from_up_to_down_smtp_check_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pingdom_from_up_to_down_smtp_check_message(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Tests if pingdom smtp check from up to down is handled correctly
 | 
			
		||||
        """
 | 
			
		||||
        expected_message = u"Service smtp.someurl.com changed its SMTP status from UP to DOWN.\nDescription: Connection refused."
 | 
			
		||||
        self.send_and_test_stream_message('smtp_up_to_down', u"SMTP check status.", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_pingdom_from_up_to_down_imap_check_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pingdom_from_up_to_down_imap_check_message(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Tests if pingdom imap check from up to down is handled correctly
 | 
			
		||||
        """
 | 
			
		||||
        expected_message = u"Service imap.someurl.com changed its IMAP status from UP to DOWN.\nDescription: Invalid hostname, address or socket."
 | 
			
		||||
        self.send_and_test_stream_message('imap_up_to_down', u"IMAP check status.", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_pingdom_from_down_to_up_imap_check_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_pingdom_from_down_to_up_imap_check_message(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Tests if pingdom imap check from down to up is handled correctly
 | 
			
		||||
        """
 | 
			
		||||
 
 | 
			
		||||
@@ -49,13 +49,11 @@ def api_pingdom_webhook(request, user_profile, payload=REQ(argument_type='body')
 | 
			
		||||
    return json_success()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_subject_for_http_request(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_subject_for_http_request(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return PINGDOM_SUBJECT_TEMPLATE.format(name=payload['check_name'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_body_for_http_request(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_body_for_http_request(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    current_state = payload['current_state']
 | 
			
		||||
    previous_state = payload['previous_state']
 | 
			
		||||
 | 
			
		||||
@@ -72,6 +70,5 @@ def get_body_for_http_request(payload):
 | 
			
		||||
    return body
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_check_type(payload):
 | 
			
		||||
    # type: (Dict[str, Any]) -> Text
 | 
			
		||||
def get_check_type(payload: Dict[str, Any]) -> Text:
 | 
			
		||||
    return payload['check_type']
 | 
			
		||||
 
 | 
			
		||||
@@ -6,95 +6,82 @@ class PivotalV3HookTests(WebhookTestCase):
 | 
			
		||||
    STREAM_NAME = 'pivotal'
 | 
			
		||||
    URL_TEMPLATE = u"/api/v1/external/pivotal?stream={stream}&api_key={api_key}"
 | 
			
		||||
 | 
			
		||||
    def test_accepted(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_accepted(self) -> None:
 | 
			
		||||
        expected_subject = 'My new Feature story'
 | 
			
		||||
        expected_message = 'Leo Franchi accepted "My new Feature story" \
 | 
			
		||||
[(view)](https://www.pivotaltracker.com/s/projects/807213/stories/48276573)'
 | 
			
		||||
        self.send_and_test_stream_message('accepted', expected_subject, expected_message, content_type="application/xml")
 | 
			
		||||
 | 
			
		||||
    def test_commented(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_commented(self) -> None:
 | 
			
		||||
        expected_subject = 'Comment added'
 | 
			
		||||
        expected_message = 'Leo Franchi added comment: "FIX THIS NOW" \
 | 
			
		||||
[(view)](https://www.pivotaltracker.com/s/projects/807213/stories/48276573)'
 | 
			
		||||
        self.send_and_test_stream_message('commented', expected_subject, expected_message, content_type="application/xml")
 | 
			
		||||
 | 
			
		||||
    def test_created(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_created(self) -> None:
 | 
			
		||||
        expected_subject = 'My new Feature story'
 | 
			
		||||
        expected_message = 'Leo Franchi added "My new Feature story" \
 | 
			
		||||
(unscheduled feature):\n\n~~~ quote\nThis is my long description\n~~~\n\n \
 | 
			
		||||
[(view)](https://www.pivotaltracker.com/s/projects/807213/stories/48276573)'
 | 
			
		||||
        self.send_and_test_stream_message('created', expected_subject, expected_message, content_type="application/xml")
 | 
			
		||||
 | 
			
		||||
    def test_delivered(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_delivered(self) -> None:
 | 
			
		||||
        expected_subject = 'Another new story'
 | 
			
		||||
        expected_message = 'Leo Franchi delivered "Another new story" \
 | 
			
		||||
[(view)](https://www.pivotaltracker.com/s/projects/807213/stories/48278289)'
 | 
			
		||||
        self.send_and_test_stream_message('delivered', expected_subject, expected_message, content_type="application/xml")
 | 
			
		||||
 | 
			
		||||
    def test_finished(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_finished(self) -> None:
 | 
			
		||||
        expected_subject = 'Another new story'
 | 
			
		||||
        expected_message = 'Leo Franchi finished "Another new story" \
 | 
			
		||||
[(view)](https://www.pivotaltracker.com/s/projects/807213/stories/48278289)'
 | 
			
		||||
        self.send_and_test_stream_message('finished', expected_subject, expected_message, content_type="application/xml")
 | 
			
		||||
 | 
			
		||||
    def test_moved(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_moved(self) -> None:
 | 
			
		||||
        expected_subject = 'My new Feature story'
 | 
			
		||||
        expected_message = 'Leo Franchi edited "My new Feature story" \
 | 
			
		||||
[(view)](https://www.pivotaltracker.com/s/projects/807213/stories/48276573)'
 | 
			
		||||
        self.send_and_test_stream_message('moved', expected_subject, expected_message, content_type="application/xml")
 | 
			
		||||
 | 
			
		||||
    def test_rejected(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_rejected(self) -> None:
 | 
			
		||||
        expected_subject = 'Another new story'
 | 
			
		||||
        expected_message = 'Leo Franchi rejected "Another new story" with comments: \
 | 
			
		||||
"Not good enough, sorry" [(view)](https://www.pivotaltracker.com/s/projects/807213/stories/48278289)'
 | 
			
		||||
        self.send_and_test_stream_message('rejected', expected_subject, expected_message, content_type="application/xml")
 | 
			
		||||
 | 
			
		||||
    def test_started(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_started(self) -> None:
 | 
			
		||||
        expected_subject = 'Another new story'
 | 
			
		||||
        expected_message = 'Leo Franchi started "Another new story" \
 | 
			
		||||
[(view)](https://www.pivotaltracker.com/s/projects/807213/stories/48278289)'
 | 
			
		||||
        self.send_and_test_stream_message('started', expected_subject, expected_message, content_type="application/xml")
 | 
			
		||||
 | 
			
		||||
    def test_created_estimate(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_created_estimate(self) -> None:
 | 
			
		||||
        expected_subject = 'Another new story'
 | 
			
		||||
        expected_message = 'Leo Franchi added "Another new story" \
 | 
			
		||||
(unscheduled feature worth 2 story points):\n\n~~~ quote\nSome loong description\n~~~\n\n \
 | 
			
		||||
[(view)](https://www.pivotaltracker.com/s/projects/807213/stories/48278289)'
 | 
			
		||||
        self.send_and_test_stream_message('created_estimate', expected_subject, expected_message, content_type="application/xml")
 | 
			
		||||
 | 
			
		||||
    def test_type_changed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_type_changed(self) -> None:
 | 
			
		||||
        expected_subject = 'My new Feature story'
 | 
			
		||||
        expected_message = 'Leo Franchi edited "My new Feature story" \
 | 
			
		||||
[(view)](https://www.pivotaltracker.com/s/projects/807213/stories/48276573)'
 | 
			
		||||
        self.send_and_test_stream_message('type_changed', expected_subject, expected_message, content_type="application/xml")
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Text
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Text:
 | 
			
		||||
        return self.fixture_data('pivotal', fixture_name, file_type='xml')
 | 
			
		||||
 | 
			
		||||
class PivotalV5HookTests(WebhookTestCase):
 | 
			
		||||
    STREAM_NAME = 'pivotal'
 | 
			
		||||
    URL_TEMPLATE = u"/api/v1/external/pivotal?stream={stream}&api_key={api_key}"
 | 
			
		||||
 | 
			
		||||
    def test_accepted(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_accepted(self) -> None:
 | 
			
		||||
        expected_subject = '#63486316: Story of the Year'
 | 
			
		||||
        expected_message = """Leo Franchi updated [Hard Code](https://www.pivotaltracker.com/s/projects/807213): [Story of the Year](http://www.pivotaltracker.com/story/show/63486316):
 | 
			
		||||
* state changed from **unstarted** to **accepted**"""
 | 
			
		||||
        self.send_and_test_stream_message('accepted', expected_subject, expected_message, content_type="application/xml")
 | 
			
		||||
 | 
			
		||||
    def test_commented(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_commented(self) -> None:
 | 
			
		||||
        expected_subject = '#63486316: Story of the Year'
 | 
			
		||||
        expected_message = """Leo Franchi added a comment to [Hard Code](https://www.pivotaltracker.com/s/projects/807213): [Story of the Year](http://www.pivotaltracker.com/story/show/63486316):
 | 
			
		||||
~~~quote
 | 
			
		||||
@@ -102,8 +89,7 @@ A comment on the story
 | 
			
		||||
~~~"""
 | 
			
		||||
        self.send_and_test_stream_message('commented', expected_subject, expected_message, content_type="application/xml")
 | 
			
		||||
 | 
			
		||||
    def test_created(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_created(self) -> None:
 | 
			
		||||
        expected_subject = '#63495662: Story that I created'
 | 
			
		||||
        expected_message = """Leo Franchi created bug: [Hard Code](https://www.pivotaltracker.com/s/projects/807213): [Story that I created](http://www.pivotaltracker.com/story/show/63495662)
 | 
			
		||||
* State is **unscheduled**
 | 
			
		||||
@@ -112,28 +98,24 @@ A comment on the story
 | 
			
		||||
> What a description"""
 | 
			
		||||
        self.send_and_test_stream_message('created', expected_subject, expected_message, content_type="application/xml")
 | 
			
		||||
 | 
			
		||||
    def test_delivered(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_delivered(self) -> None:
 | 
			
		||||
        expected_subject = '#63486316: Story of the Year'
 | 
			
		||||
        expected_message = """Leo Franchi updated [Hard Code](https://www.pivotaltracker.com/s/projects/807213): [Story of the Year](http://www.pivotaltracker.com/story/show/63486316):
 | 
			
		||||
* state changed from **accepted** to **delivered**"""
 | 
			
		||||
        self.send_and_test_stream_message('delivered', expected_subject, expected_message, content_type="application/xml")
 | 
			
		||||
 | 
			
		||||
    def test_finished(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_finished(self) -> None:
 | 
			
		||||
        expected_subject = '#63486316: Story of the Year'
 | 
			
		||||
        expected_message = """Leo Franchi updated [Hard Code](https://www.pivotaltracker.com/s/projects/807213): [Story of the Year](http://www.pivotaltracker.com/story/show/63486316):
 | 
			
		||||
* state changed from **delivered** to **accepted**"""
 | 
			
		||||
        self.send_and_test_stream_message('finished', expected_subject, expected_message, content_type="application/xml")
 | 
			
		||||
 | 
			
		||||
    def test_moved(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_moved(self) -> None:
 | 
			
		||||
        expected_subject = '#63496066: Pivotal Test'
 | 
			
		||||
        expected_message = """Leo Franchi moved [Hard Code](https://www.pivotaltracker.com/s/projects/807213): [Pivotal Test](http://www.pivotaltracker.com/story/show/63496066) from **unstarted** to **unscheduled**"""
 | 
			
		||||
        self.send_and_test_stream_message('moved', expected_subject, expected_message, content_type="application/xml")
 | 
			
		||||
 | 
			
		||||
    def test_rejected(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_rejected(self) -> None:
 | 
			
		||||
        expected_subject = '#63486316: Story of the Year'
 | 
			
		||||
        expected_message = """Leo Franchi updated [Hard Code](https://www.pivotaltracker.com/s/projects/807213): [Story of the Year](http://www.pivotaltracker.com/story/show/63486316):
 | 
			
		||||
* Comment added:
 | 
			
		||||
@@ -143,28 +125,24 @@ Try again next time
 | 
			
		||||
* state changed from **delivered** to **rejected**"""
 | 
			
		||||
        self.send_and_test_stream_message('rejected', expected_subject, expected_message, content_type="application/xml")
 | 
			
		||||
 | 
			
		||||
    def test_started(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_started(self) -> None:
 | 
			
		||||
        expected_subject = '#63495972: Fresh Story'
 | 
			
		||||
        expected_message = """Leo Franchi updated [Hard Code](https://www.pivotaltracker.com/s/projects/807213): [Fresh Story](http://www.pivotaltracker.com/story/show/63495972):
 | 
			
		||||
* state changed from **unstarted** to **started**"""
 | 
			
		||||
        self.send_and_test_stream_message('started', expected_subject, expected_message, content_type="application/xml")
 | 
			
		||||
 | 
			
		||||
    def test_created_estimate(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_created_estimate(self) -> None:
 | 
			
		||||
        expected_subject = '#63496066: Pivotal Test'
 | 
			
		||||
        expected_message = """Leo Franchi updated [Hard Code](https://www.pivotaltracker.com/s/projects/807213): [Pivotal Test](http://www.pivotaltracker.com/story/show/63496066):
 | 
			
		||||
* estimate is now **3 points**"""
 | 
			
		||||
        self.send_and_test_stream_message('created_estimate', expected_subject, expected_message, content_type="application/xml")
 | 
			
		||||
 | 
			
		||||
    def test_type_changed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_type_changed(self) -> None:
 | 
			
		||||
        expected_subject = '#63496066: Pivotal Test'
 | 
			
		||||
        expected_message = """Leo Franchi updated [Hard Code](https://www.pivotaltracker.com/s/projects/807213): [Pivotal Test](http://www.pivotaltracker.com/story/show/63496066):
 | 
			
		||||
* estimate changed from 3 to **0 points**
 | 
			
		||||
* type changed from **feature** to **bug**"""
 | 
			
		||||
        self.send_and_test_stream_message('type_changed', expected_subject, expected_message, content_type="application/xml")
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Text
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Text:
 | 
			
		||||
        return self.fixture_data('pivotal', "v5_{}".format(fixture_name), file_type='json')
 | 
			
		||||
 
 | 
			
		||||
@@ -17,12 +17,11 @@ import ujson
 | 
			
		||||
from typing import Dict, List, Optional, Tuple, Text
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def api_pivotal_webhook_v3(request, user_profile, stream):
 | 
			
		||||
    # type: (HttpRequest, UserProfile, Text) -> Tuple[Text, Text]
 | 
			
		||||
def api_pivotal_webhook_v3(request: HttpRequest, user_profile: UserProfile,
 | 
			
		||||
                           stream: Text) -> Tuple[Text, Text]:
 | 
			
		||||
    payload = xml_fromstring(request.body)
 | 
			
		||||
 | 
			
		||||
    def get_text(attrs):
 | 
			
		||||
        # type: (List[str]) -> str
 | 
			
		||||
    def get_text(attrs: List[str]) -> str:
 | 
			
		||||
        start = payload
 | 
			
		||||
        try:
 | 
			
		||||
            for attr in attrs:
 | 
			
		||||
@@ -72,8 +71,8 @@ def api_pivotal_webhook_v3(request, user_profile, stream):
 | 
			
		||||
            more_info)
 | 
			
		||||
    return subject, content
 | 
			
		||||
 | 
			
		||||
def api_pivotal_webhook_v5(request, user_profile, stream):
 | 
			
		||||
    # type: (HttpRequest, UserProfile, Text) -> Tuple[Text, Text]
 | 
			
		||||
def api_pivotal_webhook_v5(request: HttpRequest, user_profile: UserProfile,
 | 
			
		||||
                           stream: Text) -> Tuple[Text, Text]:
 | 
			
		||||
    payload = ujson.loads(request.body)
 | 
			
		||||
 | 
			
		||||
    event_type = payload["kind"]
 | 
			
		||||
@@ -97,8 +96,7 @@ def api_pivotal_webhook_v5(request, user_profile, stream):
 | 
			
		||||
    content = ""
 | 
			
		||||
    subject = "#%s: %s" % (story_id, story_name)
 | 
			
		||||
 | 
			
		||||
    def extract_comment(change):
 | 
			
		||||
        # type: (Dict[str, Dict]) -> Optional[Text]
 | 
			
		||||
    def extract_comment(change: Dict[str, Dict]) -> Optional[Text]:
 | 
			
		||||
        if change.get("kind") == "comment":
 | 
			
		||||
            return change.get("new_values", {}).get("text", None)
 | 
			
		||||
        return None
 | 
			
		||||
@@ -162,8 +160,7 @@ def api_pivotal_webhook_v5(request, user_profile, stream):
 | 
			
		||||
 | 
			
		||||
@api_key_only_webhook_view("Pivotal")
 | 
			
		||||
@has_request_variables
 | 
			
		||||
def api_pivotal_webhook(request, user_profile, stream=REQ()):
 | 
			
		||||
    # type: (HttpRequest, UserProfile, Text) -> HttpResponse
 | 
			
		||||
def api_pivotal_webhook(request: HttpRequest, user_profile: UserProfile, stream: Text=REQ()) -> HttpResponse:
 | 
			
		||||
    subject = content = None
 | 
			
		||||
    try:
 | 
			
		||||
        subject, content = api_pivotal_webhook_v3(request, user_profile, stream)
 | 
			
		||||
 
 | 
			
		||||
@@ -10,22 +10,19 @@ class SemaphoreHookTests(WebhookTestCase):
 | 
			
		||||
    # contain information on the repo and branch, and the message has links and
 | 
			
		||||
    # details about the build, deploy, server, author, and commit
 | 
			
		||||
 | 
			
		||||
    def test_semaphore_build(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_semaphore_build(self) -> None:
 | 
			
		||||
        expected_subject = u"knighthood/master"  # repo/branch
 | 
			
		||||
        expected_message = u"""[build 314](https://semaphoreci.com/donquixote/knighthood/branches/master/builds/314): passed
 | 
			
		||||
!avatar(don@lamancha.com) [`a490b8d`](https://github.com/donquixote/knighthood/commit/a490b8d508ebbdab1d77a5c2aefa35ceb2d62daf): Create user account for Rocinante :horse:."""
 | 
			
		||||
        self.send_and_test_stream_message('build', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_semaphore_deploy(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_semaphore_deploy(self) -> None:
 | 
			
		||||
        expected_subject = u"knighthood/master"
 | 
			
		||||
        expected_message = u"""[deploy 17](https://semaphoreci.com/donquixote/knighthood/servers/lamancha-271/deploys/17) of [build 314](https://semaphoreci.com/donquixote/knighthood/branches/master/builds/314) on server lamancha-271: passed
 | 
			
		||||
!avatar(don@lamancha.com) [`a490b8d`](https://github.com/donquixote/knighthood/commit/a490b8d508ebbdab1d77a5c2aefa35ceb2d62daf): Create user account for Rocinante :horse:."""
 | 
			
		||||
        self.send_and_test_stream_message('deploy', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Text
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Text:
 | 
			
		||||
        return self.fixture_data("semaphore", fixture_name, file_type="json")
 | 
			
		||||
 
 | 
			
		||||
@@ -6,8 +6,7 @@ class SentryHookTests(WebhookTestCase):
 | 
			
		||||
    URL_TEMPLATE = "/api/v1/external/sentry?&api_key={api_key}"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'sentry'
 | 
			
		||||
 | 
			
		||||
    def test_error_issue_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_error_issue_message(self) -> None:
 | 
			
		||||
        expected_subject = u"zulip"
 | 
			
		||||
        expected_message = u"New ERROR [issue](https://sentry.io/zulip/zulip/issues/156699934/): This is an example python exception."
 | 
			
		||||
        self.send_and_test_stream_message(
 | 
			
		||||
 
 | 
			
		||||
@@ -8,16 +8,14 @@ class SlackWebhookTests(WebhookTestCase):
 | 
			
		||||
    URL_TEMPLATE = "/api/v1/external/slack?stream={stream}&api_key={api_key}"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'slack'
 | 
			
		||||
 | 
			
		||||
    def test_slack_channel_to_topic(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_slack_channel_to_topic(self) -> None:
 | 
			
		||||
 | 
			
		||||
        expected_subject = u"channel: general"
 | 
			
		||||
        expected_message = u"**slack_user**: `test\n`"
 | 
			
		||||
        self.send_and_test_stream_message('message_info', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_slack_channel_to_stream(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_slack_channel_to_stream(self) -> None:
 | 
			
		||||
 | 
			
		||||
        self.STREAM_NAME = 'general'
 | 
			
		||||
        self.url = "{}{}".format(self.url, "&channels_map_to_topics=0")
 | 
			
		||||
@@ -26,39 +24,34 @@ class SlackWebhookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('message_info', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_missing_data_user_name(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_missing_data_user_name(self) -> None:
 | 
			
		||||
 | 
			
		||||
        payload = self.get_body('message_info_missing_user_name')
 | 
			
		||||
        url = self.build_webhook_url()
 | 
			
		||||
        result = self.client_post(url, payload, content_type="application/x-www-form-urlencoded")
 | 
			
		||||
        self.assert_json_error(result, "Missing 'user_name' argument")
 | 
			
		||||
 | 
			
		||||
    def test_missing_data_channel_name(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_missing_data_channel_name(self) -> None:
 | 
			
		||||
 | 
			
		||||
        payload = self.get_body('message_info_missing_channel_name')
 | 
			
		||||
        url = self.build_webhook_url()
 | 
			
		||||
        result = self.client_post(url, payload, content_type="application/x-www-form-urlencoded")
 | 
			
		||||
        self.assert_json_error(result, "Missing 'channel_name' argument")
 | 
			
		||||
 | 
			
		||||
    def test_missing_data_text(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_missing_data_text(self) -> None:
 | 
			
		||||
 | 
			
		||||
        payload = self.get_body('message_info_missing_text')
 | 
			
		||||
        url = self.build_webhook_url()
 | 
			
		||||
        result = self.client_post(url, payload, content_type="application/x-www-form-urlencoded")
 | 
			
		||||
        self.assert_json_error(result, "Missing 'text' argument")
 | 
			
		||||
 | 
			
		||||
    def test_invalid_channels_map_to_topics(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_invalid_channels_map_to_topics(self) -> None:
 | 
			
		||||
 | 
			
		||||
        payload = self.get_body('message_info')
 | 
			
		||||
        url = "{}{}".format(self.url, "&channels_map_to_topics=abc")
 | 
			
		||||
        result = self.client_post(url, payload, content_type="application/x-www-form-urlencoded")
 | 
			
		||||
        self.assert_json_error(result, 'Error: channels_map_to_topics parameter other than 0 or 1')
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (text_type) -> text_type
 | 
			
		||||
    def get_body(self, fixture_name: text_type) -> text_type:
 | 
			
		||||
 | 
			
		||||
        return self.fixture_data("slack", fixture_name, file_type="txt")
 | 
			
		||||
 
 | 
			
		||||
@@ -8,8 +8,7 @@ class SolanoHookTests(WebhookTestCase):
 | 
			
		||||
    URL_TEMPLATE = u"/api/v1/external/solano?api_key={api_key}"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'solano'
 | 
			
		||||
 | 
			
		||||
    def test_solano_message_001(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_solano_message_001(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Build notifications are generated by Solano Labs after build completes.
 | 
			
		||||
        """
 | 
			
		||||
@@ -23,8 +22,7 @@ class SolanoHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('build_001', expected_topic, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_solano_message_002(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_solano_message_002(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Build notifications are generated by Solano Labs after build completes.
 | 
			
		||||
        """
 | 
			
		||||
@@ -37,8 +35,7 @@ class SolanoHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('build_002', expected_topic, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_solano_message_received(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_solano_message_received(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Build notifications are generated by Solano Labs after build completes.
 | 
			
		||||
        """
 | 
			
		||||
@@ -52,14 +49,12 @@ class SolanoHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('received', expected_topic, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_solano_test_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_solano_test_message(self) -> None:
 | 
			
		||||
        expected_topic = u'build update'
 | 
			
		||||
        expected_message = "Solano webhook set up correctly"
 | 
			
		||||
 | 
			
		||||
        self.send_and_test_stream_message('test', expected_topic, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Text
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Text:
 | 
			
		||||
        return self.fixture_data(self.FIXTURE_DIR_NAME, fixture_name, file_type="json")
 | 
			
		||||
 
 | 
			
		||||
@@ -65,8 +65,8 @@ def api_solano_webhook(request, user_profile,
 | 
			
		||||
    check_send_stream_message(user_profile, request.client, stream, topic, body)
 | 
			
		||||
    return json_success()
 | 
			
		||||
 | 
			
		||||
def handle_test_event(user_profile, client, stream, topic):
 | 
			
		||||
    # type: (UserProfile, Client, str, str) -> HttpResponse
 | 
			
		||||
def handle_test_event(user_profile: UserProfile, client: Client, stream: str,
 | 
			
		||||
                      topic: str) -> HttpResponse:
 | 
			
		||||
    body = 'Solano webhook set up correctly'
 | 
			
		||||
    check_send_stream_message(user_profile, client, stream, topic, body)
 | 
			
		||||
    return json_success()
 | 
			
		||||
 
 | 
			
		||||
@@ -8,8 +8,7 @@ class SplunkHookTests(WebhookTestCase):
 | 
			
		||||
    URL_TEMPLATE = "/api/v1/external/splunk?api_key={api_key}&stream={stream}"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'splunk'
 | 
			
		||||
 | 
			
		||||
    def test_splunk_search_one_result(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_splunk_search_one_result(self) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url(topic=u"New Search Alert")
 | 
			
		||||
 | 
			
		||||
        # define the expected message contents
 | 
			
		||||
@@ -22,8 +21,7 @@ class SplunkHookTests(WebhookTestCase):
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_splunk_short_search_name(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_splunk_short_search_name(self) -> None:
 | 
			
		||||
 | 
			
		||||
        # don't provide a topic so the search name is used instead
 | 
			
		||||
        expected_subject = u"This search's name isn't that long"
 | 
			
		||||
@@ -34,8 +32,7 @@ class SplunkHookTests(WebhookTestCase):
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_splunk_long_search_name(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_splunk_long_search_name(self) -> None:
 | 
			
		||||
 | 
			
		||||
        # don't provide a topic so the search name is used instead
 | 
			
		||||
        expected_subject = u"this-search's-got-47-words-37-sentences-58-words-we-wanna..."
 | 
			
		||||
@@ -46,8 +43,7 @@ class SplunkHookTests(WebhookTestCase):
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_splunk_missing_results_link(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_splunk_missing_results_link(self) -> None:
 | 
			
		||||
 | 
			
		||||
        self.url = self.build_webhook_url(topic=u"New Search Alert")
 | 
			
		||||
 | 
			
		||||
@@ -59,8 +55,7 @@ class SplunkHookTests(WebhookTestCase):
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_splunk_missing_search_name(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_splunk_missing_search_name(self) -> None:
 | 
			
		||||
 | 
			
		||||
        self.url = self.build_webhook_url(topic=u"New Search Alert")
 | 
			
		||||
 | 
			
		||||
@@ -72,8 +67,7 @@ class SplunkHookTests(WebhookTestCase):
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_splunk_missing_host(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_splunk_missing_host(self) -> None:
 | 
			
		||||
 | 
			
		||||
        self.url = self.build_webhook_url(topic=u"New Search Alert")
 | 
			
		||||
 | 
			
		||||
@@ -85,8 +79,7 @@ class SplunkHookTests(WebhookTestCase):
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_splunk_missing_source(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_splunk_missing_source(self) -> None:
 | 
			
		||||
 | 
			
		||||
        self.url = self.build_webhook_url(topic=u"New Search Alert")
 | 
			
		||||
 | 
			
		||||
@@ -98,8 +91,7 @@ class SplunkHookTests(WebhookTestCase):
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_splunk_missing_raw(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_splunk_missing_raw(self) -> None:
 | 
			
		||||
 | 
			
		||||
        self.url = self.build_webhook_url(topic=u"New Search Alert")
 | 
			
		||||
 | 
			
		||||
@@ -111,6 +103,5 @@ class SplunkHookTests(WebhookTestCase):
 | 
			
		||||
                                          expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Text
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Text:
 | 
			
		||||
        return self.fixture_data("splunk", fixture_name, file_type="json")
 | 
			
		||||
 
 | 
			
		||||
@@ -9,8 +9,7 @@ class StripeHookTests(WebhookTestCase):
 | 
			
		||||
    URL_TEMPLATE = "/api/v1/external/stripe?&api_key={api_key}"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'stripe'
 | 
			
		||||
 | 
			
		||||
    def test_charge_dispute_closed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_charge_dispute_closed(self) -> None:
 | 
			
		||||
        expected_subject = u"Charge ch_00000000000000"
 | 
			
		||||
        expected_message = u"A charge dispute for **10.01aud** has been closed as **won**.\nThe charge in dispute was **[ch_00000000000000](https://dashboard.stripe.com/payments/ch_00000000000000)**."
 | 
			
		||||
 | 
			
		||||
@@ -18,8 +17,7 @@ class StripeHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('charge_dispute_closed', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_charge_dispute_created(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_charge_dispute_created(self) -> None:
 | 
			
		||||
        expected_subject = u"Charge ch_00000000000000"
 | 
			
		||||
        expected_message = u"A charge dispute for **1000jpy** has been created.\nThe charge in dispute is **[ch_00000000000000](https://dashboard.stripe.com/payments/ch_00000000000000)**."
 | 
			
		||||
 | 
			
		||||
@@ -27,8 +25,7 @@ class StripeHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('charge_dispute_created', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_charge_failed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_charge_failed(self) -> None:
 | 
			
		||||
        expected_subject = u"Charge ch_00000000000000"
 | 
			
		||||
        expected_message = u"A charge with id **[ch_00000000000000](https://dashboard.stripe.com/payments/ch_00000000000000)** for **1.00aud** has failed."
 | 
			
		||||
 | 
			
		||||
@@ -36,8 +33,7 @@ class StripeHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('charge_failed', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_charge_succeeded(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_charge_succeeded(self) -> None:
 | 
			
		||||
        expected_subject = u"Charge ch_00000000000000"
 | 
			
		||||
        expected_message = u"A charge with id **[ch_00000000000000](https://dashboard.stripe.com/payments/ch_00000000000000)** for **1.00aud** has succeeded."
 | 
			
		||||
 | 
			
		||||
@@ -45,8 +41,7 @@ class StripeHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('charge_succeeded', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_customer_created_email(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_customer_created_email(self) -> None:
 | 
			
		||||
        expected_subject = u"Customer cus_00000000000000"
 | 
			
		||||
        expected_message = u"A new customer with id **[cus_00000000000000](https://dashboard.stripe.com/customers/cus_00000000000000)** and email **example@abc.com** has been created."
 | 
			
		||||
 | 
			
		||||
@@ -54,8 +49,7 @@ class StripeHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('customer_created_email', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_customer_created(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_customer_created(self) -> None:
 | 
			
		||||
        expected_subject = u"Customer cus_00000000000000"
 | 
			
		||||
        expected_message = u"A new customer with id **[cus_00000000000000](https://dashboard.stripe.com/customers/cus_00000000000000)** has been created."
 | 
			
		||||
 | 
			
		||||
@@ -63,8 +57,7 @@ class StripeHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('customer_created', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_customer_deleted(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_customer_deleted(self) -> None:
 | 
			
		||||
        expected_subject = u"Customer cus_00000000000000"
 | 
			
		||||
        expected_message = u"A customer with id **[cus_00000000000000](https://dashboard.stripe.com/customers/cus_00000000000000)** has been deleted."
 | 
			
		||||
 | 
			
		||||
@@ -72,8 +65,7 @@ class StripeHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('customer_deleted', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_customer_subscription_created(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_customer_subscription_created(self) -> None:
 | 
			
		||||
        expected_subject = u"Customer sub_00000000000000"
 | 
			
		||||
        expected_message = u"A new customer subscription for **20.00aud** every **month** has been created.\nThe subscription has id **[sub_00000000000000](https://dashboard.stripe.com/subscriptions/sub_00000000000000)**."
 | 
			
		||||
 | 
			
		||||
@@ -81,8 +73,7 @@ class StripeHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('customer_subscription_created', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_customer_subscription_deleted(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_customer_subscription_deleted(self) -> None:
 | 
			
		||||
        expected_subject = u"Customer sub_00000000000000"
 | 
			
		||||
        expected_message = u"The customer subscription with id **[sub_00000000000000](https://dashboard.stripe.com/subscriptions/sub_00000000000000)** was deleted."
 | 
			
		||||
 | 
			
		||||
@@ -90,8 +81,7 @@ class StripeHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('customer_subscription_deleted', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_customer_subscription_trial_will_end(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_customer_subscription_trial_will_end(self) -> None:
 | 
			
		||||
        expected_subject = u"Customer sub_00000000000000"
 | 
			
		||||
        expected_message = u"The customer subscription trial with id **[sub_00000000000000](https://dashboard.stripe.com/subscriptions/sub_00000000000000)** will end in 3 days."
 | 
			
		||||
 | 
			
		||||
@@ -102,8 +92,7 @@ class StripeHookTests(WebhookTestCase):
 | 
			
		||||
                                              expected_subject, expected_message,
 | 
			
		||||
                                              content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_invoice_payment_failed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_invoice_payment_failed(self) -> None:
 | 
			
		||||
        expected_subject = u"Invoice in_00000000000000"
 | 
			
		||||
        expected_message = u"An invoice payment on invoice with id **[in_00000000000000](https://dashboard.stripe.com/invoices/in_00000000000000)** and with **0.00aud** due has failed."
 | 
			
		||||
 | 
			
		||||
@@ -111,8 +100,7 @@ class StripeHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('invoice_payment_failed', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_order_payment_failed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_order_payment_failed(self) -> None:
 | 
			
		||||
        expected_subject = u"Order or_00000000000000"
 | 
			
		||||
        expected_message = u"An order payment on order with id **[or_00000000000000](https://dashboard.stripe.com/orders/or_00000000000000)** for **15.00aud** has failed."
 | 
			
		||||
 | 
			
		||||
@@ -120,8 +108,7 @@ class StripeHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('order_payment_failed', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_order_payment_succeeded(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_order_payment_succeeded(self) -> None:
 | 
			
		||||
        expected_subject = u"Order or_00000000000000"
 | 
			
		||||
        expected_message = u"An order payment on order with id **[or_00000000000000](https://dashboard.stripe.com/orders/or_00000000000000)** for **15.00aud** has succeeded."
 | 
			
		||||
 | 
			
		||||
@@ -129,8 +116,7 @@ class StripeHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('order_payment_succeeded', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_order_updated(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_order_updated(self) -> None:
 | 
			
		||||
        expected_subject = u"Order or_00000000000000"
 | 
			
		||||
        expected_message = u"The order with id **[or_00000000000000](https://dashboard.stripe.com/orders/or_00000000000000)** for **15.00aud** has been updated."
 | 
			
		||||
 | 
			
		||||
@@ -138,8 +124,7 @@ class StripeHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('order_updated', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_transfer_failed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_transfer_failed(self) -> None:
 | 
			
		||||
        expected_subject = u"Transfer tr_00000000000000"
 | 
			
		||||
        expected_message = u"The transfer with description **Transfer to test@example.com** and id **[tr_00000000000000](https://dashboard.stripe.com/transfers/tr_00000000000000)** for amount **11.00aud** has failed."
 | 
			
		||||
 | 
			
		||||
@@ -147,8 +132,7 @@ class StripeHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('transfer_failed', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_transfer_paid(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_transfer_paid(self) -> None:
 | 
			
		||||
        expected_subject = u"Transfer tr_00000000000000"
 | 
			
		||||
        expected_message = u"The transfer with description **Transfer to test@example.com** and id **[tr_00000000000000](https://dashboard.stripe.com/transfers/tr_00000000000000)** for amount **11.00aud** has been paid."
 | 
			
		||||
 | 
			
		||||
@@ -156,6 +140,5 @@ class StripeHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('transfer_paid', expected_subject, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Text
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Text:
 | 
			
		||||
        return self.fixture_data("stripe", fixture_name, file_type="json")
 | 
			
		||||
 
 | 
			
		||||
@@ -164,8 +164,7 @@ def api_stripe_webhook(request, user_profile,
 | 
			
		||||
 | 
			
		||||
    return json_success()
 | 
			
		||||
 | 
			
		||||
def amount(amount, currency):
 | 
			
		||||
    # type: (int, str) -> str
 | 
			
		||||
def amount(amount: int, currency: str) -> str:
 | 
			
		||||
    # zero-decimal currencies
 | 
			
		||||
    zero_decimal_currencies = ["bif", "djf", "jpy", "krw", "pyg", "vnd", "xaf",
 | 
			
		||||
                               "xpf", "clp", "gnf", "kmf", "mga", "rwf", "vuv", "xof"]
 | 
			
		||||
 
 | 
			
		||||
@@ -8,281 +8,225 @@ class TaigaHookTests(WebhookTestCase):
 | 
			
		||||
    URL_TEMPLATE = u"/api/v1/external/taiga?stream={stream}&api_key={api_key}"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'taiga'
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def setUp(self) -> None:
 | 
			
		||||
        self.url = self.build_webhook_url(topic=self.TOPIC)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_userstory_deleted(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_userstory_deleted(self) -> None:
 | 
			
		||||
        message = u':x: TomaszKolek deleted user story **New userstory**.'
 | 
			
		||||
        self.send_and_test_stream_message("userstory_deleted", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_userstory_created(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_userstory_created(self) -> None:
 | 
			
		||||
        message = u':package: TomaszKolek created user story **New userstory**.'
 | 
			
		||||
        self.send_and_test_stream_message("userstory_created", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_userstory_changed_unblocked(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_userstory_changed_unblocked(self) -> None:
 | 
			
		||||
        message = u':unlock: TomaszKolek unblocked user story **UserStory**.'
 | 
			
		||||
        self.send_and_test_stream_message("userstory_changed_unblocked", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_userstory_changed_subject(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_userstory_changed_subject(self) -> None:
 | 
			
		||||
        message = u':notebook: TomaszKolek renamed user story from UserStory to **UserStoryNewSubject**.'
 | 
			
		||||
        self.send_and_test_stream_message("userstory_changed_subject", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_userstory_changed_status(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_userstory_changed_status(self) -> None:
 | 
			
		||||
        message = u':chart_with_upwards_trend: TomaszKolek changed status of user story **UserStory** from Ready to In progress.'
 | 
			
		||||
        self.send_and_test_stream_message("userstory_changed_status", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_userstory_changed_reassigned(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_userstory_changed_reassigned(self) -> None:
 | 
			
		||||
        message = u':busts_in_silhouette: TomaszKolek reassigned user story **UserStory** from TomaszKolek to HanSolo.'
 | 
			
		||||
        self.send_and_test_stream_message("userstory_changed_reassigned", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_userstory_changed_unassigned(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_userstory_changed_unassigned(self) -> None:
 | 
			
		||||
        message = u':busts_in_silhouette: TomaszKolek unassigned user story **UserStory**.'
 | 
			
		||||
        self.send_and_test_stream_message("userstory_changed_unassigned", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_userstory_changed_points(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_userstory_changed_points(self) -> None:
 | 
			
		||||
        message = u':game_die: TomaszKolek changed estimation of user story **UserStory**.'
 | 
			
		||||
        self.send_and_test_stream_message("userstory_changed_points", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_userstory_changed_new_sprint(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_userstory_changed_new_sprint(self) -> None:
 | 
			
		||||
        message = u':calendar: TomaszKolek added user story **UserStory** to sprint Sprint1.'
 | 
			
		||||
        self.send_and_test_stream_message("userstory_changed_new_sprint", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_userstory_changed_sprint(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_userstory_changed_sprint(self) -> None:
 | 
			
		||||
        message = u':calendar: TomaszKolek changed sprint of user story **UserStory** from Sprint1 to Sprint2.'
 | 
			
		||||
        self.send_and_test_stream_message("userstory_changed_sprint", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_userstory_changed_remove_sprint(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_userstory_changed_remove_sprint(self) -> None:
 | 
			
		||||
        message = u':calendar: TomaszKolek removed user story **UserStory** from sprint Sprint2.'
 | 
			
		||||
        self.send_and_test_stream_message("userstory_changed_remove_sprint", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_userstory_changed_description(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_userstory_changed_description(self) -> None:
 | 
			
		||||
        message = u':notebook: TomaszKolek updated description of user story **UserStory**.'
 | 
			
		||||
        self.send_and_test_stream_message("userstory_changed_description", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_userstory_changed_closed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_userstory_changed_closed(self) -> None:
 | 
			
		||||
        message = u':chart_with_upwards_trend: TomaszKolek changed status of user story **UserStory** from New to Done.\n:checkered_flag: TomaszKolek closed user story **UserStory**.'
 | 
			
		||||
        self.send_and_test_stream_message("userstory_changed_closed", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_userstory_changed_reopened(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_userstory_changed_reopened(self) -> None:
 | 
			
		||||
        message = u':chart_with_upwards_trend: TomaszKolek changed status of user story **UserStory** from Done to Ready.\n:package: TomaszKolek reopened user story **UserStory**.'
 | 
			
		||||
        self.send_and_test_stream_message("userstory_changed_reopened", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_userstory_changed_blocked(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_userstory_changed_blocked(self) -> None:
 | 
			
		||||
        message = u':lock: TomaszKolek blocked user story **UserStory**.'
 | 
			
		||||
        self.send_and_test_stream_message("userstory_changed_blocked", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_userstory_changed_assigned(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_userstory_changed_assigned(self) -> None:
 | 
			
		||||
        message = u':busts_in_silhouette: TomaszKolek assigned user story **UserStory** to TomaszKolek.'
 | 
			
		||||
        self.send_and_test_stream_message("userstory_changed_assigned", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_userstory_comment_added(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_userstory_comment_added(self) -> None:
 | 
			
		||||
        message = u':thought_balloon: TomaszKolek commented on user story **UserStory**.'
 | 
			
		||||
        self.send_and_test_stream_message("userstory_changed_comment_added", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_task_created(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_task_created(self) -> None:
 | 
			
		||||
        message = u':clipboard: TomaszKolek created task **New Task**.'
 | 
			
		||||
        self.send_and_test_stream_message("task_created", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_task_changed_status(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_task_changed_status(self) -> None:
 | 
			
		||||
        message = u':chart_with_upwards_trend: TomaszKolek changed status of task **New Task** from New to In progress.'
 | 
			
		||||
        self.send_and_test_stream_message("task_changed_status", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_task_changed_blocked(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_task_changed_blocked(self) -> None:
 | 
			
		||||
        message = u':lock: TomaszKolek blocked task **New Task**.'
 | 
			
		||||
        self.send_and_test_stream_message("task_changed_blocked", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_task_changed_unblocked(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_task_changed_unblocked(self) -> None:
 | 
			
		||||
        message = u':unlock: TomaszKolek unblocked task **New Task**.'
 | 
			
		||||
        self.send_and_test_stream_message("task_changed_unblocked", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_task_changed_assigned(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_task_changed_assigned(self) -> None:
 | 
			
		||||
        message = u':busts_in_silhouette: TomaszKolek assigned task **New Task** to TomaszKolek.'
 | 
			
		||||
        self.send_and_test_stream_message("task_changed_assigned", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_task_changed_reassigned(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_task_changed_reassigned(self) -> None:
 | 
			
		||||
        message = u':busts_in_silhouette: TomaszKolek reassigned task **New Task** from HanSolo to TomaszKolek.'
 | 
			
		||||
        self.send_and_test_stream_message("task_changed_reassigned", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_task_changed_subject(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_task_changed_subject(self) -> None:
 | 
			
		||||
        message = u':notebook: TomaszKolek renamed task New Task to **New Task Subject**.'
 | 
			
		||||
        self.send_and_test_stream_message("task_changed_subject", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_task_changed_description(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_task_changed_description(self) -> None:
 | 
			
		||||
        message = u':notebook: TomaszKolek updated description of task **New Task**.'
 | 
			
		||||
        self.send_and_test_stream_message("task_changed_description", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_task_deleted(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_task_deleted(self) -> None:
 | 
			
		||||
        message = u':x: TomaszKolek deleted task **New Task**.'
 | 
			
		||||
        self.send_and_test_stream_message("task_deleted", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_task_changed_comment_added(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_task_changed_comment_added(self) -> None:
 | 
			
		||||
        message = u':thought_balloon: TomaszKolek commented on task **New Task**.'
 | 
			
		||||
        self.send_and_test_stream_message("task_changed_comment_added", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_sprint_created(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_sprint_created(self) -> None:
 | 
			
		||||
        message = u':calendar: TomaszKolek created sprint **New sprint**.'
 | 
			
		||||
        self.send_and_test_stream_message("sprint_created", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_sprint_deleted(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_sprint_deleted(self) -> None:
 | 
			
		||||
        message = u':x: TomaszKolek deleted sprint **New name**.'
 | 
			
		||||
        self.send_and_test_stream_message("sprint_deleted", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_sprint_changed_time(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_sprint_changed_time(self) -> None:
 | 
			
		||||
        message = u':calendar: TomaszKolek changed estimated finish of sprint **New sprint** from 2017-01-24 to 2017-01-25.'
 | 
			
		||||
        self.send_and_test_stream_message("sprint_changed_time", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_sprint_changed_name(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_sprint_changed_name(self) -> None:
 | 
			
		||||
        message = u':notebook: TomaszKolek renamed sprint from New sprint to **New name**.'
 | 
			
		||||
        self.send_and_test_stream_message("sprint_changed_name", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_issue_created(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_issue_created(self) -> None:
 | 
			
		||||
        message = u':bulb: TomaszKolek created issue **New issue**.'
 | 
			
		||||
        self.send_and_test_stream_message("issue_created", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_issue_deleted(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_issue_deleted(self) -> None:
 | 
			
		||||
        message = u':x: TomaszKolek deleted issue **New issue**.'
 | 
			
		||||
        self.send_and_test_stream_message("issue_deleted", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_issue_changed_assigned(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_issue_changed_assigned(self) -> None:
 | 
			
		||||
        message = u':busts_in_silhouette: TomaszKolek assigned issue **New issue** to TomaszKolek.'
 | 
			
		||||
        self.send_and_test_stream_message("issue_changed_assigned", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_issue_changed_reassigned(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_issue_changed_reassigned(self) -> None:
 | 
			
		||||
        message = u':busts_in_silhouette: TomaszKolek reassigned issue **New issue** from TomaszKolek to HanSolo.'
 | 
			
		||||
        self.send_and_test_stream_message("issue_changed_reassigned", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_issue_changed_subject(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_issue_changed_subject(self) -> None:
 | 
			
		||||
        message = u':notebook: TomaszKolek renamed issue New issue to **New issueNewSubject**.'
 | 
			
		||||
        self.send_and_test_stream_message("issue_changed_subject", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_issue_changed_description(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_issue_changed_description(self) -> None:
 | 
			
		||||
        message = u':notebook: TomaszKolek updated description of issue **New issue**.'
 | 
			
		||||
        self.send_and_test_stream_message("issue_changed_description", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_issue_changed_type(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_issue_changed_type(self) -> None:
 | 
			
		||||
        message = u':bulb: TomaszKolek changed type of issue **New issue** from Bug to Question.'
 | 
			
		||||
        self.send_and_test_stream_message("issue_changed_type", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_issue_changed_status(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_issue_changed_status(self) -> None:
 | 
			
		||||
        message = u':chart_with_upwards_trend: TomaszKolek changed status of issue **New issue** from New to In progress.'
 | 
			
		||||
        self.send_and_test_stream_message("issue_changed_status", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_issue_changed_severity(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_issue_changed_severity(self) -> None:
 | 
			
		||||
        message = u':warning: TomaszKolek changed severity of issue **New issue** from Normal to Minor.'
 | 
			
		||||
        self.send_and_test_stream_message("issue_changed_severity", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_issue_changed_priority(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_issue_changed_priority(self) -> None:
 | 
			
		||||
        message = u':rocket: TomaszKolek changed priority of issue **New issue** from Normal to Low.'
 | 
			
		||||
        self.send_and_test_stream_message("issue_changed_priority", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_issue_changed_comment_added(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_issue_changed_comment_added(self) -> None:
 | 
			
		||||
        message = u':thought_balloon: TomaszKolek commented on issue **New issue**.'
 | 
			
		||||
        self.send_and_test_stream_message("issue_changed_comment_added", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_epic_created(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_epic_created(self) -> None:
 | 
			
		||||
        message = u':package: Eeshan Garg created epic **Zulip is awesome!**'
 | 
			
		||||
        self.send_and_test_stream_message("epic_created", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_epic_changed_assigned(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_epic_changed_assigned(self) -> None:
 | 
			
		||||
        message = u':busts_in_silhouette: Eeshan Garg assigned epic **Zulip is awesome!** to Eeshan Garg.'
 | 
			
		||||
        self.send_and_test_stream_message("epic_changed_assigned", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_epic_changed_unassigned(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_epic_changed_unassigned(self) -> None:
 | 
			
		||||
        message = u':busts_in_silhouette: Eeshan Garg unassigned epic **Zulip is awesome!**'
 | 
			
		||||
        self.send_and_test_stream_message("epic_changed_unassigned", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_epic_changed_reassigned(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_epic_changed_reassigned(self) -> None:
 | 
			
		||||
        message = u':busts_in_silhouette: Eeshan Garg reassigned epic **Zulip is awesome!** from Eeshan Garg to Angela Johnson.'
 | 
			
		||||
        self.send_and_test_stream_message("epic_changed_reassigned", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_epic_changed_blocked(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_epic_changed_blocked(self) -> None:
 | 
			
		||||
        message = u':lock: Eeshan Garg blocked epic **Zulip is awesome!**'
 | 
			
		||||
        self.send_and_test_stream_message("epic_changed_blocked", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_epic_changed_unblocked(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_epic_changed_unblocked(self) -> None:
 | 
			
		||||
        message = u':unlock: Eeshan Garg unblocked epic **Zulip is awesome!**'
 | 
			
		||||
        self.send_and_test_stream_message("epic_changed_unblocked", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_epic_changed_status(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_epic_changed_status(self) -> None:
 | 
			
		||||
        message = u':chart_increasing: Eeshan Garg changed status of epic **Zulip is awesome!** from New to In progress.'
 | 
			
		||||
        self.send_and_test_stream_message("epic_changed_status", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_epic_changed_renamed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_epic_changed_renamed(self) -> None:
 | 
			
		||||
        message = u':notebook: Eeshan Garg renamed epic from **Zulip is awesome!** to **Zulip is great!**'
 | 
			
		||||
        self.send_and_test_stream_message("epic_changed_renamed", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_epic_changed_description(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_epic_changed_description(self) -> None:
 | 
			
		||||
        message = u':notebook: Eeshan Garg updated description of epic **Zulip is great!**'
 | 
			
		||||
        self.send_and_test_stream_message("epic_changed_description", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_epic_changed_commented(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_epic_changed_commented(self) -> None:
 | 
			
		||||
        message = u':thought_balloon: Eeshan Garg commented on epic **Zulip is great!**'
 | 
			
		||||
        self.send_and_test_stream_message("epic_changed_commented", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_epic_deleted(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_epic_deleted(self) -> None:
 | 
			
		||||
        message = u':cross_mark: Eeshan Garg deleted epic **Zulip is great!**'
 | 
			
		||||
        self.send_and_test_stream_message("epic_deleted", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_relateduserstory_created(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_relateduserstory_created(self) -> None:
 | 
			
		||||
        message = u':package: Eeshan Garg added a related user story **A related user story** to the epic **This is Epic!**'
 | 
			
		||||
        self.send_and_test_stream_message("relateduserstory_created", u'subject', message)
 | 
			
		||||
 | 
			
		||||
    def test_taiga_relateduserstory_deleted(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_taiga_relateduserstory_deleted(self) -> None:
 | 
			
		||||
        message = u':cross_mark: Eeshan Garg removed a related user story **A related user story, which is epic** from the epic **This is Epic!**'
 | 
			
		||||
        self.send_and_test_stream_message("relateduserstory_deleted", u'subject', message)
 | 
			
		||||
 
 | 
			
		||||
@@ -139,8 +139,9 @@ templates = {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_old_and_new_values(change_type, message):
 | 
			
		||||
    # type: (str, Mapping[str, Any]) -> Tuple[Optional[Dict[str, Any]], Optional[Dict[str, Any]]]
 | 
			
		||||
return_type = Tuple[Optional[Dict[str, Any]], Optional[Dict[str, Any]]]
 | 
			
		||||
def get_old_and_new_values(change_type: str,
 | 
			
		||||
                           message: Mapping[str, Any]) -> return_type:
 | 
			
		||||
    """ Parses the payload and finds previous and current value of change_type."""
 | 
			
		||||
    if change_type in ['subject', 'name', 'estimated_finish', 'estimated_start']:
 | 
			
		||||
        old = message["change"]["diff"][change_type]["from"]
 | 
			
		||||
@@ -160,8 +161,7 @@ def get_old_and_new_values(change_type, message):
 | 
			
		||||
    return old, new
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def parse_comment(message):
 | 
			
		||||
    # type: (Mapping[str, Any]) -> Dict[str, Any]
 | 
			
		||||
def parse_comment(message: Mapping[str, Any]) -> Dict[str, Any]:
 | 
			
		||||
    """ Parses the comment to issue, task or US. """
 | 
			
		||||
    return {
 | 
			
		||||
        'event': 'commented',
 | 
			
		||||
@@ -172,8 +172,7 @@ def parse_comment(message):
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
def parse_create_or_delete(message):
 | 
			
		||||
    # type: (Mapping[str, Any]) -> Dict[str, Any]
 | 
			
		||||
def parse_create_or_delete(message: Mapping[str, Any]) -> Dict[str, Any]:
 | 
			
		||||
    """ Parses create or delete event. """
 | 
			
		||||
    if message["type"] == 'relateduserstory':
 | 
			
		||||
        return {
 | 
			
		||||
@@ -196,8 +195,7 @@ def parse_create_or_delete(message):
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def parse_change_event(change_type, message):
 | 
			
		||||
    # type: (str, Mapping[str, Any]) -> Optional[Dict[str, Any]]
 | 
			
		||||
def parse_change_event(change_type: str, message: Mapping[str, Any]) -> Optional[Dict[str, Any]]:
 | 
			
		||||
    """ Parses change event. """
 | 
			
		||||
    evt = {}  # type: Dict[str, Any]
 | 
			
		||||
    values = {
 | 
			
		||||
@@ -264,8 +262,7 @@ def parse_change_event(change_type, message):
 | 
			
		||||
    return evt
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def parse_message(message):
 | 
			
		||||
    # type: (Mapping[str, Any]) -> List[Dict[str, Any]]
 | 
			
		||||
def parse_message(message: Mapping[str, Any]) -> List[Dict[str, Any]]:
 | 
			
		||||
    """ Parses the payload by delegating to specialized functions. """
 | 
			
		||||
    events = []
 | 
			
		||||
    if message["action"] in ['create', 'delete']:
 | 
			
		||||
@@ -281,16 +278,13 @@ def parse_message(message):
 | 
			
		||||
 | 
			
		||||
    return events
 | 
			
		||||
 | 
			
		||||
def generate_content(data):
 | 
			
		||||
    # type: (Mapping[str, Any]) -> str
 | 
			
		||||
def generate_content(data: Mapping[str, Any]) -> str:
 | 
			
		||||
    """ Gets the template string and formats it with parsed data. """
 | 
			
		||||
    return templates[data['type']][data['event']] % data['values']
 | 
			
		||||
 | 
			
		||||
def get_owner_name(message):
 | 
			
		||||
    # type: (Mapping[str, Any]) -> str
 | 
			
		||||
def get_owner_name(message: Mapping[str, Any]) -> str:
 | 
			
		||||
    return message["by"]["full_name"]
 | 
			
		||||
 | 
			
		||||
def get_subject(message):
 | 
			
		||||
    # type: (Mapping[str, Any]) -> str
 | 
			
		||||
def get_subject(message: Mapping[str, Any]) -> str:
 | 
			
		||||
    data = message["data"]
 | 
			
		||||
    return data.get("subject", data.get("name"))
 | 
			
		||||
 
 | 
			
		||||
@@ -9,28 +9,23 @@ class TeamcityHookTests(WebhookTestCase):
 | 
			
		||||
    SUBJECT = u"Project :: Compile"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'teamcity'
 | 
			
		||||
 | 
			
		||||
    def test_teamcity_success(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_teamcity_success(self) -> None:
 | 
			
		||||
        expected_message = u"Project :: Compile build 5535 - CL 123456 was successful! :thumbsup:\nDetails: [changes](http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952&tab=buildChangesDiv), [build log](http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952)"
 | 
			
		||||
        self.send_and_test_stream_message('success', self.SUBJECT, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_teamcity_broken(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_teamcity_broken(self) -> None:
 | 
			
		||||
        expected_message = u"Project :: Compile build 5535 - CL 123456 is broken with status Exit code 1 (new)! :thumbsdown:\nDetails: [changes](http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952&tab=buildChangesDiv), [build log](http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952)"
 | 
			
		||||
        self.send_and_test_stream_message('broken', self.SUBJECT, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_teamcity_failure(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_teamcity_failure(self) -> None:
 | 
			
		||||
        expected_message = u"Project :: Compile build 5535 - CL 123456 is still broken with status Exit code 1! :thumbsdown:\nDetails: [changes](http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952&tab=buildChangesDiv), [build log](http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952)"
 | 
			
		||||
        self.send_and_test_stream_message('failure', self.SUBJECT, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_teamcity_fixed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_teamcity_fixed(self) -> None:
 | 
			
		||||
        expected_message = u"Project :: Compile build 5535 - CL 123456 has been fixed! :thumbsup:\nDetails: [changes](http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952&tab=buildChangesDiv), [build log](http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952)"
 | 
			
		||||
        self.send_and_test_stream_message('fixed', self.SUBJECT, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_teamcity_personal(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_teamcity_personal(self) -> None:
 | 
			
		||||
        expected_message = u"Your personal build of Project :: Compile build 5535 - CL 123456 is broken with status Exit code 1 (new)! :thumbsdown:\nDetails: [changes](http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952&tab=buildChangesDiv), [build log](http://teamcity/viewLog.html?buildTypeId=Project_Compile&buildId=19952)"
 | 
			
		||||
        payload = ujson.dumps(ujson.loads(self.fixture_data(self.FIXTURE_DIR_NAME, 'personal')))
 | 
			
		||||
        self.client_post(self.url, payload, content_type="application/json")
 | 
			
		||||
 
 | 
			
		||||
@@ -13,8 +13,7 @@ from zerver.models import UserProfile, Realm
 | 
			
		||||
import logging
 | 
			
		||||
import ujson
 | 
			
		||||
 | 
			
		||||
def guess_zulip_user_from_teamcity(teamcity_username, realm):
 | 
			
		||||
    # type: (str, Realm) -> Optional[UserProfile]
 | 
			
		||||
def guess_zulip_user_from_teamcity(teamcity_username: str, realm: Realm) -> Optional[UserProfile]:
 | 
			
		||||
    try:
 | 
			
		||||
        # Try to find a matching user in Zulip
 | 
			
		||||
        # We search a user's full name, short name,
 | 
			
		||||
@@ -29,8 +28,7 @@ def guess_zulip_user_from_teamcity(teamcity_username, realm):
 | 
			
		||||
    except IndexError:
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
def get_teamcity_property_value(property_list, name):
 | 
			
		||||
    # type: (List[Dict[str, str]], str) -> Optional[str]
 | 
			
		||||
def get_teamcity_property_value(property_list: List[Dict[str, str]], name: str) -> Optional[str]:
 | 
			
		||||
    for property in property_list:
 | 
			
		||||
        if property['name'] == name:
 | 
			
		||||
            return property['value']
 | 
			
		||||
 
 | 
			
		||||
@@ -14,8 +14,7 @@ class TransifexHookTests(WebhookTestCase):
 | 
			
		||||
    RESOURCE = 'file'
 | 
			
		||||
    REVIEWED = True
 | 
			
		||||
 | 
			
		||||
    def test_transifex_reviewed_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_transifex_reviewed_message(self) -> None:
 | 
			
		||||
        self.REVIEWED = True
 | 
			
		||||
        expected_subject = "{} in {}".format(self.PROJECT, self.LANGUAGE)
 | 
			
		||||
        expected_message = "Resource {} fully reviewed.".format(self.RESOURCE)
 | 
			
		||||
@@ -27,8 +26,7 @@ class TransifexHookTests(WebhookTestCase):
 | 
			
		||||
        )
 | 
			
		||||
        self.send_and_test_stream_message("", expected_subject, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_transifex_translated_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_transifex_translated_message(self) -> None:
 | 
			
		||||
        self.REVIEWED = False
 | 
			
		||||
        expected_subject = "{} in {}".format(self.PROJECT, self.LANGUAGE)
 | 
			
		||||
        expected_message = "Resource {} fully translated.".format(self.RESOURCE)
 | 
			
		||||
@@ -40,6 +38,5 @@ class TransifexHookTests(WebhookTestCase):
 | 
			
		||||
        )
 | 
			
		||||
        self.send_and_test_stream_message("", expected_subject, expected_message)
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Dict[str, Any]
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Dict[str, Any]:
 | 
			
		||||
        return {}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,8 +10,7 @@ class TravisHookTests(WebhookTestCase):
 | 
			
		||||
    FIXTURE_DIR_NAME = 'travis'
 | 
			
		||||
    TOPIC = 'builds'
 | 
			
		||||
 | 
			
		||||
    def test_travis_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_travis_message(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Build notifications are generated by Travis after build completes.
 | 
			
		||||
 | 
			
		||||
@@ -30,8 +29,7 @@ class TravisHookTests(WebhookTestCase):
 | 
			
		||||
            content_type="application/x-www-form-urlencoded"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_ignore_travis_pull_request_by_default(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_ignore_travis_pull_request_by_default(self) -> None:
 | 
			
		||||
        self.subscribe(self.test_user, self.STREAM_NAME)
 | 
			
		||||
        result = self.client_post(
 | 
			
		||||
            self.url,
 | 
			
		||||
@@ -42,8 +40,7 @@ class TravisHookTests(WebhookTestCase):
 | 
			
		||||
        msg = self.get_last_message()
 | 
			
		||||
        self.assertNotEquals(msg.subject, self.TOPIC)
 | 
			
		||||
 | 
			
		||||
    def test_travis_pull_requests_are_not_ignored_when_applicable(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_travis_pull_requests_are_not_ignored_when_applicable(self) -> None:
 | 
			
		||||
        self.url = "{}&ignore_pull_requests=false".format(self.build_webhook_url())
 | 
			
		||||
        expected_message = (u"Author: josh_mandel\nBuild status: Passed :thumbsup:\n"
 | 
			
		||||
                            u"Details: [changes](https://github.com/hl7-fhir/fhir-sv"
 | 
			
		||||
@@ -57,6 +54,5 @@ class TravisHookTests(WebhookTestCase):
 | 
			
		||||
            content_type="application/x-www-form-urlencoded"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Text
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Text:
 | 
			
		||||
        return urllib.parse.urlencode({'payload': self.fixture_data("travis", fixture_name, file_type="json")})
 | 
			
		||||
 
 | 
			
		||||
@@ -7,103 +7,83 @@ class TrelloHookTests(WebhookTestCase):
 | 
			
		||||
    URL_TEMPLATE = u"/api/v1/external/trello?stream={stream}&api_key={api_key}"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'trello'
 | 
			
		||||
 | 
			
		||||
    def test_trello_confirmation_request(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_trello_confirmation_request(self) -> None:
 | 
			
		||||
        response = self.client_head(self.build_webhook_url())
 | 
			
		||||
        self.assertEqual(response.status_code, 200, response)
 | 
			
		||||
 | 
			
		||||
    def test_trello_webhook_when_card_was_moved_to_another_list(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_trello_webhook_when_card_was_moved_to_another_list(self) -> None:
 | 
			
		||||
        expected_message = u"TomaszKolek moved [This is a card.](https://trello.com/c/r33ylX2Z) from Basics to Intermediate."
 | 
			
		||||
        self.send_and_test_stream_message('changing_cards_list', u"Welcome Board", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_trello_webhook_when_card_was_renamed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_trello_webhook_when_card_was_renamed(self) -> None:
 | 
			
		||||
        expected_message = u"TomaszKolek renamed the card from \"Old name\" to [New name](https://trello.com/c/r33ylX2Z)."
 | 
			
		||||
        self.send_and_test_stream_message('renaming_card', u"Welcome Board", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_trello_webhook_when_label_was_added_to_card(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_trello_webhook_when_label_was_added_to_card(self) -> None:
 | 
			
		||||
        expected_message = u"TomaszKolek added a green label with \"text value\" to [Card name](https://trello.com/c/r33ylX2Z)."
 | 
			
		||||
        self.send_and_test_stream_message('adding_label_to_card', u"Welcome Board", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_trello_webhook_when_label_was_removing_from_card(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_trello_webhook_when_label_was_removing_from_card(self) -> None:
 | 
			
		||||
        expected_message = u"TomaszKolek removed a green label with \"text value\" from [New Card](https://trello.com/c/r33ylX2Z)."
 | 
			
		||||
        self.send_and_test_stream_message('removing_label_from_card', u"Welcome Board", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_trello_webhook_when_member_was_added_to_card(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_trello_webhook_when_member_was_added_to_card(self) -> None:
 | 
			
		||||
        expected_message = u"TomaszKolek added TomaszKolek to [Card name](https://trello.com/c/9BduUcVQ)."
 | 
			
		||||
        self.send_and_test_stream_message('adding_member_to_card', u"Welcome Board", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_trello_webhook_when_member_was_removed_from_card(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_trello_webhook_when_member_was_removed_from_card(self) -> None:
 | 
			
		||||
        expected_message = u"TomaszKolek removed Trello from [Card name](https://trello.com/c/9BduUcVQ)."
 | 
			
		||||
        self.send_and_test_stream_message('removing_member_from_card', u"Welcome Board", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_trello_webhook_when_due_date_was_set(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_trello_webhook_when_due_date_was_set(self) -> None:
 | 
			
		||||
        expected_message = u"TomaszKolek set due date for [Card name](https://trello.com/c/9BduUcVQ) to 2016-05-11 10:00:00 UTC."
 | 
			
		||||
        self.send_and_test_stream_message('setting_due_date_to_card', u"Welcome Board", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_trello_webhook_when_due_date_was_changed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_trello_webhook_when_due_date_was_changed(self) -> None:
 | 
			
		||||
        expected_message = u"TomaszKolek changed due date for [Card name](https://trello.com/c/9BduUcVQ) from 2016-05-11 10:00:00 UTC to 2016-05-24 10:00:00 UTC."
 | 
			
		||||
        self.send_and_test_stream_message('changing_due_date_on_card', u"Welcome Board", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_trello_webhook_when_due_date_was_removed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_trello_webhook_when_due_date_was_removed(self) -> None:
 | 
			
		||||
        expected_message = u"TomaszKolek removed the due date from [Card name](https://trello.com/c/9BduUcVQ)."
 | 
			
		||||
        self.send_and_test_stream_message('removing_due_date_from_card', u"Welcome Board", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_trello_webhook_when_card_was_archived(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_trello_webhook_when_card_was_archived(self) -> None:
 | 
			
		||||
        expected_message = u"TomaszKolek archived [Card name](https://trello.com/c/9BduUcVQ)."
 | 
			
		||||
        self.send_and_test_stream_message('archiving_card', u"Welcome Board", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_trello_webhook_when_card_was_reopened(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_trello_webhook_when_card_was_reopened(self) -> None:
 | 
			
		||||
        expected_message = u"TomaszKolek reopened [Card name](https://trello.com/c/9BduUcVQ)."
 | 
			
		||||
        self.send_and_test_stream_message('reopening_card', u"Welcome Board", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_trello_webhook_when_card_was_created(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_trello_webhook_when_card_was_created(self) -> None:
 | 
			
		||||
        expected_message = u"TomaszKolek created [New card](https://trello.com/c/5qrgGdD5)."
 | 
			
		||||
        self.send_and_test_stream_message('creating_card', u"Welcome Board", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_trello_webhook_when_attachment_was_added_to_card(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_trello_webhook_when_attachment_was_added_to_card(self) -> None:
 | 
			
		||||
        expected_message = u"TomaszKolek added [attachment_name](http://url.com) to [New card](https://trello.com/c/xPKXoSTQ)."
 | 
			
		||||
        self.send_and_test_stream_message('adding_attachment_to_card', u"Welcome Board", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_trello_webhook_when_checklist_was_added_to_card(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_trello_webhook_when_checklist_was_added_to_card(self) -> None:
 | 
			
		||||
        expected_message = u"TomaszKolek added the Checklist checklist to [New card](https://trello.com/c/xPKXoSTQ)."
 | 
			
		||||
        self.send_and_test_stream_message('adding_checklist_to_card', u"Welcome Board", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_trello_webhook_when_member_was_removed_from_board(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_trello_webhook_when_member_was_removed_from_board(self) -> None:
 | 
			
		||||
        expected_message = u"TomaszKolek removed Trello from [Welcome Board](https://trello.com/b/iqXXzYEj)."
 | 
			
		||||
        self.send_and_test_stream_message('removing_member_from_board', u"Welcome Board", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_trello_webhook_when_member_was_added_to_board(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_trello_webhook_when_member_was_added_to_board(self) -> None:
 | 
			
		||||
        expected_message = u"TomaszKolek added Trello to [Welcome Board](https://trello.com/b/iqXXzYEj)."
 | 
			
		||||
        self.send_and_test_stream_message('adding_member_to_board', u"Welcome Board", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_trello_webhook_when_list_was_added_to_board(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_trello_webhook_when_list_was_added_to_board(self) -> None:
 | 
			
		||||
        expected_message = u"TomaszKolek added New list list to [Welcome Board](https://trello.com/b/iqXXzYEj)."
 | 
			
		||||
        self.send_and_test_stream_message('adding_new_list_to_board', u"Welcome Board", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_trello_webhook_when_comment_was_added_to_card(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_trello_webhook_when_comment_was_added_to_card(self) -> None:
 | 
			
		||||
        expected_message = u"TomaszKolek commented on [New card](https://trello.com/c/xPKXoSTQ)\n~~~ quote\nNew comment\n~~~"
 | 
			
		||||
        self.send_and_test_stream_message('adding_comment_to_card', u"Welcome Board", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_trello_webhook_when_board_was_renamed(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_trello_webhook_when_board_was_renamed(self) -> None:
 | 
			
		||||
        expected_message = u"TomaszKolek renamed the board from Welcome Board to [New name](https://trello.com/b/iqXXzYEj)."
 | 
			
		||||
        self.send_and_test_stream_message('renaming_board', u"New name", expected_message)
 | 
			
		||||
 | 
			
		||||
@@ -125,17 +105,14 @@ class TrelloHookTests(WebhookTestCase):
 | 
			
		||||
        self.assertFalse(check_send_stream_message_mock.called)
 | 
			
		||||
        self.assert_json_success(result)
 | 
			
		||||
 | 
			
		||||
    def test_trello_webhook_when_description_was_added_to_card(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_trello_webhook_when_description_was_added_to_card(self) -> None:
 | 
			
		||||
        expected_message = u"Marco Matarazzo set description for [New Card](https://trello.com/c/P2r0z66z) to\n~~~ quote\nNew Description\n~~~"
 | 
			
		||||
        self.send_and_test_stream_message('adding_description_to_card', u"Welcome Board", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_trello_webhook_when_description_was_removed_from_card(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_trello_webhook_when_description_was_removed_from_card(self) -> None:
 | 
			
		||||
        expected_message = u"Marco Matarazzo removed description from [New Card](https://trello.com/c/P2r0z66z)."
 | 
			
		||||
        self.send_and_test_stream_message('removing_description_from_card', u"Welcome Board", expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_trello_webhook_when_description_was_changed_on_card(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_trello_webhook_when_description_was_changed_on_card(self) -> None:
 | 
			
		||||
        expected_message = u"Marco Matarazzo changed description for [New Card](https://trello.com/c/P2r0z66z) from\n~~~ quote\nNew Description\n~~~\nto\n~~~ quote\nChanged Description\n~~~"
 | 
			
		||||
        self.send_and_test_stream_message('changing_description_on_card', u"Welcome Board", expected_message)
 | 
			
		||||
 
 | 
			
		||||
@@ -16,8 +16,10 @@ from .exceptions import UnsupportedAction
 | 
			
		||||
@api_key_only_webhook_view('Trello')
 | 
			
		||||
@return_success_on_head_request
 | 
			
		||||
@has_request_variables
 | 
			
		||||
def api_trello_webhook(request, user_profile, payload=REQ(argument_type='body'), stream=REQ(default='trello')):
 | 
			
		||||
    # type: (HttpRequest, UserProfile, Mapping[str, Any], Text) -> HttpResponse
 | 
			
		||||
def api_trello_webhook(request: HttpRequest,
 | 
			
		||||
                       user_profile: UserProfile,
 | 
			
		||||
                       payload: Mapping[str, Any]=REQ(argument_type='body'),
 | 
			
		||||
                       stream: Text=REQ(default='trello')) -> HttpResponse:
 | 
			
		||||
    payload = ujson.loads(request.body)
 | 
			
		||||
    action_type = payload['action'].get('type')
 | 
			
		||||
    try:
 | 
			
		||||
@@ -32,8 +34,7 @@ def api_trello_webhook(request, user_profile, payload=REQ(argument_type='body'),
 | 
			
		||||
    check_send_stream_message(user_profile, request.client, stream, subject, body)
 | 
			
		||||
    return json_success()
 | 
			
		||||
 | 
			
		||||
def get_subject_and_body(payload, action_type):
 | 
			
		||||
    # type: (Mapping[str, Any], Text) -> Optional[Tuple[Text, Text]]
 | 
			
		||||
def get_subject_and_body(payload: Mapping[str, Any], action_type: Text) -> Optional[Tuple[Text, Text]]:
 | 
			
		||||
    if action_type in SUPPORTED_CARD_ACTIONS:
 | 
			
		||||
        return process_card_action(payload, action_type)
 | 
			
		||||
    if action_type in SUPPORTED_BOARD_ACTIONS:
 | 
			
		||||
 
 | 
			
		||||
@@ -22,15 +22,14 @@ ACTIONS_TO_MESSAGE_MAPPER = {
 | 
			
		||||
    CHANGE_NAME: u'renamed the board from {old_name} to {board_url_template}.'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
def process_board_action(payload, action_type):
 | 
			
		||||
    # type: (Mapping[str, Any], Optional[Text]) -> Optional[Tuple[Text, Text]]
 | 
			
		||||
def process_board_action(payload: Mapping[str, Any],
 | 
			
		||||
                         action_type: Optional[Text]) -> Optional[Tuple[Text, Text]]:
 | 
			
		||||
    action_type = get_proper_action(payload, action_type)
 | 
			
		||||
    if action_type is not None:
 | 
			
		||||
        return get_subject(payload), get_body(payload, action_type)
 | 
			
		||||
    return None
 | 
			
		||||
 | 
			
		||||
def get_proper_action(payload, action_type):
 | 
			
		||||
    # type: (Mapping[str, Any], Optional[Text]) -> Optional[Text]
 | 
			
		||||
def get_proper_action(payload: Mapping[str, Any], action_type: Optional[Text]) -> Optional[Text]:
 | 
			
		||||
    if action_type == 'updateBoard':
 | 
			
		||||
        data = get_action_data(payload)
 | 
			
		||||
        # we don't support events for when a board's background
 | 
			
		||||
@@ -42,63 +41,54 @@ def get_proper_action(payload, action_type):
 | 
			
		||||
        raise UnknownUpdateBoardAction()
 | 
			
		||||
    return action_type
 | 
			
		||||
 | 
			
		||||
def get_subject(payload):
 | 
			
		||||
    # type: (Mapping[str, Any]) -> Text
 | 
			
		||||
def get_subject(payload: Mapping[str, Any]) -> Text:
 | 
			
		||||
    return get_action_data(payload)['board']['name']
 | 
			
		||||
 | 
			
		||||
def get_body(payload, action_type):
 | 
			
		||||
    # type: (Mapping[str, Any], Text) -> Text
 | 
			
		||||
def get_body(payload: Mapping[str, Any], action_type: Text) -> Text:
 | 
			
		||||
    message_body = ACTIONS_TO_FILL_BODY_MAPPER[action_type](payload, action_type)
 | 
			
		||||
    creator = payload['action']['memberCreator']['fullName']
 | 
			
		||||
    return u'{full_name} {rest}'.format(full_name=creator, rest=message_body)
 | 
			
		||||
 | 
			
		||||
def get_managed_member_body(payload, action_type):
 | 
			
		||||
    # type: (Mapping[str, Any], Text) -> Text
 | 
			
		||||
def get_managed_member_body(payload: Mapping[str, Any], action_type: Text) -> Text:
 | 
			
		||||
    data = {
 | 
			
		||||
        'member_name': payload['action']['member']['fullName'],
 | 
			
		||||
    }
 | 
			
		||||
    return fill_appropriate_message_content(payload, action_type, data)
 | 
			
		||||
 | 
			
		||||
def get_create_list_body(payload, action_type):
 | 
			
		||||
    # type: (Mapping[str, Any], Text) -> Text
 | 
			
		||||
def get_create_list_body(payload: Mapping[str, Any], action_type: Text) -> Text:
 | 
			
		||||
    data = {
 | 
			
		||||
        'list_name': get_action_data(payload)['list']['name'],
 | 
			
		||||
    }
 | 
			
		||||
    return fill_appropriate_message_content(payload, action_type, data)
 | 
			
		||||
 | 
			
		||||
def get_change_name_body(payload, action_type):
 | 
			
		||||
    # type: (Mapping[str, Any], Text) -> Text
 | 
			
		||||
def get_change_name_body(payload: Mapping[str, Any], action_type: Text) -> Text:
 | 
			
		||||
    data = {
 | 
			
		||||
        'old_name': get_action_data(payload)['old']['name']
 | 
			
		||||
    }
 | 
			
		||||
    return fill_appropriate_message_content(payload, action_type, data)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def fill_appropriate_message_content(payload, action_type, data=None):
 | 
			
		||||
    # type: (Mapping[str, Any], Text, Optional[Dict[str, Any]]) -> Text
 | 
			
		||||
def fill_appropriate_message_content(payload: Mapping[str, Any],
 | 
			
		||||
                                     action_type: Text,
 | 
			
		||||
                                     data: Optional[Dict[str, Any]]=None) -> Text:
 | 
			
		||||
    data = {} if data is None else data
 | 
			
		||||
    data['board_url_template'] = data.get('board_url_template', get_filled_board_url_template(payload))
 | 
			
		||||
    message_body = get_message_body(action_type)
 | 
			
		||||
    return message_body.format(**data)
 | 
			
		||||
 | 
			
		||||
def get_filled_board_url_template(payload):
 | 
			
		||||
    # type: (Mapping[str, Any]) -> Text
 | 
			
		||||
def get_filled_board_url_template(payload: Mapping[str, Any]) -> Text:
 | 
			
		||||
    return TRELLO_BOARD_URL_TEMPLATE.format(board_name=get_board_name(payload), board_url=get_board_url(payload))
 | 
			
		||||
 | 
			
		||||
def get_board_name(payload):
 | 
			
		||||
    # type: (Mapping[str, Any]) -> Text
 | 
			
		||||
def get_board_name(payload: Mapping[str, Any]) -> Text:
 | 
			
		||||
    return get_action_data(payload)['board']['name']
 | 
			
		||||
 | 
			
		||||
def get_board_url(payload):
 | 
			
		||||
    # type: (Mapping[str, Any]) -> Text
 | 
			
		||||
def get_board_url(payload: Mapping[str, Any]) -> Text:
 | 
			
		||||
    return u'https://trello.com/b/{}'.format(get_action_data(payload)['board']['shortLink'])
 | 
			
		||||
 | 
			
		||||
def get_message_body(action_type):
 | 
			
		||||
    # type: (Text) -> Text
 | 
			
		||||
def get_message_body(action_type: Text) -> Text:
 | 
			
		||||
    return ACTIONS_TO_MESSAGE_MAPPER[action_type]
 | 
			
		||||
 | 
			
		||||
def get_action_data(payload):
 | 
			
		||||
    # type: (Mapping[str, Any]) -> Mapping[str, Any]
 | 
			
		||||
def get_action_data(payload: Mapping[str, Any]) -> Mapping[str, Any]:
 | 
			
		||||
    return payload['action']['data']
 | 
			
		||||
 | 
			
		||||
ACTIONS_TO_FILL_BODY_MAPPER = {
 | 
			
		||||
 
 | 
			
		||||
@@ -57,19 +57,16 @@ ACTIONS_TO_MESSAGE_MAPPER = {
 | 
			
		||||
    COMMENT: u'commented on {card_url_template}\n~~~ quote\n{text}\n~~~\n'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
def prettify_date(date_string):
 | 
			
		||||
    # type: (str) -> str
 | 
			
		||||
def prettify_date(date_string: str) -> str:
 | 
			
		||||
    return date_string.replace('T', ' ').replace('.000', '').replace('Z', ' UTC')
 | 
			
		||||
 | 
			
		||||
def process_card_action(payload, action_type):
 | 
			
		||||
    # type: (Mapping[str, Any], Text) -> Optional[Tuple[Text, Text]]
 | 
			
		||||
def process_card_action(payload: Mapping[str, Any], action_type: Text) -> Optional[Tuple[Text, Text]]:
 | 
			
		||||
    proper_action = get_proper_action(payload, action_type)
 | 
			
		||||
    if proper_action is not None:
 | 
			
		||||
        return get_subject(payload), get_body(payload, proper_action)
 | 
			
		||||
    return None
 | 
			
		||||
 | 
			
		||||
def get_proper_action(payload, action_type):
 | 
			
		||||
    # type: (Mapping[str, Any], Text) -> Optional[Text]
 | 
			
		||||
def get_proper_action(payload: Mapping[str, Any], action_type: Text) -> Optional[Text]:
 | 
			
		||||
    if action_type == 'updateCard':
 | 
			
		||||
        data = get_action_data(payload)
 | 
			
		||||
        old_data = data['old']
 | 
			
		||||
@@ -104,33 +101,28 @@ def get_proper_action(payload, action_type):
 | 
			
		||||
 | 
			
		||||
    return action_type
 | 
			
		||||
 | 
			
		||||
def get_subject(payload):
 | 
			
		||||
    # type: (Mapping[str, Any]) -> Text
 | 
			
		||||
def get_subject(payload: Mapping[str, Any]) -> Text:
 | 
			
		||||
    return get_action_data(payload)['board'].get('name')
 | 
			
		||||
 | 
			
		||||
def get_body(payload, action_type):
 | 
			
		||||
    # type: (Mapping[str, Any], Text) -> Text
 | 
			
		||||
def get_body(payload: Mapping[str, Any], action_type: Text) -> Text:
 | 
			
		||||
    message_body = ACTIONS_TO_FILL_BODY_MAPPER[action_type](payload, action_type)
 | 
			
		||||
    creator = payload['action']['memberCreator'].get('fullName')
 | 
			
		||||
    return u'{full_name} {rest}'.format(full_name=creator, rest=message_body)
 | 
			
		||||
 | 
			
		||||
def get_added_checklist_body(payload, action_type):
 | 
			
		||||
    # type: (Mapping[str, Any], Text) -> Text
 | 
			
		||||
def get_added_checklist_body(payload: Mapping[str, Any], action_type: Text) -> Text:
 | 
			
		||||
    data = {
 | 
			
		||||
        'checklist_name': get_action_data(payload)['checklist'].get('name'),
 | 
			
		||||
    }
 | 
			
		||||
    return fill_appropriate_message_content(payload, action_type, data)
 | 
			
		||||
 | 
			
		||||
def get_added_attachment_body(payload, action_type):
 | 
			
		||||
    # type: (Mapping[str, Any], Text) -> Text
 | 
			
		||||
def get_added_attachment_body(payload: Mapping[str, Any], action_type: Text) -> Text:
 | 
			
		||||
    data = {
 | 
			
		||||
        'attachment_url': get_action_data(payload)['attachment'].get('url'),
 | 
			
		||||
        'attachment_name': get_action_data(payload)['attachment'].get('name'),
 | 
			
		||||
    }
 | 
			
		||||
    return fill_appropriate_message_content(payload, action_type, data)
 | 
			
		||||
 | 
			
		||||
def get_updated_card_body(payload, action_type):
 | 
			
		||||
    # type: (Mapping[str, Any], Text) -> Text
 | 
			
		||||
def get_updated_card_body(payload: Mapping[str, Any], action_type: Text) -> Text:
 | 
			
		||||
    data = {
 | 
			
		||||
        'card_name': get_card_name(payload),
 | 
			
		||||
        'old_list': get_action_data(payload)['listBefore'].get('name'),
 | 
			
		||||
@@ -138,8 +130,7 @@ def get_updated_card_body(payload, action_type):
 | 
			
		||||
    }
 | 
			
		||||
    return fill_appropriate_message_content(payload, action_type, data)
 | 
			
		||||
 | 
			
		||||
def get_renamed_card_body(payload, action_type):
 | 
			
		||||
    # type: (Mapping[str, Any], Text) -> Text
 | 
			
		||||
def get_renamed_card_body(payload: Mapping[str, Any], action_type: Text) -> Text:
 | 
			
		||||
 | 
			
		||||
    data = {
 | 
			
		||||
        'old_name': get_action_data(payload)['old'].get('name'),
 | 
			
		||||
@@ -147,87 +138,75 @@ def get_renamed_card_body(payload, action_type):
 | 
			
		||||
    }
 | 
			
		||||
    return fill_appropriate_message_content(payload, action_type, data)
 | 
			
		||||
 | 
			
		||||
def get_added_label_body(payload, action_type):
 | 
			
		||||
    # type: (Mapping[str, Any], Text) -> Text
 | 
			
		||||
def get_added_label_body(payload: Mapping[str, Any], action_type: Text) -> Text:
 | 
			
		||||
    data = {
 | 
			
		||||
        'color': get_action_data(payload).get('value'),
 | 
			
		||||
        'text': get_action_data(payload).get('text'),
 | 
			
		||||
    }
 | 
			
		||||
    return fill_appropriate_message_content(payload, action_type, data)
 | 
			
		||||
 | 
			
		||||
def get_managed_member_body(payload, action_type):
 | 
			
		||||
    # type: (Mapping[str, Any], Text) -> Text
 | 
			
		||||
def get_managed_member_body(payload: Mapping[str, Any], action_type: Text) -> Text:
 | 
			
		||||
    data = {
 | 
			
		||||
        'member_name': payload['action']['member'].get('fullName')
 | 
			
		||||
    }
 | 
			
		||||
    return fill_appropriate_message_content(payload, action_type, data)
 | 
			
		||||
 | 
			
		||||
def get_comment_body(payload, action_type):
 | 
			
		||||
    # type: (Mapping[str, Any], Text) -> Text
 | 
			
		||||
def get_comment_body(payload: Mapping[str, Any], action_type: Text) -> Text:
 | 
			
		||||
    data = {
 | 
			
		||||
        'text': get_action_data(payload)['text'],
 | 
			
		||||
    }
 | 
			
		||||
    return fill_appropriate_message_content(payload, action_type, data)
 | 
			
		||||
 | 
			
		||||
def get_managed_due_date_body(payload, action_type):
 | 
			
		||||
    # type: (Mapping[str, Any], Text) -> Text
 | 
			
		||||
def get_managed_due_date_body(payload: Mapping[str, Any], action_type: Text) -> Text:
 | 
			
		||||
    data = {
 | 
			
		||||
        'due_date': prettify_date(get_action_data(payload)['card'].get('due'))
 | 
			
		||||
    }
 | 
			
		||||
    return fill_appropriate_message_content(payload, action_type, data)
 | 
			
		||||
 | 
			
		||||
def get_changed_due_date_body(payload, action_type):
 | 
			
		||||
    # type: (Mapping[str, Any], Text) -> Text
 | 
			
		||||
def get_changed_due_date_body(payload: Mapping[str, Any], action_type: Text) -> Text:
 | 
			
		||||
    data = {
 | 
			
		||||
        'due_date': prettify_date(get_action_data(payload)['card'].get('due')),
 | 
			
		||||
        'old_due_date': prettify_date(get_action_data(payload)['old'].get('due'))
 | 
			
		||||
    }
 | 
			
		||||
    return fill_appropriate_message_content(payload, action_type, data)
 | 
			
		||||
 | 
			
		||||
def get_managed_desc_body(payload, action_type):
 | 
			
		||||
    # type: (Mapping[str, Any], Text) -> Text
 | 
			
		||||
def get_managed_desc_body(payload: Mapping[str, Any], action_type: Text) -> Text:
 | 
			
		||||
    data = {
 | 
			
		||||
        'desc': prettify_date(get_action_data(payload)['card']['desc'])
 | 
			
		||||
    }
 | 
			
		||||
    return fill_appropriate_message_content(payload, action_type, data)
 | 
			
		||||
 | 
			
		||||
def get_changed_desc_body(payload, action_type):
 | 
			
		||||
    # type: (Mapping[str, Any], Text) -> Text
 | 
			
		||||
def get_changed_desc_body(payload: Mapping[str, Any], action_type: Text) -> Text:
 | 
			
		||||
    data = {
 | 
			
		||||
        'desc': prettify_date(get_action_data(payload)['card']['desc']),
 | 
			
		||||
        'old_desc': prettify_date(get_action_data(payload)['old']['desc'])
 | 
			
		||||
    }
 | 
			
		||||
    return fill_appropriate_message_content(payload, action_type, data)
 | 
			
		||||
 | 
			
		||||
def get_body_by_action_type_without_data(payload, action_type):
 | 
			
		||||
    # type: (Mapping[str, Any], Text) -> Text
 | 
			
		||||
def get_body_by_action_type_without_data(payload: Mapping[str, Any], action_type: Text) -> Text:
 | 
			
		||||
    return fill_appropriate_message_content(payload, action_type)
 | 
			
		||||
 | 
			
		||||
def fill_appropriate_message_content(payload, action_type, data=None):
 | 
			
		||||
    # type: (Mapping[str, Any], Text, Optional[Dict[str, Any]]) -> Text
 | 
			
		||||
def fill_appropriate_message_content(payload: Mapping[str, Any],
 | 
			
		||||
                                     action_type: Text,
 | 
			
		||||
                                     data: Optional[Dict[str, Any]]=None) -> Text:
 | 
			
		||||
    data = {} if data is None else data
 | 
			
		||||
    data['card_url_template'] = data.get('card_url_template', get_filled_card_url_template(payload))
 | 
			
		||||
    message_body = get_message_body(action_type)
 | 
			
		||||
    return message_body.format(**data)
 | 
			
		||||
 | 
			
		||||
def get_filled_card_url_template(payload):
 | 
			
		||||
    # type: (Mapping[str, Any]) -> Text
 | 
			
		||||
def get_filled_card_url_template(payload: Mapping[str, Any]) -> Text:
 | 
			
		||||
    return TRELLO_CARD_URL_TEMPLATE.format(card_name=get_card_name(payload), card_url=get_card_url(payload))
 | 
			
		||||
 | 
			
		||||
def get_card_url(payload):
 | 
			
		||||
    # type: (Mapping[str, Any]) -> Text
 | 
			
		||||
def get_card_url(payload: Mapping[str, Any]) -> Text:
 | 
			
		||||
    return u'https://trello.com/c/{}'.format(get_action_data(payload)['card'].get('shortLink'))
 | 
			
		||||
 | 
			
		||||
def get_message_body(action_type):
 | 
			
		||||
    # type: (Text) -> Text
 | 
			
		||||
def get_message_body(action_type: Text) -> Text:
 | 
			
		||||
    return ACTIONS_TO_MESSAGE_MAPPER[action_type]
 | 
			
		||||
 | 
			
		||||
def get_card_name(payload):
 | 
			
		||||
    # type: (Mapping[str, Any]) -> Text
 | 
			
		||||
def get_card_name(payload: Mapping[str, Any]) -> Text:
 | 
			
		||||
    return get_action_data(payload)['card'].get('name')
 | 
			
		||||
 | 
			
		||||
def get_action_data(payload):
 | 
			
		||||
    # type: (Mapping[str, Any]) -> Mapping[str, Any]
 | 
			
		||||
def get_action_data(payload: Mapping[str, Any]) -> Mapping[str, Any]:
 | 
			
		||||
    return payload['action'].get('data')
 | 
			
		||||
 | 
			
		||||
ACTIONS_TO_FILL_BODY_MAPPER = {
 | 
			
		||||
 
 | 
			
		||||
@@ -6,26 +6,22 @@ class UpdownHookTests(WebhookTestCase):
 | 
			
		||||
    URL_TEMPLATE = u"/api/v1/external/updown?stream={stream}&api_key={api_key}"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'updown'
 | 
			
		||||
 | 
			
		||||
    def test_updown_check_down_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_updown_check_down_event(self) -> None:
 | 
			
		||||
        expected_subject = u"https://updown.io"
 | 
			
		||||
        expected_message = u"Service is `down`. It returned a 500 error at 2016-02-07 13:11:43 UTC."
 | 
			
		||||
        self.send_and_test_stream_message('check_down_one_event', expected_subject, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_updown_check_up_again_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_updown_check_up_again_event(self) -> None:
 | 
			
		||||
        expected_subject = u"https://updown.io"
 | 
			
		||||
        expected_message = u"Service is `up` again after 4 minutes 25 seconds."
 | 
			
		||||
        self.send_and_test_stream_message('check_up_again_one_event', expected_subject, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_updown_check_up_event(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_updown_check_up_event(self) -> None:
 | 
			
		||||
        expected_subject = u"https://updown.io"
 | 
			
		||||
        expected_message = u"Service is `up`."
 | 
			
		||||
        self.send_and_test_stream_message('check_up_first_time', expected_subject, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_updown_check_up_multiple_events(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_updown_check_up_multiple_events(self) -> None:
 | 
			
		||||
        first_message_expected_subject = u"https://updown.io"
 | 
			
		||||
        first_message_expected_message = u"Service is `up` again after 1 second."
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -14,15 +14,14 @@ from zerver.models import UserProfile, Client
 | 
			
		||||
 | 
			
		||||
SUBJECT_TEMPLATE = "{service_url}"
 | 
			
		||||
 | 
			
		||||
def send_message_for_event(event, user_profile, client, stream):
 | 
			
		||||
    # type: (Dict[str, Any], UserProfile, Client, str) -> None
 | 
			
		||||
def send_message_for_event(event: Dict[str, Any], user_profile: UserProfile,
 | 
			
		||||
                           client: Client, stream: str) -> None:
 | 
			
		||||
    event_type = get_event_type(event)
 | 
			
		||||
    subject = SUBJECT_TEMPLATE.format(service_url=event['check']['url'])
 | 
			
		||||
    body = EVENT_TYPE_BODY_MAPPER[event_type](event)
 | 
			
		||||
    check_send_stream_message(user_profile, client, stream, subject, body)
 | 
			
		||||
 | 
			
		||||
def get_body_for_up_event(event):
 | 
			
		||||
    # type: (Dict[str, Any]) -> str
 | 
			
		||||
def get_body_for_up_event(event: Dict[str, Any]) -> str:
 | 
			
		||||
    body = "Service is `up`"
 | 
			
		||||
    event_downtime = event['downtime']
 | 
			
		||||
    if event_downtime['started_at']:
 | 
			
		||||
@@ -32,8 +31,7 @@ def get_body_for_up_event(event):
 | 
			
		||||
            body = "{} after {}".format(body, string_date)
 | 
			
		||||
    return "{}.".format(body)
 | 
			
		||||
 | 
			
		||||
def get_time_string_based_on_duration(duration):
 | 
			
		||||
    # type: (int) -> str
 | 
			
		||||
def get_time_string_based_on_duration(duration: int) -> str:
 | 
			
		||||
    days, reminder = divmod(duration, 86400)
 | 
			
		||||
    hours, reminder = divmod(reminder, 3600)
 | 
			
		||||
    minutes, seconds = divmod(reminder, 60)
 | 
			
		||||
@@ -45,16 +43,14 @@ def get_time_string_based_on_duration(duration):
 | 
			
		||||
    string_date += add_time_part_to_string_date_if_needed(seconds, 'second')
 | 
			
		||||
    return string_date.rstrip()
 | 
			
		||||
 | 
			
		||||
def add_time_part_to_string_date_if_needed(value, text_name):
 | 
			
		||||
    # type: (int, str) -> str
 | 
			
		||||
def add_time_part_to_string_date_if_needed(value: int, text_name: str) -> str:
 | 
			
		||||
    if value == 1:
 | 
			
		||||
        return "1 {} ".format(text_name)
 | 
			
		||||
    if value > 1:
 | 
			
		||||
        return "{} {}s ".format(value, text_name)
 | 
			
		||||
    return ''
 | 
			
		||||
 | 
			
		||||
def get_body_for_down_event(event):
 | 
			
		||||
    # type: (Dict[str, Any]) -> str
 | 
			
		||||
def get_body_for_down_event(event: Dict[str, Any]) -> str:
 | 
			
		||||
    return "Service is `down`. It returned a {} error at {}.".format(
 | 
			
		||||
        event['downtime']['error'],
 | 
			
		||||
        event['downtime']['started_at'].replace('T', ' ').replace('Z', ' UTC'))
 | 
			
		||||
@@ -74,8 +70,7 @@ EVENT_TYPE_BODY_MAPPER = {
 | 
			
		||||
    'down': get_body_for_down_event
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
def get_event_type(event):
 | 
			
		||||
    # type: (Dict[str, Any]) -> str
 | 
			
		||||
def get_event_type(event: Dict[str, Any]) -> str:
 | 
			
		||||
    event_type_match = re.match('check.(.*)', event['event'])
 | 
			
		||||
    if event_type_match:
 | 
			
		||||
        event_type = event_type_match.group(1)
 | 
			
		||||
 
 | 
			
		||||
@@ -8,8 +8,7 @@ class WordPressHookTests(WebhookTestCase):
 | 
			
		||||
    URL_TEMPLATE = "/api/v1/external/wordpress?api_key={api_key}"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'wordpress'
 | 
			
		||||
 | 
			
		||||
    def test_publish_post(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_publish_post(self) -> None:
 | 
			
		||||
 | 
			
		||||
        expected_topic = u"WordPress Post"
 | 
			
		||||
        expected_message = u"New post published.\n[New Blog Post](http://example.com\n)"
 | 
			
		||||
@@ -17,8 +16,7 @@ class WordPressHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('publish_post', expected_topic, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_publish_post_type_not_provided(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_publish_post_type_not_provided(self) -> None:
 | 
			
		||||
 | 
			
		||||
        expected_topic = u"WordPress Post"
 | 
			
		||||
        expected_message = u"New post published.\n[New Blog Post](http://example.com\n)"
 | 
			
		||||
@@ -27,8 +25,7 @@ class WordPressHookTests(WebhookTestCase):
 | 
			
		||||
                                          expected_topic, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_publish_post_no_data_provided(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_publish_post_no_data_provided(self) -> None:
 | 
			
		||||
 | 
			
		||||
        # Note: the fixture includes 'hook=publish_post' because it's always added by HookPress
 | 
			
		||||
        expected_topic = u"WordPress Notification"
 | 
			
		||||
@@ -38,8 +35,7 @@ class WordPressHookTests(WebhookTestCase):
 | 
			
		||||
                                          expected_topic, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_publish_page(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_publish_page(self) -> None:
 | 
			
		||||
 | 
			
		||||
        expected_topic = u"WordPress Page"
 | 
			
		||||
        expected_message = u"New page published.\n" + "[New Blog Page](http://example.com\n)"
 | 
			
		||||
@@ -47,8 +43,7 @@ class WordPressHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('publish_page', expected_topic, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_user_register(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_user_register(self) -> None:
 | 
			
		||||
 | 
			
		||||
        expected_topic = u"New Blog Users"
 | 
			
		||||
        expected_message = u"New blog user registered.\nName: test_user\nemail: test_user@example.com"
 | 
			
		||||
@@ -56,8 +51,7 @@ class WordPressHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('user_register', expected_topic, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_wp_login(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_wp_login(self) -> None:
 | 
			
		||||
 | 
			
		||||
        expected_topic = u"New Login"
 | 
			
		||||
        expected_message = u"User testuser logged in."
 | 
			
		||||
@@ -65,8 +59,7 @@ class WordPressHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_stream_message('wp_login', expected_topic, expected_message,
 | 
			
		||||
                                          content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def test_unknown_action_no_data(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_unknown_action_no_data(self) -> None:
 | 
			
		||||
 | 
			
		||||
        # Mimic send_and_test_stream_message() to manually execute a negative test.
 | 
			
		||||
        # Otherwise its call to send_json_payload() would assert on the non-success
 | 
			
		||||
@@ -84,8 +77,7 @@ class WordPressHookTests(WebhookTestCase):
 | 
			
		||||
        # check that we got the expected error message
 | 
			
		||||
        self.assert_json_error(result, "Unknown WordPress webhook action: WordPress Action")
 | 
			
		||||
 | 
			
		||||
    def test_unknown_action_no_hook_provided(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_unknown_action_no_hook_provided(self) -> None:
 | 
			
		||||
 | 
			
		||||
        # Similar to unknown_action_no_data, except the fixture contains valid blog post
 | 
			
		||||
        # params but without the hook parameter. This should also return an error.
 | 
			
		||||
@@ -97,6 +89,5 @@ class WordPressHookTests(WebhookTestCase):
 | 
			
		||||
 | 
			
		||||
        self.assert_json_error(result, "Unknown WordPress webhook action: WordPress Action")
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (text_type) -> text_type
 | 
			
		||||
    def get_body(self, fixture_name: text_type) -> text_type:
 | 
			
		||||
        return self.fixture_data("wordpress", fixture_name, file_type="txt")
 | 
			
		||||
 
 | 
			
		||||
@@ -7,8 +7,7 @@ class YoHookTests(WebhookTestCase):
 | 
			
		||||
    URL_TEMPLATE = u"/api/v1/external/yo?api_key={api_key}"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'yo'
 | 
			
		||||
 | 
			
		||||
    def test_yo_message(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_yo_message(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Yo App sends notification whenever user receives a new Yo from another user.
 | 
			
		||||
        """
 | 
			
		||||
@@ -21,6 +20,5 @@ class YoHookTests(WebhookTestCase):
 | 
			
		||||
        self.send_and_test_private_message('', expected_message=expected_message,
 | 
			
		||||
                                           content_type="application/x-www-form-urlencoded")
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Dict[str, Any]
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Dict[str, Any]:
 | 
			
		||||
        return {}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,14 +6,12 @@ class ZapierHookTests(WebhookTestCase):
 | 
			
		||||
    URL_TEMPLATE = "/api/v1/external/zapier?stream={stream}&api_key={api_key}"
 | 
			
		||||
    FIXTURE_DIR_NAME = 'zapier'
 | 
			
		||||
 | 
			
		||||
    def test_zapier_when_subject_and_body_are_correct(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_zapier_when_subject_and_body_are_correct(self) -> None:
 | 
			
		||||
        expected_subject = u"New email from zulip@zulip.com"
 | 
			
		||||
        expected_message = u"Your email content is: \nMy Email content."
 | 
			
		||||
        self.send_and_test_stream_message('correct_subject_and_body', expected_subject, expected_message)
 | 
			
		||||
 | 
			
		||||
    def test_zapier_weather_update(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_zapier_weather_update(self) -> None:
 | 
			
		||||
        expected_subject = u"Here is your weather update for the day:"
 | 
			
		||||
        expected_message = u"Foggy in the morning.\nMaximum temperature to be 24.\nMinimum temperature to be 12"
 | 
			
		||||
        self.send_and_test_stream_message('weather_update', expected_subject, expected_message)
 | 
			
		||||
 
 | 
			
		||||
@@ -16,8 +16,7 @@ class ZenDeskHookTests(WebhookTestCase):
 | 
			
		||||
    DEFAULT_MESSAGE = 'Message'
 | 
			
		||||
    MESSAGE = DEFAULT_MESSAGE
 | 
			
		||||
 | 
			
		||||
    def get_body(self, fixture_name):
 | 
			
		||||
        # type: (Text) -> Dict[str, Any]
 | 
			
		||||
    def get_body(self, fixture_name: Text) -> Dict[str, Any]:
 | 
			
		||||
        return {
 | 
			
		||||
            'ticket_title': self.TICKET_TITLE,
 | 
			
		||||
            'ticket_id': self.TICKET_ID,
 | 
			
		||||
@@ -25,27 +24,23 @@ class ZenDeskHookTests(WebhookTestCase):
 | 
			
		||||
            'stream': self.STREAM_NAME,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    def do_test(self, expected_subject=None, expected_message=None):
 | 
			
		||||
        # type: (Optional[Text], Optional[Text]) -> None
 | 
			
		||||
    def do_test(self, expected_subject: Optional[Text]=None, expected_message: Optional[Text]=None) -> None:
 | 
			
		||||
        self.send_and_test_stream_message("", expected_subject, expected_message,
 | 
			
		||||
                                          content_type=None, **self.api_auth(self.TEST_USER_EMAIL))
 | 
			
		||||
        self.TICKET_TITLE = self.DEFAULT_TICKET_TITLE
 | 
			
		||||
        self.TICKET_ID = self.DEFAULT_TICKET_ID
 | 
			
		||||
        self.MESSAGE = self.DEFAULT_MESSAGE
 | 
			
		||||
 | 
			
		||||
    def test_subject(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_subject(self) -> None:
 | 
			
		||||
        self.TICKET_ID = 4
 | 
			
		||||
        self.TICKET_TITLE = "Test ticket"
 | 
			
		||||
        self.do_test(expected_subject='#4: Test ticket')
 | 
			
		||||
 | 
			
		||||
    def test_long_subject(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_long_subject(self) -> None:
 | 
			
		||||
        self.TICKET_ID = 4
 | 
			
		||||
        self.TICKET_TITLE = "Test ticket" + '!' * 80
 | 
			
		||||
        self.do_test(expected_subject='#4: Test ticket' + '!' * 42 + '...')
 | 
			
		||||
 | 
			
		||||
    def test_content(self):
 | 
			
		||||
        # type: () -> None
 | 
			
		||||
    def test_content(self) -> None:
 | 
			
		||||
        self.MESSAGE = 'New comment:\n> It is better\n* here'
 | 
			
		||||
        self.do_test(expected_message='New comment:\n> It is better\n* here')
 | 
			
		||||
 
 | 
			
		||||
@@ -7,8 +7,7 @@ from zerver.models import get_client, UserProfile
 | 
			
		||||
from django.http import HttpRequest, HttpResponse
 | 
			
		||||
from typing import Text
 | 
			
		||||
 | 
			
		||||
def truncate(string, length):
 | 
			
		||||
    # type: (Text, int) -> Text
 | 
			
		||||
def truncate(string: Text, length: int) -> Text:
 | 
			
		||||
    if len(string) > length:
 | 
			
		||||
        string = string[:length-3] + '...'
 | 
			
		||||
    return string
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user