From 83e88c76493a2b50d5adefa695c666c8e892f71c Mon Sep 17 00:00:00 2001 From: Jaydin_MacBook <74679492+TheManWhoLikesToCode@users.noreply.github.com> Date: Tue, 23 Jan 2024 14:24:39 -0500 Subject: [PATCH] Adding More Tests To Fix last 4 tests in app_login.feature --- backend/app.py | 9 ++- backend/features/app_login.feature | 58 +++++++++++++--- backend/features/steps/all_steps.py | 103 +++++++++++++++++++++++----- 3 files changed, 142 insertions(+), 28 deletions(-) diff --git a/backend/app.py b/backend/app.py index bbbe9b1..be6b41e 100644 --- a/backend/app.py +++ b/backend/app.py @@ -1,4 +1,5 @@ from functools import wraps +from json import JSONDecodeError import logging import os import threading @@ -71,7 +72,11 @@ def index(): @app.route('/login', methods=['POST']) @cross_origin(supports_credentials=True) def login(): - data = request.json + try: + data = request.get_json() + except JSONDecodeError: + return jsonify({'error': 'Invalid JSON format'}), 400 + username = data.get('username') password = data.get('password') @@ -89,7 +94,7 @@ def login(): bb_session_manager.put_bb_session(username, bb_session) resp = make_response( - jsonify({'message': 'Logged in successfully'})) + jsonify({'message': 'Logged in successfully'}), 200) resp.set_cookie('user_session', bb_session.session_id, max_age=3600, secure=True, httponly=True) return resp diff --git a/backend/features/app_login.feature b/backend/features/app_login.feature index cb78717..3857c30 100644 --- a/backend/features/app_login.feature +++ b/backend/features/app_login.feature @@ -3,22 +3,60 @@ Feature: Backend Flask App - Login As a system I want users to have to login - Scenario: Successful Login - Given App is running - When I pass valid credentials to the login endpoint - Then The response of "200" and "Logged in Successfully" should be returned + Scenario: Successful Login + Given the app is running + When I pass valid credentials to the login endpoint + Then the response of "200" and "Logged in successfully" should be returned + And cookies should be set Scenario: Unsuccessful Login - Incorrect username and password - Given App is running + Given the app is running When I pass an incorrect username and password to the login endpoint - Then The response of "401" and "The username you entered cannot be identified." should be returned + Then the response of "401" and "The username you entered cannot be identified." should be returned Scenario: Unsuccessful Login - Incorrect password - Given App is running + Given the app is running When I pass an incorrect password to the login endpoint - Then The response of "401" and "The password you entered was incorrect." should be returned + Then the response of "401" and "The password you entered was incorrect." should be returned Scenario: Unsuccessful Login - Incorrect username - Given App is running + Given the app is running When I pass an incorrect username to the login endpoint - Then The response of "401" and "The username you entered cannot be identified." should be returned \ No newline at end of file + Then the response of "401" and "The username you entered cannot be identified." should be returned + + Scenario: Unsuccessful Login - Missing password + Given the app is running + When I pass only a username to the login endpoint + Then the response of "400" and "Missing username or password" should be returned + + Scenario: Unsuccessful Login - Missing username + Given the app is running + When I pass only a password to the login endpoint + Then the response of "400" and "Missing username or password" should be returned + + Scenario: Unsuccessful Login - Missing username and password + Given the app is running + When I pass no credentials to the login endpoint + Then the response of "400" and "Missing username or password" should be returned + + Scenario: Unsuccessful Login - Invalid JSON Format in Request + Given the app is running + When I pass data in an invalid JSON format to the login endpoint + Then the response of "400" and "Invalid request format" should be returned + + Scenario: Server Error During Login Process + Given the app is running + When I pass valid credentials but the server encounters an internal error during login + Then the response of "500" and the specific error message should be returned + + Scenario: Already Logged In + Given the app is running + And the user is already logged in + When I pass valid credentials to the login endpoint again + Then the response of "200" and "Already logged in" should be returned + + Scenario: Session Cookie Attributes Verification + Given the app is running + When I pass valid credentials to the login endpoint + Then the response should include a 'Set-Cookie' header with 'user_session' cookie + And the cookie should have 'HttpOnly' and 'Secure' attributes set diff --git a/backend/features/steps/all_steps.py b/backend/features/steps/all_steps.py index 58e0eeb..0ac59a8 100644 --- a/backend/features/steps/all_steps.py +++ b/backend/features/steps/all_steps.py @@ -66,8 +66,8 @@ def step_impl(context, session_id, user1, user2): @given('I have valid credentials') def step_impl(context): - context.username = os.getenv('TEST_USERNAME') - context.password = os.getenv('TEST_PASSWORD') + context.username = os.environ.get('TEST_USERNAME') + context.password = os.environ.get('TEST_PASSWORD') context.session = BlackboardSession( username=context.username, password=context.password) @@ -92,7 +92,7 @@ def step_impl(context): username='InvalidUsername', password='InvalidPassword') context.logged_in = context.session.is_logged_in -@given('App is running') +@given('the app is running') def step_impl(context): assert context.client @@ -216,8 +216,8 @@ def step_impl(context): @when('I pass valid credentials to the login endpoint') def step_impl(context): context.page = context.client.post('/login', json=dict( - username=os.getenv('TEST_USERNAME'), - password=os.getenv('TEST_PASSWORD') + username=os.environ.get('TEST_USERNAME'), + password=os.environ.get('TEST_PASSWORD') ), headers={'Content-Type': 'application/json'}, follow_redirects=True) response = context.page.get_json() assert response['message'] == 'Logged in successfully' @@ -244,11 +244,62 @@ def step_impl(context): def step_impl(context): context.page = context.client.post('/login', json=dict( username='InvalidUsername', - password=os.getenv('TEST_PASSWORD') + password=os.environ.get('TEST_PASSWORD') ), headers={'Content-Type': 'application/json'}, follow_redirects=True) response = context.page.get_json() assert response['error'] == 'The username you entered cannot be identified.' +@when('I pass only a username to the login endpoint') +def step_impl(context): + context.page = context.client.post('/login', json=dict( + username='InvalidUsername', + password='' + ), headers={'Content-Type': 'application/json'}, follow_redirects=True) + response = context.page.get_json() + assert response['error'] == 'Missing username or password' + +@when('I pass only a password to the login endpoint') +def step_impl(context): + context.page = context.client.post('/login', json=dict( + username='', + password='InvalidPassword' + ), headers={'Content-Type': 'application/json'}, follow_redirects=True) + response = context.page.get_json() + assert response['error'] == 'Missing username or password' + +@when('I pass no credentials to the login endpoint') +def step_impl(context): + context.page = context.client.post('/login', json=dict( + username='', + password='' + ), headers={'Content-Type': 'application/json'}, follow_redirects=True) + response = context.page.get_json() + assert response['error'] == 'Missing username or password' + +@when('I pass data in an invalid JSON format to the login endpoint') +def step_impl(context): + invalid_json_data = 'Invalid JSON format' + context.page = context.client.post('/login', data=invalid_json_data, headers={'Content-Type': 'application/json'}, follow_redirects=True) + response = context.page.get_json() + assert response['error'] == 'Invalid JSON payload received.' + + +@when('I pass valid credentials but the server encounters an internal error when logging in') +def step_impl(context): + with patch('blackboard_session.BlackboardSession.login') as mock_login: + mock_login.side_effect = Exception("Internal server error") + try: + context.session.login() + except Exception as e: + context.exception_message = str(e) + +@when('the user is already logged in') +def step_impl(context): + # Assuming 'context.session' is an instance of BlackboardSession + context.session.login() # First login + context.second_login_response = context.session.login() # Second login attempt + + #* Then steps @then('a new session should be created for "{username}"') def step_impl(context, username): @@ -355,18 +406,38 @@ def step_impl(context, message): def step_impl(context, message): assert context.get_download_tasks_response == message -@then('The response of "200" and "Logged in Successfully" should be returned') -def step_impl(context): - assert context.page.get_json() == {'message': 'Logged in successfully'} +@then('the response of "{status_code}" and "{message}" should be returned') +def step_impl(context, status_code, message): + expected_status_code = int(status_code) -@then('The response of "401" and "The username you entered cannot be identified." should be returned') -def step_impl(context): - assert context.page.get_json() == {'error': 'The username you entered cannot be identified.'} + # Ensure that context.page contains the response object + assert hasattr(context, 'page'), "context does not have 'page' attribute. Make sure the HTTP request was made." + + # Get the actual status code from the response + actual_status_code = context.page.status_code + assert actual_status_code == expected_status_code, f"Expected status code {expected_status_code}, got {actual_status_code}" + + # Parse the JSON response body + response_json = context.page.get_json() -@then('The response of "401" and "The password you entered was incorrect." should be returned') + # Check the message or error based on the status code + if expected_status_code == 200: + assert response_json.get('message') == message, f"Expected message '{message}', got '{response_json.get('message')}'" + else: + assert response_json.get('error') == message, f"Expected error message '{message}', got '{response_json.get('error')}'" + +@then('cookies should be set') def step_impl(context): - assert context.page.get_json() == {'error': 'The password you entered was incorrect.'} + # Ensure that context.page contains the response object + assert hasattr(context, 'page'), "context does not have 'page' attribute. Make sure the HTTP request was made." + + # Check if the 'user_session' cookie is set in the response + cookies = context.page.headers.get('Set-Cookie') + assert cookies is not None, "No cookies were set in the response." + + # Further check for the specific 'user_session' cookie + assert 'user_session' in cookies, "The 'user_session' cookie was not set in the response." -@then('The response of "429" and "Too many requests, please try again later." should be returned') +@then('an internal server error should be raised') def step_impl(context): - assert context.page.get_json() == {'error': 'Too many requests, please try again later.'} \ No newline at end of file + assert context.exception_message == "Internal server error" \ No newline at end of file