def index(): db = open_db() posts = db.execute( 'SELECT p.id, title, body, created, author_id, username' ' FROM post p JOIN user u ON author_id = u.id' ' ORDER BY created DESC').fetchall() # query and fetch all return render_template('blog/index.html', posts=posts)
def login(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] db = open_db() error = None # validate the user whether it exists or correct user = db.execute( 'SELECT * FROM user WHERE username = ?', (username, )).fetchone() # get the user record from database if user is None: # if username is `None`, just throw an error error = 'Incorrect username.' elif not check_password_hash( user['password'], password ): # validate by hashing new password and comparing with db version error = 'Incorrect password.' if error is None: # username and password have been validated session.clear() # clear the current session cookies session['user_id'] = user[ 'id'] # add user to session dict - `id` is the unique identifier obtained from the database return redirect( url_for('index') ) # redirect the user to `index page`, after having logged in flash(error) # shows the user the error and store the error return render_template( 'auth/login.html') # otherwise return the login page again
def register(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] db = open_db() error = None if not username: # if username is `None`, just throw an error error = 'Username is required.' elif not password: # if password is `None`, also throw an error error = 'Password is required.' elif db.execute( 'SELECT id FROM user WHERE username = ?', (username, ) ).fetchone( ) is not None: # use initialized db to check if user already exists error = 'User {} is already registered.'.format(username) if error is None: # if there are no errors with username and password input, then hash and store the data in the database db.execute('INSERT INTO user (username, password) VALUES (?, ?)', (username, generate_password_hash(password))) db.commit( ) # make sure the db changes are concreted i.e. save the changes return redirect( url_for('auth.login') ) # redirect the user to `login` for them to now login flash( error ) # shows the user the error and also stores the error for subsequent usage in the template return render_template('auth/register.html')
def load_logged_in_user(): user_id = session.get( 'user_id') # extract the user_id from the current session cookie if user_id is None: # if there current session cookie is not valid [due to not even having a user_id] g.user = None else: # use the user_id to select the user details from the database db = open_db() g.user = db.execute('SELECT * FROM user WHERE id = ?', (user_id, )).fetchone()
def test_close_db(app): # to validate the app can be `opened` and `closed` () with app.app_context( ): # this gives access to the current_app [thus access to the attributes - within this block of code] db = open_db( ) # extracts and assigns the `open_db()` obtained from the app_context() assert db is open_db( ) # checks to make sure we were able to really get the same db object from app_context() with pytest.raises( sqlite3.ProgrammingError ) as e: # test if db.execute(`SELECT 1`) raises expected `sqlite3.ProgrammingError` or exception `e` [i.e. string `close`] db.execute( 'SELECT 1' ) # `SELECT 1` always returns a `1` if there are rows in the db [] - this also implicitly tests open_db() # the returned rows is unimportant, we just want to test if the code returns a `close` value assert 'close' in str( e.value ) # check that if string `close` exists to show that close_db() is working well
def app(): # for the `testing of the application itself: awokogbon` db_tempfile, db_tempfilepath = tempfile.mkstemp( ) # create temporary file/path needed for the tests database app = create_app({ # create an instance of the application 'TESTING': True, # let's flask know that the application is in testing mode 'DATABASE': db_tempfilepath, # points to the database path where `_data_sql` would be stored when used }) with app.app_context( ): # initialize the app context e.g. app `in testing mode` to use test database `_data_sql` init_db( ) # gets the app to use initiliaze the sqlite3 connections [which also gets `schema.sql` initialized BUT not used] open_db().executescript( _data_sql ) # gets the app to open/run `_data_sql`, and initialize it as an `sqlite3 db` at `db_tempfilepath` [i.e. `schema.sql` is not used] yield app # `an alternative to using return` i.e. a more efficient `return` [what works like a generator] # close the temporary file and unlink the temporary file path os.close(db_tempfile) os.unlink(db_tempfilepath)
def test_create(client, authentication, app): authentication.login() # tests to check if `/create` is accesssible assert client.get('/create').status_code == 200 # add new data via the `/create` endpoint client.post('/create', data={'title': 'created', 'body': ''}) # check if the data was indeed added # assuming that we initially had only one entry in the table `post` with app.app_context(): db = open_db() count = db.execute('SELECT COUNT(id) FROM post').fetchone()[0] assert count == 2
def test_update(client, authentication, app): authentication.login() # tests to check if `/1/update` is accesssible assert client.get('/1/update').status_code == 200 # update new data via the `/1/update` endpoint client.post('/1/update', data={'title': 'updated', 'body': ''}) # check if the data was indeed added by checking the title # assuming that we initially had only one entry in the table `post` # app.app_context is being used to simulate that the application code is the one implementing the test with app.app_context(): db = open_db() post = db.execute('SELECT * FROM post WHERE id = 1').fetchone() assert post['title'] == 'updated'
def test_logged_in_user(app, client, authentication, path): # change the post owner within the test database with app.app_context(): db = open_db() db.execute('UPDATE post SET author_id = 2 WHERE id = 1') db.commit() # ensure current user is logged in and then try to access the `/1/update` and `/1/delete` urls authentication.login() # test the routes assert client.post(path).status_code == 403 # forbidden # test visibility to edit link response = client.get('/') assert b'href="/1/update"' not in response.data
def test_delete(client, authentication, app): authentication.login() # delete the post client.post('/1/delete') # response = client.post('/1/delete') # check to see if there was indeed redirection to `/` i.e. local host # assert response.headers.get('Location') == 'http://localhost/' # check if the data was indeed removed by checking the title # assuming that we initially had only one entry in the table `post` # app.app_context is being used to simulate that the application code is the one implementing the test with app.app_context(): db = open_db() post = db.execute('SELECT * FROM post WHERE id = 1').fetchone() assert post is None
def update(id): post = get_post(id) if request.method == 'POST': title = request.form['title'] body = request.form['body'] error = None if not title: # the blog post needs to have a title, assign an error value error = 'Title is required.' if error is not None: flash(error) else: db = open_db() db.execute('UPDATE post SET title=?, body=? WHERE id = ?', (title, body, id)) db.commit() # save the changes return redirect(url_for('blog.index')) # redirect back to index page after the update return render_template('blog/update.html', post=post) # redirect back to update page if it is a `GET` request or there are issues with `initial update`
def create(): if request.method == 'POST': title = request.form['title'] body = request.form['body'] error = None if not title: # the blog post needs to have a title, assign an error value error = 'Title is required.' if error is not None: flash(error) else: db = open_db() db.execute( 'INSERT INTO post (title, body, author_id) VALUES (?, ?, ?)', (title, body, g.user['id']) ) db.commit() # save the data in the database return redirect(url_for('blog.index')) # redirect back to index page after creating the new blog post return render_template('blog/create.html') # redirect back to the create page if it is a `GET` request or there are issues with `title`
def get_post(id, check_author=True): # connect to the database to retrieve the post [using the provided `id` parameter] db = open_db() post = db.execute( 'SELECT p.id, title, body, created, author_id, username' ' FROM post p JOIN user u ON p.author_id = u.id' ' WHERE p.id = ?', (id,) ).fetchone() # check if the post exists i.e. in case the db returned a None value if post is None: abort(404, "Post id {0} doesn't exist").format(id) # if the post exists, then check if the author does NOT match the `id` of current user trying to delete the post if check_author and post['author_id'] != g.user['id']: abort(403) # this means it is now okay to delete the post return post
def test_register( client, app): # the `client` and `app` are feed to this via `conftest.py` # check if the '/auth/register' url is a accessible assert client.get('/auth/register').status_code == 200 # use the validated url `/auth/register` to register a sample user `a`, using a post request response = client.post('/auth/register', data={ 'username': '******', 'password': '******' }) # a successful registeration would re-direct the url to `url_for('auth.login')`, hence the `Location` check assert 'http://localhost/auth/login' == response.headers['Location'] # check the database to ensure the data was really inserted for username `a` with app.app_context(): db = open_db() assert db.execute( "select * from user where username = '******'").fetchone() is not None
def delete(id): get_post(id) db = open_db() db.execute('DELETE FROM post WHERE id = ?', (id,)) db.commit() # save the data in the database return render_template('blog/index.html') # redirect back to the index page