Example #1
0
    def test_user_profile_post_name_and_password_and_email(self):
        """
            Test posting to the user profile page with the name, the password, and the email changed.

            Expected result: The form is shown with the new data. The user's name and password are changed, the email
                             is not, but a mail has been sent to the new address.
        """
        email = '*****@*****.**'
        name = 'John Doe'
        password = '******'
        user = User(email, name)
        user.set_password(password + '!')
        with mail.record_messages() as outgoing:
            user.set_password(password)
            self.assertEqual(1, len(outgoing))
            self.assertIn('Your Password Has Been Changed', outgoing[0].subject)
        db.session.add(user)
        db.session.commit()

        user_id = user.id

        self.client.post('/user/login', follow_redirects=True, data=dict(
            email=email,
            password=password
        ))

        new_name = 'Jane Doe'
        new_password = '******'
        new_email = '*****@*****.**'
        with mail.record_messages() as outgoing:
            response = self.client.post('/user/profile', follow_redirects=True, data=dict(
                name=new_name,
                email=new_email,
                password=new_password,
                password_confirmation=new_password
            ))
            data = response.get_data(as_text=True)

            self.assertEqual(2, len(outgoing))
            self.assertIn('Change Your Email Address', outgoing[1].subject)
            self.assertEqual([new_email], outgoing[1].recipients)

            self.assertIn('User Profile', data)
            self.assertIn(f'value="{new_name}"', data)
            self.assertIn(f'value="{email}"', data)
            self.assertIn('Your changes have been saved.', data)
            self.assertIn('An email has been sent to the new address', data)

            user = User.load_from_id(user_id)
            self.assertEqual(new_name, user.name)
            self.assertEqual(email, user.get_email())
            self.assertTrue(user.check_password(new_password))
Example #2
0
    def test_reset_password_request_post_existing_user(self):
        """
            Test accessing the password reset request form via POST with an existing user.

            Expected result: The password reset mail is sent to the user and a message is displayed.
        """
        email = '*****@*****.**'
        password = '******'
        name = 'John Doe'
        user_id = 1
        user = User(email, name)
        user.set_password(password)

        db.session.add(user)
        db.session.commit()
        self.assertEqual(user_id, user.id)

        with mail.record_messages() as outgoing:
            response = self.client.post('/user/reset-password', follow_redirects=True, data=dict(
                email=email
            ))
            data = response.get_data(as_text=True)

            self.assertEqual(1, len(outgoing))
            self.assertIn('Reset Your Password', outgoing[0].subject)

            self.assertIn('<h1>Log In</h1>', data)
            self.assertIn('An email has been sent to the specified address.', data)
Example #3
0
    def test_send_success_multiple_recipients(self):
        """
            Test sending an email to multiple recipients.

            Expected result: The email is sent successfully.
        """

        subject = 'Test Subject'
        body_path = 'email/test'
        sender = '*****@*****.**'
        recipients = ['*****@*****.**', '*****@*****.**']
        body_plain = 'Plain Body'
        body_html = 'HTML Body'

        email = Email(subject, body_path, sender)
        email._body_plain = body_plain
        email._body_html = body_html

        with mail.record_messages() as outgoing:
            email.send(recipients)

            self.assertEqual(1, len(outgoing))
            self.assertIn(subject, outgoing[0].subject)
            self.assertEqual(sender, outgoing[0].sender)
            self.assertListEqual(recipients, outgoing[0].recipients)
            self.assertEqual(body_plain, outgoing[0].body)
            self.assertEqual(body_html, outgoing[0].html)
Example #4
0
    def test_send_failure(self):
        """
            Test sending an email to a recipient given in a wrong type.

            Expected result: The email is not sent.
        """

        subject = 'Test Subject'
        body_path = 'email/test'
        sender = '*****@*****.**'
        recipients = None
        body_plain = 'Plain Body'
        body_html = 'HTML Body'

        email = Email(subject, body_path, sender)
        email._body_plain = body_plain
        email._body_html = body_html

        with self.assertRaises(TypeError) as message:
            with mail.record_messages() as outgoing:
                # noinspection PyTypeChecker
                email.send(recipients)

                self.assertEqual(0, len(outgoing))
                self.assertEqual('Argument "recipients" must be a string or a list of strings.', message)
Example #5
0
 def test_email_confimation_token_of_invalid_user_raise_404_error_message(self):
     """
     Com:
         - email de usuário que não existe no sistema.
     Quando:
         1. enviamos emails de confirmação (utilizando diretamente notifications.py)
         2. acesssamos o link enviado por email
     Verificamos:
         - que o email enviado contem um link para confirmar email.
         - após acessar o link, a página mostra o erro 404 com a mensagem certa.
     """
     # with
     expected_error_msg = u'Usuário não encontrado'
     fake_user_email = u'*****@*****.**'
     # when
     with mail.record_messages() as outbox:
         send_confirmation_email(fake_user_email)
         # then
         # temos um email
         self.assertEqual(1, len(outbox))
         email_msg = outbox[0]
         # pegamos o link com token
         links_found = email_confirm_url_pattern.findall(email_msg.html)
         # tem pelo menos 1 link, e tem só um link para o reset/password com token
         self.assertGreaterEqual(1, len(links_found))
         email_confirmation_url_with_token = [url for url in links_found if '/admin/confirm/' in url]
         # temos a url com o token
         self.assertEqual(1, len(email_confirmation_url_with_token))
         email_confirmation_url_with_token = email_confirmation_url_with_token[0]
     # acessamos o link do email
     confirmation_response = self.client.get(email_confirmation_url_with_token, follow_redirects=True)
     self.assertStatus(confirmation_response, 404)
     self.assertTemplateUsed('errors/404.html')
     error_msg = self.get_context_variable('message')
     self.assertEqual(error_msg, error_msg)
Example #6
0
    def test_confirmation_works(self):
        self.register_and_login('*****@*****.**', 'test123')

        with mail.record_messages() as outbox:
            res = self.client.post('/confirm', data=dict(
                email=u'*****@*****.**'
            ))
            self.assertEqual(len(outbox), 1)
            msg = outbox[0]
            self.assertEqual(msg.sender, '*****@*****.**')
            self.assertEqual(msg.recipients, ['*****@*****.**'])
            assert 'http://localhost/confirm/' in msg.body
            assert ('Confirmation instructions have been '
                    'sent to [email protected]') in res.data

        token = re.search(
            r'http:\/\/localhost\/confirm\/(.+)',
            msg.body,
            flags=re.MULTILINE
        ).group(1)

        res = self.client.get('/confirm/%s' % token)
        self.assertRedirects(res, '/confirm/success')

        res = self.client.get('/confirm/success')
        self.assertRedirects(res, '/activity')

        res = self.client.get('/activity')
        self.assert200(res)
        assert 'Your email has been confirmed' in res.data
