Skip to content

Commit b4c91ae

Browse files
committed
chore: добавить just-команды для тестов и убрать лишний redis extra
Что сделано: - добавлены just-команды test, test-unit, test-cov, test-cov-html - добавлен pytest-cov в dev-зависимости и обновлён lock-файл - зависимость redis[asyncio] заменена на redis (asyncio уже встроен) - подправлены type hints в test_auth_service.py для чистого Pylance
1 parent 8907586 commit b4c91ae

4 files changed

Lines changed: 177 additions & 33 deletions

File tree

Justfile

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,22 @@ pre-commit-run:
7777
pre-commit-update:
7878
uv run pre-commit autoupdate
7979

80+
# Запустить все тесты.
81+
test:
82+
uv run pytest
83+
84+
# Запустить только unit-тесты.
85+
test-unit:
86+
uv run pytest tests/unit -q
87+
88+
# Запустить тесты с покрытием в консоли.
89+
test-cov:
90+
uv run pytest --cov=app --cov-report=term-missing
91+
92+
# Запустить тесты с HTML-отчётом покрытия (htmlcov/index.html).
93+
test-cov-html:
94+
uv run pytest --cov=app --cov-report=html
95+
8096
# Проверить код линтером Ruff (без автоисправлений).
8197
lint:
8298
uv run ruff check .

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ dependencies = [
1313
"pydantic-settings>=2.13.1",
1414
"pyjwt[crypto]>=2.11.0",
1515
"python-multipart>=0.0.9",
16-
"redis[asyncio]>=7.2.1",
16+
"redis>=7.2.1",
1717
"sqlalchemy[asyncio]>=2.0.48",
1818
"uvicorn[standard]>=0.41.0",
1919
"structlog>=25.5.0",
@@ -33,6 +33,7 @@ dev = [
3333
"pre-commit>=4.5.1",
3434
"pytest>=9.0.2",
3535
"pytest-asyncio>=1.3.0",
36+
"pytest-cov>=7.0.0",
3637
"ruff>=0.15.5",
3738
]
3839

Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,46 @@
11
from __future__ import annotations
22

3-
from types import SimpleNamespace
3+
from typing import Any, cast
44
from unittest.mock import AsyncMock, Mock
55
from uuid import uuid4
66

77
import pytest
8+
from sqlalchemy.ext.asyncio import AsyncSession
89

10+
from app.models.user import User
911
from app.services import auth as auth_service
1012

1113

1214
@pytest.fixture
13-
def fake_session() -> object:
14-
return object()
15+
def fake_session() -> AsyncSession:
16+
return cast(AsyncSession, AsyncMock(spec=AsyncSession))
1517

1618

1719
@pytest.fixture
18-
def active_user() -> SimpleNamespace:
19-
return SimpleNamespace(
20-
id=uuid4(),
21-
email="user@example.com",
22-
hashed_password="hashed-password",
23-
is_active=True,
24-
)
20+
def active_user() -> User:
21+
user = User()
22+
user.id = uuid4()
23+
user.email = "user@example.com"
24+
user.hashed_password = "hashed-password"
25+
user.is_active = True
26+
return user
2527

2628

2729
@pytest.mark.asyncio
2830
async def test_register_user_raises_when_email_already_exists(
2931
monkeypatch: pytest.MonkeyPatch,
30-
fake_session: object,
31-
active_user: SimpleNamespace,
32+
fake_session: AsyncSession,
33+
active_user: User,
3234
) -> None:
3335
get_user_by_email = AsyncMock(return_value=active_user)
3436
create_user = AsyncMock()
3537

36-
monkeypatch.setattr(auth_service.user_repo, "get_user_by_email", get_user_by_email)
37-
monkeypatch.setattr(auth_service.user_repo, "create_user", create_user)
38+
monkeypatch.setattr(
39+
auth_service.user_repo,
40+
"get_user_by_email",
41+
cast(Any, get_user_by_email),
42+
)
43+
monkeypatch.setattr(auth_service.user_repo, "create_user", cast(Any, create_user))
3844

3945
with pytest.raises(auth_service.UserAlreadyExists):
4046
await auth_service.register_user(fake_session, "user@example.com", "password")
@@ -45,16 +51,20 @@ async def test_register_user_raises_when_email_already_exists(
4551
@pytest.mark.asyncio
4652
async def test_register_user_hashes_password_and_creates_user(
4753
monkeypatch: pytest.MonkeyPatch,
48-
fake_session: object,
49-
active_user: SimpleNamespace,
54+
fake_session: AsyncSession,
55+
active_user: User,
5056
) -> None:
5157
get_user_by_email = AsyncMock(return_value=None)
5258
hash_password = AsyncMock(return_value="hashed-by-test")
5359
create_user = AsyncMock(return_value=active_user)
5460

55-
monkeypatch.setattr(auth_service.user_repo, "get_user_by_email", get_user_by_email)
56-
monkeypatch.setattr(auth_service, "hash_password", hash_password)
57-
monkeypatch.setattr(auth_service.user_repo, "create_user", create_user)
61+
monkeypatch.setattr(
62+
auth_service.user_repo,
63+
"get_user_by_email",
64+
cast(Any, get_user_by_email),
65+
)
66+
monkeypatch.setattr(auth_service, "hash_password", cast(Any, hash_password))
67+
monkeypatch.setattr(auth_service.user_repo, "create_user", cast(Any, create_user))
5868

5969
created = await auth_service.register_user(
6070
fake_session,
@@ -74,15 +84,19 @@ async def test_register_user_hashes_password_and_creates_user(
7484
@pytest.mark.asyncio
7585
async def test_authenticate_user_uses_dummy_hash_when_user_not_found(
7686
monkeypatch: pytest.MonkeyPatch,
77-
fake_session: object,
87+
fake_session: AsyncSession,
7888
) -> None:
7989
get_user_by_email = AsyncMock(return_value=None)
8090
get_dummy_hash = Mock(return_value="dummy-hash")
8191
verify_password = AsyncMock(return_value=False)
8292

83-
monkeypatch.setattr(auth_service.user_repo, "get_user_by_email", get_user_by_email)
84-
monkeypatch.setattr(auth_service, "get_dummy_hash", get_dummy_hash)
85-
monkeypatch.setattr(auth_service, "verify_password", verify_password)
93+
monkeypatch.setattr(
94+
auth_service.user_repo,
95+
"get_user_by_email",
96+
cast(Any, get_user_by_email),
97+
)
98+
monkeypatch.setattr(auth_service, "get_dummy_hash", cast(Any, get_dummy_hash))
99+
monkeypatch.setattr(auth_service, "verify_password", cast(Any, verify_password))
86100

87101
authenticated = await auth_service.authenticate_user(
88102
fake_session,
@@ -100,15 +114,20 @@ async def test_authenticate_user_uses_dummy_hash_when_user_not_found(
100114
@pytest.mark.asyncio
101115
async def test_authenticate_active_user_raises_for_inactive_user(
102116
monkeypatch: pytest.MonkeyPatch,
117+
fake_session: AsyncSession,
103118
) -> None:
104-
inactive_user = SimpleNamespace(is_active=False)
119+
inactive_user = User()
120+
inactive_user.id = uuid4()
121+
inactive_user.email = "inactive@example.com"
122+
inactive_user.hashed_password = "hashed-password"
123+
inactive_user.is_active = False
105124
authenticate_user = AsyncMock(return_value=inactive_user)
106125

107-
monkeypatch.setattr(auth_service, "authenticate_user", authenticate_user)
126+
monkeypatch.setattr(auth_service, "authenticate_user", cast(Any, authenticate_user))
108127

109128
with pytest.raises(auth_service.UserInactive):
110129
await auth_service.authenticate_active_user(
111-
object(),
130+
fake_session,
112131
"user@example.com",
113132
"password",
114133
)
@@ -117,10 +136,12 @@ async def test_authenticate_active_user_raises_for_inactive_user(
117136
@pytest.mark.asyncio
118137
async def test_validate_refresh_subject_raises_when_user_not_found(
119138
monkeypatch: pytest.MonkeyPatch,
120-
fake_session: object,
139+
fake_session: AsyncSession,
121140
) -> None:
122141
get_user_by_id = AsyncMock(return_value=None)
123-
monkeypatch.setattr(auth_service.user_repo, "get_user_by_id", get_user_by_id)
142+
monkeypatch.setattr(
143+
auth_service.user_repo, "get_user_by_id", cast(Any, get_user_by_id)
144+
)
124145

125146
with pytest.raises(auth_service.UserNotFound):
126147
await auth_service.validate_refresh_subject(fake_session, uuid4())
@@ -129,11 +150,17 @@ async def test_validate_refresh_subject_raises_when_user_not_found(
129150
@pytest.mark.asyncio
130151
async def test_validate_refresh_subject_raises_when_user_inactive(
131152
monkeypatch: pytest.MonkeyPatch,
132-
fake_session: object,
153+
fake_session: AsyncSession,
133154
) -> None:
134-
inactive_user = SimpleNamespace(is_active=False)
155+
inactive_user = User()
156+
inactive_user.id = uuid4()
157+
inactive_user.email = "inactive@example.com"
158+
inactive_user.hashed_password = "hashed-password"
159+
inactive_user.is_active = False
135160
get_user_by_id = AsyncMock(return_value=inactive_user)
136-
monkeypatch.setattr(auth_service.user_repo, "get_user_by_id", get_user_by_id)
161+
monkeypatch.setattr(
162+
auth_service.user_repo, "get_user_by_id", cast(Any, get_user_by_id)
163+
)
137164

138165
with pytest.raises(auth_service.UserInactive):
139166
await auth_service.validate_refresh_subject(fake_session, uuid4())

0 commit comments

Comments
 (0)