Skip to content

Commit 5e2bdf0

Browse files
committed
Make password hashing re-activatable
- Add marker to activate password hashing on module level. - Change scope of `db` and `client` fixtures to `module`. - Add test without patched password hashing
1 parent d37b84d commit 5e2bdf0

File tree

3 files changed

+68
-18
lines changed

3 files changed

+68
-18
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import pytest
2+
from starlette.testclient import TestClient
3+
4+
from app.core.config import settings
5+
6+
pytestmark = pytest.mark.enable_password_hashing
7+
8+
9+
def test_get_access_token_with_hashing(client: TestClient) -> None:
10+
login_data = {
11+
"username": settings.FIRST_SUPERUSER,
12+
"password": settings.FIRST_SUPERUSER_PASSWORD,
13+
}
14+
r = client.post(f"{settings.API_V1_STR}/login/access-token", data=login_data)
15+
tokens = r.json()
16+
assert r.status_code == 200
17+
assert "access_token" in tokens
18+
assert tokens["access_token"]
19+
20+
21+
def test_get_access_token_incorrect_password(client: TestClient) -> None:
22+
login_data = {
23+
"username": settings.FIRST_SUPERUSER,
24+
"password": "incorrect",
25+
}
26+
r = client.post(f"{settings.API_V1_STR}/login/access-token", data=login_data)
27+
assert r.status_code == 400

backend/app/tests/conftest.py

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from collections.abc import Generator
2+
from contextlib import nullcontext
23

34
import pytest
5+
from _pytest.fixtures import FixtureRequest
46
from fastapi.testclient import TestClient
57
from sqlmodel import Session, delete
68

@@ -12,34 +14,52 @@
1214
from app.tests.utils.utils import get_superuser_token_headers, patch_password_hashing
1315

1416

15-
@pytest.fixture(scope="session")
16-
def disable_password_hashing() -> Generator[None, None, None]:
17-
with patch_password_hashing("app.core.security"):
17+
@pytest.fixture(scope="module", autouse=True)
18+
def disable_password_hashing(request: FixtureRequest) -> Generator[None, None, None]:
19+
"""Fixture disabling password hashing
20+
21+
Password hashing can be enabled on module level by marking the module with `pytest.mark.enable_password_hashing`.
22+
"""
23+
24+
with (
25+
patch_password_hashing("app.core.security")
26+
if all(m.name != "enable_password_hashing" for m in request.node.iter_markers())
27+
else nullcontext()
28+
):
1829
yield
1930

2031

21-
@pytest.fixture(scope="session", autouse=True)
22-
def db(
23-
disable_password_hashing: Generator[None, None, None], # noqa: ARG001
24-
) -> Generator[Session, None, None]:
32+
@pytest.fixture(scope="session")
33+
def db() -> Generator[Session, None, None]:
34+
"""
35+
Module scoped fixture providing a database session initialized with `init_db`.
36+
"""
2537
with Session(engine) as session:
26-
# cleanup db to prevent interferences with tests
27-
# all existing data will be deleted anyway after the tests run
28-
session.execute(delete(User))
38+
yield session
39+
# Cleanup test database
2940
session.execute(delete(Item))
41+
session.execute(delete(User))
3042
session.commit()
3143

32-
init_db(session)
33-
yield session
34-
statement = delete(Item)
35-
session.execute(statement)
36-
statement = delete(User)
37-
session.execute(statement)
38-
session.commit()
44+
45+
@pytest.fixture(scope="module", autouse=True)
46+
def init_db_fixture(db: Session) -> None:
47+
# note: deleting all users here is required to enable or disable password hashing per test module.
48+
# If we don't delete all users here, the users created during `init_db` will not be re-created and the password will stay (un)hashed,
49+
# leading to possibly failing tests relying on the created user for authentication.
50+
db.execute(delete(Item))
51+
db.execute(delete(User))
52+
init_db(db)
53+
db.commit()
3954

4055

4156
@pytest.fixture(scope="module")
42-
def client() -> Generator[TestClient, None, None]:
57+
def client(db: Session) -> Generator[TestClient, None, None]: # noqa: ARG001
58+
"""
59+
Module scoped fixture providing a `TestClient` instance.
60+
61+
NOTE: This fixture uses the `db` fixture WITHOUT hashing passwords!
62+
"""
4363
with TestClient(app) as c:
4464
yield c
4565

backend/pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ build-backend = "hatchling.build"
4141
strict = true
4242
exclude = ["venv", ".venv", "alembic"]
4343

44+
[tool.pytest.ini_options]
45+
markers = ["enable_password_hashing: mark tests to not patch password hashing."]
46+
4447
[tool.ruff]
4548
target-version = "py310"
4649
exclude = ["alembic"]

0 commit comments

Comments
 (0)