Example #7
0
    def test_reset_password_with_unconfirmed_email_shows_unconfirm_email_error(self):
        """
        Com:
            um novo usuário (com email NÃO confirmado),
        Quando:
            1. solicitamos recuperar senha.
            2. obtemos o email com a url necessária para recuperação.
            3. e solicitamos uma nova senha, com o link (token) do email.
        Verificamos:
            - a pagina deve informar que é necessário confirmar o email.
            - a troca de senha não procede.
            - a pagina deve mostrar o template admin/auth/unconfirm_email.html
        """

        # with
        reset_pwd_url = url_for('admin.reset')
        credentials = {
            'email': '*****@*****.**',
            'password': '******'
        }

        # when
        create_user(credentials['email'], credentials['password'], False)
        with mail.record_messages() as outbox:
            response = self.client.post(
                reset_pwd_url,
                data={'email': credentials['email']},
                follow_redirects=True)
            # then
            # no foi enviado nenhum email
            self.assertEqual(0, len(outbox))
            self.assertStatus(response, 200)
            self.assertTemplateUsed('admin/auth/unconfirm_email.html')
            user = get_user_by_email(credentials['email'])
            self.assertTrue(user.is_correct_password(credentials['password']))
Example #8
0
 def test_post_with_invalid_email_shows_error(self):
     with mail.record_messages() as outbox:
         res = self.client.post("/invite", data={"email": "blarg"})
         self.assertEqual(len(outbox), 0)
     self.assert200(res)
     assert "Invalid email address" in res.data
     assert "Your form submission was invalid" in res.data
Example #9
0
    def test_user_profile_get(self):
        """
            Test accessing the user profile page.

            Expected result: The form is shown with prepopulated data.
        """
        email = '*****@*****.**'
        name = 'John Doe'
        password = '******'
        user = User(email, name)
        user.set_password(password)
        db.session.add(user)
        db.session.commit()

        self.client.post('/user/login', follow_redirects=True, data=dict(
            email=email,
            password=password
        ))

        with mail.record_messages() as outgoing:
            response = self.client.get('/user/profile', follow_redirects=True)
            data = response.get_data(as_text=True)

            self.assertEqual(0, len(outgoing))

            self.assertIn('User Profile', data)
            self.assertIn(f'value="{name}"', data)
            self.assertIn(f'value="{email}"', data)
            self.assertNotIn('Your changes have been saved.', data)
            self.assertNotIn('An email has been sent to the new address', data)
Example #10
0
    def test_delete_profile_request_failure(self):
        """
            Test requesting the deletion of the user's account with an invalid form.
            Expected result: No email is sent.
        """

        email = '*****@*****.**'
        password = '******'
        name = 'John Doe'
        user_id = 1
        user = User(email, name)
        user.set_password(password)

        db.session.add(user)
        db.session.commit()
        self.assertEqual(user_id, user.id)

        self.client.post('/user/login', follow_redirects=True, data=dict(
            email=email,
            password=password
        ))

        with mail.record_messages() as outgoing:
            response = self.client.post('/user/delete', follow_redirects=True, data=dict())
            data = response.get_data(as_text=True)

            self.assertEqual(0, len(outgoing))

            self.assertNotIn('An email has been sent to your email address.', data)
            self.assertNotIn('to delete your user profile.', data)
            self.assertIn('<h1>User Profile</h1>', data)
Example #11
0
 def test_post_with_valid_email_sends_invite(self):
     with mail.record_messages() as outbox:
         res = self.client.post('/invite', data={
             'email': '*****@*****.**'
         }, follow_redirects=True)
         self.assertEqual(len(outbox), 1)
     self.assert200(res)
     assert 'Invitation sent!' in res.data
Example #12
0
    def test_registration_sends_email(self):
        with mail.record_messages() as outbox:
            self.register_and_login('*****@*****.**', 'test123')

            self.assertEqual(len(outbox), 1)
            msg = outbox[0]
            self.assertEqual(msg.sender, '*****@*****.**')
            self.assertEqual(msg.recipients, ['*****@*****.**'])
            assert 'http://localhost/confirm/' in msg.body
Example #13
0
    def test_sends_confirmation_email_on_post(self):
        with mail.record_messages() as outbox:
            res = self.client.post('/register/step/1.5')

            self.assertEqual(len(outbox), 1)
            msg = outbox[0]
            self.assertEqual(msg.sender, '*****@*****.**')
            self.assertEqual(msg.recipients, ['*****@*****.**'])
            assert 'http://localhost/confirm/' in msg.body
Example #14
0
 def test_post_with_invalid_email_shows_error(self):
     with mail.record_messages() as outbox:
         res = self.client.post('/invite', data={
             'email': 'blarg'
         })
         self.assertEqual(len(outbox), 0)
     self.assert200(res)
     assert 'Invalid email address' in res.data
     assert 'Your form submission was invalid' in res.data
Example #15
0
 def test_post_with_valid_email_sends_invite(self):
     with mail.record_messages() as outbox:
         res = self.client.post("/invite", data={"email": "*****@*****.**"}, follow_redirects=True)
         self.assertEqual(len(outbox), 1)
         msg = outbox[0]
         self.assertEqual(msg.sender, "*****@*****.**")
         self.assertEqual(msg.recipients, ["*****@*****.**"])
         assert "https://noi.org" in msg.body
     self.assert200(res)
     assert "Invitation sent!" in res.data
