Files
mcma-backend/tests/test_auth_service.py
T
2026-06-03 10:40:00 +03:00

98 lines
3.6 KiB
Python

"""Unit tests for AuthService using in-memory ports."""
import pytest
from app.application.auth_service import AuthService
from app.application.user_service import UserService
from app.core.config import Settings
from app.core.security import Argon2PasswordHasher, JwtTokenService
from app.domain.errors import AuthenticationError
from tests.fakes import InMemoryRefreshTokenRepository, InMemoryUserRepository
@pytest.fixture
def env() -> tuple[AuthService, UserService, InMemoryUserRepository]:
users = InMemoryUserRepository()
refresh = InMemoryRefreshTokenRepository()
hasher = Argon2PasswordHasher()
tokens = JwtTokenService(Settings(jwt_secret="svc-test-secret"))
auth = AuthService(users=users, refresh_tokens=refresh, hasher=hasher, tokens=tokens)
user_svc = UserService(users=users, refresh_tokens=refresh, hasher=hasher)
return auth, user_svc, users
async def test_login_success_then_authenticate(
env: tuple[AuthService, UserService, object],
) -> None:
auth, user_svc, _ = env
created = await user_svc.create_user(username="alice", password="password123")
pair = await auth.login("alice", "password123")
user = await auth.authenticate_access(pair.access.encoded)
assert user.id == created.id
assert user.username == "alice"
async def test_login_wrong_password(env: tuple[AuthService, UserService, object]) -> None:
auth, user_svc, _ = env
await user_svc.create_user(username="alice", password="password123")
with pytest.raises(AuthenticationError):
await auth.login("alice", "nope")
async def test_login_unknown_user(env: tuple[AuthService, UserService, object]) -> None:
auth, _, _ = env
with pytest.raises(AuthenticationError):
await auth.login("ghost", "whatever")
async def test_login_inactive_user(env: tuple[AuthService, UserService, object]) -> None:
auth, user_svc, _ = env
user = await user_svc.create_user(username="alice", password="password123")
await user_svc.set_active(user.id, is_active=False)
with pytest.raises(AuthenticationError):
await auth.login("alice", "password123")
async def test_refresh_rotates_and_invalidates_old(
env: tuple[AuthService, UserService, object],
) -> None:
auth, user_svc, _ = env
await user_svc.create_user(username="alice", password="password123")
pair = await auth.login("alice", "password123")
new_pair = await auth.refresh(pair.refresh.encoded)
assert new_pair.refresh.encoded != pair.refresh.encoded
# Old refresh token is now revoked (rotation) — reuse must fail.
with pytest.raises(AuthenticationError):
await auth.refresh(pair.refresh.encoded)
# New one still works.
await auth.refresh(new_pair.refresh.encoded)
async def test_access_token_not_accepted_as_refresh(
env: tuple[AuthService, UserService, object],
) -> None:
auth, user_svc, _ = env
await user_svc.create_user(username="alice", password="password123")
pair = await auth.login("alice", "password123")
with pytest.raises(AuthenticationError):
await auth.refresh(pair.access.encoded)
async def test_logout_revokes_refresh(env: tuple[AuthService, UserService, object]) -> None:
auth, user_svc, _ = env
await user_svc.create_user(username="alice", password="password123")
pair = await auth.login("alice", "password123")
await auth.logout(pair.refresh.encoded)
with pytest.raises(AuthenticationError):
await auth.refresh(pair.refresh.encoded)
async def test_logout_ignores_garbage(env: tuple[AuthService, UserService, object]) -> None:
auth, _, _ = env
await auth.logout("not-a-jwt") # must not raise