From 6959d695d39aebc8bfae7dd412d34728384b7b7c Mon Sep 17 00:00:00 2001 From: Nicholas Staples Date: Wed, 27 Jan 2016 12:22:32 +0000 Subject: [PATCH] Working tests, hopefully all code changes done. --- app/__init__.py | 2 - app/main/dao/roles_dao.py | 11 - app/main/dao/users_dao.py | 37 +- app/main/dao/verify_codes_dao.py | 55 --- app/main/forms.py | 78 ++++- app/main/validators.py | 26 -- app/main/views/code_not_received.py | 13 +- app/main/views/new_password.py | 4 +- app/main/views/register.py | 13 +- app/main/views/sign_in.py | 35 +- app/main/views/two_factor.py | 14 +- app/main/views/user_profile.py | 46 ++- app/main/views/verify.py | 11 +- app/models.py | 127 ------- app/notify_client/sender.py | 23 -- app/notify_client/user_api_client.py | 34 ++ migrations/README | 1 - migrations/alembic.ini | 45 --- migrations/env.py | 73 ---- migrations/script.py.mako | 22 -- migrations/versions/10_create_users.py | 42 --- migrations/versions/20_initialise_data.py | 14 - migrations/versions/30_update_indexes.py | 36 -- migrations/versions/40_verify_codes.py | 42 --- .../versions/50_alter_verify_code_type.py | 30 -- migrations/versions/60_add_service.py | 42 --- migrations/versions/70_unique_email.py | 28 -- migrations/versions/80_remove_services.py | 43 --- .../90_remove_user_fk_verify_codes.py | 26 -- tests/__init__.py | 25 +- tests/app/main/dao/test_roles_dao.py | 26 -- tests/app/main/dao/test_service_dao.py | 22 +- tests/app/main/dao/test_users_dao.py | 195 ++++++----- tests/app/main/dao/test_verify_codes_dao.py | 37 -- tests/app/main/notify_client/test_sender.py | 12 +- tests/app/main/test_add_service_form.py | 4 +- tests/app/main/test_create_api_key_form.py | 4 +- tests/app/main/test_two_factor_form.py | 53 ++- tests/app/main/test_verify_form.py | 91 ++--- tests/app/main/views/test_add_service.py | 40 +-- tests/app/main/views/test_api_keys.py | 63 ++-- tests/app/main/views/test_choose_services.py | 14 +- .../app/main/views/test_code_not_received.py | 112 +++--- tests/app/main/views/test_dashboard.py | 10 +- tests/app/main/views/test_forgot_password.py | 10 +- tests/app/main/views/test_jobs.py | 31 +- tests/app/main/views/test_new_password.py | 41 ++- tests/app/main/views/test_register.py | 36 +- tests/app/main/views/test_service_settings.py | 147 ++++---- tests/app/main/views/test_sign_in.py | 21 +- tests/app/main/views/test_sign_out.py | 10 +- tests/app/main/views/test_sms.py | 45 +-- tests/app/main/views/test_templates.py | 45 ++- tests/app/main/views/test_two_factor.py | 53 +-- tests/app/main/views/test_user_profile.py | 321 ++++++++++++++---- tests/app/main/views/test_verify.py | 55 ++- tests/conftest.py | 225 +++++++----- 57 files changed, 1143 insertions(+), 1578 deletions(-) delete mode 100644 app/main/dao/roles_dao.py delete mode 100644 app/main/dao/verify_codes_dao.py delete mode 100644 app/models.py delete mode 100755 migrations/README delete mode 100644 migrations/alembic.ini delete mode 100644 migrations/env.py delete mode 100755 migrations/script.py.mako delete mode 100644 migrations/versions/10_create_users.py delete mode 100644 migrations/versions/20_initialise_data.py delete mode 100644 migrations/versions/30_update_indexes.py delete mode 100644 migrations/versions/40_verify_codes.py delete mode 100644 migrations/versions/50_alter_verify_code_type.py delete mode 100644 migrations/versions/60_add_service.py delete mode 100644 migrations/versions/70_unique_email.py delete mode 100644 migrations/versions/80_remove_services.py delete mode 100644 migrations/versions/90_remove_user_fk_verify_codes.py delete mode 100644 tests/app/main/dao/test_roles_dao.py delete mode 100644 tests/app/main/dao/test_verify_codes_dao.py diff --git a/app/__init__.py b/app/__init__.py index 80da769a3..cf2b6b3a9 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -16,7 +16,6 @@ import app.proxy_fix from config import configs from utils import logging -db = SQLAlchemy() login_manager = LoginManager() csrf = CsrfProtect() @@ -31,7 +30,6 @@ def create_app(config_name, config_overrides=None): application.config['NOTIFY_ADMIN_ENVIRONMENT'] = config_name application.config.from_object(configs[config_name]) init_app(application, config_overrides) - db.init_app(application) logging.init_app(application) init_csrf(application) diff --git a/app/main/dao/roles_dao.py b/app/main/dao/roles_dao.py deleted file mode 100644 index a9f6b1027..000000000 --- a/app/main/dao/roles_dao.py +++ /dev/null @@ -1,11 +0,0 @@ -from app import db -from app.models import Roles - - -def insert_role(role): - db.session.add(role) - db.session.commit() - - -def get_role_by_id(id): - return Roles.query.filter_by(id=id).first() diff --git a/app/main/dao/users_dao.py b/app/main/dao/users_dao.py index 357382632..e4b3da85a 100644 --- a/app/main/dao/users_dao.py +++ b/app/main/dao/users_dao.py @@ -2,8 +2,7 @@ from datetime import datetime from sqlalchemy.orm import load_only -from app import db, login_manager -from app.models import User +from app import login_manager from app.main.encryption import hashpw from app import user_api_client @@ -14,12 +13,6 @@ def load_user(user_id): return get_user_by_id(user_id) -def insert_user(user): - user.password = hashpw(user.password) - db.session.add(user) - db.session.commit() - - # TODO Would be better to have a generic get and update for user # something that replicates the sql functionality. def get_user_by_id(id): @@ -39,7 +32,7 @@ def verify_password(user, password): def update_user(user): - return user_api_client.update_user(user) + return user_api_client.update_user(user) def increment_failed_login_count(id): @@ -55,27 +48,37 @@ def activate_user(user): def update_email_address(id, email_address): user = get_user_by_id(id) user.email_address = email_address - db.session.add(user) - db.session.commit() + # TODO update user + + +def is_email_unique(email_address): + if user_api_client.get_user_by_email(email_address): + return False + return True def update_mobile_number(id, mobile_number): user = get_user_by_id(id) user.mobile_number = mobile_number - db.session.add(user) - db.session.commit() + # TODO update user def update_password(user, password): user.password = hashpw(password) user.password_changed_at = datetime.now() user.state = 'active' - db.session.add(user) - db.session.commit() + # TODO update user def request_password_reset(email): user = get_user_by_email(email) user.state = 'request_password_reset' - db.session.add(user) - db.session.commit() + # TODO update user + + +def send_verify_code(user_id, code_type): + return user_api_client.send_verify_code(user_id, code_type) + + +def check_verify_code(user_id, code, code_type): + return user_api_client.check_verify_code(user_id, code, code_type) diff --git a/app/main/dao/verify_codes_dao.py b/app/main/dao/verify_codes_dao.py deleted file mode 100644 index 111753a89..000000000 --- a/app/main/dao/verify_codes_dao.py +++ /dev/null @@ -1,55 +0,0 @@ -from datetime import datetime, timedelta - -from app import db -from app.main.encryption import hashpw -from app.models import VerifyCodes - - -def add_code(user_id, code, code_type): - code = VerifyCodes(user_id=user_id, - code=hashpw(code), - code_type=code_type, - expiry_datetime=datetime.now() + timedelta(hours=1)) - - db.session.add(code) - db.session.commit() - return code - - -def get_codes(user_id, code_type=None): - if not code_type: - return VerifyCodes.query.filter_by(user_id=user_id, code_used=False).all() - return VerifyCodes.query.filter_by(user_id=user_id, code_type=code_type, code_used=False).all() - - -def get_code_by_code(user_id, code, code_type): - return VerifyCodes.query.filter_by(user_id=user_id, code=hashpw(code), code_type=code_type).first() - - -def use_code(id): - verify_code = VerifyCodes.query.get(id) - verify_code.code_used = True - db.session.add(verify_code) - db.session.commit() - - -def use_code_for_user_and_type(user_id, code_type): - codes = VerifyCodes.query.filter_by(user_id=user_id, code_type=code_type, code_used=False).all() - for verify_code in codes: - verify_code.code_used = True - db.session.add(verify_code) - db.session.commit() - - -def get_code_by_id(id): - return VerifyCodes.query.get(id) - - -def add_code_with_expiry(user_id, code, code_type, expiry): - code = VerifyCodes(user_id=user_id, - code=hashpw(code), - code_type=code_type, - expiry_datetime=expiry) - - db.session.add(code) - db.session.commit() diff --git a/app/main/forms.py b/app/main/forms.py index 3acc41cdf..d867af614 100644 --- a/app/main/forms.py +++ b/app/main/forms.py @@ -11,8 +11,7 @@ from wtforms import ( ) from wtforms.validators import DataRequired, Email, Length, Regexp -from app.main.validators import Blacklist, ValidateUserCodes, CsvFileValidator -from app.main.dao import verify_codes_dao +from app.main.validators import Blacklist, CsvFileValidator from app.main.encryption import check_hash @@ -84,16 +83,14 @@ def sms_code(): return StringField('Text message confirmation code', validators=[DataRequired(message='Text message confirmation code can not be empty'), Regexp(regex=verify_code, - message='Text message confirmation code must be 5 digits'), - ValidateUserCodes(code_type='sms')]) + message='Text message confirmation code must be 5 digits')]) def email_code(): verify_code = '^\d{5}$' return StringField("Email confirmation code", validators=[DataRequired(message='Email confirmation code can not be empty'), - Regexp(regex=verify_code, message='Email confirmation code must be 5 digits'), - ValidateUserCodes(code_type='email')]) + Regexp(regex=verify_code, message='Email confirmation code must be 5 digits')]) class LoginForm(Form): @@ -125,31 +122,45 @@ class RegisterUserForm(Form): class TwoFactorForm(Form): - def __init__(self, user_codes, *args, **kwargs): + def __init__(self, validate_code_func, *args, **kwargs): ''' Keyword arguments: - user_codes -- List of user code objects which have the fields - (code_type, expiry_datetime, code) + validate_code_func -- Validates the code with the API. ''' - self.user_codes = user_codes + self.validate_code_func = validate_code_func super(TwoFactorForm, self).__init__(*args, **kwargs) sms_code = sms_code() + def validate_sms_code(self, field): + is_valid, reason = self.validate_code_func(field.data) + if not is_valid: + raise ValidationError(reason) + class VerifyForm(Form): - def __init__(self, user_codes, *args, **kwargs): + def __init__(self, validate_code_func, *args, **kwargs): ''' Keyword arguments: - user_codes -- List of user code objects which have the fields - (code_type, expiry_datetime, code) + validate_code_func -- Validates the code with the API. ''' - self.user_codes = user_codes + self.validate_code_func = validate_code_func super(VerifyForm, self).__init__(*args, **kwargs) sms_code = sms_code() email_code = email_code() + def _validate_code(self, cde, code_type): + is_valid, reason = self.validate_code_func(cde, code_type) + if not is_valid: + raise ValidationError(reason) + + def validate_email_code(self, field): + self._validate_code(field.data, 'email') + + def validate_sms_code(self, field): + self._validate_code(field.data, 'sms') + class EmailNotReceivedForm(Form): email_address = email_address() @@ -218,9 +229,18 @@ class NewPasswordForm(Form): class ChangePasswordForm(Form): + + def __init__(self, validate_password_func, *args, **kwargs): + self.validate_password_func = validate_password_func + super(ChangePasswordForm, self).__init__(*args, **kwargs) + old_password = password('Current password') new_password = password('New password') + def validate_old_password(self, field): + if not self.validate_password_func(field.data): + raise ValidationError('Invalid password') + class CsvUploadForm(Form): file = FileField('File to upload', validators=[DataRequired( @@ -232,20 +252,50 @@ class ChangeNameForm(Form): class ChangeEmailForm(Form): + + def __init__(self, validate_email_func, *args, **kwargs): + self.validate_email_func = validate_email_func + super(ChangeEmailForm, self).__init__(*args, **kwargs) + email_address = email_address() + def validate_email_address(self, field): + is_valid = self.validate_email_func(field.data) + if not is_valid: + raise ValidationError("The email address is already in use") + class ConfirmEmailForm(Form): + + def __init__(self, validate_code_func, *args, **kwargs): + self.validate_code_func = validate_code_func + super(ConfirmEmailForm, self).__init__(*args, **kwargs) + email_code = email_code() + def validate_email_code(self, field): + is_valid, msg = self.validate_code_func(field.data) + if not is_valid: + raise ValidationError(msg) + class ChangeMobileNumberForm(Form): mobile_number = mobile_number() class ConfirmMobileNumberForm(Form): + + def __init__(self, validate_code_func, *args, **kwargs): + self.validate_code_func = validate_code_func + super(ConfirmMobileNumberForm, self).__init__(*args, **kwargs) + sms_code = sms_code() + def validate_sms_code(self, field): + is_valid, msg = self.validate_code_func(field.data) + if not is_valid: + raise ValidationError(msg) + class CreateKeyForm(Form): def __init__(self, existing_key_names=[], *args, **kwargs): diff --git a/app/main/validators.py b/app/main/validators.py index 31313a4e6..3149a7729 100644 --- a/app/main/validators.py +++ b/app/main/validators.py @@ -14,32 +14,6 @@ class Blacklist(object): raise ValidationError(self.message) -class ValidateUserCodes(object): - def __init__(self, - expiry_msg='Code has expired', - invalid_msg='Code does not match', - code_type=None): - self.expiry_msg = expiry_msg - self.invalid_msg = invalid_msg - self.code_type = code_type - - def __call__(self, form, field): - # TODO would be great to do this sql query but - # not couple those parts of the code. - user_codes = getattr(form, 'user_codes', []) - valid_code = False - for code in user_codes: - if check_hash(field.data, code.code) and self.code_type == code.code_type: - if code.expiry_datetime <= datetime.now(): - raise ValidationError(self.expiry_msg) - else: - # Valid code - valid_code = True - break - if not valid_code: - raise ValidationError(self.invalid_msg) - - class CsvFileValidator(object): def __init__(self, message='Not a csv file'): diff --git a/app/main/views/code_not_received.py b/app/main/views/code_not_received.py index 56d68025e..d49da50f0 100644 --- a/app/main/views/code_not_received.py +++ b/app/main/views/code_not_received.py @@ -4,17 +4,16 @@ from flask import ( from app.main import main from app.main.dao import users_dao from app.main.forms import EmailNotReceivedForm, TextNotReceivedForm -from app.notify_client.sender import send_sms_code, send_email_code @main.route('/email-not-received', methods=['GET', 'POST']) def check_and_resend_email_code(): # TODO there needs to be a way to regenerate a session id - user = users_dao.get_user_by_email(session['user_email']) + user = users_dao.get_user_by_email(session['user_details']['email']) form = EmailNotReceivedForm(email_address=user.email_address) if form.validate_on_submit(): users_dao.update_email_address(id=user.id, email_address=form.email_address.data) - send_email_code(user_id=user.id, email=user.email_address) + users_dao.send_verify_code(user.id, 'email') return redirect(url_for('.verify')) return render_template('views/email-not-received.html', form=form) @@ -22,11 +21,11 @@ def check_and_resend_email_code(): @main.route('/text-not-received', methods=['GET', 'POST']) def check_and_resend_text_code(): # TODO there needs to be a way to regenerate a session id - user = users_dao.get_user_by_email(session['user_email']) + user = users_dao.get_user_by_email(session['user_details']['email']) form = TextNotReceivedForm(mobile_number=user.mobile_number) if form.validate_on_submit(): users_dao.update_mobile_number(id=user.id, mobile_number=form.mobile_number.data) - send_sms_code(user_id=user.id, mobile_number=user.mobile_number) + users_dao.send_verify_code(user.id, 'sms') return redirect(url_for('.verify')) return render_template('views/text-not-received.html', form=form) @@ -39,6 +38,6 @@ def verification_code_not_received(): @main.route('/send-new-code', methods=['GET']) def check_and_resend_verification_code(): # TODO there needs to be a way to generate a new session id - user = users_dao.get_user_by_email(session['user_email']) - send_sms_code(user.id, user.mobile_number) + user = users_dao.get_user_by_email(session['user_details']['email']) + users_dao.send_verify_code(user.id, 'sms') return redirect(url_for('main.two_factor')) diff --git a/app/main/views/new_password.py b/app/main/views/new_password.py index 7a46f5c18..941f34b83 100644 --- a/app/main/views/new_password.py +++ b/app/main/views/new_password.py @@ -3,7 +3,7 @@ from flask import (render_template, url_for, redirect, flash) from app.main import main from app.main.dao import users_dao from app.main.forms import NewPasswordForm -from app.notify_client.sender import check_token, send_sms_code +from app.notify_client.sender import check_token @main.route('/new-password/', methods=['GET', 'POST']) @@ -22,7 +22,7 @@ def new_password(token): if form.validate_on_submit(): users_dao.update_password(user, form.new_password.data) - send_sms_code(user.id, user.mobile_number) + users_dao.send_verify_code(user.id, 'sms') return redirect(url_for('main.two_factor')) else: return render_template('views/new-password.html', token=token, form=form, user=user) diff --git a/app/main/views/register.py b/app/main/views/register.py index 6d67a976a..b69958955 100644 --- a/app/main/views/register.py +++ b/app/main/views/register.py @@ -4,7 +4,8 @@ from flask import ( render_template, redirect, session, - abort + abort, + url_for ) from client.errors import HTTPError @@ -15,10 +16,6 @@ from app.main.forms import RegisterUserForm from app import user_api_client -# TODO how do we handle duplicate unverifed email addresses? -# malicious or otherwise. -from app.notify_client.sender import send_sms_code, send_email_code - @main.route('/register', methods=['GET', 'POST']) def register(): @@ -41,10 +38,10 @@ def register(): # How do we report to the user there is a problem with # sending codes apart from service unavailable? # at the moment i believe http 500 is fine. - send_sms_code(user_id=user.id, mobile_number=user.mobile_number) - send_email_code(user_id=user.id, email=user.email_address) + users_dao.send_verify_code(user.id, 'sms') + users_dao.send_verify_code(user.id, 'email') session['expiry_date'] = str(datetime.now() + timedelta(hours=1)) session['user_details'] = {"email": user.email_address, "id": user.id} - return redirect('/verify') + return redirect(url_for('main.verify')) return render_template('views/register.html', form=form) diff --git a/app/main/views/sign_in.py b/app/main/views/sign_in.py index 74a944a08..184621b4e 100644 --- a/app/main/views/sign_in.py +++ b/app/main/views/sign_in.py @@ -6,32 +6,25 @@ from flask import ( abort ) - from app.main import main from app.main.dao import users_dao from app.main.forms import LoginForm -from app.notify_client.sender import send_sms_code @main.route('/sign-in', methods=(['GET', 'POST'])) def sign_in(): - try: - form = LoginForm() - if form.validate_on_submit(): - user = users_dao.get_user_by_email(form.email_address.data) - if user: - if not user.is_locked() and user.is_active() and users_dao.verify_password(user, form.password.data): - send_sms_code(user.id, user.mobile_number) - session['user_email'] = user.email_address - return redirect(url_for('.two_factor')) - else: - # TODO re wire this increment to api - users_dao.increment_failed_login_count(user.id) - # Vague error message for login - form.password.errors.append('Username or password is incorrect') + form = LoginForm() + if form.validate_on_submit(): + user = users_dao.get_user_by_email(form.email_address.data) + if user: + if not user.is_locked() and user.is_active() and users_dao.verify_password(user, form.password.data): + users_dao.send_verify_code(user.id, 'sms') + session['user_details'] = {"email": user.email_address, "id": user.id} + return redirect(url_for('.two_factor')) + else: + # TODO re wire this increment to api + users_dao.increment_failed_login_count(user.id) + # Vague error message for login + form.password.errors.append('Username or password is incorrect') - return render_template('views/signin.html', form=form) - except: - import traceback - traceback.print_exc() - abort(500) + return render_template('views/signin.html', form=form) diff --git a/app/main/views/two_factor.py b/app/main/views/two_factor.py index 82b828391..992c520c8 100644 --- a/app/main/views/two_factor.py +++ b/app/main/views/two_factor.py @@ -5,19 +5,23 @@ from flask import ( from flask_login import login_user from app.main import main -from app.main.dao import users_dao, verify_codes_dao +from app.main.dao import users_dao from app.main.forms import TwoFactorForm @main.route('/two-factor', methods=['GET', 'POST']) def two_factor(): # TODO handle user_email not in session - user = users_dao.get_user_by_email(session['user_email']) - codes = verify_codes_dao.get_codes(user.id) - form = TwoFactorForm(codes) + user_id = session['user_details']['id'] + + def _check_code(code): + return users_dao.check_verify_code(user_id, code, "sms") + + form = TwoFactorForm(_check_code) if form.validate_on_submit(): - verify_codes_dao.use_code_for_user_and_type(user_id=user.id, code_type='sms') + del session['user_details'] + user = users_dao.get_user_by_id(user_id) login_user(user) return redirect(url_for('.choose_service')) diff --git a/app/main/views/user_profile.py b/app/main/views/user_profile.py index 7849a8133..8e2f4ddde 100644 --- a/app/main/views/user_profile.py +++ b/app/main/views/user_profile.py @@ -1,8 +1,10 @@ from flask import ( request, render_template, redirect, url_for, session) from flask.ext.login import current_user +from flask_login import login_required from app.main import main -from app.main.dao.users_dao import (verify_password, update_user) +from app.main.dao.users_dao import ( + verify_password, update_user, check_verify_code, is_email_unique) from app.main.forms import ( ChangePasswordForm, ChangeNameForm, ChangeEmailForm, ConfirmEmailForm, ChangeMobileNumberForm, ConfirmMobileNumberForm, ConfirmPasswordForm @@ -15,17 +17,19 @@ NEW_MOBILE_PASSWORD_CONFIRMED = 'new-mob-password-confirmed' @main.route("/user-profile") +@login_required def user_profile(): return render_template('views/user-profile.html') @main.route("/user-profile/name", methods=['GET', 'POST']) +@login_required def user_profile_name(): form = ChangeNameForm(new_name=current_user.name) if form.validate_on_submit(): - current_user.name = form.new_name + current_user.name = form.new_name.data update_user(current_user) return redirect(url_for('.user_profile')) @@ -37,9 +41,13 @@ def user_profile_name(): @main.route("/user-profile/email", methods=['GET', 'POST']) +@login_required def user_profile_email(): - form = ChangeEmailForm(email_address=current_user.email_address) + def _is_email_unique(email): + return is_email_unique(email) + form = ChangeEmailForm(_is_email_unique, + email_address=current_user.email_address) if form.validate_on_submit(): session[NEW_EMAIL] = form.email_address.data @@ -52,6 +60,7 @@ def user_profile_email(): @main.route("/user-profile/email/authenticate", methods=['GET', 'POST']) +@login_required def user_profile_email_authenticate(): # Validate password for form @@ -75,18 +84,21 @@ def user_profile_email_authenticate(): @main.route("/user-profile/email/confirm", methods=['GET', 'POST']) +@login_required def user_profile_email_confirm(): - # TODO add verify code support - form = ConfirmEmailForm() + # Validate verify code for form + def _check_code(cde): + return check_verify_code(current_user.id, cde, 'email') + form = ConfirmEmailForm(_check_code) if NEW_EMAIL_PASSWORD_CONFIRMED not in session: return redirect('main.user_profile_email_authenticate') if form.validate_on_submit(): + current_user.email_address = session[NEW_EMAIL] del session[NEW_EMAIL] del session[NEW_EMAIL_PASSWORD_CONFIRMED] - current_user.email_address = session['new_email'] update_user(current_user) return redirect(url_for('.user_profile')) @@ -98,6 +110,7 @@ def user_profile_email_confirm(): @main.route("/user-profile/mobile-number", methods=['GET', 'POST']) +@login_required def user_profile_mobile_number(): form = ChangeMobileNumberForm(mobile_number=current_user.mobile_number) @@ -114,6 +127,7 @@ def user_profile_mobile_number(): @main.route("/user-profile/mobile-number/authenticate", methods=['GET', 'POST']) +@login_required def user_profile_mobile_number_authenticate(): # Validate password for form @@ -137,14 +151,22 @@ def user_profile_mobile_number_authenticate(): @main.route("/user-profile/mobile-number/confirm", methods=['GET', 'POST']) +@login_required def user_profile_mobile_number_confirm(): - form = ConfirmMobileNumberForm() + # Validate verify code for form + def _check_code(cde): + return check_verify_code(current_user, cde, 'sms') + + if NEW_MOBILE_PASSWORD_CONFIRMED not in session: + return redirect(url_for('.user_profile_mobile_number')) + + form = ConfirmMobileNumberForm(_check_code) if form.validate_on_submit(): + current_user.mobile = session[NEW_MOBILE] del session[NEW_MOBILE] del session[NEW_MOBILE_PASSWORD_CONFIRMED] - current_user.mobile_user update_user(current_user) return redirect(url_for('.user_profile')) @@ -156,11 +178,17 @@ def user_profile_mobile_number_confirm(): @main.route("/user-profile/password", methods=['GET', 'POST']) +@login_required def user_profile_password(): - form = ChangePasswordForm() + # Validate password for form + def _check_password(pwd): + return verify_password(current_user, pwd) + form = ChangePasswordForm(_check_password) if form.validate_on_submit(): + current_user.set_password(form.new_password.data) + update_user(current_user) return redirect(url_for('.user_profile')) return render_template( diff --git a/app/main/views/verify.py b/app/main/views/verify.py index 0a55ddc74..e2dce93a0 100644 --- a/app/main/views/verify.py +++ b/app/main/views/verify.py @@ -11,7 +11,7 @@ from client.errors import HTTPError from flask_login import login_user from app.main import main -from app.main.dao import users_dao, verify_codes_dao +from app.main.dao import users_dao from app.main.forms import VerifyForm @@ -20,12 +20,11 @@ def verify(): # TODO there needs to be a way to regenerate a session id # or handle gracefully. user_id = session['user_details']['id'] - codes = verify_codes_dao.get_codes(user_id) - form = VerifyForm(codes) - if form.validate_on_submit(): - verify_codes_dao.use_code_for_user_and_type(user_id=user_id, code_type='email') - verify_codes_dao.use_code_for_user_and_type(user_id=user_id, code_type='sms') + def _check_code(code, code_type): + return users_dao.check_verify_code(user_id, code, code_type) + form = VerifyForm(_check_code) + if form.validate_on_submit(): try: user = users_dao.get_user_by_id(user_id) activated_user = users_dao.activate_user(user) diff --git a/app/models.py b/app/models.py deleted file mode 100644 index 14afac5f5..000000000 --- a/app/models.py +++ /dev/null @@ -1,127 +0,0 @@ -import datetime -from app import db -from flask import current_app - -DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ" -DATE_FORMAT = "%Y-%m-%d" - - -class VerifyCodes(db.Model): - __tablename__ = 'verify_codes' - - code_types = ['email', 'sms'] - - id = db.Column(db.Integer, primary_key=True) - user_id = db.Column(db.Integer, index=True, unique=False, nullable=False) - code = db.Column(db.String, nullable=False) - code_type = db.Column(db.Enum(code_types, name='verify_code_types'), index=False, unique=False, nullable=False) - expiry_datetime = db.Column(db.DateTime, nullable=False) - code_used = db.Column(db.Boolean, default=False) - - -class Roles(db.Model): - __tablename__ = 'roles' - - id = db.Column(db.Integer, primary_key=True) - role = db.Column(db.String, nullable=False, unique=True) - - -class User(db.Model): - __tablename__ = 'users' - - id = db.Column(db.Integer, primary_key=True) - name = db.Column(db.String, nullable=False, index=True, unique=False) - email_address = db.Column(db.String(255), nullable=False, index=True, unique=True) - password = db.Column(db.String, index=False, unique=False, nullable=False) - mobile_number = db.Column(db.String, index=False, unique=False, nullable=False) - created_at = db.Column(db.DateTime, - index=False, - unique=False, - nullable=False, - default=datetime.datetime.now) - updated_at = db.Column(db.DateTime, - index=False, - unique=False, - nullable=True, - onupdate=datetime.datetime.now) - password_changed_at = db.Column(db.DateTime, index=False, unique=False, nullable=True) - role_id = db.Column(db.Integer, db.ForeignKey('roles.id'), index=True, unique=False, nullable=False) - logged_in_at = db.Column(db.DateTime, nullable=True) - failed_login_count = db.Column(db.Integer, nullable=False, default=0) - # TODO should this be an enum? - state = db.Column(db.String, nullable=False, default='pending') - - def serialize(self): - serialized = { - 'id': self.id, - 'name': self.name, - 'emailAddress': self.email_address, - 'locked': self.failed_login_count > current_app.config['MAX_FAILED_LOGIN_COUNT'], - 'createdAt': self.created_at.strftime(DATETIME_FORMAT), - 'updatedAt': self.updated_at.strftime(DATETIME_FORMAT), - 'role': self.role, - 'passwordChangedAt': self.password_changed_at.strftime(DATETIME_FORMAT), - 'failedLoginCount': self.failed_login_count - } - - return filter_null_value_fields(serialized) - - def is_authenticated(self): - return True - - def is_active(self): - if self.state != 'active': - return False - else: - return True - - def is_anonymous(self): - return False - - def get_id(self): - return self.id - - def is_locked(self): - if self.failed_login_count < current_app.config['MAX_FAILED_LOGIN_COUNT']: - return False - else: - return True - - -# user_to_service = db.Table( -# 'user_to_service', -# db.Model.metadata, -# db.Column('user_id', db.Integer, db.ForeignKey('users.id')), -# db.Column('service_id', db.Integer, db.ForeignKey('services.id')) -# ) - - -# class Service(db.Model): -# __tablename__ = 'services' - -# id = db.Column(db.Integer, primary_key=True) -# name = db.Column(db.String(255), nullable=False, unique=True) -# created_at = db.Column(db.DateTime, index=False, unique=False, nullable=False) -# active = db.Column(db.Boolean, index=False, unique=False, nullable=False) -# limit = db.Column(db.BigInteger, index=False, unique=False, nullable=False) -# users = db.relationship('User', secondary=user_to_service, backref=db.backref('user_to_service', lazy='dynamic')) -# restricted = db.Column(db.Boolean, index=False, unique=False, nullable=False) - -# def serialize(self): -# serialized = { -# 'id': self.id, -# 'name': self.name, -# 'createdAt': self.created_at.strftime(DATETIME_FORMAT), -# 'active': self.active, -# 'restricted': self.restricted, -# 'limit': self.limit, -# 'user': self.users.serialize() -# } - -# return filter_null_value_fields(serialized) - - -def filter_null_value_fields(obj): - return dict( - filter(lambda x: x[1] is not None, obj.items()) - ) diff --git a/app/notify_client/sender.py b/app/notify_client/sender.py index 85acc4080..3ae7951bf 100644 --- a/app/notify_client/sender.py +++ b/app/notify_client/sender.py @@ -1,32 +1,9 @@ from random import randint from flask import url_for, current_app from itsdangerous import URLSafeTimedSerializer, SignatureExpired -from app.main.dao import verify_codes_dao from app import notifications_api_client -def create_verify_code(): - return ''.join(["%s" % randint(0, 9) for _ in range(0, 5)]) - - -def send_sms_code(user_id, mobile_number): - sms_code = create_verify_code() - verify_codes_dao.add_code(user_id=user_id, code=sms_code, code_type='sms') - notifications_api_client.send_sms(mobile_number=mobile_number, - message=sms_code) - return sms_code - - -def send_email_code(user_id, email): - email_code = create_verify_code() - verify_codes_dao.add_code(user_id=user_id, code=email_code, code_type='email') - notifications_api_client.send_email(email_address=email, - from_address='notify@digital.cabinet-office.gov.uk', - message=email_code, - subject='Verification code') - return email_code - - def send_change_password_email(email): link_to_change_password = url_for('.new_password', token=generate_token(email), _external=True) notifications_api_client.send_email(email_address=email, diff --git a/app/notify_client/user_api_client.py b/app/notify_client/user_api_client.py index 71dd8a2fa..1120d40a3 100644 --- a/app/notify_client/user_api_client.py +++ b/app/notify_client/user_api_client.py @@ -70,6 +70,25 @@ class UserApiClient(BaseAPIClient): return user[0] return None + def send_verify_code(self, user_id, code_type): + data = {'code_type': code_type} + endpoint = '/user/{}/code'.format(user_id) + resp = self.post(endpoint, data=data) + + def check_verify_code(self, user_id, code, code_type): + data = {'code_type': code_type, 'code': code} + endpoint = '/user/{}/verify/code'.format(user_id) + try: + resp = self.post(endpoint, data=data) + return True, '' + except HTTPError as e: + if e.status_code == 400 or e.status_code == 404: + if 'Code not found' in e.message: + return False, 'Code not found' + elif 'Code has expired' in e.message: + return False, 'Code has expired' + raise e + class User(object): def __init__(self, fields, max_failed_login_count=3): @@ -85,14 +104,26 @@ class User(object): def name(self): return self.fields.get('name') + @name.setter + def name(self, name): + self.fields['name'] = name + @property def email_address(self): return self.fields.get('email_address') + @email_address.setter + def email_address(self, email_address): + self.fields['email_address'] = email_address + @property def mobile_number(self): return self.fields.get('mobile_number') + @mobile_number.setter + def mobile_number(self, mobile_number): + self.fields['mobile_number'] = mobile_number + @property def password_changed_at(self): return self.fields.get('password_changed_at') @@ -130,3 +161,6 @@ class User(object): def serialize(self): return self.fields + + def set_password(self, pwd): + self.fields['password'] = pwd diff --git a/migrations/README b/migrations/README deleted file mode 100755 index 98e4f9c44..000000000 --- a/migrations/README +++ /dev/null @@ -1 +0,0 @@ -Generic single-database configuration. \ No newline at end of file diff --git a/migrations/alembic.ini b/migrations/alembic.ini deleted file mode 100644 index 0006599ec..000000000 --- a/migrations/alembic.ini +++ /dev/null @@ -1,45 +0,0 @@ -# A generic, single database configuration. - -[alembic] -# template used to generate migration files -# file_template = %%(rev)s_%%(slug)s - -# set to 'true' to run the environment during -# the 'revision' command, regardless of autogenerate -# revision_environment = false - - -# Logging configuration -[loggers] -keys = root,sqlalchemy,alembic - -[handlers] -keys = console - -[formatters] -keys = generic - -[logger_root] -level = WARN -handlers = -qualname = - -[logger_sqlalchemy] -level = WARN -handlers = -qualname = sqlalchemy.engine - -[logger_alembic] -level = INFO -handlers = -qualname = alembic - -[handler_console] -class = StreamHandler -args = (sys.stderr,) -level = NOTSET -formatter = generic - -[formatter_generic] -format = %(levelname)-5.5s [%(name)s] %(message)s -datefmt = %H:%M:%S diff --git a/migrations/env.py b/migrations/env.py deleted file mode 100644 index 70961ce2c..000000000 --- a/migrations/env.py +++ /dev/null @@ -1,73 +0,0 @@ -from __future__ import with_statement -from alembic import context -from sqlalchemy import engine_from_config, pool -from logging.config import fileConfig - -# this is the Alembic Config object, which provides -# access to the values within the .ini file in use. -config = context.config - -# Interpret the config file for Python logging. -# This line sets up loggers basically. -fileConfig(config.config_file_name) - -# add your model's MetaData object here -# for 'autogenerate' support -# from myapp import mymodel -# target_metadata = mymodel.Base.metadata -from flask import current_app -config.set_main_option('sqlalchemy.url', current_app.config.get('SQLALCHEMY_DATABASE_URI')) -target_metadata = current_app.extensions['migrate'].db.metadata - -# other values from the config, defined by the needs of env.py, -# can be acquired: -# my_important_option = config.get_main_option("my_important_option") -# ... etc. - -def run_migrations_offline(): - """Run migrations in 'offline' mode. - - This configures the context with just a URL - and not an Engine, though an Engine is acceptable - here as well. By skipping the Engine creation - we don't even need a DBAPI to be available. - - Calls to context.execute() here emit the given string to the - script output. - - """ - url = config.get_main_option("sqlalchemy.url") - context.configure(url=url) - - with context.begin_transaction(): - context.run_migrations() - -def run_migrations_online(): - """Run migrations in 'online' mode. - - In this scenario we need to create an Engine - and associate a connection with the context. - - """ - engine = engine_from_config( - config.get_section(config.config_ini_section), - prefix='sqlalchemy.', - poolclass=pool.NullPool) - - connection = engine.connect() - context.configure( - connection=connection, - target_metadata=target_metadata - ) - - try: - with context.begin_transaction(): - context.run_migrations() - finally: - connection.close() - -if context.is_offline_mode(): - run_migrations_offline() -else: - run_migrations_online() - diff --git a/migrations/script.py.mako b/migrations/script.py.mako deleted file mode 100755 index 95702017e..000000000 --- a/migrations/script.py.mako +++ /dev/null @@ -1,22 +0,0 @@ -"""${message} - -Revision ID: ${up_revision} -Revises: ${down_revision} -Create Date: ${create_date} - -""" - -# revision identifiers, used by Alembic. -revision = ${repr(up_revision)} -down_revision = ${repr(down_revision)} - -from alembic import op -import sqlalchemy as sa -${imports if imports else ""} - -def upgrade(): - ${upgrades if upgrades else "pass"} - - -def downgrade(): - ${downgrades if downgrades else "pass"} diff --git a/migrations/versions/10_create_users.py b/migrations/versions/10_create_users.py deleted file mode 100644 index fbd32f153..000000000 --- a/migrations/versions/10_create_users.py +++ /dev/null @@ -1,42 +0,0 @@ -"""empty message - -Revision ID: 10_create_users -Revises: None -Create Date: 2015-11-24 10:39:19.827534 - -""" - -# revision identifiers, used by Alembic. -revision = '10_create_users' -down_revision = None - -from alembic import op -import sqlalchemy as sa - - -def upgrade(): - op.create_table('roles', - sa.Column('id', sa.Integer, primary_key=True), - sa.Column('role', sa.String, nullable=False, unique=True) - ) - - op.create_table('users', - sa.Column('id', sa.Integer, primary_key=True), - sa.Column('name', sa.String, nullable=False, unique=True), - sa.Column('email_address', sa.String(length=255), nullable=False), - sa.Column('password', sa.String, nullable=False), - sa.Column('mobile_number', sa.String, nullable=False), - sa.Column('created_at', sa.DateTime, nullable=False), - sa.Column('updated_at', sa.DateTime), - sa.Column('password_changed_at', sa.DateTime), - sa.Column('role_id', sa.Integer, nullable=False), - sa.Column('logged_in_at', sa.DateTime), - sa.Column('failed_login_count', sa.Integer, nullable=False), - sa.Column('state', sa.String, default='pending'), - sa.ForeignKeyConstraint(['role_id'], ['roles.id']) - ) - - -def downgrade(): - op.drop_table('users') - op.drop_table('roles') diff --git a/migrations/versions/20_initialise_data.py b/migrations/versions/20_initialise_data.py deleted file mode 100644 index ce5726cfc..000000000 --- a/migrations/versions/20_initialise_data.py +++ /dev/null @@ -1,14 +0,0 @@ -# revision identifiers, used by Alembic. -revision = '20_initialise_data' -down_revision = '10_create_users' -from app.models import Roles -from alembic import op - -def upgrade(): - op.execute("insert into roles(role) values('platform_admin')") - op.execute("insert into roles(role) values('service_user')") - - -def downgrade(): - op.drop_table('users') - op.drop_table('roles') diff --git a/migrations/versions/30_update_indexes.py b/migrations/versions/30_update_indexes.py deleted file mode 100644 index afb4603be..000000000 --- a/migrations/versions/30_update_indexes.py +++ /dev/null @@ -1,36 +0,0 @@ -"""empty message - -Revision ID: 1691efaa342 -Revises: 30_update_indexes.py -Create Date: 2015-12-09 13:15:00.773848 - -""" - -# revision identifiers, used by Alembic. -revision = '30_update_indexes' -down_revision = '20_initialise_data' - -from alembic import op -import sqlalchemy as sa - - -def upgrade(): - ### commands auto generated by Alembic - please adjust! ### - op.alter_column('users', 'state', - existing_type=sa.VARCHAR(), - nullable=False) - op.create_index(op.f('ix_users_email_address'), 'users', ['email_address'], unique=False) - op.create_index(op.f('ix_users_name'), 'users', ['name'], unique=False) - op.create_index(op.f('ix_users_role_id'), 'users', ['role_id'], unique=False) - ### end Alembic commands ### - - -def downgrade(): - ### commands auto generated by Alembic - please adjust! ### - op.drop_index(op.f('ix_users_role_id'), table_name='users') - op.drop_index(op.f('ix_users_name'), table_name='users') - op.drop_index(op.f('ix_users_email_address'), table_name='users') - op.alter_column('users', 'state', - existing_type=sa.VARCHAR(), - nullable=True) - ### end Alembic commands ### diff --git a/migrations/versions/40_verify_codes.py b/migrations/versions/40_verify_codes.py deleted file mode 100644 index 01ed89897..000000000 --- a/migrations/versions/40_verify_codes.py +++ /dev/null @@ -1,42 +0,0 @@ -"""empty message - -Revision ID: 40_verify_codes -Revises: 30_update_indexes -Create Date: 2015-12-09 16:39:44.673094 - -""" - -# revision identifiers, used by Alembic. -revision = '40_verify_codes' -down_revision = '30_update_indexes' - -from alembic import op -import sqlalchemy as sa - - -def upgrade(): - code_type_enum = sa.Enum('email', 'sms', name='verify_code_types') - # code_type_enum.create(op.get_bind(), checkfirst=False) - - op.create_table('verify_codes', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('user_id', sa.Integer(), nullable=False), - sa.Column('code', sa.String, nullable=False), - sa.Column('code_type', code_type_enum), - sa.Column('expiry_datetime', sa.DateTime(), nullable=False), - sa.Column('code_used', sa.Boolean(), nullable=True), - sa.ForeignKeyConstraint(['user_id'], ['users.id'], ), - sa.PrimaryKeyConstraint('id') - ) - op.create_index(op.f('ix_verify_codes_user_id'), 'verify_codes', ['user_id'], unique=False) - op.drop_constraint('users_name_key', 'users', type_='unique') - ### end Alembic commands ### - - -def downgrade(): - ### commands auto generated by Alembic - please adjust! ### - op.create_unique_constraint('users_name_key', 'users', ['name']) - op.drop_index(op.f('ix_verify_codes_user_id'), table_name='verify_codes') - op.drop_table('verify_codes') - op.execute('DROP TYPE verify_code_types') - ### end Alembic commands ### diff --git a/migrations/versions/50_alter_verify_code_type.py b/migrations/versions/50_alter_verify_code_type.py deleted file mode 100644 index 3915238b7..000000000 --- a/migrations/versions/50_alter_verify_code_type.py +++ /dev/null @@ -1,30 +0,0 @@ -"""empty message - -Revision ID: 50_alter_verify_code_type -Revises: 40_verify_codes -Create Date: 2015-12-11 10:21:24.098275 - -""" - -# revision identifiers, used by Alembic. -revision = '50_alter_verify_code_type' -down_revision = '40_verify_codes' - -from alembic import op -import sqlalchemy as sa -from sqlalchemy.dialects import postgresql - -def upgrade(): - ### commands auto generated by Alembic - please adjust! ### - op.alter_column('verify_codes', 'code_type', - existing_type=postgresql.ENUM('email', 'sms', name='verify_code_types'), - nullable=False) - ### end Alembic commands ### - - -def downgrade(): - ### commands auto generated by Alembic - please adjust! ### - op.alter_column('verify_codes', 'code_type', - existing_type=postgresql.ENUM('email', 'sms', name='verify_code_types'), - nullable=True) - ### end Alembic commands ### diff --git a/migrations/versions/60_add_service.py b/migrations/versions/60_add_service.py deleted file mode 100644 index 5dd28284d..000000000 --- a/migrations/versions/60_add_service.py +++ /dev/null @@ -1,42 +0,0 @@ -"""empty message - -Revision ID: 60_add_service -Revises: 50_alter_verify_code_type -Create Date: 2015-12-15 09:25:09.000431 - -""" - -# revision identifiers, used by Alembic. -revision = '60_add_service' -down_revision = '50_alter_verify_code_type' - -from alembic import op -import sqlalchemy as sa - - -def upgrade(): - ### commands auto generated by Alembic - please adjust! ### - op.create_table('services', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('name', sa.String(length=255), nullable=False), - sa.Column('created_at', sa.DateTime(), nullable=False), - sa.Column('active', sa.Boolean(), nullable=False), - sa.Column('limit', sa.BigInteger(), nullable=False), - sa.Column('restricted', sa.Boolean(), nullable=False), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('name') - ) - op.create_table('user_to_service', - sa.Column('user_id', sa.Integer(), nullable=True), - sa.Column('service_id', sa.Integer(), nullable=True), - sa.ForeignKeyConstraint(['service_id'], ['services.id'], ), - sa.ForeignKeyConstraint(['user_id'], ['users.id'], ) - ) - ### end Alembic commands ### - - -def downgrade(): - ### commands auto generated by Alembic - please adjust! ### - op.drop_table('user_to_service') - op.drop_table('services') - ### end Alembic commands ### diff --git a/migrations/versions/70_unique_email.py b/migrations/versions/70_unique_email.py deleted file mode 100644 index 7a186dd73..000000000 --- a/migrations/versions/70_unique_email.py +++ /dev/null @@ -1,28 +0,0 @@ -"""empty message - -Revision ID: 70_unique_email -Revises: 60_add_service -Create Date: 2015-12-17 14:46:52.831849 - -""" - -# revision identifiers, used by Alembic. -revision = '70_unique_email' -down_revision = '60_add_service' - -from alembic import op -import sqlalchemy as sa - - -def upgrade(): - ### commands auto generated by Alembic - please adjust! ### - op.drop_index('ix_users_email_address', table_name='users') - op.create_index(op.f('ix_users_email_address'), 'users', ['email_address'], unique=True) - ### end Alembic commands ### - - -def downgrade(): - ### commands auto generated by Alembic - please adjust! ### - op.drop_index(op.f('ix_users_email_address'), table_name='users') - op.create_index('ix_users_email_address', 'users', ['email_address'], unique=False) - ### end Alembic commands ### diff --git a/migrations/versions/80_remove_services.py b/migrations/versions/80_remove_services.py deleted file mode 100644 index ceb668cc8..000000000 --- a/migrations/versions/80_remove_services.py +++ /dev/null @@ -1,43 +0,0 @@ -"""empty message - -Revision ID: 80_remove_services -Revises: 70_unique_email -Create Date: 2016-01-14 17:48:42.426979 - -""" - -# revision identifiers, used by Alembic. -revision = '80_remove_services' -down_revision = '70_unique_email' - -from alembic import op -import sqlalchemy as sa -from sqlalchemy.dialects import postgresql - -def upgrade(): - ### commands auto generated by Alembic - please adjust! ### - op.drop_table('user_to_service') - op.drop_table('services') - ### end Alembic commands ### - - -def downgrade(): - ### commands auto generated by Alembic - please adjust! ### - op.create_table('services', - sa.Column('id', sa.INTEGER(), server_default=sa.text("nextval('services_id_seq'::regclass)"), nullable=False), - sa.Column('name', sa.VARCHAR(length=255), autoincrement=False, nullable=False), - sa.Column('created_at', postgresql.TIMESTAMP(), autoincrement=False, nullable=False), - sa.Column('active', sa.BOOLEAN(), autoincrement=False, nullable=False), - sa.Column('limit', sa.BIGINT(), autoincrement=False, nullable=False), - sa.Column('restricted', sa.BOOLEAN(), autoincrement=False, nullable=False), - sa.PrimaryKeyConstraint('id', name='services_pkey'), - sa.UniqueConstraint('name', name='services_name_key'), - postgresql_ignore_search_path=False - ) - op.create_table('user_to_service', - sa.Column('user_id', sa.INTEGER(), autoincrement=False, nullable=True), - sa.Column('service_id', sa.INTEGER(), autoincrement=False, nullable=True), - sa.ForeignKeyConstraint(['service_id'], ['services.id'], name='user_to_service_service_id_fkey'), - sa.ForeignKeyConstraint(['user_id'], ['users.id'], name='user_to_service_user_id_fkey') - ) - ### end Alembic commands ### diff --git a/migrations/versions/90_remove_user_fk_verify_codes.py b/migrations/versions/90_remove_user_fk_verify_codes.py deleted file mode 100644 index efa5a0d0c..000000000 --- a/migrations/versions/90_remove_user_fk_verify_codes.py +++ /dev/null @@ -1,26 +0,0 @@ -"""empty message - -Revision ID: 90_remove_user_fk_verify_codes -Revises: 80_remove_services -Create Date: 2016-01-19 20:57:41.986177 - -""" - -# revision identifiers, used by Alembic. -revision = '90_remove_user_fk_verify_codes' -down_revision = '80_remove_services' - -from alembic import op -import sqlalchemy as sa - - -def upgrade(): - ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint('verify_codes_user_id_fkey', 'verify_codes', type_='foreignkey') - ### end Alembic commands ### - - -def downgrade(): - ### commands auto generated by Alembic - please adjust! ### - op.create_foreign_key('verify_codes_user_id_fkey', 'verify_codes', 'users', ['user_id'], ['id']) - ### end Alembic commands ### diff --git a/tests/__init__.py b/tests/__init__.py index c0806dcbb..b9ac57935 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,15 +1,18 @@ from flask.testing import FlaskClient from flask import url_for -from app.models import User -from app.main.dao import (users_dao, verify_codes_dao) +from app.main.dao import (users_dao) class TestClient(FlaskClient): def login(self, user): # Skipping authentication here and just log them in with self.session_transaction() as session: - session['user_email'] = user.email_address - verify_codes_dao.add_code(user_id=user.id, code='12345', code_type='sms') + session['user_details'] = { + "email": user.email_address, + "id": user.id} + # Include mock_login fixture in test for this to work. + # TODO would be better for it to be mocked in this + # function response = self.post( url_for('main.two_factor'), data={'sms_code': '12345'}) assert response.status_code == 302 @@ -49,12 +52,7 @@ TEST_USER_EMAIL = 'test@user.gov.uk' def create_test_user(state): - user = User(name='Test User', - password='somepassword', - email_address=TEST_USER_EMAIL, - mobile_number='+441234123412', - role_id=1, - state=state) + user = None users_dao.insert_user(user) return user @@ -73,12 +71,7 @@ def create_test_api_user(state): def create_another_test_user(state): - user = User(name='Another Test User', - password='someOtherpassword', - email_address='another_test@user.gov.uk', - mobile_number='+442233123412', - role_id=1, - state=state) + user = None users_dao.insert_user(user) return user diff --git a/tests/app/main/dao/test_roles_dao.py b/tests/app/main/dao/test_roles_dao.py deleted file mode 100644 index fbae56436..000000000 --- a/tests/app/main/dao/test_roles_dao.py +++ /dev/null @@ -1,26 +0,0 @@ -import pytest -import sqlalchemy - -from app.models import Roles -from app.main.dao import roles_dao - - -def test_insert_role_should_be_able_to_get_role(app_, db_, db_session): - role = Roles(id=1000, role='some role for test') - roles_dao.insert_role(role) - - saved_role = roles_dao.get_role_by_id(role.id) - assert saved_role == role - - -def test_insert_role_will_throw_error_if_role_already_exists(app_, - db_, - db_session): - role1 = roles_dao.get_role_by_id(1) - assert role1.id == 1 - - role = Roles(id=1, role='cannot create a duplicate') - - with pytest.raises(sqlalchemy.orm.exc.FlushError) as error: - roles_dao.insert_role(role) - assert 'conflicts with persistent instance' in str(error.value) diff --git a/tests/app/main/dao/test_service_dao.py b/tests/app/main/dao/test_service_dao.py index c51a6bb0c..a9538c8ce 100644 --- a/tests/app/main/dao/test_service_dao.py +++ b/tests/app/main/dao/test_service_dao.py @@ -3,20 +3,16 @@ import sqlalchemy from app.main.dao import services_dao -def test_can_insert_new_service(db_, - db_session, - mock_api_user, +def test_can_insert_new_service(api_user_active, mock_create_service, mock_user_dao_get_by_email): service_name = 'testing service' - id_ = services_dao.insert_new_service(service_name, mock_api_user.id) + id_ = services_dao.insert_new_service(service_name, api_user_active.id) mock_create_service.assert_called_once_with( - service_name, False, 1000, True, mock_api_user.id) + service_name, False, 1000, True, api_user_active.id) -def test_unrestrict_service_updates_the_service(db_, - db_session, - mock_get_service, +def test_unrestrict_service_updates_the_service(mock_get_service, mock_update_service, mock_user_dao_get_by_email): service_one = mock_get_service.side_effect(123)['data'] @@ -29,9 +25,7 @@ def test_unrestrict_service_updates_the_service(db_, service_one['users']) -def test_activate_service_update_service(db_, - db_session, - mock_api_user, +def test_activate_service_update_service(api_user_active, mock_get_service, mock_update_service, mock_user_dao_get_by_email): @@ -45,20 +39,20 @@ def test_activate_service_update_service(db_, service_one['users']) -def test_get_service_returns_none_if_service_does_not_exist(db_, db_session, mock_get_service): +def test_get_service_returns_none_if_service_does_not_exist(mock_get_service): mock_get_service.side_effect = lambda x: None service = services_dao.get_service_by_id(1) assert service is None -def test_find_by_service_name_returns_right_service(db_, db_session, mock_get_services): +def test_find_by_service_name_returns_right_service(mock_get_services): service_name = "service_one" service = services_dao.find_service_by_service_name(service_name) assert mock_get_services.called assert service['name'] == service_name -def test_should_return_list_of_service_names(db_, db_session, mock_get_services): +def test_should_return_list_of_service_names(mock_get_services): expected = ['service_one', 'service_two'] actual = services_dao.find_all_service_names() assert mock_get_services.called diff --git a/tests/app/main/dao/test_users_dao.py b/tests/app/main/dao/test_users_dao.py index 733b2930c..855aeb8fe 100644 --- a/tests/app/main/dao/test_users_dao.py +++ b/tests/app/main/dao/test_users_dao.py @@ -2,17 +2,18 @@ from datetime import datetime import pytest import sqlalchemy from app.main.encryption import check_hash -from app.models import User from app.main.dao import users_dao @pytest.mark.xfail(reason='Tests will be moved to api') -def test_insert_user_should_add_user(db_, db_session): - user = User(name='test insert', - password='somepassword', - email_address='test@insert.gov.uk', - mobile_number='+441234123412', - role_id=1) +def test_insert_user_should_add_user(): + # user = User(name='test insert', + # password='somepassword', + # email_address='test@insert.gov.uk', + # mobile_number='+441234123412', + # role_id=1) + + user = None users_dao.insert_user(user) saved_user = users_dao.get_user_by_id(user.id) @@ -20,24 +21,28 @@ def test_insert_user_should_add_user(db_, db_session): @pytest.mark.xfail(reason='Tests will be moved to api') -def test_insert_user_with_role_that_does_not_exist_fails(db_, db_session): - user = User(name='role does not exist', - password='somepassword', - email_address='test@insert.gov.uk', - mobile_number='+441234123412', - role_id=100) +def test_insert_user_with_role_that_does_not_exist_fails(): + # user = User(name='role does not exist', + # password='somepassword', + # email_address='test@insert.gov.uk', + # mobile_number='+441234123412', + # role_id=100) + + user = None with pytest.raises(sqlalchemy.exc.IntegrityError) as error: users_dao.insert_user(user) assert 'insert or update on table "users" violates foreign key constraint "users_role_id_fkey"' in str(error.value) @pytest.mark.xfail(reason='Not implemented yet on api client') -def test_get_user_by_email(db_, db_session): - user = User(name='test_get_by_email', - password='somepassword', - email_address='email@example.gov.uk', - mobile_number='+441234153412', - role_id=1) +def test_get_user_by_email(): + # user = User(name='test_get_by_email', + # password='somepassword', + # email_address='email@example.gov.uk', + # mobile_number='+441234153412', + # role_id=1) + + user = None users_dao.insert_user(user) retrieved = users_dao.get_user_by_email(user.email_address) @@ -45,22 +50,26 @@ def test_get_user_by_email(db_, db_session): @pytest.mark.xfail(reason='Not implemented yet on api client') -def test_get_all_users_returns_all_users(db_, db_session): - user1 = User(name='test one', - password='somepassword', - email_address='test1@get_all.gov.uk', - mobile_number='+441234123412', - role_id=1) - user2 = User(name='test two', - password='some2ndpassword', - email_address='test2@get_all.gov.uk', - mobile_number='+441234123412', - role_id=1) - user3 = User(name='test three', - password='some2ndpassword', - email_address='test3@get_all.gov.uk', - mobile_number='+441234123412', - role_id=1) +def test_get_all_users_returns_all_users(): + # user1 = User(name='test one', + # password='somepassword', + # email_address='test1@get_all.gov.uk', + # mobile_number='+441234123412', + # role_id=1) + # user2 = User(name='test two', + # password='some2ndpassword', + # email_address='test2@get_all.gov.uk', + # mobile_number='+441234123412', + # role_id=1) + # user3 = User(name='test three', + # password='some2ndpassword', + # email_address='test3@get_all.gov.uk', + # mobile_number='+441234123412', + # role_id=1) + + user1 = None + user2 = None + user3 = None users_dao.insert_user(user1) users_dao.insert_user(user2) @@ -71,12 +80,13 @@ def test_get_all_users_returns_all_users(db_, db_session): @pytest.mark.xfail(reason='Not implemented yet on api client') -def test_increment_failed_lockout_count_should_increade_count_by_1(db_, db_session): - user = User(name='cannot remember password', - password='somepassword', - email_address='test1@get_all.gov.uk', - mobile_number='+441234123412', - role_id=1) +def test_increment_failed_lockout_count_should_increade_count_by_1(): + # user = User(name='cannot remember password', + # password='somepassword', + # email_address='test1@get_all.gov.uk', + # mobile_number='+441234123412', + # role_id=1) + user = None users_dao.insert_user(user) savedUser = users_dao.get_user_by_id(user.id) @@ -86,12 +96,13 @@ def test_increment_failed_lockout_count_should_increade_count_by_1(db_, db_sessi @pytest.mark.xfail(reason='Not implemented yet on api client') -def test_user_is_locked_if_failed_login_count_is_10_or_greater(db_, db_session): - user = User(name='cannot remember password', - password='somepassword', - email_address='test1@get_all.gov.uk', - mobile_number='+441234123412', - role_id=1) +def test_user_is_locked_if_failed_login_count_is_10_or_greater(): + # user = User(name='cannot remember password', + # password='somepassword', + # email_address='test1@get_all.gov.uk', + # mobile_number='+441234123412', + # role_id=1) + user = None users_dao.insert_user(user) saved_user = users_dao.get_user_by_id(user.id) assert saved_user.is_locked() is False @@ -105,13 +116,14 @@ def test_user_is_locked_if_failed_login_count_is_10_or_greater(db_, db_session): @pytest.mark.xfail(reason='Not implemented yet on api client') -def test_user_is_active_is_false_if_state_is_inactive(db_, db_session): - user = User(name='inactive user', - password='somepassword', - email_address='test1@get_all.gov.uk', - mobile_number='+441234123412', - role_id=1, - state='inactive') +def test_user_is_active_is_false_if_state_is_inactive(): + # user = User(name='inactive user', + # password='somepassword', + # email_address='test1@get_all.gov.uk', + # mobile_number='+441234123412', + # role_id=1, + # state='inactive') + user = None users_dao.insert_user(user) saved_user = users_dao.get_user_by_id(user.id) @@ -132,20 +144,21 @@ def test_should_update_user_to_active(mock_activate_user): @pytest.mark.xfail(reason='Not implemented yet on api client') -def test_should_throws_error_when_id_does_not_exist(db_, db_session): +def test_should_throws_error_when_id_does_not_exist(): with pytest.raises(AttributeError) as error: users_dao.activate_user(123) assert '''object has no attribute 'state''''' in str(error.value) @pytest.mark.xfail(reason='Not implemented yet on api client') -def test_should_update_email_address(db_, db_session): - user = User(name='Update Email', - password='somepassword', - email_address='test@it.gov.uk', - mobile_number='+441234123412', - role_id=1, - state='inactive') +def test_should_update_email_address(): + # user = User(name='Update Email', + # password='somepassword', + # email_address='test@it.gov.uk', + # mobile_number='+441234123412', + # role_id=1, + # state='inactive') + user = None users_dao.insert_user(user) saved = users_dao.get_user_by_id(user.id) @@ -156,13 +169,14 @@ def test_should_update_email_address(db_, db_session): @pytest.mark.xfail(reason='Not implemented yet on api client') -def test_should_update_password(db_, db_session): - user = User(name='Update Email', - password='somepassword', - email_address='test@it.gov.uk', - mobile_number='+441234123412', - role_id=1, - state='active') +def test_should_update_password(): + # user = User(name='Update Email', + # password='somepassword', + # email_address='test@it.gov.uk', + # mobile_number='+441234123412', + # role_id=1, + # state='active') + user = None start = datetime.now() users_dao.insert_user(user) @@ -177,19 +191,21 @@ def test_should_update_password(db_, db_session): @pytest.mark.xfail(reason='Not implemented yet on api client') -def test_should_return_list_of_all_email_addresses(db_, db_session): - first = User(name='First Person', - password='somepassword', - email_address='first@it.gov.uk', - mobile_number='+441234123412', - role_id=1, - state='active') - second = User(name='Second Person', - password='somepassword', - email_address='second@it.gov.uk', - mobile_number='+441234123412', - role_id=1, - state='active') +def test_should_return_list_of_all_email_addresses(): + # first = User(name='First Person', + # password='somepassword', + # email_address='first@it.gov.uk', + # mobile_number='+441234123412', + # role_id=1, + # state='active') + first = None + # second = User(name='Second Person', + # password='somepassword', + # email_address='second@it.gov.uk', + # mobile_number='+441234123412', + # role_id=1, + # state='active') + second = None users_dao.insert_user(first) users_dao.insert_user(second) @@ -199,13 +215,14 @@ def test_should_return_list_of_all_email_addresses(db_, db_session): @pytest.mark.xfail(reason='Not implemented yet on api client') -def test_should_update_state_to_request_password_reset(db_, db_session): - user = User(name='Requesting Password Resest', - password='somepassword', - email_address='request@new_password.gov.uk', - mobile_number='+441234123412', - role_id=1, - state='active') +def test_should_update_state_to_request_password_reset(): + # user = User(name='Requesting Password Resest', + # password='somepassword', + # email_address='request@new_password.gov.uk', + # mobile_number='+441234123412', + # role_id=1, + # state='active') + user = None users_dao.insert_user(user) users_dao.request_password_reset(user.email_address) saved = users_dao.get_user_by_email(user.email_address) diff --git a/tests/app/main/dao/test_verify_codes_dao.py b/tests/app/main/dao/test_verify_codes_dao.py deleted file mode 100644 index 0bd825d19..000000000 --- a/tests/app/main/dao/test_verify_codes_dao.py +++ /dev/null @@ -1,37 +0,0 @@ -import sqlalchemy -from pytest import fail - -from app.main.dao import verify_codes_dao -from app.main.encryption import check_hash - - -def test_insert_new_code_and_get_it_back(app_, db_, db_session): - - verify_codes_dao.add_code(user_id=1, code='12345', code_type='email') - saved_codes = verify_codes_dao.get_codes(user_id=1, code_type='email') - assert len(saved_codes) == 1 - saved_code = saved_codes[0] - assert saved_code.user_id == 1 - assert check_hash('12345', saved_code.code) - assert saved_code.code_type == 'email' - assert saved_code.code_used is False - - -def test_insert_new_code_should_thrw_exception_when_type_does_not_exist(app_, - db_, - db_session): - try: - verify_codes_dao.add_code(user_id=1, code='23545', code_type='not_real') - fail('Should have thrown an exception') - except sqlalchemy.exc.DataError as e: - assert 'invalid input value for enum verify_code_types: "not_real"' in e.orig.pgerror - - -def test_should_return_none_if_code_is_used(app_, - db_, - db_session): - - code = verify_codes_dao.add_code(user_id=1, code='12345', code_type='email') - verify_codes_dao.use_code(code.id) - saved_code = verify_codes_dao.get_code_by_code(user_id=1, code_type='email', code='12345') - assert not saved_code diff --git a/tests/app/main/notify_client/test_sender.py b/tests/app/main/notify_client/test_sender.py index e49ba5a9c..aef8d1f0c 100644 --- a/tests/app/main/notify_client/test_sender.py +++ b/tests/app/main/notify_client/test_sender.py @@ -4,17 +4,13 @@ from pytest import fail from app.notify_client.sender import generate_token, check_token -def test_should_return_email_from_signed_token(app_, - db_, - db_session): +def test_should_return_email_from_signed_token(app_): email = 'email@something.com' token = generate_token(email) assert email == check_token(token) -def test_should_throw_exception_when_token_is_tampered_with(app_, - db_, - db_session): +def test_should_throw_exception_when_token_is_tampered_with(app_): email = 'email@something.com' token = generate_token(email) try: @@ -24,9 +20,7 @@ def test_should_throw_exception_when_token_is_tampered_with(app_, pass -def test_return_none_when_token_is_expired(app_, - db_, - db_session): +def test_return_none_when_token_is_expired(app_): with app_.test_request_context(): app_.config['TOKEN_MAX_AGE_SECONDS'] = -1000 email = 'email@something.com' diff --git a/tests/app/main/test_add_service_form.py b/tests/app/main/test_add_service_form.py index 4bc866501..a89170c1e 100644 --- a/tests/app/main/test_add_service_form.py +++ b/tests/app/main/test_add_service_form.py @@ -2,9 +2,7 @@ from app.main.forms import AddServiceForm from werkzeug.datastructures import MultiDict -def test_form_should_have_errors_when_duplicate_service_is_added(app_, - db_, - db_session): +def test_form_should_have_errors_when_duplicate_service_is_added(app_): def _get_form_names(): return ['some service', 'more names'] with app_.test_request_context(): diff --git a/tests/app/main/test_create_api_key_form.py b/tests/app/main/test_create_api_key_form.py index c19114b58..c13fe9fe3 100644 --- a/tests/app/main/test_create_api_key_form.py +++ b/tests/app/main/test_create_api_key_form.py @@ -3,9 +3,7 @@ from werkzeug.datastructures import MultiDict from app.main.forms import CreateKeyForm -def test_return_validation_error_when_key_name_exists(app_, - db_, - db_session): +def test_return_validation_error_when_key_name_exists(app_): def _get_names(): return ['some key', 'another key'] diff --git a/tests/app/main/test_two_factor_form.py b/tests/app/main/test_two_factor_form.py index df4f32177..640e81e58 100644 --- a/tests/app/main/test_two_factor_form.py +++ b/tests/app/main/test_two_factor_form.py @@ -1,70 +1,61 @@ from datetime import datetime, timedelta -from app.main.dao import verify_codes_dao from app.main.forms import TwoFactorForm +from app.main.dao import users_dao from tests import create_test_user -def test_form_is_valid_returns_no_errors(app_, db_, db_session): +def test_form_is_valid_returns_no_errors(app_, mock_check_verify_code): with app_.test_request_context(method='POST', data={'sms_code': '12345'}) as req: - user = set_up_test_data() - codes = verify_codes_dao.get_codes(user.id) - form = TwoFactorForm(codes) + def _check_code(code): + return users_dao.check_verify_code('1', code, "sms") + form = TwoFactorForm(_check_code) assert form.validate() is True assert len(form.errors) == 0 -def test_returns_errors_when_code_is_too_short(app_, db_, db_session): +def test_returns_errors_when_code_is_too_short(app_, mock_check_verify_code): with app_.test_request_context(method='POST', data={'sms_code': '145'}) as req: - user = set_up_test_data() - codes = verify_codes_dao.get_codes(user.id) - form = TwoFactorForm(codes) + def _check_code(code): + return users_dao.check_verify_code('1', code, "sms") + form = TwoFactorForm(_check_code) assert form.validate() is False assert len(form.errors) == 1 assert set(form.errors) == set({'sms_code': ['Code must be 5 digits', 'Code does not match']}) -def test_returns_errors_when_code_is_missing(app_, db_, db_session): +def test_returns_errors_when_code_is_missing(app_, mock_check_verify_code): with app_.test_request_context(method='POST', data={}) as req: - user = set_up_test_data() - codes = verify_codes_dao.get_codes(user.id) - form = TwoFactorForm(codes) + def _check_code(code): + return users_dao.check_verify_code('1', code, "sms") + form = TwoFactorForm(_check_code) assert form.validate() is False assert len(form.errors) == 1 assert set(form.errors) == set({'sms_code': ['Code must not be empty']}) -def test_returns_errors_when_code_contains_letters(app_, db_, db_session): +def test_returns_errors_when_code_contains_letters(app_, mock_check_verify_code): with app_.test_request_context(method='POST', data={'sms_code': 'asdfg'}) as req: - user = set_up_test_data() - codes = verify_codes_dao.get_codes(user.id) - form = TwoFactorForm(codes) + def _check_code(code): + return users_dao.check_verify_code('1', code, "sms") + form = TwoFactorForm(_check_code) assert form.validate() is False assert len(form.errors) == 1 assert set(form.errors) == set({'sms_code': ['Code must be 5 digits', 'Code does not match']}) -def test_should_return_errors_when_code_is_expired(app_, db_, db_session): +def test_should_return_errors_when_code_is_expired(app_, + mock_check_verify_code_code_expired): with app_.test_request_context(method='POST', data={'sms_code': '23456'}) as req: - user = create_test_user('active') - verify_codes_dao.add_code_with_expiry(user_id=user.id, - code='23456', - code_type='sms', - expiry=datetime.now() + timedelta(hours=-2)) - codes = verify_codes_dao.get_codes(user.id) - form = TwoFactorForm(codes) + def _check_code(code): + return users_dao.check_verify_code('1', code, "sms") + form = TwoFactorForm(_check_code) assert form.validate() is False errors = form.errors assert len(errors) == 1 assert errors == {'sms_code': ['Code has expired']} - - -def set_up_test_data(): - user = create_test_user('active') - verify_codes_dao.add_code(user_id=user.id, code='12345', code_type='sms') - return user diff --git a/tests/app/main/test_verify_form.py b/tests/app/main/test_verify_form.py index 2521a8a27..51bcf4b46 100644 --- a/tests/app/main/test_verify_form.py +++ b/tests/app/main/test_verify_form.py @@ -1,15 +1,18 @@ from datetime import datetime, timedelta -from app.main.dao import verify_codes_dao from app.main.forms import VerifyForm +from app.main.dao import users_dao from tests import create_test_user -def test_form_should_have_error_when_code_is_not_valid(app_, db_, db_session): +def test_form_should_have_error_when_code_is_not_valid(app_, + mock_check_verify_code): with app_.test_request_context(method='POST', data={'sms_code': '12345aa', 'email_code': 'abcde'}) as req: - user = set_up_test_data() - codes = verify_codes_dao.get_codes(user.id) - form = VerifyForm(codes) + + def _check_code(code, code_type): + return users_dao.check_verify_code('1', code, code_type) + + form = VerifyForm(_check_code) assert form.validate() is False errors = form.errors assert len(errors) == 2 @@ -19,12 +22,15 @@ def test_form_should_have_error_when_code_is_not_valid(app_, db_, db_session): assert set(errors) == set(expected) -def test_should_return_errors_when_code_missing(app_, db_, db_session): +def test_should_return_errors_when_code_missing(app_, + mock_check_verify_code): with app_.test_request_context(method='POST', data={}) as req: - user = set_up_test_data() - codes = verify_codes_dao.get_codes(user.id) - form = VerifyForm(codes) + + def _check_code(code, code_type): + return users_dao.check_verify_code('1', code, code_type) + + form = VerifyForm(_check_code) assert form.validate() is False errors = form.errors expected = {'sms_code': ['SMS code can not be empty'], @@ -33,12 +39,15 @@ def test_should_return_errors_when_code_missing(app_, db_, db_session): assert set(errors) == set(expected) -def test_should_return_errors_when_code_is_too_short(app_, db_, db_session): +def test_should_return_errors_when_code_is_too_short(app_, + mock_check_verify_code): with app_.test_request_context(method='POST', data={'sms_code': '123', 'email_code': '123'}) as req: - user = set_up_test_data() - codes = verify_codes_dao.get_codes(user.id) - form = VerifyForm(codes) + + def _check_code(code, code_type): + return users_dao.check_verify_code('1', code, code_type) + + form = VerifyForm(_check_code) assert form.validate() is False errors = form.errors expected = {'sms_code': ['Code must be 5 digits', 'Code does not match'], @@ -47,62 +56,36 @@ def test_should_return_errors_when_code_is_too_short(app_, db_, db_session): assert set(errors) == set(expected) -def test_should_return_errors_when_code_does_not_match(app_, db_, db_session): +def test_should_return_errors_when_code_does_not_match(app_, + mock_check_verify_code_code_not_found): with app_.test_request_context(method='POST', data={'sms_code': '34567', 'email_code': '34567'}) as req: - user = set_up_test_data() - codes = verify_codes_dao.get_codes(user.id) - form = VerifyForm(codes) + + def _check_code(code, code_type): + return users_dao.check_verify_code('1', code, code_type) + + form = VerifyForm(_check_code) assert form.validate() is False errors = form.errors - expected = {'sms_code': ['Code does not match'], - 'email_code': ['Code does not match']} + expected = {'sms_code': ['Code not found'], + 'email_code': ['Code not found']} assert len(errors) == 2 assert set(errors) == set(expected) -def test_should_return_errors_when_code_is_expired(app_, db_, db_session): +def test_should_return_errors_when_code_is_expired(app_, + mock_check_verify_code_code_expired): with app_.test_request_context(method='POST', data={'sms_code': '23456', 'email_code': '23456'}) as req: - user = create_test_user('pending') - verify_codes_dao.add_code_with_expiry(user_id=user.id, - code='23456', - code_type='sms', - expiry=datetime.now() + timedelta(hours=-2)) - verify_codes_dao.add_code_with_expiry(user_id=user.id, - code='23456', - code_type='email', - expiry=datetime.now() + timedelta(hours=-2)) - codes = verify_codes_dao.get_codes(user.id) - form = VerifyForm(codes) + def _check_code(code, code_type): + return users_dao.check_verify_code('1', code, code_type) + + form = VerifyForm(_check_code) assert form.validate() is False errors = form.errors expected = {'sms_code': ['Code has expired'], 'email_code': ['Code has expired']} assert len(errors) == 2 assert set(errors) == set(expected) - - -def test_should_return_valid_form_when_many_codes_exist(app_, - db_, - db_session): - with app_.test_request_context(method='POST', - data={'sms_code': '23456', - 'email_code': '23456'}) as req: - user = set_up_test_data() - verify_codes_dao.add_code(user_id=user.id, code='23456', code_type='email') - verify_codes_dao.add_code(user_id=user.id, code='23456', code_type='sms') - verify_codes_dao.add_code(user_id=user.id, code='60456', code_type='email') - verify_codes_dao.add_code(user_id=user.id, code='27856', code_type='sms') - codes = verify_codes_dao.get_codes(user.id) - form = VerifyForm(codes) - assert form.validate() is True - - -def set_up_test_data(): - user = create_test_user('pending') - verify_codes_dao.add_code(user_id=user.id, code='12345', code_type='email') - verify_codes_dao.add_code(user_id=user.id, code='12345', code_type='sms') - return user diff --git a/tests/app/main/views/test_add_service.py b/tests/app/main/views/test_add_service.py index e7d14e1db..b7cd97ed1 100644 --- a/tests/app/main/views/test_add_service.py +++ b/tests/app/main/views/test_add_service.py @@ -1,36 +1,32 @@ from flask import url_for -from app.main.dao import verify_codes_dao, services_dao -from tests import create_test_user -from app.models import User +from app.main.dao import services_dao def test_get_should_render_add_service_template(app_, - db_, - db_session, - mock_api_user, + api_user_active, mock_get_service, mock_get_services, mock_user_loader, - mock_user_dao_get_by_email): + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) response = client.get(url_for('main.add_service')) assert response.status_code == 200 assert 'Set up notifications for your service' in response.get_data(as_text=True) def test_should_add_service_and_redirect_to_next_page(app_, - db_, - db_session, mock_create_service, mock_get_services, - mock_api_user, + api_user_active, mock_user_loader, - mock_user_dao_get_by_email): + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) response = client.post( url_for('main.add_service'), data={'name': 'testing the post'}) @@ -41,31 +37,29 @@ def test_should_add_service_and_redirect_to_next_page(app_, def test_should_return_form_errors_when_service_name_is_empty(app_, - db_, - db_session, - mock_api_user, + api_user_active, mock_get_service, mock_get_services, mock_user_loader, - mock_user_dao_get_by_email): + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) response = client.post(url_for('main.add_service'), data={}) assert response.status_code == 200 assert 'Service name can not be empty' in response.get_data(as_text=True) def test_should_return_form_errors_with_duplicate_service_name(app_, - db_, - db_session, mock_get_services, mock_user_loader, - mock_api_user, - mock_user_dao_get_by_email): + api_user_active, + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) response = client.post( url_for('main.add_service'), data={'name': 'service_one'}) assert response.status_code == 200 diff --git a/tests/app/main/views/test_api_keys.py b/tests/app/main/views/test_api_keys.py index 71eed1353..02b3e06e0 100644 --- a/tests/app/main/views/test_api_keys.py +++ b/tests/app/main/views/test_api_keys.py @@ -3,29 +3,27 @@ from flask import url_for def test_should_show_api_keys_and_documentation_page(app_, - db_, - db_session, - mock_api_user, + api_user_active, mock_user_loader, - mock_user_dao_get_by_email): + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) response = client.get(url_for('main.documentation', service_id=123)) assert response.status_code == 200 def test_should_show_empty_api_keys_page(app_, - db_, - db_session, - mock_api_user, + api_user_active, mock_user_loader, mock_user_dao_get_by_email, - mock_get_no_api_keys): + mock_get_no_api_keys, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) response = client.get(url_for('main.api_keys', service_id=123)) assert response.status_code == 200 @@ -35,15 +33,14 @@ def test_should_show_empty_api_keys_page(app_, def test_should_show_api_keys_page(app_, - db_, - db_session, - mock_api_user, + api_user_active, mock_user_loader, mock_user_dao_get_by_email, - mock_get_api_keys): + mock_get_api_keys, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) response = client.get(url_for('main.api_keys', service_id=123)) assert response.status_code == 200 @@ -54,31 +51,29 @@ def test_should_show_api_keys_page(app_, def test_should_show_name_api_key_page(app_, - db_, - db_session, - mock_api_user, + api_user_active, mock_user_loader, mock_user_dao_get_by_email, - mock_get_api_keys): + mock_get_api_keys, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) response = client.get(url_for('main.create_api_key', service_id=123)) assert response.status_code == 200 def test_should_render_show_api_key(app_, - db_, - db_session, - mock_api_user, + api_user_active, mock_user_loader, mock_user_dao_get_by_email, mock_create_api_key, - mock_get_api_keys): + mock_get_api_keys, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) response = client.post(url_for('main.create_api_key', service_id=123), data={'key_name': 'some default key name'}) @@ -88,15 +83,14 @@ def test_should_render_show_api_key(app_, def test_should_show_confirm_revoke_api_key(app_, - db_, - db_session, - mock_api_user, + api_user_active, mock_user_loader, mock_user_dao_get_by_email, - mock_get_api_keys): + mock_get_api_keys, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) response = client.get(url_for('main.revoke_api_key', service_id=123, key_id=321)) assert response.status_code == 200 @@ -105,16 +99,15 @@ def test_should_show_confirm_revoke_api_key(app_, def test_should_redirect_after_revoking_api_key(app_, - db_, - db_session, - mock_api_user, + api_user_active, mock_user_loader, mock_user_dao_get_by_email, mock_revoke_api_key, - mock_get_api_keys): + mock_get_api_keys, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) response = client.post(url_for('main.revoke_api_key', service_id=123, key_id=321)) assert response.status_code == 302 diff --git a/tests/app/main/views/test_choose_services.py b/tests/app/main/views/test_choose_services.py index 619beecec..ed58c02b8 100644 --- a/tests/app/main/views/test_choose_services.py +++ b/tests/app/main/views/test_choose_services.py @@ -1,20 +1,16 @@ from tests import create_test_user from flask import url_for -from app.models import User - import pytest -@pytest.mark.xfail(reason='Requires completed move of user dao methods to api methods') def test_should_show_choose_services_page(app_, - db_, - db_session, - active_user, - mock_get_services): + mock_user_dao_get_user, + api_user_active, + mock_get_services, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - user = User.query.first() - client.login(user) + client.login(api_user_active) response = client.get(url_for('main.choose_service')) assert response.status_code == 200 diff --git a/tests/app/main/views/test_code_not_received.py b/tests/app/main/views/test_code_not_received.py index 9603e9793..3d1d2234a 100644 --- a/tests/app/main/views/test_code_not_received.py +++ b/tests/app/main/views/test_code_not_received.py @@ -1,19 +1,19 @@ -from app.main.dao import verify_codes_dao from tests import create_test_api_user from flask import url_for def test_should_render_email_code_not_received_template_and_populate_email_address(app_, - db_, - db_session, mock_send_sms, mock_send_email, - mock_api_user, - mock_user_dao_get_by_email): + api_user_active, + mock_user_dao_get_by_email, + mock_send_verify_code): with app_.test_request_context(): with app_.test_client() as client: with client.session_transaction() as session: - session['user_email'] = mock_api_user.email_address + session['user_details'] = { + 'id': api_user_active.id, + 'email': api_user_active.email_address} response = client.get(url_for('main.check_and_resend_email_code')) assert response.status_code == 200 assert 'Check your email address is correct and then resend the confirmation code' \ @@ -22,18 +22,18 @@ def test_should_render_email_code_not_received_template_and_populate_email_addre def test_should_check_and_resend_email_code_redirect_to_verify(app_, - db_, - db_session, mock_send_sms, mock_send_email, - mock_api_user, + api_user_active, mock_user_dao_get_by_email, - mock_user_dao_update_email): + mock_user_dao_update_email, + mock_send_verify_code): with app_.test_request_context(): with app_.test_client() as client: with client.session_transaction() as session: - session['user_email'] = mock_api_user.email_address - verify_codes_dao.add_code(mock_api_user.id, code='12345', code_type='email') + session['user_details'] = { + 'id': api_user_active.id, + 'email': api_user_active.email_address} response = client.post(url_for('main.check_and_resend_email_code'), data={'email_address': 'test@user.gov.uk'}) assert response.status_code == 302 @@ -41,17 +41,17 @@ def test_should_check_and_resend_email_code_redirect_to_verify(app_, def test_should_render_text_code_not_received_template(app_, - db_, - db_session, mock_send_sms, mock_send_email, - mock_api_user, - mock_user_dao_get_by_email): + api_user_active, + mock_user_dao_get_by_email, + mock_send_verify_code): with app_.test_request_context(): with app_.test_client() as client: with client.session_transaction() as session: - session['user_email'] = mock_api_user.email_address - verify_codes_dao.add_code(mock_api_user.id, code='12345', code_type='sms') + session['user_details'] = { + 'id': api_user_active.id, + 'email': api_user_active.email_address} response = client.get(url_for('main.check_and_resend_text_code')) assert response.status_code == 200 assert 'Check your mobile phone number is correct and then resend the confirmation code.' \ @@ -60,18 +60,18 @@ def test_should_render_text_code_not_received_template(app_, def test_should_check_and_redirect_to_verify(app_, - db_, - db_session, mock_send_sms, mock_send_email, - mock_api_user, + api_user_active, mock_user_dao_get_by_email, - mock_user_dao_update_mobile): + mock_user_dao_update_mobile, + mock_send_verify_code): with app_.test_request_context(): with app_.test_client() as client: with client.session_transaction() as session: - session['user_email'] = mock_api_user.email_address - verify_codes_dao.add_code(mock_api_user.id, code='12345', code_type='sms') + session['user_details'] = { + 'id': api_user_active.id, + 'email': api_user_active.email_address} response = client.post(url_for('main.check_and_resend_text_code'), data={'mobile_number': '+447700900460'}) assert response.status_code == 302 @@ -79,53 +79,54 @@ def test_should_check_and_redirect_to_verify(app_, def test_should_update_email_address_resend_code(app_, - db_, - db_session, mock_send_sms, mock_send_email, - mock_api_user, + api_user_active, mock_user_dao_get_by_email, - mock_user_dao_update_email): + mock_user_dao_update_email, + mock_send_verify_code): with app_.test_request_context(): with app_.test_client() as client: with client.session_transaction() as session: - session['user_email'] = mock_api_user.email_address - verify_codes_dao.add_code(user_id=mock_api_user.id, code='12345', code_type='email') + session['user_details'] = { + 'id': api_user_active.id, + 'email': api_user_active.email_address} response = client.post(url_for('main.check_and_resend_email_code'), data={'email_address': 'new@address.gov.uk'}) assert response.status_code == 302 assert response.location == url_for('main.verify', _external=True) - assert mock_api_user.email_address == 'new@address.gov.uk' + assert api_user_active.email_address == 'new@address.gov.uk' def test_should_update_mobile_number_resend_code(app_, - db_, - db_session, mock_send_sms, mock_send_email, - mock_api_user, + api_user_active, mock_user_dao_get_by_email, - mock_user_dao_update_mobile): + mock_user_dao_update_mobile, + mock_send_verify_code): with app_.test_request_context(): with app_.test_client() as client: with client.session_transaction() as session: - session['user_email'] = mock_api_user.email_address - verify_codes_dao.add_code(user_id=mock_api_user.id, code='12345', code_type='sms') + session['user_details'] = { + 'id': api_user_active.id, + 'email': api_user_active.email_address} response = client.post(url_for('main.check_and_resend_text_code'), data={'mobile_number': '+447700900460'}) assert response.status_code == 302 assert response.location == url_for('main.verify', _external=True) - assert mock_api_user.mobile_number == '+447700900460' + assert api_user_active.mobile_number == '+447700900460' def test_should_render_verification_code_not_received(app_, - db_, - db_session, - mock_api_user): + api_user_active, + mock_send_verify_code): with app_.test_request_context(): with app_.test_client() as client: with client.session_transaction() as session: - session['user_email'] = mock_api_user.email_address + session['user_details'] = { + 'id': api_user_active.id, + 'email': api_user_active.email_address} response = client.get(url_for('main.verification_code_not_received')) assert response.status_code == 200 assert 'Resend verification code' in response.get_data(as_text=True) @@ -134,37 +135,34 @@ def test_should_render_verification_code_not_received(app_, def test_check_and_redirect_to_two_factor(app_, - db_, - db_session, - mock_api_user, + api_user_active, mock_send_sms, mock_send_email, - mock_user_dao_get_by_email): + mock_user_dao_get_by_email, + mock_send_verify_code): with app_.test_request_context(): with app_.test_client() as client: with client.session_transaction() as session: - session['user_email'] = mock_api_user.email_address + session['user_details'] = { + 'id': api_user_active.id, + 'email': api_user_active.email_address} response = client.get(url_for('main.check_and_resend_verification_code')) assert response.status_code == 302 assert response.location == url_for('main.two_factor', _external=True) def test_should_create_new_code_for_user(app_, - db_, - db_session, - mock_api_user, + api_user_active, mock_send_sms, mock_send_email, - mock_user_dao_get_by_email): + mock_user_dao_get_by_email, + mock_send_verify_code): with app_.test_request_context(): with app_.test_client() as client: with client.session_transaction() as session: - session['user_email'] = mock_api_user.email_address - verify_codes_dao.add_code(user_id=mock_api_user.id, code='12345', code_type='sms') + session['user_details'] = { + 'id': api_user_active.id, + 'email': api_user_active.email_address} response = client.get(url_for('main.check_and_resend_verification_code')) assert response.status_code == 302 assert response.location == url_for('main.two_factor', _external=True) - codes = verify_codes_dao.get_codes(user_id=mock_api_user.id, code_type='sms') - assert len(codes) == 2 - for x in ([used.code_used for used in codes]): - assert x is False diff --git a/tests/app/main/views/test_dashboard.py b/tests/app/main/views/test_dashboard.py index 67520c8e2..3cdaa2198 100644 --- a/tests/app/main/views/test_dashboard.py +++ b/tests/app/main/views/test_dashboard.py @@ -1,17 +1,15 @@ -from app.models import User from flask import url_for def test_should_show_recent_jobs_on_dashboard(app_, - db_, - db_session, - mock_api_user, + api_user_active, mock_get_service, mock_user_loader, - mock_user_dao_get_by_email): + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) response = client.get(url_for('main.service_dashboard', service_id=123)) assert response.status_code == 200 diff --git a/tests/app/main/views/test_forgot_password.py b/tests/app/main/views/test_forgot_password.py index 03ffb9955..5425968a4 100644 --- a/tests/app/main/views/test_forgot_password.py +++ b/tests/app/main/views/test_forgot_password.py @@ -3,7 +3,7 @@ from app.main.dao import users_dao from tests import create_test_user -def test_should_render_forgot_password(app_, db_, db_session): +def test_should_render_forgot_password(app_): with app_.test_request_context(): response = app_.test_client().get(url_for('.forgot_password')) assert response.status_code == 200 @@ -12,18 +12,16 @@ def test_should_render_forgot_password(app_, db_, db_session): def test_should_redirect_to_password_reset_sent_and_state_updated(app_, - db_, - db_session, mock_send_email, - mock_api_user, + api_user_active, mock_user_dao_get_by_email, mock_user_dao_password_reset): with app_.test_request_context(): response = app_.test_client().post( url_for('.forgot_password'), - data={'email_address': mock_api_user.email_address}) + data={'email_address': api_user_active.email_address}) assert response.status_code == 200 assert ( 'You have been sent an email containing a link' ' to reset your password.') in response.get_data(as_text=True) - assert mock_api_user.state == 'request_password_reset' + assert api_user_active.state == 'request_password_reset' diff --git a/tests/app/main/views/test_jobs.py b/tests/app/main/views/test_jobs.py index c10605570..48ba89884 100644 --- a/tests/app/main/views/test_jobs.py +++ b/tests/app/main/views/test_jobs.py @@ -1,27 +1,34 @@ from flask import url_for -from app.models import User from tests import create_test_user -def test_should_return_list_of_all_jobs(app_, db_, db_session, service_one, mock_api_user, - mock_user_loader, mock_user_dao_get_by_email): +def test_should_return_list_of_all_jobs(app_, + service_one, + api_user_active, + mock_user_loader, + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) response = client.get(url_for('main.view_jobs', service_id=101)) assert response.status_code == 200 assert 'You haven’t sent any notifications yet' in response.get_data(as_text=True) -def test_should_show_page_for_one_job(app_, db_, db_session, service_one, mock_api_user, - mock_user_loader, mock_user_dao_get_by_email): +def test_should_show_page_for_one_job(app_, + service_one, + api_user_active, + mock_login, + mock_user_loader, + mock_user_dao_get_by_email): with app_.test_request_context(): with app_.test_client() as client: # TODO filename will be part of job metadata not in session with client.session_transaction() as s: s[456] = 'dispatch_20151114.csv' - client.login(mock_api_user) + client.login(api_user_active) response = client.get(url_for('main.view_job', service_id=123, job_id=456)) assert response.status_code == 200 @@ -29,11 +36,15 @@ def test_should_show_page_for_one_job(app_, db_, db_session, service_one, mock_a assert 'Test message 1' in response.get_data(as_text=True) -def test_should_show_page_for_one_notification(app_, db_, db_session, service_one, mock_api_user, - mock_user_loader, mock_user_dao_get_by_email): +def test_should_show_page_for_one_notification(app_, + service_one, + api_user_active, + mock_user_loader, + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) response = client.get(url_for( 'main.view_notification', service_id=101, diff --git a/tests/app/main/views/test_new_password.py b/tests/app/main/views/test_new_password.py index 8904278fc..25813ef76 100644 --- a/tests/app/main/views/test_new_password.py +++ b/tests/app/main/views/test_new_password.py @@ -8,17 +8,19 @@ from tests import create_test_user import pytest -def test_should_render_new_password_template(app_, db_, db_session, mock_api_user, - mock_user_dao_get_new_password): +def test_should_render_new_password_template(app_, + api_user_active, + mock_user_dao_get_new_password, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - token = generate_token(mock_api_user.email_address) + token = generate_token(api_user_active.email_address) response = client.get(url_for('.new_password', token=token)) assert response.status_code == 200 assert ' You can now create a new password for your account.' in response.get_data(as_text=True) -# def test_should_render_new_password_template_with_message_of_bad_token(app_, db_, db_session, +# def test_should_render_new_password_template_with_message_of_bad_token(app_, # mock_user_dao_get_by_email): # with app_.test_request_context(): # with app_.test_client() as client: @@ -31,15 +33,14 @@ def test_should_render_new_password_template(app_, db_, db_session, mock_api_use @pytest.mark.xfail(reason='Password reset not implemented') def test_should_redirect_to_two_factor_when_password_reset_is_successful(app_, - db_, - db_session, mock_send_sms, - mock_api_user, - mock_user_dao_get_new_password): + api_user_active, + mock_user_dao_get_new_password, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - mock_api_user.state = 'request_password_reset' - token = generate_token(mock_api_user.email_address) + api_user_active.state = 'request_password_reset' + token = generate_token(api_user_active.email_address) response = client.post(url_for('.new_password', token=token), data={'new_password': 'a-new_password'}) assert response.status_code == 302 assert response.location == url_for('.two_factor', _external=True) @@ -49,14 +50,13 @@ def test_should_redirect_to_two_factor_when_password_reset_is_successful(app_, def test_should_redirect_to_forgot_password_with_flash_message_when_token_is_expired(app_, - db_, - db_session, - mock_api_user): + api_user_active, + mock_login): with app_.test_request_context(): with app_.test_client() as client: app_.config['TOKEN_MAX_AGE_SECONDS'] = -1000 - mock_api_user.state = 'request_password_reset' - token = generate_token(mock_api_user.email_address) + api_user_active.state = 'request_password_reset' + token = generate_token(api_user_active.email_address) response = client.post(url_for('.new_password', token=token), data={'new_password': 'a-new_password'}) assert response.status_code == 302 assert response.location == url_for('.forgot_password', _external=True) @@ -65,14 +65,13 @@ def test_should_redirect_to_forgot_password_with_flash_message_when_token_is_exp @pytest.mark.xfail(reason='Password reset not implemented') def test_should_redirect_to_forgot_pass_when_user_active_should_be_request_passw_reset(app_, - db_, - db_session, - mock_api_user, - mock_user_dao_get_by_email): + api_user_active, + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - mock_api_user.state = 'request_password_reset' - token = generate_token(mock_api_user.email_address) + api_user_active.state = 'request_password_reset' + token = generate_token(api_user_active.email_address) response = client.post(url_for('.new_password', token=token), data={'new_password': 'a-new_password'}) assert response.status_code == 302 assert response.location == url_for('.index', _external=True) diff --git a/tests/app/main/views/test_register.py b/tests/app/main/views/test_register.py index 0d0f0766e..8ae6d66d4 100644 --- a/tests/app/main/views/test_register.py +++ b/tests/app/main/views/test_register.py @@ -11,12 +11,10 @@ from tests.conftest import mock_register_user as mock_user def test_process_register_creates_new_user(app_, - db_, - db_session, - mock_send_sms, - mock_send_email, + mock_send_verify_code, mock_register_user, - mock_user_by_email_not_found): + mock_user_by_email_not_found, + mock_login): user_data = { 'name': 'Some One Valid', 'email_address': 'someone@example.gov.uk', @@ -32,11 +30,9 @@ def test_process_register_creates_new_user(app_, def test_process_register_returns_400_when_mobile_number_is_invalid(app_, - db_, - db_session, - mock_send_sms, - mock_send_email, - mock_user_by_email_not_found): + mock_send_verify_code, + mock_user_by_email_not_found, + mock_login): response = app_.test_client().post('/register', data={'name': 'Bad Mobile', 'email_address': 'bad_mobile@example.gov.uk', @@ -48,11 +44,9 @@ def test_process_register_returns_400_when_mobile_number_is_invalid(app_, def test_should_return_400_when_email_is_not_gov_uk(app_, - db_, - db_session, - mock_send_sms, - mock_send_email, - mock_user_by_email_not_found): + mock_send_verify_code, + mock_user_by_email_not_found, + mock_login): response = app_.test_client().post('/register', data={'name': 'Bad Mobile', 'email_address': 'bad_mobile@example.not.right', @@ -64,13 +58,11 @@ def test_should_return_400_when_email_is_not_gov_uk(app_, def test_should_add_verify_codes_on_session(app_, - db_, - db_session, - mock_send_sms, - mock_send_email, + mock_send_verify_code, mock_register_user, mock_user_loader, - mock_user_by_email_not_found): + mock_user_by_email_not_found, + mock_login): user_data = { 'name': 'Test Codes', 'email_address': 'test@example.gov.uk', @@ -85,7 +77,9 @@ def test_should_add_verify_codes_on_session(app_, assert 'notify_admin_session' in response.headers.get('Set-Cookie') -def test_should_return_400_if_password_is_blacklisted(app_, db_, db_session, mock_user_by_email_not_found): +def test_should_return_400_if_password_is_blacklisted(app_, + mock_user_by_email_not_found, + mock_login): response = app_.test_client().post('/register', data={'name': 'Bad Mobile', 'email_address': 'bad_mobile@example.not.right', diff --git a/tests/app/main/views/test_service_settings.py b/tests/app/main/views/test_service_settings.py index 70458a9f0..13726a41a 100644 --- a/tests/app/main/views/test_service_settings.py +++ b/tests/app/main/views/test_service_settings.py @@ -1,11 +1,15 @@ from flask import (url_for, session) -def test_should_show_overview(app_, db_, db_session, mock_api_user, mock_get_service, - mock_user_loader, mock_user_dao_get_by_email): +def test_should_show_overview(app_, + api_user_active, + mock_get_service, + mock_user_loader, + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) service_id = 123 response = client.get(url_for( 'main.service_settings', service_id=service_id)) @@ -16,11 +20,15 @@ def test_should_show_overview(app_, db_, db_session, mock_api_user, mock_get_ser assert mock_get_service.called -def test_should_show_service_name(app_, db_, db_session, mock_api_user, mock_get_service, - mock_user_loader, mock_user_dao_get_by_email): +def test_should_show_service_name(app_, + api_user_active, + mock_get_service, + mock_user_loader, + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) service_id = 123 response = client.get(url_for( 'main.service_name_change', service_id=service_id)) @@ -31,11 +39,15 @@ def test_should_show_service_name(app_, db_, db_session, mock_api_user, mock_get service = mock_get_service.side_effect(service_id)['data'] -def test_should_redirect_after_change_service_name(app_, db_, db_session, mock_api_user, mock_get_service, - mock_user_loader, mock_user_dao_get_by_email): +def test_should_redirect_after_change_service_name(app_, + api_user_active, + mock_get_service, + mock_user_loader, + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) service_id = 123 response = client.post(url_for( 'main.service_name_change', service_id=service_id)) @@ -48,15 +60,14 @@ def test_should_redirect_after_change_service_name(app_, db_, db_session, mock_a def test_should_show_service_name_confirmation(app_, - db_, - db_session, - mock_api_user, + api_user_active, mock_get_service, mock_user_loader, - mock_user_dao_get_by_email): + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) service_id = 123 response = client.get(url_for( 'main.service_name_change_confirm', service_id=service_id)) @@ -68,16 +79,16 @@ def test_should_show_service_name_confirmation(app_, def test_should_redirect_after_service_name_confirmation(app_, - db_, - db_session, - mock_api_user, + api_user_active, mock_get_service, mock_update_service, mock_user_loader, - mock_user_dao_get_by_email): + mock_user_dao_get_by_email, + mock_login, + mock_verify_password): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) service_id = 123 service_new_name = 'New Name' with client.session_transaction() as session: @@ -94,11 +105,15 @@ def test_should_redirect_after_service_name_confirmation(app_, assert mock_update_service.called -def test_should_show_request_to_go_live(app_, db_, db_session, mock_api_user, mock_get_service, - mock_user_loader, mock_user_dao_get_by_email): +def test_should_show_request_to_go_live(app_, + api_user_active, + mock_get_service, + mock_user_loader, + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) service_id = 123 response = client.get( url_for('main.service_request_to_go_live', service_id=service_id)) @@ -110,16 +125,15 @@ def test_should_show_request_to_go_live(app_, db_, db_session, mock_api_user, mo def test_should_redirect_after_request_to_go_live(app_, - db_, - db_session, - mock_api_user, + api_user_active, mock_get_service, mock_update_service, mock_user_loader, - mock_user_dao_get_by_email): + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) service_id = 123 response = client.post(url_for( 'main.service_request_to_go_live', service_id=service_id)) @@ -132,11 +146,15 @@ def test_should_redirect_after_request_to_go_live(app_, assert mock_update_service.called -def test_should_show_status_page(app_, db_, db_session, mock_api_user, mock_get_service, - mock_user_loader, mock_user_dao_get_by_email): +def test_should_show_status_page(app_, + api_user_active, + mock_get_service, + mock_user_loader, + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) service_id = 123 response = client.get(url_for( 'main.service_status_change', service_id=service_id)) @@ -148,15 +166,14 @@ def test_should_show_status_page(app_, db_, db_session, mock_api_user, mock_get_ def test_should_show_redirect_after_status_change(app_, - db_, - db_session, - mock_api_user, + api_user_active, mock_get_service, mock_user_loader, - mock_user_dao_get_by_email): + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) service_id = 123 response = client.post(url_for( 'main.service_status_change', service_id=service_id)) @@ -168,11 +185,15 @@ def test_should_show_redirect_after_status_change(app_, assert mock_get_service.called -def test_should_show_status_confirmation(app_, db_, db_session, mock_api_user, mock_get_service, - mock_user_loader, mock_user_dao_get_by_email): +def test_should_show_status_confirmation(app_, + api_user_active, + mock_get_service, + mock_user_loader, + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) service_id = 123 response = client.get(url_for( 'main.service_status_change_confirm', service_id=service_id)) @@ -184,16 +205,16 @@ def test_should_show_status_confirmation(app_, db_, db_session, mock_api_user, m def test_should_redirect_after_status_confirmation(app_, - db_, - db_session, - mock_api_user, + api_user_active, mock_get_service, mock_update_service, mock_user_loader, - mock_user_dao_get_by_email): + mock_user_dao_get_by_email, + mock_login, + mock_verify_password): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) service_id = 123 response = client.post(url_for( 'main.service_status_change_confirm', service_id=service_id)) @@ -206,11 +227,15 @@ def test_should_redirect_after_status_confirmation(app_, assert mock_update_service.called -def test_should_show_delete_page(app_, db_, db_session, mock_api_user, mock_get_service, - mock_user_loader, mock_user_dao_get_by_email): +def test_should_show_delete_page(app_, + api_user_active, + mock_get_service, + mock_user_loader, + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) service_id = 123 response = client.get(url_for( 'main.service_delete', service_id=service_id)) @@ -220,11 +245,15 @@ def test_should_show_delete_page(app_, db_, db_session, mock_api_user, mock_get_ assert mock_get_service.called -def test_should_show_redirect_after_deleting_service(app_, db_, db_session, mock_api_user, mock_get_service, - mock_user_loader, mock_user_dao_get_by_email): +def test_should_show_redirect_after_deleting_service(app_, + api_user_active, + mock_get_service, + mock_user_loader, + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) service_id = 123 response = client.post(url_for( 'main.service_delete', service_id=service_id)) @@ -235,11 +264,15 @@ def test_should_show_redirect_after_deleting_service(app_, db_, db_session, mock assert delete_url == response.location -def test_should_show_delete_confirmation(app_, db_, db_session, mock_api_user, mock_get_service, - mock_user_loader, mock_user_dao_get_by_email): +def test_should_show_delete_confirmation(app_, + api_user_active, + mock_get_service, + mock_user_loader, + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) service_id = 123 response = client.get(url_for( 'main.service_delete_confirm', service_id=service_id)) @@ -250,16 +283,16 @@ def test_should_show_delete_confirmation(app_, db_, db_session, mock_api_user, m def test_should_redirect_delete_confirmation(app_, - db_, - db_session, - mock_api_user, + api_user_active, mock_get_service, mock_delete_service, mock_user_loader, - mock_user_dao_get_by_email): + mock_user_dao_get_by_email, + mock_login, + mock_verify_password): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) service_id = 123 response = client.post(url_for( 'main.service_delete_confirm', service_id=service_id)) diff --git a/tests/app/main/views/test_sign_in.py b/tests/app/main/views/test_sign_in.py index 4ce61cf40..6d60984d5 100644 --- a/tests/app/main/views/test_sign_in.py +++ b/tests/app/main/views/test_sign_in.py @@ -1,7 +1,6 @@ from datetime import datetime from app.main.dao import users_dao -from app.models import User from flask import url_for import pytest @@ -18,14 +17,10 @@ def test_render_sign_in_returns_sign_in_template(app_): def test_process_sign_in_return_2fa_template(app_, - db_, - db_session, - mock_send_sms, - mock_send_email, - mock_user_dao_get_user, - mock_user_loader, - mock_user_dao_get_by_email, - mock_user_dao_checkpassword): + mock_send_verify_code, + mock_get_user, + mock_get_user_by_email, + mock_verify_password): with app_.test_request_context(): response = app_.test_client().post( url_for('main.sign_in'), data={ @@ -37,8 +32,6 @@ def test_process_sign_in_return_2fa_template(app_, @pytest.mark.xfail(reason='User failed logins not implemented yet') def test_should_return_locked_out_true_when_user_is_locked(app_, - db_, - db_session, mock_user_dao_get_user, mock_inactive_user_dao_get_by_email): with app_.test_request_context(): @@ -66,8 +59,6 @@ def test_should_return_locked_out_true_when_user_is_locked(app_, # @pytest.mark.xfail(reason='User failed logins not implemented yet') # def test_should_return_active_user_is_false_if_user_is_inactive(app_, -# db_, -# db_session, # mock_user_dao_get_user, # mock_inactive_user_dao_get_by_email): # with app_.test_request_context(): @@ -80,7 +71,7 @@ def test_should_return_locked_out_true_when_user_is_locked(app_, # assert 'Username or password is incorrect' in response.get_data(as_text=True) -# def test_should_return_200_when_user_does_not_exist(app_, db_, db_session, +# def test_should_return_200_when_user_does_not_exist(app_, # mock_user_dao_get_user, # mock_user_dao_get_by_email): # with app_.test_request_context(): @@ -92,7 +83,7 @@ def test_should_return_locked_out_true_when_user_is_locked(app_, # assert 'Username or password is incorrect' in response.get_data(as_text=True) -# def test_should_return_200_when_user_is_not_active(app_, db_, db_session): +# def test_should_return_200_when_user_is_not_active(app_): # user = User(email_address='PendingUser@example.gov.uk', # password='val1dPassw0rd!', # mobile_number='+441234123123', diff --git a/tests/app/main/views/test_sign_out.py b/tests/app/main/views/test_sign_out.py index 47b5c8367..2013c0004 100644 --- a/tests/app/main/views/test_sign_out.py +++ b/tests/app/main/views/test_sign_out.py @@ -1,7 +1,6 @@ from datetime import datetime from flask import url_for from app.main.dao import users_dao -from app.models import User def test_render_sign_out_redirects_to_sign_in(app_): @@ -14,19 +13,18 @@ def test_render_sign_out_redirects_to_sign_in(app_): def test_sign_out_user(app_, - db_, - db_session, mock_send_sms, mock_send_email, mock_get_service, - mock_api_user, + api_user_active, mock_user_loader, - mock_user_dao_get_by_email): + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): email = 'valid@example.gov.uk' password = 'val1dPassw0rd!' with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) # Check we are logged in response = client.get( url_for('main.service_dashboard', service_id="123")) diff --git a/tests/app/main/views/test_sms.py b/tests/app/main/views/test_sms.py index e2e72e241..159aa47a1 100644 --- a/tests/app/main/views/test_sms.py +++ b/tests/app/main/views/test_sms.py @@ -4,13 +4,14 @@ from flask import url_for import moto -def test_upload_empty_csvfile_returns_to_upload_page(app_, db_, db_session, - mock_api_user, +def test_upload_empty_csvfile_returns_to_upload_page(app_, + api_user_active, mock_user_loader, - mock_user_dao_get_by_email): + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) upload_data = {'file': (BytesIO(''.encode('utf-8')), 'emtpy.csv')} response = client.post(url_for('main.send_sms', service_id=123), data=upload_data, follow_redirects=True) @@ -22,19 +23,18 @@ def test_upload_empty_csvfile_returns_to_upload_page(app_, db_, db_session, @moto.mock_s3 def test_upload_csvfile_with_invalid_phone_shows_check_page_with_errors(app_, - db_, - db_session, mocker, - mock_api_user, + api_user_active, mock_user_loader, - mock_user_dao_get_by_email): + mock_user_dao_get_by_email, + mock_login): contents = 'phone\n+44 123\n+44 456' file_data = (BytesIO(contents.encode('utf-8')), 'invalid.csv') with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) upload_data = {'file': file_data} response = client.post(url_for('main.send_sms', service_id=123), data=upload_data, @@ -49,12 +49,11 @@ def test_upload_csvfile_with_invalid_phone_shows_check_page_with_errors(app_, @moto.mock_s3 def test_upload_csvfile_with_valid_phone_shows_first3_and_last3_numbers(app_, - db_, - db_session, mocker, - mock_api_user, + api_user_active, mock_user_loader, - mock_user_dao_get_by_email): + mock_user_dao_get_by_email, + mock_login): contents = 'phone\n+44 7700 900981\n+44 7700 900982\n+44 7700 900983\n+44 7700 900984\n+44 7700 900985\n+44 7700 900986\n+44 7700 900987\n+44 7700 900988\n+44 7700 900989' # noqa @@ -62,7 +61,7 @@ def test_upload_csvfile_with_valid_phone_shows_first3_and_last3_numbers(app_, with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) upload_data = {'file': file_data} response = client.post(url_for('main.send_sms', service_id=123), data=upload_data, @@ -87,12 +86,11 @@ def test_upload_csvfile_with_valid_phone_shows_first3_and_last3_numbers(app_, @moto.mock_s3 def test_upload_csvfile_with_valid_phone_shows_all_if_6_or_less_numbers(app_, - db_, - db_session, mocker, - mock_api_user, + api_user_active, mock_user_loader, - mock_user_dao_get_by_email): + mock_user_dao_get_by_email, + mock_login): contents = 'phone\n+44 7700 900981\n+44 7700 900982\n+44 7700 900983\n+44 7700 900984\n+44 7700 900985\n+44 7700 900986' # noqa @@ -100,7 +98,7 @@ def test_upload_csvfile_with_valid_phone_shows_all_if_6_or_less_numbers(app_, with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) upload_data = {'file': file_data} response = client.post(url_for('main.send_sms', service_id=123), data=upload_data, @@ -120,11 +118,14 @@ def test_upload_csvfile_with_valid_phone_shows_all_if_6_or_less_numbers(app_, @moto.mock_s3 -def test_should_redirect_to_job(app_, db_, db_session, mocker, mock_api_user, - mock_user_loader, mock_user_dao_get_by_email): +def test_should_redirect_to_job(app_, + api_user_active, + mock_user_loader, + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) response = client.post(url_for('main.check_sms', service_id=123, upload_id='someid')) diff --git a/tests/app/main/views/test_templates.py b/tests/app/main/views/test_templates.py index cdf7284cf..fab365bc6 100644 --- a/tests/app/main/views/test_templates.py +++ b/tests/app/main/views/test_templates.py @@ -3,15 +3,14 @@ from flask import url_for def test_should_return_list_of_all_templates(app_, - db_, - db_session, - mock_api_user, + api_user_active, mock_get_service_templates, mock_user_loader, - mock_user_dao_get_by_email): + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) service_id = 123 response = client.get(url_for( '.manage_service_templates', service_id=service_id)) @@ -21,15 +20,14 @@ def test_should_return_list_of_all_templates(app_, def test_should_show_page_for_one_templates(app_, - db_, - db_session, - mock_api_user, + api_user_active, mock_get_service_template, mock_user_loader, - mock_user_dao_get_by_email): + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) service_id = 123 template_id = 456 response = client.get(url_for( @@ -43,16 +41,15 @@ def test_should_show_page_for_one_templates(app_, def test_should_redirect_when_saving_a_template(app_, - db_, - db_session, - mock_api_user, + api_user_active, mock_get_service_template, mock_update_service_template, mock_user_loader, - mock_user_dao_get_by_email): + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) service_id = 123 template_id = 456 name = "new name" @@ -78,15 +75,14 @@ def test_should_redirect_when_saving_a_template(app_, def test_should_show_delete_template_page(app_, - db_, - db_session, - mock_api_user, + api_user_active, mock_get_service_template, mock_user_loader, - mock_user_dao_get_by_email): + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) service_id = 123 template_id = 456 response = client.get(url_for( @@ -101,16 +97,15 @@ def test_should_show_delete_template_page(app_, def test_should_redirect_when_deleting_a_template(app_, - db_, - db_session, - mock_api_user, + api_user_active, mock_get_service_template, mock_delete_service_template, mock_user_loader, - mock_user_dao_get_by_email): + mock_user_dao_get_by_email, + mock_login): with app_.test_request_context(): with app_.test_client() as client: - client.login(mock_api_user) + client.login(api_user_active) service_id = 123 template_id = 456 name = "new name" diff --git a/tests/app/main/views/test_two_factor.py b/tests/app/main/views/test_two_factor.py index a6e397a7e..373636561 100644 --- a/tests/app/main/views/test_two_factor.py +++ b/tests/app/main/views/test_two_factor.py @@ -1,27 +1,35 @@ from flask import json, url_for -from app.main.dao import verify_codes_dao from tests import create_test_user -def test_should_render_two_factor_page(app_, db_, db_session, mock_api_user, mock_user_dao_get_by_email): +def test_should_render_two_factor_page(app_, + api_user_active, + mock_user_dao_get_by_email): with app_.test_request_context(): with app_.test_client() as client: # TODO this lives here until we work out how to # reassign the session after it is lost mid register process with client.session_transaction() as session: - session['user_email'] = mock_api_user.email_address + session['user_details'] = { + 'id': api_user_active.id, + 'email': api_user_active.email_address} response = client.get(url_for('main.two_factor')) assert response.status_code == 200 assert '''We've sent you a text message with a verification code.''' in response.get_data(as_text=True) -def test_should_login_user_and_redirect_to_dashboard(app_, db_, db_session, mock_api_user, mock_user_dao_get_by_email): +def test_should_login_user_and_redirect_to_dashboard(app_, + api_user_active, + mock_get_user, + mock_user_dao_get_by_email, + mock_check_verify_code): with app_.test_request_context(): with app_.test_client() as client: with client.session_transaction() as session: - session['user_email'] = mock_api_user.email_address - verify_codes_dao.add_code(user_id=mock_api_user.id, code='12345', code_type='sms') + session['user_details'] = { + 'id': api_user_active.id, + 'email': api_user_active.email_address} response = client.post(url_for('main.two_factor'), data={'sms_code': '12345'}) @@ -30,37 +38,32 @@ def test_should_login_user_and_redirect_to_dashboard(app_, db_, db_session, mock def test_should_return_200_with_sms_code_error_when_sms_code_is_wrong(app_, - db_, - db_session, - mock_api_user, - mock_user_dao_get_by_email): + api_user_active, + mock_user_dao_get_by_email, + mock_check_verify_code_code_not_found): with app_.test_request_context(): with app_.test_client() as client: with client.session_transaction() as session: - session['user_email'] = mock_api_user.email_address - verify_codes_dao.add_code(user_id=mock_api_user.id, code='12345', code_type='sms') + session['user_details'] = { + 'id': api_user_active.id, + 'email': api_user_active.email_address} response = client.post(url_for('main.two_factor'), data={'sms_code': '23456'}) assert response.status_code == 200 - assert 'Code does not match' in response.get_data(as_text=True) + assert 'Code not found' in response.get_data(as_text=True) def test_should_login_user_when_multiple_valid_codes_exist(app_, - db_, - db_session, - mock_api_user, - mock_user_dao_get_by_email): + api_user_active, + mock_get_user, + mock_user_dao_get_by_email, + mock_check_verify_code): with app_.test_request_context(): with app_.test_client() as client: with client.session_transaction() as session: - session['user_email'] = mock_api_user.email_address - verify_codes_dao.add_code(user_id=mock_api_user.id, code='23456', code_type='sms') - verify_codes_dao.add_code(user_id=mock_api_user.id, code='12345', code_type='sms') - verify_codes_dao.add_code(user_id=mock_api_user.id, code='34567', code_type='sms') - assert len(verify_codes_dao.get_codes(user_id=mock_api_user.id, code_type='sms')) == 3 + session['user_details'] = { + 'id': api_user_active.id, + 'email': api_user_active.email_address} response = client.post(url_for('main.two_factor'), data={'sms_code': '23456'}) assert response.status_code == 302 - codes = verify_codes_dao.get_codes(user_id=mock_api_user.id, code_type='sms') - # query will only return codes where code_used == False - assert len(codes) == 0 diff --git a/tests/app/main/views/test_user_profile.py b/tests/app/main/views/test_user_profile.py index 7c7acc20f..ab7391921 100644 --- a/tests/app/main/views/test_user_profile.py +++ b/tests/app/main/views/test_user_profile.py @@ -1,121 +1,292 @@ -def test_should_show_overview_page(app_, db_, db_session): - response = app_.test_client().get('/user-profile') - - assert 'Your profile' in response.get_data(as_text=True) - assert response.status_code == 200 +import json +from flask import url_for -def test_should_show_name_page(app_, db_, db_session): - response = app_.test_client().get('/user-profile/name') +def test_should_show_overview_page(app_, + api_user_active, + mock_login, + mock_user_dao_get_user): + with app_.test_request_context(): + with app_.test_client() as client: + client.login(api_user_active) + response = client.get(url_for('main.user_profile')) - assert 'Change your name' in response.get_data(as_text=True) - assert response.status_code == 200 + assert 'Your profile' in response.get_data(as_text=True) + assert response.status_code == 200 -def test_should_redirect_after_name_change(app_, db_, db_session): - response = app_.test_client().post('/user-profile/name') +def test_should_show_name_page(app_, + api_user_active, + mock_login, + mock_user_dao_get_user): + with app_.test_request_context(): + with app_.test_client() as client: + client.login(api_user_active) + response = client.get(url_for('main.user_profile_name')) - assert response.status_code == 302 - assert response.location == 'http://localhost/user-profile' + assert 'Change your name' in response.get_data(as_text=True) + assert response.status_code == 200 -def test_should_show_email_page(app_, db_, db_session): - response = app_.test_client().get('/user-profile/email') +def test_should_redirect_after_name_change(app_, + api_user_active, + mock_login, + mock_update_user, + mock_user_dao_get_user): + with app_.test_request_context(): + with app_.test_client() as client: + client.login(api_user_active) + new_name = 'New Name' + data = {'new_name': new_name} + response = client.post(url_for( + 'main.user_profile_name'), data=data) - assert 'Change your email address' in response.get_data(as_text=True) - assert response.status_code == 200 + assert response.status_code == 302 + assert response.location == url_for( + 'main.user_profile', _external=True) + api_user_active.name = new_name + assert mock_update_user.called -def test_should_redirect_after_email_change(app_, db_, db_session): - response = app_.test_client().post('/user-profile/email') +def test_should_show_email_page(app_, + api_user_active, + mock_login, + mock_user_dao_get_user): + with app_.test_request_context(): + with app_.test_client() as client: + client.login(api_user_active) + response = client.get(url_for( + 'main.user_profile_email')) - assert response.status_code == 302 - assert response.location == 'http://localhost/user-profile/email/authenticate' + assert 'Change your email address' in response.get_data(as_text=True) + assert response.status_code == 200 -def test_should_show_authenticate_after_email_change(app_, db_, db_session): - response = app_.test_client().get('/user-profile/email/authenticate') +def test_should_redirect_after_email_change(app_, + api_user_active, + mock_login, + mock_user_dao_get_user, + mock_get_user_by_email_not_found): + with app_.test_request_context(): + with app_.test_client() as client: + client.login(api_user_active) + data = {'email_address': 'new_notify@notify.gov.uk'} + response = client.post( + url_for('main.user_profile_email'), + data=data) - assert 'Change your email address' in response.get_data(as_text=True) - assert 'Confirm' in response.get_data(as_text=True) - assert response.status_code == 200 + assert response.status_code == 302 + assert response.location == url_for( + 'main.user_profile_email_authenticate', _external=True) -def test_should_redirect_after_email_change_confirm(app_, db_, db_session): - response = app_.test_client().post('/user-profile/email/authenticate') +def test_should_show_authenticate_after_email_change(app_, + api_user_active, + mock_login, + mock_user_dao_get_user, + mock_user_dao_checkpassword): + with app_.test_request_context(): + with app_.test_client() as client: + client.login(api_user_active) + with client.session_transaction() as session: + session['new-email'] = 'new_notify@notify.gov.uk' + response = client.get(url_for('main.user_profile_email_authenticate')) - assert response.status_code == 302 - assert response.location == 'http://localhost/user-profile/email/confirm' + assert 'Change your email address' in response.get_data(as_text=True) + assert 'Confirm' in response.get_data(as_text=True) + assert response.status_code == 200 -def test_should_show_confirm_after_email_change(app_, db_, db_session): - response = app_.test_client().get('/user-profile/email/confirm') +def test_should_redirect_after_email_change_confirm(app_, + api_user_active, + mock_login, + mock_user_dao_get_user): + with app_.test_request_context(): + with app_.test_client() as client: + client.login(api_user_active) + data = {'email-code': '12345'} + with client.session_transaction() as session: + session['new-email'] = 'new_notify@notify.gov.uk' + response = client.post( + url_for('main.user_profile_email_authenticate'), + data=data) - assert 'Change your email address' in response.get_data(as_text=True) - assert 'Confirm' in response.get_data(as_text=True) - assert response.status_code == 200 + assert response.status_code == 302 + assert response.location == url_for( + 'main.user_profile_email_confirm', _external=True) -def test_should_redirect_after_email_change_confirm(app_, db_, db_session): - response = app_.test_client().post('/user-profile/email/confirm') +def test_should_show_confirm_after_email_change(app_, + api_user_active, + mock_login, + mock_user_dao_get_user): + with app_.test_request_context(): + with app_.test_client() as client: + client.login(api_user_active) + with client.session_transaction() as session: + session['new-email-password-confirmed'] = True + response = client.get(url_for('main.user_profile_email_confirm')) - assert response.status_code == 302 - assert response.location == 'http://localhost/user-profile' + assert 'Change your email address' in response.get_data(as_text=True) + assert 'Confirm' in response.get_data(as_text=True) + assert response.status_code == 200 -def test_should_show_mobile_number_page(app_, db_, db_session): - response = app_.test_client().get('/user-profile/mobile-number') +def test_should_redirect_after_email_change_confirm(app_, + api_user_active, + mock_login, + mock_user_dao_get_user, + mock_check_verify_code): + with app_.test_request_context(): + with app_.test_client() as client: + client.login(api_user_active) + with client.session_transaction() as session: + session['new-email-password-confirmed'] = True + session['new-email'] = 'new_notify@notify.gov.uk' + data = {'email_code': '12345'} + response = client.post( + url_for('main.user_profile_email_confirm'), + data=data) - assert 'Change your mobile number' in response.get_data(as_text=True) - assert response.status_code == 200 + assert response.status_code == 302 + assert response.location == url_for( + 'main.user_profile', _external=True) -def test_should_redirect_after_mobile_number_change(app_, db_, db_session): - response = app_.test_client().post('/user-profile/email') +def test_should_show_mobile_number_page(app_, + api_user_active, + mock_login, + mock_user_dao_get_user): + with app_.test_request_context(): + with app_.test_client() as client: + client.login(api_user_active) + response = client.get(url_for('main.user_profile_mobile_number')) - assert response.status_code == 302 - assert response.location == 'http://localhost/user-profile/email/authenticate' + assert 'Change your mobile number' in response.get_data(as_text=True) + assert response.status_code == 200 -def test_should_show_authenticate_after_mobile_number_change(app_, db_, db_session): - response = app_.test_client().get('/user-profile/mobile-number/authenticate') - - assert 'Change your mobile number' in response.get_data(as_text=True) - assert 'Confirm' in response.get_data(as_text=True) - assert response.status_code == 200 +def test_should_redirect_after_mobile_number_change(app_, + api_user_active, + mock_login, + mock_user_dao_get_user): + with app_.test_request_context(): + with app_.test_client() as client: + client.login(api_user_active) + data = {'mobile_number': '07121231234'} + response = client.post( + url_for('main.user_profile_mobile_number'), + data=data) + assert response.status_code == 302 + assert response.location == url_for( + 'main.user_profile_mobile_number_authenticate', _external=True) -def test_should_redirect_after_mobile_number_authenticate(app_, db_, db_session): - response = app_.test_client().post('/user-profile/mobile-number/authenticate') +def test_should_show_authenticate_after_mobile_number_change(app_, + api_user_active, + mock_login, + mock_user_dao_get_user): + with app_.test_request_context(): + with app_.test_client() as client: + client.login(api_user_active) + with client.session_transaction() as session: + session['new-mob'] = '+441234123123' + response = client.get( + url_for('main.user_profile_mobile_number_authenticate')) - assert response.status_code == 302 - assert response.location == 'http://localhost/user-profile/mobile-number/confirm' + assert 'Change your mobile number' in response.get_data(as_text=True) + assert 'Confirm' in response.get_data(as_text=True) + assert response.status_code == 200 -def test_should_show_confirm_after_mobile_number_change(app_, db_, db_session): - response = app_.test_client().get('/user-profile/mobile-number/confirm') +def test_should_redirect_after_mobile_number_authenticate(app_, + api_user_active, + mock_login, + mock_user_dao_get_user, + mock_verify_password): + with app_.test_request_context(): + with app_.test_client() as client: + client.login(api_user_active) + with client.session_transaction() as session: + session['new-mob'] = '+441234123123' + data = {'password': '12345667'} + response = client.post( + url_for('main.user_profile_mobile_number_authenticate'), + data=data) - assert 'Change your mobile number' in response.get_data(as_text=True) - assert 'Confirm' in response.get_data(as_text=True) - assert response.status_code == 200 + assert response.status_code == 302 + assert response.location == url_for( + 'main.user_profile_mobile_number_confirm', _external=True) -def test_should_redirect_after_mobile_number_confirm(app_, db_, db_session): - response = app_.test_client().post('/user-profile/mobile-number/confirm') +def test_should_show_confirm_after_mobile_number_change(app_, + api_user_active, + mock_login, + mock_user_dao_get_user): + with app_.test_request_context(): + with app_.test_client() as client: + client.login(api_user_active) + with client.session_transaction() as session: + session['new-mob-password-confirmed'] = True + response = client.get( + url_for('main.user_profile_mobile_number_confirm')) - assert response.status_code == 302 - assert response.location == 'http://localhost/user-profile' + assert 'Change your mobile number' in response.get_data(as_text=True) + assert 'Confirm' in response.get_data(as_text=True) + assert response.status_code == 200 -def test_should_show_password_page(app_, db_, db_session): - response = app_.test_client().get('/user-profile/password') - - assert 'Change your password' in response.get_data(as_text=True) - assert response.status_code == 200 +def test_should_redirect_after_mobile_number_confirm(app_, + api_user_active, + mock_login, + mock_user_dao_get_user, + mock_check_verify_code): + with app_.test_request_context(): + with app_.test_client() as client: + client.login(api_user_active) + with client.session_transaction() as session: + session['new-mob-password-confirmed'] = True + session['new-mob'] = '+441234123123' + data = {'sms_code': '12345'} + response = client.post( + url_for('main.user_profile_mobile_number_confirm'), + data=data) + print(response.get_data(as_text=True)) + assert response.status_code == 302 + assert response.location == url_for( + 'main.user_profile', _external=True) -def test_should_redirect_after_password_change(app_, db_, db_session): - response = app_.test_client().post('/user-profile/password') +def test_should_show_password_page(app_, + api_user_active, + mock_login, + mock_user_dao_get_user): + with app_.test_request_context(): + with app_.test_client() as client: + client.login(api_user_active) + response = client.get(url_for('main.user_profile_password')) - assert response.status_code == 302 - assert response.location == 'http://localhost/user-profile' + assert 'Change your password' in response.get_data(as_text=True) + assert response.status_code == 200 + + +def test_should_redirect_after_password_change(app_, + api_user_active, + mock_login, + mock_get_user, + mock_update_user, + mock_verify_password): + with app_.test_request_context(): + with app_.test_client() as client: + client.login(api_user_active) + data = { + 'new_password': '1234567890', + 'old_password': '4567676328'} + response = client.post( + url_for('main.user_profile_password'), + data=data) + + print(response.get_data(as_text=True)) + assert response.status_code == 302 + assert response.location == url_for( + 'main.user_profile', _external=True) diff --git a/tests/app/main/views/test_verify.py b/tests/app/main/views/test_verify.py index 1d58a3b90..c2b7773df 100644 --- a/tests/app/main/views/test_verify.py +++ b/tests/app/main/views/test_verify.py @@ -1,17 +1,19 @@ from flask import json, url_for -from app.main.dao import users_dao, verify_codes_dao +from app.main.dao import users_dao from tests import create_test_api_user import pytest -def test_should_return_verify_template(app_, db_, db_session, mock_api_user): +def test_should_return_verify_template(app_, + api_user_active, + mock_send_verify_code): with app_.test_request_context(): with app_.test_client() as client: # TODO this lives here until we work out how to # reassign the session after it is lost mid register process with client.session_transaction() as session: - session['user_details'] = {'email_address': mock_api_user.email_address, 'id': mock_api_user.id} + session['user_details'] = {'email_address': api_user_active.email_address, 'id': api_user_active.id} response = client.get(url_for('main.verify')) assert response.status_code == 200 assert ( @@ -20,18 +22,15 @@ def test_should_return_verify_template(app_, db_, db_session, mock_api_user): def test_should_redirect_to_add_service_when_code_are_correct(app_, - db_, - db_session, - mock_api_user, + api_user_active, mock_user_dao_get_user, mock_activate_user, - mock_user_loader): + mock_user_loader, + mock_check_verify_code): with app_.test_request_context(): with app_.test_client() as client: with client.session_transaction() as session: - session['user_details'] = {'email_address': mock_api_user.email_address, 'id': mock_api_user.id} - verify_codes_dao.add_code(user_id=mock_api_user.id, code='12345', code_type='sms') - verify_codes_dao.add_code(user_id=mock_api_user.id, code='23456', code_type='email') + session['user_details'] = {'email_address': api_user_active.email_address, 'id': api_user_active.id} response = client.post(url_for('main.verify'), data={'sms_code': '12345', 'email_code': '23456'}) @@ -39,47 +38,45 @@ def test_should_redirect_to_add_service_when_code_are_correct(app_, assert response.location == url_for('main.add_service', first='first', _external=True) -# def test_should_activate_user_after_verify(app_, db_, db_session, mock_api_user, mock_activate_user): +# def test_should_activate_user_after_verify(app_, api_user_active, mock_activate_user): # with app_.test_request_context(): # with app_.test_client() as client: # with client.session_transaction() as session: -# session['user_details'] = {'email_address': mock_api_user.email_address, 'id': mock_api_user.id} -# verify_codes_dao.add_code(user_id=mock_api_user.id, code='12345', code_type='sms') -# verify_codes_dao.add_code(user_id=mock_api_user.id, code='23456', code_type='email') +# session['user_details'] = {'email_address': api_user_active.email_address, 'id': api_user_active.id} +# verify_codes_dao.add_code(user_id=api_user_active.id, code='12345', code_type='sms') +# verify_codes_dao.add_code(user_id=api_user_active.id, code='23456', code_type='email') # client.post(url_for('main.verify'), # data={'sms_code': '12345', # 'email_code': '23456'}) -# assert mock_api_user.state == 'active' +# assert api_user_active.state == 'active' -def test_should_return_200_when_codes_are_wrong(app_, db_, db_session, mock_api_user, mock_user_dao_get_user): +def test_should_return_200_when_codes_are_wrong(app_, + api_user_active, + mock_user_dao_get_user, + mock_check_verify_code_code_not_found): with app_.test_request_context(): with app_.test_client() as client: with client.session_transaction() as session: - session['user_details'] = {'email_address': mock_api_user.email_address, 'id': mock_api_user.id} - verify_codes_dao.add_code(user_id=mock_api_user.id, code='23345', code_type='sms') - verify_codes_dao.add_code(user_id=mock_api_user.id, code='98456', code_type='email') + session['user_details'] = {'email_address': api_user_active.email_address, 'id': api_user_active.id} response = client.post(url_for('main.verify'), data={'sms_code': '12345', 'email_code': '23456'}) - print(response.location) assert response.status_code == 200 resp_data = response.get_data(as_text=True) - assert resp_data.count('Code does not match') == 2 + assert resp_data.count('Code not found') == 2 # def test_should_mark_all_codes_as_used_when_many_codes_exist(app_, -# db_, -# db_session, -# mock_api_user): +# api_user_active): # with app_.test_request_context(): # with app_.test_client() as client: # with client.session_transaction() as session: -# session['user_details'] = {'email_address': mock_api_user.email_address, 'id': mock_api_user.id} -# code1 = verify_codes_dao.add_code(user_id=mock_api_user.id, code='23345', code_type='sms') -# code2 = verify_codes_dao.add_code(user_id=mock_api_user.id, code='98456', code_type='email') -# code3 = verify_codes_dao.add_code(user_id=mock_api_user.id, code='12345', code_type='sms') -# code4 = verify_codes_dao.add_code(user_id=mock_api_user.id, code='23412', code_type='email') +# session['user_details'] = {'email_address': api_user_active.email_address, 'id': api_user_active.id} +# code1 = verify_codes_dao.add_code(user_id=api_user_active.id, code='23345', code_type='sms') +# code2 = verify_codes_dao.add_code(user_id=api_user_active.id, code='98456', code_type='email') +# code3 = verify_codes_dao.add_code(user_id=api_user_active.id, code='12345', code_type='sms') +# code4 = verify_codes_dao.add_code(user_id=api_user_active.id, code='23412', code_type='email') # response = client.post(url_for('main.verify'), # data={'sms_code': '23345', # 'email_code': '23412'}) diff --git a/tests/conftest.py b/tests/conftest.py index b713cf664..d9c97e491 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,7 +8,7 @@ from flask.ext.migrate import Migrate, MigrateCommand from flask.ext.script import Manager from sqlalchemy.schema import MetaData -from app import create_app, db +from app import create_app from . import ( create_test_user, service_json, TestClient, get_test_user, template_json, api_key_json) @@ -29,42 +29,9 @@ def app_(request): return app -@pytest.fixture(scope='session') -def db_(app_, request): - Migrate(app_, db) - Manager(db, MigrateCommand) - BASE_DIR = os.path.dirname(os.path.dirname(__file__)) - ALEMBIC_CONFIG = os.path.join(BASE_DIR, 'migrations') - config = Config(ALEMBIC_CONFIG + '/alembic.ini') - config.set_main_option("script_location", ALEMBIC_CONFIG) - - with app_.app_context(): - upgrade(config, 'head') - - def teardown(): - db.session.remove() - db.drop_all() - db.engine.execute("drop table alembic_version") - db.get_engine(app_).dispose() - - request.addfinalizer(teardown) - - @pytest.fixture(scope='function') -def db_session(request): - def teardown(): - db.session.remove() - for tbl in reversed(meta.sorted_tables): - if tbl.fullname not in ['roles']: - db.engine.execute(tbl.delete()) - - meta = MetaData(bind=db.engine, reflect=True) - request.addfinalizer(teardown) - - -@pytest.fixture(scope='function') -def service_one(request, mock_api_user): - return service_json(1, 'service one', [mock_api_user.id]) +def service_one(request, api_user_active): + return service_json(1, 'service one', [api_user_active.id]) # @pytest.fixture(scope='function') @@ -86,10 +53,10 @@ def mock_send_email(request, mocker): @pytest.fixture(scope='function') -def mock_get_service(mocker, mock_api_user): +def mock_get_service(mocker, api_user_active): def _create(service_id): service = service_json( - service_id, "Test Service", [mock_api_user.id], limit=1000, + service_id, "Test Service", [api_user_active.id], limit=1000, active=False, restricted=True) return {'data': service, 'token': 1} @@ -128,12 +95,12 @@ def mock_update_service(mocker): @pytest.fixture(scope='function') -def mock_get_services(mocker, mock_api_user): +def mock_get_services(mocker, api_user_active): def _create(): service_one = service_json( - 1, "service_one", [mock_api_user.id], 1000, True, False) + 1, "service_one", [api_user_active.id], 1000, True, False) service_two = service_json( - 2, "service_two", [mock_api_user.id], 1000, True, False) + 2, "service_two", [api_user_active.id], 1000, True, False) return {'data': [service_one, service_two]} mock_class = mocker.patch( @@ -217,7 +184,7 @@ def mock_delete_service_template(mocker): @pytest.fixture(scope='function') -def mock_api_user(mocker): +def api_user_pending(mocker): from app.notify_client.user_api_client import User user_data = {'id': 1, 'name': 'Test User', @@ -232,55 +199,76 @@ def mock_api_user(mocker): @pytest.fixture(scope='function') -def mock_register_user(mocker, mock_api_user): +def api_user_active(mocker): + from app.notify_client.user_api_client import User + user_data = {'id': 1, + 'name': 'Test User', + 'password': 'somepassword', + 'email_address': 'test@user.gov.uk', + 'mobile_number': '+4412341234', + 'state': 'active', + 'failed_login_count': 0 + } + user = User(user_data) + return user + + +@pytest.fixture(scope='function') +def mock_register_user(mocker, api_user_pending): def _register(name, email_address, mobile_number, password): - mock_api_user.fields['name'] = name - mock_api_user.fields['email_address'] = email_address - mock_api_user.fields['mobile_number'] = mobile_number - mock_api_user.fields['password'] = password - return mock_api_user + api_user_pending.fields['name'] = name + api_user_pending.fields['email_address'] = email_address + api_user_pending.fields['mobile_number'] = mobile_number + api_user_pending.fields['password'] = password + return api_user_pending return mocker.patch('app.user_api_client.register_user', side_effect=_register) @pytest.fixture(scope='function') -def mock_user_loader(mocker, mock_api_user): +def mock_user_loader(mocker, api_user_active): mock_class = mocker.patch('app.main.dao.users_dao.get_user_by_id') - mock_class.return_value = mock_api_user + mock_class.return_value = api_user_active return mock_class @pytest.fixture(scope='function') -def mock_activate_user(mocker, mock_api_user): - def _activate(mock_api_user): - mock_api_user.state = 'active' - return mock_api_user +def mock_activate_user(mocker, api_user_pending): + def _activate(api_user_pending): + api_user_pending.state = 'active' + return api_user_pending return mocker.patch('app.user_api_client.update_user', side_effect=_activate) @pytest.fixture(scope='function') -def mock_user_dao_get_user(mocker, mock_api_user): +def mock_user_dao_get_user(mocker, api_user_active): def _get_user(id): - return mock_api_user + return api_user_active return mocker.patch('app.main.dao.users_dao.get_user_by_id', side_effect=_get_user) @pytest.fixture(scope='function') -def mock_user_dao_get_by_email(mocker, mock_api_user): - mock_api_user.state = 'active' +def mock_get_user(mocker, api_user_active): + def _get_user(id): + return api_user_active + return mocker.patch( + 'app.user_api_client.get_user', side_effect=_get_user) + + +@pytest.fixture(scope='function') +def mock_user_dao_get_by_email(mocker, api_user_active): def _get_user(email_address): - mock_api_user.fields['email_address'] = email_address - return mock_api_user + api_user_active.fields['email_address'] = email_address + return api_user_active return mocker.patch('app.main.dao.users_dao.get_user_by_email', side_effect=_get_user) @pytest.fixture(scope='function') -def mock_inactive_user_dao_get_by_email(mocker, mock_api_user): +def mock_inactive_user_dao_get_by_email(mocker, api_user_pending): def _get_user(email_address): - mock_api_user.fields['email_address'] = email_address - mock_api_user.state = 'pending' - mock_api_user.fields['is_locked'] = True - return mock_api_user + api_user_pending.fields['email_address'] = email_address + api_user_pending.fields['is_locked'] = True + return api_user_pending return mocker.patch('app.main.dao.users_dao.get_user_by_email', side_effect=_get_user) @@ -290,42 +278,77 @@ def mock_user_by_email_not_found(mocker): @pytest.fixture(scope='function') -def mock_user_dao_checkpassword(mocker, mock_api_user): +def mock_get_user_by_email(mocker, api_user_active): - def _check(mock_api_user, password): + def _get_user(email_address): + api_user_active.fields['email_address'] = email_address + return api_user_active + return mocker.patch( + 'app.user_api_client.get_user_by_email', side_effect=_get_user) + + +@pytest.fixture(scope='function') +def mock_get_user_by_email_not_found(mocker): + return mocker.patch( + 'app.user_api_client.get_user_by_email', return_value=None) + + +@pytest.fixture(scope='function') +def mock_user_dao_checkpassword(mocker, api_user_active): + + def _check(api_user_active, password): return True return mocker.patch('app.main.dao.users_dao.verify_password', side_effect=_check) @pytest.fixture(scope='function') -def mock_user_dao_update_email(mocker, mock_api_user): +def mock_verify_password(mocker): + def _verify_password(user, password): + return True + return mocker.patch( + 'app.user_api_client.verify_password', + side_effect=_verify_password) + + +@pytest.fixture(scope='function') +def mock_user_dao_update_email(mocker, api_user_active): def _update(id, email_address): - mock_api_user.fields['email_address'] = email_address + api_user_active.fields['email_address'] = email_address + return api_user_active return mocker.patch('app.main.dao.users_dao.update_email_address', side_effect=_update) @pytest.fixture(scope='function') -def mock_user_dao_update_mobile(mocker, mock_api_user): +def mock_user_dao_update_mobile(mocker, api_user_active): def _update(id, mobile_number): - mock_api_user.fields['mobile_number'] = mobile_number + api_user_active.fields['mobile_number'] = mobile_number + return api_user_active return mocker.patch('app.main.dao.users_dao.update_mobile_number', side_effect=_update) @pytest.fixture(scope='function') -def mock_user_dao_password_reset(mocker, mock_api_user): +def mock_user_dao_password_reset(mocker, api_user_active): def _reset(email): - mock_api_user.state = 'request_password_reset' + api_user_active.state = 'request_password_reset' return mocker.patch('app.main.dao.users_dao.request_password_reset', side_effect=_reset) @pytest.fixture(scope='function') -def mock_user_dao_get_new_password(mocker, mock_api_user): - mock_api_user.state = 'request_password_reset' +def mock_update_user(mocker): + + def _update(user): + return user + return mocker.patch('app.user_api_client.update_user', side_effect=_update) + + +@pytest.fixture(scope='function') +def mock_user_dao_get_new_password(mocker, api_user_active): + api_user_active.state = 'request_password_reset' mock_class = mocker.patch('app.main.dao.users_dao.get_user_by_email') - mock_class.return_value = mock_api_user + mock_class.return_value = api_user_active return mock_class @@ -344,7 +367,9 @@ def mock_revoke_api_key(mocker): def _revoke(service_id, key_id): return {} - mock_class = mocker.patch('app.api_key_api_client.revoke_api_key', side_effect=_revoke) + mock_class = mocker.patch( + 'app.api_key_api_client.revoke_api_key', + side_effect=_revoke) return mock_class @@ -367,3 +392,49 @@ def mock_get_no_api_keys(mocker): mock_class = mocker.patch('app.api_key_api_client.get_api_keys', side_effect=_get_keys) return mock_class + + +@pytest.fixture(scope='function') +def mock_login(mocker, mock_user_dao_get_user, mock_update_user): + def _verify_code(user_id, code, code_type): + return True, '' + mock_class = mocker.patch( + 'app.user_api_client.check_verify_code', + side_effect=_verify_code) + return mock_class + + +@pytest.fixture(scope='function') +def mock_send_verify_code(mocker): + mock_class = mocker.patch('app.user_api_client.send_verify_code') + return mock_class + + +@pytest.fixture(scope='function') +def mock_check_verify_code(mocker): + def _verify(user_id, code, code_type): + return True, '' + mock_class = mocker.patch( + 'app.user_api_client.check_verify_code', + side_effect=_verify) + return mock_class + + +@pytest.fixture(scope='function') +def mock_check_verify_code_code_not_found(mocker): + def _verify(user_id, code, code_type): + return False, 'Code not found' + mock_class = mocker.patch( + 'app.user_api_client.check_verify_code', + side_effect=_verify) + return mock_class + + +@pytest.fixture(scope='function') +def mock_check_verify_code_code_expired(mocker): + def _verify(user_id, code, code_type): + return False, 'Code has expired' + mock_class = mocker.patch( + 'app.user_api_client.check_verify_code', + side_effect=_verify) + return mock_class