Example #16
0
    def test_reset_password_with_unconfirmed_email_raise_validation_error_2(self):
        """
        Com:
            um novo usuário (com email confirmado),
        Quando:
            1. solicitamos recuperar senha.
            2. obtemos o email com a url necessária para recuperação.
            3. mudamos o usuário para ter seu email como NÃO confirmado.
            4. e solicitamos uma nova senha, com o link (token) do email.
        Verificamos:
            - a pagina deve informar que é necessário confirmar o email.
            - a troca de senha não procede.
            - a pagina deve mostrar o template admin/auth/unconfirm_email.html
        """

        # with
        reset_pwd_url = url_for('admin.reset')
        credentials = {
            'email': '*****@*****.**',
            'password': '******'
        }

        # when
        create_user(credentials['email'], credentials['password'], True)
        with mail.record_messages() as outbox:
            response = self.client.post(
                reset_pwd_url,
                data={'email': credentials['email']},
                follow_redirects=True)
            # then
            self.assertEqual(1, len(outbox))
            email_msg = outbox[0]
            # recupero os links do email
            links_found = reset_pwd_url_pattern.findall(email_msg.html)
            # tem pelo menos 1 link, e tem só um link para o reset/password com token
            self.assertGreaterEqual(1, len(links_found))
            resert_url_with_token = [url for url in links_found if reset_pwd_url in url][0]

        # agora o usuário tem o email NÃO confirmado.
        user = get_user_by_email(credentials['email'])
        user.email_confirmed = False
        dbsql.session.add(user)
        dbsql.session.commit()
        # tentamos recuperar a senha com o link/token do email
        new_password = '******'
        response = self.client.post(
                resert_url_with_token,
                data={'password': new_password},
                follow_redirects=True)
        self.assertStatus(response, 200)
        self.assertTemplateUsed('admin/auth/unconfirm_email.html')
        user = get_user_by_email(credentials['email'])
        self.assertTrue(user.is_correct_password(credentials['password']))
Example #17
0
 def test_post_with_valid_email_sends_invite(self):
     with mail.record_messages() as outbox:
         res = self.client.post('/invite', data={
             'email': '*****@*****.**'
         }, follow_redirects=True)
         self.assertEqual(len(outbox), 1)
         msg = outbox[0]
         self.assertEqual(msg.sender, '*****@*****.**')
         self.assertEqual(msg.recipients, ['*****@*****.**'])
         assert 'https://noi.org' in msg.body
     self.assert200(res)
     assert 'Invitation sent!' in res.data
Example #18
0
    def test_registration_complains_if_email_is_taken(self):
        self.register_and_login('*****@*****.**', 'test123')
        self.logout()

        with mail.record_messages() as outbox:
            res = self.client.post('/register', data=dict(
                email=u'*****@*****.**'
            ))
            self.assertEqual(len(outbox), 0)

        assert ('[email protected] is already '
                'associated with an account') in res.data
Example #19
0
    def test_try_to_create_user_without_email_must_raise_error_notification(self):
        """
        Com:
            - usuario administrador (com email confirmado)
        Quando:
            1. acessamos /admin
            2. tentamos cadastrar um novo usuário, ** sem inserir email **
        Verificamos:
            - o usuário não é criado.
            - o usuário administrado é notificodo do erro da operação.
        """

        # with
        admin_user = {
            'email': '*****@*****.**',
            'password': '******',
        }
        create_user(admin_user['email'], admin_user['password'], True)
        new_user = {
            'email': '*****@*****.**',
            'password': '******'
        }
        login_url = url_for('admin.login_view')
        create_user_url = '/admin/user/new/'
        expected_form_error = {'email': [u'This field is required.']}
        # when
        with mail.record_messages() as outbox:

            with self.client as client:
                # login do usuario admin
                login_response = client.post(
                    login_url,
                    data=admin_user,
                    follow_redirects=True)
                self.assertStatus(login_response, 200)
                self.assertTemplateUsed('admin/index.html')
                self.assertTrue(current_user.is_authenticated)

                # "preencher" from sem o email do novo usuário
                create_user_response = client.post(
                    create_user_url,
                    data={'email': ''},
                    follow_redirects=True)
                # then
                self.assertStatus(create_user_response, 200)
                self.assertTemplateUsed('admin/model/create.html')
                # tem erro no formulario
                context_form = self.get_context_variable('form')
                # self.assertIsInstance(context_form, admin_form.UserForm)
                self.assertEqual(expected_form_error, context_form.errors)
            # não temos email
            self.assertEqual(0, len(outbox))
Example #20
0
    def test_reset_password_with_invalid_password_raise_validation_error(self):
        """
        Com:
            um novo usuário (com email confirmado),
        Quando:
            1. solicitamos recuperar senha.
            2. obtemos o email com a url necessária para recuperação.
            3. e solicitamos uma nova senha, com o link do email.
            4. inserimos uma senha inválida ('')
        Verificamos:
            - a pagina deve informar de que senha é requerida
            - a senha do usuário não deve ser modificada
        """

        # with
        reset_pwd_url = url_for('admin.reset')
        credentials = {
            'email': '*****@*****.**',
            'password': '******'
        }

        # when
        create_user(credentials['email'], credentials['password'], True)
        with mail.record_messages() as outbox:
            response = self.client.post(
                reset_pwd_url,
                data={'email': credentials['email']},
                follow_redirects=True)
            # then
            self.assertEqual(1, len(outbox))
            email_msg = outbox[0]
            # recupero os links do email
            links_found = reset_pwd_url_pattern.findall(email_msg.html)
            # tem pelo menos 1 link, e tem só um link para o reset/password com token
            self.assertGreaterEqual(1, len(links_found))
            resert_url_with_token = [url for url in links_found if reset_pwd_url in url][0]

        invalid_password = ''
        response = self.client.post(
                resert_url_with_token,
                data={'password': invalid_password},
                follow_redirects=True)
        self.assertStatus(response, 200)
        context_form = self.get_context_variable('form')
        expected_form_error = {'password': [u'This field is required.']}
        self.assertEqual(expected_form_error, context_form.errors)
        self.assertIn(expected_form_error['password'][0], response.data.decode('utf-8'))
        user = get_user_by_email(credentials['email'])
        self.assertFalse(user.is_correct_password(invalid_password))
Example #21
0
    def test_link_sent_via_email_to_reset_password_works_fine(self):
        """
        Com:
            um novo usuário (com email confirmado),
        Quando:
            1. solicitamos recuperar senha.
            2. obtemos o email com a url necessária para recuperação.
            3. e solicitamos uma nova senha, com o link do email.
            4. inserimos uma nova senha para o úsuario.
        Verificamos:
            - a pagina de recuperar senha tenha o form esperado.
            - a senha do usuário deve ser atualizada.
        """

        # with
        reset_pwd_url = url_for('admin.reset')
        credentials = {
            'email': '*****@*****.**',
            'password': '******'
        }

        # when
        create_user(credentials['email'], credentials['password'], True)
        with mail.record_messages() as outbox:
            response = self.client.post(
                reset_pwd_url,
                data={'email': credentials['email']},
                follow_redirects=True)
            # then
            self.assertEqual(1, len(outbox))
            email_msg = outbox[0]
            # recupero os links do email
            links_found = reset_pwd_url_pattern.findall(email_msg.html)
            # tem pelo menos 1 link, e tem só um link para o reset/password com token
            self.assertGreaterEqual(1, len(links_found))
            resert_url_with_token = [url for url in links_found if reset_pwd_url in url][0]

        new_password = '******'
        response = self.client.post(
                resert_url_with_token,
                data={'password': new_password},
                follow_redirects=True)
        self.assertStatus(response, 200)
        # verificação da nova senha do usuario
        user = get_user_by_email(credentials['email'])
        self.assertTrue(user.is_correct_password(new_password))
