phase 8
This commit is contained in:
236
backend/tests/images/test_transformations.py
Normal file
236
backend/tests/images/test_transformations.py
Normal file
@@ -0,0 +1,236 @@
|
||||
"""Tests for image transformation validation."""
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from app.images.schemas import BoardImageUpdate
|
||||
|
||||
|
||||
def test_valid_transformations():
|
||||
"""Test that valid transformations are accepted."""
|
||||
data = BoardImageUpdate(
|
||||
transformations={
|
||||
"scale": 1.5,
|
||||
"rotation": 45,
|
||||
"opacity": 0.8,
|
||||
"flipped_h": True,
|
||||
"flipped_v": False,
|
||||
"greyscale": False,
|
||||
}
|
||||
)
|
||||
|
||||
assert data.transformations is not None
|
||||
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 False
|
||||
|
||||
|
||||
def test_minimal_transformations():
|
||||
"""Test that minimal transformation data is accepted."""
|
||||
data = BoardImageUpdate(
|
||||
transformations={
|
||||
"scale": 1.0,
|
||||
"rotation": 0,
|
||||
"opacity": 1.0,
|
||||
}
|
||||
)
|
||||
|
||||
assert data.transformations is not None
|
||||
|
||||
|
||||
def test_transformation_scale_bounds():
|
||||
"""Test scale bounds validation."""
|
||||
# Valid scales
|
||||
valid_scales = [0.01, 0.5, 1.0, 5.0, 10.0]
|
||||
|
||||
for scale in valid_scales:
|
||||
data = BoardImageUpdate(transformations={"scale": scale})
|
||||
assert data.transformations["scale"] == scale
|
||||
|
||||
|
||||
def test_transformation_rotation_bounds():
|
||||
"""Test rotation bounds (any value allowed, normalized client-side)."""
|
||||
# Various rotation values
|
||||
rotations = [0, 45, 90, 180, 270, 360, 450, -90]
|
||||
|
||||
for rotation in rotations:
|
||||
data = BoardImageUpdate(transformations={"rotation": rotation})
|
||||
assert data.transformations["rotation"] == rotation
|
||||
|
||||
|
||||
def test_transformation_opacity_bounds():
|
||||
"""Test opacity bounds."""
|
||||
# Valid opacity values
|
||||
valid_opacities = [0.0, 0.25, 0.5, 0.75, 1.0]
|
||||
|
||||
for opacity in valid_opacities:
|
||||
data = BoardImageUpdate(transformations={"opacity": opacity})
|
||||
assert data.transformations["opacity"] == opacity
|
||||
|
||||
|
||||
def test_transformation_boolean_flags():
|
||||
"""Test boolean transformation flags."""
|
||||
data = BoardImageUpdate(
|
||||
transformations={
|
||||
"flipped_h": True,
|
||||
"flipped_v": True,
|
||||
"greyscale": True,
|
||||
}
|
||||
)
|
||||
|
||||
assert data.transformations["flipped_h"] is True
|
||||
assert data.transformations["flipped_v"] is True
|
||||
assert data.transformations["greyscale"] is True
|
||||
|
||||
|
||||
def test_transformation_crop_data():
|
||||
"""Test crop transformation data."""
|
||||
data = BoardImageUpdate(
|
||||
transformations={
|
||||
"crop": {
|
||||
"x": 10,
|
||||
"y": 10,
|
||||
"width": 100,
|
||||
"height": 100,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
assert data.transformations["crop"] is not None
|
||||
assert data.transformations["crop"]["x"] == 10
|
||||
assert data.transformations["crop"]["width"] == 100
|
||||
|
||||
|
||||
def test_transformation_null_crop():
|
||||
"""Test that crop can be null (no crop)."""
|
||||
data = BoardImageUpdate(
|
||||
transformations={
|
||||
"crop": None,
|
||||
}
|
||||
)
|
||||
|
||||
assert data.transformations["crop"] is None
|
||||
|
||||
|
||||
def test_partial_transformation_update():
|
||||
"""Test updating only some transformation fields."""
|
||||
# Only update scale
|
||||
data = BoardImageUpdate(transformations={"scale": 2.0})
|
||||
assert data.transformations["scale"] == 2.0
|
||||
|
||||
# Only update rotation
|
||||
data = BoardImageUpdate(transformations={"rotation": 90})
|
||||
assert data.transformations["rotation"] == 90
|
||||
|
||||
# Only update opacity
|
||||
data = BoardImageUpdate(transformations={"opacity": 0.5})
|
||||
assert data.transformations["opacity"] == 0.5
|
||||
|
||||
|
||||
def test_complete_transformation_update():
|
||||
"""Test updating all transformation fields."""
|
||||
data = BoardImageUpdate(
|
||||
transformations={
|
||||
"scale": 1.5,
|
||||
"rotation": 45,
|
||||
"opacity": 0.8,
|
||||
"flipped_h": True,
|
||||
"flipped_v": False,
|
||||
"greyscale": True,
|
||||
"crop": {
|
||||
"x": 20,
|
||||
"y": 20,
|
||||
"width": 150,
|
||||
"height": 150,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
assert data.transformations is not None
|
||||
assert len(data.transformations) == 7
|
||||
|
||||
|
||||
def test_position_validation_with_transformations():
|
||||
"""Test that position and transformations can be updated together."""
|
||||
data = BoardImageUpdate(
|
||||
position={"x": 100, "y": 200},
|
||||
transformations={"scale": 1.5, "rotation": 45},
|
||||
)
|
||||
|
||||
assert data.position == {"x": 100, "y": 200}
|
||||
assert data.transformations["scale"] == 1.5
|
||||
assert data.transformations["rotation"] == 45
|
||||
|
||||
|
||||
def test_invalid_position_missing_x():
|
||||
"""Test that position without x coordinate is rejected."""
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
BoardImageUpdate(position={"y": 100})
|
||||
|
||||
assert "must contain 'x' and 'y'" in str(exc_info.value)
|
||||
|
||||
|
||||
def test_invalid_position_missing_y():
|
||||
"""Test that position without y coordinate is rejected."""
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
BoardImageUpdate(position={"x": 100})
|
||||
|
||||
assert "must contain 'x' and 'y'" in str(exc_info.value)
|
||||
|
||||
|
||||
def test_z_order_update():
|
||||
"""Test Z-order update."""
|
||||
data = BoardImageUpdate(z_order=5)
|
||||
assert data.z_order == 5
|
||||
|
||||
# Negative Z-order allowed (layering)
|
||||
data = BoardImageUpdate(z_order=-1)
|
||||
assert data.z_order == -1
|
||||
|
||||
# Large Z-order allowed
|
||||
data = BoardImageUpdate(z_order=999999)
|
||||
assert data.z_order == 999999
|
||||
|
||||
|
||||
def test_group_id_update():
|
||||
"""Test group ID update."""
|
||||
from uuid import uuid4
|
||||
|
||||
group_id = uuid4()
|
||||
data = BoardImageUpdate(group_id=group_id)
|
||||
assert data.group_id == group_id
|
||||
|
||||
# Null group ID (remove from group)
|
||||
data = BoardImageUpdate(group_id=None)
|
||||
assert data.group_id is None
|
||||
|
||||
|
||||
def test_empty_update():
|
||||
"""Test that empty update (no fields) is valid."""
|
||||
data = BoardImageUpdate()
|
||||
|
||||
assert data.position is None
|
||||
assert data.transformations is None
|
||||
assert data.z_order is None
|
||||
assert data.group_id is None
|
||||
|
||||
|
||||
def test_transformation_data_types():
|
||||
"""Test that transformation data types are validated."""
|
||||
# Valid types
|
||||
data = BoardImageUpdate(
|
||||
transformations={
|
||||
"scale": 1.5, # float
|
||||
"rotation": 45, # int (converted to float)
|
||||
"opacity": 0.8, # float
|
||||
"flipped_h": True, # bool
|
||||
"flipped_v": False, # bool
|
||||
"greyscale": True, # bool
|
||||
}
|
||||
)
|
||||
|
||||
assert isinstance(data.transformations["scale"], (int, float))
|
||||
assert isinstance(data.transformations["flipped_h"], bool)
|
||||
|
||||
Reference in New Issue
Block a user