vendredi 22 juin 2018

Pytest Flask Request Referrer

I'm currently testing my Flask application using Pytest and ran into a problem with a POST request and a redirect. Let me explain a bit more.

A user wants to register for our new site, but must confirm they have an account with a different site. Once they confirm the credentials of the other account, they are taken to the register page. They can only hit the register page if coming from the confirmation page else they are redirected back to the home page.

I want to test this functionality and can successfully make a POST request to the confirmation page. If I don't specify follow_redirects=True and print the response data, I get the following HTML:



  Redirecting...

  

Redirecting...


  
You should be redirected automatically to target URL: /register?emp_id=1. If not click the link.

Great! Exactly what I'm looking for! I want to be redirected to the registration page.

Now when I do specify follow_redirects=True and print out the response data, I expected the register page HTML to return. The response data instead returns the home page HTML.

I further investigated where the problem was. As I mentioned before, the only way you can hit the registration page is from the confirmation page. I took a look at the request.referrer attribute in the view during the test and it will return None. I attempted setting the Referrer header content in the test's POST request, but to no luck.

Here is the code I'm working with:

views.py

@app.route('/confirm', methods=['GET', 'POST'])
def confirm_bpm():
    if current_user.is_authenticated:
        return redirect(url_for('index'))
    form = BPMLoginForm()
    if form.validate_on_submit():
        bpm_user = BPMUser.query\
            .filter(and_(BPMUser.group_name == form.group_name.data,
                         BPMUser.user_name == form.username.data,
                         BPMUser.password == encrypt(form.password.data)))\
            .first()
        if not bpm_user:
            flash('BPM user account does not exist!')
            url = url_for('confirm_bpm')
            return redirect(url)
        if bpm_user.user_level != 3:
            flash('Only L3 Users can register through this portal.')
            url = url_for('confirm_bpm')
            return redirect(url)
        url = url_for('register', emp_id=bpm_user.emp_id)
        return redirect(url)
    return render_template('login/confirm_bpm.html', form=form)

@app.route('/register', methods=['GET', 'POST'])
def register():
    if current_user.is_authenticated:
        return redirect(url_for('index'))
    if request.method == 'GET' and\
            request.referrer != request.url_root + 'confirm':
        return redirect(url_for('index'))

    emp_id = request.args.get('emp_id')
    emp_id_exists = User.query.filter_by(emp_id=emp_id).first()
    if emp_id_exists:
        flash('User is already registered!')
        return redirect(url_for('login'))

    form = RegistrationForm()
    if form.validate_on_submit():
        new_user = User(login_type=form.login_type.data, login=form.login.data,
                        emp_id=emp_id)
        new_user.set_password(form.password.data)
        db.session.add(new_user)
        db.session.commit()
        flash('Registration successful!')
        return redirect(url_for('login'))
    return render_template('login/register.html', form=form)

TESTS

base.py

from config import TestConfig
from app import app, db

@pytest.fixture
def client():
    """
    Initializes test requests for each individual test.  The test request
    keeps track of cookies.
    """
    app.config.from_object(TestConfig)

    client = app.test_client()
    ctx = app.app_context()
    ctx.push()

    yield client

    ctx.pop()


def confirm_bpm_login(client, group_name, username, password):
    """
    POST to /confirm
    """
    return client.post('/confirm', data=dict(
        group_name=group_name,
        username=username,
        password=password,
        submit=True
    ), follow_redirects=True)

test_auth.py

from app import db
from app.models import BPMCompany, BPMEmployee, User, BPMUser

from tests.base import client, db_data, login, confirm_bpm_login

def test_registration_page_from_confirm(client, db_data):
    """
    Test registration page by HTTP GET request not from "/confirm" url.
    Should cause redirect to index page.

    !!! FAILING !!!
    Reason: The POST to /confirm will redirect us to /register?emp_id=1,
    but it will return the index.html because in the register view,
    request.referrer does not recognized the post is coming from /confirm_bpm
    """
    bpm_user = BPMUser.query.filter_by(id=1).first()

    rv = confirm_bpm_login(client, bpm_user.group_name,
                           bpm_user.user_name, 'jsmith01')

    assert b'Register' in rv.data

The db_data parameter is a fixture in base.py that just populates the DB with the necessary data for the registration flow to work.

My goal is to test a complete registration flow without having to separate the confirmation and registration into two tests.

Aucun commentaire:

Enregistrer un commentaire