def test_send_broadcast_event_queues_up_for_active_providers(mocker, notify_api, sample_broadcast_service): template = create_template(sample_broadcast_service, BROADCAST_TYPE) broadcast_message = create_broadcast_message(template, status=BroadcastStatusType.BROADCASTING) event = create_broadcast_event(broadcast_message) mock_send_broadcast_provider_message = mocker.patch( 'app.celery.broadcast_message_tasks.send_broadcast_provider_message', ) with set_config(notify_api, 'ENABLED_CBCS', ['ee', 'vodafone']): send_broadcast_event(event.id) assert mock_send_broadcast_provider_message.apply_async.call_args_list == [ call(kwargs={'broadcast_event_id': event.id, 'provider': 'ee'}, queue='broadcast-tasks'), call(kwargs={'broadcast_event_id': event.id, 'provider': 'vodafone'}, queue='broadcast-tasks') ]
def test_should_allow_international_number_on_sms_notification( client, sample_service_full_permissions, mocker): mocker.patch('app.celery.provider_tasks.deliver_sms.apply_async') template = create_template(sample_service_full_permissions) data = {'to': '20-12-1234-1234', 'template': str(template.id)} auth_header = create_authorization_header( service_id=sample_service_full_permissions.id) response = client.post(path='/notifications/sms', data=json.dumps(data), headers=[('Content-Type', 'application/json'), auth_header]) assert response.status_code == 201
def test_post_letter_notification_returns_403_if_not_allowed_to_send_notification( client, notify_db_session, service_args): service = create_service(**service_args) template = create_template(service, template_type=LETTER_TYPE) data = {'template_id': str(template.id), 'personalisation': test_address} error_json = letter_request(client, data, service_id=service.id, _expected_status=400) assert error_json['status_code'] == 400 assert error_json['errors'] == [{ 'error': 'BadRequestError', 'message': 'Cannot send letters' }]
def test_update_broadcast_message_status(admin_request, sample_service): t = create_template(sample_service, BROADCAST_TYPE) bm = create_broadcast_message(t, status=BroadcastStatusType.DRAFT) response = admin_request.post( 'broadcast_message.update_broadcast_message_status', _data={ 'status': BroadcastStatusType.PENDING_APPROVAL, 'created_by': str(t.created_by_id) }, service_id=t.service_id, broadcast_message_id=bm.id, _expected_status=200) assert response['status'] == BroadcastStatusType.PENDING_APPROVAL assert response['updated_at'] is not None
def test_get_broadcast_message(admin_request, sample_service): t = create_template(sample_service, BROADCAST_TYPE) bm = create_broadcast_message(t, areas=['place A', 'region B']) response = admin_request.get('broadcast_message.get_broadcast_message', service_id=t.service_id, broadcast_message_id=bm.id, _expected_status=200) assert response['id'] == str(bm.id) assert response['template_name'] == t.name assert response['status'] == BroadcastStatusType.DRAFT assert response['created_at'] is not None assert response['starts_at'] is None assert response['areas'] == ['place A', 'region B'] assert response['personalisation'] == {}
def test_check_content_char_count_fails(notify_db_session, show_prefix, char_count): with pytest.raises(BadRequestError) as e: service = create_service(prefix_sms=show_prefix) t = create_template(service=service, content='a' * char_count, template_type='sms') template = templates_dao.dao_get_template_by_id_and_service_id( template_id=t.id, service_id=service.id) template_with_content = get_template_instance( template=template.__dict__, values={}) check_content_char_count(template_with_content) assert e.value.status_code == 400 assert e.value.message == f'Text messages cannot be longer than {SMS_CHAR_COUNT_LIMIT} characters. ' \ f'Your message is {char_count} characters' assert e.value.fields == []
def test_fetch_billing_data_for_today_includes_data_with_the_right_status( notify_db_session, ): service = create_service() template = create_template(service=service, template_type="email") for status in ["created", "technical-failure"]: create_notification(template=template, status=status) today = convert_utc_to_local_timezone(datetime.utcnow()) results = fetch_billing_data_for_day(today) assert results == [] for status in ["delivered", "sending", "temporary-failure"]: create_notification(template=template, status=status) results = fetch_billing_data_for_day(today) assert len(results) == 1 assert results[0].notifications_sent == 3
def test_fetch_billing_data_for_day_is_grouped_by_rate_mulitplier( notify_db_session): service = create_service() template = create_template(service=service) create_notification(template=template, status='delivered', rate_multiplier=1) create_notification(template=template, status='delivered', rate_multiplier=2) today = convert_utc_to_bst(datetime.utcnow()) results = fetch_billing_data_for_day(today.date()) assert len(results) == 2 assert results[0].notifications_sent == 1 assert results[1].notifications_sent == 1
def test_send_one_off_notification_raises_if_over_limit( notify_db_session, mocker): service = create_service(message_limit=0) template = create_template(service=service) mocker.patch( 'app.service.send_notification.check_service_over_daily_message_limit', side_effect=TooManyRequestsError(1)) post_data = { 'template_id': str(template.id), 'to': '07700 900 001', 'created_by': str(service.created_by_id) } with pytest.raises(TooManyRequestsError): send_one_off_notification(service.id, post_data)
def test_fetch_billing_data_for_day_is_grouped_by_international( notify_db_session): service = create_service() template = create_template(service=service) create_notification(template=template, status='delivered', international=True) create_notification(template=template, status='delivered', international=False) today = convert_utc_to_local_timezone(datetime.utcnow()) results = fetch_billing_data_for_day(today) assert len(results) == 2 assert results[0].notifications_sent == 1 assert results[1].notifications_sent == 1
def test_fetch_monthly_billing_for_year_adds_data_for_today(notify_db_session): service = create_service() template = create_template(service=service, template_type="email") for i in range(1, 32): create_ft_billing(utc_date='2018-07-{}'.format(i), service=service, template=template, notification_type='email', rate=0.162) create_notification(template=template, status='delivered') assert db.session.query(FactBilling.bst_date).count() == 31 results = fetch_monthly_billing_for_year(service_id=service.id, year=2018) assert db.session.query(FactBilling.bst_date).count() == 32 assert len(results) == 2
def test_should_handle_sms_sender_and_prefix_message( mocker, sms_sender, prefix_sms, expected_sender, expected_content, notify_db_session ): mocker.patch("app.aws_sns_client.send_sms", return_value="message_id_from_sns") 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) aws_sns_client.send_sms.assert_called_once_with( content=expected_content, sender=expected_sender, to=ANY, reference=ANY, )
def test_get_all_complaints_returns_complaints_for_multiple_services( client, notify_db_session): service = create_service(service_name="service1") template = create_template(service=service) notification = create_notification(template=template) complaint_1 = create_complaint() # default service complaint_2 = create_complaint(service=service, notification=notification) response = client.get("/complaint", headers=[create_authorization_header()]) assert response.status_code == 200 assert json.loads(response.get_data(as_text=True))["complaints"] == [ complaint_2.serialize(), complaint_1.serialize(), ]
def test_get_broadcast_messages_for_service(admin_request, sample_broadcast_service): t = create_template(sample_broadcast_service, BROADCAST_TYPE) with freeze_time('2020-01-01 12:00'): bm1 = create_broadcast_message(t, personalisation={'foo': 'bar'}) with freeze_time('2020-01-01 13:00'): bm2 = create_broadcast_message(t, personalisation={'foo': 'baz'}) response = admin_request.get( 'broadcast_message.get_broadcast_messages_for_service', service_id=t.service_id, _expected_status=200) assert response['broadcast_messages'][0]['id'] == str(bm1.id) assert response['broadcast_messages'][1]['id'] == str(bm2.id)
def test_validate_template_calls_all_validators(mocker, fake_uuid, sample_service): template = create_template(sample_service, template_type="email") mock_check_type = mocker.patch('app.notifications.validators.check_template_is_for_notification_type') mock_check_if_active = mocker.patch('app.notifications.validators.check_template_is_active') mock_create_conent = mocker.patch( 'app.notifications.validators.create_content_for_notification', return_value="content" ) mock_check_not_empty = mocker.patch('app.notifications.validators.check_notification_content_is_not_empty') mock_check_message_is_too_long = mocker.patch('app.notifications.validators.check_content_char_count') template, template_with_content = validate_template(template.id, {}, sample_service, "email") mock_check_type.assert_called_once_with("email", "email") mock_check_if_active.assert_called_once_with(template) mock_create_conent.assert_called_once_with(template, {}) mock_check_not_empty.assert_called_once_with("content") mock_check_message_is_too_long.assert_called_once_with("content")
def test_dao_fetch_todays_stats_for_all_services_only_includes_today(notify_db_session): template = create_template(service=create_service()) with freeze_time('2001-01-01T23:59:00'): # just_before_midnight_yesterday create_notification(template=template, to_field='1', status='delivered') with freeze_time('2001-01-02T00:01:00'): # just_after_midnight_today create_notification(template=template, to_field='2', status='failed') with freeze_time('2001-01-02T12:00:00'): stats = dao_fetch_todays_stats_for_all_services() stats = {row.status: row.count for row in stats} assert 'delivered' not in stats assert stats['failed'] == 1
def sample_notification_with_job(notify_db_session): service = create_service(check_if_service_exists=True) template = create_template(service=service) job = create_job(template=template) return create_notification(template=template, job=job, job_row_number=None, to_field=None, status='created', reference=None, created_at=None, sent_at=None, billable_units=1, personalisation=None, api_key=None, key_type=KEY_TYPE_NORMAL)
def test_send_one_off_notification_raises_if_message_too_long(persist_mock, notify_db_session): service = create_service() template = create_template(service=service, content="Hello (( Name))\nYour thing is due soon") post_data = { 'template_id': str(template.id), 'to': '07700 900 001', 'personalisation': {'name': '🚫' * 1000}, 'created_by': str(service.created_by_id) } with pytest.raises(BadRequestError) as e: send_one_off_notification(service.id, post_data) assert e.value.message == f'Text messages cannot be longer than {SMS_CHAR_COUNT_LIMIT} characters. ' \ f'Your message is {1029} characters'
def test_preview_letter_template_precompiled_png_file_type( notify_api, client, admin_request, sample_service, mocker ): 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' }): with requests_mock.Mocker() as request_mock: pdf_content = b'\x00\x01' png_content = b'\x00\x02' mock_get_letter_pdf = mocker.patch('app.template.rest.get_letter_pdf', return_value=pdf_content) mocker.patch('app.template.rest.extract_page_from_pdf', return_value=pdf_content) mock_post = request_mock.post( 'http://localhost/notifications-template-preview/precompiled-preview.png', content=png_content, headers={'X-pdf-page-count': '1'}, status_code=200 ) resp = admin_request.get( 'template.preview_letter_template_by_notification_id', service_id=notification.service_id, notification_id=notification.id, file_type='png' ) with pytest.raises(ValueError): mock_post.last_request.json() assert mock_get_letter_pdf.called_once_with(notification) assert base64.b64decode(resp['content']) == png_content
def test_send_broadcast_provider_message_sends_cancel_with_references( mocker, sample_broadcast_service, provider, provider_capitalised): template = create_template(sample_broadcast_service, BROADCAST_TYPE, content='content') broadcast_message = create_broadcast_message( template, areas={ 'areas': ['london'], 'simple_polygons': [ [[50.12, 1.2], [50.13, 1.2], [50.14, 1.21]], ], }, status=BroadcastStatusType.BROADCASTING) alert_event = create_broadcast_event( broadcast_message, message_type=BroadcastEventMessageType.ALERT) update_event = create_broadcast_event( broadcast_message, message_type=BroadcastEventMessageType.UPDATE) cancel_event = create_broadcast_event( broadcast_message, message_type=BroadcastEventMessageType.CANCEL) create_broadcast_provider_message( alert_event, provider, status=BroadcastProviderMessageStatus.ACK) create_broadcast_provider_message( update_event, provider, status=BroadcastProviderMessageStatus.ACK) mock_cancel_broadcast = mocker.patch( f'app.clients.cbc_proxy.CBCProxy{provider_capitalised}.cancel_broadcast', ) send_broadcast_provider_message(provider=provider, broadcast_event_id=str(cancel_event.id)) broadcast_provider_message = cancel_event.get_provider_message(provider) assert broadcast_provider_message.status == BroadcastProviderMessageStatus.ACK mock_cancel_broadcast.assert_called_once_with( identifier=str(broadcast_provider_message.id), message_number=mocker.ANY, previous_provider_messages=[ alert_event.get_provider_message(provider), update_event.get_provider_message(provider) ], sent=cancel_event.sent_at_as_cap_datetime_string, )
def test_preview_letter_template_precompiled_png_template_preview_pdf_error( notify_api, client, admin_request, sample_service, mocker ): 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' }): with requests_mock.Mocker() as request_mock: pdf_content = b'\x00\x01' png_content = b'\x00\x02' mocker.patch('app.template.rest.get_letter_pdf', return_value=pdf_content) error_message = "PDF Error message" mocker.patch('app.template.rest.extract_page_from_pdf', side_effect=PdfReadError(error_message)) request_mock.post( 'http://localhost/notifications-template-preview/precompiled-preview.png', content=png_content, headers={'X-pdf-page-count': '1'}, status_code=404 ) request = admin_request.get( 'template.preview_letter_template_by_notification_id', service_id=notification.service_id, notification_id=notification.id, file_type='png', _expected_status=500 ) assert request['message'] == "Error extracting requested page from PDF file for notification_id {} type " \ "{} {}".format(notification.id, type(PdfReadError()), error_message)
def test_send_one_off_notification_calls_persist_correctly_for_letter( mocker, persist_mock, celery_mock, notify_db_session ): mocker.patch( 'app.service.send_notification.create_random_identifier', return_value='this-is-random-in-real-life', ) service = create_service() template = create_template( service=service, template_type=LETTER_TYPE, postage='first', subject="Test subject", content="Hello (( Name))\nYour thing is due soon", ) post_data = { 'template_id': str(template.id), 'to': 'First Last', 'personalisation': { 'name': 'foo', 'address line 1': 'First Last', 'address line 2': '1 Example Street', 'postcode': 'SW1A 1AA', }, 'created_by': str(service.created_by_id) } send_one_off_notification(service.id, post_data) persist_mock.assert_called_once_with( template_id=template.id, template_version=template.version, template_postage='first', recipient=post_data['to'], service=template.service, personalisation=post_data['personalisation'], notification_type=LETTER_TYPE, api_key_id=None, key_type=KEY_TYPE_NORMAL, created_by_id=str(service.created_by_id), reply_to_text=None, reference='this-is-random-in-real-life', )
def test_send_broadcast_provider_message_sends_update_with_references( mocker, sample_broadcast_service, provider, provider_capitalised): template = create_template(sample_broadcast_service, BROADCAST_TYPE, content='content') broadcast_message = create_broadcast_message( template, areas={ 'areas': ['london'], 'simple_polygons': [ [[50.12, 1.2], [50.13, 1.2], [50.14, 1.21]], ], }, status=BroadcastStatusType.BROADCASTING) alert_event = create_broadcast_event( broadcast_message, message_type=BroadcastEventMessageType.ALERT) create_broadcast_provider_message( alert_event, provider, status=BroadcastProviderMessageStatus.ACK) update_event = create_broadcast_event( broadcast_message, message_type=BroadcastEventMessageType.UPDATE) mock_update_broadcast = mocker.patch( f'app.clients.cbc_proxy.CBCProxy{provider_capitalised}.update_and_send_broadcast', ) send_broadcast_provider_message(provider=provider, broadcast_event_id=str(update_event.id)) broadcast_provider_message = update_event.get_provider_message(provider) assert broadcast_provider_message.status == BroadcastProviderMessageStatus.ACK mock_update_broadcast.assert_called_once_with( identifier=str(broadcast_provider_message.id), message_number=mocker.ANY, headline="GOV.UK Notify Broadcast", description='this is an emergency broadcast message', areas=[{ "polygon": [[50.12, 1.2], [50.13, 1.2], [50.14, 1.21]], }], previous_provider_messages=[ alert_event.get_provider_message(provider) ], sent=update_event.sent_at_as_cap_datetime_string, expires=update_event.transmitted_finishes_at_as_cap_datetime_string, channel="severe")
def test_send_one_off_notification_raises_if_over_limit( notify_db_session, mocker): service = create_service(message_limit=0) template = create_template(service=service) mocker.patch( "app.service.send_notification.check_service_over_daily_message_limit", side_effect=TooManyRequestsError(1), ) post_data = { "template_id": str(template.id), "to": "6502532222", "created_by": str(service.created_by_id), } with pytest.raises(TooManyRequestsError): send_one_off_notification(service.id, post_data)
def test_get_yearly_usage_by_monthly_from_ft_billing_populates_deltas(client, notify_db_session): service = create_service() sms_template = create_template(service=service, template_type="sms") create_rate(start_date=datetime.utcnow() - timedelta(days=1), value=0.158, notification_type='sms') create_notification(template=sms_template, status='delivered') assert FactBilling.query.count() == 0 response = client.get('service/{}/billing/ft-monthly-usage?year=2018'.format(service.id), headers=[('Content-Type', 'application/json'), create_authorization_header()]) assert response.status_code == 200 assert len(json.loads(response.get_data(as_text=True))) == 1 fact_billing = FactBilling.query.all() assert len(fact_billing) == 1 assert fact_billing[0].notification_type == 'sms'
def test_send_one_off_notification_honors_process_type(notify_db_session, persist_mock, celery_mock, process_type): service = create_service() template = create_template(service=service) template.process_type = process_type post_data = { "template_id": str(template.id), "to": "6502532222", "created_by": str(service.created_by_id), } send_one_off_notification(service.id, post_data) assert celery_mock.call_args[1]["queue"] == f"{process_type}-tasks"
def test_get_last_send_for_api_key(notify_db_session): service = create_service(service_name='First Service') api_key = create_api_key(service) template_email = create_template(service=service, template_type=EMAIL_TYPE) total_sends = 10 last_send = get_last_send_for_api_key(str(api_key.id)) assert last_send == [] for x in range(total_sends): create_notification(template=template_email, api_key=api_key) # the following lines test that a send has occurred within the last second last_send = get_last_send_for_api_key(str(api_key.id))[0][0] now = datetime.utcnow() time_delta = now - last_send assert abs(time_delta.total_seconds()) < 1
def test_post_notification_with_document_upload(client, notify_db_session, mocker, csv_param): service = create_service(service_permissions=[EMAIL_TYPE]) service.contact_link = '*****@*****.**' template = create_template( service=service, template_type='email', content="Document 1: ((first_link)). Document 2: ((second_link))" ) mocker.patch('app.celery.provider_tasks.deliver_email.apply_async') document_download_mock = mocker.patch('app.v2.notifications.post_notifications.document_download_client') document_download_mock.upload_document.side_effect = lambda service_id, content, is_csv: f'{content}-link' data = { "email_address": service.users[0].email_address, "template_id": template.id, "personalisation": { "first_link": {"file": "abababab", **csv_param}, "second_link": {"file": "cdcdcdcd", **csv_param} } } auth_header = create_authorization_header(service_id=service.id) response = client.post( path="v2/notifications/email", data=json.dumps(data), headers=[('Content-Type', 'application/json'), auth_header]) assert response.status_code == 201, response.get_data(as_text=True) resp_json = json.loads(response.get_data(as_text=True)) assert validate(resp_json, post_email_response) == resp_json assert document_download_mock.upload_document.call_args_list == [ call(str(service.id), 'abababab', csv_param.get('is_csv')), call(str(service.id), 'cdcdcdcd', csv_param.get('is_csv')) ] notification = Notification.query.one() assert notification.status == NOTIFICATION_CREATED assert notification.personalisation == { 'first_link': 'abababab-link', 'second_link': 'cdcdcdcd-link' } assert notification.document_download_count == 2 assert resp_json['content']['body'] == 'Document 1: abababab-link. Document 2: cdcdcdcd-link'
def test_get_jobs_for_service(sample_template): one_job = create_job(sample_template) other_service = create_service(service_name="other service") other_template = create_template(service=other_service) other_job = create_job(other_template) one_job_from_db = dao_get_jobs_by_service_id(one_job.service_id).items other_job_from_db = dao_get_jobs_by_service_id(other_job.service_id).items assert len(one_job_from_db) == 1 assert one_job == one_job_from_db[0] assert len(other_job_from_db) == 1 assert other_job == other_job_from_db[0] assert one_job_from_db != other_job_from_db
def archived_service_with_deleted_stuff(client, sample_service): with freeze_time('2001-01-01'): template = create_template(sample_service, template_name='a') api_key = create_api_key(sample_service) expire_api_key(sample_service.id, api_key.id) template.archived = True dao_update_template(template) with freeze_time('2002-02-02'): auth_header = create_authorization_header() response = client.post('/service/{}/archive'.format(sample_service.id), headers=[auth_header]) assert response.status_code == 204 assert response.data == b'' return sample_service