# What this PR does
Email tasks are failing and retrying when they use the fallback default
notification policy as it does not get saved in the DB. This PR uses the
same way as UserNotificationPolicyLogRecord to set that field to null to
avoid `ValueError("save() prohibited to prevent data loss due to unsaved
related object 'notification_policy'.").` when that is the case.
## 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
- [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.
246 lines
7.9 KiB
Python
246 lines
7.9 KiB
Python
import socket
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
from django.core import mail
|
|
from django.core.mail.backends.locmem import EmailBackend
|
|
|
|
from apps.base.models import UserNotificationPolicy, UserNotificationPolicyLogRecord
|
|
from apps.email.alert_rendering import build_subject_and_message
|
|
from apps.email.models import EmailMessage
|
|
from apps.email.tasks import get_from_email, notify_user_async
|
|
from apps.user_management.subscription_strategy.free_public_beta_subscription_strategy import (
|
|
FreePublicBetaSubscriptionStrategy,
|
|
)
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_notify_user(
|
|
settings,
|
|
make_organization,
|
|
make_user_for_organization,
|
|
make_token_for_organization,
|
|
make_alert_receive_channel,
|
|
make_alert_group,
|
|
make_alert,
|
|
make_user_notification_policy,
|
|
):
|
|
settings.EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
|
|
settings.EMAIL_HOST = "test"
|
|
|
|
organization = make_organization()
|
|
user = make_user_for_organization(organization)
|
|
|
|
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)
|
|
|
|
notification_policy = make_user_notification_policy(
|
|
user,
|
|
UserNotificationPolicy.Step.NOTIFY,
|
|
notify_by=8,
|
|
important=False,
|
|
)
|
|
|
|
notify_user_async(user.pk, alert_group.pk, notification_policy.pk)
|
|
assert len(mail.outbox) == 1
|
|
|
|
log_record = notification_policy.personal_log_records.last()
|
|
assert log_record.type == UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_SUCCESS
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_notify_empty_email_host(
|
|
settings,
|
|
make_organization,
|
|
make_user_for_organization,
|
|
make_token_for_organization,
|
|
make_alert_receive_channel,
|
|
make_alert_group,
|
|
make_alert,
|
|
make_user_notification_policy,
|
|
):
|
|
settings.EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
|
|
settings.EMAIL_HOST = None
|
|
|
|
organization = make_organization()
|
|
user = make_user_for_organization(organization)
|
|
|
|
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)
|
|
|
|
notification_policy = make_user_notification_policy(
|
|
user,
|
|
UserNotificationPolicy.Step.NOTIFY,
|
|
notify_by=8,
|
|
important=False,
|
|
)
|
|
|
|
notify_user_async(user.pk, alert_group.pk, notification_policy.pk)
|
|
assert len(mail.outbox) == 0
|
|
|
|
log_record = notification_policy.personal_log_records.last()
|
|
assert log_record.type == UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_FAILED
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_notify_user_bad_smtp_host(
|
|
settings,
|
|
make_organization,
|
|
make_user_for_organization,
|
|
make_token_for_organization,
|
|
make_alert_receive_channel,
|
|
make_alert_group,
|
|
make_alert,
|
|
make_user_notification_policy,
|
|
):
|
|
settings.EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
|
|
settings.EMAIL_HOST = "test"
|
|
|
|
organization = make_organization()
|
|
user = make_user_for_organization(organization)
|
|
|
|
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)
|
|
|
|
notification_policy = make_user_notification_policy(
|
|
user,
|
|
UserNotificationPolicy.Step.NOTIFY,
|
|
notify_by=8,
|
|
important=False,
|
|
)
|
|
|
|
with patch.object(EmailBackend, "send_messages", side_effect=socket.gaierror):
|
|
notify_user_async(user.pk, alert_group.pk, notification_policy.pk)
|
|
|
|
assert len(mail.outbox) == 0
|
|
|
|
log_record = notification_policy.personal_log_records.last()
|
|
assert log_record.type == UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_FAILED
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_notify_user_no_emails_left(
|
|
settings,
|
|
make_organization,
|
|
make_user_for_organization,
|
|
make_token_for_organization,
|
|
make_alert_receive_channel,
|
|
make_alert_group,
|
|
make_alert,
|
|
make_user_notification_policy,
|
|
):
|
|
settings.EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
|
|
settings.EMAIL_HOST = "test"
|
|
|
|
organization = make_organization()
|
|
user = make_user_for_organization(organization)
|
|
|
|
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)
|
|
|
|
notification_policy = make_user_notification_policy(
|
|
user,
|
|
UserNotificationPolicy.Step.NOTIFY,
|
|
notify_by=8,
|
|
important=False,
|
|
)
|
|
|
|
with patch.object(FreePublicBetaSubscriptionStrategy, "emails_left", return_value=0):
|
|
notify_user_async(user.pk, alert_group.pk, notification_policy.pk)
|
|
|
|
assert len(mail.outbox) == 0
|
|
log_record = notification_policy.personal_log_records.last()
|
|
assert log_record.type == UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_FAILED
|
|
assert log_record.notification_error_code == UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_MAIL_LIMIT_EXCEEDED
|
|
|
|
|
|
@pytest.mark.django_db
|
|
@pytest.mark.parametrize(
|
|
"license_name,email_host_user,email_from_address,expected",
|
|
[
|
|
("Cloud", "user", "from_address", "from_address"),
|
|
("OpenSource", "user", "from_address", "from_address"),
|
|
("Cloud", "user", None, "oncall@slug.grafana.net"),
|
|
("Cloud", None, None, "oncall@slug.grafana.net"),
|
|
("OpenSource", "user", None, "user"),
|
|
("OpenSource", None, None, None),
|
|
],
|
|
)
|
|
def test_get_from_email(
|
|
settings,
|
|
make_organization,
|
|
make_user_for_organization,
|
|
license_name,
|
|
email_host_user,
|
|
email_from_address,
|
|
expected,
|
|
):
|
|
settings.EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
|
|
settings.EMAIL_HOST = "test"
|
|
settings.EMAIL_HOST_PASSWORD = "password"
|
|
|
|
settings.LICENSE = license_name
|
|
settings.EMAIL_HOST_USER = email_host_user
|
|
settings.EMAIL_FROM_ADDRESS = email_from_address
|
|
|
|
organization = make_organization(stack_slug="slug")
|
|
user = make_user_for_organization(organization)
|
|
|
|
assert get_from_email(user) == expected
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_subject_newlines_removed(
|
|
make_organization,
|
|
make_alert_receive_channel,
|
|
make_alert_group,
|
|
make_alert,
|
|
):
|
|
organization = make_organization()
|
|
alert_receive_channel = make_alert_receive_channel(
|
|
organization, messaging_backends_templates={"EMAIL": {"title": "test\nnewlines"}}
|
|
)
|
|
alert_group = make_alert_group(alert_receive_channel)
|
|
make_alert(alert_group, raw_request_data={})
|
|
|
|
subject, _ = build_subject_and_message(alert_group, 1)
|
|
assert subject == "testnewlines"
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_notify_user_fallback_default_policy(
|
|
settings,
|
|
make_organization,
|
|
make_user_for_organization,
|
|
make_token_for_organization,
|
|
make_alert_receive_channel,
|
|
make_alert_group,
|
|
make_alert,
|
|
make_user_notification_policy,
|
|
):
|
|
settings.EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
|
|
settings.EMAIL_HOST = "test"
|
|
|
|
organization = make_organization()
|
|
user = make_user_for_organization(organization)
|
|
|
|
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)
|
|
|
|
notify_user_async(user.pk, alert_group.pk, None)
|
|
assert len(mail.outbox) == 1
|
|
|
|
log_record = UserNotificationPolicyLogRecord.objects.filter(author=user, alert_group=alert_group).first()
|
|
assert log_record.type == UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_SUCCESS
|
|
|
|
EmailMessage.objects.get(receiver=user, represents_alert_group=alert_group, notification_policy=None)
|