WIP
This commit is contained in:
197
tests/web/test_admin_annotations.py
Normal file
197
tests/web/test_admin_annotations.py
Normal file
@@ -0,0 +1,197 @@
|
||||
"""
|
||||
Tests for Admin Annotation Routes.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from datetime import datetime
|
||||
from unittest.mock import MagicMock, patch
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import HTTPException
|
||||
|
||||
from src.data.admin_models import AdminAnnotation, AdminDocument, FIELD_CLASSES
|
||||
from src.web.api.v1.admin.annotations import _validate_uuid, create_annotation_router
|
||||
from src.web.schemas.admin import (
|
||||
AnnotationCreate,
|
||||
AnnotationUpdate,
|
||||
AutoLabelRequest,
|
||||
BoundingBox,
|
||||
)
|
||||
|
||||
|
||||
# Test UUIDs
|
||||
TEST_DOC_UUID = "550e8400-e29b-41d4-a716-446655440000"
|
||||
TEST_ANN_UUID = "660e8400-e29b-41d4-a716-446655440001"
|
||||
TEST_TOKEN = "test-admin-token-12345"
|
||||
|
||||
|
||||
class TestAnnotationRouterCreation:
|
||||
"""Tests for annotation router creation."""
|
||||
|
||||
def test_creates_router_with_endpoints(self):
|
||||
"""Test router is created with expected endpoints."""
|
||||
router = create_annotation_router()
|
||||
|
||||
# Get route paths (includes prefix)
|
||||
paths = [route.path for route in router.routes]
|
||||
|
||||
# Paths include the /admin/documents prefix
|
||||
assert any("{document_id}/annotations" in p for p in paths)
|
||||
assert any("{annotation_id}" in p for p in paths)
|
||||
assert any("auto-label" in p for p in paths)
|
||||
assert any("images" in p for p in paths)
|
||||
|
||||
|
||||
class TestAnnotationCreateSchema:
|
||||
"""Tests for AnnotationCreate schema."""
|
||||
|
||||
def test_valid_annotation(self):
|
||||
"""Test valid annotation creation."""
|
||||
ann = AnnotationCreate(
|
||||
page_number=1,
|
||||
class_id=0,
|
||||
bbox=BoundingBox(x=100, y=100, width=200, height=50),
|
||||
text_value="12345",
|
||||
)
|
||||
|
||||
assert ann.page_number == 1
|
||||
assert ann.class_id == 0
|
||||
assert ann.bbox.x == 100
|
||||
assert ann.text_value == "12345"
|
||||
|
||||
def test_class_id_range(self):
|
||||
"""Test class_id must be 0-9."""
|
||||
# Valid class IDs
|
||||
for class_id in range(10):
|
||||
ann = AnnotationCreate(
|
||||
page_number=1,
|
||||
class_id=class_id,
|
||||
bbox=BoundingBox(x=0, y=0, width=100, height=50),
|
||||
)
|
||||
assert ann.class_id == class_id
|
||||
|
||||
def test_bbox_validation(self):
|
||||
"""Test bounding box validation."""
|
||||
bbox = BoundingBox(x=0, y=0, width=100, height=50)
|
||||
assert bbox.width >= 1
|
||||
assert bbox.height >= 1
|
||||
|
||||
|
||||
class TestAnnotationUpdateSchema:
|
||||
"""Tests for AnnotationUpdate schema."""
|
||||
|
||||
def test_partial_update(self):
|
||||
"""Test partial update with only some fields."""
|
||||
update = AnnotationUpdate(
|
||||
text_value="new value",
|
||||
)
|
||||
|
||||
assert update.text_value == "new value"
|
||||
assert update.class_id is None
|
||||
assert update.bbox is None
|
||||
|
||||
def test_bbox_update(self):
|
||||
"""Test bounding box update."""
|
||||
update = AnnotationUpdate(
|
||||
bbox=BoundingBox(x=50, y=50, width=150, height=75),
|
||||
)
|
||||
|
||||
assert update.bbox.x == 50
|
||||
assert update.bbox.width == 150
|
||||
|
||||
|
||||
class TestAutoLabelRequestSchema:
|
||||
"""Tests for AutoLabelRequest schema."""
|
||||
|
||||
def test_valid_request(self):
|
||||
"""Test valid auto-label request."""
|
||||
request = AutoLabelRequest(
|
||||
field_values={
|
||||
"InvoiceNumber": "12345",
|
||||
"Amount": "1000.00",
|
||||
},
|
||||
replace_existing=True,
|
||||
)
|
||||
|
||||
assert len(request.field_values) == 2
|
||||
assert request.field_values["InvoiceNumber"] == "12345"
|
||||
assert request.replace_existing is True
|
||||
|
||||
def test_requires_field_values(self):
|
||||
"""Test that field_values is required."""
|
||||
with pytest.raises(Exception):
|
||||
AutoLabelRequest(replace_existing=True)
|
||||
|
||||
|
||||
class TestFieldClasses:
|
||||
"""Tests for field class mapping."""
|
||||
|
||||
def test_all_classes_defined(self):
|
||||
"""Test all 10 field classes are defined."""
|
||||
assert len(FIELD_CLASSES) == 10
|
||||
|
||||
def test_class_ids_sequential(self):
|
||||
"""Test class IDs are 0-9."""
|
||||
assert set(FIELD_CLASSES.keys()) == set(range(10))
|
||||
|
||||
def test_known_field_names(self):
|
||||
"""Test known field names are present."""
|
||||
names = list(FIELD_CLASSES.values())
|
||||
|
||||
assert "invoice_number" in names
|
||||
assert "invoice_date" in names
|
||||
assert "amount" in names
|
||||
assert "bankgiro" in names
|
||||
assert "ocr_number" in names
|
||||
|
||||
|
||||
class TestAnnotationModel:
|
||||
"""Tests for AdminAnnotation model."""
|
||||
|
||||
def test_annotation_creation(self):
|
||||
"""Test annotation model creation."""
|
||||
ann = AdminAnnotation(
|
||||
document_id=UUID(TEST_DOC_UUID),
|
||||
page_number=1,
|
||||
class_id=0,
|
||||
class_name="invoice_number",
|
||||
x_center=0.5,
|
||||
y_center=0.5,
|
||||
width=0.2,
|
||||
height=0.05,
|
||||
bbox_x=100,
|
||||
bbox_y=100,
|
||||
bbox_width=200,
|
||||
bbox_height=50,
|
||||
text_value="12345",
|
||||
confidence=0.95,
|
||||
source="manual",
|
||||
)
|
||||
|
||||
assert str(ann.document_id) == TEST_DOC_UUID
|
||||
assert ann.class_id == 0
|
||||
assert ann.x_center == 0.5
|
||||
assert ann.source == "manual"
|
||||
|
||||
def test_normalized_coordinates(self):
|
||||
"""Test normalized coordinates are 0-1 range."""
|
||||
# Valid normalized coords
|
||||
ann = AdminAnnotation(
|
||||
document_id=UUID(TEST_DOC_UUID),
|
||||
page_number=1,
|
||||
class_id=0,
|
||||
class_name="test",
|
||||
x_center=0.5,
|
||||
y_center=0.5,
|
||||
width=0.2,
|
||||
height=0.05,
|
||||
bbox_x=0,
|
||||
bbox_y=0,
|
||||
bbox_width=100,
|
||||
bbox_height=50,
|
||||
)
|
||||
|
||||
assert 0 <= ann.x_center <= 1
|
||||
assert 0 <= ann.y_center <= 1
|
||||
assert 0 <= ann.width <= 1
|
||||
assert 0 <= ann.height <= 1
|
||||
Reference in New Issue
Block a user