Example #22
0
    def test_reset_password_send_valid_link_via_email(self):
        """
        Com:
            um novo usuário (com email confirmado),
        Quando:
            solicitamos recuperar senha, e obtemos o email com
            a url necessária para concluir a operação.
        Verificamos:
            - o email enviado contém um link para recupear senha.
            - a pagina de recuparar senha com token seja a correta.
        """

        # with
        reset_pwd_url = url_for('admin.reset')
        credentials = {
            'email': '*****@*****.**',
            'password': '******'
        }

        # when
        create_user(credentials['email'], credentials['password'], True)
        with mail.record_messages() as outbox:
            response = self.client.post(
                reset_pwd_url,
                data={'email': credentials['email']},
                follow_redirects=True)
            # then
            self.assertEqual(1, len(outbox))
            email_msg = outbox[0]
            # recupero os links do email
            links_found = reset_pwd_url_pattern.findall(email_msg.html)
            # tem pelo menos 1 link, e tem só um link para o reset/password com token
            self.assertGreaterEqual(1, len(links_found))
            resert_url_with_token = [url for url in links_found if reset_pwd_url in url]
            self.assertEqual(1, len(resert_url_with_token))
            resert_url_with_token = resert_url_with_token[0]

        # requisição de reset passoword com token
        reset_pwd_response = self.client.get(
            resert_url_with_token,
            follow_redirects=True)
        self.assertStatus(reset_pwd_response, 200)
        self.assertTemplateUsed('admin/auth/reset_with_token.html')
        context_form = self.get_context_variable('form')
        self.assertIsInstance(context_form, forms.PasswordForm)
Example #23
0
    def test_reset_password_request_post_non_existing_user(self):
        """
            Test accessing the password reset request form via POST with a non-existing user.

            Expected result: No password reset mail is sent, but a message is displayed that it has.
        """
        email = '*****@*****.**'

        with mail.record_messages() as outgoing:
            response = self.client.post('/user/reset-password', follow_redirects=True, data=dict(
                email=email
            ))
            data = response.get_data(as_text=True)

            self.assertEqual(0, len(outgoing))

            self.assertIn('<h1>Log In</h1>', data)
            self.assertIn('An email has been sent to the specified address.', data)
Example #24
0
    def test_user_profile_post_only_name(self):
        """
            Test posting to the user profile page with only the name changed.

            Expected result: The form is shown with the new data. The user's name is changed, everything else is not.
        """
        email = '*****@*****.**'
        name = 'John Doe'
        password = '******'
        user = User(email, name)
        user.set_password(password)
        db.session.add(user)
        db.session.commit()

        user_id = user.id

        self.client.post('/user/login', follow_redirects=True, data=dict(
            email=email,
            password=password
        ))

        new_name = 'Jane Doe'
        with mail.record_messages() as outgoing:
            response = self.client.post('/user/profile', follow_redirects=True, data=dict(
                name=new_name,
                email=email
            ))
            data = response.get_data(as_text=True)

            self.assertEqual(0, len(outgoing))

            self.assertIn('User Profile', data)
            self.assertIn(f'value="{new_name}"', data)
            self.assertIn(f'value="{email}"', data)
            self.assertIn('Your changes have been saved.', data)
            self.assertNotIn('An email has been sent to the new address', data)

            user = User.load_from_id(user_id)
            self.assertEqual(new_name, user.name)
            self.assertEqual(email, user.get_email())
            self.assertTrue(user.check_password(password))
Example #25
0
    def test_reset_password_of_valid_user_email_sent(self):
        """
        Com:
            um novo usuário (com email confirmado)
        Quando:
            solicitar a recuperação de senha
        Verificamos:
            Que a mensagem no email enviado contém o
            link para continuar a operação.
        """

        # with
        reset_pwd_url = url_for('admin.reset')
        credentials = {
            'email': '*****@*****.**',
            'password': '******'
        }
        expected_email = {
            'subject': 'Instruções para recuperar sua senha',
            'recipients': [credentials['email'], ],
            'body_has_link': u'<a href="http://localhost%s' % reset_pwd_url
        }

        # when
        create_user(credentials['email'], credentials['password'], True)
        with mail.record_messages() as outbox:
            response = self.client.post(
                reset_pwd_url,
                data={'email': credentials['email']},
                follow_redirects=True)
            # then
            self.assertStatus(response, 200)

            self.assertEqual(1, len(outbox))
            email_msg = outbox[0]
            self.assertEqual(expected_email['subject'], email_msg.subject)
            self.assertEqual(expected_email['recipients'], email_msg.recipients)
            self.assertIn(expected_email['body_has_link'], email_msg.html.decode('utf-8'))
