"""Integration tests for image position update endpoint.""" from uuid import uuid4 import pytest from httpx import AsyncClient from sqlalchemy.ext.asyncio import AsyncSession from app.database.models.board import Board from app.database.models.board_image import BoardImage from app.database.models.image import Image from app.database.models.user import User @pytest.mark.asyncio async def test_update_image_position(client: AsyncClient, test_user: User, db: AsyncSession): """Test updating image position on board.""" # Create a board board = Board( id=uuid4(), user_id=test_user.id, title="Test Board", viewport_state={"x": 0, "y": 0, "zoom": 1.0, "rotation": 0}, ) db.add(board) # Create an image image = Image( id=uuid4(), user_id=test_user.id, filename="test.jpg", storage_path=f"{test_user.id}/test.jpg", file_size=1024, mime_type="image/jpeg", width=800, height=600, metadata={"format": "jpeg", "checksum": "abc123"}, ) db.add(image) # Add image to board board_image = BoardImage( id=uuid4(), board_id=board.id, image_id=image.id, position={"x": 100, "y": 100}, transformations={ "scale": 1.0, "rotation": 0, "opacity": 1.0, "flipped_h": False, "flipped_v": False, "greyscale": False, }, z_order=0, ) db.add(board_image) await db.commit() # Update position response = await client.patch( f"/api/images/boards/{board.id}/images/{image.id}", json={"position": {"x": 200, "y": 250}}, ) assert response.status_code == 200 data = response.json() assert data["position"]["x"] == 200 assert data["position"]["y"] == 250 @pytest.mark.asyncio async def test_update_image_transformations(client: AsyncClient, test_user: User, db: AsyncSession): """Test updating image transformations.""" # Create board, image, and board_image board = Board( id=uuid4(), user_id=test_user.id, title="Test Board", viewport_state={"x": 0, "y": 0, "zoom": 1.0, "rotation": 0}, ) db.add(board) image = Image( id=uuid4(), user_id=test_user.id, filename="test.jpg", storage_path=f"{test_user.id}/test.jpg", file_size=1024, mime_type="image/jpeg", width=800, height=600, metadata={"format": "jpeg", "checksum": "abc123"}, ) db.add(image) board_image = BoardImage( id=uuid4(), board_id=board.id, image_id=image.id, position={"x": 100, "y": 100}, transformations={ "scale": 1.0, "rotation": 0, "opacity": 1.0, "flipped_h": False, "flipped_v": False, "greyscale": False, }, z_order=0, ) db.add(board_image) await db.commit() # Update transformations response = await client.patch( f"/api/images/boards/{board.id}/images/{image.id}", json={ "transformations": { "scale": 1.5, "rotation": 45, "opacity": 0.8, "flipped_h": True, "flipped_v": False, "greyscale": True, } }, ) assert response.status_code == 200 data = response.json() assert data["transformations"]["scale"] == 1.5 assert data["transformations"]["rotation"] == 45 assert data["transformations"]["opacity"] == 0.8 assert data["transformations"]["flipped_h"] is True assert data["transformations"]["greyscale"] is True @pytest.mark.asyncio async def test_update_image_z_order(client: AsyncClient, test_user: User, db: AsyncSession): """Test updating image Z-order.""" board = Board( id=uuid4(), user_id=test_user.id, title="Test Board", viewport_state={"x": 0, "y": 0, "zoom": 1.0, "rotation": 0}, ) db.add(board) image = Image( id=uuid4(), user_id=test_user.id, filename="test.jpg", storage_path=f"{test_user.id}/test.jpg", file_size=1024, mime_type="image/jpeg", width=800, height=600, metadata={"format": "jpeg", "checksum": "abc123"}, ) db.add(image) board_image = BoardImage( id=uuid4(), board_id=board.id, image_id=image.id, position={"x": 100, "y": 100}, transformations={ "scale": 1.0, "rotation": 0, "opacity": 1.0, "flipped_h": False, "flipped_v": False, "greyscale": False, }, z_order=0, ) db.add(board_image) await db.commit() # Update Z-order response = await client.patch( f"/api/images/boards/{board.id}/images/{image.id}", json={"z_order": 5}, ) assert response.status_code == 200 data = response.json() assert data["z_order"] == 5 @pytest.mark.asyncio async def test_update_multiple_fields(client: AsyncClient, test_user: User, db: AsyncSession): """Test updating position, transformations, and z-order together.""" board = Board( id=uuid4(), user_id=test_user.id, title="Test Board", viewport_state={"x": 0, "y": 0, "zoom": 1.0, "rotation": 0}, ) db.add(board) image = Image( id=uuid4(), user_id=test_user.id, filename="test.jpg", storage_path=f"{test_user.id}/test.jpg", file_size=1024, mime_type="image/jpeg", width=800, height=600, metadata={"format": "jpeg", "checksum": "abc123"}, ) db.add(image) board_image = BoardImage( id=uuid4(), board_id=board.id, image_id=image.id, position={"x": 100, "y": 100}, transformations={ "scale": 1.0, "rotation": 0, "opacity": 1.0, "flipped_h": False, "flipped_v": False, "greyscale": False, }, z_order=0, ) db.add(board_image) await db.commit() # Update everything response = await client.patch( f"/api/images/boards/{board.id}/images/{image.id}", json={ "position": {"x": 300, "y": 400}, "transformations": {"scale": 2.0, "rotation": 90}, "z_order": 10, }, ) assert response.status_code == 200 data = response.json() assert data["position"]["x"] == 300 assert data["position"]["y"] == 400 assert data["transformations"]["scale"] == 2.0 assert data["transformations"]["rotation"] == 90 assert data["z_order"] == 10 @pytest.mark.asyncio async def test_update_image_not_on_board(client: AsyncClient, test_user: User, db: AsyncSession): """Test updating image that's not on the specified board.""" board = Board( id=uuid4(), user_id=test_user.id, title="Test Board", viewport_state={"x": 0, "y": 0, "zoom": 1.0, "rotation": 0}, ) db.add(board) image = Image( id=uuid4(), user_id=test_user.id, filename="test.jpg", storage_path=f"{test_user.id}/test.jpg", file_size=1024, mime_type="image/jpeg", width=800, height=600, metadata={"format": "jpeg", "checksum": "abc123"}, ) db.add(image) await db.commit() # Try to update image that's not on board response = await client.patch( f"/api/images/boards/{board.id}/images/{image.id}", json={"position": {"x": 200, "y": 200}}, ) assert response.status_code == 404 assert "not on this board" in response.json()["detail"].lower() @pytest.mark.asyncio async def test_update_image_invalid_position(client: AsyncClient, test_user: User, db: AsyncSession): """Test updating with invalid position data.""" board = Board( id=uuid4(), user_id=test_user.id, title="Test Board", viewport_state={"x": 0, "y": 0, "zoom": 1.0, "rotation": 0}, ) db.add(board) image = Image( id=uuid4(), user_id=test_user.id, filename="test.jpg", storage_path=f"{test_user.id}/test.jpg", file_size=1024, mime_type="image/jpeg", width=800, height=600, metadata={"format": "jpeg", "checksum": "abc123"}, ) db.add(image) board_image = BoardImage( id=uuid4(), board_id=board.id, image_id=image.id, position={"x": 100, "y": 100}, transformations={ "scale": 1.0, "rotation": 0, "opacity": 1.0, "flipped_h": False, "flipped_v": False, "greyscale": False, }, z_order=0, ) db.add(board_image) await db.commit() # Try to update with missing y coordinate response = await client.patch( f"/api/images/boards/{board.id}/images/{image.id}", json={"position": {"x": 200}}, ) assert response.status_code == 422 @pytest.mark.asyncio async def test_update_image_unauthorized(client: AsyncClient, test_user: User, db: AsyncSession): """Test that other users cannot update images on boards they don't own.""" # Create another user other_user = User(id=uuid4(), email="other@example.com", password_hash="hashed") db.add(other_user) # Create board owned by other user board = Board( id=uuid4(), user_id=other_user.id, title="Other User's Board", viewport_state={"x": 0, "y": 0, "zoom": 1.0, "rotation": 0}, ) db.add(board) image = Image( id=uuid4(), user_id=other_user.id, filename="test.jpg", storage_path=f"{other_user.id}/test.jpg", file_size=1024, mime_type="image/jpeg", width=800, height=600, metadata={"format": "jpeg", "checksum": "abc123"}, ) db.add(image) board_image = BoardImage( id=uuid4(), board_id=board.id, image_id=image.id, position={"x": 100, "y": 100}, transformations={ "scale": 1.0, "rotation": 0, "opacity": 1.0, "flipped_h": False, "flipped_v": False, "greyscale": False, }, z_order=0, ) db.add(board_image) await db.commit() # Try to update as current user (should fail) response = await client.patch( f"/api/images/boards/{board.id}/images/{image.id}", json={"position": {"x": 200, "y": 200}}, ) assert response.status_code == 403 @pytest.mark.asyncio async def test_update_preserves_other_fields(client: AsyncClient, test_user: User, db: AsyncSession): """Test that updating one field preserves others.""" board = Board( id=uuid4(), user_id=test_user.id, title="Test Board", viewport_state={"x": 0, "y": 0, "zoom": 1.0, "rotation": 0}, ) db.add(board) image = Image( id=uuid4(), user_id=test_user.id, filename="test.jpg", storage_path=f"{test_user.id}/test.jpg", file_size=1024, mime_type="image/jpeg", width=800, height=600, metadata={"format": "jpeg", "checksum": "abc123"}, ) db.add(image) board_image = BoardImage( id=uuid4(), board_id=board.id, image_id=image.id, position={"x": 100, "y": 100}, transformations={ "scale": 1.5, "rotation": 45, "opacity": 0.9, "flipped_h": True, "flipped_v": False, "greyscale": False, }, z_order=3, ) db.add(board_image) await db.commit() # Update only position response = await client.patch( f"/api/images/boards/{board.id}/images/{image.id}", json={"position": {"x": 200, "y": 200}}, ) assert response.status_code == 200 data = response.json() # Position should be updated assert data["position"]["x"] == 200 assert data["position"]["y"] == 200 # Other fields should be preserved assert data["transformations"]["scale"] == 1.5 assert data["transformations"]["rotation"] == 45 assert data["transformations"]["opacity"] == 0.9 assert data["z_order"] == 3