Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
22 changes: 22 additions & 0 deletions backend/tests/api/routes/test_login_with_hashing.py
Original file line number Diff line number Diff line change
@@ -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"]
32 changes: 25 additions & 7 deletions backend/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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()


Expand Down
19 changes: 19 additions & 0 deletions backend/tests/utils/utils.py
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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:
Comment on lines +35 to +37
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Contextmanager to patch ``pwd_context`` in the given modules.
:param modules: list of modules to patch.
:return:
Contextmanager to patch `pwd_context` in the given modules.

We don't use this style for docstrings in this project

"""
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