oncall-engine/engine/apps/phone_notifications/tests/test_phone_backend_call.py
Joey Orlando 49d20f1a7e
bump uwsgi to 2.0.26 + Python to 3.12.3 (#4495)
# What this PR does

- bumps `uwsgi` to latest version (`2.0.26`), which unblocks us from
bumping Python to 3.12
- bumps Python to 3.12.3
- refactor the Snyk GitHub Actions workflow to use the composable
actions for installed frontend and backend dependencies
- fixes several `AttributeError`s in our tests that went from a warning
to an error in Python 3.12 (see
https://github.com/python/cpython/issues/100690)

# Which issue(s) this PR closes

Closes #4358
Closes https://github.com/grafana/oncall/issues/4387
2024-06-10 15:33:37 -04:00

227 lines
8.4 KiB
Python

from unittest import mock
import pytest
from django.test import override_settings
from apps.base.models import UserNotificationPolicy, UserNotificationPolicyLogRecord
from apps.phone_notifications.exceptions import (
CallsLimitExceeded,
FailedToMakeCall,
NumberNotVerified,
ProviderNotSupports,
)
from apps.phone_notifications.models import PhoneCallRecord
from apps.phone_notifications.phone_backend import PhoneBackend
notify = UserNotificationPolicy.Step.NOTIFY
notify_by_phone = 2
@pytest.fixture()
def setup(
make_organization_and_user, make_alert_receive_channel, make_alert_group, make_alert, make_user_notification_policy
):
org, user = make_organization_and_user()
arc = make_alert_receive_channel(org)
alert_group = make_alert_group(arc)
make_alert(alert_group, {})
notification_policy = make_user_notification_policy(
user, UserNotificationPolicy.Step.NOTIFY, notify_by=notify_by_phone
)
return user, alert_group, notification_policy
@pytest.mark.django_db
@mock.patch("apps.phone_notifications.phone_backend.PhoneBackend._notify_by_provider_call")
@override_settings(GRAFANA_CLOUD_NOTIFICATIONS_ENABLED=False)
def test_notify_by_call_uses_provider(mock_notify_by_provider_call, setup):
"""
test if make_provider_call called when GRAFANA_CLOUD_NOTIFICATIONS_ENABLED is False
"""
user, alert_group, notification_policy = setup
phone_backend = PhoneBackend()
phone_backend.notify_by_call(user, alert_group, notification_policy)
assert mock_notify_by_provider_call.called
assert (
PhoneCallRecord.objects.filter(
exceeded_limit=False,
represents_alert_group=alert_group,
notification_policy=notification_policy,
receiver=user,
grafana_cloud_notification=False,
).count()
== 1
)
@pytest.mark.django_db
@mock.patch("apps.phone_notifications.phone_backend.PhoneBackend._notify_by_cloud_call")
@override_settings(GRAFANA_CLOUD_NOTIFICATIONS_ENABLED=True)
def test_notify_by_call_uses_cloud(mock_notify_by_cloud_call, setup):
"""
test if notify_by_cloud_call called when GRAFANA_CLOUD_NOTIFICATIONS_ENABLED is True
"""
user, alert_group, notification_policy = setup
phone_backend = PhoneBackend()
phone_backend.notify_by_call(user, alert_group, notification_policy)
assert mock_notify_by_cloud_call.called
assert (
PhoneCallRecord.objects.filter(
exceeded_limit=False,
represents_alert_group=alert_group,
notification_policy=notification_policy,
receiver=user,
grafana_cloud_notification=False,
).count()
== 1
)
@pytest.mark.django_db
@mock.patch("apps.phone_notifications.phone_backend.PhoneBackend._validate_user_number", return_value=False)
@override_settings(GRAFANA_CLOUD_NOTIFICATIONS_ENABLED=False)
def test_notify_by_provider_call_raises_number_not_verified(
mock_validate_user_number,
make_organization_and_user,
):
_, user = make_organization_and_user()
phone_backend = PhoneBackend()
with pytest.raises(NumberNotVerified):
phone_backend._notify_by_provider_call(user, "some_message")
@pytest.mark.django_db
@mock.patch("apps.phone_notifications.phone_backend.PhoneBackend._validate_user_number", return_value=True)
@mock.patch("apps.phone_notifications.phone_backend.PhoneBackend._validate_phone_calls_left", return_value=0)
@mock.patch("apps.phone_notifications.tests.mock_phone_provider.MockPhoneProvider.make_notification_call")
@override_settings(GRAFANA_CLOUD_NOTIFICATIONS_ENABLED=False)
def test_notify_by_provider_call_rases_limit_exceeded(
mock_make_notification_call,
mock_phone_calls_left,
mock_validate_user_number,
make_organization_and_user,
):
"""
test if CallsLimitExceeded raised when phone notifications limit is empty
"""
_, user = make_organization_and_user()
phone_backend = PhoneBackend()
with pytest.raises(CallsLimitExceeded):
phone_backend._notify_by_provider_call(user, "some_message")
assert mock_make_notification_call.called is False
assert PhoneCallRecord.objects.all().count() == 0
@mock.patch("apps.phone_notifications.phone_backend.PhoneBackend._validate_user_number", return_value=True)
@mock.patch("apps.phone_notifications.phone_backend.PhoneBackend._validate_phone_calls_left", return_value=2)
@mock.patch(
"apps.phone_notifications.phone_backend.PhoneBackend._add_call_limit_warning", return_value="mock warning value"
)
@mock.patch("apps.phone_notifications.tests.mock_phone_provider.MockPhoneProvider.make_notification_call")
@override_settings(GRAFANA_CLOUD_NOTIFICATIONS_ENABLED=False)
@pytest.mark.django_db
def test_notify_by_provider_call_limits_warning(
mock_make_notification_call,
mock_add_call_limit_warning,
mock_validate_phone_calls_left,
mock_validate_user_number,
make_organization_and_user,
):
"""
test if warning message added to call message, when almost no phone notifications left
"""
_, user = make_organization_and_user()
phone_backend = PhoneBackend()
phone_backend._notify_by_provider_call(user, "some_message")
mock_add_call_limit_warning.assert_called_once_with(2, "some_message")
@pytest.mark.django_db
@mock.patch("apps.phone_notifications.phone_backend.PhoneBackend._notify_by_provider_call")
@pytest.mark.parametrize(
"exc,log_err_code",
[
(NumberNotVerified, UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_PHONE_NUMBER_IS_NOT_VERIFIED),
(CallsLimitExceeded, UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_PHONE_CALLS_LIMIT_EXCEEDED),
(FailedToMakeCall, UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_NOT_ABLE_TO_CALL),
(ProviderNotSupports, UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_NOT_ABLE_TO_CALL),
],
)
@override_settings(GRAFANA_CLOUD_NOTIFICATIONS_ENABLED=False)
def test_notify_by_call_handles_exceptions_from_provider(
mock_notify_by_provider_call,
setup,
exc,
log_err_code,
):
"""
test if UserNotificationPolicyLogRecord is created when exception is raised from _notify_by_provider_call.
_notify_by_provider_call is mocked to raise exceptions which may occur while checking if phone call possible to male and
exceptions from phone_provider also
"""
user, alert_group, notification_policy = setup
mock_notify_by_provider_call.side_effect = exc
phone_backend = PhoneBackend()
phone_backend.notify_by_call(user, alert_group, notification_policy)
assert (
UserNotificationPolicyLogRecord.objects.filter(
author=user,
type=UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_FAILED,
notification_policy=notification_policy,
alert_group=alert_group,
notification_error_code=log_err_code,
notification_step=notification_policy.step,
notification_channel=notification_policy.notify_by,
).count()
== 1
)
@pytest.mark.django_db
@mock.patch("apps.phone_notifications.phone_backend.PhoneBackend._notify_by_cloud_call")
@pytest.mark.parametrize(
"exc,log_err_code",
[
(FailedToMakeCall, UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_NOT_ABLE_TO_CALL),
(NumberNotVerified, UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_PHONE_NUMBER_IS_NOT_VERIFIED),
(CallsLimitExceeded, UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_PHONE_CALLS_LIMIT_EXCEEDED),
],
)
@override_settings(GRAFANA_CLOUD_NOTIFICATIONS_ENABLED=True)
def test_notify_by_cloud_call_handles_exceptions_from_cloud(
mock_notify_by_cloud_call,
setup,
exc,
log_err_code,
):
"""
test if UserNotificationPolicyLogRecord is created when exception is raised from _notify_by_cloud_call
"""
user, alert_group, notification_policy = setup
mock_notify_by_cloud_call.side_effect = exc
phone_backend = PhoneBackend()
phone_backend.notify_by_call(user, alert_group, notification_policy)
assert (
UserNotificationPolicyLogRecord.objects.filter(
author=user,
type=UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_FAILED,
notification_policy=notification_policy,
alert_group=alert_group,
notification_error_code=log_err_code,
notification_step=notification_policy.step,
notification_channel=notification_policy.notify_by,
).count()
== 1
)