"""Unit tests for Pydantic data models.""" import pytest from pydantic import ValidationError from fansifter_clipper.models import ( CropKeyframe, CropPath, Detection, ProcessingConfig, SegmentsResponse, VideoMetadata, VideoSegment, ) class TestVideoSegment: def test_valid_segment(self): seg = VideoSegment(start=0.0, end=15.0, score=0.9, description="Chorus section") assert seg.start == 0.0 assert seg.end == 15.0 assert seg.score == 0.9 assert seg.description == "Chorus section" def test_segment_duration(self): seg = VideoSegment(start=10.0, end=25.0, score=0.8, description="Test") assert seg.end - seg.start == 15.0 class TestDetection: def test_detection_with_position(self): d = Detection(time=1.5, center_x=640, center_y=360, confidence=0.9, detection_method="mediapipe_face") assert d.center_x == 640 assert d.center_y == 360 assert d.confidence == 0.9 def test_detection_no_position(self): d = Detection(time=2.0, center_x=None, center_y=None, confidence=0.0, detection_method="none") assert d.center_x is None assert d.center_y is None def test_confidence_bounds(self): with pytest.raises(ValidationError): Detection(time=1.0, center_x=100, center_y=100, confidence=1.5, detection_method="test") with pytest.raises(ValidationError): Detection(time=1.0, center_x=100, center_y=100, confidence=-0.1, detection_method="test") class TestCropKeyframe: def test_valid_keyframe(self): kf = CropKeyframe(time=0.0, x=100, y=50, width=720, height=1280) assert kf.x == 100 assert kf.y == 50 assert kf.width == 720 assert kf.height == 1280 assert kf.scene_change is False assert kf.confidence == 0.0 assert kf.detection_method == "unknown" def test_scene_change_flag(self): kf = CropKeyframe(time=3.0, x=200, y=100, width=720, height=1280, scene_change=True) assert kf.scene_change is True def test_negative_x_invalid(self): with pytest.raises(ValidationError): CropKeyframe(time=0.0, x=-1, y=0, width=720, height=1280) def test_zero_width_invalid(self): with pytest.raises(ValidationError): CropKeyframe(time=0.0, x=0, y=0, width=0, height=1280) class TestCropPath: def test_crop_path_defaults(self): seg = VideoSegment(start=0.0, end=12.0, score=0.85, description="Test") kf = CropKeyframe(time=0.0, x=100, y=50, width=720, height=1280) cp = CropPath(segment=seg, keyframes=[kf]) assert cp.debug is False assert len(cp.keyframes) == 1 def test_crop_path_debug_mode(self): seg = VideoSegment(start=0.0, end=12.0, score=0.85, description="Test") kf = CropKeyframe(time=0.0, x=100, y=50, width=720, height=1280) cp = CropPath(segment=seg, keyframes=[kf], debug=True) assert cp.debug is True class TestVideoMetadata: def test_valid_metadata(self): meta = VideoMetadata(width=1920, height=1080, duration=180.0, fps=30.0, codec="h264", aspect_ratio=16/9) assert meta.aspect_ratio == pytest.approx(16 / 9) def test_zero_width_invalid(self): with pytest.raises(ValidationError): VideoMetadata(width=0, height=1080, duration=180.0, fps=30.0, codec="h264", aspect_ratio=1.78) def test_zero_duration_invalid(self): with pytest.raises(ValidationError): VideoMetadata(width=1920, height=1080, duration=0.0, fps=30.0, codec="h264", aspect_ratio=1.78) class TestSegmentsResponse: def test_empty_segments(self): resp = SegmentsResponse(segments=[]) assert resp.segments == [] def test_multiple_segments(self): resp = SegmentsResponse(segments=[ VideoSegment(start=0.0, end=12.0, score=0.9, description="A"), VideoSegment(start=20.0, end=32.0, score=0.8, description="B"), ]) assert len(resp.segments) == 2 class TestProcessingConfig: def test_default_values(self, tmp_path): cfg = ProcessingConfig(input_video=tmp_path / "video.mp4", output_dir=tmp_path / "out") assert cfg.target_aspect_ratio == (9, 16) assert cfg.num_segments == 3 assert cfg.dry_run is False assert cfg.debug is False def test_custom_values(self, tmp_path): cfg = ProcessingConfig( input_video=tmp_path / "video.mp4", output_dir=tmp_path / "clips", num_segments=5, dry_run=True, debug=True, ) assert cfg.num_segments == 5 assert cfg.dry_run is True assert cfg.debug is True def test_num_segments_minimum(self, tmp_path): with pytest.raises(ValidationError): ProcessingConfig( input_video=tmp_path / "video.mp4", output_dir=tmp_path / "out", num_segments=0, )