125 lines
3.7 KiB
Python
125 lines
3.7 KiB
Python
import asyncio
|
|
from unittest.mock import AsyncMock
|
|
|
|
import pytest
|
|
|
|
from app.domain.models.role import UserRole
|
|
from app.domain.schemas.user import UserCreate, UserUpdate
|
|
from app.infra.db.metadb.repositories.user_repository import UserRepository
|
|
from tests.conftest import FakeCursor, FakeDB
|
|
|
|
|
|
def _user_row(**overrides):
|
|
base = {
|
|
"id": 1,
|
|
"username": "tester",
|
|
"email": "tester@example.com",
|
|
"hashed_password": "hashed-password",
|
|
"role": "USER",
|
|
"is_active": True,
|
|
"is_superuser": False,
|
|
"created_at": "2025-01-01T00:00:00+00:00",
|
|
"updated_at": "2025-01-01T00:00:00+00:00",
|
|
}
|
|
base.update(overrides)
|
|
return base
|
|
|
|
|
|
def test_create_user_hashes_password_and_returns_model(monkeypatch):
|
|
cursor = FakeCursor(fetchone_results=[_user_row()])
|
|
repo = UserRepository(FakeDB(cursor))
|
|
monkeypatch.setattr(
|
|
"app.infra.db.metadb.repositories.user_repository.get_password_hash",
|
|
lambda password: f"hashed::{password}",
|
|
)
|
|
|
|
result = asyncio.run(
|
|
repo.create_user(
|
|
UserCreate(
|
|
username="tester",
|
|
email="tester@example.com",
|
|
password="secret123",
|
|
)
|
|
)
|
|
)
|
|
|
|
assert result is not None
|
|
assert result.username == "tester"
|
|
assert cursor.executed[0][1]["hashed_password"] == "hashed::secret123"
|
|
|
|
|
|
def test_update_user_without_fields_returns_existing_user(monkeypatch):
|
|
repo = UserRepository(FakeDB(FakeCursor()))
|
|
existing_user = AsyncMock(return_value="existing")
|
|
monkeypatch.setattr(repo, "get_user_by_id", existing_user)
|
|
|
|
result = asyncio.run(repo.update_user(1, UserUpdate()))
|
|
|
|
assert result == "existing"
|
|
existing_user.assert_awaited_once_with(1)
|
|
|
|
|
|
def test_update_user_builds_dynamic_query(monkeypatch):
|
|
cursor = FakeCursor(fetchone_results=[_user_row(role="ADMIN", email="new@example.com")])
|
|
repo = UserRepository(FakeDB(cursor))
|
|
monkeypatch.setattr(
|
|
"app.infra.db.metadb.repositories.user_repository.get_password_hash",
|
|
lambda password: f"hashed::{password}",
|
|
)
|
|
|
|
result = asyncio.run(
|
|
repo.update_user(
|
|
1,
|
|
UserUpdate(
|
|
email="new@example.com",
|
|
password="new-secret",
|
|
role=UserRole.ADMIN,
|
|
is_active=False,
|
|
),
|
|
),
|
|
)
|
|
|
|
assert result is not None
|
|
query, params = cursor.executed[0]
|
|
assert "email = %(email)s" in query
|
|
assert "hashed_password = %(hashed_password)s" in query
|
|
assert "role = %(role)s" in query
|
|
assert "is_active = %(is_active)s" in query
|
|
assert params["hashed_password"] == "hashed::new-secret"
|
|
assert params["role"] == "ADMIN"
|
|
assert params["is_active"] is False
|
|
|
|
|
|
def test_delete_user_returns_false_when_execute_raises():
|
|
cursor = FakeCursor()
|
|
cursor.execute = AsyncMock(side_effect=RuntimeError("boom"))
|
|
repo = UserRepository(FakeDB(cursor))
|
|
|
|
result = asyncio.run(repo.delete_user(1))
|
|
|
|
assert result is False
|
|
|
|
|
|
def test_user_exists_short_circuits_without_filters():
|
|
cursor = FakeCursor()
|
|
repo = UserRepository(FakeDB(cursor))
|
|
|
|
result = asyncio.run(repo.user_exists())
|
|
|
|
assert result is False
|
|
assert cursor.executed == []
|
|
|
|
|
|
def test_user_exists_checks_username_or_email():
|
|
cursor = FakeCursor(fetchone_results=[{"exists": True}])
|
|
repo = UserRepository(FakeDB(cursor))
|
|
|
|
result = asyncio.run(
|
|
repo.user_exists(username="tester", email="tester@example.com")
|
|
)
|
|
|
|
assert result is True
|
|
query, params = cursor.executed[0]
|
|
assert "username = %(username)s OR email = %(email)s" in query
|
|
assert params == {"username": "tester", "email": "tester@example.com"}
|