diff --git a/src/file-processor/app/api/dependencies.py b/src/file-processor/app/api/dependencies.py index cf930b9..e52a3de 100644 --- a/src/file-processor/app/api/dependencies.py +++ b/src/file-processor/app/api/dependencies.py @@ -79,7 +79,7 @@ def get_current_user( return user -def get_admin_user(current_user: UserInDB = Depends(get_current_user)) -> UserInDB: +def get_admin_user(current_user: UserInDB = Depends(get_current_user)) -> UserInDB: """ Dependency to ensure current user has admin role. diff --git a/tests/api/test_users.py b/tests/api/test_users.py new file mode 100644 index 0000000..8e359bf --- /dev/null +++ b/tests/api/test_users.py @@ -0,0 +1,167 @@ +# 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"