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"}