oncall-engine/engine/apps/mattermost/tests/test_tasks.py
Matias Bordese dcae98b02a
feat: add support for mattermost chatops (#5321)
Related to https://github.com/grafana/oncall/issues/96

---------

Co-authored-by: Ravishankar <ravishankar.gnanaprakasam@gmail.com>
2025-04-21 14:23:37 -03:00

495 lines
20 KiB
Python

import json
from unittest.mock import patch
import httpretty
import pytest
from django.conf import settings
from django.utils import timezone
from rest_framework import status
from apps.alerts.models import AlertGroupLogRecord
from apps.base.models import UserNotificationPolicyLogRecord
from apps.base.models.user_notification_policy import UserNotificationPolicy
from apps.mattermost.client import MattermostAPIException
from apps.mattermost.models import MattermostMessage
from apps.mattermost.tasks import (
notify_user_about_alert_async,
on_alert_group_action_triggered_async,
on_create_alert_async,
)
@pytest.mark.django_db
@httpretty.activate(verbose=True, allow_net_connect=False)
def test_on_create_alert_async_success(
make_organization_and_user,
make_alert_receive_channel,
make_alert_group,
make_alert,
make_mattermost_channel,
make_mattermost_post_response,
):
organization, _ = make_organization_and_user()
alert_receive_channel = make_alert_receive_channel(organization)
alert_group = make_alert_group(alert_receive_channel)
alert = make_alert(alert_group=alert_group, raw_request_data=alert_receive_channel.config.example_payload)
make_mattermost_channel(organization=organization, is_default_channel=True)
url = "{}/api/v4/posts".format(settings.MATTERMOST_HOST)
data = make_mattermost_post_response()
mock_response = httpretty.Response(json.dumps(data), status=status.HTTP_200_OK)
httpretty.register_uri(httpretty.POST, url, responses=[mock_response])
on_create_alert_async(alert_pk=alert.pk)
mattermost_message = alert_group.mattermost_messages.order_by("created_at").first()
assert mattermost_message.post_id == data["id"]
assert mattermost_message.channel_id == data["channel_id"]
assert mattermost_message.message_type == MattermostMessage.ALERT_GROUP_MESSAGE
@pytest.mark.django_db
def test_on_create_alert_async_skip_post_for_duplicate(
make_organization_and_user,
make_alert_receive_channel,
make_alert_group,
make_alert,
make_mattermost_channel,
make_mattermost_message,
):
organization, _ = make_organization_and_user()
alert_receive_channel = make_alert_receive_channel(organization)
alert_group = make_alert_group(alert_receive_channel)
alert = make_alert(alert_group=alert_group, raw_request_data=alert_receive_channel.config.example_payload)
make_mattermost_channel(organization=organization, is_default_channel=True)
make_mattermost_message(alert_group, MattermostMessage.ALERT_GROUP_MESSAGE)
with patch("apps.mattermost.client.MattermostClient.create_post") as mock_post_call:
on_create_alert_async(alert_pk=alert.pk)
mock_post_call.assert_not_called()
@pytest.mark.django_db
def test_on_create_alert_async_skip_post_for_no_channel(
make_organization_and_user,
make_alert_receive_channel,
make_alert_group,
make_alert,
make_mattermost_message,
):
organization, _ = make_organization_and_user()
alert_receive_channel = make_alert_receive_channel(organization)
alert_group = make_alert_group(alert_receive_channel)
alert = make_alert(alert_group=alert_group, raw_request_data=alert_receive_channel.config.example_payload)
make_mattermost_message(alert_group, MattermostMessage.ALERT_GROUP_MESSAGE)
with patch("apps.mattermost.client.MattermostClient.create_post") as mock_post_call:
on_create_alert_async(alert_pk=alert.pk)
mock_post_call.assert_not_called()
@pytest.mark.django_db
@httpretty.activate(verbose=True, allow_net_connect=False)
@pytest.mark.parametrize("status_code", [400, 401])
def test_on_create_alert_async_mattermost_api_failure(
make_organization_and_user,
make_alert_receive_channel,
make_alert_group,
make_alert,
make_mattermost_channel,
make_mattermost_post_response_failure,
status_code,
):
organization, _ = make_organization_and_user()
alert_receive_channel = make_alert_receive_channel(organization)
alert_group = make_alert_group(alert_receive_channel)
alert = make_alert(alert_group=alert_group, raw_request_data=alert_receive_channel.config.example_payload)
make_mattermost_channel(organization=organization, is_default_channel=True)
url = "{}/api/v4/posts".format(settings.MATTERMOST_HOST)
data = make_mattermost_post_response_failure(status_code=status_code)
mock_response = httpretty.Response(json.dumps(data), status=status_code)
httpretty.register_uri(httpretty.POST, url, status=status_code, responses=[mock_response])
on_create_alert_async(alert_pk=alert.pk)
mattermost_message = alert_group.mattermost_messages.order_by("created_at").first()
assert mattermost_message is None
@pytest.mark.django_db
@httpretty.activate(verbose=True, allow_net_connect=False)
def test_on_alert_group_action_triggered_async_success(
make_organization_and_user,
make_alert_receive_channel,
make_alert_group,
make_alert,
make_mattermost_channel,
make_mattermost_post_response,
make_alert_group_log_record,
make_mattermost_message,
):
organization, _ = make_organization_and_user()
alert_receive_channel = make_alert_receive_channel(organization)
ack_alert_group = make_alert_group(alert_receive_channel, acknowledged_at=timezone.now(), acknowledged=True)
make_alert(alert_group=ack_alert_group, raw_request_data=alert_receive_channel.config.example_payload)
make_mattermost_channel(organization=organization, is_default_channel=True)
ack_log_record = make_alert_group_log_record(ack_alert_group, type=AlertGroupLogRecord.TYPE_ACK, author=None)
mattermost_message = make_mattermost_message(ack_alert_group, MattermostMessage.ALERT_GROUP_MESSAGE)
expected_button_ids = ["unacknowledge", "resolve"]
url = "{}/api/v4/posts/{}".format(settings.MATTERMOST_HOST, mattermost_message.post_id)
data = make_mattermost_post_response()
mock_response = httpretty.Response(json.dumps(data), status=status.HTTP_200_OK)
httpretty.register_uri(httpretty.PUT, url, responses=[mock_response])
on_alert_group_action_triggered_async(ack_log_record.pk)
last_request = httpretty.last_request()
assert last_request.method == "PUT"
assert last_request.url == url
request_body = json.loads(last_request.body)
ids = [a["id"] for a in request_body["props"]["attachments"][0]["actions"]]
for id in ids:
assert id in expected_button_ids
@pytest.mark.django_db
@httpretty.activate(verbose=True, allow_net_connect=False)
def test_on_alert_group_action_triggered_async_fails_without_alert_group_message(
make_organization_and_user,
make_alert_receive_channel,
make_alert_group,
make_alert,
make_mattermost_channel,
make_alert_group_log_record,
):
organization, _ = make_organization_and_user()
alert_receive_channel = make_alert_receive_channel(organization)
ack_alert_group = make_alert_group(alert_receive_channel, acknowledged_at=timezone.now(), acknowledged=True)
make_alert(alert_group=ack_alert_group, raw_request_data=alert_receive_channel.config.example_payload)
make_mattermost_channel(organization=organization, is_default_channel=True)
ack_log_record = make_alert_group_log_record(ack_alert_group, type=AlertGroupLogRecord.TYPE_ACK, author=None)
with pytest.raises(MattermostMessage.DoesNotExist):
on_alert_group_action_triggered_async(ack_log_record.pk)
@pytest.mark.django_db
@httpretty.activate(verbose=True, allow_net_connect=False)
@pytest.mark.parametrize("status_code", [400, 401])
def test_on_alert_group_action_triggered_async_failure(
make_organization_and_user,
make_alert_receive_channel,
make_alert_group,
make_alert,
make_mattermost_channel,
make_alert_group_log_record,
make_mattermost_message,
make_mattermost_post_response_failure,
status_code,
):
organization, _ = make_organization_and_user()
alert_receive_channel = make_alert_receive_channel(organization)
ack_alert_group = make_alert_group(alert_receive_channel, acknowledged_at=timezone.now(), acknowledged=True)
make_alert(alert_group=ack_alert_group, raw_request_data=alert_receive_channel.config.example_payload)
make_mattermost_channel(organization=organization, is_default_channel=True)
ack_log_record = make_alert_group_log_record(ack_alert_group, type=AlertGroupLogRecord.TYPE_ACK, author=None)
mattermost_message = make_mattermost_message(ack_alert_group, MattermostMessage.ALERT_GROUP_MESSAGE)
url = "{}/api/v4/posts/{}".format(settings.MATTERMOST_HOST, mattermost_message.post_id)
data = make_mattermost_post_response_failure(status_code=status_code)
mock_response = httpretty.Response(json.dumps(data), status=status_code)
httpretty.register_uri(httpretty.PUT, url, status=status_code, responses=[mock_response])
if status_code != 401:
with pytest.raises(MattermostAPIException):
on_alert_group_action_triggered_async(ack_log_record.pk)
else:
on_alert_group_action_triggered_async(ack_log_record.pk)
last_request = httpretty.last_request()
assert last_request.method == "PUT"
assert last_request.url == url
@pytest.mark.django_db
@httpretty.activate(verbose=True, allow_net_connect=False)
def test_notify_user_about_alert_async_success(
make_organization_and_user,
make_alert_receive_channel,
make_alert_group,
make_alert,
make_mattermost_channel,
make_mattermost_post_response,
make_mattermost_message,
make_user_notification_policy,
make_mattermost_user,
):
organization, user = make_organization_and_user()
alert_receive_channel = make_alert_receive_channel(organization)
alert_group = make_alert_group(alert_receive_channel)
make_alert(alert_group=alert_group, raw_request_data=alert_receive_channel.config.example_payload)
make_mattermost_channel(organization=organization, is_default_channel=True)
make_mattermost_message(alert_group, MattermostMessage.ALERT_GROUP_MESSAGE)
make_mattermost_user(user=user)
user_notification_policy = make_user_notification_policy(
user=user,
step=UserNotificationPolicy.Step.NOTIFY,
notify_by=UserNotificationPolicy.NotificationChannel.TESTONLY,
)
url = "{}/api/v4/posts".format(settings.MATTERMOST_HOST)
data = make_mattermost_post_response()
mock_response = httpretty.Response(json.dumps(data), status=status.HTTP_200_OK)
httpretty.register_uri(httpretty.POST, url, responses=[mock_response])
notify_user_about_alert_async(
user_pk=user.pk, alert_group_pk=alert_group.pk, notification_policy_pk=user_notification_policy.pk
)
log_record = alert_group.personal_log_records.last()
assert log_record.type == UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_SUCCESS
assert log_record.alert_group.pk == alert_group.pk
@pytest.mark.django_db
def test_notify_user_about_alert_async_user_does_not_exist(
make_organization_and_user,
make_alert_receive_channel,
make_alert_group,
make_alert,
make_mattermost_channel,
make_mattermost_message,
make_mattermost_user,
make_user_notification_policy,
):
organization, user = make_organization_and_user()
alert_receive_channel = make_alert_receive_channel(organization)
alert_group = make_alert_group(alert_receive_channel)
make_alert(alert_group=alert_group, raw_request_data=alert_receive_channel.config.example_payload)
make_mattermost_channel(organization=organization, is_default_channel=True)
make_mattermost_message(alert_group, MattermostMessage.ALERT_GROUP_MESSAGE)
make_mattermost_user(user=user)
user_notification_policy = make_user_notification_policy(
user=user,
step=UserNotificationPolicy.Step.NOTIFY,
notify_by=UserNotificationPolicy.NotificationChannel.TESTONLY,
)
with patch("apps.mattermost.client.MattermostClient.create_post") as mock_post_call:
notify_user_about_alert_async(
user_pk=123, alert_group_pk=alert_group.pk, notification_policy_pk=user_notification_policy.pk
)
mock_post_call.assert_not_called()
@pytest.mark.django_db
def test_notify_user_about_alert_async_alert_does_not_exist(
make_organization_and_user,
make_mattermost_channel,
make_mattermost_user,
make_user_notification_policy,
):
organization, user = make_organization_and_user()
make_mattermost_channel(organization=organization, is_default_channel=True)
make_mattermost_user(user=user)
user_notification_policy = make_user_notification_policy(
user=user,
step=UserNotificationPolicy.Step.NOTIFY,
notify_by=UserNotificationPolicy.NotificationChannel.TESTONLY,
)
with patch("apps.mattermost.client.MattermostClient.create_post") as mock_post_call:
notify_user_about_alert_async(
user_pk=user.pk, alert_group_pk=123, notification_policy_pk=user_notification_policy.pk
)
mock_post_call.assert_not_called()
@pytest.mark.django_db
def test_notify_user_about_alert_async_notification_policy_does_not_exist(
make_organization_and_user,
make_alert_receive_channel,
make_alert_group,
make_alert,
make_mattermost_channel,
make_mattermost_message,
make_mattermost_user,
):
organization, user = make_organization_and_user()
alert_receive_channel = make_alert_receive_channel(organization)
alert_group = make_alert_group(alert_receive_channel)
make_alert(alert_group=alert_group, raw_request_data=alert_receive_channel.config.example_payload)
make_mattermost_channel(organization=organization, is_default_channel=True)
make_mattermost_message(alert_group, MattermostMessage.ALERT_GROUP_MESSAGE)
make_mattermost_user(user=user)
with patch("apps.mattermost.client.MattermostClient.create_post") as mock_post_call:
notify_user_about_alert_async(user_pk=user.pk, alert_group_pk=alert_group.pk, notification_policy_pk=123)
mock_post_call.assert_not_called()
@pytest.mark.django_db
def test_notify_user_about_alert_async_mattermost_message_does_not_exist(
make_organization_and_user,
make_alert_receive_channel,
make_alert_group,
make_alert,
make_mattermost_channel,
make_mattermost_user,
make_user_notification_policy,
):
organization, user = make_organization_and_user()
alert_receive_channel = make_alert_receive_channel(organization)
alert_group = make_alert_group(alert_receive_channel)
make_alert(alert_group=alert_group, raw_request_data=alert_receive_channel.config.example_payload)
make_mattermost_channel(organization=organization, is_default_channel=True)
make_mattermost_user(user=user)
user_notification_policy = make_user_notification_policy(
user=user,
step=UserNotificationPolicy.Step.NOTIFY,
notify_by=UserNotificationPolicy.NotificationChannel.TESTONLY,
)
with pytest.raises(MattermostMessage.DoesNotExist):
notify_user_about_alert_async(
user_pk=user.pk, alert_group_pk=alert_group.pk, notification_policy_pk=user_notification_policy.pk
)
@pytest.mark.django_db
@httpretty.activate(verbose=True, allow_net_connect=False)
def test_notify_user_about_alert_async_mattermost_user_does_not_exist(
make_organization_and_user,
make_alert_receive_channel,
make_alert_group,
make_alert,
make_mattermost_channel,
make_mattermost_message,
make_user_notification_policy,
make_mattermost_post_response,
):
organization, user = make_organization_and_user()
alert_receive_channel = make_alert_receive_channel(organization)
alert_group = make_alert_group(alert_receive_channel)
make_alert(alert_group=alert_group, raw_request_data=alert_receive_channel.config.example_payload)
make_mattermost_channel(organization=organization, is_default_channel=True)
make_mattermost_message(alert_group, MattermostMessage.ALERT_GROUP_MESSAGE)
user_notification_policy = make_user_notification_policy(
user=user,
step=UserNotificationPolicy.Step.NOTIFY,
notify_by=UserNotificationPolicy.NotificationChannel.TESTONLY,
)
url = "{}/api/v4/posts".format(settings.MATTERMOST_HOST)
data = make_mattermost_post_response()
mock_response = httpretty.Response(json.dumps(data), status=status.HTTP_200_OK)
httpretty.register_uri(httpretty.POST, url, responses=[mock_response])
notify_user_about_alert_async(
user_pk=user.pk, alert_group_pk=alert_group.pk, notification_policy_pk=user_notification_policy.pk
)
assert alert_group.personal_log_records.filter(
notification_error_code=UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_IN_MATTERMOST_USER_NOT_IN_MATTERMOST
).exists()
log_record = alert_group.personal_log_records.last()
assert log_record.type == UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_SUCCESS
assert log_record.alert_group.pk == alert_group.pk
@pytest.mark.django_db
@httpretty.activate(verbose=True, allow_net_connect=False)
@pytest.mark.parametrize("status_code", [400, 401])
def test_notify_user_about_alert_async_api_failure(
make_organization_and_user,
make_alert_receive_channel,
make_alert_group,
make_alert,
make_mattermost_channel,
make_mattermost_message,
make_mattermost_post_response_failure,
make_user_notification_policy,
make_mattermost_user,
status_code,
):
organization, user = make_organization_and_user()
alert_receive_channel = make_alert_receive_channel(organization)
alert_group = make_alert_group(alert_receive_channel)
make_alert(alert_group=alert_group, raw_request_data=alert_receive_channel.config.example_payload)
make_mattermost_channel(organization=organization, is_default_channel=True)
make_mattermost_message(alert_group, MattermostMessage.ALERT_GROUP_MESSAGE)
make_mattermost_user(user=user)
user_notification_policy = make_user_notification_policy(
user=user,
step=UserNotificationPolicy.Step.NOTIFY,
notify_by=UserNotificationPolicy.NotificationChannel.TESTONLY,
)
url = "{}/api/v4/posts".format(settings.MATTERMOST_HOST)
data = make_mattermost_post_response_failure(status_code=status_code)
mock_response = httpretty.Response(json.dumps(data), status=status_code)
httpretty.register_uri(httpretty.POST, url, status=status_code, responses=[mock_response])
if status_code != 401:
with pytest.raises(MattermostAPIException):
notify_user_about_alert_async(
user_pk=user.pk, alert_group_pk=alert_group.pk, notification_policy_pk=user_notification_policy.pk
)
else:
notify_user_about_alert_async(
user_pk=user.pk, alert_group_pk=alert_group.pk, notification_policy_pk=user_notification_policy.pk
)
log_record = alert_group.personal_log_records.last()
assert (
log_record.notification_error_code
== UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_IN_MATTERMOST_API_UNAUTHORIZED
)
last_request = httpretty.last_request()
assert last_request.method == "POST"
assert last_request.url == url
@pytest.mark.django_db
def test_notify_user_about_alert_async_skip_post_for_no_channel(
make_organization_and_user,
make_alert_receive_channel,
make_alert_group,
make_alert,
make_mattermost_message,
make_mattermost_user,
make_user_notification_policy,
):
organization, user = make_organization_and_user()
alert_receive_channel = make_alert_receive_channel(organization)
alert_group = make_alert_group(alert_receive_channel)
make_alert(alert_group=alert_group, raw_request_data=alert_receive_channel.config.example_payload)
make_mattermost_message(alert_group, MattermostMessage.ALERT_GROUP_MESSAGE)
make_mattermost_user(user=user)
user_notification_policy = make_user_notification_policy(
user=user,
step=UserNotificationPolicy.Step.NOTIFY,
notify_by=UserNotificationPolicy.NotificationChannel.TESTONLY,
)
with patch("apps.mattermost.client.MattermostClient.create_post") as mock_post_call:
notify_user_about_alert_async(
user_pk=user.pk, alert_group_pk=alert_group.pk, notification_policy_pk=user_notification_policy.pk
)
mock_post_call.assert_not_called()