Example #26
0
    def test_create_user_from_admin_page_creates_a_new_user(self):
        """
        Com:
            - usuario administrador (com email confirmado)
        Quando:
            1. acessamos /admin e cadastramos um novo usuário
            2. acesssamos o link enviado por email
        Verificamos:
            - o usuário é criado.
            - o usuário administrador é notificodo do sucesso da operação.
            - o novo usuário não tem email confirmado.
            - o novo usuário é notificado por email para confirmar email.
        """

        # with
        admin_user = {
            'email': '*****@*****.**',
            'password': '******',
        }
        create_user(admin_user['email'], admin_user['password'], True)
        new_user = {
            'email': '*****@*****.**',
            'password': '******'
        }
        login_url = url_for('admin.login_view')
        create_user_url = '/admin/user/new/'
        expected_msgs = [
            u'Enviamos o email de confirmação para: %s' % new_user['email'],
            u'Registro criado com sucesso.',
        ]
        # when
        with mail.record_messages() as outbox:

            with self.client as client:
                # login do usuario admin
                login_response = client.post(
                    login_url,
                    data=admin_user,
                    follow_redirects=True)
                self.assertStatus(login_response, 200)
                self.assertTemplateUsed('admin/index.html')
                self.assertTrue(current_user.is_authenticated)
                # requisição da ação para enviar email de confirmação
                create_user_response = client.post(
                    create_user_url,
                    data={'email': new_user['email']},
                    follow_redirects=True)
                # then
                self.assertStatus(create_user_response, 200)
                self.assertTemplateUsed('admin/model/list.html')
                for msg in expected_msgs:
                    self.assertIn(msg, action_response.data.decode('utf-8'))
                # temos um email
            self.assertEqual(1, len(outbox))
            email_msg = outbox[0]
            # pegamos o link com token
            links_found = email_confirm_url_pattern.findall(email_msg.html)
            # tem pelo menos 1 link, e tem só um link para o reset/password com token
            self.assertGreaterEqual(1, len(links_found))
            email_confirmation_url_with_token = [url for url in links_found if '/admin/confirm/' in url]
            # temos a url com o token
            self.assertEqual(1, len(email_confirmation_url_with_token))
            email_confirmation_url_with_token = email_confirmation_url_with_token[0]
            self.assertIsNotNone(email_confirmation_url_with_token)
            self.assertFalse(email_confirmation_url_with_token == '')
        # acessamos o link do email
        user = get_user_by_email(new_user['email'])
        confirmation_response = self.client.get(email_confirmation_url_with_token, follow_redirects=True)
        self.assertStatus(confirmation_response, 200)
        self.assertTemplateUsed('admin/index.html')
        # confirmação com sucesso
        self.assertIn(expected_msg, confirmation_response.data.decode('utf-8'))
        # confirmamos alteração do usuário
        self.assertTrue(user.email_confirmed)
Example #27
0
def mail_outbox():
    with mail.record_messages() as outbox:
        yield outbox
Example #28
0
 def test_email(self):
     with mail.record_messages() as outbox:
         self.app.post('/wt_feed',
                       data=json.dumps(self.data),
                       content_type='application/json')
     self.assertEqual(len(outbox), 1)
Example #29
0
def mailbox(app):
    return _mail.record_messages()
Example #30
0
def test_fix_registration_email(client, user_info, user2_info, test_config):
    """Registered users can fix errors in their email addresses."""
    rv = client.get(url_for("auth.register"))
    data = dict(
        csrf_token=csrf_token(rv.data),
        username=user_info["username"],
        password=user_info["password"],
        confirm=user_info["password"],
        email_required=user_info["email"],
        invitecode="",
        accept_tos=True,
        captcha="xyzzy",
    )

    # Register and save the link from the first email.
    with mail.record_messages() as outbox:
        rv = client.post(url_for("auth.register"),
                         data=data,
                         follow_redirects=True)
        assert b"spam" in rv.data  # Telling user to go check it.
        message = outbox.pop()
        soup = BeautifulSoup(message.html, "html.parser")
        first_token = soup.a["href"].split("/")[-1]

    # Find the resend link.
    soup = BeautifulSoup(rv.data, "html.parser", from_encoding="utf-8")
    links = soup.find_all(
        lambda tag: tag.name == "a" and tag.string == "Resend")
    url = links[0]["href"]

    # Request the resend form and verify the registered email is shown.
    rv = client.get(url)
    assert b"Resend account confirmation instructions" in rv.data
    soup = BeautifulSoup(rv.data, "html.parser", from_encoding="utf-8")
    tag = soup.find_all(lambda tag: tag.get("name") == "email")[0]
    assert tag["value"] == user_info["email"]

    # Ask for emails to be sent to a different address.
    with mail.record_messages() as outbox:
        rv = client.post(
            url,
            data=dict(csrf_token=csrf_token(rv.data),
                      email=user2_info["email"]),
            follow_redirects=True,
        )
        assert b"spam" in rv.data  # Telling user to go check it.
        message = outbox.pop()
        assert message.recipients == [user2_info["email"]]
        soup = BeautifulSoup(message.html, "html.parser")
        token = soup.a["href"].split("/")[-1]

        # Use the confirmation link from the email.
        rv = client.get(url_for("auth.login_with_token", token=token),
                        follow_redirects=True)
        assert b"Log out" in rv.data

    # Verify that the user's confirmed email is the second one.
    rv = client.get(url_for("user.edit_account"))
    soup = BeautifulSoup(rv.data, "html.parser", from_encoding="utf-8")
    tag = soup.find_all(lambda tag: tag.get("name") == "email_required")[0]
    assert tag["value"] == user2_info["email"]

    log_out_current_user(client)

    # Try to use the first token and verify that it is no longer valid.
    rv = client.get(url_for("auth.login_with_token", token=first_token),
                    follow_redirects=True)
    assert b"The link you used is invalid or has expired" in rv.data
    assert b"Log out" not in rv.data

    # Try to use the resend form and verify that it no longer works.
    rv = client.get(url, follow_redirects=True)
    assert b"The link you used is invalid or has expired" in rv.data
    assert b"Log out" not in rv.data
Example #31
0
    def test_confirmation_email_send_email_with_token(self):
        """
        Com:
            - o usuário 'administrador' logado (email confirmado)
            - um novo usuário, com email NÃO confirmado
        Quando:
            1. enviamos emails de confirmação (utilizando a ação do admin/user)
            2.
        Verificamos:
            - que o email enviado contem um link para confirmar email.
            - o email é enviado para o destinatario certo.
            - após a operação, a página é a correta.
            - as notifiação para usuário deve ser mostrada na página.
        """

        # with
        admin_user = {
            'email': '*****@*****.**',
            'password': '******',
        }
        create_user(admin_user['email'], admin_user['password'], True)
        normal_user = {
            'email': '*****@*****.**',
            'password': '******'
        }
        create_user(normal_user['email'], normal_user['password'], False)
        login_url = url_for('admin.login_view')
        action_payload = {
            'action': 'confirm_email',
            'rowid': get_user_by_email(normal_user['email']).id,
            'url': '/admin/user/'
        }
        expected_email_sent_notifications = [
            u"Enviamos o email de confirmação para: %s" % normal_user['email'],
            u"1 usuários foram notificados com sucesso!",
        ]
        expected_email = {
            'subject': u'Confirmação de email',
            'recipients': [normal_user['email'], ],
        }
        # when
        # login do usuario admin
        login_response = self.client.post(
            login_url,
            data=admin_user,
            follow_redirects=True)
        self.assertStatus(login_response, 200)
        self.assertTemplateUsed('admin/index.html')
        # requisição da ação para enviar email de confirmação
        with mail.record_messages() as outbox:
            action_response = self.client.post(
                '/admin/user/action/',
                data=action_payload,
                follow_redirects=True)
            # then
            self.assertStatus(action_response, 200)
            self.assertTemplateUsed('admin/model/list.html')
            for msg in expected_email_sent_notifications:
                self.assertIn(msg, action_response.data.decode('utf-8'))

            # temos um email
            self.assertEqual(1, len(outbox))
            email_msg = outbox[0]
            # email enviado ao destinatario certo, com assunto certo
            self.assertEqual(expected_email['recipients'], email_msg.recipients)
            self.assertEqual(expected_email['subject'], email_msg.subject.decode('utf-8'))
            # pegamos o link com token
            links_found = email_confirm_url_pattern.findall(email_msg.html)
            # tem pelo menos 1 link, e tem só um link para o reset/password com token
            self.assertGreaterEqual(1, len(links_found))
            email_confirmation_url_with_token = [url for url in links_found if '/admin/confirm/' in url]
            # temos a url com o token
            self.assertEqual(1, len(email_confirmation_url_with_token))
            email_confirmation_url_with_token = email_confirmation_url_with_token[0]
            self.assertIsNotNone(email_confirmation_url_with_token)
            self.assertFalse(email_confirmation_url_with_token == '')
