From faf70627e95504c2c17f8f96f0d2aec373b2255a Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 11 Nov 2024 07:17:36 -0800 Subject: [PATCH 01/22] start trying to get to 95% coverage --- tests/app/organization/test_rest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/app/organization/test_rest.py b/tests/app/organization/test_rest.py index a9d7db135..6884a057a 100644 --- a/tests/app/organization/test_rest.py +++ b/tests/app/organization/test_rest.py @@ -35,7 +35,7 @@ def test_get_all_organizations(admin_request, notify_db_session): response = admin_request.get("organization.get_organizations", _expected_status=200) - assert len(response) == 2 + assert len(response) == 3 assert ( set(response[0].keys()) == set(response[1].keys()) From fe2a5a31aac3c7b9ea6dc9c2112c51fe84091445 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 11 Nov 2024 07:28:36 -0800 Subject: [PATCH 02/22] start trying to get to 95% coverage --- tests/app/organization/test_rest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/app/organization/test_rest.py b/tests/app/organization/test_rest.py index 6884a057a..a9d7db135 100644 --- a/tests/app/organization/test_rest.py +++ b/tests/app/organization/test_rest.py @@ -35,7 +35,7 @@ def test_get_all_organizations(admin_request, notify_db_session): response = admin_request.get("organization.get_organizations", _expected_status=200) - assert len(response) == 3 + assert len(response) == 2 assert ( set(response[0].keys()) == set(response[1].keys()) From 5d902dc94587b0420913f2d040494b5cdd5797c5 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 11 Nov 2024 08:20:56 -0800 Subject: [PATCH 03/22] fix --- .ds.baseline | 4 +-- tests/app/service/test_rest.py | 62 +++++++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/.ds.baseline b/.ds.baseline index dd916c550..4dd3b3fb1 100644 --- a/.ds.baseline +++ b/.ds.baseline @@ -305,7 +305,7 @@ "filename": "tests/app/service/test_rest.py", "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", "is_verified": false, - "line_number": 1275, + "line_number": 1278, "is_secret": false } ], @@ -384,5 +384,5 @@ } ] }, - "generated_at": "2024-10-28T20:26:27Z" + "generated_at": "2024-11-11T16:20:52Z" } diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index a5b22ddd3..d5791ed80 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -1,10 +1,11 @@ import json import uuid from datetime import date, datetime, timedelta +from unittest import TestCase from unittest.mock import ANY import pytest -from flask import current_app, url_for +from flask import Request, current_app, url_for from freezegun import freeze_time from sqlalchemy.exc import SQLAlchemyError @@ -24,6 +25,7 @@ StatisticsType, TemplateType, ) +from app.errors import InvalidRequest from app.models import ( AnnualBilling, EmailBranding, @@ -36,6 +38,7 @@ ServiceSmsSender, User, ) +from app.service.rest import check_request_args from app.utils import utc_now from tests import create_admin_authorization_header from tests.app.db import ( @@ -3674,3 +3677,60 @@ def test_get_service_notification_statistics_by_day( assert mock_get_service_statistics_for_specific_days.assert_called_once assert response == mock_data + + +class TestCheckRequestArgs(TestCase): + + def test_check_request_args_valid(self): + request = Request.from_values( + query_string={ + "service_id": "123", + "name": "Test Service", + "email_from": "test@example.com", + } + ) + + service_id, name, email_from = check_request_args(request) + self.assertEqual(service_id, "123") + self.assertEqual(name, "Test Service") + self.assertEqual(email_from, "test@example.com") + + def test_check_request_args_missing_service_id(self): + request = Request.from_values( + query_string={"name": "Test Service", "email_from": "test@example.com"} + ) + + with self.assertRaise(InvalidRequest) as context: + check_request_args(request) + self.assertEqual(context.exception.status_code, 400) + self.assertIn({"service_id": ["Can't be empty"]}, context.exception.errors) + + def test_check_request_args_missing_name(self): + request = Request.from_values( + query_string={"service_id": "123", "email_from": "test@example.com"} + ) + + with self.assertRaise(InvalidRequest) as context: + check_request_args(request) + self.assertEqual(context.exception.status_code, 400) + self.assertIn({"name": ["Can't be empty"]}, context.exception.errors) + + def test_check_request_args_missing_email_from(self): + request = Request.from_values( + query_string={"service_id": "123", "name": "Test Service"} + ) + + with self.assertRaise(InvalidRequest) as context: + check_request_args(request) + self.assertEqual(context.exception.status_code, 400) + self.assertIn({"email_from": ["Can't be empty"]}, context.exception.errors) + + def test_check_request_args_missing_all(self): + request = Request.from_values(query_string={}) + + with self.assertRaise(InvalidRequest) as context: + check_request_args(request) + self.assertEqual(context.exception.status_code, 400) + self.assertIn({"email_from": ["Can't be empty"]}, context.exception.errors) + self.assertIn({"name": ["Can't be empty"]}, context.exception.errors) + self.assertIn({"service_id": ["Can't be empty"]}, context.exception.errors) From c0912622f1695a2b0f932ce6bae7ddd6ca527822 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 11 Nov 2024 08:38:08 -0800 Subject: [PATCH 04/22] Fix --- tests/app/service/test_rest.py | 69 ++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index d5791ed80..b0172e7a4 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -5,7 +5,7 @@ from unittest.mock import ANY import pytest -from flask import Request, current_app, url_for +from flask import Flask, current_app, request, url_for from freezegun import freeze_time from sqlalchemy.exc import SQLAlchemyError @@ -3679,58 +3679,61 @@ def test_get_service_notification_statistics_by_day( assert response == mock_data +test_app = Flask(__name__) + + class TestCheckRequestArgs(TestCase): def test_check_request_args_valid(self): - request = Request.from_values( + with test_app.test_request_context( query_string={ "service_id": "123", "name": "Test Service", "email_from": "test@example.com", } - ) + ): - service_id, name, email_from = check_request_args(request) - self.assertEqual(service_id, "123") - self.assertEqual(name, "Test Service") - self.assertEqual(email_from, "test@example.com") + service_id, name, email_from = check_request_args(request) + self.assertEqual(service_id, "123") + self.assertEqual(name, "Test Service") + self.assertEqual(email_from, "test@example.com") def test_check_request_args_missing_service_id(self): - request = Request.from_values( + with test_app.test_request_context( query_string={"name": "Test Service", "email_from": "test@example.com"} - ) + ): - with self.assertRaise(InvalidRequest) as context: - check_request_args(request) - self.assertEqual(context.exception.status_code, 400) - self.assertIn({"service_id": ["Can't be empty"]}, context.exception.errors) + with self.assertRaise(InvalidRequest) as context: + check_request_args(request) + self.assertEqual(context.exception.status_code, 400) + self.assertIn({"service_id": ["Can't be empty"]}, context.exception.errors) def test_check_request_args_missing_name(self): - request = Request.from_values( + with test_app.test_request_context( query_string={"service_id": "123", "email_from": "test@example.com"} - ) + ): - with self.assertRaise(InvalidRequest) as context: - check_request_args(request) - self.assertEqual(context.exception.status_code, 400) - self.assertIn({"name": ["Can't be empty"]}, context.exception.errors) + with self.assertRaise(InvalidRequest) as context: + check_request_args(request) + self.assertEqual(context.exception.status_code, 400) + self.assertIn({"name": ["Can't be empty"]}, context.exception.errors) def test_check_request_args_missing_email_from(self): - request = Request.from_values( + with test_app.test_request_context( query_string={"service_id": "123", "name": "Test Service"} - ) + ): - with self.assertRaise(InvalidRequest) as context: - check_request_args(request) - self.assertEqual(context.exception.status_code, 400) - self.assertIn({"email_from": ["Can't be empty"]}, context.exception.errors) + with self.assertRaise(InvalidRequest) as context: + check_request_args(request) + self.assertEqual(context.exception.status_code, 400) + self.assertIn({"email_from": ["Can't be empty"]}, context.exception.errors) def test_check_request_args_missing_all(self): - request = Request.from_values(query_string={}) - - with self.assertRaise(InvalidRequest) as context: - check_request_args(request) - self.assertEqual(context.exception.status_code, 400) - self.assertIn({"email_from": ["Can't be empty"]}, context.exception.errors) - self.assertIn({"name": ["Can't be empty"]}, context.exception.errors) - self.assertIn({"service_id": ["Can't be empty"]}, context.exception.errors) + with test_app.test_request_context(query_string={}): + + with self.assertRaise(InvalidRequest) as context: + check_request_args(request) + self.assertEqual(context.exception.status_code, 400) + self.assertIn({"email_from": ["Can't be empty"]}, context.exception.errors) + self.assertIn({"name": ["Can't be empty"]}, context.exception.errors) + self.assertIn({"service_id": ["Can't be empty"]}, context.exception.errors) From 85b98cd0184c9705adeeaa697cce9414e9ce19cc Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 11 Nov 2024 08:49:41 -0800 Subject: [PATCH 05/22] revert --- .ds.baseline | 4 +-- tests/app/service/test_rest.py | 65 +--------------------------------- 2 files changed, 3 insertions(+), 66 deletions(-) diff --git a/.ds.baseline b/.ds.baseline index 4dd3b3fb1..44e49ccd2 100644 --- a/.ds.baseline +++ b/.ds.baseline @@ -305,7 +305,7 @@ "filename": "tests/app/service/test_rest.py", "hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8", "is_verified": false, - "line_number": 1278, + "line_number": 1275, "is_secret": false } ], @@ -384,5 +384,5 @@ } ] }, - "generated_at": "2024-11-11T16:20:52Z" + "generated_at": "2024-11-11T16:49:37Z" } diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index b0172e7a4..a5b22ddd3 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -1,11 +1,10 @@ import json import uuid from datetime import date, datetime, timedelta -from unittest import TestCase from unittest.mock import ANY import pytest -from flask import Flask, current_app, request, url_for +from flask import current_app, url_for from freezegun import freeze_time from sqlalchemy.exc import SQLAlchemyError @@ -25,7 +24,6 @@ StatisticsType, TemplateType, ) -from app.errors import InvalidRequest from app.models import ( AnnualBilling, EmailBranding, @@ -38,7 +36,6 @@ ServiceSmsSender, User, ) -from app.service.rest import check_request_args from app.utils import utc_now from tests import create_admin_authorization_header from tests.app.db import ( @@ -3677,63 +3674,3 @@ def test_get_service_notification_statistics_by_day( assert mock_get_service_statistics_for_specific_days.assert_called_once assert response == mock_data - - -test_app = Flask(__name__) - - -class TestCheckRequestArgs(TestCase): - - def test_check_request_args_valid(self): - with test_app.test_request_context( - query_string={ - "service_id": "123", - "name": "Test Service", - "email_from": "test@example.com", - } - ): - - service_id, name, email_from = check_request_args(request) - self.assertEqual(service_id, "123") - self.assertEqual(name, "Test Service") - self.assertEqual(email_from, "test@example.com") - - def test_check_request_args_missing_service_id(self): - with test_app.test_request_context( - query_string={"name": "Test Service", "email_from": "test@example.com"} - ): - - with self.assertRaise(InvalidRequest) as context: - check_request_args(request) - self.assertEqual(context.exception.status_code, 400) - self.assertIn({"service_id": ["Can't be empty"]}, context.exception.errors) - - def test_check_request_args_missing_name(self): - with test_app.test_request_context( - query_string={"service_id": "123", "email_from": "test@example.com"} - ): - - with self.assertRaise(InvalidRequest) as context: - check_request_args(request) - self.assertEqual(context.exception.status_code, 400) - self.assertIn({"name": ["Can't be empty"]}, context.exception.errors) - - def test_check_request_args_missing_email_from(self): - with test_app.test_request_context( - query_string={"service_id": "123", "name": "Test Service"} - ): - - with self.assertRaise(InvalidRequest) as context: - check_request_args(request) - self.assertEqual(context.exception.status_code, 400) - self.assertIn({"email_from": ["Can't be empty"]}, context.exception.errors) - - def test_check_request_args_missing_all(self): - with test_app.test_request_context(query_string={}): - - with self.assertRaise(InvalidRequest) as context: - check_request_args(request) - self.assertEqual(context.exception.status_code, 400) - self.assertIn({"email_from": ["Can't be empty"]}, context.exception.errors) - self.assertIn({"name": ["Can't be empty"]}, context.exception.errors) - self.assertIn({"service_id": ["Can't be empty"]}, context.exception.errors) From e8edf33f1da47f2c29156d3eae09a96807cf1c2f Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 11 Nov 2024 10:18:16 -0800 Subject: [PATCH 06/22] back to s3 --- tests/app/aws/test_s3.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/app/aws/test_s3.py b/tests/app/aws/test_s3.py index e4a9c1c07..4e443e6f9 100644 --- a/tests/app/aws/test_s3.py +++ b/tests/app/aws/test_s3.py @@ -255,6 +255,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( @@ -266,6 +276,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_slowdown + ) + 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 == 8 + + 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()) From 694b6315345f8d5fb16f8bbb66da676ce666fe82 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 11 Nov 2024 10:30:15 -0800 Subject: [PATCH 07/22] add no such key test --- tests/app/aws/test_s3.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/app/aws/test_s3.py b/tests/app/aws/test_s3.py index 4e443e6f9..b5fe7348d 100644 --- a/tests/app/aws/test_s3.py +++ b/tests/app/aws/test_s3.py @@ -279,12 +279,12 @@ def test_get_job_from_s3_exponential_backoff_on_throttling(mocker): 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_slowdown + "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 == 8 + assert mock_get_object.call_count == 2 def test_get_job_from_s3_exponential_backoff_on_random_exception(mocker): From f564b3a3561bad599913e721343abe88fdcd50be Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 11 Nov 2024 11:00:14 -0800 Subject: [PATCH 08/22] billing dao test --- tests/app/dao/test_annual_billing_dao.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/app/dao/test_annual_billing_dao.py b/tests/app/dao/test_annual_billing_dao.py index f4c3e3d57..bac76e941 100644 --- a/tests/app/dao/test_annual_billing_dao.py +++ b/tests/app/dao/test_annual_billing_dao.py @@ -3,6 +3,7 @@ from app.dao.annual_billing_dao import ( dao_create_or_update_annual_billing_for_year, + 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, @@ -122,3 +123,19 @@ 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"] == service_id + + assert result == ["billing_entry1", "billing_entry2"] From d27b234cdb55e9aa8b8333b8b24ab1eb70e1e11e Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 11 Nov 2024 11:13:29 -0800 Subject: [PATCH 09/22] billing dao test --- tests/app/dao/test_annual_billing_dao.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/app/dao/test_annual_billing_dao.py b/tests/app/dao/test_annual_billing_dao.py index bac76e941..de07e21c4 100644 --- a/tests/app/dao/test_annual_billing_dao.py +++ b/tests/app/dao/test_annual_billing_dao.py @@ -136,6 +136,8 @@ def test_dao_get_annual_billing(mocker): result = dao_get_annual_billing(service_id) mock_db_session.assert_called_once() stmt = mock_db_session.call_args[0][0] + print(f"stmt = {stmt}") + print(f"params = {stmt.compile().params}") assert stmt.compile().params["service_id"] == service_id assert result == ["billing_entry1", "billing_entry2"] From 4bcb94c98b6f58084ce570ccc322f69576a8caba Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 11 Nov 2024 11:22:10 -0800 Subject: [PATCH 10/22] billing dao test --- tests/app/dao/test_annual_billing_dao.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/app/dao/test_annual_billing_dao.py b/tests/app/dao/test_annual_billing_dao.py index de07e21c4..df248bd0f 100644 --- a/tests/app/dao/test_annual_billing_dao.py +++ b/tests/app/dao/test_annual_billing_dao.py @@ -138,6 +138,6 @@ def test_dao_get_annual_billing(mocker): stmt = mock_db_session.call_args[0][0] print(f"stmt = {stmt}") print(f"params = {stmt.compile().params}") - assert stmt.compile().params["service_id"] == service_id + assert stmt.compile().params["service_id_1"] == service_id assert result == ["billing_entry1", "billing_entry2"] From 266a5b198b32700e263ed1123bf4a27b1e67a489 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 11 Nov 2024 11:34:21 -0800 Subject: [PATCH 11/22] billing dao test --- tests/app/dao/test_annual_billing_dao.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/tests/app/dao/test_annual_billing_dao.py b/tests/app/dao/test_annual_billing_dao.py index df248bd0f..aa13d4c1b 100644 --- a/tests/app/dao/test_annual_billing_dao.py +++ b/tests/app/dao/test_annual_billing_dao.py @@ -136,8 +136,21 @@ def test_dao_get_annual_billing(mocker): result = dao_get_annual_billing(service_id) mock_db_session.assert_called_once() stmt = mock_db_session.call_args[0][0] - print(f"stmt = {stmt}") - print(f"params = {stmt.compile().params}") 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_arg[0][0] + print(f"params = {stmt.compile().params}") + assert stmt.compile().params["service_id"] == service_id + assert result == ["sms_limit1", "sms_limit2"] From 2f9dcc54ec34973d45f0d4d53b014633d161ef56 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 11 Nov 2024 11:42:15 -0800 Subject: [PATCH 12/22] fix --- tests/app/dao/test_annual_billing_dao.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/app/dao/test_annual_billing_dao.py b/tests/app/dao/test_annual_billing_dao.py index aa13d4c1b..e3c629924 100644 --- a/tests/app/dao/test_annual_billing_dao.py +++ b/tests/app/dao/test_annual_billing_dao.py @@ -3,6 +3,7 @@ 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, @@ -140,9 +141,13 @@ def test_dao_get_annual_billing(mocker): 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"] + mock_db_session.return_value.scalars.return_value.all.return_value = [ + "sms_limit1", + "sms_limit2", + ] service_id = "test_service_id" From 323c9f00f0db27abff774b7e0c4800322fa1dae0 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 11 Nov 2024 11:54:07 -0800 Subject: [PATCH 13/22] fix --- tests/app/dao/test_annual_billing_dao.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/app/dao/test_annual_billing_dao.py b/tests/app/dao/test_annual_billing_dao.py index e3c629924..b23b56b21 100644 --- a/tests/app/dao/test_annual_billing_dao.py +++ b/tests/app/dao/test_annual_billing_dao.py @@ -155,7 +155,8 @@ def test_dao_get_all_free_sms_fragment_limit(mocker): mock_db_session.assert_called_once() - stmt = mock_db_session.call_arg[0][0] + stmt = mock_db_session.call_args[0][0] + print(f"stmt = {stmt}") print(f"params = {stmt.compile().params}") assert stmt.compile().params["service_id"] == service_id assert result == ["sms_limit1", "sms_limit2"] From 78f010ffa21baf04092a4f4f39e3c33c9590d7ec Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 11 Nov 2024 12:07:08 -0800 Subject: [PATCH 14/22] fix --- tests/app/dao/test_annual_billing_dao.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/app/dao/test_annual_billing_dao.py b/tests/app/dao/test_annual_billing_dao.py index b23b56b21..e23fc7ddb 100644 --- a/tests/app/dao/test_annual_billing_dao.py +++ b/tests/app/dao/test_annual_billing_dao.py @@ -156,7 +156,5 @@ def test_dao_get_all_free_sms_fragment_limit(mocker): mock_db_session.assert_called_once() stmt = mock_db_session.call_args[0][0] - print(f"stmt = {stmt}") - print(f"params = {stmt.compile().params}") - assert stmt.compile().params["service_id"] == service_id + assert stmt.compile().params["service_id_1"] == service_id assert result == ["sms_limit1", "sms_limit2"] From 194df023d76091329e1f0f1f52db4887a96c0ee9 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 11 Nov 2024 12:34:07 -0800 Subject: [PATCH 15/22] receive notifications --- .ds.baseline | 6 +++--- .../notifications/test_receive_notification.py | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.ds.baseline b/.ds.baseline index 44e49ccd2..8af5b59da 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": 24, + "line_number": 25, "is_secret": false }, { @@ -285,7 +285,7 @@ "filename": "tests/app/notifications/test_receive_notification.py", "hashed_secret": "d70eab08607a4d05faa2d0d6647206599e9abc65", "is_verified": false, - "line_number": 54, + "line_number": 55, "is_secret": false } ], @@ -384,5 +384,5 @@ } ] }, - "generated_at": "2024-11-11T16:49:37Z" + "generated_at": "2024-11-11T20:34:04Z" } diff --git a/tests/app/notifications/test_receive_notification.py b/tests/app/notifications/test_receive_notification.py index c95088803..3d1c827d7 100644 --- a/tests/app/notifications/test_receive_notification.py +++ b/tests/app/notifications/test_receive_notification.py @@ -11,6 +11,7 @@ create_inbound_sms_object, fetch_potential_service, has_inbound_sms_permissions, + receive_sns_sms, unescape_string, ) from tests.app.db import ( @@ -369,3 +370,17 @@ 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): + mocker.patch( + "app.notifications.receive_notifications.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", + } From 959e3ffe0ee429dae525953ec173a5d1d5ed6a7e Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 11 Nov 2024 12:49:15 -0800 Subject: [PATCH 16/22] receive notifications --- tests/app/notifications/test_receive_notification.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/app/notifications/test_receive_notification.py b/tests/app/notifications/test_receive_notification.py index 3d1c827d7..aa2972bc1 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 app.enums import ServicePermissionType from app.models import InboundSms @@ -373,10 +373,7 @@ def test_fetch_potential_service_cant_find_it(mock_dao): def test_receive_sns_sms_inbound_disabled(mocker): - mocker.patch( - "app.notifications.receive_notifications.current_app.config", - {"RECEIVE_INBOUND_SMS": False}, - ) + current_app.config["RECEIVE_INBOUND_SMS"] = False response, status_code = receive_sns_sms() assert status_code == 200 From b875259ce6eb31d5be0a852ef974a19e3ebdaf7f Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 11 Nov 2024 13:02:41 -0800 Subject: [PATCH 17/22] receive notifications --- .../test_receive_notification.py | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/app/notifications/test_receive_notification.py b/tests/app/notifications/test_receive_notification.py index aa2972bc1..f7bf117b1 100644 --- a/tests/app/notifications/test_receive_notification.py +++ b/tests/app/notifications/test_receive_notification.py @@ -381,3 +381,34 @@ def test_receive_sns_sms_inbound_disabled(mocker): "result": "success", "message": "SMS-SNS callback succeeded", } + + +def test_receive_sns_sms_no_service_found(mocker): + current_app.config["RECEIVE_INBOUND_SMS"] = True + response, status_code = receive_sns_sms() + mock_sns_handler = mocker.patch( + "app.notifications.receive_notifications.sns_notification_handler" + ) + mock_sns_handler.return_value = { + "Message": json.dumos( + { + "originationNumber": "+15555555555", + "destinationNumber": "+15555554444", + "messageBody": "Hello", + "inboundMessageId": "inbound-id", + } + ), + "Timestamp": "2024-11-10T00:00:00Z", + } + mock_fetch_service = mocker.patch( + "app.notifications.receive_notifications.fetch_potential_service" + ) + mock_fetch_service.return_value = None + + response, status_code = receive_sns_sms() + + assert status_code == 200 + assert response.json == { + "result": "success", + "message": "SMS-SNS callback succeeded", + } From 58d383ec8428b6644d401854b88443a8366a4da3 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 11 Nov 2024 13:19:51 -0800 Subject: [PATCH 18/22] receive notifications --- tests/app/notifications/test_receive_notification.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/app/notifications/test_receive_notification.py b/tests/app/notifications/test_receive_notification.py index f7bf117b1..2bc129e9b 100644 --- a/tests/app/notifications/test_receive_notification.py +++ b/tests/app/notifications/test_receive_notification.py @@ -384,6 +384,10 @@ def test_receive_sns_sms_inbound_disabled(mocker): def test_receive_sns_sms_no_service_found(mocker): + + mocker.patch( + "app.notifications.receive_notifications.tasks.send_inbound_sms_to_service.apply_async" + ) current_app.config["RECEIVE_INBOUND_SMS"] = True response, status_code = receive_sns_sms() mock_sns_handler = mocker.patch( From ca3dcfc31c1b48d5ce26dc0a7b377456b79a8223 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 11 Nov 2024 13:29:44 -0800 Subject: [PATCH 19/22] receive notifications --- .../test_receive_notification.py | 35 ------------------- 1 file changed, 35 deletions(-) diff --git a/tests/app/notifications/test_receive_notification.py b/tests/app/notifications/test_receive_notification.py index 2bc129e9b..aa2972bc1 100644 --- a/tests/app/notifications/test_receive_notification.py +++ b/tests/app/notifications/test_receive_notification.py @@ -381,38 +381,3 @@ def test_receive_sns_sms_inbound_disabled(mocker): "result": "success", "message": "SMS-SNS callback succeeded", } - - -def test_receive_sns_sms_no_service_found(mocker): - - mocker.patch( - "app.notifications.receive_notifications.tasks.send_inbound_sms_to_service.apply_async" - ) - current_app.config["RECEIVE_INBOUND_SMS"] = True - response, status_code = receive_sns_sms() - mock_sns_handler = mocker.patch( - "app.notifications.receive_notifications.sns_notification_handler" - ) - mock_sns_handler.return_value = { - "Message": json.dumos( - { - "originationNumber": "+15555555555", - "destinationNumber": "+15555554444", - "messageBody": "Hello", - "inboundMessageId": "inbound-id", - } - ), - "Timestamp": "2024-11-10T00:00:00Z", - } - mock_fetch_service = mocker.patch( - "app.notifications.receive_notifications.fetch_potential_service" - ) - mock_fetch_service.return_value = None - - response, status_code = receive_sns_sms() - - assert status_code == 200 - assert response.json == { - "result": "success", - "message": "SMS-SNS callback succeeded", - } From 233fd59c278cc898e95d24257e724e158981dd52 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 11 Nov 2024 14:12:08 -0800 Subject: [PATCH 20/22] fix --- app/clients/sms/aws_sns.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/app/clients/sms/aws_sns.py b/app/clients/sms/aws_sns.py index 8b5d6c963..86b212e8e 100644 --- a/app/clients/sms/aws_sns.py +++ b/app/clients/sms/aws_sns.py @@ -1,11 +1,14 @@ +import datetime import os import re from time import monotonic +from unittest.mock import MagicMock import botocore import phonenumbers from boto3 import client +from app.celery.tasks import __total_sending_limits_for_job_exceeded from app.clients import AWS_CLIENT_CONFIG from app.clients.sms import SmsClient from app.cloudfoundry_config import cloud_config @@ -95,3 +98,27 @@ def send_sms(self, to, content, reference, sender=None, international=False): if not matched: self.current_app.logger.error("No valid numbers found in {}".format(to)) raise ValueError("No valid numbers found for SMS delivery") + + 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.clients.sms.aws_sns.check_service_over_total_message_limit" + ) + mock_check_service_limit.return_value = 800 + + mock_utc_now = mocker.patch("app.clients.sms.aws_sns.utc_now") + mock_utc_now.return_value = datetime(2024, 11, 10, 12, 0, 0) + + mock_dao_update_job = mocker.patch("app.clients.sms.aws_sns.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 == "sensding limits exceeded" + assert mock_job.processing_finished == datetime(2024, 11, 10, 12, 0, 0) + mock_dao_update_job.assert_called_once_with(mock_job) From be0422b9afa82bc46a9ff94682cd7502fbcd6d4f Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 11 Nov 2024 14:22:48 -0800 Subject: [PATCH 21/22] fix --- app/clients/sms/aws_sns.py | 27 --------------------------- tests/app/celery/test_tasks.py | 28 +++++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/app/clients/sms/aws_sns.py b/app/clients/sms/aws_sns.py index 86b212e8e..8b5d6c963 100644 --- a/app/clients/sms/aws_sns.py +++ b/app/clients/sms/aws_sns.py @@ -1,14 +1,11 @@ -import datetime import os import re from time import monotonic -from unittest.mock import MagicMock import botocore import phonenumbers from boto3 import client -from app.celery.tasks import __total_sending_limits_for_job_exceeded from app.clients import AWS_CLIENT_CONFIG from app.clients.sms import SmsClient from app.cloudfoundry_config import cloud_config @@ -98,27 +95,3 @@ def send_sms(self, to, content, reference, sender=None, international=False): if not matched: self.current_app.logger.error("No valid numbers found in {}".format(to)) raise ValueError("No valid numbers found for SMS delivery") - - 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.clients.sms.aws_sns.check_service_over_total_message_limit" - ) - mock_check_service_limit.return_value = 800 - - mock_utc_now = mocker.patch("app.clients.sms.aws_sns.utc_now") - mock_utc_now.return_value = datetime(2024, 11, 10, 12, 0, 0) - - mock_dao_update_job = mocker.patch("app.clients.sms.aws_sns.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 == "sensding 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/celery/test_tasks.py b/tests/app/celery/test_tasks.py index 593926c18..9a85bd058 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 Mock, call +from unittest.mock import MagicMock, Mock, call import pytest import requests_mock @@ -13,6 +13,7 @@ from app import 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, @@ -1634,3 +1635,28 @@ def create_encrypted_notification(): assert len(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 == "sensding limits exceeded" + assert mock_job.processing_finished == datetime(2024, 11, 10, 12, 0, 0) + mock_dao_update_job.assert_called_once_with(mock_job) From 0107e0143f0d7513a2e933d072c026ebeab99c9f Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 11 Nov 2024 14:30:35 -0800 Subject: [PATCH 22/22] fix --- tests/app/celery/test_tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/app/celery/test_tasks.py b/tests/app/celery/test_tasks.py index 9a85bd058..5cd0a8c74 100644 --- a/tests/app/celery/test_tasks.py +++ b/tests/app/celery/test_tasks.py @@ -1657,6 +1657,6 @@ def test_total_sending_limits_exceeded(mocker): result = __total_sending_limits_for_job_exceeded(mock_service, mock_job, job_id) assert result is True - assert mock_job.job_status == "sensding limits exceeded" + 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)