"""Integration tests for image position update endpoint.""" import pytest from httpx import AsyncClient from sqlalchemy.ext.asyncio import AsyncSession from uuid import uuid4 from app.database.models.user import User from app.database.models.board import Board from app.database.models.image import Image from app.database.models.board_image import BoardImage @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