# What this PR does Allows custom wait durations for: * `Wait` escalation policy * `>X alerts per Y minutes` escalation policy * `Wait` user notification policy ## Which issue(s) this PR closes Related to https://github.com/grafana/oncall/issues/2464 ## Checklist - [x] Unit, integration, and e2e (if applicable) tests updated - [x] Documentation added (or `pr:no public docs` PR label added if not required) - [x] Added the relevant release notes label (see labels prefixed w/ `release:`). These labels dictate how your PR will show up in the autogenerated release notes. --------- Co-authored-by: Rares Mardare <rares.mardare@grafana.com>
498 lines
18 KiB
Python
498 lines
18 KiB
Python
import json
|
|
from datetime import timedelta
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
from django.urls import reverse
|
|
from django.utils import timezone
|
|
from rest_framework import status
|
|
from rest_framework.test import APIClient
|
|
|
|
from apps.api.permissions import LegacyAccessControlRole
|
|
from apps.base.models import UserNotificationPolicy
|
|
from apps.base.tests.messaging_backend import TestOnlyBackend
|
|
|
|
DEFAULT_NOTIFICATION_CHANNEL = UserNotificationPolicy.NotificationChannel.SLACK
|
|
|
|
|
|
@pytest.fixture()
|
|
def user_notification_policy_internal_api_setup(
|
|
make_organization_and_user_with_plugin_token, make_user_for_organization, make_user_notification_policy
|
|
):
|
|
organization, admin, token = make_organization_and_user_with_plugin_token()
|
|
user = make_user_for_organization(organization, role=LegacyAccessControlRole.EDITOR)
|
|
|
|
wait_notification_step = make_user_notification_policy(
|
|
admin, UserNotificationPolicy.Step.WAIT, wait_delay=timezone.timedelta(minutes=15), important=False
|
|
)
|
|
notify_notification_step = make_user_notification_policy(
|
|
admin,
|
|
UserNotificationPolicy.Step.NOTIFY,
|
|
notify_by=UserNotificationPolicy.NotificationChannel.SLACK,
|
|
important=False,
|
|
)
|
|
|
|
important_notification_step = make_user_notification_policy(
|
|
admin,
|
|
UserNotificationPolicy.Step.NOTIFY,
|
|
notify_by=UserNotificationPolicy.NotificationChannel.SLACK,
|
|
important=True,
|
|
)
|
|
|
|
second_user_step = make_user_notification_policy(
|
|
user,
|
|
UserNotificationPolicy.Step.NOTIFY,
|
|
notify_by=UserNotificationPolicy.NotificationChannel.SLACK,
|
|
important=False,
|
|
)
|
|
steps = wait_notification_step, notify_notification_step, important_notification_step, second_user_step
|
|
users = admin, user
|
|
return token, steps, users
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_create_notification_policy(user_notification_policy_internal_api_setup, make_user_auth_headers):
|
|
token, _, users = user_notification_policy_internal_api_setup
|
|
admin, _ = users
|
|
client = APIClient()
|
|
url = reverse("api-internal:notification_policy-list")
|
|
|
|
data = {
|
|
"step": UserNotificationPolicy.Step.NOTIFY,
|
|
"notify_by": UserNotificationPolicy.NotificationChannel.SLACK,
|
|
"wait_delay": None,
|
|
"important": False,
|
|
"user": admin.public_primary_key,
|
|
}
|
|
response = client.post(url, data, format="json", **make_user_auth_headers(admin, token))
|
|
assert response.status_code == status.HTTP_201_CREATED
|
|
|
|
|
|
@pytest.mark.django_db
|
|
@pytest.mark.parametrize("wait_delay", (timedelta(seconds=59), timedelta(hours=24, seconds=1)))
|
|
def test_create_notification_policy_wait_delay_invalid(
|
|
user_notification_policy_internal_api_setup, make_user_auth_headers, wait_delay
|
|
):
|
|
token, _, users = user_notification_policy_internal_api_setup
|
|
admin, _ = users
|
|
client = APIClient()
|
|
url = reverse("api-internal:notification_policy-list")
|
|
|
|
data = {
|
|
"step": UserNotificationPolicy.Step.WAIT,
|
|
"wait_delay": int(wait_delay.total_seconds()),
|
|
"important": False,
|
|
"user": admin.public_primary_key,
|
|
}
|
|
response = client.post(url, data, format="json", **make_user_auth_headers(admin, token))
|
|
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_admin_can_create_notification_policy_for_user(
|
|
user_notification_policy_internal_api_setup, make_user_auth_headers
|
|
):
|
|
token, _, users = user_notification_policy_internal_api_setup
|
|
admin, user = users
|
|
client = APIClient()
|
|
url = reverse("api-internal:notification_policy-list")
|
|
|
|
data = {
|
|
"step": UserNotificationPolicy.Step.NOTIFY,
|
|
"notify_by": UserNotificationPolicy.NotificationChannel.SLACK,
|
|
"wait_delay": None,
|
|
"important": False,
|
|
"user": user.public_primary_key,
|
|
}
|
|
response = client.post(url, data, format="json", **make_user_auth_headers(admin, token))
|
|
assert response.status_code == status.HTTP_201_CREATED
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_user_cant_create_notification_policy_for_user(
|
|
user_notification_policy_internal_api_setup,
|
|
make_user_auth_headers,
|
|
):
|
|
token, _, users = user_notification_policy_internal_api_setup
|
|
admin, user = users
|
|
|
|
client = APIClient()
|
|
url = reverse("api-internal:notification_policy-list")
|
|
|
|
data = {
|
|
"step": UserNotificationPolicy.Step.NOTIFY,
|
|
"notify_by": UserNotificationPolicy.NotificationChannel.SLACK,
|
|
"wait_delay": None,
|
|
"important": False,
|
|
"user": admin.public_primary_key,
|
|
}
|
|
response = client.post(url, data, format="json", **make_user_auth_headers(user, token))
|
|
assert response.status_code == status.HTTP_403_FORBIDDEN
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_create_notification_policy_order_is_ignored(
|
|
user_notification_policy_internal_api_setup,
|
|
make_user_auth_headers,
|
|
):
|
|
token, steps, users = user_notification_policy_internal_api_setup
|
|
wait_notification_step, _, _, _ = steps
|
|
admin, _ = users
|
|
client = APIClient()
|
|
url = reverse("api-internal:notification_policy-list")
|
|
|
|
data = {
|
|
"position": 2023,
|
|
"step": UserNotificationPolicy.Step.NOTIFY,
|
|
"notify_by": UserNotificationPolicy.NotificationChannel.SLACK,
|
|
"wait_delay": None,
|
|
"important": False,
|
|
"user": admin.public_primary_key,
|
|
}
|
|
response = client.post(url, data, format="json", **make_user_auth_headers(admin, token))
|
|
assert response.status_code == status.HTTP_201_CREATED
|
|
assert response.data["order"] == 2
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_move_to_position_position_error(user_notification_policy_internal_api_setup, make_user_auth_headers):
|
|
token, steps, users = user_notification_policy_internal_api_setup
|
|
admin, _ = users
|
|
step = steps[0]
|
|
client = APIClient()
|
|
url = reverse("api-internal:notification_policy-move-to-position", kwargs={"pk": step.public_primary_key})
|
|
|
|
# position value only can be 0 or 1 for this test setup, because there are only 2 steps
|
|
response = client.put(f"{url}?position=2", content_type="application/json", **make_user_auth_headers(admin, token))
|
|
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_update_step(user_notification_policy_internal_api_setup, make_user_auth_headers):
|
|
token, steps, users = user_notification_policy_internal_api_setup
|
|
admin, _ = users
|
|
|
|
_, notify_notification_step, _, _ = steps
|
|
client = APIClient()
|
|
url = reverse("api-internal:notification_policy-detail", kwargs={"pk": notify_notification_step.public_primary_key})
|
|
|
|
response = client.patch(
|
|
url,
|
|
data=json.dumps({"notify_by": UserNotificationPolicy.NotificationChannel.PHONE_CALL}),
|
|
content_type="application/json",
|
|
**make_user_auth_headers(admin, token),
|
|
)
|
|
assert response.status_code == status.HTTP_200_OK
|
|
assert response.data["notify_by"] == UserNotificationPolicy.NotificationChannel.PHONE_CALL
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_admin_can_update_user_step(user_notification_policy_internal_api_setup, make_user_auth_headers):
|
|
token, steps, users = user_notification_policy_internal_api_setup
|
|
admin, _ = users
|
|
_, _, _, second_user_step = steps
|
|
client = APIClient()
|
|
url = reverse("api-internal:notification_policy-detail", kwargs={"pk": second_user_step.public_primary_key})
|
|
|
|
response = client.patch(
|
|
url,
|
|
data=json.dumps({"notify_by": UserNotificationPolicy.NotificationChannel.PHONE_CALL}),
|
|
content_type="application/json",
|
|
**make_user_auth_headers(admin, token),
|
|
)
|
|
assert response.status_code == status.HTTP_200_OK
|
|
assert response.data["notify_by"] == UserNotificationPolicy.NotificationChannel.PHONE_CALL
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_user_cant_update_admin_step(
|
|
user_notification_policy_internal_api_setup,
|
|
make_user_auth_headers,
|
|
):
|
|
token, steps, users = user_notification_policy_internal_api_setup
|
|
_, user = users
|
|
|
|
admin_step, _, _, _ = steps
|
|
client = APIClient()
|
|
url = reverse("api-internal:notification_policy-detail", kwargs={"pk": admin_step.public_primary_key})
|
|
|
|
response = client.patch(
|
|
url,
|
|
data=json.dumps({"notify_by": UserNotificationPolicy.NotificationChannel.PHONE_CALL}),
|
|
content_type="application/json",
|
|
**make_user_auth_headers(user, token),
|
|
)
|
|
assert response.status_code == status.HTTP_403_FORBIDDEN
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_admin_can_move_user_step(user_notification_policy_internal_api_setup, make_user_auth_headers):
|
|
token, steps, users = user_notification_policy_internal_api_setup
|
|
admin, _ = users
|
|
_, _, _, second_user_step = steps
|
|
client = APIClient()
|
|
url = reverse(
|
|
"api-internal:notification_policy-move-to-position", kwargs={"pk": second_user_step.public_primary_key}
|
|
)
|
|
|
|
response = client.put(f"{url}?position=0", content_type="application/json", **make_user_auth_headers(admin, token))
|
|
assert response.status_code == status.HTTP_200_OK
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_user_cant_move_admin_step(user_notification_policy_internal_api_setup, make_user_auth_headers):
|
|
token, steps, users = user_notification_policy_internal_api_setup
|
|
_, user = users
|
|
|
|
admin_step, _, _, _ = steps
|
|
client = APIClient()
|
|
url = reverse("api-internal:notification_policy-move-to-position", kwargs={"pk": admin_step.public_primary_key})
|
|
|
|
response = client.put(f"{url}?position=1", content_type="application/json", **make_user_auth_headers(user, token))
|
|
assert response.status_code == status.HTTP_403_FORBIDDEN
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_unable_to_change_importance(user_notification_policy_internal_api_setup, make_user_auth_headers):
|
|
token, steps, users = user_notification_policy_internal_api_setup
|
|
admin, _ = users
|
|
_, notify_notification_step, _, _ = steps
|
|
client = APIClient()
|
|
url = reverse("api-internal:notification_policy-detail", kwargs={"pk": notify_notification_step.public_primary_key})
|
|
|
|
data = {
|
|
"important": True,
|
|
}
|
|
response = client.patch(
|
|
url, data=json.dumps(data), content_type="application/json", **make_user_auth_headers(admin, token)
|
|
)
|
|
notify_notification_step.refresh_from_db()
|
|
|
|
assert response.status_code == status.HTTP_200_OK
|
|
assert notify_notification_step.important != data["important"]
|
|
|
|
|
|
@pytest.mark.django_db
|
|
@pytest.mark.parametrize("wait_delay, expected_wait_delay", [(None, 300), (900, 900)])
|
|
def test_switch_step_type_from_notify_to_wait(
|
|
make_organization_and_user_with_plugin_token,
|
|
make_user_auth_headers,
|
|
make_user_notification_policy,
|
|
wait_delay,
|
|
expected_wait_delay,
|
|
):
|
|
organization, admin, token = make_organization_and_user_with_plugin_token()
|
|
|
|
notify_notification_step = make_user_notification_policy(
|
|
admin,
|
|
UserNotificationPolicy.Step.NOTIFY,
|
|
notify_by=UserNotificationPolicy.NotificationChannel.SLACK,
|
|
important=False,
|
|
)
|
|
client = APIClient()
|
|
url = reverse("api-internal:notification_policy-detail", kwargs={"pk": notify_notification_step.public_primary_key})
|
|
|
|
data = {
|
|
"id": notify_notification_step.public_primary_key,
|
|
"important": False,
|
|
"notify_by": None,
|
|
"order": 0,
|
|
"user": admin.public_primary_key,
|
|
"step": UserNotificationPolicy.Step.WAIT,
|
|
"wait_delay": wait_delay,
|
|
}
|
|
|
|
expected_response = {
|
|
"id": notify_notification_step.public_primary_key,
|
|
"important": False,
|
|
"notify_by": None,
|
|
"order": 0,
|
|
"user": admin.public_primary_key,
|
|
"step": UserNotificationPolicy.Step.WAIT,
|
|
"wait_delay": expected_wait_delay,
|
|
}
|
|
response = client.put(
|
|
url, data=json.dumps(data), content_type="application/json", **make_user_auth_headers(admin, token)
|
|
)
|
|
notify_notification_step.refresh_from_db()
|
|
|
|
assert response.status_code == status.HTTP_200_OK
|
|
assert response.json() == expected_response
|
|
|
|
|
|
@pytest.mark.django_db
|
|
@pytest.mark.parametrize(
|
|
"notification_channel, expected_notification_channel",
|
|
[
|
|
(None, DEFAULT_NOTIFICATION_CHANNEL),
|
|
(
|
|
UserNotificationPolicy.NotificationChannel.PHONE_CALL,
|
|
UserNotificationPolicy.NotificationChannel.PHONE_CALL,
|
|
),
|
|
],
|
|
)
|
|
def test_switch_step_type_wait_to_notify(
|
|
make_organization_and_user_with_plugin_token,
|
|
make_user_auth_headers,
|
|
make_user_notification_policy,
|
|
notification_channel,
|
|
expected_notification_channel,
|
|
):
|
|
organization, admin, token = make_organization_and_user_with_plugin_token()
|
|
|
|
wait_notification_step = make_user_notification_policy(
|
|
admin,
|
|
UserNotificationPolicy.Step.WAIT,
|
|
important=False,
|
|
)
|
|
|
|
client = APIClient()
|
|
url = reverse("api-internal:notification_policy-detail", kwargs={"pk": wait_notification_step.public_primary_key})
|
|
|
|
data = {
|
|
"id": wait_notification_step.public_primary_key,
|
|
"important": False,
|
|
"notify_by": notification_channel,
|
|
"order": 0,
|
|
"user": admin.public_primary_key,
|
|
"step": UserNotificationPolicy.Step.NOTIFY,
|
|
"wait_delay": None,
|
|
}
|
|
|
|
expected_response = {
|
|
"id": wait_notification_step.public_primary_key,
|
|
"important": False,
|
|
"notify_by": expected_notification_channel,
|
|
"order": 0,
|
|
"user": admin.public_primary_key,
|
|
"step": UserNotificationPolicy.Step.NOTIFY,
|
|
"wait_delay": None,
|
|
}
|
|
response = client.put(
|
|
url, data=json.dumps(data), content_type="application/json", **make_user_auth_headers(admin, token)
|
|
)
|
|
|
|
assert response.status_code == status.HTTP_200_OK
|
|
assert response.json() == expected_response
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_switch_notification_channel(
|
|
make_organization_and_user_with_plugin_token,
|
|
make_user_auth_headers,
|
|
make_user_notification_policy,
|
|
):
|
|
organization, admin, token = make_organization_and_user_with_plugin_token()
|
|
|
|
notify_notification_step = make_user_notification_policy(
|
|
admin,
|
|
UserNotificationPolicy.Step.NOTIFY,
|
|
notify_by=UserNotificationPolicy.NotificationChannel.SLACK,
|
|
important=False,
|
|
)
|
|
client = APIClient()
|
|
url = reverse("api-internal:notification_policy-detail", kwargs={"pk": notify_notification_step.public_primary_key})
|
|
|
|
data = {
|
|
"id": notify_notification_step.public_primary_key,
|
|
"important": False,
|
|
"notify_by": UserNotificationPolicy.NotificationChannel.PHONE_CALL,
|
|
"order": 0,
|
|
"user": admin.public_primary_key,
|
|
"step": UserNotificationPolicy.Step.NOTIFY,
|
|
"wait_delay": None,
|
|
}
|
|
|
|
expected_response = {
|
|
"id": notify_notification_step.public_primary_key,
|
|
"important": False,
|
|
"notify_by": UserNotificationPolicy.NotificationChannel.PHONE_CALL,
|
|
"order": 0,
|
|
"user": admin.public_primary_key,
|
|
"step": UserNotificationPolicy.Step.NOTIFY,
|
|
"wait_delay": None,
|
|
}
|
|
response = client.put(
|
|
url, data=json.dumps(data), content_type="application/json", **make_user_auth_headers(admin, token)
|
|
)
|
|
|
|
assert response.status_code == status.HTTP_200_OK
|
|
assert response.json() == expected_response
|
|
|
|
|
|
@pytest.mark.django_db
|
|
@pytest.mark.parametrize("from_wait_delay, to_wait_delay", [(None, 300), (timezone.timedelta(seconds=900), 900)])
|
|
def test_switch_wait_delay(
|
|
make_organization_and_user_with_plugin_token,
|
|
make_user_auth_headers,
|
|
make_user_notification_policy,
|
|
from_wait_delay,
|
|
to_wait_delay,
|
|
):
|
|
organization, admin, token = make_organization_and_user_with_plugin_token()
|
|
wait_notification_step = make_user_notification_policy(
|
|
admin, UserNotificationPolicy.Step.WAIT, wait_delay=from_wait_delay, important=False
|
|
)
|
|
client = APIClient()
|
|
url = reverse("api-internal:notification_policy-detail", kwargs={"pk": wait_notification_step.public_primary_key})
|
|
|
|
data = {
|
|
"id": wait_notification_step.public_primary_key,
|
|
"important": False,
|
|
"notify_by": DEFAULT_NOTIFICATION_CHANNEL,
|
|
"order": 0,
|
|
"user": admin.public_primary_key,
|
|
"step": UserNotificationPolicy.Step.NOTIFY,
|
|
"wait_delay": to_wait_delay,
|
|
}
|
|
|
|
expected_response = {
|
|
"id": wait_notification_step.public_primary_key,
|
|
"important": False,
|
|
"notify_by": DEFAULT_NOTIFICATION_CHANNEL,
|
|
"order": 0,
|
|
"user": admin.public_primary_key,
|
|
"step": UserNotificationPolicy.Step.NOTIFY,
|
|
"wait_delay": to_wait_delay,
|
|
}
|
|
response = client.put(
|
|
url, data=json.dumps(data), content_type="application/json", **make_user_auth_headers(admin, token)
|
|
)
|
|
|
|
assert response.status_code == status.HTTP_200_OK
|
|
assert response.json() == expected_response
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_notification_policy_backends_enabled(
|
|
user_notification_policy_internal_api_setup, settings, make_user_auth_headers
|
|
):
|
|
token, _, users = user_notification_policy_internal_api_setup
|
|
admin, _ = users
|
|
|
|
client = APIClient()
|
|
url = reverse("api-internal:notification_policy-notify-by-options")
|
|
|
|
response = client.get(url, **make_user_auth_headers(admin, token))
|
|
assert response.status_code == status.HTTP_200_OK
|
|
options = [opt["display_name"] for opt in response.json()]
|
|
assert "Test Only Backend" in options
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_notification_policy_backends_disabled_for_organization(
|
|
user_notification_policy_internal_api_setup, settings, make_user_auth_headers
|
|
):
|
|
token, _, users = user_notification_policy_internal_api_setup
|
|
admin, _ = users
|
|
|
|
client = APIClient()
|
|
url = reverse("api-internal:notification_policy-notify-by-options")
|
|
|
|
with patch.object(TestOnlyBackend, "is_enabled_for_organization", return_value=False):
|
|
response = client.get(url, **make_user_auth_headers(admin, token))
|
|
|
|
assert response.status_code == status.HTTP_200_OK
|
|
|
|
options = [opt["display_name"] for opt in response.json()]
|
|
assert "Test Only Backend" not in options
|