diff --git a/app/celery/tasks.py b/app/celery/tasks.py index ebfa2c4af..a7a47e087 100644 --- a/app/celery/tasks.py +++ b/app/celery/tasks.py @@ -3,13 +3,14 @@ from app import notify_celery, encryption, firetext_client, aws_ses_client from app.clients.email.aws_ses import AwsSesClientException from app.clients.sms.firetext import FiretextClientException from app.dao.templates_dao import dao_get_template_by_id -from app.dao.notifications_dao import save_notification +from app.dao.notifications_dao import dao_create_notification, dao_update_notification from app.dao.jobs_dao import dao_update_job, dao_get_job_by_id from app.models import Notification from flask import current_app from sqlalchemy.exc import SQLAlchemyError from app.aws import s3 -from app.csv import get_mobile_numbers_from_csv +from app.csv import get_recipient_from_csv +from datetime import datetime @notify_celery.task(name="process-job") @@ -19,28 +20,38 @@ def process_job(job_id): dao_update_job(job) file = s3.get_job_from_s3(job.bucket_name, job_id) - mobile_numbers = get_mobile_numbers_from_csv(file) + recipients = get_recipient_from_csv(file) - for mobile_number in mobile_numbers: - notification = encryption.encrypt({ + for recipient in recipients: + encrypted = encryption.encrypt({ 'template': job.template_id, 'job': str(job.id), - 'to': mobile_number + 'to': recipient }) - send_sms.apply_async(( - str(job.service_id), - str(create_uuid()), - notification), - queue='sms' - ) + if job.template.template_type == 'sms': + send_sms.apply_async(( + str(job.service_id), + str(create_uuid()), + encrypted), + queue='bulk-sms' + ) + + if job.template.template_type == 'email': + send_email.apply_async(( + str(job.service_id), + str(create_uuid()), + job.template.subject, + "{}@{}".format(job.service.email_from, current_app.config['NOTIFY_EMAIL_DOMAIN']), + encrypted), + queue='bulk-email') job.status = 'finished' dao_update_job(job) @notify_celery.task(name="send-sms") -def send_sms(service_id, notification_id, encrypted_notification): +def send_sms(service_id, notification_id, encrypted_notification, created_at): notification = encryption.decrypt(encrypted_notification) template = dao_get_template_by_id(notification['template']) @@ -51,22 +62,25 @@ def send_sms(service_id, notification_id, encrypted_notification): to=notification['to'], service_id=service_id, job_id=notification.get('job', None), - status='sent' + status='sent', + created_at=created_at, + sent_at=datetime.utcnow() ) - save_notification(notification_db_object) + dao_create_notification(notification_db_object) try: firetext_client.send_sms(notification['to'], template.content) except FiretextClientException as e: current_app.logger.debug(e) - save_notification(notification_db_object, {"status": "failed"}) + notification_db_object.status = 'failed' + dao_update_notification(notification_db_object) except SQLAlchemyError as e: current_app.logger.debug(e) @notify_celery.task(name="send-email") -def send_email(service_id, notification_id, subject, from_address, encrypted_notification): +def send_email(service_id, notification_id, subject, from_address, encrypted_notification, created_at): notification = encryption.decrypt(encrypted_notification) template = dao_get_template_by_id(notification['template']) @@ -77,9 +91,11 @@ def send_email(service_id, notification_id, subject, from_address, encrypted_not to=notification['to'], service_id=service_id, job_id=notification.get('job', None), - status='sent' + status='sent', + created_at=created_at, + sent_at=datetime.utcnow() ) - save_notification(notification_db_object) + dao_create_notification(notification_db_object) try: aws_ses_client.send_email( @@ -90,7 +106,8 @@ def send_email(service_id, notification_id, subject, from_address, encrypted_not ) except AwsSesClientException as e: current_app.logger.debug(e) - save_notification(notification_db_object, {"status": "failed"}) + notification_db_object.status = 'failed' + dao_update_notification(notification_db_object) except SQLAlchemyError as e: current_app.logger.debug(e) diff --git a/app/csv.py b/app/csv.py index 371203a54..db0199a48 100644 --- a/app/csv.py +++ b/app/csv.py @@ -1,12 +1,12 @@ import csv -def get_mobile_numbers_from_csv(file_data): +def get_recipient_from_csv(file_data): numbers = [] reader = csv.DictReader( file_data.splitlines(), lineterminator='\n', quoting=csv.QUOTE_NONE) for i, row in enumerate(reader): - numbers.append(row['phone'].replace(' ', '')) + numbers.append(row['to'].replace(' ', '')) return numbers diff --git a/app/dao/notifications_dao.py b/app/dao/notifications_dao.py index f917f4429..f92ac0393 100644 --- a/app/dao/notifications_dao.py +++ b/app/dao/notifications_dao.py @@ -2,15 +2,13 @@ from app import db from app.models import Notification -def save_notification(notification, update_dict={}): - if update_dict: - update_dict.pop('id', None) - update_dict.pop('job', None) - update_dict.pop('service', None) - update_dict.pop('template', None) - Notification.query.filter_by(id=notification.id).update(update_dict) - else: - db.session.add(notification) +def dao_create_notification(notification): + db.session.add(notification) + db.session.commit() + + +def dao_update_notification(notification): + db.session.add(notification) db.session.commit() diff --git a/app/models.py b/app/models.py index 84726164a..14da343d5 100644 --- a/app/models.py +++ b/app/models.py @@ -160,6 +160,16 @@ class Job(db.Model): onupdate=datetime.datetime.now) status = db.Column(db.Enum(*JOB_STATUS_TYPES, name='job_status_types'), nullable=False, default='pending') notification_count = db.Column(db.Integer, nullable=False) + processing_started = db.Column( + db.DateTime, + index=False, + unique=False, + nullable=True) + processing_finished = db.Column( + db.DateTime, + index=False, + unique=False, + nullable=True) VERIFY_CODE_TYPES = ['email', 'sms'] @@ -214,8 +224,13 @@ class Notification(db.Model): db.DateTime, index=False, unique=False, - nullable=False, - default=datetime.datetime.now) + nullable=False) + sent_at = db.Column( + db.DateTime, + index=False, + unique=False, + nullable=True) + sent_by = db.Column(db.String, nullable=True) updated_at = db.Column( db.DateTime, index=False, diff --git a/migrations/versions/0022_add_processing_dates.py b/migrations/versions/0022_add_processing_dates.py new file mode 100644 index 000000000..89f2d4d71 --- /dev/null +++ b/migrations/versions/0022_add_processing_dates.py @@ -0,0 +1,26 @@ +"""empty message + +Revision ID: 0022_add_processing_dates +Revises: 0021_add_job_metadata +Create Date: 2016-02-24 17:15:47.457200 + +""" + +# revision identifiers, used by Alembic. +revision = '0022_add_processing_dates' +down_revision = '0021_add_job_metadata' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + op.add_column('jobs', sa.Column('processing_finished', sa.DateTime(), nullable=True)) + op.add_column('jobs', sa.Column('processing_started', sa.DateTime(), nullable=True)) + op.add_column('notifications', sa.Column('sent_at', sa.DateTime(), nullable=True)) + + +def downgrade(): + op.drop_column('notifications', 'sent_at') + op.drop_column('jobs', 'processing_started') + op.drop_column('jobs', 'processing_finished') diff --git a/migrations/versions/0023_add_sender.py b/migrations/versions/0023_add_sender.py new file mode 100644 index 000000000..fbc41aa99 --- /dev/null +++ b/migrations/versions/0023_add_sender.py @@ -0,0 +1,26 @@ +"""empty message + +Revision ID: d98c7c20f1d4 +Revises: 0022_add_processing_dates +Create Date: 2016-02-24 17:18:21.942772 + +""" + +# revision identifiers, used by Alembic. +revision = 'd98c7c20f1d4' +down_revision = '0022_add_processing_dates' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + ### commands auto generated by Alembic - please adjust! ### + op.add_column('notifications', sa.Column('sent_by', sa.String(), nullable=True)) + ### end Alembic commands ### + + +def downgrade(): + ### commands auto generated by Alembic - please adjust! ### + op.drop_column('notifications', 'sent_by') + ### end Alembic commands ### diff --git a/scripts/run_celery.sh b/scripts/run_celery.sh index d86656a98..eb0e1805b 100755 --- a/scripts/run_celery.sh +++ b/scripts/run_celery.sh @@ -3,4 +3,4 @@ set -e source environment.sh -celery -A run_celery.notify_celery worker --loglevel=INFO --logfile=/var/log/notify/application.log --concurrency=4 -Q sms,sms-code,email-code,email,process-job +celery -A run_celery.notify_celery worker --loglevel=INFO --logfile=/var/log/notify/application.log --concurrency=4 -Q sms,sms-code,email-code,email,process-job,bulk-sms,bulk-email diff --git a/test_csv_files/email.csv b/test_csv_files/email.csv new file mode 100644 index 000000000..581e64627 --- /dev/null +++ b/test_csv_files/email.csv @@ -0,0 +1,2 @@ +to +test@test.com \ No newline at end of file diff --git a/test_csv_files/empty.csv b/test_csv_files/empty.csv new file mode 100644 index 000000000..bf3efa02f --- /dev/null +++ b/test_csv_files/empty.csv @@ -0,0 +1 @@ +to diff --git a/test_csv_files/multiple_email.csv b/test_csv_files/multiple_email.csv new file mode 100644 index 000000000..2e7bab603 --- /dev/null +++ b/test_csv_files/multiple_email.csv @@ -0,0 +1,12 @@ +to +test1@test.com +test2@test.com +test3@test.com +test4@test.com +test5@test.com +test6@test.com +test7@test.com +test8@test.com +test9@test.com +test0@test.com + diff --git a/test_csv_files/multiple_sms.csv b/test_csv_files/multiple_sms.csv index 3a459d07a..224a1a4d7 100644 --- a/test_csv_files/multiple_sms.csv +++ b/test_csv_files/multiple_sms.csv @@ -1,4 +1,4 @@ -phone +to +441234123121 +441234123122 +441234123123 diff --git a/test_csv_files/sms.csv b/test_csv_files/sms.csv index 39e2eda10..3776c8832 100644 --- a/test_csv_files/sms.csv +++ b/test_csv_files/sms.csv @@ -1,2 +1,2 @@ -phone +to +441234123123 \ No newline at end of file diff --git a/tests/app/celery/test_tasks.py b/tests/app/celery/test_tasks.py index f4b3b3013..c0abb4c06 100644 --- a/tests/app/celery/test_tasks.py +++ b/tests/app/celery/test_tasks.py @@ -11,6 +11,7 @@ from sqlalchemy.orm.exc import NoResultFound from app.celery.tasks import s3 from app.celery import tasks from tests.app import load_example_csv +from datetime import datetime def test_should_process_sms_job(sample_job, mocker): @@ -26,12 +27,45 @@ def test_should_process_sms_job(sample_job, mocker): (str(sample_job.service_id), "uuid", "something_encrypted"), - queue="sms" + queue="bulk-sms" ) job = jobs_dao.dao_get_job_by_id(sample_job.id) assert job.status == 'finished' +def test_should_not_create_send_task_for_empty_file(sample_job, mocker): + mocker.patch('app.celery.tasks.s3.get_job_from_s3', return_value=load_example_csv('empty')) + mocker.patch('app.celery.tasks.send_sms.apply_async') + + process_job(sample_job.id) + + s3.get_job_from_s3.assert_called_once_with(sample_job.bucket_name, sample_job.id) + job = jobs_dao.dao_get_job_by_id(sample_job.id) + assert job.status == 'finished' + tasks.send_sms.apply_async.assert_not_called + + +def test_should_process_email_job(sample_email_job, mocker): + mocker.patch('app.celery.tasks.s3.get_job_from_s3', return_value=load_example_csv('email')) + mocker.patch('app.celery.tasks.send_email.apply_async') + mocker.patch('app.encryption.encrypt', return_value="something_encrypted") + mocker.patch('app.celery.tasks.create_uuid', return_value="uuid") + + process_job(sample_email_job.id) + + s3.get_job_from_s3.assert_called_once_with(sample_email_job.bucket_name, sample_email_job.id) + tasks.send_email.apply_async.assert_called_once_with( + (str(sample_email_job.service_id), + "uuid", + sample_email_job.template.subject, + "{}@{}".format(sample_email_job.service.email_from, "test.notify.com"), + "something_encrypted"), + queue="bulk-email" + ) + job = jobs_dao.dao_get_job_by_id(sample_email_job.id) + assert job.status == 'finished' + + def test_should_process_all_sms_job(sample_job, mocker): mocker.patch('app.celery.tasks.s3.get_job_from_s3', return_value=load_example_csv('multiple_sms')) mocker.patch('app.celery.tasks.send_sms.apply_async') @@ -55,11 +89,13 @@ def test_should_send_template_to_correct_sms_provider_and_persist(sample_templat mocker.patch('app.firetext_client.send_sms') notification_id = uuid.uuid4() - + now = datetime.utcnow() send_sms( sample_template.service_id, notification_id, - "encrypted-in-reality") + "encrypted-in-reality", + now + ) firetext_client.send_sms.assert_called_once_with("+441234123123", sample_template.content) persisted_notification = notifications_dao.get_notification(sample_template.service_id, notification_id) @@ -67,6 +103,8 @@ def test_should_send_template_to_correct_sms_provider_and_persist(sample_templat assert persisted_notification.to == '+441234123123' assert persisted_notification.template_id == sample_template.id assert persisted_notification.status == 'sent' + assert persisted_notification.created_at == now + assert persisted_notification.sent_at > now assert not persisted_notification.job_id @@ -80,11 +118,12 @@ def test_should_send_template_to_correct_sms_provider_and_persist_with_job_id(sa mocker.patch('app.firetext_client.send_sms') notification_id = uuid.uuid4() - + now = datetime.utcnow() send_sms( sample_job.service.id, notification_id, - "encrypted-in-reality") + "encrypted-in-reality", + now) firetext_client.send_sms.assert_called_once_with("+441234123123", sample_job.template.content) persisted_notification = notifications_dao.get_notification(sample_job.template.service_id, notification_id) @@ -93,9 +132,11 @@ def test_should_send_template_to_correct_sms_provider_and_persist_with_job_id(sa assert persisted_notification.job_id == sample_job.id assert persisted_notification.template_id == sample_job.template.id assert persisted_notification.status == 'sent' + assert persisted_notification.sent_at > now + assert persisted_notification.created_at == now -def test_should_send_template_to_email_provider_and_persist(sample_email_template, mocker): +def test_should_use_email_template_and_persist(sample_email_template, mocker): notification = { "template": sample_email_template.id, "to": "my_email@my_email.com" @@ -104,13 +145,14 @@ def test_should_send_template_to_email_provider_and_persist(sample_email_templat mocker.patch('app.aws_ses_client.send_email') notification_id = uuid.uuid4() - + now = datetime.utcnow() send_email( sample_email_template.service_id, notification_id, 'subject', 'email_from', - "encrypted-in-reality") + "encrypted-in-reality", + now) aws_ses_client.send_email.assert_called_once_with( "email_from", @@ -122,6 +164,8 @@ def test_should_send_template_to_email_provider_and_persist(sample_email_templat assert persisted_notification.id == notification_id assert persisted_notification.to == 'my_email@my_email.com' assert persisted_notification.template_id == sample_email_template.id + assert persisted_notification.created_at == now + assert persisted_notification.sent_at > now assert persisted_notification.status == 'sent' @@ -132,13 +176,15 @@ def test_should_persist_notification_as_failed_if_sms_client_fails(sample_templa } mocker.patch('app.encryption.decrypt', return_value=notification) mocker.patch('app.firetext_client.send_sms', side_effect=FiretextClientException()) + now = datetime.utcnow() notification_id = uuid.uuid4() send_sms( sample_template.service_id, notification_id, - "encrypted-in-reality") + "encrypted-in-reality", + now) firetext_client.send_sms.assert_called_once_with("+441234123123", sample_template.content) persisted_notification = notifications_dao.get_notification(sample_template.service_id, notification_id) @@ -146,6 +192,8 @@ def test_should_persist_notification_as_failed_if_sms_client_fails(sample_templa assert persisted_notification.to == '+441234123123' assert persisted_notification.template_id == sample_template.id assert persisted_notification.status == 'failed' + assert persisted_notification.created_at == now + assert persisted_notification.sent_at > now def test_should_persist_notification_as_failed_if_email_client_fails(sample_email_template, mocker): @@ -155,6 +203,7 @@ def test_should_persist_notification_as_failed_if_email_client_fails(sample_emai } mocker.patch('app.encryption.decrypt', return_value=notification) mocker.patch('app.aws_ses_client.send_email', side_effect=AwsSesClientException()) + now = datetime.utcnow() notification_id = uuid.uuid4() @@ -163,7 +212,8 @@ def test_should_persist_notification_as_failed_if_email_client_fails(sample_emai notification_id, 'subject', 'email_from', - "encrypted-in-reality") + "encrypted-in-reality", + now) aws_ses_client.send_email.assert_called_once_with( "email_from", @@ -176,6 +226,8 @@ def test_should_persist_notification_as_failed_if_email_client_fails(sample_emai assert persisted_notification.to == 'my_email@my_email.com' assert persisted_notification.template_id == sample_email_template.id assert persisted_notification.status == 'failed' + assert persisted_notification.created_at == now + assert persisted_notification.sent_at > now def test_should_not_send_sms_if_db_peristance_failed(sample_template, mocker): @@ -186,13 +238,15 @@ def test_should_not_send_sms_if_db_peristance_failed(sample_template, mocker): mocker.patch('app.encryption.decrypt', return_value=notification) mocker.patch('app.firetext_client.send_sms') mocker.patch('app.db.session.add', side_effect=SQLAlchemyError()) + now = datetime.utcnow() notification_id = uuid.uuid4() send_sms( sample_template.service_id, notification_id, - "encrypted-in-reality") + "encrypted-in-reality", + now) firetext_client.send_sms.assert_not_called() with pytest.raises(NoResultFound) as e: @@ -208,6 +262,7 @@ def test_should_not_send_email_if_db_peristance_failed(sample_email_template, mo mocker.patch('app.encryption.decrypt', return_value=notification) mocker.patch('app.aws_ses_client.send_email') mocker.patch('app.db.session.add', side_effect=SQLAlchemyError()) + now = datetime.utcnow() notification_id = uuid.uuid4() @@ -216,7 +271,8 @@ def test_should_not_send_email_if_db_peristance_failed(sample_email_template, mo notification_id, 'subject', 'email_from', - "encrypted-in-reality") + "encrypted-in-reality", + now) aws_ses_client.send_email.assert_not_called() with pytest.raises(NoResultFound) as e: diff --git a/tests/app/conftest.py b/tests/app/conftest.py index 4b09250ff..1b9e1df9e 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -1,5 +1,5 @@ import pytest - +from datetime import datetime from app import email_safe from app.models import (User, Service, Template, ApiKey, Job, Notification) from app.dao.users_dao import (save_model_user, create_user_code, create_secret_code) @@ -7,7 +7,7 @@ from app.dao.services_dao import dao_create_service from app.dao.templates_dao import dao_create_template from app.dao.api_key_dao import save_model_api_key from app.dao.jobs_dao import dao_create_job -from app.dao.notifications_dao import save_notification +from app.dao.notifications_dao import dao_create_notification import uuid @@ -208,6 +208,35 @@ def sample_job(notify_db, return job +@pytest.fixture(scope='function') +def sample_email_job(notify_db, + notify_db_session, + service=None, + template=None): + if service is None: + service = sample_service(notify_db, notify_db_session) + if template is None: + template = sample_email_template( + notify_db, + notify_db_session, + service=service) + job_id = uuid.uuid4() + bucket_name = 'service-{}-notify'.format(service.id) + file_name = '{}.csv'.format(job_id) + data = { + 'id': uuid.uuid4(), + 'service_id': service.id, + 'template_id': template.id, + 'bucket_name': bucket_name, + 'file_name': file_name, + 'original_file_name': 'some.csv', + 'notification_count': 1 + } + job = Job(**data) + dao_create_job(job) + return job + + @pytest.fixture(scope='function') def sample_admin_service_id(notify_db, notify_db_session): admin_user = sample_user(notify_db, notify_db_session, email="notify_admin@digital.cabinet-office.gov.uk") @@ -248,10 +277,11 @@ def sample_notification(notify_db, 'to': to, 'job': job, 'service': service, - 'template': template + 'template': template, + 'created_at': datetime.utcnow() } notification = Notification(**data) - save_notification(notification) + dao_create_notification(notification) return notification diff --git a/tests/app/dao/test_notification_dao.py b/tests/app/dao/test_notification_dao.py index 42e74b111..9ba17c727 100644 --- a/tests/app/dao/test_notification_dao.py +++ b/tests/app/dao/test_notification_dao.py @@ -1,7 +1,8 @@ from app.models import Notification - +from datetime import datetime from app.dao.notifications_dao import ( - save_notification, + dao_create_notification, + dao_update_notification, get_notification, get_notification_for_job, get_notifications_for_job @@ -11,16 +12,16 @@ from app.dao.notifications_dao import ( def test_save_notification(sample_template, sample_job): assert Notification.query.count() == 0 - to = '+44709123456' data = { - 'to': to, + 'to': '+44709123456', 'job': sample_job, 'service': sample_template.service, - 'template': sample_template + 'template': sample_template, + 'created_at': datetime.utcnow() } notification = Notification(**data) - save_notification(notification) + dao_create_notification(notification) assert Notification.query.count() == 1 notification_from_db = Notification.query.all()[0] @@ -29,28 +30,30 @@ def test_save_notification(sample_template, sample_job): assert data['job'] == notification_from_db.job assert data['service'] == notification_from_db.service assert data['template'] == notification_from_db.template + assert data['created_at'] == notification_from_db.created_at assert 'sent' == notification_from_db.status -def test_get_notification(notify_db, notify_db_session, sample_notification): +def test_get_notification(sample_notification): notifcation_from_db = get_notification( sample_notification.service.id, sample_notification.id) assert sample_notification == notifcation_from_db -def test_save_notification_no_job_id(notify_db, notify_db_session, sample_template): +def test_save_notification_no_job_id(sample_template): assert Notification.query.count() == 0 to = '+44709123456' data = { 'to': to, 'service': sample_template.service, - 'template': sample_template + 'template': sample_template, + 'created_at': datetime.utcnow() } notification = Notification(**data) - save_notification(notification) + dao_create_notification(notification) assert Notification.query.count() == 1 notification_from_db = Notification.query.all()[0] @@ -61,7 +64,7 @@ def test_save_notification_no_job_id(notify_db, notify_db_session, sample_templa assert 'sent' == notification_from_db.status -def test_get_notification_for_job(notify_db, notify_db_session, sample_notification): +def test_get_notification_for_job(sample_notification): notifcation_from_db = get_notification_for_job( sample_notification.service.id, sample_notification.job_id, @@ -83,17 +86,9 @@ def test_get_all_notifications_for_job(notify_db, notify_db_session, sample_job) assert len(notifcations_from_db) == 5 -def test_update_notification(notify_db, notify_db_session, sample_notification): +def test_update_notification(sample_notification): assert sample_notification.status == 'sent' - - update_dict = { - 'id': str(sample_notification.id), - 'service': str(sample_notification.service.id), - 'template': sample_notification.template.id, - 'job': str(sample_notification.job.id), - 'status': 'failed' - } - - save_notification(sample_notification, update_dict=update_dict) + sample_notification.status = 'failed' + dao_update_notification(sample_notification) notification_from_db = Notification.query.get(sample_notification.id) assert notification_from_db.status == 'failed' diff --git a/tests/app/test_csv.py b/tests/app/test_csv.py index 50fa84122..df8fdcb70 100644 --- a/tests/app/test_csv.py +++ b/tests/app/test_csv.py @@ -1,15 +1,28 @@ -from app.csv import get_mobile_numbers_from_csv +from app.csv import get_recipient_from_csv from tests.app import load_example_csv def test_should_process_single_phone_number_file(): sms_file = load_example_csv('sms') - len(get_mobile_numbers_from_csv(sms_file)) == 1 - assert get_mobile_numbers_from_csv(sms_file)[0] == '+441234123123' + len(get_recipient_from_csv(sms_file)) == 1 + assert get_recipient_from_csv(sms_file)[0] == '+441234123123' def test_should_process_multple_phone_number_file_in_order(): sms_file = load_example_csv('multiple_sms') - len(get_mobile_numbers_from_csv(sms_file)) == 10 - assert get_mobile_numbers_from_csv(sms_file)[0] == '+441234123121' - assert get_mobile_numbers_from_csv(sms_file)[9] == '+441234123120' + len(get_recipient_from_csv(sms_file)) == 10 + assert get_recipient_from_csv(sms_file)[0] == '+441234123121' + assert get_recipient_from_csv(sms_file)[9] == '+441234123120' + + +def test_should_process_single_email_file(): + sms_file = load_example_csv('email') + len(get_recipient_from_csv(sms_file)) == 1 + assert get_recipient_from_csv(sms_file)[0] == 'test@test.com' + + +def test_should_process_multple_email_file_in_order(): + sms_file = load_example_csv('multiple_email') + len(get_recipient_from_csv(sms_file)) == 10 + assert get_recipient_from_csv(sms_file)[0] == 'test1@test.com' + assert get_recipient_from_csv(sms_file)[9] == 'test0@test.com'