Merge branch 'master' into dont-send-message-twice

This commit is contained in:
Rebecca Law
2016-11-28 16:43:17 +00:00
11 changed files with 448 additions and 90 deletions

View File

@@ -21,7 +21,7 @@ from app.models import (
NotificationStatistics,
ServiceWhitelist,
KEY_TYPE_NORMAL, KEY_TYPE_TEST, KEY_TYPE_TEAM,
MOBILE_TYPE, EMAIL_TYPE)
MOBILE_TYPE, EMAIL_TYPE, NOTIFICATION_STATUS_TYPES_COMPLETED)
from app.dao.users_dao import (save_model_user, create_user_code, create_secret_code)
from app.dao.services_dao import (dao_create_service, dao_add_user_to_service)
from app.dao.templates_dao import dao_create_template
@@ -444,7 +444,8 @@ def sample_notification(notify_db,
'notification_type': template.template_type,
'api_key_id': api_key_id,
'key_type': key_type,
'sent_by': sent_by
'sent_by': sent_by,
'updated_at': created_at if status in NOTIFICATION_STATUS_TYPES_COMPLETED else None
}
if job_row_number:
data['job_row_number'] = job_row_number

View File

@@ -8,8 +8,6 @@ import pytest
from freezegun import freeze_time
from sqlalchemy.exc import SQLAlchemyError, IntegrityError
from app import db
from app.models import (
Notification,
NotificationHistory,
@@ -17,6 +15,7 @@ from app.models import (
NotificationStatistics,
TemplateStatistics,
NOTIFICATION_STATUS_TYPES,
NOTIFICATION_STATUS_TYPES_FAILED,
KEY_TYPE_NORMAL,
KEY_TYPE_TEAM,
KEY_TYPE_TEST
@@ -683,7 +682,10 @@ def test_get_all_notifications_for_job_by_status(notify_db, notify_db_session, s
assert len(notifications().items) == len(NOTIFICATION_STATUS_TYPES)
for status in NOTIFICATION_STATUS_TYPES:
assert len(notifications(filter_dict={'status': status}).items) == 1
if status == 'failed':
assert len(notifications(filter_dict={'status': status}).items) == len(NOTIFICATION_STATUS_TYPES_FAILED)
else:
assert len(notifications(filter_dict={'status': status}).items) == 1
assert len(notifications(filter_dict={'status': NOTIFICATION_STATUS_TYPES[:3]}).items) == 3

View File

@@ -1,7 +1,7 @@
from . import return_json_from_response, validate_v0, validate
from app.models import ApiKey, KEY_TYPE_NORMAL
from app.dao.api_key_dao import save_model_api_key
from app.v2.notifications.notification_schemas import get_notification_response
from app.v2.notifications.notification_schemas import get_notification_response, get_notifications_response
from tests import create_authorization_header
@@ -16,6 +16,8 @@ def _get_notification(client, notification, url):
return client.get(url, headers=[auth_header])
# v2
def test_get_v2_sms_contract(client, sample_notification):
response_json = return_json_from_response(_get_notification(
client, sample_notification, '/v2/notifications/{}'.format(sample_notification.id)
@@ -30,6 +32,15 @@ def test_get_v2_email_contract(client, sample_email_notification):
validate(response_json, get_notification_response)
def test_get_v2_notifications_contract(client, sample_notification):
response_json = return_json_from_response(_get_notification(
client, sample_notification, '/v2/notifications'
))
validate(response_json, get_notifications_response)
# v0
def test_get_api_sms_contract(client, sample_notification):
response_json = return_json_from_response(_get_notification(
client, sample_notification, '/notifications/{}'.format(sample_notification.id)

View File

@@ -1,11 +1,16 @@
import pytest
from datetime import datetime
from sqlalchemy.orm.exc import NoResultFound
from tests.app.conftest import sample_notification, sample_provider_rate
from app.models import (
ServiceWhitelist,
MOBILE_TYPE, EMAIL_TYPE)
Notification,
MOBILE_TYPE,
EMAIL_TYPE,
NOTIFICATION_CREATED,
NOTIFICATION_PENDING,
NOTIFICATION_FAILED,
NOTIFICATION_TECHNICAL_FAILURE,
NOTIFICATION_STATUS_TYPES_FAILED
)
@pytest.mark.parametrize('mobile_number', [
@@ -37,35 +42,26 @@ def test_should_not_build_service_whitelist_from_invalid_contact(recipient_type,
ServiceWhitelist.from_string('service_id', recipient_type, contact)
@pytest.mark.parametrize('provider, billable_units, expected_cost', [
('mmg', 1, 3.5),
('firetext', 2, 0.025),
('ses', 0, 0)
@pytest.mark.parametrize('initial_statuses, expected_statuses', [
# passing in single statuses as strings
(NOTIFICATION_FAILED, NOTIFICATION_STATUS_TYPES_FAILED),
(NOTIFICATION_CREATED, NOTIFICATION_CREATED),
(NOTIFICATION_TECHNICAL_FAILURE, NOTIFICATION_TECHNICAL_FAILURE),
# passing in lists containing single statuses
([NOTIFICATION_FAILED], NOTIFICATION_STATUS_TYPES_FAILED),
([NOTIFICATION_CREATED], [NOTIFICATION_CREATED]),
([NOTIFICATION_TECHNICAL_FAILURE], [NOTIFICATION_TECHNICAL_FAILURE]),
# passing in lists containing multiple statuses
([NOTIFICATION_FAILED, NOTIFICATION_CREATED], NOTIFICATION_STATUS_TYPES_FAILED + [NOTIFICATION_CREATED]),
([NOTIFICATION_CREATED, NOTIFICATION_PENDING], [NOTIFICATION_CREATED, NOTIFICATION_PENDING]),
([NOTIFICATION_CREATED, NOTIFICATION_TECHNICAL_FAILURE], [NOTIFICATION_CREATED, NOTIFICATION_TECHNICAL_FAILURE]),
# checking we don't end up with duplicates
(
[NOTIFICATION_FAILED, NOTIFICATION_CREATED, NOTIFICATION_TECHNICAL_FAILURE],
NOTIFICATION_STATUS_TYPES_FAILED + [NOTIFICATION_CREATED]
),
])
def test_calculate_cost_from_notification_billable_units(
notify_db, notify_db_session, provider, billable_units, expected_cost
):
provider_rates = [
('mmg', datetime(2016, 7, 1), 1.5),
('firetext', datetime(2016, 7, 1), 0.0125),
('mmg', datetime.utcnow(), 3.5),
]
for provider_identifier, valid_from, rate in provider_rates:
sample_provider_rate(
notify_db,
notify_db_session,
provider_identifier=provider_identifier,
valid_from=valid_from,
rate=rate
)
notification = sample_notification(notify_db, notify_db_session, billable_units=billable_units, sent_by=provider)
assert notification.cost() == expected_cost
def test_billable_units_without_provider_rates_entry_raises_exception(
notify_db, notify_db_session, sample_provider_rate
):
notification = sample_notification(notify_db, notify_db_session, sent_by='not_a_provider')
with pytest.raises(NoResultFound):
notification.cost()
def test_status_conversion_handles_failed_statuses(initial_statuses, expected_statuses):
converted_statuses = Notification.substitute_status(initial_statuses)
assert len(converted_statuses) == len(expected_statuses)
assert set(converted_statuses) == set(expected_statuses)

View File

@@ -3,7 +3,10 @@ import pytest
from app import DATETIME_FORMAT
from tests import create_authorization_header
from tests.app.conftest import sample_notification as create_sample_notification
from tests.app.conftest import (
sample_notification as create_sample_notification,
sample_template as create_sample_template
)
@pytest.mark.parametrize('billable_units, provider', [
@@ -46,7 +49,6 @@ def test_get_notification_by_id_returns_200(
'line_5': None,
'line_6': None,
'postcode': None,
'cost': sample_notification.cost(),
'type': '{}'.format(sample_notification.notification_type),
'status': '{}'.format(sample_notification.status),
'template': expected_template_response,
@@ -56,3 +58,250 @@ def test_get_notification_by_id_returns_200(
}
assert json_response == expected_response
def test_get_all_notifications_returns_200(client, notify_db, notify_db_session):
notifications = [create_sample_notification(notify_db, notify_db_session) for _ in range(2)]
notification = notifications[-1]
auth_header = create_authorization_header(service_id=notification.service_id)
response = client.get(
path='/v2/notifications',
headers=[('Content-Type', 'application/json'), auth_header])
json_response = json.loads(response.get_data(as_text=True))
assert response.status_code == 200
assert response.headers['Content-type'] == "application/json"
assert json_response['links']['current'].endswith("/v2/notifications")
assert 'next' in json_response['links'].keys()
assert len(json_response['notifications']) == 2
assert json_response['notifications'][0]['id'] == str(notification.id)
assert json_response['notifications'][0]['status'] == "created"
assert json_response['notifications'][0]['template'] == {
'id': str(notification.template.id),
'uri': notification.template.get_link(),
'version': 1
}
assert json_response['notifications'][0]['phone_number'] == "+447700900855"
assert json_response['notifications'][0]['type'] == "sms"
def test_get_all_notifications_no_notifications_if_no_notificatons(client, sample_service):
auth_header = create_authorization_header(service_id=sample_service.id)
response = client.get(
path='/v2/notifications',
headers=[('Content-Type', 'application/json'), auth_header])
json_response = json.loads(response.get_data(as_text=True))
assert response.status_code == 200
assert response.headers['Content-type'] == "application/json"
assert json_response['links']['current'].endswith("/v2/notifications")
assert 'next' not in json_response['links'].keys()
assert len(json_response['notifications']) == 0
def test_get_all_notifications_filter_by_template_type(client, notify_db, notify_db_session):
email_template = create_sample_template(notify_db, notify_db_session, template_type="email")
sms_template = create_sample_template(notify_db, notify_db_session, template_type="sms")
notification = create_sample_notification(
notify_db, notify_db_session, template=email_template, to_field="don.draper@scdp.biz")
create_sample_notification(notify_db, notify_db_session, template=sms_template)
auth_header = create_authorization_header(service_id=notification.service_id)
response = client.get(
path='/v2/notifications?template_type=email',
headers=[('Content-Type', 'application/json'), auth_header])
json_response = json.loads(response.get_data(as_text=True))
assert response.status_code == 200
assert response.headers['Content-type'] == "application/json"
assert json_response['links']['current'].endswith("/v2/notifications?template_type=email")
assert 'next' in json_response['links'].keys()
assert len(json_response['notifications']) == 1
assert json_response['notifications'][0]['id'] == str(notification.id)
assert json_response['notifications'][0]['status'] == "created"
assert json_response['notifications'][0]['template'] == {
'id': str(email_template.id),
'uri': email_template.get_link(),
'version': 1
}
assert json_response['notifications'][0]['email_address'] == "don.draper@scdp.biz"
assert json_response['notifications'][0]['type'] == "email"
def test_get_all_notifications_filter_by_single_status(client, notify_db, notify_db_session):
notification = create_sample_notification(notify_db, notify_db_session, status="pending")
create_sample_notification(notify_db, notify_db_session)
auth_header = create_authorization_header(service_id=notification.service_id)
response = client.get(
path='/v2/notifications?status=pending',
headers=[('Content-Type', 'application/json'), auth_header])
json_response = json.loads(response.get_data(as_text=True))
assert response.status_code == 200
assert response.headers['Content-type'] == "application/json"
assert json_response['links']['current'].endswith("/v2/notifications?status=pending")
assert 'next' in json_response['links'].keys()
assert len(json_response['notifications']) == 1
assert json_response['notifications'][0]['id'] == str(notification.id)
assert json_response['notifications'][0]['status'] == "pending"
def test_get_all_notifications_filter_by_multiple_statuses(client, notify_db, notify_db_session):
notifications = [
create_sample_notification(notify_db, notify_db_session, status=_status)
for _status in ["created", "pending", "sending"]
]
failed_notification = create_sample_notification(notify_db, notify_db_session, status="permanent-failure")
auth_header = create_authorization_header(service_id=notifications[0].service_id)
response = client.get(
path='/v2/notifications?status=created&status=pending&status=sending',
headers=[('Content-Type', 'application/json'), auth_header])
json_response = json.loads(response.get_data(as_text=True))
assert response.status_code == 200
assert response.headers['Content-type'] == "application/json"
assert json_response['links']['current'].endswith("/v2/notifications?status=created&status=pending&status=sending")
assert 'next' in json_response['links'].keys()
assert len(json_response['notifications']) == 3
returned_notification_ids = [_n['id'] for _n in json_response['notifications']]
for _id in [_notification.id for _notification in notifications]:
assert str(_id) in returned_notification_ids
assert failed_notification.id not in returned_notification_ids
def test_get_all_notifications_filter_by_failed_status(client, notify_db, notify_db_session):
created_notification = create_sample_notification(notify_db, notify_db_session, status="created")
failed_notifications = [
create_sample_notification(notify_db, notify_db_session, status=_status)
for _status in ["technical-failure", "temporary-failure", "permanent-failure"]
]
auth_header = create_authorization_header(service_id=created_notification.service_id)
response = client.get(
path='/v2/notifications?status=failed',
headers=[('Content-Type', 'application/json'), auth_header])
json_response = json.loads(response.get_data(as_text=True))
assert response.status_code == 200
assert response.headers['Content-type'] == "application/json"
assert json_response['links']['current'].endswith("/v2/notifications?status=failed")
assert 'next' in json_response['links'].keys()
assert len(json_response['notifications']) == 3
returned_notification_ids = [n['id'] for n in json_response['notifications']]
for _id in [_notification.id for _notification in failed_notifications]:
assert str(_id) in returned_notification_ids
assert created_notification.id not in returned_notification_ids
def test_get_all_notifications_filter_by_id(client, notify_db, notify_db_session):
older_notification = create_sample_notification(notify_db, notify_db_session)
newer_notification = create_sample_notification(notify_db, notify_db_session)
auth_header = create_authorization_header(service_id=newer_notification.service_id)
response = client.get(
path='/v2/notifications?older_than={}'.format(newer_notification.id),
headers=[('Content-Type', 'application/json'), auth_header])
json_response = json.loads(response.get_data(as_text=True))
assert response.status_code == 200
assert response.headers['Content-type'] == "application/json"
assert json_response['links']['current'].endswith("/v2/notifications?older_than={}".format(newer_notification.id))
assert 'next' in json_response['links'].keys()
assert len(json_response['notifications']) == 1
assert json_response['notifications'][0]['id'] == str(older_notification.id)
def test_get_all_notifications_filter_by_id_no_notifications_if_nonexistent_id(client, notify_db, notify_db_session):
notification = create_sample_notification(notify_db, notify_db_session)
auth_header = create_authorization_header(service_id=notification.service_id)
response = client.get(
path='/v2/notifications?older_than=dd4b8b9d-d414-4a83-9256-580046bf18f9',
headers=[('Content-Type', 'application/json'), auth_header])
json_response = json.loads(response.get_data(as_text=True))
assert response.status_code == 200
assert response.headers['Content-type'] == "application/json"
assert json_response['links']['current'].endswith(
"/v2/notifications?older_than=dd4b8b9d-d414-4a83-9256-580046bf18f9")
assert 'next' not in json_response['links'].keys()
assert len(json_response['notifications']) == 0
def test_get_all_notifications_filter_by_id_no_notifications_if_last_notification(client, notify_db, notify_db_session):
notification = create_sample_notification(notify_db, notify_db_session)
auth_header = create_authorization_header(service_id=notification.service_id)
response = client.get(
path='/v2/notifications?older_than={}'.format(notification.id),
headers=[('Content-Type', 'application/json'), auth_header])
json_response = json.loads(response.get_data(as_text=True))
assert response.status_code == 200
assert response.headers['Content-type'] == "application/json"
assert json_response['links']['current'].endswith("/v2/notifications?older_than={}".format(notification.id))
assert 'next' not in json_response['links'].keys()
assert len(json_response['notifications']) == 0
def test_get_all_notifications_filter_multiple_query_parameters(client, notify_db, notify_db_session):
email_template = create_sample_template(notify_db, notify_db_session, template_type="email")
# this is the notification we are looking for
older_notification = create_sample_notification(
notify_db, notify_db_session, template=email_template, status="pending")
# wrong status
create_sample_notification(notify_db, notify_db_session, template=email_template)
# wrong template
create_sample_notification(notify_db, notify_db_session, status="pending")
# we only want notifications created before this one
newer_notification = create_sample_notification(notify_db, notify_db_session)
# this notification was created too recently
create_sample_notification(notify_db, notify_db_session, template=email_template, status="pending")
auth_header = create_authorization_header(service_id=newer_notification.service_id)
response = client.get(
path='/v2/notifications?status=pending&template_type=email&older_than={}'.format(newer_notification.id),
headers=[('Content-Type', 'application/json'), auth_header])
json_response = json.loads(response.get_data(as_text=True))
assert response.status_code == 200
assert response.headers['Content-type'] == "application/json"
# query parameters aren't returned in order
for url_part in [
"/v2/notifications?",
"template_type=email",
"status=pending",
"older_than={}".format(newer_notification.id)
]:
assert url_part in json_response['links']['current']
assert 'next' in json_response['links'].keys()
assert len(json_response['notifications']) == 1
assert json_response['notifications'][0]['id'] == str(older_notification.id)