diff --git a/qa327/backend.py b/qa327/backend.py index 482aa39..ca75911 100644 --- a/qa327/backend.py +++ b/qa327/backend.py @@ -48,7 +48,7 @@ def register_user(email, name, password, password2): db.session.commit() except: return False - + return True @@ -78,11 +78,10 @@ def update_ticket(name, quantity, price, date): ticket.quantity = quantity ticket.price = price ticket.date = date - db.session.update(ticket) db.session.commit() return 1 - + # Backend functionality for ticket buying def buy_ticket(name, user, quantity): # Make sure ticket exists @@ -92,12 +91,12 @@ def buy_ticket(name, user, quantity): # Make sure enough quantity of tickets exist and user has enough balance if ticket.quantity < quantity: return 0 - elif user.balance < (ticket.price*quantity + (ticket.price*quantity*0.4)): + elif user.balance < (ticket.price * quantity + (ticket.price * quantity * 0.4)): return 0 else: # Subtracts the ticket amount plus services and tax from the buyers account - user.balance -= quantity*ticket.price + (quantity*ticket.price*0.4) + user.balance -= quantity * ticket.price + (quantity * ticket.price * 0.4) # Gets the seller's user data seller = get_user(ticket.email) @@ -106,7 +105,7 @@ def buy_ticket(name, user, quantity): if not seller.balance: seller.balance = 0 # Add the ticket sale revenue to the sellers balance - seller.balance += quantity*ticket.price + seller.balance += quantity * ticket.price # Check if there are still tickets left after the order is complete if ticket.quantity > quantity: @@ -118,5 +117,5 @@ def buy_ticket(name, user, quantity): # Commit all changes to the database db.session.commit() else: - return 0 - return 1 + return 0 + return 1 diff --git a/qa327/frontend.py b/qa327/frontend.py index 5ada6a8..49b7ffa 100644 --- a/qa327/frontend.py +++ b/qa327/frontend.py @@ -12,6 +12,7 @@ The html templates are stored in the 'templates' folder. """ + @app.route('/register', methods=['GET']) def register_get(): if 'logged_in' in session: @@ -21,7 +22,6 @@ def register_get(): @app.route('/register', methods=['POST']) def register_post(): - patternEmail = re.compile(r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)") patternPass = re.compile("^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[^A-Za-z0-9]).{6,}$") @@ -38,17 +38,17 @@ def register_post(): session['error'] = error_message return redirect('/login') elif not patternEmail.fullmatch(email): - error_message = '{} format is incorrect.'.format("Email") + error_message = '{} format is incorrect.'.format("Email") session['error'] = error_message return redirect('/login') elif not patternPass.fullmatch(password): - error_message = '{} format is incorrect.'.format("Password") + error_message = '{} format is incorrect.'.format("Password") session['error'] = error_message return redirect('/login') elif not patternName.fullmatch(name) or len(name) < 2 or len(name) > 20: - error_message = '{} format is incorrect.'.format("Name") + error_message = '{} format is incorrect.'.format("Name") session['error'] = error_message return redirect('/login') @@ -82,10 +82,10 @@ def login_get(): def login_post(): email = request.form.get('email') password = request.form.get('password') - #regex for email obtained from https://emailregex.com/ + # regex for email obtained from https://emailregex.com/ EMAIL_REGEX = re.compile(r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)") PASSWORD_REGEX = re.compile(r"^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[^A-Za-z0-9]).{6,}$") - if not EMAIL_REGEX.match(email) or not PASSWORD_REGEX.match(password): + if not EMAIL_REGEX.match(email) or not PASSWORD_REGEX.match(password): return render_template('login.html', message='email/password format invalid') user = bn.login_user(email, password) if user: @@ -159,6 +159,7 @@ def profile(user): tickets = bn.get_all_tickets() return render_template('index.html', user=user, tickets=tickets) + @app.errorhandler(404) def page_not_found(e): return render_template('404.html'), 404 @@ -198,9 +199,9 @@ def check_ticket_form(name=None, quantity=None, price=None, date=None): @app.route('/sell', methods=['POST']) def sell(): """ - Route to sell a new ticket. + Route to sell a new ticket. This route will validate the ticket form, if valid it will use a backend function - to commit to the database + to commit to the database """ if 'logged_in' not in session: return redirect('/login') @@ -230,32 +231,30 @@ def buy(): if 'logged_in' not in session: return redirect('/login') email = session['logged_in'] - #Get user information + # Get user information user = bn.get_user(email) - #Sets the error message to blank initially - error_message="" - #Get information from the form + # Sets the error message to blank initially + error_message = "" + # Get information from the form name = request.form.get('name') quantity = request.form.get('quantity') - #Get all tickets to pass to backend function + # Get all tickets to pass to backend function tickets = bn.get_all_tickets() error_message = check_ticket_form(name, quantity) if not error_message: - if bn.buy_ticket(name,user,int(quantity)): + if bn.buy_ticket(name, user, int(quantity)): message = "Tickets bought succesfully" else: error_message = "Ticket could not be bought" - #Checks if there is an error, and if there is set the error message + # Checks if there is an error, and if there is set the error message if len(error_message) > 0: session['error'] = error_message message = session["error"] del session["error"] return render_template('index.html', buy_message=message, user=user, tickets=tickets) -def displayUpdateMessage(message): - return render_template('index.html', update_message=message) - + @app.route('/update', methods=['POST']) def update(): """ @@ -266,7 +265,10 @@ def update(): if 'logged_in' not in session: return redirect('/login') - #Grab necessary information from update form + # Grab necessary information from update form + user = bn.get_user(session['logged_in']) + tickets = bn.get_all_tickets() + name = request.form.get('name') quantity = request.form.get('quantity') price = request.form.get('price') @@ -274,15 +276,15 @@ def update(): error_message = check_ticket_form(name, quantity, price, date) if error_message: - return displayUpdateMessage(error_message) + return render_template('index.html', update_message=error_message, user=user, tickets=tickets) - #Check if ticket exists in database + # Check if ticket exists in database ticket = bn.get_ticket(name) - if (ticket is None): - return displayUpdateMessage('Ticket does not exist') + if ticket is None: + return render_template('index.html', update_message='Ticket does not exist', user=user, tickets=tickets) - #Update tickets to database + # Update tickets to database bn.update_ticket(name, quantity, price, date) - return displayUpdateMessage('Successfully updated tickets') + return render_template('index.html', update_message='Successfully updated tickets', user=user, tickets=tickets) diff --git a/qa327/templates/index.html b/qa327/templates/index.html index 3230217..80031f8 100644 --- a/qa327/templates/index.html +++ b/qa327/templates/index.html @@ -53,18 +53,18 @@

