def test_get_notifications_renames_letter_statuses(client, sample_letter_template, db_status, expected_status): letter_noti = create_notification(sample_letter_template, status=db_status, personalisation={ 'address_line_1': 'Mr Foo', 'address_line_2': '1 Bar Street', 'postcode': 'N1' }) auth_header = create_authorization_header( service_id=letter_noti.service_id) response = client.get( path=url_for('v2_notifications.get_notification_by_id', notification_id=letter_noti.id), headers=[('Content-Type', 'application/json'), auth_header]) json_response = json.loads(response.get_data(as_text=True)) assert response.status_code == 200 assert json_response['status'] == expected_status
def test_upload_letter_pdf_uses_postage_from_notification( sample_letter_template, mocker, postage, expected_postage): letter_notification = create_notification(template=sample_letter_template, postage=postage) mock_s3 = mocker.patch('app.letters.utils.s3upload') filename = generate_letter_pdf_filename( reference=letter_notification.reference, crown=letter_notification.service.crown, created_at=letter_notification.created_at, ignore_folder=False, postage=letter_notification.postage) upload_letter_pdf(letter_notification, b'\x00\x01', precompiled=False) mock_s3.assert_called_once_with( bucket_name=current_app.config['LETTERS_PDF_BUCKET_NAME'], file_location=filename, filedata=b'\x00\x01', region=current_app.config['AWS_REGION'])
def test_notification_raises_a_retry_exception_if_mlwr_state_is_not_complete( sample_email_template, mocker): mocker.patch('app.aws_ses_client.send_email', return_value='reference') mocker.patch('app.delivery.send_to_providers.check_mlwr', return_value={"state": "foo"}) personalisation = { "file": { "document": { "mlwr_sid": "foo", "direct_file_url": "http://foo.bar", "url": "http://foo.bar" } } } db_notification = create_notification(template=sample_email_template, personalisation=personalisation) with pytest.raises(MalwarePendingException): send_to_providers.send_email_to_provider(db_notification, )
def test_ses_callback_does_not_call_send_delivery_status_if_no_db_entry( client, notify_db_session, sample_email_template, mocker): with freeze_time('2001-01-01T12:00:00'): send_mock = mocker.patch( 'app.celery.service_callback_tasks.send_delivery_status_to_service.apply_async' ) notification = create_notification( template=sample_email_template, status='sending', reference='ref', ) assert get_notification_by_id(notification.id).status == 'sending' assert process_ses_receipts_tasks.process_ses_results( ses_notification_callback(reference='ref')) assert get_notification_by_id(notification.id).status == 'delivered' send_mock.assert_not_called()
def test_preview_letter_template_precompiled_png_file_type_hide_notify_tag_only_on_first_page( notify_api, client, admin_request, sample_service, mocker, page_number, expect_preview_url ): template = create_template(sample_service, template_type='letter', template_name='Pre-compiled PDF', subject='Pre-compiled PDF', hidden=True) notification = create_notification(template) with set_config_values(notify_api, { 'TEMPLATE_PREVIEW_API_HOST': 'http://localhost/notifications-template-preview', 'TEMPLATE_PREVIEW_API_KEY': 'test-key' }): pdf_content = b'\x00\x01' png_content = b'\x00\x02' encoded = base64.b64encode(png_content).decode('utf-8') mocker.patch('app.template.rest.get_letter_pdf', return_value=pdf_content) mocker.patch('app.template.rest.extract_page_from_pdf', return_value=png_content) mock_get_png_preview = mocker.patch('app.template.rest._get_png_preview', return_value=encoded) admin_request.get( 'template.preview_letter_template_by_notification_id', service_id=notification.service_id, notification_id=notification.id, file_type='png', page=page_number ) mock_get_png_preview.assert_called_once_with( expect_preview_url, encoded, notification.id, json=False )
def test_find_missing_row_for_job_more_than_one_missing_row( sample_email_template): job = create_job(template=sample_email_template, notification_count=5, job_status=JOB_STATUS_FINISHED, processing_finished=datetime.utcnow() - timedelta(minutes=11)) create_notification(job=job, job_row_number=0) create_notification(job=job, job_row_number=1) create_notification(job=job, job_row_number=4) results = find_missing_row_for_job(job.id, 5) assert len(results) == 2 assert results[0].missing_row == 2 assert results[1].missing_row == 3
def test_alert_if_letter_notifications_still_sending_only_alerts_sending(sample_letter_template, mocker): two_days_ago = datetime(2018, 1, 15, 13, 30) create_notification(template=sample_letter_template, status='sending', sent_at=two_days_ago) create_notification(template=sample_letter_template, status='delivered', sent_at=two_days_ago) create_notification(template=sample_letter_template, status='failed', sent_at=two_days_ago) mock_create_ticket = mocker.patch("app.celery.nightly_tasks.zendesk_client.create_ticket") raise_alert_if_letter_notifications_still_sending() mock_create_ticket.assert_called_once_with( subject="[test] Letters still sending", message="There are 1 letters in the 'sending' state from Monday 15 January", ticket_type='incident' )
def test_upload_letter_pdf_uses_postage_from_notification( sample_letter_template, mocker, postage, expected_postage): letter_notification = create_notification(template=sample_letter_template, postage=postage) mock_s3 = mocker.patch("app.letters.utils.s3upload") filename = get_letter_pdf_filename( reference=letter_notification.reference, crown=letter_notification.service.crown, is_scan_letter=False, postage=letter_notification.postage, ) upload_letter_pdf(letter_notification, b"\x00\x01", precompiled=False) mock_s3.assert_called_once_with( bucket_name=current_app.config["LETTERS_PDF_BUCKET_NAME"], file_location=filename, filedata=b"\x00\x01", region=current_app.config["AWS_REGION"], )
def test_send_delivery_status_to_service_succeeds_if_sent_at_is_none( notify_db_session, mocker ): callback_api, template = _set_up_test_data('email', "delivery_status") datestr = datetime(2017, 6, 20) notification = create_notification(template=template, created_at=datestr, updated_at=datestr, sent_at=None, status='technical-failure' ) encrypted_data = _set_up_data_for_status_update(callback_api, notification) mocked = mocker.patch('app.celery.service_callback_tasks.send_delivery_status_to_service.retry') with requests_mock.Mocker() as request_mock: request_mock.post(callback_api.url, json={}, status_code=404) send_delivery_status_to_service(notification.id, encrypted_status_update=encrypted_data) assert mocked.call_count == 0
def test_get_notification_by_reference_returns_200(client, sample_template): sample_notification_with_reference = create_notification( template=sample_template, client_reference='some-client-reference') auth_header = create_authorization_header( service_id=sample_notification_with_reference.service_id) response = client.get(path='/v2/notifications?reference={}'.format( sample_notification_with_reference.client_reference), headers=[('Content-Type', 'application/json'), auth_header]) assert response.status_code == 200 assert response.headers['Content-type'] == 'application/json' json_response = json.loads(response.get_data(as_text=True)) assert len(json_response['notifications']) == 1 assert json_response['notifications'][0]['id'] == str( sample_notification_with_reference.id) assert json_response['notifications'][0][ 'reference'] == "some-client-reference"
def test_should_send_sms_with_downgraded_content(notify_db_session, mocker): # é, o, and u are in GSM. # ī, grapes, tabs, zero width space and ellipsis are not # ó isn't in GSM, but it is in the welsh alphabet so will still be sent msg = "a é ī o u 🍇 foo\tbar\u200bbaz((misc))…" placeholder = '∆∆∆abc' gsm_message = "?ódz Housing Service: a é i o u ? foo barbaz???abc..." service = create_service(service_name='Łódź Housing Service') template = create_template(service, content=msg) db_notification = create_notification( template=template, personalisation={'misc': placeholder}) res = Mock(content=json.dumps({'sid': 1})) mocker.patch('app.twilio_client.send_sms', return_value=res) send_to_providers.send_sms_to_provider(db_notification) twilio_client.send_sms.assert_called_once_with(to=ANY, content=gsm_message, reference=ANY, sender=ANY)
def test_should_send_sms_with_downgraded_content(notify_db_session, mock_sms_client): # é, o, and u are in GSM. # á, ï, grapes, tabs, zero width space and ellipsis are not msg = "á é ï o u � foo\tbar\u200bbaz((misc))…" placeholder = '∆∆∆abc' gsm_message = "?odz Housing Service: a é i o u ? foo barbaz???abc..." service = create_service(service_name='�ódź Housing Service', prefix_sms=True) template = create_template(service, content=msg) db_notification = create_notification( template=template, personalisation={'misc': placeholder} ) send_to_providers.send_sms_to_provider(db_notification) mock_sms_client.send_sms.assert_called_once_with( to=ANY, content=gsm_message, reference=ANY, sender=ANY )
def test_notification_raises_error_if_message_contains_sin_pii_that_passes_luhn( sample_email_template_with_html, mocker, notify_api): send_mock = mocker.patch("app.aws_ses_client.send_email", return_value='reference') db_notification = create_notification( template=sample_email_template_with_html, to_field="*****@*****.**", personalisation={'name': '046-454-286'}) with set_config_values(notify_api, { 'SCAN_FOR_PII': "True", }): with pytest.raises(NotificationTechnicalFailureException) as e: send_to_providers.send_email_to_provider(db_notification) assert db_notification.id in e.value send_mock.assert_not_called() assert Notification.query.get( db_notification.id).status == 'pii-check-failed'
def test__send_data_to_service_callback_api_retries_if_request_returns_500_with_encrypted_data( notify_db_session, mocker, notification_type ): callback_api, template = _set_up_test_data(notification_type, "delivery_status") datestr = datetime(2017, 6, 20) notification = create_notification(template=template, created_at=datestr, updated_at=datestr, sent_at=datestr, status='sent' ) encrypted_data = _set_up_data_for_status_update(callback_api, notification) mocked = mocker.patch('app.celery.service_callback_tasks.send_delivery_status_to_service.retry') with requests_mock.Mocker() as request_mock: request_mock.post(callback_api.url, json={}, status_code=500) send_delivery_status_to_service(notification.id, encrypted_status_update=encrypted_data) assert mocked.call_count == 1 assert mocked.call_args[1]['queue'] == 'retry-tasks'
def test_should_respect_custom_sending_domains(sample_service, mocker, sample_email_template_with_html): db_notification = create_notification( template=sample_email_template_with_html, to_field="*****@*****.**", personalisation={"name": "Jo"}, ) sample_service.sending_domain = "foo.bar" mocker.patch("app.aws_ses_client.send_email", return_value="reference") send_to_providers.send_email_to_provider(db_notification) app.aws_ses_client.send_email.assert_called_once_with( '"Sample service" <*****@*****.**>', "*****@*****.**", "Jo <em>some HTML</em>", body="Hello Jo\nThis is an email from GOV.\u200bUK with <em>some HTML</em>\n", html_body=ANY, reply_to_address=None, attachments=[], )
def test_should_handle_sms_sender_and_prefix_message( mocker, sms_sender, prefix_sms, expected_sender, expected_content, notify_db_session ): mocker.patch('app.mmg_client.send_sms') service = create_service_with_defined_sms_sender(sms_sender_value=sms_sender, prefix_sms=prefix_sms) template = create_template(service, content='bar') notification = create_notification(template, reply_to_text=sms_sender) send_to_providers.send_sms_to_provider(notification) mmg_client.send_sms.assert_called_once_with( content=expected_content, sender=expected_sender, to=ANY, reference=ANY, )
def test_ses_callback_should_set_status_to_temporary_failure( client, notify_db_session, sample_email_template, mocker): send_mock = mocker.patch( 'app.celery.service_callback_tasks.send_delivery_status_to_service.apply_async' ) mock_logger = mocker.patch( 'app.celery.process_ses_receipts_tasks.current_app.logger.info') notification = create_notification( template=sample_email_template, status='sending', reference='ref', ) create_service_callback_api(service=notification.service, url="https://original_url.com") assert get_notification_by_id(notification.id).status == 'sending' assert process_ses_results(ses_soft_bounce_callback(reference='ref')) assert get_notification_by_id( notification.id).status == 'temporary-failure' assert send_mock.called assert f'SES bounce for notification ID {notification.id}: ' in mock_logger.call_args[ 0][0]
def test_should_update_billable_units_and_status_according_to_research_mode_and_key_type( sample_template, mocker, research_mode, key_type, billable_units, expected_status ): notification = create_notification(template=sample_template, billable_units=0, status='created', key_type=key_type) mocker.patch('app.mmg_client.send_sms') mocker.patch('app.delivery.send_to_providers.send_sms_response', side_effect=__update_notification(notification, research_mode, expected_status)) if research_mode: sample_template.service.research_mode = True send_to_providers.send_sms_to_provider( notification ) assert notification.billable_units == billable_units assert notification.status == expected_status
def test_send_email_should_use_service_reply_to_email( sample_service, sample_email_template, mocker): mocker.patch('app.aws_ses_client.send_email', return_value='reference') db_notification = create_notification(template=sample_email_template, reply_to_text='*****@*****.**') create_reply_to_email(service=sample_service, email_address='*****@*****.**') send_to_providers.send_email_to_provider( db_notification, ) app.aws_ses_client.send_email.assert_called_once_with( ANY, ANY, ANY, body=ANY, html_body=ANY, reply_to_address='*****@*****.**' )
def test_notification_can_have_document_attachment_without_mlwr_sid( sample_email_template, mocker, mock_email_client, mocked_build_ga_pixel_url): mlwr_mock = mocker.patch('app.delivery.send_to_providers.check_mlwr') personalisation = { "file": { "document": { "id": "foo", "direct_file_url": "http://foo.bar", "url": "http://foo.bar" } } } db_notification = create_notification(template=sample_email_template, personalisation=personalisation) send_to_providers.send_email_to_provider(db_notification, ) mock_email_client.send_email.assert_called() mlwr_mock.assert_not_called()
def test_should_send_sms_with_downgraded_content(notify_db_session, mock_sms_client): # é, o, and u are in GSM. # á, ï, grapes, tabs, zero width space and ellipsis are not msg = "á é ï o u 🍇 foo\tbar\u200bbaz((misc))…" placeholder = '∆∆∆abc' gsm_message = "?odz Housing Service: a é i o u ? foo barbaz???abc..." service = create_service(service_name='Łódź Housing Service') template = create_template(service, content=msg) db_notification = create_notification( template=template, personalisation={'misc': placeholder} ) send_to_providers.send_sms_to_provider(db_notification) mock_sms_client.send_sms.assert_called_once_with( to=ANY, content=gsm_message, reference=ANY, sender=ANY )
def test_send_delivery_status_to_service_post_https_request_to_service_with_encrypted_data( notify_db_session, notification_type): callback_api, template = _set_up_test_data(notification_type, "delivery_status") datestr = datetime(2017, 6, 20) notification = create_notification(template=template, created_at=datestr, updated_at=datestr, sent_at=datestr, status='sent') encrypted_status_update = _set_up_data_for_status_update( callback_api, notification) with requests_mock.Mocker() as request_mock: request_mock.post(callback_api.url, json={}, status_code=200) send_delivery_status_to_service( notification.id, encrypted_status_update=encrypted_status_update) mock_data = { "id": str(notification.id), "reference": notification.client_reference, "to": notification.to, "status": notification.status, "created_at": datestr.strftime(DATETIME_FORMAT), "completed_at": datestr.strftime(DATETIME_FORMAT), "sent_at": datestr.strftime(DATETIME_FORMAT), "notification_type": notification_type, "template_id": str(template.id), "template_version": 1 } assert request_mock.call_count == 1 assert request_mock.request_history[0].url == callback_api.url assert request_mock.request_history[0].method == 'POST' assert request_mock.request_history[0].text == json.dumps(mock_data) assert request_mock.request_history[0].headers[ "Content-type"] == "application/json" assert request_mock.request_history[0].headers[ "Authorization"] == "Bearer {}".format(callback_api.bearer_token)
def test_get_total_notifications_counts_messages_within_10_seconds( sample_template): created_at = datetime.utcnow() create_notification(sample_template, sent_at=created_at + timedelta(seconds=5)) create_notification(sample_template, sent_at=created_at + timedelta(seconds=10)) create_notification(sample_template, sent_at=created_at + timedelta(seconds=15)) result = dao_get_total_notifications_sent_per_day_for_performance_platform( BEGINNING_OF_DAY, END_OF_DAY) assert result.messages_total == 3 assert result.messages_within_10_secs == 2
def test_filter_by_status_and_template_type(client, sample_template, sample_email_template): create_notification(sample_template) create_notification(sample_email_template) create_notification(sample_email_template, status="delivered") auth_header = create_authorization_header(service_id=sample_email_template.service_id) response = client.get( '/notifications?template_type=email&status=delivered', headers=[auth_header]) notifications = json.loads(response.get_data(as_text=True)) assert response.status_code == 200 assert len(notifications['notifications']) == 1 assert notifications['notifications'][0]['template']['template_type'] == 'email' assert notifications['notifications'][0]['status'] == 'delivered'
def test_populate_redis_template_usage_only_populates_for_today( mocker, notify_api, sample_template): mock_redis = mocker.patch('app.commands.redis_store') # created at in utc create_notification(sample_template, created_at=datetime(2017, 6, 9, 23, 0, 0)) create_notification(sample_template, created_at=datetime(2017, 6, 9, 23, 0, 0)) create_notification(sample_template, created_at=datetime(2017, 6, 10, 0, 0, 0)) create_notification(sample_template, created_at=datetime(2017, 6, 10, 23, 0, 0)) # actually on 11th BST with set_config(notify_api, 'REDIS_ENABLED', True): populate_redis_template_usage.callback.__wrapped__( sample_template.service_id, datetime(2017, 6, 10)) mock_redis.set_hash_and_expire.assert_called_once_with( 'service-{}-template-usage-2017-06-10'.format( sample_template.service_id), {str(sample_template.id): 3}, notify_api.config['EXPIRE_CACHE_EIGHT_DAYS'], raise_exception=True)
def test_fetch_billing_data_for_today_includes_data_with_the_right_date( notify_db_session): process_day = datetime(2018, 4, 1, 13, 30, 0) service = create_service() template = create_template(service=service, template_type="email") create_notification(template=template, status='delivered', created_at=process_day) create_notification(template=template, status='delivered', created_at=datetime(2018, 4, 1, 4, 23, 23)) create_notification(template=template, status='delivered', created_at=datetime(2018, 3, 31, 20, 23, 23)) create_notification(template=template, status='sending', created_at=process_day + timedelta(days=1)) day_under_test = convert_utc_to_local_timezone(process_day) results = fetch_billing_data_for_day(day_under_test) assert len(results) == 1 assert results[0].notifications_sent == 2
def test_get_monthly_notification_stats_combines_todays_data_and_historic_stats( admin_request, sample_template): create_ft_notification_status(datetime(2016, 5, 1), template=sample_template, count=1) create_ft_notification_status( datetime(2016, 6, 1), template=sample_template, notification_status="created", count=2, ) # noqa create_notification(sample_template, created_at=datetime(2016, 6, 5), status="created") create_notification(sample_template, created_at=datetime(2016, 6, 5), status="delivered") # this doesn't get returned in the stats because it is old - it should be in ft_notification_status by now create_notification(sample_template, created_at=datetime(2016, 6, 4), status="sending") response = admin_request.get( "service.get_monthly_notification_stats", service_id=sample_template.service_id, year=2016, ) assert len(response["data"]) == 3 # apr, may, jun assert response["data"]["2016-05"] == { "sms": { "delivered": 1 }, "email": {}, "letter": {}, } assert response["data"]["2016-06"] == { "sms": { # combines the stats from the historic ft_notification_status and the current notifications "created": 3, "delivered": 1, }, "email": {}, "letter": {}, }
def test_send_sms_should_use_template_version_from_notification_not_latest( sample_template, mocker): db_notification = create_notification( template=sample_template, to_field='+447234123123', status='created', reply_to_text=sample_template.service.get_default_sms_sender(), normalised_to='447234123123') mocker.patch('app.mmg_client.send_sms') version_on_notification = sample_template.version # Change the template from app.dao.templates_dao import ( dao_get_template_by_id, dao_update_template, ) sample_template.content = sample_template.content + " another version of the template" dao_update_template(sample_template) t = dao_get_template_by_id(sample_template.id) assert t.version > version_on_notification send_to_providers.send_sms_to_provider(db_notification) mmg_client.send_sms.assert_called_once_with( to=validate_and_format_phone_number("+447234123123"), content="Sample service: This is a template:\nwith a newline", reference=str(db_notification.id), sender=current_app.config['FROM_NUMBER']) persisted_notification = notifications_dao.get_notification_by_id( db_notification.id) assert persisted_notification.to == db_notification.to assert persisted_notification.template_id == sample_template.id assert persisted_notification.template_version == version_on_notification assert persisted_notification.template_version != sample_template.version assert persisted_notification.status == 'sending' assert not persisted_notification.personalisation
def test_get_notification_by_reference_returns_200(client, sample_template): sample_notification_with_reference = create_notification( template=sample_template, client_reference="some-client-reference") auth_header = create_authorization_header( service_id=sample_notification_with_reference.service_id) response = client.get( path="/v2/notifications?reference={}".format( sample_notification_with_reference.client_reference), headers=[("Content-Type", "application/json"), auth_header], ) assert response.status_code == 200 assert response.headers["Content-type"] == "application/json" json_response = json.loads(response.get_data(as_text=True)) assert len(json_response["notifications"]) == 1 assert json_response["notifications"][0]["id"] == str( sample_notification_with_reference.id) assert json_response["notifications"][0][ "reference"] == "some-client-reference"
def test_notification_can_have_document_attachment_without_mlwr_sid( sample_email_template, mocker): send_mock = mocker.patch('app.aws_ses_client.send_email', return_value='reference') mlwr_mock = mocker.patch('app.delivery.send_to_providers.check_mlwr') personalisation = { "file": { "document": { "id": "foo", "direct_file_url": "http://foo.bar", "url": "http://foo.bar" } } } db_notification = create_notification(template=sample_email_template, personalisation=personalisation) send_to_providers.send_email_to_provider(db_notification, ) send_mock.assert_called() mlwr_mock.assert_not_called()