def your_details_review(session_id): app.logger.info("starting controller", {'controller': "yourdetails.your_details_review"}) form = ReviewForm() user_details = UserDetails() if form.validate_on_submit(): result = do_pds_search(user_details, session_id) if result == constants.PDS_SEARCH_SUCCESS: app.logger.info("redirecting", {'location': "verification.waiting_for_results"}) session.pop('timeout_threshold', None) return redirect( routes.get_absolute("verification.waiting_for_results")) elif result == constants.PDS_RESULT_INVALID_AGE: app.logger.info("redirecting", {'location': "verification.age_restriction_error"}) session.pop('timeout_threshold', None) return redirect( routes.get_absolute("verification.age_restriction_error")) else: app.logger.warning("pds search failure") # for PDS_REQUEST_TIMEOUT and other unknown errors return redirect(routes.get_absolute("yourdetails.generic_error")) elif form.errors: app.logger.info("submission contains errors") app.logger.info('rendering page', {'page': 'your_details_review'}) return render_template("your-details-review.html", form=form, details=user_details, routes=routes)
def test_confirmation_sender_redirects(self, user_details_mock, set_preference_mock, _): """ Test confirmation_sender redirects properly """ test_cases = ( #(result, expected_endpoint) (False, routes.get_absolute("yourdetails.generic_error")), (True, routes.get_absolute("yourdetails.store_preference_result")), ) user_details_mock.return_value = None for case in test_cases: with self.subTest(case=case): set_preference_mock.return_value, endpoint = case headers = { 'Content-type': 'application/json', 'cookie': common.SESSION_ID } result = self.client.post( routes.get_raw("yourdetails.confirmation_sender"), headers=headers) assert result.status_code == HTTPStatus.FOUND self.assertIn(endpoint, result.headers['Location'])
def waiting_for_results(session_id): app.logger.info("starting controller", {'controller': "verification.waiting_for_results"}) search_result = None if not session.get('timeout_threshold'): session['timeout_threshold'] = int(time.time()) + int( app.config["PDS_REQUEST_TIMEOUT"]) elif int(session.get('timeout_threshold')) <= int(time.time()): search_result = constants.PDS_REQUEST_TIMEOUT if not search_result: search_result = check_status_of_pds_search_result(session_id) result_redirects = { 'success': "verification.verification_option", 'invalid_user': "******", 'insufficient_data': 'verification.contact_details_not_found', 'age_restriction_error': 'verification.age_restriction_error', constants.PDS_REQUEST_TIMEOUT: 'yourdetails.generic_error' } redirect_controller = result_redirects.get(search_result) if redirect_controller: app.logger.info("redirecting", {'location': routes.get_absolute(redirect_controller)}) session.pop('timeout_threshold', None) return redirect(routes.get_absolute(redirect_controller)) app.logger.info('rendering page', {'page': 'waiting_for_pds_results'}) return render_template( "waiting-for-results.html", waiting_message=constants.PDS_SEARCH_WAITING_MESSAGE, routes=routes)
def submit_preference(session_id): user_details = UserDetails() result = set_preference(user_details, session_id) if result is True: return redirect(routes.get_absolute('yourdetails.review_your_choice')) return redirect(routes.get_absolute('yourdetails.generic_error'))
def confirmation_sender(session_id): result = confirm_preference(session_id) if result is True: return redirect( routes.get_absolute('yourdetails.store_preference_result')) return redirect(routes.get_absolute('yourdetails.generic_error'))
def test_verificationoption_redirecting(self, verification_option_mock, client_redirect_mock, request_code_by_pds_mock, render_mock, _): """ Test that verificationoption template is redirecting properly """ test_cases = ( #(delivery_type, expected_result) ('Email', constants.OTP_REQUEST_SUCCESS), ('Email', constants.OTP_REQUEST_MAX_RETRIES), ('SMS', constants.OTP_REQUEST_SUCCESS), ('SMS', constants.OTP_REQUEST_MAX_RETRIES), ('Unrecognised', constants.OTP_REQUEST_SUCCESS), ('SMS', constants.OTP_REQUEST_FAILURE), ) headers = {'Content-type': 'application/json', 'cookie': None} for case in test_cases: with self.subTest(case=case): delivery_type, expected_result = case form_mock = MagicMock() form_mock.validate_on_submit.return_value = True form_mock.radio.data = delivery_type verification_option_mock.return_value = form_mock client_redirect_mock.return_value = "_" request_code_by_pds_mock.return_value = expected_result render_mock.return_value = "_" with self.client as c: with c.session_transaction() as session: session["sms"] = common.USER_DETAILS.get("sms") session["email"] = common.USER_DETAILS.get("email") result = self.client.post('/verificationoption', data=json.dumps(common.USER_DETAILS), headers=headers) if delivery_type not in ('Email', 'SMS'): client_redirect_mock.assert_called_with( routes.get_absolute( "verification.contact_details_not_recognised")) elif expected_result == constants.OTP_REQUEST_SUCCESS: client_redirect_mock.assert_called_with( routes.get_absolute("verification.enter_your_code")) elif expected_result == constants.OTP_REQUEST_MAX_RETRIES: client_redirect_mock.assert_called_with( routes.get_absolute("verification.resend_code_error")) else: render_mock.assert_called()
def set_your_preference(session_id): form = ChoiceOption() is_timeout = None if form.validate_on_submit() and form.validate(): if form.radio.data == 'Yes': session['opted_out'] = 'inactive' session['preference'] = 'optedOut' if form.radio.data == 'No': session['opted_out'] = 'active' session['preference'] = 'optedIn' session.pop('timeout_threshold', None) return redirect(routes.get_absolute('yourdetails.submit_preference')) else: if form.errors: flash(form.errors) if not session.get('timeout_threshold'): session['timeout_threshold'] = int(time.time()) + int( app.config["PDS_REQUEST_TIMEOUT"]) elif int(session.get('timeout_threshold')) <= int(time.time()): is_timeout = constants.PDS_REQUEST_TIMEOUT if is_timeout: session.pop('timeout_threshold', None) return redirect(routes.get_absolute('yourdetails.generic_error')) if session.get('pds_opted_out') not in ('active', 'inactive', constants.GET_PREFERENCE_EMPTY): session['pds_opted_out'] = get_current_preference(session_id) if session.get('pds_opted_out') == constants.GET_PREFERENCE_INCOMPLETE: return render_template( 'waiting-for-results.html', waiting_message=constants.PDS_SEARCH_WAITING_MESSAGE) if session.get('pds_opted_out') == constants.GET_PREFERENCE_FAILURE: session.pop('timeout_threshold', None) return redirect(routes.get_absolute('yourdetails.generic_error')) if session.get('pds_opted_out') in ('active', 'inactive'): form.radio.data = 'Yes' if session.get( 'pds_opted_out') == 'inactive' else 'No' session.pop('timeout_threshold', None) app.logger.info('rendering page', {'page': 'setyourpreferences'}) return render_template('setyourpreferences.html', form=form, current_preference=session.get('pds_opted_out'), routes=routes)
def enteryourcode(session_id): app.logger.info("starting controller", {'controller': "verification.enter_your_code"}) form = CodeForm() is_reenter_code = False def generate_template(form_local, sess, is_reenter): return { 'template_name_or_list': 'enteryourcode.html', 'form': form_local, 'is_resent': sess.get('is_resent'), # set in /resendcode 'is_resent_max_reached': sess.get('is_resent_max_reached'), 'is_reenter_code': is_reenter, 'routes': routes, } if not form.validate_on_submit() or not form.validate(): if form.errors: app.logger.info("submission contains errors") flash(form.errors) app.logger.info('rendering page', {'page': 'enteryourcode'}) return render_template( **generate_template(form, session, is_reenter_code)) verification_result = is_otp_verified_by_pds(session_id, form.enterOtpInput.data) if verification_result == constants.CORRECT_OTP: app.logger.info("redirecting", {'location': "yourdetails.set_your_preference"}) return redirect(routes.get_absolute('yourdetails.set_your_preference')) if verification_result == constants.INCORRECT_OTP_MAX_RETRIES: app.logger.info("max otp retries reached") app.logger.info("redirecting", {'location': "verification.incorrect_code_error"}) return redirect( routes.get_absolute('verification.incorrect_code_error')) if verification_result == constants.INCORRECT_OTP: app.logger.info("incorrect otp") form.add_incorrect_otp_error() is_reenter_code = True flash(form.errors) return render_template( **generate_template(form, session, is_reenter_code)) if verification_result == constants.CORRECT_OTP_EXPIRED: return redirect(routes.get_absolute('verification.expired_code_error')) return redirect(routes.get_absolute('yourdetails.generic_error'))
def test_thank_you(self, user_details_mock, is_session_valid_mock): """Test correct text is displayed if user has opted_out""" headers = { 'Content-type': 'application/json', 'cookie': common.SESSION_ID } userDetails = common.get_user_details() test_cases = [ # (is_session_valid, opted_out, flask_session, expected_status, expected_text, expected_text_id, expected_location) (True, 'active', { 'is_successfully_stored': True }, HTTPStatus.OK, 'will not be shared', 'not-shared', None), (True, 'inactive', { 'is_successfully_stored': True }, HTTPStatus.OK, 'can be shared', 'shared', None), (True, 'active', { 'is_successfully_stored': False }, HTTPStatus.FOUND, None, None, routes.get_absolute('yourdetails.choice_not_saved')), (True, 'inactive', { 'is_successfully_stored': False }, HTTPStatus.FOUND, None, None, routes.get_absolute('yourdetails.choice_not_saved')), (False, 'inactive', {}, HTTPStatus.OK, 'Sorry, you\'ll need to start again', 'mainBody', None), (True, 'inactive', {}, HTTPStatus.OK, 'Sorry, you\'ll need to start again', 'mainBody', None), ] for case in test_cases: is_session_valid, opted_out, flask_session, expected_status, expected_text, \ expected_text_id, expected_location = case with self.subTest(case=case): userDetails["opted_out"] = opted_out user_details_mock.return_value = userDetails is_session_valid_mock.return_value = is_session_valid common.set_session_data(self.client, flask_session) result = self.client.get( routes.get_raw('yourdetails.thank_you'), headers=headers) self.assertEqual(result.status_code, expected_status) if expected_text: doc = common.html(result) self.assertIn(expected_text, str(doc.find(id=expected_text_id))) if expected_location: self.assertIn(expected_location, result.headers['Location'])
def test_catch_unhandled_exception(self, redirect_mock, _): def bang(): raise Exception('bang') wrapper = utils.catch_unhandled_exceptions(bang) wrapper() redirect_mock.assert_called_with(routes.get_absolute('yourdetails.generic_error'))
def test_review_your_choice_redirects_to_generic_error_when_get_confirmation_delivery_details_returns_none( self, redirect_mock, _): review_your_choice.__wrapped__('some session id') redirect_mock.assert_called_with( routes.get_absolute('yourdetails.generic_error'))
def test_resendcode_when_retries_exceeded_max_is_redirecting_to_resendcodeerror( self, resend_code_by_pds_mock): resend_code_by_pds_mock.return_value = constants.RESEND_CODE_MAX_EXCEEDED controllers.resend_code.__wrapped__('session_id') self.redirect_mock.assert_called_with( routes.get_absolute('verification.resend_code_error'))
def test_root_redirect(self): """ Test root URL gives a 302 """ result = self.client.get('/') assert HTTPStatus(result.status_code) == HTTPStatus.FOUND assert routes.get_absolute( "main.landing_page") in result.headers['Location']
def test_post_set_preference(self, choice_option_mock, _, **kwargs): """ Test your_details_review page returns a 200 """ test_cases = ('Yes', 'No') form_mock = MagicMock() for case in test_cases: with self.subTest(case=case): form_mock.validate_on_submit.return_value = True form_mock.radio.data = case choice_option_mock.return_value = form_mock mock = kwargs['mock'] mock.get(self.app.config['PREFERENCE_RESULT_URL'], text=get_preference_results_callback_inactive) mock.post(self.app.config['SET_PREFERENCE_URL'], text="{}") with self.client as c: with c.session_transaction() as session: session['timeout_threshold'] = 1 result = self.client.post('/setyourpreference') assert HTTPStatus(result.status_code) == HTTPStatus.FOUND assert routes.get_absolute("yourdetails.review_your_choice" ) in result.headers['Location'] with self.client as c: with c.session_transaction() as session: assert not 'timeout_threshold' in session
def test_enteryourcode_when_code_expired_is_redirecting_to_expiredcodeerror( self, is_otp_verified_by_pds_mock, _): is_otp_verified_by_pds_mock.return_value = constants.CORRECT_OTP_EXPIRED controllers.enteryourcode.__wrapped__('session_id') self.redirect_mock.assert_called_with( routes.get_absolute('verification.expired_code_error'))
def verificationoption(session_id): app.logger.info("starting controller", {'controller': "verification.verification_option"}) user_details = UserDetails() form = VerificationOption(user_details=user_details) if form.validate_on_submit(): if form.radio.data in ('Email', 'SMS'): # below 2 lines can be removed when session cleaning # will be implemented session['is_resent'] = False session['is_resent_max_reached'] = False request_code_ret = request_code_by_pds( session_id, otp_delivery_type=str(form.radio.data).lower()) if request_code_ret == constants.OTP_REQUEST_SUCCESS: app.logger.info("redirecting", {'location': "verification.enter_your_code"}) return redirect( routes.get_absolute('verification.enter_your_code')) elif request_code_ret == constants.OTP_REQUEST_MAX_RETRIES: # it is reached when we already exceeded /resend_code limit and # we try to use /requestcode afterwards. Then API returns # 'max_count_exceeded' the content of the response app.logger.warning("max retries reached") app.logger.info("redirecting", {'location': "verification.resend_code_error"}) return redirect( routes.get_absolute('verification.resend_code_error')) elif form.radio.data == 'Unrecognised': app.logger.info("redirecting", {'location': "verification.enter_your_code"}) return redirect( routes.get_absolute( 'verification.contact_details_not_recognised')) elif form.errors: app.logger.info("submission contains errors", {'errors': json.dumps(form.errors)}) app.logger.info('rendering page', {'page': 'verificationoption'}) return render_template('verificationoption.html', user_details=user_details, form=form, routes=routes)
def resend_code(session_id): app.logger.info("starting controller", {'controller': "verification.resend_code"}) result = resend_code_by_pds(session_id) if result == constants.RESEND_CODE_MAX_EXCEEDED: return redirect(routes.get_absolute('verification.resend_code_error')) if result == constants.RESEND_CODE_SUCCESS: session['is_resent'] = True session['is_resent_max_reached'] = False elif result == constants.RESEND_CODE_MAX_REACHED: session['is_resent'] = True session['is_resent_max_reached'] = True return redirect(routes.get_absolute('verification.enter_your_code'))
def contactdetailsnotrecognised(session_id): app.logger.info( "starting controller", {'controller': "verification.contact_details_not_recognised"}) if not utils.clean_state_model(): return redirect(routes.get_absolute('yourdetails.generic_error')) app.logger.info('rendering page', {'page': 'contactdetailsnotrecognised'}) return render_template('contactdetailsnotrecognised.html', routes=routes)
def test_waiting_for_results_redirecting_when_timeout( self, check_status_of_pds_search_result_mock, redirect_mock, _): """ Test waiting_for_results page redirect to error when timeout""" common.registerExceptionHandlers(self.app) redirect_mock.return_value = "_" check_status_of_pds_search_result_mock.return_value = "pds_request_timeout" self.client.post(routes.get_raw("verification.waiting_for_results")) redirect_mock.assert_called_with( routes.get_absolute("yourdetails.generic_error"))
def test_handle_requests_when_an_exception_is_raised(self, name_form_mock, redirect_mock, log_safe_exception_mock): app = create_app('ndopapp.config.Config') # force raising exception on a controller logic test_exception = Exception('message') name_form_mock.side_effect = test_exception client = app.test_client() result = client.get(routes.get_absolute("yourdetails.your_details")) log_safe_exception_mock.assert_called_with(test_exception) redirect_mock.assert_called_with("main.generic_error")
def thank_you(): if not session.get('is_successfully_stored'): return redirect(routes.get_absolute('yourdetails.choice_not_saved')) user_details = UserDetails() session.clear() app.logger.info('rendering page', {'page': 'thank_you'}) response = make_response( render_template("thank-you.html", user_details=user_details, routes=routes)) response.set_cookie("session_id_nojs", '', max_age=0) return response
def review_your_choice(session_id): user_details = UserDetails() delivery_ret = get_confirmation_delivery_details(session_id) if delivery_ret and delivery_ret.get('method') in ("sms", "email"): app.logger.info('rendering page', {'page': 'review_your_choice'}) return render_template("reviewyourchoice.html", user_details=user_details, confirmation_delivery_details=delivery_ret, routes=routes) return redirect(routes.get_absolute('yourdetails.generic_error'))
def test_get_store_preference_result(self, get_store_preference_result_mock, _): """ Test store_preference_result redirects properly """ test_cases = ( #(store_ret, is_timeout_in_session, timeout_threshold, expected_status, expected_endpoint) ("success", False, 1, HTTPStatus.FOUND, routes.get_absolute('main.generic_error')), ("success", False, constants.FAR_IN_THE_FUTURE, HTTPStatus.FOUND, routes.get_absolute('yourdetails.thank_you')), ("not_completed", True, None, HTTPStatus.OK, None), ("failure", False, None, HTTPStatus.FOUND, routes.get_absolute('yourdetails.choice_not_saved')), ) for case in test_cases: with self.subTest(case=case): store_ret, is_timeout_in_session, timeout_threshold, status, endpoint = case get_store_preference_result_mock.return_value = store_ret common.update_session_data( self.client, {'timeout_threshold': timeout_threshold}) headers = { 'Content-type': 'application/json', 'cookie': common.SESSION_ID } result = self.client.get( routes.get_raw("yourdetails.store_preference_result"), headers=headers) self.assertEqual(result.status_code, status) if status == HTTPStatus.FOUND: self.assertIn(endpoint, result.headers['Location']) self.assertEqual( 'timeout_threshold' in common.get_session_data(self.client), is_timeout_in_session)
def details_auth_option(session_id): app.logger.info("starting controller", {'controller': "yourdetails.details_auth_option"}) form = AuthOption() if form.validate_on_submit() and form.validate(): if form.radio.data == "Yes": app.logger.info("redirecting", {'location': "yourdetails.details_nhs_number"}) return redirect( routes.get_absolute("yourdetails.details_nhs_number")) app.logger.info("redirecting", {'location': "yourdetails.details_postcode"}) return redirect(routes.get_absolute("yourdetails.details_postcode")) elif form.errors: app.logger.info("submission contains errors") flash(form.errors) app.logger.info('rendering page', {'page': 'your_details_route_selector'}) return render_template("details-auth-option.html", form=form, routes=routes)
def test_waiting_for_results_redirecting_on_timeout( self, redirect_mock, _): """ Test waiting_for_results page redirect to genericerror on timeout""" redirect_mock.return_value = "_" with self.client as c: with c.session_transaction() as session: session['timeout_threshold'] = 1 self.client.post(routes.get_raw("verification.waiting_for_results")) redirect_mock.assert_called_with( routes.get_absolute("yourdetails.generic_error"))
def ensure_safe_redirect_url(target): white_list_endpoints = ( routes.get_relative('yourdetails.your_details'), routes.get_relative('yourdetails.details_dob'), routes.get_relative('yourdetails.details_auth_option'), routes.get_relative('yourdetails.details_nhs_number'), routes.get_relative('yourdetails.details_postcode'), routes.get_relative('yourdetails.your_details_review')) if target not in white_list_endpoints: return routes.get_absolute('main.landing_page') return routes.make_absolute(target)
def test_waiting_for_results_redirecting_when_no_contact_details( self, check_status_of_pds_search_result_mock, redirect_mock, _): """ Test waiting_for_results page redirect to contactdetailsnotfound when no contact details""" redirect_mock.return_value = "_" check_status_of_pds_search_result_mock.return_value = "insufficient_data" self.client.post(routes.get_raw("verification.waiting_for_results")) redirect_mock.assert_called_with( routes.get_absolute("verification.contact_details_not_found")) with self.client as c: with c.session_transaction() as session: assert not session.get("email") assert not session.get("sms")
def test_waiting_for_results_redirecting_when_having_contact_details( self, check_status_of_pds_search_result_mock, redirect_mock, _): """ Test waiting_for_results page redirect to verificationoption when having contact details""" redirect_mock.return_value = "_" check_status_of_pds_search_result_mock.return_value = "success" with self.client as c: with c.session_transaction() as session: session["sms"] = common.USER_DETAILS.get("sms") session["email"] = common.USER_DETAILS.get("email") self.client.post(routes.get_raw("verification.waiting_for_results")) redirect_mock.assert_called_with( routes.get_absolute("verification.verification_option"))
def store_preference_result(session_id): if not session.get('timeout_threshold'): session['timeout_threshold'] = int(time.time()) + int( app.config["PDS_REQUEST_TIMEOUT"]) elif int(session.get('timeout_threshold')) <= int(time.time()): session.pop('timeout_threshold', None) return redirect(routes.get_absolute('yourdetails.generic_error')) result = store_preference(session_id) if result == "success": session['is_successfully_stored'] = True session.pop('timeout_threshold', None) return redirect(routes.get_absolute('yourdetails.thank_you')) if result == "failure": session.pop('timeout_threshold', None) return redirect(routes.get_absolute('yourdetails.choice_not_saved')) app.logger.info('rendering page', {'page': 'waiting_for_store_preference_results'}) return render_template("waiting-for-results.html", waiting_message=constants.PREF_WAITING_MESSAGE, routes=routes)
def test_enteryourcode_status_is_200(self, is_otp_verified_by_pds_mock, code_form_mock, client_redirect_mock, _): """Test that enteryourcode redirects properly and returns code 200""" form_mock = MagicMock() form_mock.validate_on_submit.return_value = True code_form_mock.return_value = form_mock client_redirect_mock.return_value = "_" is_otp_verified_by_pds_mock.return_value = constants.CORRECT_OTP headers = {'Content-type': 'application/json', 'cookie': None} result = self.client.post('/enteryourcode', headers=headers) client_redirect_mock.assert_called_with( routes.get_absolute('yourdetails.set_your_preference'))