{{buy_message}}


Update a Ticket

-

{{update_message}}

+

{{update_message}}

- + - + - + - - + +
diff --git a/qa327_test/frontend/test_home.py b/qa327_test/frontend/test_home.py index 7804f06..aa9eceb 100644 --- a/qa327_test/frontend/test_home.py +++ b/qa327_test/frontend/test_home.py @@ -171,10 +171,10 @@ def test_home_update_form_exists(self, *_): self.open(base_url) # make sure each field exists under a form with the action to update - self.assert_element_present('form[action*="/update"] #name') - self.assert_element_present('form[action*="/update"] #quantity') - self.assert_element_present('form[action*="/update"] #price') - self.assert_element_present('form[action*="/update"] #date') + self.assert_element_present('form[action*="/update"] #update-name') + self.assert_element_present('form[action*="/update"] #update-quantity') + self.assert_element_present('form[action*="/update"] #update-price') + self.assert_element_present('form[action*="/update"] #update-date') @patch('qa327.backend.get_user', return_value=test_user) @patch('qa327.backend.get_all_tickets', return_value=test_tickets) diff --git a/qa327_test/frontend/test_update.py b/qa327_test/frontend/test_update.py new file mode 100644 index 0000000..2c3867a --- /dev/null +++ b/qa327_test/frontend/test_update.py @@ -0,0 +1,198 @@ +from time import sleep + +import pytest +from seleniumbase import BaseCase + +from qa327_test.conftest import base_url +from unittest.mock import patch +from qa327.models import db, User +from werkzeug.security import generate_password_hash, check_password_hash + +# Mock a sample user +test_user = User( + email='test_frontend@test.com', + name='test_frontend', + password=generate_password_hash('Test_frontend', method='sha256') +) + + +class UpdatePageTest(BaseCase): + + @patch('qa327.backend.get_user', return_value=test_user) + def login(self, *_): + self.open(base_url + '/login') + # fill in form + self.type("#email", "test_frontend@test.com") + self.type("#password", 'Test_frontend') + # submit + self.click('input[type="submit"]') + + @patch('qa327.backend.get_user', return_value=test_user) + def test_update_invalid_name(self, *_): + """ + R5.1.1 + """ + # invalidate any logged in sessions + self.open(base_url + '/logout') + # open login page + self.login() + # open home page + self.open(base_url) + + # enter in update form information + self.type("#update-name","*hi^xd!") + self.type("#update-quantity", 1) + self.type("#update-price", 15) + self.execute_script("document.querySelector('#update-date').setAttribute('value', '{}')".format('2020-12-10')) + + self.click('input[value="Update Ticket"]') + + self.assert_text("Name can only contain alphanumeric characters", '#update-message') + + @patch('qa327.backend.get_user', return_value=test_user) + def test_update_invalid_name_space(self, *_): + """ + R5.1.2 + """ + # invalidate any logged in sessions + self.open(base_url + '/logout') + # open login page + self.login() + # open home page + self.open(base_url) + # enter in update form information + self.type("#update-name"," ohno") + self.type("#update-quantity", 1) + self.type("#update-price", 15) + self.execute_script("document.querySelector('#update-date').setAttribute('value', '{}')".format('2020-12-10')) + + self.click('input[value="Update Ticket"]') + + self.assert_text("Name has space at beginning or end", '#update-message') + + @patch('qa327.backend.get_user', return_value=test_user) + def test_update_name_length(self, *_): + """ + R5.2.1 + """ + # invalidate any logged in sessions + self.open(base_url + '/logout') + # open login page + self.login() + # open home page + self.open(base_url) + # enter in update form information + self.type("#update-name","imtoolong"*7) + self.type("#update-quantity", 1) + self.type("#update-price", 15) + self.execute_script("document.querySelector('#update-date').setAttribute('value', '{}')".format('2020-12-10')) + + self.click('input[value="Update Ticket"]') + + self.assert_text("Name is too long, it must be shorter than 60 characters", "#update-message") + + @patch('qa327.backend.get_user', return_value=test_user) + def test_update_quantity_range_high(self, *_): + """ + R5.3.1 + """ + # invalidate any logged in sessions + self.open(base_url + '/logout') + # open login page + self.login() + # open home page + self.open(base_url) + # enter in update form information + self.type("#update-name","Test123") + self.type("#update-quantity", 101) + self.type("#update-price", 15) + self.execute_script("document.querySelector('#update-date').setAttribute('value', '{}')".format('2020-12-10')) + + self.click('input[value="Update Ticket"]') + + self.assert_text("Quantity must be greater than 0 and less than or equal to 100", "#update-message") + + @patch('qa327.backend.get_user', return_value=test_user) + def test_update_quantity_range_low(self, *_): + """ + R5.3.2 + """ + # invalidate any logged in sessions + self.open(base_url + '/logout') + # open login page + self.login() + # open home page + self.open(base_url) + # enter in update form information + self.type("#update-name","Test123") + self.type("#update-quantity", 0) + self.type("#update-price", 15) + self.execute_script("document.querySelector('#update-date').setAttribute('value', '{}')".format('2020-12-10')) + + self.click('input[value="Update Ticket"]') + + self.assert_text("Quantity must be greater than 0 and less than or equal to 100", "#update-message") + + @patch('qa327.backend.get_user', return_value=test_user) + def test_update_price_range_low(self, *_): + """ + R5.4.1 + """ + # invalidate any logged in sessions + self.open(base_url + '/logout') + # open login page + self.login() + # open home page + self.open(base_url) + # enter in update form information + self.type("#update-name","Test123") + self.type("#update-quantity", 2) + self.type("#update-price", 5) + self.execute_script("document.querySelector('#update-date').setAttribute('value', '{}')".format('2020-12-10')) + + self.click('input[value="Update Ticket"]') + + self.assert_text("Price must be greater than or equal to 10 and less than or equal to 100", "#update-message") + + @patch('qa327.backend.get_user', return_value=test_user) + def test_update_price_range_high(self, *_): + """ + R5.4.2 + """ + # invalidate any logged in sessions + self.open(base_url + '/logout') + # open login page + self.login() + # open home page + self.open(base_url) + # enter in update form information + self.type("#update-name","Test123") + self.type("#update-quantity", 2) + self.type("#update-price", 105) + self.execute_script("document.querySelector('#update-date').setAttribute('value', '{}')".format('2020-12-10')) + + self.click('input[value="Update Ticket"]') + + self.assert_text("Price must be greater than or equal to 10 and less than or equal to 100", "#update-message") + + @patch('qa327.backend.get_user', return_value=test_user) + def test_update_user_profile(self, *_): + """ + R5.5.1 + """ + # invalidate any logged in sessions + self.open(base_url + '/logout') + # open login page + self.login() + # open home page + self.open(base_url) + # enter in update form information + self.type("#update-name","Test123") + self.type("#update-quantity", 2) + self.type("#update-price", 15) + # valid date format + self.execute_script("document.querySelector('#update-date').setAttribute('value', '{}')".format('2020-12-10')) + + # successfully update ticket + self.click('input[value="Update Ticket"]') + diff --git a/qa327_test/test_failures/R5_failures.md b/qa327_test/test_failures/R5_failures.md new file mode 100644 index 0000000..17bb397 --- /dev/null +++ b/qa327_test/test_failures/R5_failures.md @@ -0,0 +1,8 @@ +# R5 Requirement Failures +These are the failures found and fixed on a specific branch for the R@ requirement. + +Branch: **52_test** +- Issue with return statements from /update post function, it does not render the html correctly, resulting in server error + - Fixed by rendering html correctly through passing all required arguments +- Issue with backend function to update a ticket, the code runs unnecessary db.session.update function + - Fixed by removing that line of code so it'll update a ticket without error. \ No newline at end of file