Related to [logs](https://ops.grafana-ops.net/explore?schemaVersion=1&panes=%7B%22hum%22:%7B%22datasource%22:%22c-R8UWvVk%22,%22queries%22:%5B%7B%22refId%22:%22A%22,%22expr%22:%22%7Bnamespace%3D%5C%22amixr-prod%5C%22,%20job%3D%5C%22amixr-prod%2Famixr-integrations%5C%22%7D%20%7C%3D%20%5C%22django.core.serializers.base.DeserializationError%5C%22%22,%22queryType%22:%22range%22,%22datasource%22:%7B%22type%22:%22loki%22,%22uid%22:%22c-R8UWvVk%22%7D,%22editorMode%22:%22code%22%7D%5D,%22range%22:%7B%22from%22:%221706023840486%22,%22to%22:%221706024722486%22%7D%7D%7D&orgId=1)
517 lines
16 KiB
Python
517 lines
16 KiB
Python
from unittest.mock import call, patch
|
|
|
|
import pytest
|
|
from django.core.cache import cache
|
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
|
from django.db import OperationalError
|
|
from django.urls import reverse
|
|
from django.utils import timezone
|
|
from pytest_django.plugin import _DatabaseBlocker
|
|
from rest_framework import status
|
|
from rest_framework.test import APIClient
|
|
|
|
from apps.alerts.models import AlertReceiveChannel
|
|
from apps.integrations.mixins import AlertChannelDefiningMixin
|
|
|
|
|
|
class DatabaseBlocker(_DatabaseBlocker):
|
|
"""Customize pytest_django db blocker to raise OperationalError exception."""
|
|
|
|
def _blocking_wrapper(*args, **kwargs):
|
|
__tracebackhide__ = True
|
|
__tracebackhide__ # Silence pyflakes
|
|
# mimic DB unavailable error
|
|
raise OperationalError("Database access disabled")
|
|
|
|
|
|
def setup_failing_redis_cache(settings):
|
|
settings.DJANGO_REDIS_IGNORE_EXCEPTIONS = True
|
|
settings.RATELIMIT_FAIL_OPEN = True
|
|
settings.CACHES = {
|
|
"default": {
|
|
"BACKEND": "django_redis.cache.RedisCache",
|
|
"LOCATION": "redis://no-redis-here/",
|
|
}
|
|
}
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_integration_json_data_too_big(settings, make_organization_and_user, make_alert_receive_channel):
|
|
settings.DATA_UPLOAD_MAX_MEMORY_SIZE = 50
|
|
|
|
organization, user = make_organization_and_user()
|
|
alert_receive_channel = make_alert_receive_channel(
|
|
organization=organization,
|
|
author=user,
|
|
integration=AlertReceiveChannel.INTEGRATION_ALERTMANAGER,
|
|
)
|
|
|
|
client = APIClient()
|
|
url = reverse("integrations:alertmanager", kwargs={"alert_channel_key": alert_receive_channel.token})
|
|
|
|
data = {"value": "a" * settings.DATA_UPLOAD_MAX_MEMORY_SIZE}
|
|
response = client.post(url, data, format="json")
|
|
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_integration_form_data_too_big(settings, make_organization_and_user, make_alert_receive_channel):
|
|
settings.DATA_UPLOAD_MAX_MEMORY_SIZE = 50
|
|
|
|
organization, user = make_organization_and_user()
|
|
alert_receive_channel = make_alert_receive_channel(
|
|
organization=organization,
|
|
author=user,
|
|
integration=AlertReceiveChannel.INTEGRATION_ALERTMANAGER,
|
|
)
|
|
|
|
client = APIClient()
|
|
url = reverse("integrations:alertmanager", kwargs={"alert_channel_key": alert_receive_channel.token})
|
|
|
|
data = {"value": "a" * settings.DATA_UPLOAD_MAX_MEMORY_SIZE}
|
|
response = client.post(url, data, content_type="application/x-www-form-urlencoded")
|
|
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
|
|
|
|
|
@patch("apps.integrations.views.create_alert")
|
|
@pytest.mark.parametrize(
|
|
"integration_type",
|
|
[
|
|
arc_type
|
|
for arc_type in AlertReceiveChannel.INTEGRATION_TYPES
|
|
if arc_type not in ["amazon_sns", "grafana", "alertmanager", "grafana_alerting", "maintenance"]
|
|
],
|
|
)
|
|
@pytest.mark.django_db
|
|
def test_integration_universal_endpoint(
|
|
mock_create_alert, make_organization_and_user, make_alert_receive_channel, integration_type
|
|
):
|
|
organization, user = make_organization_and_user()
|
|
alert_receive_channel = make_alert_receive_channel(
|
|
organization=organization,
|
|
author=user,
|
|
integration=integration_type,
|
|
)
|
|
|
|
client = APIClient()
|
|
url = reverse(
|
|
"integrations:universal",
|
|
kwargs={"integration_type": integration_type, "alert_channel_key": alert_receive_channel.token},
|
|
)
|
|
|
|
data = {"foo": "bar"}
|
|
now = timezone.now()
|
|
with patch("django.utils.timezone.now") as mock_now:
|
|
mock_now.return_value = now
|
|
response = client.post(url, data, format="json")
|
|
assert response.status_code == status.HTTP_200_OK
|
|
|
|
mock_create_alert.apply_async.assert_called_once_with(
|
|
[],
|
|
{
|
|
"title": None,
|
|
"message": None,
|
|
"image_url": None,
|
|
"link_to_upstream_details": None,
|
|
"alert_receive_channel_pk": alert_receive_channel.pk,
|
|
"integration_unique_data": None,
|
|
"raw_request_data": data,
|
|
"received_at": now.isoformat(),
|
|
},
|
|
)
|
|
|
|
|
|
@patch("apps.integrations.views.create_alertmanager_alerts")
|
|
@pytest.mark.django_db
|
|
def test_integration_grafana_endpoint_wrong_endpoint(
|
|
mock_create_alertmanager_alerts, make_organization_and_user, make_alert_receive_channel
|
|
):
|
|
integration_type = "grafana_alerting"
|
|
organization, user = make_organization_and_user()
|
|
alert_receive_channel = make_alert_receive_channel(
|
|
organization=organization,
|
|
author=user,
|
|
integration=integration_type,
|
|
)
|
|
|
|
client = APIClient()
|
|
url = reverse("integrations:grafana", kwargs={"alert_channel_key": alert_receive_channel.token})
|
|
|
|
response = client.post(url, {}, format="json")
|
|
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
|
|
|
mock_create_alertmanager_alerts.assert_not_called()
|
|
|
|
|
|
@patch("apps.integrations.views.create_alertmanager_alerts")
|
|
@pytest.mark.django_db
|
|
def test_integration_grafana_endpoint_has_alerts(
|
|
mock_create_alertmanager_alerts, settings, make_organization_and_user, make_alert_receive_channel
|
|
):
|
|
settings.DEBUG = False
|
|
|
|
integration_type = "grafana"
|
|
organization, user = make_organization_and_user()
|
|
alert_receive_channel = make_alert_receive_channel(
|
|
organization=organization,
|
|
author=user,
|
|
integration=integration_type,
|
|
)
|
|
|
|
client = APIClient()
|
|
url = reverse("integrations:grafana", kwargs={"alert_channel_key": alert_receive_channel.token})
|
|
|
|
data = {
|
|
"alerts": [
|
|
{
|
|
"foo": 123,
|
|
},
|
|
{
|
|
"foo": 456,
|
|
},
|
|
]
|
|
}
|
|
now = timezone.now()
|
|
with patch("django.utils.timezone.now") as mock_now:
|
|
mock_now.return_value = now
|
|
response = client.post(url, data, format="json")
|
|
assert response.status_code == status.HTTP_200_OK
|
|
|
|
mock_create_alertmanager_alerts.apply_async.assert_has_calls(
|
|
[
|
|
call((alert_receive_channel.pk, data["alerts"][0]), kwargs={"received_at": now.isoformat()}),
|
|
call((alert_receive_channel.pk, data["alerts"][1]), kwargs={"received_at": now.isoformat()}),
|
|
]
|
|
)
|
|
|
|
|
|
@patch("apps.integrations.views.create_alert")
|
|
@pytest.mark.django_db
|
|
def test_integration_old_grafana_endpoint(
|
|
mock_create_alert, settings, make_organization_and_user, make_alert_receive_channel
|
|
):
|
|
settings.DEBUG = False
|
|
|
|
integration_type = "grafana"
|
|
organization, user = make_organization_and_user()
|
|
alert_receive_channel = make_alert_receive_channel(
|
|
organization=organization,
|
|
author=user,
|
|
integration=integration_type,
|
|
)
|
|
|
|
client = APIClient()
|
|
url = reverse("integrations:grafana", kwargs={"alert_channel_key": alert_receive_channel.token})
|
|
|
|
data = {}
|
|
now = timezone.now()
|
|
with patch("django.utils.timezone.now") as mock_now:
|
|
mock_now.return_value = now
|
|
response = client.post(url, data, format="json")
|
|
assert response.status_code == status.HTTP_200_OK
|
|
|
|
mock_create_alert.apply_async.assert_called_once_with(
|
|
[],
|
|
{
|
|
"title": "Title",
|
|
"message": None,
|
|
"image_url": None,
|
|
"link_to_upstream_details": None,
|
|
"alert_receive_channel_pk": alert_receive_channel.pk,
|
|
"integration_unique_data": '{"evalMatches": []}',
|
|
"raw_request_data": data,
|
|
"received_at": now.isoformat(),
|
|
},
|
|
)
|
|
|
|
|
|
@patch("apps.integrations.views.create_alert")
|
|
@pytest.mark.parametrize(
|
|
"integration_type",
|
|
[
|
|
arc_type
|
|
for arc_type in AlertReceiveChannel.INTEGRATION_TYPES
|
|
if arc_type not in ["amazon_sns", "grafana", "alertmanager", "grafana_alerting", "maintenance"]
|
|
],
|
|
)
|
|
@pytest.mark.django_db
|
|
def test_integration_universal_endpoint_not_allow_files(
|
|
mock_create_alert, make_organization_and_user, make_alert_receive_channel, integration_type
|
|
):
|
|
organization, user = make_organization_and_user()
|
|
alert_receive_channel = make_alert_receive_channel(
|
|
organization=organization,
|
|
author=user,
|
|
integration=integration_type,
|
|
)
|
|
|
|
client = APIClient()
|
|
url = reverse(
|
|
"integrations:universal",
|
|
kwargs={"integration_type": integration_type, "alert_channel_key": alert_receive_channel.token},
|
|
)
|
|
|
|
test_file = SimpleUploadedFile("testing", b"file_content")
|
|
data = {"foo": "bar", "f": test_file}
|
|
response = client.post(url, data)
|
|
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
|
|
|
assert not mock_create_alert.apply_async.called
|
|
|
|
|
|
@patch("apps.integrations.views.create_alert")
|
|
@pytest.mark.parametrize(
|
|
"integration_type",
|
|
[
|
|
arc_type
|
|
for arc_type in AlertReceiveChannel.INTEGRATION_TYPES
|
|
if arc_type not in ["amazon_sns", "grafana", "alertmanager", "grafana_alerting", "maintenance"]
|
|
],
|
|
)
|
|
@pytest.mark.django_db
|
|
def test_integration_universal_endpoint_works_without_db(
|
|
mock_create_alert, make_organization_and_user, make_alert_receive_channel, integration_type
|
|
):
|
|
organization, user = make_organization_and_user()
|
|
alert_receive_channel = make_alert_receive_channel(
|
|
organization=organization,
|
|
author=user,
|
|
integration=integration_type,
|
|
)
|
|
|
|
client = APIClient()
|
|
url = reverse(
|
|
"integrations:universal",
|
|
kwargs={"integration_type": integration_type, "alert_channel_key": alert_receive_channel.token},
|
|
)
|
|
|
|
# populate cache
|
|
AlertChannelDefiningMixin().update_alert_receive_channel_cache()
|
|
|
|
now = timezone.now()
|
|
with patch("django.utils.timezone.now") as mock_now:
|
|
mock_now.return_value = now
|
|
# disable DB access
|
|
with DatabaseBlocker().block():
|
|
data = {"foo": "bar"}
|
|
response = client.post(url, data, format="json")
|
|
|
|
assert response.status_code == status.HTTP_200_OK
|
|
|
|
mock_create_alert.apply_async.assert_called_once_with(
|
|
[],
|
|
{
|
|
"title": None,
|
|
"message": None,
|
|
"image_url": None,
|
|
"link_to_upstream_details": None,
|
|
"alert_receive_channel_pk": alert_receive_channel.pk,
|
|
"integration_unique_data": None,
|
|
"raw_request_data": data,
|
|
"received_at": now.isoformat(),
|
|
},
|
|
)
|
|
|
|
|
|
@patch("apps.integrations.views.create_alertmanager_alerts")
|
|
@pytest.mark.django_db
|
|
def test_integration_grafana_endpoint_without_db_has_alerts(
|
|
mock_create_alertmanager_alerts, settings, make_organization_and_user, make_alert_receive_channel
|
|
):
|
|
settings.DEBUG = False
|
|
|
|
integration_type = "grafana"
|
|
organization, user = make_organization_and_user()
|
|
alert_receive_channel = make_alert_receive_channel(
|
|
organization=organization,
|
|
author=user,
|
|
integration=integration_type,
|
|
)
|
|
|
|
client = APIClient()
|
|
url = reverse("integrations:grafana", kwargs={"alert_channel_key": alert_receive_channel.token})
|
|
|
|
data = {
|
|
"alerts": [
|
|
{
|
|
"foo": 123,
|
|
},
|
|
{
|
|
"foo": 456,
|
|
},
|
|
]
|
|
}
|
|
|
|
# populate cache
|
|
AlertChannelDefiningMixin().update_alert_receive_channel_cache()
|
|
|
|
now = timezone.now()
|
|
with patch("django.utils.timezone.now") as mock_now:
|
|
mock_now.return_value = now
|
|
# disable DB access
|
|
with DatabaseBlocker().block():
|
|
response = client.post(url, data, format="json")
|
|
|
|
assert response.status_code == status.HTTP_200_OK
|
|
|
|
mock_create_alertmanager_alerts.apply_async.assert_has_calls(
|
|
[
|
|
call((alert_receive_channel.pk, data["alerts"][0]), kwargs={"received_at": now.isoformat()}),
|
|
call((alert_receive_channel.pk, data["alerts"][1]), kwargs={"received_at": now.isoformat()}),
|
|
]
|
|
)
|
|
|
|
|
|
@patch("apps.integrations.views.create_alert")
|
|
@pytest.mark.parametrize(
|
|
"integration_type",
|
|
[
|
|
arc_type
|
|
for arc_type in AlertReceiveChannel.INTEGRATION_TYPES
|
|
if arc_type not in ["amazon_sns", "grafana", "alertmanager", "grafana_alerting", "maintenance"]
|
|
],
|
|
)
|
|
@pytest.mark.django_db
|
|
def test_integration_universal_endpoint_works_without_cache(
|
|
mock_create_alert,
|
|
make_organization_and_user,
|
|
make_alert_receive_channel,
|
|
integration_type,
|
|
settings,
|
|
):
|
|
# setup failing redis cache and ignore exception settings
|
|
setup_failing_redis_cache(settings)
|
|
|
|
organization, user = make_organization_and_user()
|
|
alert_receive_channel = make_alert_receive_channel(
|
|
organization=organization,
|
|
author=user,
|
|
integration=integration_type,
|
|
)
|
|
|
|
client = APIClient()
|
|
url = reverse(
|
|
"integrations:universal",
|
|
kwargs={"integration_type": integration_type, "alert_channel_key": alert_receive_channel.token},
|
|
)
|
|
data = {"foo": "bar"}
|
|
now = timezone.now()
|
|
with patch("django.utils.timezone.now") as mock_now:
|
|
mock_now.return_value = now
|
|
response = client.post(url, data, format="json")
|
|
|
|
assert response.status_code == status.HTTP_200_OK
|
|
|
|
mock_create_alert.apply_async.assert_called_once_with(
|
|
[],
|
|
{
|
|
"title": None,
|
|
"message": None,
|
|
"image_url": None,
|
|
"link_to_upstream_details": None,
|
|
"alert_receive_channel_pk": alert_receive_channel.pk,
|
|
"integration_unique_data": None,
|
|
"raw_request_data": data,
|
|
"received_at": now.isoformat(),
|
|
},
|
|
)
|
|
|
|
|
|
@patch("apps.integrations.views.create_alertmanager_alerts")
|
|
@pytest.mark.django_db
|
|
def test_integration_grafana_endpoint_without_cache_has_alerts(
|
|
mock_create_alertmanager_alerts, settings, make_organization_and_user, make_alert_receive_channel
|
|
):
|
|
settings.DEBUG = False
|
|
# setup failing redis cache and ignore exception settings
|
|
setup_failing_redis_cache(settings)
|
|
|
|
integration_type = "grafana"
|
|
organization, user = make_organization_and_user()
|
|
alert_receive_channel = make_alert_receive_channel(
|
|
organization=organization,
|
|
author=user,
|
|
integration=integration_type,
|
|
)
|
|
|
|
client = APIClient()
|
|
url = reverse("integrations:grafana", kwargs={"alert_channel_key": alert_receive_channel.token})
|
|
|
|
data = {
|
|
"alerts": [
|
|
{
|
|
"foo": 123,
|
|
},
|
|
{
|
|
"foo": 456,
|
|
},
|
|
]
|
|
}
|
|
now = timezone.now()
|
|
with patch("django.utils.timezone.now") as mock_now:
|
|
mock_now.return_value = now
|
|
response = client.post(url, data, format="json")
|
|
|
|
assert response.status_code == status.HTTP_200_OK
|
|
|
|
mock_create_alertmanager_alerts.apply_async.assert_has_calls(
|
|
[
|
|
call((alert_receive_channel.pk, data["alerts"][0]), kwargs={"received_at": now.isoformat()}),
|
|
call((alert_receive_channel.pk, data["alerts"][1]), kwargs={"received_at": now.isoformat()}),
|
|
]
|
|
)
|
|
|
|
|
|
@patch("apps.integrations.views.create_alert")
|
|
@pytest.mark.parametrize(
|
|
"integration_type",
|
|
[
|
|
arc_type
|
|
for arc_type in AlertReceiveChannel.INTEGRATION_TYPES
|
|
if arc_type not in ["amazon_sns", "grafana", "alertmanager", "grafana_alerting", "maintenance"]
|
|
],
|
|
)
|
|
@pytest.mark.django_db
|
|
def test_integration_outdated_cached_model(
|
|
mock_create_alert,
|
|
make_organization_and_user,
|
|
make_alert_receive_channel,
|
|
integration_type,
|
|
):
|
|
organization, user = make_organization_and_user()
|
|
alert_receive_channel = make_alert_receive_channel(
|
|
organization=organization,
|
|
author=user,
|
|
integration=integration_type,
|
|
)
|
|
|
|
# set an invalid cache value for the requested integration
|
|
cache_key = AlertChannelDefiningMixin.CACHE_KEY_SHORT_TERM + "_" + alert_receive_channel.token
|
|
cache.set(cache_key, '{"some": "invalid json model"}')
|
|
|
|
client = APIClient()
|
|
url = reverse(
|
|
"integrations:universal",
|
|
kwargs={"integration_type": integration_type, "alert_channel_key": alert_receive_channel.token},
|
|
)
|
|
data = {"foo": "bar"}
|
|
now = timezone.now()
|
|
with patch("django.utils.timezone.now") as mock_now:
|
|
mock_now.return_value = now
|
|
response = client.post(url, data, format="json")
|
|
|
|
assert response.status_code == status.HTTP_200_OK
|
|
|
|
mock_create_alert.apply_async.assert_called_once_with(
|
|
[],
|
|
{
|
|
"title": None,
|
|
"message": None,
|
|
"image_url": None,
|
|
"link_to_upstream_details": None,
|
|
"alert_receive_channel_pk": alert_receive_channel.pk,
|
|
"integration_unique_data": None,
|
|
"raw_request_data": data,
|
|
"received_at": now.isoformat(),
|
|
},
|
|
)
|