oncall-engine/engine/apps/public_api/tests/test_escalation.py
Ildar Iskhakov 784b7e5344
Add latest alert to public api alert groups endpoint (#5059)
# What this PR does

Added last alert information 
and optimized the API call so it makes 10x less queries by:
* prefetching chatops messages (based on @vadimkerr 's
https://github.com/grafana/oncall/pull/4738)
* using `enrich` from private api

Previously: 
<img width="1102" alt="Screenshot 2024-09-24 at 4 47 00 PM"
src="https://github.com/user-attachments/assets/84edb78e-257a-49cd-bc94-083dd8d043d7">
Now:
<img width="1066" alt="Screenshot 2024-09-24 at 4 44 56 PM"
src="https://github.com/user-attachments/assets/e7dfcc40-dce6-4a0d-9677-910aab2b4f17">



## Which issue(s) this PR closes

Related to [issue link here]

<!--
*Note*: If you want the issue to be auto-closed once the PR is merged,
change "Related to" to "Closes" in the line above.
If you have more than one GitHub issue that this PR closes, be sure to
preface
each issue link with a [closing
keyword](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests#linking-a-pull-request-to-an-issue).
This ensures that the issue(s) are auto-closed once the PR has been
merged.
-->

## Checklist

- [ ] Unit, integration, and e2e (if applicable) tests updated
- [ ] Documentation added (or `pr:no public docs` PR label added if not
required)
- [ ] 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: Vadim Stepanov <vadimkerr@gmail.com>
2024-10-02 17:09:50 +00:00

302 lines
8.3 KiB
Python

from unittest import mock
import pytest
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APIClient
from apps.alerts.models import AlertGroup
from apps.alerts.paging import DirectPagingAlertGroupResolvedError, DirectPagingUserTeamValidationError
title = "Custom title"
message = "Testing escalation with new alert group"
source_url = "https://www.example.com"
@pytest.mark.django_db
def test_escalation_new_alert_group(
make_organization_and_user_with_token,
make_user,
make_user_auth_headers,
):
organization, user, token = make_organization_and_user_with_token()
users_to_page = [
{
"id": make_user(organization=organization).public_primary_key,
"important": False,
},
{
"id": make_user(organization=organization).public_primary_key,
"important": True,
},
]
client = APIClient()
url = reverse("api-public:escalation")
response = client.post(
url,
data={
"users": users_to_page,
"title": title,
"message": message,
},
format="json",
**make_user_auth_headers(user, token),
)
assert response.status_code == status.HTTP_200_OK
alert_groups = AlertGroup.objects.all()
assert alert_groups.count() == 1
ag = alert_groups.get()
assert response.json() == {
"id": ag.public_primary_key,
"integration_id": ag.channel.public_primary_key,
"route_id": ag.channel_filter.public_primary_key,
"team_id": None,
"labels": [],
"alerts_count": 1,
"state": "firing",
"created_at": mock.ANY,
"resolved_at": None,
"resolved_by": None,
"acknowledged_at": None,
"acknowledged_by": None,
"title": title,
"permalinks": {
"slack": None,
"slack_app": None,
"telegram": None,
"web": f"a/grafana-oncall-app/alert-groups/{ag.public_primary_key}",
},
"silenced_at": None,
"last_alert": {
"id": ag.alerts.last().public_primary_key,
"alert_group_id": ag.public_primary_key,
"created_at": ag.alerts.last().created_at.isoformat().replace("+00:00", "Z"),
"payload": ag.alerts.last().raw_request_data,
},
}
alert = ag.alerts.get()
assert ag.web_title_cache == title
assert alert.title == title
assert alert.message == message
@pytest.mark.django_db
def test_escalation_team(
make_organization_and_user_with_token,
make_team,
make_user_auth_headers,
):
organization, user, token = make_organization_and_user_with_token()
team = make_team(organization=organization)
# user must be part of the team
user.teams.add(team)
client = APIClient()
url = reverse("api-public:escalation")
response = client.post(
url,
data={
"team": team.public_primary_key,
"message": message,
"source_url": source_url,
},
format="json",
**make_user_auth_headers(user, token),
)
assert response.status_code == status.HTTP_200_OK
alert_group = AlertGroup.objects.get(public_primary_key=response.json()["id"])
alert = alert_group.alerts.first()
assert alert.raw_request_data["oncall"]["permalink"] == source_url
@pytest.mark.django_db
def test_escalation_existing_alert_group(
make_organization_and_user_with_token,
make_user,
make_alert_receive_channel,
make_alert_group,
make_user_auth_headers,
):
organization, user, token = make_organization_and_user_with_token()
users_to_page = [
{
"id": make_user(organization=organization).public_primary_key,
"important": False,
},
{
"id": make_user(
organization=organization,
).public_primary_key,
"important": True,
},
]
alert_receive_channel = make_alert_receive_channel(organization)
alert_group = make_alert_group(alert_receive_channel)
client = APIClient()
url = reverse("api-public:escalation")
response = client.post(
url,
data={"users": users_to_page, "alert_group_id": alert_group.public_primary_key},
format="json",
**make_user_auth_headers(user, token),
)
assert response.status_code == status.HTTP_200_OK
assert response.json()["id"] == alert_group.public_primary_key
@pytest.mark.django_db
def test_escalation_existing_alert_group_resolved(
make_organization_and_user_with_token,
make_user,
make_alert_receive_channel,
make_alert_group,
make_user_auth_headers,
):
organization, user, token = make_organization_and_user_with_token()
alert_receive_channel = make_alert_receive_channel(organization)
alert_group = make_alert_group(alert_receive_channel, resolved=True)
users_to_page = [
{
"id": make_user(organization=organization).public_primary_key,
"important": False,
},
]
client = APIClient()
url = reverse("api-public:escalation")
response = client.post(
url,
data={
"alert_group_id": alert_group.public_primary_key,
"users": users_to_page,
},
format="json",
**make_user_auth_headers(user, token),
)
assert response.status_code == status.HTTP_400_BAD_REQUEST
assert response.json()["detail"] == DirectPagingAlertGroupResolvedError.DETAIL
@pytest.mark.django_db
def test_escalation_no_user_or_team_specified(
make_organization_and_user_with_token,
make_user_auth_headers,
):
_, user, token = make_organization_and_user_with_token()
client = APIClient()
url = reverse("api-public:escalation")
response = client.post(
url,
data={
"team": None,
"users": [],
},
format="json",
**make_user_auth_headers(user, token),
)
assert response.status_code == status.HTTP_400_BAD_REQUEST
assert response.json()["detail"] == DirectPagingUserTeamValidationError.DETAIL
@pytest.mark.django_db
def test_escalation_both_team_and_users_specified(
make_organization_and_user_with_token,
make_user_auth_headers,
make_user,
make_team,
):
organization, user, token = make_organization_and_user_with_token()
team = make_team(organization=organization)
client = APIClient()
url = reverse("api-public:escalation")
response = client.post(
url,
data={
"team": team.public_primary_key,
"users": [
{
"id": make_user(organization=organization).public_primary_key,
"important": False,
},
],
},
format="json",
**make_user_auth_headers(user, token),
)
assert response.status_code == status.HTTP_400_BAD_REQUEST
assert response.json()["non_field_errors"] == ["users and team are mutually exclusive"]
@pytest.mark.parametrize(
"field_name,field_value",
[
("title", title),
("message", message),
("source_url", source_url),
],
)
@pytest.mark.django_db
def test_escalation_alert_group_id_and_other_fields_are_mutually_exclusive(
make_organization_and_user_with_token,
make_team,
make_user_auth_headers,
make_alert_receive_channel,
make_alert_group,
field_name,
field_value,
):
error_msg = "alert_group_id and (title, message, source_url) are mutually exclusive"
organization, user, token = make_organization_and_user_with_token()
team = make_team(organization=organization)
# user must be part of the team
user.teams.add(team)
alert_receive_channel = make_alert_receive_channel(organization)
alert_group = make_alert_group(alert_receive_channel, resolved=True)
client = APIClient()
url = reverse("api-public:escalation")
response = client.post(
url,
data={
"team": team.public_primary_key,
"alert_group_id": alert_group.public_primary_key,
field_name: field_value,
},
format="json",
**make_user_auth_headers(user, token),
)
assert response.status_code == status.HTTP_400_BAD_REQUEST
assert response.json()["non_field_errors"] == [error_msg]