Files
webref/backend/tests/api/test_image_position.py
Danilo Reyes cd8ce33f5e
All checks were successful
CI/CD Pipeline / VM Test - backend-integration (push) Successful in 8s
CI/CD Pipeline / VM Test - full-stack (push) Successful in 7s
CI/CD Pipeline / VM Test - performance (push) Successful in 8s
CI/CD Pipeline / VM Test - security (push) Successful in 7s
CI/CD Pipeline / Backend Linting (push) Successful in 3s
CI/CD Pipeline / Frontend Linting (push) Successful in 15s
CI/CD Pipeline / Nix Flake Check (push) Successful in 36s
CI/CD Pipeline / CI Summary (push) Successful in 0s
phase 7
2025-11-02 14:07:13 -06:00

455 lines
12 KiB
Python

"""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