def test_webhook_record_exchange(self, mock_request):
     webhook = get(
         WebHook,
         url='https://example.com/webhook/',
         project=self.project,
         events=[WebHookEvent.objects.get(name=WebHookEvent.BUILD_FAILED)],
         payload='{"request": "ok"}',
     )
     post = mock_request.post(
         webhook.url,
         json={'response': 'ok'},
         headers={'X-Greeting': 'Hi!'},
         status_code=201,
     )
     send_build_notifications(
         version_pk=self.version.pk,
         build_pk=self.build.pk,
         event=WebHookEvent.BUILD_FAILED,
     )
     self.assertTrue(post.called_once)
     self.assertEqual(webhook.exchanges.all().count(), 1)
     exchange = webhook.exchanges.all().first()
     self.assertTrue(exchange.request_headers['User-Agent'].startswith(
         'Read-the-Docs/'))
     self.assertIn('X-Hub-Signature', exchange.request_headers)
     self.assertEqual(exchange.request_body, webhook.payload)
     self.assertEqual(exchange.response_headers, {'X-Greeting': 'Hi!'})
     self.assertEqual(exchange.response_body, '{"response": "ok"}')
     self.assertEqual(exchange.status_code, 201)
 def test_send_notification_none(self):
     send_build_notifications(
         version_pk=self.version.pk,
         build_pk=self.build.pk,
         event=WebHookEvent.BUILD_FAILED,
     )
     self.assertEqual(len(mail.outbox), 0)
    def test_webhook_headers(self, mock_request):
        secret = '1234'
        webhook = get(
            WebHook,
            url='https://example.com/webhook/',
            project=self.project,
            events=[WebHookEvent.objects.get(name=WebHookEvent.BUILD_FAILED)],
            payload='{"sign": "me"}',
            secret=secret,
        )
        post = mock_request.post(webhook.url)
        signature = hmac.new(
            key=secret.encode(),
            msg=webhook.payload.encode(),
            digestmod=hashlib.sha256,
        ).hexdigest()

        send_build_notifications(
            version_pk=self.version.pk,
            build_pk=self.build.pk,
            event=WebHookEvent.BUILD_FAILED,
        )
        self.assertTrue(post.called_once)
        request = post.request_history[0]
        headers = request.headers
        self.assertTrue(headers['User-Agent'].startswith('Read-the-Docs/'))
        self.assertEqual(headers['X-Hub-Signature'], signature)
        self.assertEqual(headers['X-RTD-Event'], WebHookEvent.BUILD_FAILED)
        self.assertEqual(webhook.exchanges.all().count(), 1)
 def test_send_email_notification_on_build_failure(self):
     get(EmailHook, project=self.project)
     send_build_notifications(
         version_pk=self.version.pk,
         build_pk=self.build.pk,
         event=WebHookEvent.BUILD_FAILED,
     )
     self.assertEqual(len(mail.outbox), 1)
 def test_dont_send_email_notifications_for_other_events(self):
     """Email notifications are only send for BUILD_FAILED events."""
     get(EmailHook, project=self.project)
     for event in [WebHookEvent.BUILD_PASSED, WebHookEvent.BUILD_TRIGGERED]:
         send_build_notifications(
             version_pk=self.version.pk,
             build_pk=self.build.pk,
             event=event,
         )
     self.assertEqual(len(mail.outbox), 0)
 def test_send_notification_none_if_wrong_version_pk(self, mock_logger):
     self.assertFalse(Version.objects.filter(pk=345343).exists())
     send_build_notifications(
         version_pk=345343,
         build_pk=None,
         event=WebHookEvent.BUILD_FAILED,
     )
     mock_logger.warning.assert_called_with(
         'Version not found for given kwargs.',
         kwargs={'pk': 345343},
     )
    def test_dont_send_email_notifications_for_external_versions(self):
        get(EmailHook, project=self.project)
        self.version.type = EXTERNAL
        self.version.save()

        send_build_notifications(
            version_pk=self.version.pk,
            build_pk=self.build.pk,
            event=WebHookEvent.BUILD_FAILED,
        )
        self.assertEqual(len(mail.outbox), 0)
 def test_dont_send_webhook_notifications_for_external_versions(self):
     webhook = get(WebHook,
                   url='https://example.com/webhook/',
                   project=self.project)
     self.version.type = EXTERNAL
     self.version.save()
     send_build_notifications(
         version_pk=self.version.pk,
         build_pk=self.build.pk,
         event=WebHookEvent.BUILD_FAILED,
     )
     self.assertEqual(webhook.exchanges.all().count(), 0)
 def test_send_webhook_notification(self, mock_request):
     webhook = get(
         WebHook,
         url='https://example.com/webhook/',
         project=self.project,
         events=[
             WebHookEvent.objects.get(name=WebHookEvent.BUILD_FAILED).id
         ],
     )
     self.assertEqual(webhook.exchanges.all().count(), 0)
     mock_request.post(webhook.url)
     send_build_notifications(
         version_pk=self.version.pk,
         build_pk=self.build.pk,
         event=WebHookEvent.BUILD_FAILED,
     )
     self.assertEqual(webhook.exchanges.all().count(), 1)
     self.assertEqual(len(mail.outbox), 0)
 def test_send_webhook_custom_on_given_event(self, mock_request):
     webhook = get(
         WebHook,
         url='https://example.com/webhook/',
         project=self.project,
         events=[
             WebHookEvent.objects.get(name=WebHookEvent.BUILD_TRIGGERED),
             WebHookEvent.objects.get(name=WebHookEvent.BUILD_FAILED),
         ],
         payload='{}',
     )
     mock_request.post(webhook.url)
     for event, _ in WebHookEvent.EVENTS:
         send_build_notifications(
             version_pk=self.version.pk,
             build_pk=self.build.pk,
             event=event,
         )
     self.assertEqual(len(mail.outbox), 0)
     self.assertEqual(webhook.exchanges.all().count(), 2)
 def test_webhook_notification_has_content_type_header(self):
     webhook = get(
         WebHook,
         url='https://example.com/webhook/',
         project=self.project,
         events=[
             WebHookEvent.objects.get(name=WebHookEvent.BUILD_FAILED).id
         ],
     )
     data = json.dumps({
         'name': self.project.name,
         'slug': self.project.slug,
         'build': {
             'id': self.build.id,
             'commit': self.build.commit,
             'state': self.build.state,
             'success': self.build.success,
             'date': self.build.date.strftime('%Y-%m-%d %H:%M:%S'),
         },
     })
     with mock.patch('readthedocs.builds.tasks.requests.post') as post:
         post.return_value = None
         send_build_notifications(
             version_pk=self.version.pk,
             build_pk=self.build.pk,
             event=WebHookEvent.BUILD_FAILED,
         )
         post.assert_called_once_with(
             webhook.url,
             data=data,
             headers={
                 'content-type': 'application/json',
                 'X-Hub-Signature': mock.ANY,
                 'User-Agent': mock.ANY,
                 'X-RTD-Event': mock.ANY,
             },
             timeout=mock.ANY,
         )
 def test_send_webhook_custom_payload(self, mock_request):
     self.build.date = timezone.datetime(
         year=2021,
         month=3,
         day=15,
         hour=15,
         minute=30,
         second=4,
     )
     self.build.save()
     webhook = get(
         WebHook,
         url='https://example.com/webhook/',
         project=self.project,
         events=[WebHookEvent.objects.get(name=WebHookEvent.BUILD_FAILED)],
         payload=json.dumps({
             'message':
             'Event {{ event }} triggered for {{ version.slug }}',
             'extra-data': {
                 'build_id': '{{build.id}}',
                 'build_commit': '{{build.commit}}',
                 'build_url': '{{ build.url }}',
                 'build_docsurl': '{{ build.docs_url }}',
                 'build_start_date': '{{ build.start_date }}',
                 'organization_slug': '{{ organization.slug }}',
                 'organization_name': '{{ organization.name }}',
                 'project_slug': '{{ project.slug }}',
                 'project_name': '{{ project.name }}',
                 'project_url': '{{ project.url }}',
                 'version_slug': '{{ version.slug }}',
                 'version_name': '{{ version.name }}',
                 'invalid_substitution': '{{ invalid.substitution }}',
             }
         }),
     )
     post = mock_request.post(webhook.url)
     send_build_notifications(
         version_pk=self.version.pk,
         build_pk=self.build.pk,
         event=WebHookEvent.BUILD_FAILED,
     )
     self.assertTrue(post.called_once)
     request = post.request_history[0]
     self.assertEqual(
         request.json(), {
             'message':
             f'Event build:failed triggered for {self.version.slug}',
             'extra-data': {
                 'build_id': str(self.build.pk),
                 'build_commit': self.build.commit,
                 'build_url':
                 f'https://readthedocs.org{self.build.get_absolute_url()}',
                 'build_docsurl': 'http://test.readthedocs.io/en/1.0/',
                 'build_start_date': '2021-03-15T15:30:04',
                 'organization_name': '',
                 'organization_slug': '',
                 'project_name': self.project.name,
                 'project_slug': self.project.slug,
                 'project_url':
                 f'https://readthedocs.org{self.project.get_absolute_url()}',
                 'version_name': self.version.verbose_name,
                 'version_slug': self.version.slug,
                 'invalid_substitution': '{{ invalid.substitution }}',
             },
         })
     self.assertEqual(webhook.exchanges.all().count(), 1)