Files
notifications-api/tests/app/notifications/test_notifications_ses_callback.py
Alexey Bezhan 2932b44eb8 Add retries for SES callbacks for recent notifications
We've seen errors caused by what we suspect is a race condition when
SES callback processing tries to look up the notification before the
sender worker has saved notification reference from the SES POST
response to the database.

This adds a retry for SES callback task if the notification was not
found and the message is less than 10 minutes old and removes the
error log message for notifications older than 3 days (since they
might no longer exist in the notifications table and would've been
marked as failure by then either way).

In order to be able to call retry and silence the error log based on
notification time this change inlines `process_ses_response` and
`update_notification_by_reference` functions into the celery task.
It also removes a lot of defensive error-handling that doesn't appear
to have been triggered in the last few months (for things like missing
keys in SES callback data).
2019-02-25 10:36:37 +00:00

64 lines
2.5 KiB
Python

from datetime import datetime
import pytest
from flask import json
from sqlalchemy.exc import SQLAlchemyError
from app.dao.notifications_dao import get_notification_by_id
from app.models import Complaint
from app.notifications.notifications_ses_callback import handle_complaint
from tests.app.conftest import sample_notification as create_sample_notification
from tests.app.db import (
create_notification, ses_complaint_callback_malformed_message_id,
ses_complaint_callback_with_missing_complaint_type,
ses_complaint_callback
)
def test_ses_callback_should_not_set_status_once_status_is_delivered(client,
notify_db,
notify_db_session,
sample_email_template,
mocker):
notification = create_sample_notification(
notify_db,
notify_db_session,
template=sample_email_template,
reference='ref',
status='delivered',
sent_at=datetime.utcnow()
)
assert get_notification_by_id(notification.id).status == 'delivered'
def test_process_ses_results_in_complaint(sample_email_template):
notification = create_notification(template=sample_email_template, reference='ref1')
handle_complaint(json.loads(ses_complaint_callback()['Message']))
complaints = Complaint.query.all()
assert len(complaints) == 1
assert complaints[0].notification_id == notification.id
def test_handle_complaint_does_not_raise_exception_if_reference_is_missing(notify_api):
response = json.loads(ses_complaint_callback_malformed_message_id()['Message'])
handle_complaint(response)
assert len(Complaint.query.all()) == 0
def test_handle_complaint_does_raise_exception_if_notification_not_found(notify_api):
response = json.loads(ses_complaint_callback()['Message'])
with pytest.raises(expected_exception=SQLAlchemyError):
handle_complaint(response)
def test_process_ses_results_in_complaint_save_complaint_with_null_complaint_type(notify_api, sample_email_template):
notification = create_notification(template=sample_email_template, reference='ref1')
msg = json.loads(ses_complaint_callback_with_missing_complaint_type()['Message'])
handle_complaint(msg)
complaints = Complaint.query.all()
assert len(complaints) == 1
assert complaints[0].notification_id == notification.id
assert not complaints[0].complaint_type