diff --git a/.ds.baseline b/.ds.baseline index ad8d6eb1e..864192e1e 100644 --- a/.ds.baseline +++ b/.ds.baseline @@ -277,7 +277,7 @@ "filename": "tests/app/notifications/test_receive_notification.py", "hashed_secret": "913a73b565c8e2c8ed94497580f619397709b8b6", "is_verified": false, - "line_number": 26, + "line_number": 27, "is_secret": false }, { @@ -285,7 +285,7 @@ "filename": "tests/app/notifications/test_receive_notification.py", "hashed_secret": "d70eab08607a4d05faa2d0d6647206599e9abc65", "is_verified": false, - "line_number": 56, + "line_number": 57, "is_secret": false } ], @@ -384,5 +384,5 @@ } ] }, - "generated_at": "2025-01-28T20:24:49Z" + "generated_at": "2025-02-10T16:57:15Z" } diff --git a/tests/app/aws/test_s3.py b/tests/app/aws/test_s3.py index 843ce3ba0..de26f5760 100644 --- a/tests/app/aws/test_s3.py +++ b/tests/app/aws/test_s3.py @@ -269,6 +269,16 @@ def mock_s3_get_object_slowdown(*args, **kwargs): raise ClientError(error_response, "GetObject") +def mock_s3_get_object_no_such_key(*args, **kwargs): + error_response = { + "Error": { + "Code": "NoSuchKey", + "Message": "Couldn't find it", + } + } + raise ClientError(error_response, "GetObject") + + def test_get_job_from_s3_exponential_backoff_on_throttling(mocker): # We try multiple times to retrieve the job, and if we can't we return None mock_get_object = mocker.patch( @@ -280,6 +290,17 @@ def test_get_job_from_s3_exponential_backoff_on_throttling(mocker): assert mock_get_object.call_count == 8 +def test_get_job_from_s3_exponential_backoff_on_no_such_key(mocker): + # We try multiple times to retrieve the job, and if we can't we return None + mock_get_object = mocker.patch( + "app.aws.s3.get_s3_object", side_effect=mock_s3_get_object_no_such_key + ) + mocker.patch("app.aws.s3.file_exists", return_value=True) + job = get_job_from_s3("service_id", "job_id") + assert job is None + assert mock_get_object.call_count == 2 + + def test_get_job_from_s3_exponential_backoff_on_random_exception(mocker): # We try multiple times to retrieve the job, and if we can't we return None mock_get_object = mocker.patch("app.aws.s3.get_s3_object", side_effect=Exception()) diff --git a/tests/app/celery/test_tasks.py b/tests/app/celery/test_tasks.py index 7f6b940c2..fac121047 100644 --- a/tests/app/celery/test_tasks.py +++ b/tests/app/celery/test_tasks.py @@ -1,7 +1,7 @@ import json import uuid from datetime import datetime, timedelta -from unittest.mock import ANY, Mock, call +from unittest.mock import ANY, MagicMock, Mock, call import pytest import requests_mock @@ -14,6 +14,7 @@ from app import db, encryption from app.celery import provider_tasks, tasks from app.celery.tasks import ( + __total_sending_limits_for_job_exceeded, get_recipient_csv_and_template_and_sender_id, process_incomplete_job, process_incomplete_jobs, @@ -1695,3 +1696,28 @@ def create_encrypted_notification(): assert len(_get_notification_query_all()) == 3 assert len(mock_provider_task.call_args_list) == 3 + + +def test_total_sending_limits_exceeded(mocker): + mock_service = MagicMock() + mock_service.total_message_limit = 1000 + mock_job = MagicMock() + mock_job.notification_count = 300 + job_id = "test_job_id" + + mock_check_service_limit = mocker.patch( + "app.celery.tasks.check_service_over_total_message_limit" + ) + mock_check_service_limit.return_value = 800 + + mock_utc_now = mocker.patch("app.celery.tasks.utc_now") + mock_utc_now.return_value = datetime(2024, 11, 10, 12, 0, 0) + + mock_dao_update_job = mocker.patch("app.celery.tasks.dao_update_job") + + result = __total_sending_limits_for_job_exceeded(mock_service, mock_job, job_id) + assert result is True + + assert mock_job.job_status == "sending limits exceeded" + assert mock_job.processing_finished == datetime(2024, 11, 10, 12, 0, 0) + mock_dao_update_job.assert_called_once_with(mock_job) diff --git a/tests/app/dao/test_annual_billing_dao.py b/tests/app/dao/test_annual_billing_dao.py index e3d269763..72a7d3a3a 100644 --- a/tests/app/dao/test_annual_billing_dao.py +++ b/tests/app/dao/test_annual_billing_dao.py @@ -5,6 +5,8 @@ from app import db from app.dao.annual_billing_dao import ( dao_create_or_update_annual_billing_for_year, + dao_get_all_free_sms_fragment_limit, + dao_get_annual_billing, dao_get_free_sms_fragment_limit_for_year, dao_update_annual_billing_for_future_years, set_default_free_allowance_for_service, @@ -124,3 +126,37 @@ def test_set_default_free_allowance_for_service_updates_existing_year(sample_ser assert len(annual_billing) == 1 assert annual_billing[0].service_id == sample_service.id assert annual_billing[0].free_sms_fragment_limit == 150000 + + +def test_dao_get_annual_billing(mocker): + mock_db_session = mocker.patch("app.dao.db.session.execute") + + mock_db_session.return_value.scalars.return_value.all.return_value = [ + "billing_entry1", + "billing_entry2", + ] + service_id = "test_service_id" + result = dao_get_annual_billing(service_id) + mock_db_session.assert_called_once() + stmt = mock_db_session.call_args[0][0] + assert stmt.compile().params["service_id_1"] == service_id + + assert result == ["billing_entry1", "billing_entry2"] + + +def test_dao_get_all_free_sms_fragment_limit(mocker): + mock_db_session = mocker.patch("app.dao.db.session.execute") + mock_db_session.return_value.scalars.return_value.all.return_value = [ + "sms_limit1", + "sms_limit2", + ] + + service_id = "test_service_id" + + result = dao_get_all_free_sms_fragment_limit(service_id) + + mock_db_session.assert_called_once() + + stmt = mock_db_session.call_args[0][0] + assert stmt.compile().params["service_id_1"] == service_id + assert result == ["sms_limit1", "sms_limit2"] diff --git a/tests/app/notifications/test_receive_notification.py b/tests/app/notifications/test_receive_notification.py index 9bc9d35f6..a3c1dad1a 100644 --- a/tests/app/notifications/test_receive_notification.py +++ b/tests/app/notifications/test_receive_notification.py @@ -3,7 +3,7 @@ from unittest import mock import pytest -from flask import json +from flask import current_app, json from sqlalchemy import func, select from app import db @@ -13,6 +13,7 @@ create_inbound_sms_object, fetch_potential_service, has_inbound_sms_permissions, + receive_sns_sms, unescape_string, ) from tests.app.db import ( @@ -376,3 +377,14 @@ def test_fetch_potential_service_cant_find_it(mock_dao): mock_dao.return_value = create_service() found_service = fetch_potential_service(234, "sns") assert found_service is False + + +def test_receive_sns_sms_inbound_disabled(mocker): + current_app.config["RECEIVE_INBOUND_SMS"] = False + response, status_code = receive_sns_sms() + + assert status_code == 200 + assert response.json == { + "result": "success", + "message": "SMS-SNS callback succeeded", + }