diff --git a/src/controllers/__init__.py b/src/controller/__init__.py similarity index 100% rename from src/controllers/__init__.py rename to src/controller/__init__.py diff --git a/src/controllers/auth_controller.py b/src/controller/auth_controller.py similarity index 93% rename from src/controllers/auth_controller.py rename to src/controller/auth_controller.py index 1c8f8c9..e66e2ab 100644 --- a/src/controllers/auth_controller.py +++ b/src/controller/auth_controller.py @@ -1,4 +1,4 @@ -from flask import Blueprint, flash, redirect, render_template, url_for +from flask import Blueprint, flash, redirect, render_template, url_for, current_app from flask_login import login_required, login_user, logout_user from src.forms.user_forms import LogInForm, RegisterForm @@ -16,9 +16,9 @@ def login(): if form.validate_on_submit(): username = form.username.data password = form.password.data - - print("Form submitted with:", username, password) - session = SessionLocal() + + # Enables connection to different databases, e.g., test & prod + session = current_app.session user_repo = UsersRepository(session) user = user_repo.find_user_by_username(username) diff --git a/src/controllers/tasks_controller.py b/src/controller/tasks_controller.py similarity index 100% rename from src/controllers/tasks_controller.py rename to src/controller/tasks_controller.py diff --git a/src/main.py b/src/main.py index db6fa51..160ba5f 100644 --- a/src/main.py +++ b/src/main.py @@ -1,13 +1,14 @@ from flask import Flask, redirect, url_for from flask_login import LoginManager -from src.controllers.auth_controller import auth_blueprint -from src.controllers.tasks_controller import tasks_blueprint +from src.controller.auth_controller import auth_blueprint +from src.controller.tasks_controller import tasks_blueprint from src.models import Base, SessionLocal, engine from src.repository.users_repository import UsersRepository app = Flask(__name__, static_folder="../static", template_folder="../templates") app.config["SECRET_KEY"] = "shhhh_dont_tell_anyone" +app.session = SessionLocal() login_manager = LoginManager() login_manager.init_app(app) diff --git a/tests/conftest.py b/tests/conftest.py index 3cca3e2..4735d67 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,12 +4,15 @@ from sqlalchemy.orm import sessionmaker from src.config import TaskStatus -from src.controllers.auth_controller import auth_blueprint +from src.controller.auth_controller import auth_blueprint from src.models import Base, Tasks, Users from src.repository.tasks_repository import TasksRespository from src.repository.users_repository import UsersRepository from src.security import generate_password_hash +from flask_login import LoginManager, AnonymousUserMixin +from src.controller.tasks_controller import tasks_blueprint +from flask.testing import FlaskClient @pytest.fixture(scope="function") def test_db(): @@ -89,13 +92,42 @@ def users_repository(test_db): @pytest.fixture(scope="function") -def app(): - app = Flask(__name__) - app.register_blueprint(auth_blueprint) +def app(test_db): + app = Flask(__name__, static_folder="../static", template_folder="../templates") app.config["TESTING"] = True + app.config["SERVER_NAME"] = "localhost" + app.config["APPLICATION_ROOT"] = "/" + app.config["SECRET_KEY"] = "shhhh_dont_tell_anyone" + app.config["WTF_CSRF_ENABLED"] = False + + # Attach test_db session for testing + app.session = test_db + + # Initialize Flask-login + login_manager = LoginManager() + login_manager.init_app(app) + login_manager.login_view = "login" + + # Enable the loading of users during the test + @login_manager.user_loader + def load_user(user_id): + session = test_db + user_repo = UsersRepository(session) + return user_repo.find_user_by_id(user_id) + + # Register blueprints + app.register_blueprint(auth_blueprint) + app.register_blueprint(tasks_blueprint) + return app @pytest.fixture(scope="function") def client(app): - return app.test_client() + # Enables simulation of client requests in Flask for testing + app.test_client_class = FlaskClient + client = app.test_client() + + # Set 'current_user' to anonymous user if no user is logged in + app.jinja_env.globals["current_user"] = AnonymousUserMixin() + return client diff --git a/tests/test_controller/test_auth_controller.py b/tests/test_controller/test_auth_controller.py new file mode 100644 index 0000000..9796b91 --- /dev/null +++ b/tests/test_controller/test_auth_controller.py @@ -0,0 +1,45 @@ +from unittest.mock import patch +from urllib.parse import urlparse + +from src.models import Users, Tasks +from src.repository.users_repository import UsersRepository +from flask import url_for +from flask_login import login_user + +from unittest.mock import patch, MagicMock + +from src.security import check_password_hash, generate_password_hash + + +def test_login_page_load_success(client, app): + with app.app_context(): + mock_user = MagicMock() + mock_user.is_authenticated = True + mock_user.is_active = True + mock_user.username = "Will_Smith" + + # Log in the user + with patch("flask_login.utils._get_user", return_value=mock_user): + # perform the request + response = client.get(url_for("auth.login")) + assert response.status_code == 200 + assert b'LOGIN' in response.data + assert b'Peak Performer' in response.data + + +def test_login_valid_credentails(client, app): + with app.app_context(): + # simulate user login + response = client.post( + url_for("auth.login"), + data={"username": "Will_Smith", "password": "fresh_password"}, + follow_redirects=False, # prevents the test client from retriev view_tasks.html content + ) + + # Assert + assert response.status_code == 302 # Redirect + + # Ensure only paths are compared. Ignore protocl and hostname differences. + response_path = urlparse(response.location).path + expected_path = url_for("tasks.view_tasks", user_id=1, _external=False) + assert response_path == expected_path \ No newline at end of file diff --git a/tests/test_controllers/test_user_controller.py b/tests/test_controllers/test_user_controller.py deleted file mode 100644 index 8e54e56..0000000 --- a/tests/test_controllers/test_user_controller.py +++ /dev/null @@ -1,33 +0,0 @@ -from unittest.mock import patch - -from src.repository.users_repository import UsersRepository - - -def test_register_user_missing_fields(client): - response = client.post("/users/register", json={}) - assert response.status_code == 400 - assert response.json == {"error": "Username and password are required."} - - -def test_register_user_existing_username(client): - # Mock username already exists - with patch.object(UsersRepository, "find_user_by_username", return_value=True): - response = client.post( - "/ussers/register", - json={"username": "existing_user", "password": "password123"}, - ) - assert response.status_code == 409 - assert response.json == {"error": "Username already exists"} - - -def test_register_user_success(client): - # Mock username does not exist - with patch.object(UsersRepository, "find_user_by_username", return_value=False): - # Mock username - with patch.object(UsersRepository, "add_user", return_value=True): - response = client.post( - "/users/register", - json={"username": "new_user", "password": "password123"}, - ) - assert response.status_code == 201 - assert response.json == {"message": "User registered."}