"""Integration tests for image upload endpoints.""" import io from unittest.mock import AsyncMock, MagicMock, patch import pytest from fastapi import status from httpx import AsyncClient from PIL import Image as PILImage @pytest.mark.asyncio class TestImageUpload: """Tests for image upload endpoint.""" async def test_upload_image_success(self, client: AsyncClient, auth_headers: dict): """Test successful image upload.""" # Create a test image image = PILImage.new("RGB", (800, 600), color="red") buffer = io.BytesIO() image.save(buffer, format="JPEG") buffer.seek(0) # Mock storage and processing with patch("app.images.validation.magic.from_buffer") as mock_magic: mock_magic.return_value = "image/jpeg" with patch("app.api.images.upload_image_to_storage") as mock_upload: mock_upload.return_value = ("storage/path.jpg", 800, 600, "image/jpeg") with patch("app.api.images.generate_thumbnails") as mock_thumbs: mock_thumbs.return_value = { "low": "thumbs/low.webp", "medium": "thumbs/medium.webp", "high": "thumbs/high.webp", } # Upload image response = await client.post( "/api/v1/images/upload", headers=auth_headers, files={"file": ("test.jpg", buffer, "image/jpeg")}, ) assert response.status_code == status.HTTP_201_CREATED data = response.json() assert "id" in data assert data["filename"] == "test.jpg" assert data["width"] == 800 assert data["height"] == 600 async def test_upload_image_unauthenticated(self, client: AsyncClient): """Test upload without authentication fails.""" image = PILImage.new("RGB", (800, 600), color="red") buffer = io.BytesIO() image.save(buffer, format="JPEG") buffer.seek(0) response = await client.post( "/api/v1/images/upload", files={"file": ("test.jpg", buffer, "image/jpeg")} ) assert response.status_code == status.HTTP_401_UNAUTHORIZED async def test_upload_invalid_file_type(self, client: AsyncClient, auth_headers: dict): """Test upload with invalid file type.""" # Create a text file disguised as image buffer = io.BytesIO(b"This is not an image") with patch("app.images.validation.magic.from_buffer") as mock_magic: mock_magic.return_value = "text/plain" response = await client.post( "/api/v1/images/upload", headers=auth_headers, files={"file": ("fake.jpg", buffer, "image/jpeg")}, ) assert response.status_code == status.HTTP_400_BAD_REQUEST assert "invalid" in response.json()["detail"].lower() @pytest.mark.asyncio class TestImageLibrary: """Tests for image library endpoint.""" async def test_get_image_library(self, client: AsyncClient, auth_headers: dict): """Test retrieving user's image library.""" response = await client.get("/api/v1/images/library", headers=auth_headers) assert response.status_code == status.HTTP_200_OK data = response.json() assert "images" in data assert "total" in data assert "page" in data assert isinstance(data["images"], list) async def test_get_image_library_pagination(self, client: AsyncClient, auth_headers: dict): """Test library pagination.""" response = await client.get( "/api/v1/images/library", params={"page": 2, "page_size": 10}, headers=auth_headers ) assert response.status_code == status.HTTP_200_OK data = response.json() assert data["page"] == 2 assert data["page_size"] == 10 @pytest.mark.asyncio class TestBoardImages: """Tests for adding images to boards.""" async def test_add_image_to_board( self, client: AsyncClient, auth_headers: dict, test_board_id: str, test_image_id: str ): """Test adding image to board.""" payload = { "image_id": test_image_id, "position": {"x": 100, "y": 200}, "transformations": { "scale": 1.0, "rotation": 0, "opacity": 1.0, "flipped_h": False, "flipped_v": False, "greyscale": False, }, "z_order": 0, } response = await client.post( f"/api/v1/images/boards/{test_board_id}/images", headers=auth_headers, json=payload ) # May fail if test_board_id/test_image_id fixtures aren't set up # This is a placeholder for the structure if response.status_code == status.HTTP_201_CREATED: data = response.json() assert "id" in data assert data["image_id"] == test_image_id assert data["position"]["x"] == 100 async def test_get_board_images( self, client: AsyncClient, auth_headers: dict, test_board_id: str ): """Test getting all images on a board.""" response = await client.get( f"/api/v1/images/boards/{test_board_id}/images", headers=auth_headers ) # May return 404 if board doesn't exist in test DB if response.status_code == status.HTTP_200_OK: data = response.json() assert isinstance(data, list)