"""Integration tests for group endpoints.""" from uuid import uuid4 import pytest from httpx import AsyncClient from sqlalchemy.orm import Session 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 pytestmark = pytest.mark.asyncio async def test_create_group(client: AsyncClient, test_user: User, db: Session): """Test creating a group with images.""" # Create 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 images images = [] for i in range(3): image = Image( id=uuid4(), user_id=test_user.id, filename=f"test{i}.jpg", storage_path=f"{test_user.id}/test{i}.jpg", file_size=1024, mime_type="image/jpeg", width=800, height=600, metadata={"format": "jpeg", "checksum": f"abc{i}"}, ) db.add(image) images.append(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}, z_order=i, ) db.add(board_image) db.commit() # Create group response = await client.post( f"/api/boards/{board.id}/groups", json={ "name": "Test Group", "color": "#FF5733", "annotation": "Group annotation", "image_ids": [str(img.id) for img in images[:2]], }, ) assert response.status_code == 201 data = response.json() assert data["name"] == "Test Group" assert data["color"] == "#FF5733" assert data["annotation"] == "Group annotation" assert data["member_count"] == 2 async def test_list_groups(client: AsyncClient, test_user: User, db: Session): """Test listing groups on a board.""" from app.database.models.group import Group 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 groups for i in range(3): group = Group( id=uuid4(), board_id=board.id, name=f"Group {i}", color=f"#FF573{i}", annotation=f"Annotation {i}", ) db.add(group) db.commit() # List groups response = await client.get(f"/api/boards/{board.id}/groups") assert response.status_code == 200 data = response.json() assert len(data) == 3 assert data[0]["name"] == "Group 2" # Most recent first async def test_get_group(client: AsyncClient, test_user: User, db: Session): """Test getting a specific group.""" from app.database.models.group import Group 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) group = Group( id=uuid4(), board_id=board.id, name="Test Group", color="#FF5733", annotation="Test annotation", ) db.add(group) db.commit() # Get group response = await client.get(f"/api/boards/{board.id}/groups/{group.id}") assert response.status_code == 200 data = response.json() assert data["name"] == "Test Group" assert data["color"] == "#FF5733" async def test_update_group(client: AsyncClient, test_user: User, db: Session): """Test updating group metadata.""" from app.database.models.group import Group 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) group = Group( id=uuid4(), board_id=board.id, name="Original Name", color="#FF5733", annotation="Original annotation", ) db.add(group) db.commit() # Update group response = await client.patch( f"/api/boards/{board.id}/groups/{group.id}", json={ "name": "Updated Name", "color": "#00FF00", "annotation": "Updated annotation", }, ) assert response.status_code == 200 data = response.json() assert data["name"] == "Updated Name" assert data["color"] == "#00FF00" assert data["annotation"] == "Updated annotation" async def test_delete_group(client: AsyncClient, test_user: User, db: Session): """Test deleting a group.""" from app.database.models.group import Group 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 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": "abc"}, ) db.add(image) # Create group group = Group( id=uuid4(), board_id=board.id, name="Test Group", color="#FF5733", ) db.add(group) # Add image to board and group 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}, z_order=0, group_id=group.id, ) db.add(board_image) db.commit() # Delete group response = await client.delete(f"/api/boards/{board.id}/groups/{group.id}") assert response.status_code == 204 # Verify image is ungrouped db.refresh(board_image) assert board_image.group_id is None async def test_group_unauthorized_board(client: AsyncClient, test_user: User, db: Session): """Test that users can't create groups 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 Board", viewport_state={"x": 0, "y": 0, "zoom": 1.0, "rotation": 0}, ) db.add(board) db.commit() # Try to create group response = await client.post( f"/api/boards/{board.id}/groups", json={ "name": "Test Group", "color": "#FF5733", "image_ids": [str(uuid4())], }, ) assert response.status_code == 404 # Board not found (for security) async def test_invalid_color_format(client: AsyncClient, test_user: User, db: Session): """Test that invalid color formats are rejected.""" 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) db.commit() # Try with invalid color response = await client.post( f"/api/boards/{board.id}/groups", json={ "name": "Test Group", "color": "red", # Invalid: not hex "image_ids": [str(uuid4())], }, ) assert response.status_code == 422