Example #32
0
    def test_open_confirm_url_with_token_sent_via_email_open_the_correct_page(self):
        """
        Com:
            - o usuário 'administrador' logado (email confirmado)
            - um novo usuário, com email NÃO confirmado
        Quando:
            1. enviamos emails de confirmação (utilizando a ação do admin/user)
            2. acesssamos o link enviado por email
        Verificamos:
            - que o email enviado contem um link para confirmar email.
            - após acessar o link, a página é a correta.
            - após acessar o link, a págian mostra a notificação de operação ok.
            - após acessar o link, o usuário tem seu email confirmado.
        """

        # with
        admin_user = {
            'email': '*****@*****.**',
            'password': '******',
        }
        create_user(admin_user['email'], admin_user['password'], True)
        normal_user = {
            'email': '*****@*****.**',
            'password': '******'
        }
        create_user(normal_user['email'], normal_user['password'], False)
        login_url = url_for('admin.login_view')
        action_payload = {
            'action': 'confirm_email',
            'rowid': get_user_by_email(normal_user['email']).id,
            'url': '/admin/user/'
        }
        expected_msg = u'Email: %s confirmado com sucesso!' % normal_user['email']
        # when
        # login do usuario admin
        login_response = self.client.post(
            login_url,
            data=admin_user,
            follow_redirects=True)
        self.assertStatus(login_response, 200)
        # requisição da ação para enviar email de confirmação
        with mail.record_messages() as outbox:
            action_response = self.client.post(
                '/admin/user/action/',
                data=action_payload,
                follow_redirects=True)
            # then
            self.assertStatus(action_response, 200)
            # temos um email
            self.assertEqual(1, len(outbox))
            email_msg = outbox[0]
            # pegamos o link com token
            links_found = email_confirm_url_pattern.findall(email_msg.html)
            # tem pelo menos 1 link, e tem só um link para o reset/password com token
            self.assertGreaterEqual(1, len(links_found))
            email_confirmation_url_with_token = [url for url in links_found if '/admin/confirm/' in url]
            # temos a url com o token
            self.assertEqual(1, len(email_confirmation_url_with_token))
            email_confirmation_url_with_token = email_confirmation_url_with_token[0]
        # acessamos o link do email
        confirmation_response = self.client.get(email_confirmation_url_with_token, follow_redirects=True)
        self.assertStatus(confirmation_response, 200)
        self.assertTemplateUsed('admin/index.html')
        # confirmação com sucesso
        self.assertIn(expected_msg, confirmation_response.data.decode('utf-8'))
        # confirmamos alteração do usuário
        user = get_user_by_email(normal_user['email'])
        self.assertTrue(user.email_confirmed)
Example #33
0
def test_change_password_recovery_email(client, user_info):
    """The user can change their password recovery email."""
    register_user(client, user_info)
    new_email = '*****@*****.**'
    assert new_email != user_info['email']

    rv = client.get(url_for('user.edit_account'))
    data = dict(csrf_token=csrf_token(rv.data),
                oldpassword=user_info['password'],
                password='',
                confirm='')
    if email_validation_is_required():
        data['email_required'] = new_email
    else:
        data['email_optional'] = new_email

    with mail.record_messages() as outbox:
        rv = client.post(url_for('do.edit_account'),
                         data=data,
                         follow_redirects=True)
        log_out_current_user(client)

        if email_validation_is_required():
            message = outbox.pop()

            # Make sure that password recovery emails go to the former address
            # if the new one has not yet been confirmed.
            rv = client.get(url_for('user.password_recovery'))
            rv = client.post(url_for('user.password_recovery'),
                             data=dict(csrf_token=csrf_token(rv.data),
                                       email=new_email,
                                       captcha='xyzzy'))
            assert len(outbox) == 0

            rv = client.get(url_for('user.password_recovery'))
            rv = client.post(url_for('user.password_recovery'),
                             data=dict(csrf_token=csrf_token(rv.data),
                                       email=user_info['email'],
                                       captcha='xyzzy'))
            assert outbox.pop().send_to == {user_info['email']}

            # Now click the confirm link.
            assert message.send_to == {new_email}
            soup = BeautifulSoup(message.html, 'html.parser')
            token = soup.a['href'].split('/')[-1]
            rv = client.get(url_for('user.confirm_email_change', token=token),
                            follow_redirects=True)
        else:
            assert len(outbox) == 0

    # Verify password recovery email goes to the right place.
    with mail.record_messages() as outbox:
        rv = client.get(url_for('user.password_recovery'))
        rv = client.post(url_for('user.password_recovery'),
                         data=dict(csrf_token=csrf_token(rv.data),
                                   email=user_info['email'],
                                   captcha='xyzzy'))
        assert len(outbox) == 0
        rv = client.get(url_for('user.password_recovery'))
        rv = client.post(url_for('user.password_recovery'),
                         data=dict(csrf_token=csrf_token(rv.data),
                                   email=new_email,
                                   captcha='xyzzy'))
        assert outbox.pop().send_to == {new_email}
