diff --git a/app/dao/jobs_dao.py b/app/dao/jobs_dao.py index 73a9a23e3..2ff2908c2 100644 --- a/app/dao/jobs_dao.py +++ b/app/dao/jobs_dao.py @@ -170,56 +170,3 @@ def dao_get_letter_job_ids_by_status(status): ).all() return [str(job.id) for job in jobs] - - -@statsd(namespace="dao") -def dao_get_job_statistics_for_job(service_id, job_id): - query = Job.query.join( - JobStatistics, Job.id == JobStatistics.job_id - ).filter( - Job.id == job_id, - Job.service_id == service_id - ).add_columns( - JobStatistics.job_id, - Job.original_file_name, - Job.created_at, - Job.scheduled_for, - Job.template_id, - Job.template_version, - Job.job_status, - Job.service_id, - Job.notification_count, - JobStatistics.sent, - JobStatistics.delivered, - JobStatistics.failed - ) - return query.one() - - -@statsd(namespace="dao") -def dao_get_job_stats_for_service(service_id, page=1, page_size=50, limit_days=None, statuses=None): - query = Job.query.join( - JobStatistics, Job.id == JobStatistics.job_id - ).filter( - Job.service_id == service_id - ).add_columns( - JobStatistics.job_id, - Job.original_file_name, - Job.created_at, - Job.scheduled_for, - Job.template_id, - Job.template_version, - Job.job_status, - Job.service_id, - Job.notification_count, - JobStatistics.sent, - JobStatistics.delivered, - JobStatistics.failed - ) - if limit_days: - query = query.filter(Job.created_at >= days_ago(limit_days)) - if statuses is not None and statuses != ['']: - query = query.filter(Job.job_status.in_(statuses)) - - query = query.order_by(Job.created_at.desc()) - return query.paginate(page=page, per_page=page_size) diff --git a/app/dao/statistics_dao.py b/app/dao/statistics_dao.py deleted file mode 100644 index 94f2edc85..000000000 --- a/app/dao/statistics_dao.py +++ /dev/null @@ -1,158 +0,0 @@ -from datetime import datetime, timedelta -from itertools import groupby - -from flask import current_app -from notifications_utils.statsd_decorators import statsd -from sqlalchemy import func -from sqlalchemy.exc import IntegrityError, SQLAlchemyError - -from app import db -from app.dao.dao_utils import transactional -from app.models import ( - JobStatistics, - Notification, - EMAIL_TYPE, - SMS_TYPE, - LETTER_TYPE, - NOTIFICATION_STATUS_TYPES_FAILED, - NOTIFICATION_STATUS_SUCCESS, - NOTIFICATION_DELIVERED, - NOTIFICATION_SENT) - - -@transactional -def timeout_job_counts(notifications_type, timeout_start): - total_updated = 0 - - sent = columns(notifications_type, 'sent') - delivered = columns(notifications_type, 'delivered') - failed = columns(notifications_type, 'failed') - - results = db.session.query( - JobStatistics.job_id.label('job_id'), - func.count(Notification.status).label('count'), - Notification.status - ).filter( - Notification.notification_type == notifications_type, - JobStatistics.job_id == Notification.job_id, - JobStatistics.created_at < timeout_start, - sent != failed + delivered - ).group_by(Notification.status, JobStatistics.job_id).order_by(JobStatistics.job_id).all() - - sort = sorted(results, key=lambda result: result.job_id) - groups = [] - for k, g in groupby(sort, key=lambda result: result.job_id): - groups.append(list(g)) - - for job in groups: - sent_count = 0 - delivered_count = 0 - failed_count = 0 - for notification_status in job: - if notification_status.status in NOTIFICATION_STATUS_SUCCESS: - delivered_count += notification_status.count - else: - failed_count += notification_status.count - sent_count += notification_status.count - - total_updated += JobStatistics.query.filter_by( - job_id=notification_status.job_id - ).update({ - sent: sent_count, - failed: failed_count, - delivered: delivered_count, - 'sent': sent_count, - 'delivered': delivered_count, - 'failed': failed_count - }, synchronize_session=False) - return total_updated - - -@statsd(namespace="dao") -def dao_timeout_job_statistics(timeout_period): - timeout_start = datetime.utcnow() - timedelta(seconds=timeout_period) - sms_count = timeout_job_counts(SMS_TYPE, timeout_start) - email_count = timeout_job_counts(EMAIL_TYPE, timeout_start) - return sms_count + email_count - - -@statsd(namespace="dao") -def create_or_update_job_sending_statistics(notification): - if __update_job_stats_sent_count(notification) == 0: - try: - __insert_job_stats(notification) - except IntegrityError as e: - current_app.logger.exception(e) - if __update_job_stats_sent_count(notification) == 0: - raise SQLAlchemyError("Failed to create job statistics for {}".format(notification.job_id)) - - -@transactional -def __update_job_stats_sent_count(notification): - column = columns(notification.notification_type, 'sent') - new_column = 'sent' - - return db.session.query(JobStatistics).filter_by( - job_id=notification.job_id, - ).update({ - column: column + 1, - new_column: column + 1 - }) - - -@transactional -def __insert_job_stats(notification): - stats = JobStatistics( - job_id=notification.job_id, - emails_sent=1 if notification.notification_type == EMAIL_TYPE else 0, - sms_sent=1 if notification.notification_type == SMS_TYPE else 0, - letters_sent=1 if notification.notification_type == LETTER_TYPE else 0, - updated_at=datetime.utcnow(), - sent=1 - ) - db.session.add(stats) - - -def columns(notification_type, status): - keys = { - EMAIL_TYPE: { - 'failed': JobStatistics.emails_failed, - 'delivered': JobStatistics.emails_delivered, - 'sent': JobStatistics.emails_sent - }, - SMS_TYPE: { - 'failed': JobStatistics.sms_failed, - 'delivered': JobStatistics.sms_delivered, - 'sent': JobStatistics.sms_sent - }, - LETTER_TYPE: { - 'failed': JobStatistics.letters_failed, - 'sent': JobStatistics.letters_sent - } - } - return keys.get(notification_type).get(status) - - -@transactional -def update_job_stats_outcome_count(notification): - if notification.status in NOTIFICATION_STATUS_TYPES_FAILED: - column = columns(notification.notification_type, 'failed') - new_column = 'failed' - - elif notification.status in [NOTIFICATION_DELIVERED, - NOTIFICATION_SENT] and notification.notification_type != LETTER_TYPE: - column = columns(notification.notification_type, 'delivered') - new_column = 'delivered' - - else: - column = None - - if column: - return db.session.query(JobStatistics).filter_by( - job_id=notification.job_id, - ).update({ - column: column + 1, - new_column: column + 1 - }) - else: - return 0 diff --git a/tests/app/dao/test_jobs_dao.py b/tests/app/dao/test_jobs_dao.py index 285983174..8680068ec 100644 --- a/tests/app/dao/test_jobs_dao.py +++ b/tests/app/dao/test_jobs_dao.py @@ -15,10 +15,7 @@ from app.dao.jobs_dao import ( dao_get_notification_outcomes_for_job, dao_update_job_status, dao_get_jobs_older_than_limited_by, - dao_get_job_statistics_for_job, - dao_get_job_stats_for_service, dao_get_letter_job_ids_by_status) -from app.dao.statistics_dao import create_or_update_job_sending_statistics, update_job_stats_outcome_count from app.models import ( Job, JobStatistics, EMAIL_TYPE, SMS_TYPE, LETTER_TYPE, @@ -397,101 +394,6 @@ def test_should_get_jobs_seven_days_old_filters_type(notify_db, notify_db_sessio assert job_to_remain.id not in [job.id for job in jobs] -def test_dao_get_job_statistics_for_job_calculates_stats(notify_db, notify_db_session, sample_job): - notification = create_notification(notify_db=notify_db, notify_db_session=notify_db_session, job=sample_job) - notification_delivered = create_notification(notify_db=notify_db, notify_db_session=notify_db_session, - job=sample_job, status='delivered') - notification_failed = create_notification(notify_db=notify_db, notify_db_session=notify_db_session, job=sample_job, - status='permanent-failure') - - create_or_update_job_sending_statistics(notification) - create_or_update_job_sending_statistics(notification_delivered) - create_or_update_job_sending_statistics(notification_failed) - update_job_stats_outcome_count(notification_delivered) - update_job_stats_outcome_count(notification_failed) - - result = dao_get_job_statistics_for_job(sample_job.service_id, sample_job.id) - - assert_job_stat(job=sample_job, result=result, sent=3, delivered=1, failed=1) - - -def test_dao_get_job_statistics_for_job_separates_jobs(notify_db, notify_db_session, sample_service): - job_1, job_2 = stats_set_up(notify_db, notify_db_session, sample_service) - - result = dao_get_job_statistics_for_job(sample_service.id, job_1.id) - result_2 = dao_get_job_statistics_for_job(sample_service.id, job_2.id) - - assert_job_stat(job=job_1, result=result, sent=2, delivered=1, failed=0) - assert_job_stat(job=job_2, result=result_2, sent=1, delivered=0, failed=1) - - -def test_dao_get_job_stats_for_service(notify_db, notify_db_session, sample_service): - job_1, job_2 = stats_set_up(notify_db, notify_db_session, sample_service) - - results = dao_get_job_stats_for_service(sample_service.id).items - assert len(results) == 2 - assert_job_stat(job_2, results[0], 1, 0, 1) - assert_job_stat(job_1, results[1], 2, 1, 0) - - -def test_dao_get_job_stats_for_service_only_returns_stats_for_service(notify_db, notify_db_session, sample_service): - job_1, job_2 = stats_set_up(notify_db, notify_db_session, sample_service) - another_service = create_service(notify_db=notify_db, notify_db_session=notify_db_session, - service_name='Another Service') - job_3, job_4 = stats_set_up(notify_db, notify_db_session, service=another_service) - - results = dao_get_job_stats_for_service(sample_service.id).items - assert len(results) == 2 - assert_job_stat(job_2, results[0], 1, 0, 1) - assert_job_stat(job_1, results[1], 2, 1, 0) - - results = dao_get_job_stats_for_service(another_service.id).items - assert len(results) == 2 - assert_job_stat(job_4, results[0], 1, 0, 1) - assert_job_stat(job_3, results[1], 2, 1, 0) - - -def test_dao_get_job_stats_for_service_only_returns_jobs_created_within_limited_days( - notify_db, notify_db_session, sample_service): - job_1, job_2 = stats_set_up(notify_db, notify_db_session, sample_service) - - results = dao_get_job_stats_for_service(sample_service.id, limit_days=1) - assert results.total == 1 - assert_job_stat(job_2, results.items[0], 1, 0, 1) - - -def test_dao_get_job_stats_for_service_only_returns_jobs_created_within_limited_days_inclusive( - notify_db, notify_db_session, sample_service): - job_1, job_2 = stats_set_up(notify_db, notify_db_session, sample_service) - - results = dao_get_job_stats_for_service(sample_service.id, limit_days=2).items - assert len(results) == 2 - assert_job_stat(job_2, results[0], 1, 0, 1) - assert_job_stat(job_1, results[1], 2, 1, 0) - - -def test_dao_get_job_stats_paginates_results( - notify_db, notify_db_session, sample_service): - job_1, job_2 = stats_set_up(notify_db, notify_db_session, sample_service) - - results = dao_get_job_stats_for_service(sample_service.id, page=1, page_size=1).items - assert len(results) == 1 - assert_job_stat(job_2, results[0], 1, 0, 1) - results_2 = dao_get_job_stats_for_service(sample_service.id, page=2, page_size=1).items - assert len(results_2) == 1 - assert_job_stat(job_1, results_2[0], 2, 1, 0) - - -def test_dao_get_job_returns_jobs_for_status( - notify_db, notify_db_session, sample_service): - stats_set_up(notify_db, notify_db_session, sample_service) - - results = dao_get_job_stats_for_service(sample_service.id, statuses=['pending']) - assert results.total == 1 - results_2 = dao_get_job_stats_for_service(sample_service.id, statuses=['pending', 'finished']) - assert results_2.total == 2 - - def assert_job_stat(job, result, sent, delivered, failed): assert result.job_id == job.id assert result.original_file_name == job.original_file_name @@ -507,24 +409,6 @@ def assert_job_stat(job, result, sent, delivered, failed): assert result.failed == failed -def stats_set_up(notify_db, notify_db_session, service): - job_1 = create_job(notify_db=notify_db, notify_db_session=notify_db_session, - service=service, created_at=datetime.utcnow() - timedelta(days=2)) - job_2 = create_job(notify_db=notify_db, notify_db_session=notify_db_session, - service=service, original_file_name='Another job', job_status='finished') - notification = create_notification(notify_db=notify_db, notify_db_session=notify_db_session, job=job_1) - notification_delivered = create_notification(notify_db=notify_db, notify_db_session=notify_db_session, - job=job_1, status='delivered') - notification_failed = create_notification(notify_db=notify_db, notify_db_session=notify_db_session, job=job_2, - status='permanent-failure') - create_or_update_job_sending_statistics(notification) - create_or_update_job_sending_statistics(notification_delivered) - create_or_update_job_sending_statistics(notification_failed) - update_job_stats_outcome_count(notification_delivered) - update_job_stats_outcome_count(notification_failed) - return job_1, job_2 - - def test_dao_get_letter_job_ids_by_status(sample_service): another_service = create_db_service(service_name="another service") diff --git a/tests/app/dao/test_statistics_dao.py b/tests/app/dao/test_statistics_dao.py deleted file mode 100644 index 530478f19..000000000 --- a/tests/app/dao/test_statistics_dao.py +++ /dev/null @@ -1,937 +0,0 @@ -from datetime import datetime, timedelta -from unittest.mock import call - -import pytest -from sqlalchemy.exc import IntegrityError, SQLAlchemyError - -from app.dao.statistics_dao import ( - create_or_update_job_sending_statistics, - update_job_stats_outcome_count, - dao_timeout_job_statistics) -from app.models import ( - JobStatistics, - SMS_TYPE, - EMAIL_TYPE, - LETTER_TYPE, - NOTIFICATION_STATUS_TYPES, - NOTIFICATION_CREATED, - NOTIFICATION_DELIVERED, - NOTIFICATION_FAILED, - NOTIFICATION_PENDING, - NOTIFICATION_PERMANENT_FAILURE, - NOTIFICATION_SENDING, - NOTIFICATION_SENT, - NOTIFICATION_STATUS_SUCCESS, - NOTIFICATION_TECHNICAL_FAILURE, - NOTIFICATION_TEMPORARY_FAILURE, -) -from tests.app.conftest import sample_notification, sample_email_template, sample_template, sample_job, sample_service - - -@pytest.mark.parametrize('notification_type, sms_count, email_count, letter_count', [ - (SMS_TYPE, 1, 0, 0), - (EMAIL_TYPE, 0, 1, 0), - (LETTER_TYPE, 0, 0, 1) -]) -def test_should_create_a_stats_entry_for_a_job( - notify_db, - notify_db_session, - sample_job, - sample_letter_template, - notification_type, - sms_count, - email_count, - letter_count -): - template = None - - if notification_type == SMS_TYPE: - template = sample_template(notify_db, notify_db_session, service=sample_job.service) - - if notification_type == EMAIL_TYPE: - template = sample_email_template(notify_db, notify_db_session, service=sample_job.service) - - if notification_type == LETTER_TYPE: - template = sample_letter_template - - notification = sample_notification( - notify_db, notify_db_session, service=sample_job.service, template=template, job=sample_job - ) - - create_or_update_job_sending_statistics(notification) - - stats = JobStatistics.query.all() - - assert len(stats) == 1 - - stat = stats[0] - assert stat.job_id == sample_job.id - - assert stat.emails_sent == email_count - assert stat.sms_sent == sms_count - assert stat.letters_sent == letter_count - - assert stat.emails_delivered == 0 - assert stat.emails_failed == 0 - assert stat.sms_delivered == 0 - assert stat.sms_failed == 0 - assert stat.letters_failed == 0 - - -@pytest.mark.parametrize('notification_type, sms_count, email_count, letter_count', [ - (SMS_TYPE, 2, 0, 0), - (EMAIL_TYPE, 0, 2, 0), - (LETTER_TYPE, 0, 0, 2) -]) -def test_should_update_a_stats_entry_for_a_job( - notify_db, - notify_db_session, - sample_job, - sample_letter_template, - notification_type, - sms_count, - email_count, - letter_count -): - template = None - - if notification_type == SMS_TYPE: - template = sample_template(notify_db, notify_db_session, service=sample_job.service) - - if notification_type == EMAIL_TYPE: - template = sample_email_template(notify_db, notify_db_session, service=sample_job.service) - - if notification_type == LETTER_TYPE: - template = sample_letter_template - - notification = sample_notification( - notify_db, notify_db_session, service=sample_job.service, template=template, job=sample_job - ) - - create_or_update_job_sending_statistics(notification) - - stats = JobStatistics.query.all() - - assert len(stats) == 1 - - create_or_update_job_sending_statistics(notification) - - stat = stats[0] - assert stat.job_id == sample_job.id - - assert stat.emails_sent == email_count - assert stat.sms_sent == sms_count - assert stat.letters_sent == letter_count - - assert stat.emails_delivered == 0 - assert stat.emails_failed == 0 - assert stat.sms_delivered == 0 - assert stat.sms_failed == 0 - assert stat.letters_failed == 0 - - -def test_should_handle_error_conditions( - notify_db, - notify_db_session, - sample_job, - mocker): - create_mock = mocker.patch("app.dao.statistics_dao.__insert_job_stats", side_effect=IntegrityError("1", "2", "3")) - update_mock = mocker.patch("app.dao.statistics_dao.__update_job_stats_sent_count", return_value=0) - - notification = sample_notification(notify_db, notify_db_session, job=sample_job) - - with pytest.raises(SQLAlchemyError) as e: - create_or_update_job_sending_statistics(notification) - assert 'Failed to create job statistics for {}'.format(sample_job.id) in str(e.value) - - update_mock.assert_has_calls([call(notification), call(notification)]) - create_mock.assert_called_once_with(notification) - - -@pytest.mark.parametrize('notification_type, sms_count, email_count, letter_count', [ - (SMS_TYPE, 1, 0, 0), - (EMAIL_TYPE, 0, 1, 0), - (LETTER_TYPE, 0, 0, 1) -]) -def test_should_update_a_stats_entry_with_its_success_outcome_for_a_job( - notify_db, - notify_db_session, - sample_job, - sample_letter_template, - notification_type, - sms_count, - email_count, - letter_count -): - template = None - - if notification_type == SMS_TYPE: - template = sample_template(notify_db, notify_db_session, service=sample_job.service) - - if notification_type == EMAIL_TYPE: - template = sample_email_template(notify_db, notify_db_session, service=sample_job.service) - - if notification_type == LETTER_TYPE: - template = sample_letter_template - - notification = sample_notification( - notify_db, - notify_db_session, - service=sample_job.service, - template=template, - job=sample_job, - status=NOTIFICATION_DELIVERED - ) - - create_or_update_job_sending_statistics(notification) - - stats = JobStatistics.query.all() - - assert len(stats) == 1 - - update_job_stats_outcome_count(notification) - - stat = stats[0] - assert stat.job_id == sample_job.id - - assert stat.emails_sent == email_count - assert stat.sms_sent == sms_count - assert stat.letters_sent == letter_count - - assert stat.emails_delivered == email_count - assert stat.sms_delivered == sms_count - - assert stat.emails_failed == 0 - assert stat.sms_failed == 0 - assert stat.letters_failed == 0 - - assert stat.sent == email_count + sms_count + letter_count - assert stat.delivered == email_count + sms_count - assert stat.failed == 0 - - -@pytest.mark.parametrize('notification_type, sms_count, email_count, letter_count, status', [ - (SMS_TYPE, 1, 0, 0, NOTIFICATION_TECHNICAL_FAILURE), - (SMS_TYPE, 1, 0, 0, NOTIFICATION_TEMPORARY_FAILURE), - (SMS_TYPE, 1, 0, 0, NOTIFICATION_PERMANENT_FAILURE), - (EMAIL_TYPE, 0, 1, 0, NOTIFICATION_TECHNICAL_FAILURE), - (EMAIL_TYPE, 0, 1, 0, NOTIFICATION_PERMANENT_FAILURE), - (EMAIL_TYPE, 0, 1, 0, NOTIFICATION_TEMPORARY_FAILURE), - (LETTER_TYPE, 0, 0, 1, NOTIFICATION_PERMANENT_FAILURE), - (LETTER_TYPE, 0, 0, 1, NOTIFICATION_TEMPORARY_FAILURE), - (LETTER_TYPE, 0, 0, 1, NOTIFICATION_TECHNICAL_FAILURE) -]) -def test_should_update_a_stats_entry_with_its_error_outcomes_for_a_job( - notify_db, - notify_db_session, - sample_job, - sample_letter_template, - notification_type, - sms_count, - email_count, - letter_count, - status -): - template = None - - if notification_type == SMS_TYPE: - template = sample_template(notify_db, notify_db_session, service=sample_job.service) - - if notification_type == EMAIL_TYPE: - template = sample_email_template(notify_db, notify_db_session, service=sample_job.service) - - if notification_type == LETTER_TYPE: - template = sample_letter_template - - notification = sample_notification( - notify_db, - notify_db_session, - service=sample_job.service, - template=template, - job=sample_job, - status=status - ) - - create_or_update_job_sending_statistics(notification) - - stats = JobStatistics.query.all() - - assert len(stats) == 1 - - update_job_stats_outcome_count(notification) - - stat = stats[0] - assert stat.job_id == sample_job.id - - assert stat.emails_sent == email_count - assert stat.sms_sent == sms_count - assert stat.letters_sent == letter_count - - assert stat.emails_failed == email_count - assert stat.letters_failed == letter_count - assert stat.sms_failed == sms_count - - assert stat.emails_delivered == 0 - assert stat.sms_delivered == 0 - - assert stat.sent == email_count + sms_count + letter_count - assert stat.delivered == 0 - assert stat.failed == email_count + sms_count + letter_count - - -@pytest.mark.parametrize('notification_type, sms_count, email_count, letter_count, status', [ - (SMS_TYPE, 1, 0, 0, NOTIFICATION_DELIVERED), - (SMS_TYPE, 1, 0, 0, NOTIFICATION_SENT), - (EMAIL_TYPE, 0, 1, 0, NOTIFICATION_DELIVERED), - (EMAIL_TYPE, 0, 1, 0, NOTIFICATION_SENT), - (LETTER_TYPE, 0, 0, 1, NOTIFICATION_SENT), - (LETTER_TYPE, 0, 0, 1, NOTIFICATION_DELIVERED), -]) -def test_should_update_a_stats_entry_with_its_success_outcomes_for_a_job( - notify_db, - notify_db_session, - sample_job, - sample_letter_template, - notification_type, - sms_count, - email_count, - letter_count, - status -): - template = None - - if notification_type == SMS_TYPE: - template = sample_template(notify_db, notify_db_session, service=sample_job.service) - - if notification_type == EMAIL_TYPE: - template = sample_email_template(notify_db, notify_db_session, service=sample_job.service) - - if notification_type == LETTER_TYPE: - template = sample_letter_template - - notification = sample_notification( - notify_db, - notify_db_session, - service=sample_job.service, - template=template, - job=sample_job, - status=status - ) - - create_or_update_job_sending_statistics(notification) - - stats = JobStatistics.query.all() - - assert len(stats) == 1 - - update_job_stats_outcome_count(notification) - - stat = stats[0] - assert stat.job_id == sample_job.id - - assert stat.emails_sent == email_count - assert stat.sms_sent == sms_count - assert stat.letters_sent == letter_count - - assert stat.emails_failed == 0 - assert stat.letters_failed == 0 - assert stat.sms_failed == 0 - - assert stat.emails_delivered == email_count - assert stat.sms_delivered == sms_count - - assert stat.sent == email_count + sms_count + letter_count - assert stat.delivered == 0 if notification_type == LETTER_TYPE else 1 - assert stat.failed == 0 - - -@pytest.mark.parametrize('notification_type, sms_count, email_count, letter_count, status', [ - (SMS_TYPE, 1, 0, 0, NOTIFICATION_PENDING), - (SMS_TYPE, 1, 0, 0, NOTIFICATION_CREATED), - (SMS_TYPE, 1, 0, 0, NOTIFICATION_FAILED), - (SMS_TYPE, 1, 0, 0, NOTIFICATION_SENDING), - (EMAIL_TYPE, 0, 1, 0, NOTIFICATION_PENDING), - (EMAIL_TYPE, 0, 1, 0, NOTIFICATION_CREATED), - (EMAIL_TYPE, 0, 1, 0, NOTIFICATION_FAILED), - (EMAIL_TYPE, 0, 1, 0, NOTIFICATION_SENDING), - (LETTER_TYPE, 0, 0, 1, NOTIFICATION_PENDING), - (LETTER_TYPE, 0, 0, 1, NOTIFICATION_CREATED), - (LETTER_TYPE, 0, 0, 1, NOTIFICATION_FAILED), - (LETTER_TYPE, 0, 0, 1, NOTIFICATION_SENDING) -]) -def test_should_not_update_job_stats_if_irrelevant_status( - notify_db, - notify_db_session, - sample_job, - sample_letter_template, - notification_type, - sms_count, - email_count, - letter_count, - status -): - template = None - - if notification_type == SMS_TYPE: - template = sample_template(notify_db, notify_db_session, service=sample_job.service) - - if notification_type == EMAIL_TYPE: - template = sample_email_template(notify_db, notify_db_session, service=sample_job.service) - - if notification_type == LETTER_TYPE: - template = sample_letter_template - - notification = sample_notification( - notify_db, - notify_db_session, - service=sample_job.service, - template=template, - job=sample_job, - status=status - ) - - create_or_update_job_sending_statistics(notification) - - stats = JobStatistics.query.all() - - assert len(stats) == 1 - - update_job_stats_outcome_count(notification) - - stat = stats[0] - assert stat.job_id == sample_job.id - - assert stat.emails_sent == email_count - assert stat.sms_sent == sms_count - assert stat.letters_sent == letter_count - - assert stat.emails_failed == 0 - assert stat.letters_failed == 0 - assert stat.sms_failed == 0 - - assert stat.emails_delivered == 0 - assert stat.sms_delivered == 0 - - assert stat.sent == email_count + sms_count + letter_count - assert stat.delivered == 0 - assert stat.failed == 0 - - -@pytest.mark.parametrize('notification_type, sms_count, email_count, letter_count', [ - (SMS_TYPE, 2, 1, 1), - (EMAIL_TYPE, 1, 2, 1), - (LETTER_TYPE, 1, 1, 2) -]) -def test_inserting_one_type_of_notification_maintains_other_counts( - notify_db, - notify_db_session, - sample_job, - sample_letter_template, - notification_type, - sms_count, - email_count, - letter_count -): - sms_template = sample_template(notify_db, notify_db_session, service=sample_job.service) - email_template = sample_email_template(notify_db, notify_db_session, service=sample_job.service) - letter_template = sample_letter_template - - template = None - - if notification_type == SMS_TYPE: - template = sms_template - - if notification_type == EMAIL_TYPE: - template = email_template - - if notification_type == LETTER_TYPE: - template = letter_template - - notification = sample_notification( - notify_db, - notify_db_session, - service=sample_job.service, - template=template, - job=sample_job, - status=NOTIFICATION_CREATED - ) - - letter = sample_notification( - notify_db, - notify_db_session, - service=sample_job.service, - template=letter_template, - job=sample_job, - status=NOTIFICATION_CREATED - ) - - email = sample_notification( - notify_db, - notify_db_session, - service=sample_job.service, - template=email_template, - job=sample_job, - status=NOTIFICATION_CREATED - ) - - sms = sample_notification( - notify_db, - notify_db_session, - service=sample_job.service, - template=sms_template, - job=sample_job, - status=NOTIFICATION_CREATED - ) - - create_or_update_job_sending_statistics(email) - create_or_update_job_sending_statistics(sms) - create_or_update_job_sending_statistics(letter) - - intitial_stats = JobStatistics.query.all() - assert len(intitial_stats) == 1 - assert intitial_stats[0].emails_sent == 1 - assert intitial_stats[0].sms_sent == 1 - assert intitial_stats[0].letters_sent == 1 - - create_or_update_job_sending_statistics(notification) - - updated_stats = JobStatistics.query.all() - assert updated_stats[0].job_id == sample_job.id - - assert updated_stats[0].emails_sent == email_count - assert updated_stats[0].sms_sent == sms_count - assert updated_stats[0].letters_sent == letter_count - - if notification_type == EMAIL_TYPE: - assert updated_stats[0].sent == email_count - elif notification_type == SMS_TYPE: - assert updated_stats[0].sent == sms_count - elif notification_type == LETTER_TYPE: - assert updated_stats[0].sent == letter_count - - -def test_updating_one_type_of_notification_to_success_maintains_other_counts( - notify_db, - notify_db_session, - sample_service, - sample_letter_template -): - job_1 = sample_job(notify_db, notify_db_session, service=sample_service) - job_2 = sample_job(notify_db, notify_db_session, service=sample_service) - job_3 = sample_job(notify_db, notify_db_session, service=sample_service) - - sms_template = sample_template(notify_db, notify_db_session, service=sample_service) - email_template = sample_email_template(notify_db, notify_db_session, service=sample_service) - letter_template = sample_letter_template - - letter = sample_notification( - notify_db, - notify_db_session, - service=sample_service, - template=letter_template, - job=job_1, - status=NOTIFICATION_CREATED - ) - - email = sample_notification( - notify_db, - notify_db_session, - service=sample_service, - template=email_template, - job=job_2, - status=NOTIFICATION_CREATED - ) - - sms = sample_notification( - notify_db, - notify_db_session, - service=sample_service, - template=sms_template, - job=job_3, - status=NOTIFICATION_CREATED - ) - - create_or_update_job_sending_statistics(email) - create_or_update_job_sending_statistics(sms) - create_or_update_job_sending_statistics(letter) - - sms.status = NOTIFICATION_DELIVERED - email.status = NOTIFICATION_DELIVERED - letter.status = NOTIFICATION_DELIVERED - - update_job_stats_outcome_count(letter) - update_job_stats_outcome_count(email) - update_job_stats_outcome_count(sms) - - stats = JobStatistics.query.order_by(JobStatistics.created_at).all() - assert len(stats) == 3 - assert stats[0].letters_sent == 1 - assert stats[0].emails_sent == 0 - assert stats[0].sms_sent == 0 - assert stats[0].emails_delivered == 0 - assert stats[0].sms_delivered == 0 - - assert stats[1].letters_sent == 0 - assert stats[1].emails_sent == 1 - assert stats[1].sms_sent == 0 - assert stats[1].emails_delivered == 1 - assert stats[1].sms_delivered == 0 - - assert stats[2].letters_sent == 0 - assert stats[2].emails_sent == 0 - assert stats[2].sms_sent == 1 - assert stats[2].emails_delivered == 0 - assert stats[2].sms_delivered == 1 - - assert stats[0].sent == 1 - assert stats[0].delivered == 0 - assert stats[0].failed == 0 - - assert stats[1].sent == 1 - assert stats[1].delivered == 1 - assert stats[1].failed == 0 - - assert stats[2].sent == 1 - assert stats[2].delivered == 1 - assert stats[2].failed == 0 - - -def test_updating_one_type_of_notification_to_error_maintains_other_counts( - notify_db, - notify_db_session, - sample_service, - sample_letter_template -): - job_1 = sample_job(notify_db, notify_db_session, service=sample_service) - job_2 = sample_job(notify_db, notify_db_session, service=sample_service) - job_3 = sample_job(notify_db, notify_db_session, service=sample_service) - sms_template = sample_template(notify_db, notify_db_session, service=sample_service) - email_template = sample_email_template(notify_db, notify_db_session, service=sample_service) - letter_template = sample_letter_template - - letter = sample_notification( - notify_db, - notify_db_session, - service=sample_service, - template=letter_template, - job=job_1, - status=NOTIFICATION_CREATED - ) - - email = sample_notification( - notify_db, - notify_db_session, - service=sample_service, - template=email_template, - job=job_2, - status=NOTIFICATION_CREATED - ) - - sms = sample_notification( - notify_db, - notify_db_session, - service=sample_service, - template=sms_template, - job=job_3, - status=NOTIFICATION_CREATED - ) - - create_or_update_job_sending_statistics(email) - create_or_update_job_sending_statistics(sms) - create_or_update_job_sending_statistics(letter) - - sms.status = NOTIFICATION_TECHNICAL_FAILURE - email.status = NOTIFICATION_TECHNICAL_FAILURE - letter.status = NOTIFICATION_TECHNICAL_FAILURE - - update_job_stats_outcome_count(letter) - update_job_stats_outcome_count(email) - update_job_stats_outcome_count(sms) - - stats = JobStatistics.query.order_by(JobStatistics.created_at).all() - assert len(stats) == 3 - assert stats[0].emails_sent == 0 - assert stats[0].sms_sent == 0 - assert stats[0].letters_sent == 1 - assert stats[0].emails_delivered == 0 - assert stats[0].sms_delivered == 0 - assert stats[0].sms_failed == 0 - assert stats[0].emails_failed == 0 - assert stats[0].letters_failed == 1 - - assert stats[1].emails_sent == 1 - assert stats[1].sms_sent == 0 - assert stats[1].letters_sent == 0 - assert stats[1].emails_delivered == 0 - assert stats[1].sms_delivered == 0 - assert stats[1].sms_failed == 0 - assert stats[1].emails_failed == 1 - assert stats[1].letters_failed == 0 - - assert stats[2].emails_sent == 0 - assert stats[2].sms_sent == 1 - assert stats[2].letters_sent == 0 - assert stats[2].emails_delivered == 0 - assert stats[2].sms_delivered == 0 - assert stats[2].sms_failed == 1 - assert stats[2].emails_failed == 0 - assert stats[1].letters_failed == 0 - - assert stats[0].sent == 1 - assert stats[0].delivered == 0 - assert stats[0].failed == 1 - - assert stats[1].sent == 1 - assert stats[1].delivered == 0 - assert stats[1].failed == 1 - - assert stats[2].sent == 1 - assert stats[2].delivered == 0 - assert stats[2].failed == 1 - - -def test_will_not_timeout_job_counts_before_notification_timeouts(notify_db, notify_db_session, - sample_job, sample_template): - - one_minute_ago = datetime.utcnow() - timedelta(minutes=1) - - sms = sample_notification( - notify_db, - notify_db_session, - service=sample_job.service, - template=sample_template, - job=sample_job, - status=NOTIFICATION_CREATED - ) - - sms_2 = sample_notification( - notify_db, - notify_db_session, - service=sample_job.service, - template=sample_template, - job=sample_job, - status=NOTIFICATION_CREATED - ) - - create_or_update_job_sending_statistics(sms) - create_or_update_job_sending_statistics(sms_2) - - JobStatistics.query.update({JobStatistics.created_at: one_minute_ago}) - - initial_stats = JobStatistics.query.all() - - assert initial_stats[0].emails_sent == 0 - assert initial_stats[0].sms_sent == 2 - assert initial_stats[0].emails_delivered == 0 - assert initial_stats[0].sms_delivered == 0 - assert initial_stats[0].sms_failed == 0 - assert initial_stats[0].emails_failed == 0 - - assert initial_stats[0].sent == 2 - assert initial_stats[0].delivered == 0 - assert initial_stats[0].failed == 0 - - dao_timeout_job_statistics(61) - updated_stats = JobStatistics.query.all() - assert updated_stats[0].emails_sent == 0 - assert updated_stats[0].sms_sent == 2 - assert updated_stats[0].emails_delivered == 0 - assert updated_stats[0].sms_delivered == 0 - assert updated_stats[0].sms_failed == 0 - assert updated_stats[0].emails_failed == 0 - - assert initial_stats[0].sent == 2 - assert initial_stats[0].delivered == 0 - assert initial_stats[0].failed == 0 - - -@pytest.mark.parametrize('notification_type, sms_count, email_count', [ - (SMS_TYPE, 3, 0), - (EMAIL_TYPE, 0, 3), -]) -def test_timeout_job_counts_timesout_multiple_jobs( - notify_db, notify_db_session, notification_type, sms_count, email_count -): - one_minute_ago = datetime.utcnow() - timedelta(minutes=1) - - job_1 = sample_job(notify_db, notify_db_session) - job_2 = sample_job(notify_db, notify_db_session) - job_3 = sample_job(notify_db, notify_db_session) - - jobs = [job_1, job_2, job_3] - - for job in jobs: - if notification_type == EMAIL_TYPE: - template = sample_email_template(notify_db, notify_db_session, service=job.service) - else: - template = sample_template(notify_db, notify_db_session, service=job.service) - - for i in range(3): - n = sample_notification( - notify_db, - notify_db_session, - service=job.service, - template=template, - job=job, - status=NOTIFICATION_CREATED - ) - create_or_update_job_sending_statistics(n) - - JobStatistics.query.update({JobStatistics.created_at: one_minute_ago}) - initial_stats = JobStatistics.query.all() - for stats in initial_stats: - assert stats.emails_sent == email_count - assert stats.sms_sent == sms_count - assert stats.emails_delivered == 0 - assert stats.sms_delivered == 0 - assert stats.sms_failed == 0 - assert stats.emails_failed == 0 - assert stats.sent == email_count + sms_count - assert stats.delivered == 0 - assert stats.failed == 0 - - dao_timeout_job_statistics(1) - updated_stats = JobStatistics.query.all() - for stats in updated_stats: - assert stats.emails_sent == email_count - assert stats.sms_sent == sms_count - assert stats.emails_delivered == 0 - assert stats.sms_delivered == 0 - assert stats.sms_failed == sms_count - assert stats.emails_failed == email_count - assert stats.sent == email_count + sms_count - assert stats.delivered == 0 - assert stats.failed == email_count + sms_count - - -count_notifications = len(NOTIFICATION_STATUS_TYPES) -count_success_notifications = len(NOTIFICATION_STATUS_SUCCESS) -count_error_notifications = len(NOTIFICATION_STATUS_TYPES) - len(NOTIFICATION_STATUS_SUCCESS) - - -def test_timeout_job_sets_all_non_delivered_emails_to_error_and_doesnt_affect_sms( - notify_db, - notify_db_session -): - service = sample_service(notify_db, notify_db_session) - - sms_template = sample_template(notify_db, notify_db_session, service=service) - email_template = sample_email_template(notify_db, notify_db_session, service=service) - - email_job = sample_job( - notify_db, notify_db_session, template=email_template, service=service - ) - sms_job = sample_job( - notify_db, notify_db_session, template=sms_template, service=service - ) - - # Make an email notification in every state - for i in range(len(NOTIFICATION_STATUS_TYPES)): - n = sample_notification( - notify_db, - notify_db_session, - service=email_job.service, - template=email_template, - job=email_job, - status=NOTIFICATION_STATUS_TYPES[i] - ) - create_or_update_job_sending_statistics(n) - - # single sms notification - sms_notification = sample_notification( - notify_db, notify_db_session, service=service, template=sms_template, job=sms_job - ) - create_or_update_job_sending_statistics(sms_notification) - - # fudge the created at time on the job stats table to make the eligible for timeout query - JobStatistics.query.update({ - JobStatistics.created_at: datetime.utcnow() - timedelta(minutes=1) - }) - - # should have sent an email for every state (len(NOTIFICATION_STATUS_TYPES)) - initial_stats = JobStatistics.query.filter_by(job_id=email_job.id).all() - assert len(initial_stats) == 1 - assert initial_stats[0].emails_sent == count_notifications - assert initial_stats[0].sms_sent == 0 - assert initial_stats[0].emails_delivered == 0 - assert initial_stats[0].sms_delivered == 0 - assert initial_stats[0].sms_failed == 0 - assert initial_stats[0].emails_failed == 0 - - assert initial_stats[0].sent == count_notifications - assert initial_stats[0].delivered == 0 - assert initial_stats[0].failed == 0 - - # timeout the notifications - dao_timeout_job_statistics(1) - - # after timeout all delivered states are success and ALL other states are failed - updated_stats = JobStatistics.query.filter_by(job_id=email_job.id).all() - assert updated_stats[0].emails_sent == count_notifications - assert updated_stats[0].sms_sent == 0 - assert updated_stats[0].emails_delivered == count_success_notifications - assert updated_stats[0].sms_delivered == 0 - assert updated_stats[0].sms_failed == 0 - assert updated_stats[0].emails_failed == count_error_notifications - - assert initial_stats[0].sent == count_notifications - assert initial_stats[0].delivered == count_success_notifications - assert initial_stats[0].failed == count_error_notifications - - sms_stats = JobStatistics.query.filter_by(job_id=sms_job.id).all() - assert sms_stats[0].emails_sent == 0 - assert sms_stats[0].sms_sent == 1 - assert sms_stats[0].emails_delivered == 0 - assert sms_stats[0].sms_delivered == 0 - assert sms_stats[0].sms_failed == 1 - assert sms_stats[0].emails_failed == 0 - assert sms_stats[0].sent == 1 - assert sms_stats[0].delivered == 0 - assert sms_stats[0].failed == 1 - - -# this test is as above, but for SMS not email -def test_timeout_job_sets_all_non_delivered_states_to_error( - notify_db, - notify_db_session, - sample_job -): - for i in range(len(NOTIFICATION_STATUS_TYPES)): - n = sample_notification( - notify_db, - notify_db_session, - service=sample_job.service, - template=sample_template(notify_db, notify_db_session, service=sample_job.service), - job=sample_job, - status=NOTIFICATION_STATUS_TYPES[i] - ) - create_or_update_job_sending_statistics(n) - - JobStatistics.query.update({JobStatistics.created_at: datetime.utcnow() - timedelta(minutes=1)}) - initial_stats = JobStatistics.query.all() - for stats in initial_stats: - assert stats.emails_sent == 0 - assert stats.sms_sent == count_notifications - assert stats.emails_delivered == 0 - assert stats.sms_delivered == 0 - assert stats.sms_failed == 0 - assert stats.emails_failed == 0 - - assert stats.sent == count_notifications - assert stats.delivered == 0 - assert stats.failed == 0 - - dao_timeout_job_statistics(1) - updated_stats = JobStatistics.query.all() - - for stats in updated_stats: - assert stats.emails_sent == 0 - assert stats.sms_sent == count_notifications - assert stats.emails_delivered == 0 - assert stats.sms_delivered == count_success_notifications - assert stats.sms_failed == count_error_notifications - assert stats.emails_failed == 0 - - assert stats.sent == count_notifications - assert stats.delivered == count_success_notifications - assert stats.failed == count_error_notifications