diff --git a/backend/pyproject.toml b/backend/pyproject.toml index d72454c28a..57d943f354 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -41,6 +41,9 @@ build-backend = "hatchling.build" strict = true exclude = ["venv", ".venv", "alembic"] +[tool.pytest.ini_options] +markers = ["enable_password_hashing: mark tests to not patch password hashing."] + [tool.ruff] target-version = "py310" exclude = ["alembic"] diff --git a/backend/tests/api/routes/test_login_with_hashing.py b/backend/tests/api/routes/test_login_with_hashing.py new file mode 100644 index 0000000000..d4610c06be --- /dev/null +++ b/backend/tests/api/routes/test_login_with_hashing.py @@ -0,0 +1,22 @@ +import pytest +from starlette.testclient import TestClient + +from app.core.config import settings + +pytestmark = pytest.mark.enable_password_hashing + + +def test_get_access_token_with_hashing( + client: TestClient, + disable_password_hashing: bool, +) -> None: + assert disable_password_hashing is False + login_data = { + "username": settings.FIRST_SUPERUSER, + "password": settings.FIRST_SUPERUSER_PASSWORD, + } + r = client.post(f"{settings.API_V1_STR}/login/access-token", data=login_data) + tokens = r.json() + assert r.status_code == 200 + assert "access_token" in tokens + assert tokens["access_token"] diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py index 8ddab7b321..d499c5494e 100644 --- a/backend/tests/conftest.py +++ b/backend/tests/conftest.py @@ -1,6 +1,7 @@ from collections.abc import Generator import pytest +from _pytest.fixtures import FixtureRequest from fastapi.testclient import TestClient from sqlmodel import Session, delete @@ -9,18 +10,35 @@ from app.main import app from app.models import Item, User from tests.utils.user import authentication_token_from_email -from tests.utils.utils import get_superuser_token_headers +from tests.utils.utils import get_superuser_token_headers, patch_password_hashing -@pytest.fixture(scope="session", autouse=True) -def db() -> Generator[Session, None, None]: +@pytest.fixture(scope="module") +def disable_password_hashing(request: FixtureRequest) -> Generator[bool, None, None]: + """ + Disable password hashing if no `enable_password_hashing` marker set for module. + """ + + module = request.node.getparent(pytest.Module) + if not module.get_closest_marker("enable_password_hashing"): + with patch_password_hashing("app.core.security"): + yield True + else: + yield False # Don't patch if `enable_password_hashing` marker is set + + +@pytest.fixture(scope="module", autouse=True) +def db(disable_password_hashing: bool) -> Generator[Session, None, None]: # noqa: ARG001 with Session(engine) as session: + session.execute( # Recreate user for every module, with\without pwd hashing + delete(User) + ) init_db(session) + session.commit() yield session - statement = delete(Item) - session.execute(statement) - statement = delete(User) - session.execute(statement) + # Cleanup test database + session.execute(delete(Item)) + session.execute(delete(User)) session.commit() diff --git a/backend/tests/utils/utils.py b/backend/tests/utils/utils.py index 184bac44d9..c1ba93fd13 100644 --- a/backend/tests/utils/utils.py +++ b/backend/tests/utils/utils.py @@ -1,5 +1,8 @@ import random import string +from collections.abc import Generator +from contextlib import ExitStack, contextmanager +from unittest.mock import patch from fastapi.testclient import TestClient @@ -24,3 +27,19 @@ def get_superuser_token_headers(client: TestClient) -> dict[str, str]: a_token = tokens["access_token"] headers = {"Authorization": f"Bearer {a_token}"} return headers + + +@contextmanager +def patch_password_hashing(*modules: str) -> Generator[None, None, None]: + """ + Contextmanager to patch ``pwd_context`` in the given modules. + :param modules: list of modules to patch. + :return: + """ + with ExitStack() as stack: + for module in modules: + stack.enter_context( + patch(f"{module}.pwd_context.verify", lambda x, y: x == y) + ) + stack.enter_context(patch(f"{module}.pwd_context.hash", lambda x: x)) + yield