feat: auth & admin
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user