# File: tests/api/test_users.py from datetime import datetime from unittest.mock import MagicMock import pytest from fastapi import status from fastapi.testclient import TestClient from app.api.dependencies import get_admin_user, get_user_service from app.main import app from app.models.auth import UserRole from app.models.types import PyObjectId from app.models.user import UserInDB, UserCreate from app.services.user_service import UserService # ----------------------- # Fixtures # ----------------------- @pytest.fixture def fake_user_admin(): return UserInDB( _id=PyObjectId(), username="admin", email="admin@example.com", role=UserRole.ADMIN, is_active=True, hashed_password="hashed-secret", created_at=datetime(2025, 1, 1), updated_at=datetime(2025, 1, 2), ) @pytest.fixture def fake_user_response(): return UserInDB( _id=PyObjectId(), username="other", email="other@example.com", role=UserRole.USER, is_active=True, hashed_password="hashed-secret-2", created_at=datetime(2025, 1, 1), updated_at=datetime(2025, 1, 2), ) @pytest.fixture def client(fake_user_admin): # Fake admin dependency def get_admin_user_override(): return fake_user_admin # Fake user service user_service_mock = MagicMock(spec=UserService) def get_user_service_override(): return user_service_mock client = TestClient(app) client.app.dependency_overrides = { get_admin_user: get_admin_user_override, get_user_service: get_user_service_override } client.user_service_mock = user_service_mock return client # ----------------------- # Tests # ----------------------- class TestListUsers: def test_i_can_list_users(self, client, fake_user_admin, fake_user_response): client.user_service_mock.list_users.return_value = [fake_user_admin, fake_user_response] response = client.get("/users") assert response.status_code == status.HTTP_200_OK data = response.json() assert len(data) == 2 assert data[0]["username"] == "admin" def test_i_can_list_users_when_empty(self, client): client.user_service_mock.list_users.return_value = [] response = client.get("/users") assert response.status_code == status.HTTP_200_OK assert response.json() == [] class TestGetUserById: def test_i_can_get_user_by_id(self, client, fake_user_response): client.user_service_mock.get_user_by_id.return_value = fake_user_response response = client.get(f"/users/{fake_user_response.id}") assert response.status_code == status.HTTP_200_OK data = response.json() assert data["username"] == fake_user_response.username def test_i_cannot_get_user_by_id_not_found(self, client): client.user_service_mock.get_user_by_id.return_value = None response = client.get("/users/64f0c9f4b0d1c8b7b8e1f0a2") assert response.status_code == status.HTTP_404_NOT_FOUND assert response.json()["detail"] == "User not found" class TestCreateUser: def test_i_can_create_user(self, client, fake_user_response): user_data = UserCreate(username="newuser", email="new@example.com", password="#Passw0rd!", role=UserRole.USER) client.user_service_mock.create_user.return_value = fake_user_response response = client.post("/users", json=user_data.model_dump(mode="json")) assert response.status_code == status.HTTP_201_CREATED data = response.json() assert data["username"] == fake_user_response.username def test_i_cannot_create_user_when_service_raises_value_error(self, client): user_data = {"username": "baduser", "email": "bad@example.com", "role": "user", "password": "password"} client.user_service_mock.create_user.side_effect = ValueError("Invalid data") response = client.post("/users", json=user_data) assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY class TestUpdateUser: def test_i_can_update_user(self, client, fake_user_response): user_data = {"username": "updateduser", "email": "updated@example.com"} client.user_service_mock.update_user.return_value = fake_user_response response = client.put(f"/users/{fake_user_response.id}", json=user_data) assert response.status_code == status.HTTP_200_OK data = response.json() assert data["username"] == fake_user_response.username def test_i_cannot_update_user_not_found(self, client): client.user_service_mock.update_user.return_value = None user_data = {"username": "updateduser"} response = client.put("/users/64f0c9f4b0d1c8b7b8e1f0a2", json=user_data) assert response.status_code == status.HTTP_404_NOT_FOUND assert response.json()["detail"] == "User not found" def test_i_cannot_update_user_when_service_raises_value_error(self, client): client.user_service_mock.update_user.side_effect = ValueError("Invalid update") user_data = {"username": "badupdate"} response = client.put("/users/64f0c9f4b0d1c8b7b8e1f0a2", json=user_data) assert response.status_code == status.HTTP_400_BAD_REQUEST assert response.json()["detail"] == "Invalid update" class TestDeleteUser: def test_i_can_delete_user(self, client): client.user_service_mock.delete_user.return_value = True response = client.delete("/users/64f0c9f4b0d1c8b7b8e1f0a1") assert response.status_code == status.HTTP_200_OK data = response.json() assert data["message"] == "User successfully deleted" def test_i_cannot_delete_user_not_found(self, client): client.user_service_mock.delete_user.return_value = False response = client.delete("/users/64f0c9f4b0d1c8b7b8e1f0a2") assert response.status_code == status.HTTP_404_NOT_FOUND assert response.json()["detail"] == "User not found"