Example #34
0
def test_send_mail(client, init_database):
    """
    GIVEN a Flask application, admin, study
    WHEN study is today
    THEN check emails are sent with correct content
    """
    with client.application.test_request_context():

        admin = create_admin(client, username="******", password="******")
        participant_1 = create_participant(client, username="******")
        participant_2 = create_participant(client, username="******")
        participant_3 = create_participant(client, username="******")
        participant_4 = create_participant(client, username="******")
        participant_5 = create_participant(client, username="******")

        user_group = create_user_group(
            client, creator=admin, participants=[participant_1, participant_2])
        user_group_2 = create_user_group(
            client,
            creator=admin,
            participants=[participant_3, participant_4, participant_5],
        )
        study = create_study(
            client,
            start_date=date.today(),
            creator=admin,
            user_group=user_group,
        )
        study2 = create_study(client,
                              creator=admin,
                              start_date=date.today() + timedelta(days=3))
        study_3 = create_study(
            client,
            creator=admin,
            start_date=date.today(),
            user_group=user_group_2,
        )

        studies = Study.query.filter(
            func.DATE(Study.start_date) == date.today()).all()
        assert study2 not in studies

        letters = string.ascii_letters
        strength = 8
        for study in studies:
            if study.mail_sent == False:
                user_group = UserGroup.query.filter_by(
                    id=study.user_group_id).first()
                if user_group:
                    with mail.record_messages() as outbox:

                        for user in user_group.users:
                            password = "".join(
                                random.choice(letters)
                                for i in range(strength))
                            user.set_password(password)
                            body = render_template(
                                "email.html",
                                study=study,
                                username=user.username,
                                password=password,
                            )

                            msg = Message(
                                "You Have Been Invited To A Study!",
                                recipients=[user.email],
                                sender=current_app.config["MAIL_USERNAME"],
                            )
                            msg.html = body

                            assert password in msg.html
                            assert user.username in msg.html
                            assert msg.recipients[0] == user.email

                            mail.send(msg)
                            user.email = None
                            assert user.email is None
                        assert len(outbox) == len(user_group.users)

                try:
                    study.mail_sent = True
                    db.session.commit()
                except:
                    db.session.rollback()
    def test_auth_registeration_login_logout(self):
        # Check that Sign In and Register Links are present in the main page
        response = self.client.get(url_for('main.index'))
        data = response.get_data(as_text=True)
        assert_true("Sign In" in data)
        assert_true("Register" in data)

        # Check that the Registration page has the form
        response = self.client.get(url_for("auth.register"))
        data = response.get_data(as_text=True)
        assert_true('Register' in data)
        assert_true('Email' in data)
        assert_true('Username' in data)
        assert_true('Password' in data)
        assert_true('Confirm Password' in data)

        # Register - should redirect to main page
        with mail.record_messages() as outbox:
            response = self.client.post(url_for('auth.register'), data={
                "email": "*****@*****.**",
                "username": "******",
                "password": "******",
                "password2": "cat"
                }, follow_redirects=True)
            data = response.get_data(as_text=True)
            assert_true("A confirmation email has been sent to your email address."
                in data)
            assert_true(len(outbox) == 1) # Mail was sent
            assert_true('Confirm Your Account' in outbox[0].subject)

        # Login page contains the form
        response = self.client.get(url_for('auth.login'))
        data = response.get_data(as_text=True)
        assert_true('Login' in data)
        assert_true('Email' in data)
        assert_true('Password' in data)
        assert_true('Keep me logged in' in data)
        assert_true('Log In' in data)

        # Incorrect logins return error
        response = self.login('*****@*****.**', 'badpassword')
        assert_true('Invalid email or password' in response.get_data(as_text=True))

        response = self.login('*****@*****.**', 'badpassword')
        assert_true('Invalid email or password' in response.get_data(as_text=True))

        # Correct login
        response = self.login('*****@*****.**', 'cat')
        data = response.get_data(as_text=True)
        assert_true('You were logged in' in data)
        assert_true(re.compile("Hello,\s+sam").search(data))
        # Should prompt the user to confirm their email address
        assert_true("You have not confirmed your account yet" in data)

        # Resend confirmation
        with mail.record_messages() as outbox:
            response = self.client.get(url_for('auth.resend_confirmation'), follow_redirects=True)
            data = response.get_data(as_text=True)
            assert_true('A new confirmation email has been sent to you' in data)
            assert_true(len(outbox) == 1)

        # Confirm account
        user = User.query.filter_by(email='*****@*****.**').first()

        # Attempt to use another user's token rejected
        token = User.query.filter_by(email='*****@*****.**').first().generate_confirmation_token()
        response = self.client.get(url_for('auth.confirm', token=token),
            follow_redirects=True)
        data = response.get_data(as_text=True)
        assert_true(re.compile('The confirmation link is invalid or has expired.').search(data))

        # Incorrect token rejected
        token = user.generate_confirmation_token()
        response = self.client.get(url_for('auth.confirm', token=' ' + token),
            follow_redirects=True)
        data = response.get_data(as_text=True)
        assert_true(re.compile('The confirmation link is invalid or has expired.').search(data))

        # Correct token accepted
        response = self.client.get(url_for('auth.confirm', token=token),
            follow_redirects=True)
        data = response.get_data(as_text=True)
        assert_true("You have confirmed your account." in data)

        # Main page should have Sign Out link, and welcome us by username
        response = self.client.get(url_for('main.index'))
        data = response.get_data(as_text=True)
        assert_true(re.compile("Hello,\s+sam").search(data))
        assert_true("Sign Out" in data)

        # Check logout - should redirect to main page
        response = self.client.get(url_for('auth.logout'), follow_redirects=False)
        assert_true(response.status_code == 302)

        # Should have Sign In link on main page now
        response = self.client.get(url_for('main.index'))
        assert_true("Sign In" in response.get_data(as_text=True))

        # Delete test user
        User.query.filter_by(email='*****@*****.**').delete()
