clean flake8 except provider code

This commit is contained in:
stvnrlly
2022-10-19 16:16:26 +00:00
parent 65f15b21b0
commit e9fdfd59f4
35 changed files with 178 additions and 166 deletions
+4 -1
View File
@@ -18,7 +18,10 @@ from sqlalchemy.orm.exc import NoResultFound
from app.serialised_models import SerialisedService from app.serialised_models import SerialisedService
GENERAL_TOKEN_ERROR_MESSAGE = 'Invalid token: make sure your API token matches the example at https://docs.notifications.service.gov.uk/rest-api.html#authorisation-header' # nosec B105 GENERAL_TOKEN_ERROR_MESSAGE = '''
Invalid token: make sure your API token matches the example
at https://docs.notifications.service.gov.uk/rest-api.html#authorisation-header
''' # nosec B105
AUTH_DB_CONNECTION_DURATION_SECONDS = Histogram( AUTH_DB_CONNECTION_DURATION_SECONDS = Histogram(
'auth_db_connection_duration_seconds', 'auth_db_connection_duration_seconds',
+10 -3
View File
@@ -10,18 +10,25 @@ default_access_key = os.environ.get('AWS_ACCESS_KEY_ID')
default_secret_key = os.environ.get('AWS_SECRET_ACCESS_KEY') default_secret_key = os.environ.get('AWS_SECRET_ACCESS_KEY')
default_region = os.environ.get('AWS_REGION') default_region = os.environ.get('AWS_REGION')
def get_s3_file(bucket_name, file_location, access_key=default_access_key, secret_key=default_secret_key, region=default_region):
def get_s3_file(
bucket_name, file_location, access_key=default_access_key, secret_key=default_secret_key, region=default_region
):
s3_file = get_s3_object(bucket_name, file_location, access_key, secret_key, region) s3_file = get_s3_object(bucket_name, file_location, access_key, secret_key, region)
return s3_file.get()['Body'].read().decode('utf-8') return s3_file.get()['Body'].read().decode('utf-8')
def get_s3_object(bucket_name, file_location, access_key=default_access_key, secret_key=default_secret_key, region=default_region): def get_s3_object(
bucket_name, file_location, access_key=default_access_key, secret_key=default_secret_key, region=default_region
):
session = Session(aws_access_key_id=access_key, aws_secret_access_key=secret_key, region_name=region) session = Session(aws_access_key_id=access_key, aws_secret_access_key=secret_key, region_name=region)
s3 = session.resource('s3') s3 = session.resource('s3')
return s3.Object(bucket_name, file_location) return s3.Object(bucket_name, file_location)
def file_exists(bucket_name, file_location, access_key=default_access_key, secret_key=default_secret_key, region=default_region): def file_exists(
bucket_name, file_location, access_key=default_access_key, secret_key=default_secret_key, region=default_region
):
try: try:
# try and access metadata of object # try and access metadata of object
get_s3_object(bucket_name, file_location, access_key, secret_key, region).metadata get_s3_object(bucket_name, file_location, access_key, secret_key, region).metadata
+20 -8
View File
@@ -29,7 +29,10 @@ def process_ses_results(self, response):
ses_message = json.loads(response["Message"]) ses_message = json.loads(response["Message"])
notification_type = ses_message["notificationType"] notification_type = ses_message["notificationType"]
# TODO remove after smoke testing on prod is implemented # TODO remove after smoke testing on prod is implemented
current_app.logger.info(f"Attempting to process SES delivery status message from SNS with type: {notification_type} and body: {ses_message}") current_app.logger.info(
f"Attempting to process SES delivery status message \
from SNS with type: {notification_type} and body: {ses_message}"
)
bounce_message = None bounce_message = None
if notification_type == 'Bounce': if notification_type == 'Bounce':
@@ -49,13 +52,16 @@ def process_ses_results(self, response):
message_time = iso8601.parse_date(ses_message["mail"]["timestamp"]).replace(tzinfo=None) message_time = iso8601.parse_date(ses_message["mail"]["timestamp"]).replace(tzinfo=None)
if datetime.utcnow() - message_time < timedelta(minutes=5): if datetime.utcnow() - message_time < timedelta(minutes=5):
current_app.logger.info( current_app.logger.info(
f"notification not found for reference: {reference} (while attempting update to {notification_status}). " f"notification not found for reference: {reference} \
f"Callback may have arrived before notification was persisted to the DB. Adding task to retry queue" (while attempting update to {notification_status}). "
f"Callback may have arrived before notification was \
persisted to the DB. Adding task to retry queue"
) )
self.retry(queue=QueueNames.RETRY) self.retry(queue=QueueNames.RETRY)
else: else:
current_app.logger.warning( current_app.logger.warning(
"notification not found for reference: {} (while attempting update to {})".format(reference, notification_status) "notification not found for reference: {} (while \
attempting update to {})".format(reference, notification_status)
) )
return return
@@ -102,6 +108,7 @@ def process_ses_results(self, response):
current_app.logger.exception("Error processing SES results: {}".format(type(e))) current_app.logger.exception("Error processing SES results: {}".format(type(e)))
self.retry(queue=QueueNames.RETRY) self.retry(queue=QueueNames.RETRY)
def determine_notification_bounce_type(ses_message): def determine_notification_bounce_type(ses_message):
notification_type = ses_message["notificationType"] notification_type = ses_message["notificationType"]
if notification_type in ["Delivery", "Complaint"]: if notification_type in ["Delivery", "Complaint"]:
@@ -116,14 +123,16 @@ def determine_notification_bounce_type(ses_message):
return "Permanent" return "Permanent"
return "Temporary" return "Temporary"
def determine_notification_type(ses_message): def determine_notification_type(ses_message):
notification_type = ses_message["notificationType"] notification_type = ses_message["notificationType"]
if notification_type not in ["Bounce","Complaint","Delivery"]: if notification_type not in ["Bounce", "Complaint", "Delivery"]:
raise KeyError(f"Unhandled sns notification type {notification_type}") raise KeyError(f"Unhandled sns notification type {notification_type}")
if notification_type == 'Bounce': if notification_type == 'Bounce':
return determine_notification_bounce_type(ses_message) return determine_notification_bounce_type(ses_message)
return notification_type return notification_type
def _determine_provider_response(ses_message): def _determine_provider_response(ses_message):
if ses_message["notificationType"] != "Bounce": if ses_message["notificationType"] != "Bounce":
return None return None
@@ -175,7 +184,9 @@ def get_aws_responses(ses_message):
def handle_complaint(ses_message): def handle_complaint(ses_message):
recipient_email = remove_emails_from_complaint(ses_message)[0] recipient_email = remove_emails_from_complaint(ses_message)[0]
current_app.logger.info("Complaint from SES: \n{}".format(json.dumps(ses_message).replace("{", "(").replace("}", ")"))) current_app.logger.info(
"Complaint from SES: \n{}".format(json.dumps(ses_message).replace("{", "(").replace("}", ")"))
)
try: try:
reference = ses_message["mail"]["messageId"] reference = ses_message["mail"]["messageId"]
except KeyError as e: except KeyError as e:
@@ -219,7 +230,9 @@ def check_and_queue_callback_task(notification):
service_callback_api = get_service_delivery_status_callback_api_for_service(service_id=notification.service_id) service_callback_api = get_service_delivery_status_callback_api_for_service(service_id=notification.service_id)
if service_callback_api: if service_callback_api:
notification_data = create_delivery_status_callback_data(notification, service_callback_api) notification_data = create_delivery_status_callback_data(notification, service_callback_api)
send_delivery_status_to_service.apply_async([str(notification.id), notification_data], queue=QueueNames.CALLBACKS) send_delivery_status_to_service.apply_async(
[str(notification.id), notification_data], queue=QueueNames.CALLBACKS
)
def _check_and_queue_complaint_callback_task(complaint, notification, recipient): def _check_and_queue_complaint_callback_task(complaint, notification, recipient):
@@ -228,4 +241,3 @@ def _check_and_queue_complaint_callback_task(complaint, notification, recipient)
if service_callback_api: if service_callback_api:
complaint_data = create_complaint_callback_data(complaint, notification, service_callback_api, recipient) complaint_data = create_complaint_callback_data(complaint, notification, service_callback_api, recipient)
send_complaint_to_service.apply_async([complaint_data], queue=QueueNames.CALLBACKS) send_complaint_to_service.apply_async([complaint_data], queue=QueueNames.CALLBACKS)
@@ -1,7 +1,6 @@
import uuid import uuid
from datetime import datetime from datetime import datetime
import pytest
from flask import current_app from flask import current_app
from notifications_utils.template import SMSMessageTemplate from notifications_utils.template import SMSMessageTemplate
+1 -1
View File
@@ -114,7 +114,7 @@ def create_delivery_status_callback_data(notification, service_callback_api):
"notification_client_reference": notification.client_reference, "notification_client_reference": notification.client_reference,
"notification_to": notification.to, "notification_to": notification.to,
"notification_status": notification.status, "notification_status": notification.status,
"notification_provider_response": notification.provider_response, # TODO do we have a test for provider_response "notification_provider_response": notification.provider_response, # TODO do we test for provider_response?
"notification_created_at": notification.created_at.strftime(DATETIME_FORMAT), "notification_created_at": notification.created_at.strftime(DATETIME_FORMAT),
"notification_updated_at": "notification_updated_at":
notification.updated_at.strftime(DATETIME_FORMAT) if notification.updated_at else None, notification.updated_at.strftime(DATETIME_FORMAT) if notification.updated_at else None,
-3
View File
@@ -1,6 +1,3 @@
from celery import current_app
class ClientException(Exception): class ClientException(Exception):
''' '''
Base Exceptions for sending notifications that fail Base Exceptions for sending notifications that fail
+9 -4
View File
@@ -8,16 +8,20 @@ def find_by_service_name(services, service_name):
return services[i] return services[i]
return None return None
def extract_cloudfoundry_config(): def extract_cloudfoundry_config():
vcap_services = json.loads(os.environ['VCAP_SERVICES']) vcap_services = json.loads(os.environ['VCAP_SERVICES'])
# Postgres config # Postgres config
os.environ['SQLALCHEMY_DATABASE_URI'] = vcap_services['aws-rds'][0]['credentials']['uri'].replace('postgres','postgresql') os.environ['SQLALCHEMY_DATABASE_URI'] = \
vcap_services['aws-rds'][0]['credentials']['uri'].replace('postgres', 'postgresql')
# Redis config # Redis config
os.environ['REDIS_URL'] = vcap_services['aws-elasticache-redis'][0]['credentials']['uri'].replace('redis://','rediss://') os.environ['REDIS_URL'] = \
vcap_services['aws-elasticache-redis'][0]['credentials']['uri'].replace('redis://', 'rediss://')
# CSV Upload Bucket Name # CSV Upload Bucket Name
bucket_service = find_by_service_name(vcap_services['s3'], f"notifications-api-csv-upload-bucket-{os.environ['DEPLOY_ENV']}") bucket_service = \
find_by_service_name(vcap_services['s3'], f"notifications-api-csv-upload-bucket-{os.environ['DEPLOY_ENV']}")
if bucket_service: if bucket_service:
os.environ['CSV_UPLOAD_BUCKET_NAME'] = bucket_service['credentials']['bucket'] os.environ['CSV_UPLOAD_BUCKET_NAME'] = bucket_service['credentials']['bucket']
os.environ['CSV_UPLOAD_ACCESS_KEY'] = bucket_service['credentials']['access_key_id'] os.environ['CSV_UPLOAD_ACCESS_KEY'] = bucket_service['credentials']['access_key_id']
@@ -25,7 +29,8 @@ def extract_cloudfoundry_config():
os.environ['CSV_UPLOAD_REGION'] = bucket_service['credentials']['region'] os.environ['CSV_UPLOAD_REGION'] = bucket_service['credentials']['region']
# Contact List Bucket Name # Contact List Bucket Name
bucket_service = find_by_service_name(vcap_services['s3'], f"notifications-api-contact-list-bucket-{os.environ['DEPLOY_ENV']}") bucket_service = \
find_by_service_name(vcap_services['s3'], f"notifications-api-contact-list-bucket-{os.environ['DEPLOY_ENV']}")
if bucket_service: if bucket_service:
os.environ['CONTACT_LIST_BUCKET_NAME'] = bucket_service['credentials']['bucket'] os.environ['CONTACT_LIST_BUCKET_NAME'] = bucket_service['credentials']['bucket']
os.environ['CONTACT_LIST_ACCESS_KEY'] = bucket_service['credentials']['access_key_id'] os.environ['CONTACT_LIST_ACCESS_KEY'] = bucket_service['credentials']['access_key_id']
-80
View File
@@ -142,86 +142,6 @@ def purge_functional_test_data(user_email_prefix):
delete_model_user(usr) delete_model_user(usr)
@notify_command()
def backfill_notification_statuses():
"""
DEPRECATED. Populates notification_status.
This will be used to populate the new `Notification._status_fkey` with the old
`Notification._status_enum`
"""
LIMIT = 250000
subq = "SELECT id FROM notification_history WHERE notification_status is NULL LIMIT {}".format(LIMIT) # nosec B608 no user-controlled input
update = "UPDATE notification_history SET notification_status = status WHERE id in ({})".format(subq) # nosec B608 no user-controlled input
result = db.session.execute(subq).fetchall()
while len(result) > 0:
db.session.execute(update)
print('commit {} updates at {}'.format(LIMIT, datetime.utcnow()))
db.session.commit()
result = db.session.execute(subq).fetchall()
@notify_command()
def update_notification_international_flag():
"""
DEPRECATED. Set notifications.international=false.
"""
# 250,000 rows takes 30 seconds to update.
subq = "select id from notifications where international is null limit 250000"
update = "update notifications set international = False where id in ({})".format(subq) # nosec B608 no user-controlled input
result = db.session.execute(subq).fetchall()
while len(result) > 0:
db.session.execute(update)
print('commit 250000 updates at {}'.format(datetime.utcnow()))
db.session.commit()
result = db.session.execute(subq).fetchall()
# Now update notification_history
subq_history = "select id from notification_history where international is null limit 250000"
update_history = "update notification_history set international = False where id in ({})".format(subq_history) # nosec B608 no user-controlled input
result_history = db.session.execute(subq_history).fetchall()
while len(result_history) > 0:
db.session.execute(update_history)
print('commit 250000 updates at {}'.format(datetime.utcnow()))
db.session.commit()
result_history = db.session.execute(subq_history).fetchall()
@notify_command()
def fix_notification_statuses_not_in_sync():
"""
DEPRECATED.
This will be used to correct an issue where Notification._status_enum and NotificationHistory._status_fkey
became out of sync. See 979e90a.
Notification._status_enum is the source of truth so NotificationHistory._status_fkey will be updated with
these values.
"""
MAX = 10000
subq = "SELECT id FROM notifications WHERE cast (status as text) != notification_status LIMIT {}".format(MAX) # nosec B608 no user-controlled input
update = "UPDATE notifications SET notification_status = status WHERE id in ({})".format(subq) # nosec B608 no user-controlled input
result = db.session.execute(subq).fetchall()
while len(result) > 0:
db.session.execute(update)
print('Committed {} updates at {}'.format(len(result), datetime.utcnow()))
db.session.commit()
result = db.session.execute(subq).fetchall()
subq_hist = "SELECT id FROM notification_history WHERE cast (status as text) != notification_status LIMIT {}".format(MAX) # nosec B608
update = "UPDATE notification_history SET notification_status = status WHERE id in ({})".format(subq_hist) # nosec B608 no user-controlled input
result = db.session.execute(subq_hist).fetchall()
while len(result) > 0:
db.session.execute(update)
print('Committed {} updates at {}'.format(len(result), datetime.utcnow()))
db.session.commit()
result = db.session.execute(subq_hist).fetchall()
@notify_command(name='insert-inbound-numbers') @notify_command(name='insert-inbound-numbers')
@click.option('-f', '--file_name', required=True, @click.option('-f', '--file_name', required=True,
help="""Full path of the file to upload, file is a contains inbound numbers, help="""Full path of the file to upload, file is a contains inbound numbers,
+16 -4
View File
@@ -448,7 +448,10 @@ class Development(Config):
NOTIFY_EMAIL_DOMAIN = os.getenv('NOTIFY_EMAIL_DOMAIN', 'notify.sandbox.10x.gsa.gov') NOTIFY_EMAIL_DOMAIN = os.getenv('NOTIFY_EMAIL_DOMAIN', 'notify.sandbox.10x.gsa.gov')
SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_DATABASE_URI', 'postgresql://postgres:chummy@db:5432/notification_api') SQLALCHEMY_DATABASE_URI = os.environ.get(
'SQLALCHEMY_DATABASE_URI',
'postgresql://postgres:chummy@db:5432/notification_api'
)
ANTIVIRUS_ENABLED = os.environ.get('ANTIVIRUS_ENABLED') == '1' ANTIVIRUS_ENABLED = os.environ.get('ANTIVIRUS_ENABLED') == '1'
@@ -486,7 +489,10 @@ class Test(Development):
# LETTER_SANITISE_BUCKET_NAME = 'test-letters-sanitise' # LETTER_SANITISE_BUCKET_NAME = 'test-letters-sanitise'
# this is overriden in CI # this is overriden in CI
SQLALCHEMY_DATABASE_URI = os.getenv('SQLALCHEMY_DATABASE_TEST_URI', 'postgresql://postgres:chummy@db:5432/test_notification_api') SQLALCHEMY_DATABASE_URI = os.getenv(
'SQLALCHEMY_DATABASE_TEST_URI',
'postgresql://postgres:chummy@db:5432/test_notification_api'
)
CELERY = { CELERY = {
**Config.CELERY, **Config.CELERY,
@@ -546,11 +552,17 @@ class Staging(Config):
class Live(Config): class Live(Config):
NOTIFY_ENVIRONMENT = 'live' NOTIFY_ENVIRONMENT = 'live'
# buckets # buckets
CSV_UPLOAD_BUCKET_NAME = os.environ.get('CSV_UPLOAD_BUCKET_NAME', 'notifications-prototype-csv-upload') # created in gsa sandbox CSV_UPLOAD_BUCKET_NAME = os.environ.get(
'CSV_UPLOAD_BUCKET_NAME',
'notifications-prototype-csv-upload'
) # created in gsa sandbox
CSV_UPLOAD_ACCESS_KEY = os.environ.get('CSV_UPLOAD_ACCESS_KEY') CSV_UPLOAD_ACCESS_KEY = os.environ.get('CSV_UPLOAD_ACCESS_KEY')
CSV_UPLOAD_SECRET_KEY = os.environ.get('CSV_UPLOAD_SECRET_KEY') CSV_UPLOAD_SECRET_KEY = os.environ.get('CSV_UPLOAD_SECRET_KEY')
CSV_UPLOAD_REGION = os.environ.get('CSV_UPLOAD_REGION') CSV_UPLOAD_REGION = os.environ.get('CSV_UPLOAD_REGION')
CONTACT_LIST_BUCKET_NAME = os.environ.get('CONTACT_LIST_BUCKET_NAME', 'notifications-prototype-contact-list-upload') # created in gsa sandbox CONTACT_LIST_BUCKET_NAME = os.environ.get(
'CONTACT_LIST_BUCKET_NAME',
'notifications-prototype-contact-list-upload'
) # created in gsa sandbox
CONTACT_LIST_ACCESS_KEY = os.environ.get('CONTACT_LIST_ACCESS_KEY') CONTACT_LIST_ACCESS_KEY = os.environ.get('CONTACT_LIST_ACCESS_KEY')
CONTACT_LIST_SECRET_KEY = os.environ.get('CONTACT_LIST_SECRET_KEY') CONTACT_LIST_SECRET_KEY = os.environ.get('CONTACT_LIST_SECRET_KEY')
CONTACT_LIST_REGION = os.environ.get('CONTACT_LIST_REGION') CONTACT_LIST_REGION = os.environ.get('CONTACT_LIST_REGION')
+2
View File
@@ -87,6 +87,7 @@ def country_records_delivery(phone_prefix):
dlr = INTERNATIONAL_BILLING_RATES[phone_prefix]['attributes']['dlr'] dlr = INTERNATIONAL_BILLING_RATES[phone_prefix]['attributes']['dlr']
return dlr and dlr.lower() == 'yes' return dlr and dlr.lower() == 'yes'
def _decide_permanent_temporary_failure(current_status, status): def _decide_permanent_temporary_failure(current_status, status):
# If we go from pending to delivered we need to set failure type as temporary-failure # If we go from pending to delivered we need to set failure type as temporary-failure
if current_status == NOTIFICATION_PENDING and status == NOTIFICATION_PERMANENT_FAILURE: if current_status == NOTIFICATION_PENDING and status == NOTIFICATION_PERMANENT_FAILURE:
@@ -102,6 +103,7 @@ def _update_notification_status(notification, status, provider_response=None):
dao_update_notification(notification) dao_update_notification(notification)
return notification return notification
@autocommit @autocommit
def update_notification_status_by_id(notification_id, status, sent_by=None, detailed_status_code=None): def update_notification_status_by_id(notification_id, status, sent_by=None, detailed_status_code=None):
notification = Notification.query.with_for_update().filter(Notification.id == notification_id).first() notification = Notification.query.with_for_update().filter(Notification.id == notification_id).first()
+4 -2
View File
@@ -178,7 +178,9 @@ def provider_to_use(notification_type, international=True):
international = False # TODO: remove or resolve the functionality of this flag international = False # TODO: remove or resolve the functionality of this flag
# TODO rip firetext and mmg out of early migrations and clean up the expression below # TODO rip firetext and mmg out of early migrations and clean up the expression below
active_providers = [ active_providers = [
p for p in get_provider_details_by_notification_type(notification_type, international) if p.active and p.identifier not in ['firetext','mmg'] p for p in get_provider_details_by_notification_type(
notification_type, international
) if p.active and p.identifier not in ['firetext', 'mmg']
] ]
if not active_providers: if not active_providers:
@@ -191,7 +193,7 @@ def provider_to_use(notification_type, international=True):
chosen_provider = active_providers[0] chosen_provider = active_providers[0]
else: else:
weights = [p.priority for p in active_providers] weights = [p.priority for p in active_providers]
chosen_provider = random.choices(active_providers, weights=weights)[0] # nosec B311 - this is not security/cryptography related chosen_provider = random.choices(active_providers, weights=weights)[0] # nosec B311 - not sec/crypto related
return notification_provider_clients.get_client_by_name_and_type(chosen_provider.identifier, notification_type) return notification_provider_clients.get_client_by_name_and_type(chosen_provider.identifier, notification_type)
+1 -1
View File
@@ -1,5 +1,5 @@
from flask import Blueprint, jsonify, request from flask import Blueprint, jsonify, request
from notifications_utils.recipients import try_validate_and_format_phone_number # from notifications_utils.recipients import try_validate_and_format_phone_number
from app.dao.inbound_sms_dao import ( from app.dao.inbound_sms_dao import (
dao_count_inbound_sms_for_service, dao_count_inbound_sms_for_service,
+5 -3
View File
@@ -122,7 +122,9 @@ class User(db.Model):
state = db.Column(db.String, nullable=False, default='pending') state = db.Column(db.String, nullable=False, default='pending')
platform_admin = db.Column(db.Boolean, nullable=False, default=False) platform_admin = db.Column(db.Boolean, nullable=False, default=False)
current_session_id = db.Column(UUID(as_uuid=True), nullable=True) current_session_id = db.Column(UUID(as_uuid=True), nullable=True)
auth_type = db.Column(db.String, db.ForeignKey('auth_type.name'), index=True, nullable=False, default=EMAIL_AUTH_TYPE) auth_type = db.Column(
db.String, db.ForeignKey('auth_type.name'), index=True, nullable=False, default=EMAIL_AUTH_TYPE
)
email_access_validated_at = db.Column( email_access_validated_at = db.Column(
db.DateTime, index=False, unique=False, nullable=False, default=datetime.datetime.utcnow db.DateTime, index=False, unique=False, nullable=False, default=datetime.datetime.utcnow
) )
@@ -608,7 +610,7 @@ class AnnualBilling(db.Model):
"name": self.service.name "name": self.service.name
} }
return{ return {
"id": str(self.id), "id": str(self.id),
'free_sms_fragment_limit': self.free_sms_fragment_limit, 'free_sms_fragment_limit': self.free_sms_fragment_limit,
'service_id': self.service_id, 'service_id': self.service_id,
@@ -1645,7 +1647,7 @@ class Notification(db.Model):
""" """
# this should only ever be called for letter notifications - it makes no sense otherwise and I'd rather not # this should only ever be called for letter notifications - it makes no sense otherwise and I'd rather not
# get the two code flows mixed up at all # get the two code flows mixed up at all
assert self.notification_type == LETTER_TYPE # nosec B101 - current calling code already validates the correct type assert self.notification_type == LETTER_TYPE # nosec B101 - current calling code validates correct type
if self.status in [NOTIFICATION_CREATED, NOTIFICATION_SENDING]: if self.status in [NOTIFICATION_CREATED, NOTIFICATION_SENDING]:
return NOTIFICATION_STATUS_LETTER_ACCEPTED return NOTIFICATION_STATUS_LETTER_ACCEPTED
@@ -10,6 +10,7 @@ from app.notifications.sns_handlers import sns_notification_handler
ses_callback_blueprint = Blueprint('notifications_ses_callback', __name__) ses_callback_blueprint = Blueprint('notifications_ses_callback', __name__)
DEFAULT_MAX_AGE = timedelta(days=10000) DEFAULT_MAX_AGE = timedelta(days=10000)
# 400 counts as a permanent failure so SNS will not retry. # 400 counts as a permanent failure so SNS will not retry.
# 500 counts as a failed delivery attempt so SNS will retry. # 500 counts as a failed delivery attempt so SNS will retry.
# See https://docs.aws.amazon.com/sns/latest/dg/DeliveryPolicies.html#DeliveryPolicies # See https://docs.aws.amazon.com/sns/latest/dg/DeliveryPolicies.html#DeliveryPolicies
@@ -1,10 +1,10 @@
from flask import Blueprint, json, jsonify, request from flask import Blueprint # , json, jsonify, request
# from app.celery.process_sms_client_response_tasks import ( # from app.celery.process_sms_client_response_tasks import (
# process_sms_client_response, # process_sms_client_response,
# ) # )
from app.config import QueueNames # from app.config import QueueNames
from app.errors import InvalidRequest, register_errors from app.errors import register_errors
sms_callback_blueprint = Blueprint("sms_callback", __name__, url_prefix="/notifications/sms") sms_callback_blueprint = Blueprint("sms_callback", __name__, url_prefix="/notifications/sms")
register_errors(sms_callback_blueprint) register_errors(sms_callback_blueprint)
+6 -3
View File
@@ -24,6 +24,7 @@ INBOUND_SMS_COUNTER = Counter(
['provider'] ['provider']
) )
@receive_notifications_blueprint.route('/notifications/sms/receive/sns', methods=['POST']) @receive_notifications_blueprint.route('/notifications/sms/receive/sns', methods=['POST'])
def receive_sns_sms(): def receive_sns_sms():
""" """
@@ -53,13 +54,16 @@ def receive_sns_sms():
# TODO wrap this up # TODO wrap this up
if "inboundMessageId" in message: if "inboundMessageId" in message:
# TODO use standard formatting we use for all US numbers # TODO use standard formatting we use for all US numbers
inbound_number = message['destinationNumber'].replace('+','') inbound_number = message['destinationNumber'].replace('+', '')
service = fetch_potential_service(inbound_number, 'sns') service = fetch_potential_service(inbound_number, 'sns')
if not service: if not service:
# since this is an issue with our service <-> number mapping, or no inbound_sms service permission # since this is an issue with our service <-> number mapping, or no inbound_sms service permission
# we should still tell SNS that we received it successfully # we should still tell SNS that we received it successfully
current_app.logger.warning(f"Mapping between service and inbound number: {inbound_number} is broken, or service does not have permission to receive inbound sms") current_app.logger.warning(
f"Mapping between service and inbound number: {inbound_number} is broken, \
or service does not have permission to receive inbound sms"
)
return jsonify( return jsonify(
result="success", message="SMS-SNS callback succeeded" result="success", message="SMS-SNS callback succeeded"
), 200 ), 200
@@ -79,7 +83,6 @@ def receive_sns_sms():
date_received=date_received, date_received=date_received,
provider_name=provider_name) provider_name=provider_name)
# TODO ensure inbound sms callback endpoints are accessible and functioning for notify api users, then uncomment the task below
tasks.send_inbound_sms_to_service.apply_async([str(inbound.id), str(service.id)], queue=QueueNames.NOTIFY) tasks.send_inbound_sms_to_service.apply_async([str(inbound.id), str(service.id)], queue=QueueNames.NOTIFY)
current_app.logger.debug( current_app.logger.debug(
+4 -2
View File
@@ -19,6 +19,7 @@ _cert_url_re = re.compile(
r'sns\.([a-z]{1,3}-[a-z]+-[0-9]{1,2})\.amazonaws\.com', r'sns\.([a-z]{1,3}-[a-z]+-[0-9]{1,2})\.amazonaws\.com',
) )
class ValidationError(Exception): class ValidationError(Exception):
""" """
ValidationError. Raised when a message fails integrity checks. ValidationError. Raised when a message fails integrity checks.
@@ -56,7 +57,7 @@ def get_string_to_sign(sns_payload):
for field in fields: for field in fields:
field_value = sns_payload.get(field) field_value = sns_payload.get(field)
if not isinstance(field_value, str): if not isinstance(field_value, str):
if field == 'Subject' and field_value == None: if field == 'Subject' and field_value is None:
continue continue
raise ValidationError(f"In {field}, found non-string value: {field_value}") raise ValidationError(f"In {field}, found non-string value: {field_value}")
string_to_sign += field + '\n' + field_value + '\n' string_to_sign += field + '\n' + field_value + '\n'
@@ -83,7 +84,8 @@ def validate_sns_cert(sns_payload):
string_to_sign = get_string_to_sign(sns_payload) string_to_sign = get_string_to_sign(sns_payload)
# Key signing cert url via Lambda and via webhook are slightly different # Key signing cert url via Lambda and via webhook are slightly different
signing_cert_url = sns_payload.get('SigningCertUrl') if 'SigningCertUrl' in sns_payload else sns_payload.get('SigningCertURL') signing_cert_url = sns_payload.get('SigningCertUrl') if 'SigningCertUrl' in \
sns_payload else sns_payload.get('SigningCertURL')
if not isinstance(signing_cert_url, str): if not isinstance(signing_cert_url, str):
raise ValidationError("Signing cert url must be a string") raise ValidationError("Signing cert url must be a string")
cert_scheme, cert_netloc, *_ = urlparse(signing_cert_url) cert_scheme, cert_netloc, *_ = urlparse(signing_cert_url)
+11 -3
View File
@@ -45,7 +45,9 @@ def sns_notification_handler(data, headers):
try: try:
validate_sns_cert(message) validate_sns_cert(message)
except Exception as e: except Exception as e:
current_app.logger.error(f"SES-SNS callback failed: validation failed with error: Signature validation failed with error {e}") current_app.logger.error(
f"SES-SNS callback failed: validation failed with error: Signature validation failed with error {e}"
)
raise InvalidRequest("SES-SNS callback failed: validation failed", 400) raise InvalidRequest("SES-SNS callback failed: validation failed", 400)
if message.get('Type') == 'SubscriptionConfirmation': if message.get('Type') == 'SubscriptionConfirmation':
@@ -55,8 +57,14 @@ def sns_notification_handler(data, headers):
try: try:
response.raise_for_status() response.raise_for_status()
except Exception as e: except Exception as e:
current_app.logger.warning(f"Attempt to raise_for_status()SubscriptionConfirmation Type message files for response: {response.text} with error {e}") current_app.logger.warning(
raise InvalidRequest("SES-SNS callback failed: attempt to raise_for_status()SubscriptionConfirmation Type message failed", 400) f"Attempt to raise_for_status()SubscriptionConfirmation Type \
message files for response: {response.text} with error {e}"
)
raise InvalidRequest(
"SES-SNS callback failed: attempt to raise_for_status()SubscriptionConfirmation \
Type message failed", 400
)
current_app.logger.info("SES-SNS auto-confirm subscription callback succeeded") current_app.logger.info("SES-SNS auto-confirm subscription callback succeeded")
return message return message
@@ -87,7 +87,7 @@ def test_notifications_ses_200_autoconfirms_subscription(client, mocker):
def test_notifications_ses_200_call_process_task(client, mocker): def test_notifications_ses_200_call_process_task(client, mocker):
process_mock = mocker.patch("app.notifications.notifications_ses_callback.process_ses_results.apply_async") process_mock = mocker.patch("app.notifications.notifications_ses_callback.process_ses_results.apply_async")
mocker.patch("app.notifications.sns_handlers.validate_sns_cert", return_value=True) mocker.patch("app.notifications.sns_handlers.validate_sns_cert", return_value=True)
data = {"Type": "Notification", "foo": "bar", "Message": {"mail": "baz"} } data = {"Type": "Notification", "foo": "bar", "Message": {"mail": "baz"}}
mocker.patch("app.notifications.sns_handlers.sns_notification_handler", return_value=data) mocker.patch("app.notifications.sns_handlers.sns_notification_handler", return_value=data)
json_data = json.dumps(data) json_data = json.dumps(data)
response = client.post( response = client.post(
@@ -156,7 +156,10 @@ def test_ses_callback_should_update_notification_status(
status='sending', status='sending',
sent_at=datetime.utcnow() sent_at=datetime.utcnow()
) )
callback_api = create_service_callback_api(service=sample_email_template.service, url="https://original_url.com") callback_api = create_service_callback_api(
service=sample_email_template.service,
url="https://original_url.com"
)
assert get_notification_by_id(notification.id).status == 'sending' assert get_notification_by_id(notification.id).status == 'sending'
assert process_ses_results(ses_notification_callback(reference='ref')) assert process_ses_results(ses_notification_callback(reference='ref'))
assert get_notification_by_id(notification.id).status == 'delivered' assert get_notification_by_id(notification.id).status == 'delivered'
@@ -186,13 +189,19 @@ def test_ses_callback_should_retry_if_notification_is_new(mocker):
assert process_ses_results(ses_notification_callback(reference='ref')) is None assert process_ses_results(ses_notification_callback(reference='ref')) is None
assert mock_logger.call_count == 0 assert mock_logger.call_count == 0
assert mock_retry.call_count == 1 assert mock_retry.call_count == 1
def test_ses_callback_should_log_if_notification_is_missing(client, _notify_db, mocker): def test_ses_callback_should_log_if_notification_is_missing(client, _notify_db, mocker):
mock_retry = mocker.patch('app.celery.process_ses_receipts_tasks.process_ses_results.retry') mock_retry = mocker.patch('app.celery.process_ses_receipts_tasks.process_ses_results.retry')
mock_logger = mocker.patch('app.celery.process_ses_receipts_tasks.current_app.logger.warning') mock_logger = mocker.patch('app.celery.process_ses_receipts_tasks.current_app.logger.warning')
with freeze_time('2017-11-17T12:34:03.646Z'): with freeze_time('2017-11-17T12:34:03.646Z'):
assert process_ses_results(ses_notification_callback(reference='ref')) is None assert process_ses_results(ses_notification_callback(reference='ref')) is None
assert mock_retry.call_count == 0 assert mock_retry.call_count == 0
mock_logger.assert_called_once_with('notification not found for reference: ref (while attempting update to delivered)') mock_logger.assert_called_once_with(
'notification not found for reference: ref (while attempting update to delivered)'
)
def test_ses_callback_should_not_retry_if_notification_is_old(mocker): def test_ses_callback_should_not_retry_if_notification_is_old(mocker):
mock_retry = mocker.patch('app.celery.process_ses_receipts_tasks.process_ses_results.retry') mock_retry = mocker.patch('app.celery.process_ses_receipts_tasks.process_ses_results.retry')
mock_logger = mocker.patch('app.celery.process_ses_receipts_tasks.current_app.logger.error') mock_logger = mocker.patch('app.celery.process_ses_receipts_tasks.current_app.logger.error')
@@ -200,6 +209,8 @@ def test_ses_callback_should_not_retry_if_notification_is_old(mocker):
assert process_ses_results(ses_notification_callback(reference='ref')) is None assert process_ses_results(ses_notification_callback(reference='ref')) is None
assert mock_logger.call_count == 0 assert mock_logger.call_count == 0
assert mock_retry.call_count == 0 assert mock_retry.call_count == 0
def test_ses_callback_does_not_call_send_delivery_status_if_no_db_entry( def test_ses_callback_does_not_call_send_delivery_status_if_no_db_entry(
client, client,
_notify_db, _notify_db,
@@ -222,6 +233,8 @@ def test_ses_callback_does_not_call_send_delivery_status_if_no_db_entry(
assert process_ses_results(ses_notification_callback(reference='ref')) assert process_ses_results(ses_notification_callback(reference='ref'))
assert get_notification_by_id(notification.id).status == 'delivered' assert get_notification_by_id(notification.id).status == 'delivered'
send_mock.assert_not_called() send_mock.assert_not_called()
def test_ses_callback_should_update_multiple_notification_status_sent( def test_ses_callback_should_update_multiple_notification_status_sent(
client, client,
_notify_db, _notify_db,
@@ -257,6 +270,8 @@ def test_ses_callback_should_update_multiple_notification_status_sent(
assert process_ses_results(ses_notification_callback(reference='ref2')) assert process_ses_results(ses_notification_callback(reference='ref2'))
assert process_ses_results(ses_notification_callback(reference='ref3')) assert process_ses_results(ses_notification_callback(reference='ref3'))
assert send_mock.called assert send_mock.called
def test_ses_callback_should_set_status_to_temporary_failure(client, def test_ses_callback_should_set_status_to_temporary_failure(client,
_notify_db, _notify_db,
notify_db_session, notify_db_session,
@@ -278,6 +293,8 @@ def test_ses_callback_should_set_status_to_temporary_failure(client,
assert process_ses_results(ses_soft_bounce_callback(reference='ref')) assert process_ses_results(ses_soft_bounce_callback(reference='ref'))
assert get_notification_by_id(notification.id).status == 'temporary-failure' assert get_notification_by_id(notification.id).status == 'temporary-failure'
assert send_mock.called assert send_mock.called
def test_ses_callback_should_set_status_to_permanent_failure(client, def test_ses_callback_should_set_status_to_permanent_failure(client,
_notify_db, _notify_db,
notify_db_session, notify_db_session,
@@ -299,6 +316,8 @@ def test_ses_callback_should_set_status_to_permanent_failure(client,
assert process_ses_results(ses_hard_bounce_callback(reference='ref')) assert process_ses_results(ses_hard_bounce_callback(reference='ref'))
assert get_notification_by_id(notification.id).status == 'permanent-failure' assert get_notification_by_id(notification.id).status == 'permanent-failure'
assert send_mock.called assert send_mock.called
def test_ses_callback_should_send_on_complaint_to_user_callback_api(sample_email_template, mocker): def test_ses_callback_should_send_on_complaint_to_user_callback_api(sample_email_template, mocker):
send_mock = mocker.patch( send_mock = mocker.patch(
'app.celery.service_callback_tasks.send_complaint_to_service.apply_async' 'app.celery.service_callback_tasks.send_complaint_to_service.apply_async'
@@ -321,4 +340,3 @@ def test_ses_callback_should_send_on_complaint_to_user_callback_api(sample_email
'service_callback_api_url': 'https://original_url.com', 'service_callback_api_url': 'https://original_url.com',
'to': 'recipient1@example.com' 'to': 'recipient1@example.com'
} }
@@ -5,9 +5,9 @@ import pytest
from freezegun import freeze_time from freezegun import freeze_time
from app import statsd_client from app import statsd_client
# from app.celery.process_sms_client_response_tasks import ( from app.celery.process_sms_client_response_tasks import (
# process_sms_client_response, process_sms_client_response,
# ) )
from app.clients import ClientException from app.clients import ClientException
from app.models import NOTIFICATION_TECHNICAL_FAILURE from app.models import NOTIFICATION_TECHNICAL_FAILURE
+1
View File
@@ -126,6 +126,7 @@ def test_should_add_to_retry_queue_if_notification_not_found_in_deliver_email_ta
app.delivery.send_to_providers.send_email_to_provider.assert_not_called() app.delivery.send_to_providers.send_email_to_provider.assert_not_called()
app.celery.provider_tasks.deliver_email.retry.assert_called_with(queue="retry-tasks") app.celery.provider_tasks.deliver_email.retry.assert_called_with(queue="retry-tasks")
@pytest.mark.skip(reason="Needs updating for TTS: Failing for unknown reason") @pytest.mark.skip(reason="Needs updating for TTS: Failing for unknown reason")
@pytest.mark.parametrize( @pytest.mark.parametrize(
'exception_class', [ 'exception_class', [
+1
View File
@@ -290,6 +290,7 @@ def test_create_nightly_billing_for_day_different_sent_by(
assert record.billable_units == 1 assert record.billable_units == 1
assert record.rate_multiplier == 1.0 assert record.rate_multiplier == 1.0
@pytest.mark.skip(reason="Needs updating for TTS: Remove mail") @pytest.mark.skip(reason="Needs updating for TTS: Remove mail")
def test_create_nightly_billing_for_day_different_letter_postage( def test_create_nightly_billing_for_day_different_letter_postage(
notify_db_session, notify_db_session,
+2
View File
@@ -15,6 +15,7 @@ def fake_client(notify_api):
fake_client.init_app(notify_api, statsd_client) fake_client.init_app(notify_api, statsd_client)
return fake_client return fake_client
@pytest.mark.skip(reason="Needs updating for TTS: New SMS client") @pytest.mark.skip(reason="Needs updating for TTS: New SMS client")
def test_send_sms(fake_client, mocker): def test_send_sms(fake_client, mocker):
mock_send = mocker.patch.object(fake_client, 'try_send_sms') mock_send = mocker.patch.object(fake_client, 'try_send_sms')
@@ -31,6 +32,7 @@ def test_send_sms(fake_client, mocker):
'to', 'content', 'reference', False, 'testing' 'to', 'content', 'reference', False, 'testing'
) )
@pytest.mark.skip(reason="Needs updating for TTS: New SMS client") @pytest.mark.skip(reason="Needs updating for TTS: New SMS client")
def test_send_sms_error(fake_client, mocker): def test_send_sms_error(fake_client, mocker):
mocker.patch.object( mocker.patch.object(
@@ -144,6 +144,7 @@ def test_adjust_provider_priority_sets_priority(
assert mmg_provider.created_by.id == notify_user.id assert mmg_provider.created_by.id == notify_user.id
assert mmg_provider.priority == 50 assert mmg_provider.priority == 50
@pytest.mark.skip(reason="Needs updating for TTS: MMG removal") @pytest.mark.skip(reason="Needs updating for TTS: MMG removal")
@freeze_time('2016-01-01 00:30') @freeze_time('2016-01-01 00:30')
def test_adjust_provider_priority_adds_history( def test_adjust_provider_priority_adds_history(
@@ -172,6 +173,7 @@ def test_adjust_provider_priority_adds_history(
assert updated_provider_history_rows[0].version - old_provider_history_rows[0].version == 1 assert updated_provider_history_rows[0].version - old_provider_history_rows[0].version == 1
assert updated_provider_history_rows[0].priority == 50 assert updated_provider_history_rows[0].priority == 50
@pytest.mark.skip(reason="Needs updating for TTS: MMG removal") @pytest.mark.skip(reason="Needs updating for TTS: MMG removal")
@freeze_time('2016-01-01 01:00') @freeze_time('2016-01-01 01:00')
def test_get_sms_providers_for_update_returns_providers(restore_provider_details): def test_get_sms_providers_for_update_returns_providers(restore_provider_details):
@@ -43,6 +43,7 @@ def setup_function(_function):
# state of the cache is not shared between tests. # state of the cache is not shared between tests.
send_to_providers.provider_cache.clear() send_to_providers.provider_cache.clear()
@pytest.mark.skip(reason="Needs updating for TTS: Update with new providers") @pytest.mark.skip(reason="Needs updating for TTS: Update with new providers")
def test_provider_to_use_should_return_random_provider(mocker, notify_db_session): def test_provider_to_use_should_return_random_provider(mocker, notify_db_session):
mmg = get_provider_details_by_identifier('mmg') mmg = get_provider_details_by_identifier('mmg')
@@ -72,6 +73,7 @@ def test_provider_to_use_should_cache_repeated_calls(mocker, notify_db_session):
assert all(result == results[0] for result in results) assert all(result == results[0] for result in results)
assert len(mock_choices.call_args_list) == 1 assert len(mock_choices.call_args_list) == 1
@pytest.mark.skip(reason="Needs updating for TTS: Update with new providers") @pytest.mark.skip(reason="Needs updating for TTS: Update with new providers")
@pytest.mark.parametrize('international_provider_priority', ( @pytest.mark.parametrize('international_provider_priority', (
# Since theres only one international provider it should always # Since theres only one international provider it should always
@@ -592,6 +594,7 @@ def test_should_not_update_notification_if_research_mode_on_exception(
assert persisted_notification.billable_units == 0 assert persisted_notification.billable_units == 0
assert update_mock.called assert update_mock.called
@pytest.mark.skip(reason="Needs updating for TTS: Update with new providers") @pytest.mark.skip(reason="Needs updating for TTS: Update with new providers")
@pytest.mark.parametrize("starting_status, expected_status", [ @pytest.mark.parametrize("starting_status, expected_status", [
("delivered", "delivered"), ("delivered", "delivered"),
+1 -1
View File
@@ -31,8 +31,8 @@ from tests.app.db import create_notification
FROZEN_DATE_TIME = "2018-03-14 17:00:00" FROZEN_DATE_TIME = "2018-03-14 17:00:00"
pytest.skip(reason="Skipping letter-related functionality for now", allow_module_level=True)
@pytest.skip(reason="Skipping letter-related functionality for now", allow_module_level=True)
@pytest.fixture(name='sample_precompiled_letter_notification') @pytest.fixture(name='sample_precompiled_letter_notification')
def _sample_precompiled_letter_notification(sample_letter_notification): def _sample_precompiled_letter_notification(sample_letter_notification):
sample_letter_notification.template.hidden = True sample_letter_notification.template.hidden = True
@@ -17,6 +17,7 @@ def mmg_post(client, data):
data=data, data=data,
headers=[('Content-Type', 'application/json')]) headers=[('Content-Type', 'application/json')])
@pytest.mark.skip(reason="Needs updating for TTS: Firetext removal") @pytest.mark.skip(reason="Needs updating for TTS: Firetext removal")
def test_firetext_callback_should_not_need_auth(client, mocker): def test_firetext_callback_should_not_need_auth(client, mocker):
mocker.patch('app.notifications.notifications_sms_callback.process_sms_client_response') mocker.patch('app.notifications.notifications_sms_callback.process_sms_client_response')
@@ -36,6 +37,7 @@ def test_firetext_callback_should_return_400_if_empty_reference(client, mocker):
assert json_resp['result'] == 'error' assert json_resp['result'] == 'error'
assert json_resp['message'] == ['Firetext callback failed: reference missing'] assert json_resp['message'] == ['Firetext callback failed: reference missing']
@pytest.mark.skip(reason="Needs updating for TTS: Firetext removal") @pytest.mark.skip(reason="Needs updating for TTS: Firetext removal")
def test_firetext_callback_should_return_400_if_no_reference(client, mocker): def test_firetext_callback_should_return_400_if_no_reference(client, mocker):
data = 'mobile=441234123123&status=0&time=2016-03-10 14:17:00' data = 'mobile=441234123123&status=0&time=2016-03-10 14:17:00'
@@ -45,6 +47,7 @@ def test_firetext_callback_should_return_400_if_no_reference(client, mocker):
assert json_resp['result'] == 'error' assert json_resp['result'] == 'error'
assert json_resp['message'] == ['Firetext callback failed: reference missing'] assert json_resp['message'] == ['Firetext callback failed: reference missing']
@pytest.mark.skip(reason="Needs updating for TTS: Firetext removal") @pytest.mark.skip(reason="Needs updating for TTS: Firetext removal")
def test_firetext_callback_should_return_400_if_no_status(client, mocker): def test_firetext_callback_should_return_400_if_no_status(client, mocker):
data = 'mobile=441234123123&time=2016-03-10 14:17:00&reference=notification_id' data = 'mobile=441234123123&time=2016-03-10 14:17:00&reference=notification_id'
@@ -54,6 +57,7 @@ def test_firetext_callback_should_return_400_if_no_status(client, mocker):
assert json_resp['result'] == 'error' assert json_resp['result'] == 'error'
assert json_resp['message'] == ['Firetext callback failed: status missing'] assert json_resp['message'] == ['Firetext callback failed: status missing']
@pytest.mark.skip(reason="Needs updating for TTS: Firetext removal") @pytest.mark.skip(reason="Needs updating for TTS: Firetext removal")
def test_firetext_callback_should_return_200_and_call_task_with_valid_data(client, mocker): def test_firetext_callback_should_return_200_and_call_task_with_valid_data(client, mocker):
mock_celery = mocker.patch( mock_celery = mocker.patch(
@@ -70,6 +74,7 @@ def test_firetext_callback_should_return_200_and_call_task_with_valid_data(clien
queue='sms-callbacks', queue='sms-callbacks',
) )
@pytest.mark.skip(reason="Needs updating for TTS: Firetext removal") @pytest.mark.skip(reason="Needs updating for TTS: Firetext removal")
def test_firetext_callback_including_a_code_should_return_200_and_call_task_with_valid_data(client, mocker): def test_firetext_callback_including_a_code_should_return_200_and_call_task_with_valid_data(client, mocker):
mock_celery = mocker.patch( mock_celery = mocker.patch(
@@ -86,6 +91,7 @@ def test_firetext_callback_including_a_code_should_return_200_and_call_task_with
queue='sms-callbacks', queue='sms-callbacks',
) )
@pytest.mark.skip(reason="Needs updating for TTS: MMG removal") @pytest.mark.skip(reason="Needs updating for TTS: MMG removal")
def test_mmg_callback_should_not_need_auth(client, mocker, sample_notification): def test_mmg_callback_should_not_need_auth(client, mocker, sample_notification):
mocker.patch('app.notifications.notifications_sms_callback.process_sms_client_response') mocker.patch('app.notifications.notifications_sms_callback.process_sms_client_response')
@@ -98,6 +104,7 @@ def test_mmg_callback_should_not_need_auth(client, mocker, sample_notification):
response = mmg_post(client, data) response = mmg_post(client, data)
assert response.status_code == 200 assert response.status_code == 200
@pytest.mark.skip(reason="Needs updating for TTS: MMG removal") @pytest.mark.skip(reason="Needs updating for TTS: MMG removal")
def test_process_mmg_response_returns_400_for_malformed_data(client): def test_process_mmg_response_returns_400_for_malformed_data(client):
data = json.dumps({"reference": "mmg_reference", data = json.dumps({"reference": "mmg_reference",
@@ -114,6 +121,7 @@ def test_process_mmg_response_returns_400_for_malformed_data(client):
assert "{} callback failed: {} missing".format('MMG', 'status') in json_data['message'] assert "{} callback failed: {} missing".format('MMG', 'status') in json_data['message']
assert "{} callback failed: {} missing".format('MMG', 'CID') in json_data['message'] assert "{} callback failed: {} missing".format('MMG', 'CID') in json_data['message']
@pytest.mark.skip(reason="Needs updating for TTS: MMG removal") @pytest.mark.skip(reason="Needs updating for TTS: MMG removal")
def test_mmg_callback_should_return_200_and_call_task_with_valid_data(client, mocker): def test_mmg_callback_should_return_200_and_call_task_with_valid_data(client, mocker):
mock_celery = mocker.patch( mock_celery = mocker.patch(
@@ -238,6 +238,7 @@ def test_should_cache_template_lookups_in_memory(mocker, client, sample_template
] ]
assert Notification.query.count() == 5 assert Notification.query.count() == 5
@pytest.mark.skip(reason="Needs updating for TTS: cloud.gov redis fails, local docker works, mock redis fails") @pytest.mark.skip(reason="Needs updating for TTS: cloud.gov redis fails, local docker works, mock redis fails")
def test_should_cache_template_and_service_in_redis(mocker, client, sample_template): def test_should_cache_template_and_service_in_redis(mocker, client, sample_template):
@@ -288,6 +289,7 @@ def test_should_cache_template_and_service_in_redis(mocker, client, sample_templ
assert json.loads(templates_call[0][1]) == {'data': template_dict} assert json.loads(templates_call[0][1]) == {'data': template_dict}
assert templates_call[1]['ex'] == 604_800 assert templates_call[1]['ex'] == 604_800
@pytest.mark.skip(reason="Needs updating for TTS: cloud.gov redis fails, local docker works, mock redis fails") @pytest.mark.skip(reason="Needs updating for TTS: cloud.gov redis fails, local docker works, mock redis fails")
def test_should_return_template_if_found_in_redis(mocker, client, sample_template): def test_should_return_template_if_found_in_redis(mocker, client, sample_template):