XmLLM / tests /unit /test_geometry.py
Claude
Code quality: fix all ruff warnings, add CI/CD, improve test coverage
bbbfba8 unverified
"""Tests for geometry primitives and Geometry model."""
from __future__ import annotations
import pytest
from pydantic import ValidationError
from src.app.domain.models import (
Baseline,
BBox,
ClipRect,
Geometry,
GeometryContext,
GeometryStatus,
Point,
)
class TestPoint:
def test_valid(self) -> None:
p = Point(x=10.0, y=20.0)
assert p.x == 10.0
assert p.y == 20.0
def test_origin(self) -> None:
p = Point(x=0, y=0)
assert p.x == 0.0
def test_negative_x_rejected(self) -> None:
with pytest.raises(ValidationError):
Point(x=-1, y=0)
def test_negative_y_rejected(self) -> None:
with pytest.raises(ValidationError):
Point(x=0, y=-1)
def test_frozen(self) -> None:
p = Point(x=1, y=2)
with pytest.raises(ValidationError):
p.x = 5 # type: ignore[misc]
class TestBBox:
def test_valid(self) -> None:
b = BBox(x=10, y=20, width=100, height=50)
assert b.x2 == 110.0
assert b.y2 == 70.0
def test_as_tuple(self) -> None:
b = BBox(x=10, y=20, width=100, height=50)
assert b.as_tuple() == (10.0, 20.0, 100.0, 50.0)
def test_zero_width_rejected(self) -> None:
with pytest.raises(ValidationError):
BBox(x=0, y=0, width=0, height=10)
def test_zero_height_rejected(self) -> None:
with pytest.raises(ValidationError):
BBox(x=0, y=0, width=10, height=0)
def test_negative_x_rejected(self) -> None:
with pytest.raises(ValidationError):
BBox(x=-1, y=0, width=10, height=10)
class TestBaseline:
def test_valid(self) -> None:
bl = Baseline(start=Point(x=0, y=100), end=Point(x=200, y=100))
assert bl.start.x == 0
assert bl.end.x == 200
class TestClipRect:
def test_valid(self) -> None:
cr = ClipRect(x=0, y=0, width=100, height=100)
assert cr.width == 100
def test_zero_dimension_rejected(self) -> None:
with pytest.raises(ValidationError):
ClipRect(x=0, y=0, width=0, height=100)
class TestGeometry:
def test_valid_exact(self) -> None:
g = Geometry(bbox=(100, 200, 300, 50), status=GeometryStatus.EXACT)
assert g.bbox == (100, 200, 300, 50)
assert g.polygon is None
def test_valid_with_polygon(self) -> None:
g = Geometry(
bbox=(100, 200, 300, 50),
polygon=[(100, 200), (400, 200), (400, 250), (100, 250)],
status=GeometryStatus.EXACT,
)
assert len(g.polygon) == 4
def test_zero_width_rejected(self) -> None:
with pytest.raises(ValidationError, match="width and height must be > 0"):
Geometry(bbox=(100, 200, 0, 50), status=GeometryStatus.EXACT)
def test_zero_height_rejected(self) -> None:
with pytest.raises(ValidationError, match="width and height must be > 0"):
Geometry(bbox=(100, 200, 300, 0), status=GeometryStatus.EXACT)
def test_negative_x_rejected(self) -> None:
with pytest.raises(ValidationError, match="x and y must be >= 0"):
Geometry(bbox=(-1, 200, 300, 50), status=GeometryStatus.EXACT)
def test_negative_y_rejected(self) -> None:
with pytest.raises(ValidationError, match="x and y must be >= 0"):
Geometry(bbox=(100, -1, 300, 50), status=GeometryStatus.EXACT)
def test_all_statuses_accepted(self) -> None:
for status in GeometryStatus:
g = Geometry(bbox=(10, 10, 10, 10), status=status)
assert g.status == status
def test_frozen(self) -> None:
g = Geometry(bbox=(10, 20, 30, 40), status=GeometryStatus.EXACT)
with pytest.raises(ValidationError):
g.status = GeometryStatus.INFERRED # type: ignore[misc]
class TestGeometryContext:
def test_valid_minimal(self) -> None:
gc = GeometryContext(source_width=2480, source_height=3508)
assert gc.source_width == 2480
assert gc.resize_factor is None
def test_valid_full(self) -> None:
gc = GeometryContext(
source_width=2480,
source_height=3508,
provided_width=1240,
provided_height=1754,
resize_factor=0.5,
rotation=90.0,
)
assert gc.resize_factor == 0.5
def test_zero_source_rejected(self) -> None:
with pytest.raises(ValidationError):
GeometryContext(source_width=0, source_height=100)