Example #36
0
def send_message(to_address_list=None, **kwargs):
    """Send an email with the parameters as:
        to_address_list=[list of tuples (recipient name,recipient address)]=None
        
        If the to_address_list is not provided, mail will be sent to the admin
        
        -- all templates must use 'context' as their only context variable
        **kwargs:
            context = {a dictionary like object with data for rendering all emails} = {}
            body = <text for body of email> = None
            body_text_is_html = <True | False> = False
            text_template=<template to render as plain text message> = None
            html_template=<template to render as html message> = None
            subject=<subject text (will be rendered with the current context>)>= a default subject
            subject_prefix=<some text to prepend to the subject: = ''
            from_address=<from address> = app.config['MAIL_DEFAULT_ADDR']
            from_sender=<name of sender> = app.config['MAIL_DEFAULT_SENDER']
            reply_to_address=<replyto address> = from_address
            reply_to_name=<name of reply to account> = from_sender
            
        On completion returns a tuple of:
            success [True or False]
            message "some message"
    """
    #import pdb;pdb.set_trace()

    app_config = get_app_config(
    )  #update the settings. this also recreates the mail var in app with new settings

    context = kwargs.get('context', {})
    body = kwargs.get('body', None)
    body_is_html = kwargs.get('body_is_html', None)
    text_template = kwargs.get('text_template', None)
    html_template = kwargs.get('html_template', None)
    subject_prefix = kwargs.get('subject_prefix', '')

    try:
        admin_addr = app_config['MAIL_DEFAULT_ADDR']
        admin_name = app_config['MAIL_DEFAULT_SENDER']
    except KeyError as e:
        mes = "MAIL Settings not found"
        mes = printException(mes, 'error', e)
        return (False, mes)

    from_address = kwargs.get('from_address', admin_addr)
    from_sender = kwargs.get('from_sender', admin_name)
    reply_to = kwargs.get('reply_to', from_address)

    subject = subject_prefix + ' ' + kwargs.get(
        'subject', 'A message from {}'.format(from_sender))

    if not text_template and not html_template and not body:
        mes = "No message body was specified"
        printException(mes, "error")
        return (False, mes)

    if not to_address_list or len(to_address_list) == 0:
        #no valid address, so send it to the admin
        to_address_list = [
            (admin_name, admin_addr),
        ]

    with mail.record_messages() as outbox:
        sent_cnt = 0
        err_cnt = 0
        err_list = []
        result = True
        for who in to_address_list:
            #import pdb;pdb.set_trace()
            name = ""
            address = ""
            body_err_head = ""
            if type(who) is tuple:
                if len(who) == 1:
                    # extend whp
                    who = who[0] + (who[0], )
                name = who[0]
                address = who[1]
            else:
                address = who  #assume its a str
                name = who

            if not looksLikeEmailAddress(address) and looksLikeEmailAddress(
                    name):
                # swap values
                temp = address
                address = name
                name = temp
            if not looksLikeEmailAddress(address):
                # still not a good address...
                address = admin_addr
                name = admin_name
                if not body:
                    body = ""

                body_err_head = "Bad Addres: {}\r\r".format(who, )

            subject = render_template_string(subject.strip(), context=context)
            #Start a message
            msg = Message(subject,
                          sender=(from_sender, from_address),
                          recipients=[(name, address)])

            #Get the text body verson
            if body:
                if body_is_html:
                    msg.html = render_template_string("{}{}".format(
                        body_err_head,
                        body,
                    ),
                                                      context=context)
                else:
                    msg.body = render_template_string("{}{}".format(
                        body_err_head,
                        body,
                    ),
                                                      context=context)
            if html_template:
                msg.html = render_template(html_template, context=context)
            if text_template:
                msg.body = render_template(text_template, context=context)

            msg.reply_to = reply_to

            try:
                mail.send(msg)
                sent_cnt += 1
            except Exception as e:
                mes = "Error Sending email"
                printException(mes, "error", e)
                err_cnt += 1
                err_list.append("Error sending message to {} err: {}".format(
                    who, str(e)))

        # End Loop
        if sent_cnt == 0:
            mes = "No messages were sent."
            result = False
        else:
            mes = "{} messages sent successfully.".format(sent_cnt)
        if err_cnt > 0:
            mes = mes + " {} messages had errors.\r\r{}".format(
                err_cnt, err_list)

        return (result, mes)
Example #37
0
 def test_email(self):
     with mail.record_messages() as outbox:
         self.app.post('/wt_feed',
                       data=json.dumps(self.data),
                       content_type='application/json')
     self.assertEqual(len(outbox), 1)
Example #38
0
def test_silent_reset_failures(client):
    with mail.record_messages() as outbox:
        client.post('/reset',
                    data=dict(email='*****@*****.**'),
                    follow_redirects=True)
        assert len(outbox) == 0
Example #39
0
def test_change_password_recovery_email(client, user_info, test_config):
    """The user can change their password recovery email."""
    register_user(client, user_info)
    new_email = "*****@*****.**"
    assert new_email != user_info["email"]

    rv = client.get(url_for("user.edit_account"))
    data = dict(
        csrf_token=csrf_token(rv.data),
        oldpassword=user_info["password"],
        password="",
        confirm="",
    )
    if email_validation_is_required():
        data["email_required"] = new_email
    else:
        data["email_optional"] = new_email

    with mail.record_messages() as outbox:
        rv = client.post(url_for("do.edit_account"),
                         data=data,
                         follow_redirects=True)
        log_out_current_user(client)

        if email_validation_is_required():
            message = outbox.pop()

            # Make sure that password recovery emails go to the former address
            # if the new one has not yet been confirmed.
            rv = client.get(url_for("user.password_recovery"))
            rv = client.post(
                url_for("user.password_recovery"),
                data=dict(csrf_token=csrf_token(rv.data),
                          email=new_email,
                          captcha="xyzzy"),
            )
            assert len(outbox) == 0

            rv = client.get(url_for("user.password_recovery"))
            rv = client.post(
                url_for("user.password_recovery"),
                data=dict(
                    csrf_token=csrf_token(rv.data),
                    email=user_info["email"],
                    captcha="xyzzy",
                ),
            )
            assert outbox.pop().send_to == {user_info["email"]}

            # Now click the confirm link.
            assert message.send_to == {new_email}
            soup = BeautifulSoup(message.html, "html.parser")
            token = soup.a["href"].split("/")[-1]
            rv = client.get(url_for("user.confirm_email_change", token=token),
                            follow_redirects=True)
        else:
            assert len(outbox) == 0

    # Verify password recovery email goes to the right place.
    with mail.record_messages() as outbox:
        rv = client.get(url_for("user.password_recovery"))
        rv = client.post(
            url_for("user.password_recovery"),
            data=dict(
                csrf_token=csrf_token(rv.data),
                email=user_info["email"],
                captcha="xyzzy",
            ),
        )
        assert len(outbox) == 0
        rv = client.get(url_for("user.password_recovery"))
        rv = client.post(
            url_for("user.password_recovery"),
            data=dict(csrf_token=csrf_token(rv.data),
                      email=new_email,
                      captcha="xyzzy"),
        )
        assert outbox.pop().send_to == {new_email}