"""Tests for core data models.""" import json from dataclasses import FrozenInstanceError import pytest from pdf2imos.models import ( ClassifiedLine, DimensionAnnotation, DimensionDirection, DrillingAnnotation, EdgebandAnnotation, HardwareAnnotation, LineRole, MaterialAnnotation, PageExtraction, PartGeometry, PartMetadata, PipelineResult, RawPath, RawText, ViewRegion, ViewType, ) class TestRawPath: """Tests for RawPath dataclass.""" def test_instantiate(self): """Test RawPath instantiation.""" path = RawPath( items=(("l", 0, 0, 10, 10),), color=(0.0, 0.0, 0.0), fill=None, dashes="", width=1.0, rect=(0.0, 0.0, 10.0, 10.0), ) assert path.color == (0.0, 0.0, 0.0) assert path.width == 1.0 def test_to_dict(self): """Test RawPath.to_dict() serialization.""" path = RawPath( items=(("l", 0, 0, 10, 10),), color=(0.5, 0.5, 0.5), fill=(1.0, 1.0, 1.0), dashes="[3 2] 0", width=2.5, rect=(0.0, 0.0, 10.0, 10.0), ) d = path.to_dict() assert d["color"] == (0.5, 0.5, 0.5) assert d["fill"] == (1.0, 1.0, 1.0) assert d["dashes"] == "[3 2] 0" assert d["width"] == 2.5 assert d["rect"] == [0.0, 0.0, 10.0, 10.0] # Verify JSON serializable json.dumps(d) def test_frozen(self): """Test that RawPath is frozen.""" path = RawPath( items=(("l", 0, 0, 10, 10),), color=(0.0, 0.0, 0.0), fill=None, dashes="", width=1.0, rect=(0.0, 0.0, 10.0, 10.0), ) with pytest.raises(FrozenInstanceError): path.width = 2.0 class TestRawText: """Tests for RawText dataclass.""" def test_instantiate(self): """Test RawText instantiation.""" text = RawText( text="Hello", bbox=(0.0, 0.0, 50.0, 20.0), font="Helvetica", size=12.0, color=0, ) assert text.text == "Hello" assert text.size == 12.0 def test_to_dict(self): """Test RawText.to_dict() serialization.""" text = RawText( text="Test", bbox=(10.0, 20.0, 60.0, 40.0), font="Arial", size=14.0, color=16777215, ) d = text.to_dict() assert d["text"] == "Test" assert d["bbox"] == [10.0, 20.0, 60.0, 40.0] assert d["font"] == "Arial" assert d["size"] == 14.0 assert d["color"] == 16777215 json.dumps(d) def test_frozen(self): """Test that RawText is frozen.""" text = RawText( text="Hello", bbox=(0.0, 0.0, 50.0, 20.0), font="Helvetica", size=12.0, color=0, ) with pytest.raises(FrozenInstanceError): text.text = "World" class TestPageExtraction: """Tests for PageExtraction dataclass.""" def test_instantiate(self): """Test PageExtraction instantiation.""" path = RawPath( items=(("l", 0, 0, 10, 10),), color=(0.0, 0.0, 0.0), fill=None, dashes="", width=1.0, rect=(0.0, 0.0, 10.0, 10.0), ) text = RawText( text="Test", bbox=(0.0, 0.0, 50.0, 20.0), font="Helvetica", size=12.0, color=0, ) page = PageExtraction( paths=(path,), texts=(text,), page_width=100.0, page_height=200.0, ) assert len(page.paths) == 1 assert len(page.texts) == 1 def test_to_dict(self): """Test PageExtraction.to_dict() serialization.""" path = RawPath( items=(("l", 0, 0, 10, 10),), color=(0.0, 0.0, 0.0), fill=None, dashes="", width=1.0, rect=(0.0, 0.0, 10.0, 10.0), ) text = RawText( text="Test", bbox=(0.0, 0.0, 50.0, 20.0), font="Helvetica", size=12.0, color=0, ) page = PageExtraction( paths=(path,), texts=(text,), page_width=100.0, page_height=200.0, ) d = page.to_dict() assert len(d["paths"]) == 1 assert len(d["texts"]) == 1 assert d["page_width"] == 100.0 assert d["page_height"] == 200.0 json.dumps(d) class TestViewType: """Tests for ViewType enum.""" def test_enum_values(self): """Test ViewType enum values.""" assert ViewType.FRONT.value == "front" assert ViewType.TOP.value == "top" assert ViewType.SIDE.value == "side" assert ViewType.UNKNOWN.value == "unknown" class TestViewRegion: """Tests for ViewRegion dataclass.""" def test_instantiate(self): """Test ViewRegion instantiation.""" path = RawPath( items=(("l", 0, 0, 10, 10),), color=(0.0, 0.0, 0.0), fill=None, dashes="", width=1.0, rect=(0.0, 0.0, 10.0, 10.0), ) region = ViewRegion( view_type=ViewType.FRONT, bounds=(0.0, 0.0, 100.0, 200.0), paths=(path,), texts=(), ) assert region.view_type == ViewType.FRONT def test_to_dict(self): """Test ViewRegion.to_dict() serialization.""" path = RawPath( items=(("l", 0, 0, 10, 10),), color=(0.0, 0.0, 0.0), fill=None, dashes="", width=1.0, rect=(0.0, 0.0, 10.0, 10.0), ) region = ViewRegion( view_type=ViewType.TOP, bounds=(10.0, 20.0, 110.0, 220.0), paths=(path,), texts=(), ) d = region.to_dict() assert d["view_type"] == "top" assert d["bounds"] == [10.0, 20.0, 110.0, 220.0] json.dumps(d) class TestLineRole: """Tests for LineRole enum.""" def test_enum_values(self): """Test LineRole enum values.""" assert LineRole.GEOMETRY.value == "geometry" assert LineRole.HIDDEN.value == "hidden" assert LineRole.CENTER.value == "center" assert LineRole.DIMENSION.value == "dimension" assert LineRole.BORDER.value == "border" assert LineRole.CONSTRUCTION.value == "construction" assert LineRole.UNKNOWN.value == "unknown" class TestClassifiedLine: """Tests for ClassifiedLine dataclass.""" def test_instantiate(self): """Test ClassifiedLine instantiation.""" path = RawPath( items=(("l", 0, 0, 10, 10),), color=(0.0, 0.0, 0.0), fill=None, dashes="", width=1.0, rect=(0.0, 0.0, 10.0, 10.0), ) line = ClassifiedLine( start=(0.0, 0.0), end=(10.0, 10.0), role=LineRole.GEOMETRY, confidence=0.95, original_path=path, ) assert line.role == LineRole.GEOMETRY assert line.confidence == 0.95 def test_to_dict(self): """Test ClassifiedLine.to_dict() serialization.""" path = RawPath( items=(("l", 0, 0, 10, 10),), color=(0.0, 0.0, 0.0), fill=None, dashes="", width=1.0, rect=(0.0, 0.0, 10.0, 10.0), ) line = ClassifiedLine( start=(5.0, 5.0), end=(15.0, 15.0), role=LineRole.DIMENSION, confidence=0.85, original_path=path, ) d = line.to_dict() assert d["start"] == [5.0, 5.0] assert d["end"] == [15.0, 15.0] assert d["role"] == "dimension" assert d["confidence"] == 0.85 json.dumps(d) class TestDimensionAnnotation: """Tests for DimensionAnnotation dataclass.""" def test_instantiate(self): """Test DimensionAnnotation instantiation.""" dim = DimensionAnnotation( value_mm=100.0, direction=DimensionDirection.HORIZONTAL, dim_line_start=(0.0, 0.0), dim_line_end=(100.0, 0.0), text_bbox=(40.0, -10.0, 60.0, 0.0), ) assert dim.value_mm == 100.0 assert dim.direction == DimensionDirection.HORIZONTAL def test_to_dict(self): """Test DimensionAnnotation.to_dict() serialization.""" dim = DimensionAnnotation( value_mm=50.5, direction=DimensionDirection.VERTICAL, dim_line_start=(10.0, 10.0), dim_line_end=(10.0, 60.0), text_bbox=(0.0, 30.0, 10.0, 40.0), ) d = dim.to_dict() assert d["value_mm"] == 50.5 assert d["direction"] == "vertical" assert d["dim_line_start"] == [10.0, 10.0] assert d["dim_line_end"] == [10.0, 60.0] json.dumps(d) class TestMaterialAnnotation: """Tests for MaterialAnnotation dataclass.""" def test_instantiate(self): """Test MaterialAnnotation instantiation.""" mat = MaterialAnnotation( text="MDF 18mm white melamine", thickness_mm=18.0, material_type="MDF", finish="white melamine", ) assert mat.material_type == "MDF" assert mat.thickness_mm == 18.0 def test_to_dict(self): """Test MaterialAnnotation.to_dict() serialization.""" mat = MaterialAnnotation( text="Plywood 12mm", thickness_mm=12.0, material_type="plywood", finish="natural", ) d = mat.to_dict() assert d["material_type"] == "plywood" assert d["thickness_mm"] == 12.0 json.dumps(d) class TestEdgebandAnnotation: """Tests for EdgebandAnnotation dataclass.""" def test_instantiate(self): """Test EdgebandAnnotation instantiation.""" edge = EdgebandAnnotation( edge_id="top", material="PVC", thickness_mm=2.0, ) assert edge.edge_id == "top" assert edge.material == "PVC" def test_to_dict(self): """Test EdgebandAnnotation.to_dict() serialization.""" edge = EdgebandAnnotation( edge_id="left", material="ABS", thickness_mm=1.5, ) d = edge.to_dict() assert d["edge_id"] == "left" assert d["material"] == "ABS" json.dumps(d) class TestHardwareAnnotation: """Tests for HardwareAnnotation dataclass.""" def test_instantiate(self): """Test HardwareAnnotation instantiation.""" hw = HardwareAnnotation( type="hinge", model="Blum 110°", position_description="top left", ) assert hw.type == "hinge" assert hw.model == "Blum 110°" def test_to_dict(self): """Test HardwareAnnotation.to_dict() serialization.""" hw = HardwareAnnotation( type="handle", model="Ergonomic", position_description="center front", ) d = hw.to_dict() assert d["type"] == "handle" json.dumps(d) class TestDrillingAnnotation: """Tests for DrillingAnnotation dataclass.""" def test_instantiate(self): """Test DrillingAnnotation instantiation.""" drill = DrillingAnnotation( x_mm=50.0, y_mm=100.0, diameter_mm=8.0, depth_mm=10.0, ) assert drill.x_mm == 50.0 assert drill.diameter_mm == 8.0 def test_to_dict(self): """Test DrillingAnnotation.to_dict() serialization.""" drill = DrillingAnnotation( x_mm=25.0, y_mm=75.0, diameter_mm=5.0, depth_mm=15.0, ) d = drill.to_dict() assert d["x_mm"] == 25.0 assert d["diameter_mm"] == 5.0 json.dumps(d) class TestPartMetadata: """Tests for PartMetadata dataclass.""" def test_instantiate(self): """Test PartMetadata instantiation.""" mat = MaterialAnnotation( text="MDF 18mm", thickness_mm=18.0, material_type="MDF", finish="white", ) edge = EdgebandAnnotation( edge_id="top", material="PVC", thickness_mm=2.0, ) metadata = PartMetadata( materials=(mat,), edgebanding=(edge,), hardware=(), drilling=(), raw_annotations=("annotation1", "annotation2"), ) assert len(metadata.materials) == 1 assert len(metadata.raw_annotations) == 2 def test_to_dict(self): """Test PartMetadata.to_dict() serialization.""" mat = MaterialAnnotation( text="Plywood", thickness_mm=12.0, material_type="plywood", finish="natural", ) metadata = PartMetadata( materials=(mat,), edgebanding=(), hardware=(), drilling=(), raw_annotations=(), ) d = metadata.to_dict() assert len(d["materials"]) == 1 assert d["materials"][0]["material_type"] == "plywood" json.dumps(d) class TestPartGeometry: """Tests for PartGeometry dataclass.""" def test_instantiate(self): """Test PartGeometry instantiation.""" geom = PartGeometry( width_mm=500.0, height_mm=800.0, depth_mm=400.0, origin=(0.0, 0.0, 0.0), name="Cabinet", ) assert geom.width_mm == 500.0 assert geom.name == "Cabinet" def test_to_dict(self): """Test PartGeometry.to_dict() serialization.""" geom = PartGeometry( width_mm=600.0, height_mm=900.0, depth_mm=350.0, origin=(10.0, 20.0, 0.0), name="Shelf", ) d = geom.to_dict() assert d["width_mm"] == 600.0 assert d["origin"] == [10.0, 20.0, 0.0] assert d["name"] == "Shelf" json.dumps(d) def test_frozen(self): """Test that PartGeometry is frozen.""" geom = PartGeometry( width_mm=500.0, height_mm=800.0, depth_mm=400.0, origin=(0.0, 0.0, 0.0), name="Cabinet", ) with pytest.raises(FrozenInstanceError): geom.width_mm = 600.0 class TestPipelineResult: """Tests for PipelineResult dataclass.""" def test_instantiate(self): """Test PipelineResult instantiation.""" geom = PartGeometry( width_mm=500.0, height_mm=800.0, depth_mm=400.0, origin=(0.0, 0.0, 0.0), name="Cabinet", ) metadata = PartMetadata( materials=(), edgebanding=(), hardware=(), drilling=(), raw_annotations=(), ) result = PipelineResult( part_geometry=geom, part_metadata=metadata, source_pdf_path="/path/to/input.pdf", dxf_output_path="/path/to/output.dxf", json_output_path="/path/to/output.json", ) assert result.source_pdf_path == "/path/to/input.pdf" assert result.dxf_output_path == "/path/to/output.dxf" def test_to_dict(self): """Test PipelineResult.to_dict() serialization.""" geom = PartGeometry( width_mm=500.0, height_mm=800.0, depth_mm=400.0, origin=(0.0, 0.0, 0.0), name="Cabinet", ) metadata = PartMetadata( materials=(), edgebanding=(), hardware=(), drilling=(), raw_annotations=(), ) result = PipelineResult( part_geometry=geom, part_metadata=metadata, source_pdf_path="/input.pdf", dxf_output_path=None, json_output_path="/output.json", ) d = result.to_dict() assert d["source_pdf_path"] == "/input.pdf" assert d["dxf_output_path"] is None assert d["json_output_path"] == "/output.json" json.dumps(d) def test_frozen(self): """Test that PipelineResult is frozen.""" geom = PartGeometry( width_mm=500.0, height_mm=800.0, depth_mm=400.0, origin=(0.0, 0.0, 0.0), name="Cabinet", ) metadata = PartMetadata( materials=(), edgebanding=(), hardware=(), drilling=(), raw_annotations=(), ) result = PipelineResult( part_geometry=geom, part_metadata=metadata, source_pdf_path="/input.pdf", dxf_output_path=None, json_output_path=None, ) with pytest.raises(FrozenInstanceError): result.source_pdf_path = "/other.pdf" class TestJSONRoundTrip: """Test JSON serialization round-trip.""" def test_raw_path_roundtrip(self): """Test RawPath JSON round-trip.""" path = RawPath( items=(("l", 0, 0, 10, 10),), color=(0.5, 0.5, 0.5), fill=(1.0, 1.0, 1.0), dashes="[3 2] 0", width=2.5, rect=(0.0, 0.0, 10.0, 10.0), ) d = path.to_dict() json_str = json.dumps(d) loaded = json.loads(json_str) assert loaded["color"] == [0.5, 0.5, 0.5] assert loaded["width"] == 2.5 def test_page_extraction_roundtrip(self): """Test PageExtraction JSON round-trip.""" path = RawPath( items=(("l", 0, 0, 10, 10),), color=(0.0, 0.0, 0.0), fill=None, dashes="", width=1.0, rect=(0.0, 0.0, 10.0, 10.0), ) text = RawText( text="Test", bbox=(0.0, 0.0, 50.0, 20.0), font="Helvetica", size=12.0, color=0, ) page = PageExtraction( paths=(path,), texts=(text,), page_width=100.0, page_height=200.0, ) d = page.to_dict() json_str = json.dumps(d) loaded = json.loads(json_str) assert loaded["page_width"] == 100.0 assert len(loaded["paths"]) == 1 assert len(loaded["texts"]) == 1 def test_pipeline_result_roundtrip(self): """Test PipelineResult JSON round-trip.""" geom = PartGeometry( width_mm=500.0, height_mm=800.0, depth_mm=400.0, origin=(0.0, 0.0, 0.0), name="Cabinet", ) metadata = PartMetadata( materials=(), edgebanding=(), hardware=(), drilling=(), raw_annotations=(), ) result = PipelineResult( part_geometry=geom, part_metadata=metadata, source_pdf_path="/input.pdf", dxf_output_path="/output.dxf", json_output_path="/output.json", ) d = result.to_dict() json_str = json.dumps(d) loaded = json.loads(json_str) assert loaded["source_pdf_path"] == "/input.pdf" assert loaded["part_geometry"]["width_mm"] == 500.0