""" 模板编辑器 Schema 验证测试. 覆盖 ExportUpdateRequest 等带 validator 的 Schema. 纯数据 model 不写单测(无业务逻辑)。 """ from __future__ import annotations import pytest from app.api.routes.templates_editor.schemas import ( ExportUpdateRequest, ) class TestExportUpdateRequestResolution: """resolution 字段验证.""" def test_valid_1080p(self): req = ExportUpdateRequest(resolution="1920x1080") assert req.resolution == "1920x1080" def test_valid_portrait(self): req = ExportUpdateRequest(resolution="1080x1920") assert req.resolution == "1080x1920" def test_valid_square(self): req = ExportUpdateRequest(resolution="1080x1080") assert req.resolution == "1080x1080" def test_valid_min(self): req = ExportUpdateRequest(resolution="100x100") assert req.resolution == "100x100" def test_valid_max(self): req = ExportUpdateRequest(resolution="4096x4096") assert req.resolution == "4096x4096" def test_none_is_valid(self): req = ExportUpdateRequest() assert req.resolution is None def test_invalid_format_no_x(self): with pytest.raises(ValueError, match="分辨率格式错误"): ExportUpdateRequest(resolution="19201080") def test_invalid_format_letters(self): with pytest.raises(ValueError, match="分辨率格式错误"): ExportUpdateRequest(resolution="abcxdef") def test_invalid_format_empty(self): with pytest.raises(ValueError, match="分辨率格式错误"): ExportUpdateRequest(resolution="") def test_invalid_format_upper_x(self): """大写 X 不匹配正则""" with pytest.raises(ValueError, match="分辨率格式错误"): ExportUpdateRequest(resolution="1920X1080") def test_too_small_width(self): with pytest.raises(ValueError, match="过小"): ExportUpdateRequest(resolution="50x1080") def test_too_small_height(self): with pytest.raises(ValueError, match="过小"): ExportUpdateRequest(resolution="1920x50") def test_too_large_width(self): with pytest.raises(ValueError, match="过大"): ExportUpdateRequest(resolution="5000x1080") def test_too_large_height(self): with pytest.raises(ValueError, match="过大"): ExportUpdateRequest(resolution="1920x5000") def test_extra_chars_after(self): """后面多字符也不匹配""" with pytest.raises(ValueError, match="分辨率格式错误"): ExportUpdateRequest(resolution="1920x1080p") def test_extra_chars_before(self): with pytest.raises(ValueError, match="分辨率格式错误"): ExportUpdateRequest(resolution="fhd1920x1080") class TestExportUpdateRequestFormat: """format 字段验证.""" def test_valid_mp4(self): req = ExportUpdateRequest(format="mp4") assert req.format == "mp4" def test_valid_mov(self): req = ExportUpdateRequest(format="mov") assert req.format == "mov" def test_none_is_valid(self): req = ExportUpdateRequest() assert req.format is None def test_invalid_avi(self): with pytest.raises(ValueError, match="无效格式"): ExportUpdateRequest(format="avi") def test_invalid_empty(self): with pytest.raises(ValueError, match="无效格式"): ExportUpdateRequest(format="") def test_invalid_upper(self): with pytest.raises(ValueError, match="无效格式"): ExportUpdateRequest(format="MP4") class TestExportUpdateRequestQualityPreset: """quality_preset 字段验证.""" @pytest.mark.parametrize("preset", ["ultra_fast", "fast", "balanced", "high", "best"]) def test_valid_presets(self, preset): req = ExportUpdateRequest(quality_preset=preset) assert req.quality_preset == preset def test_none_is_valid(self): req = ExportUpdateRequest() assert req.quality_preset is None def test_invalid_preset(self): with pytest.raises(ValueError, match="无效质量预设"): ExportUpdateRequest(quality_preset="ultra") def test_invalid_empty(self): with pytest.raises(ValueError, match="无效质量预设"): ExportUpdateRequest(quality_preset="") class TestExportUpdateRequestFps: """fps 字段范围验证(由 Field ge/le 控制).""" def test_valid_30(self): req = ExportUpdateRequest(fps=30) assert req.fps == 30 def test_valid_min(self): req = ExportUpdateRequest(fps=15) assert req.fps == 15 def test_valid_max(self): req = ExportUpdateRequest(fps=60) assert req.fps == 60 def test_below_min(self): with pytest.raises(ValueError): ExportUpdateRequest(fps=10) def test_above_max(self): with pytest.raises(ValueError): ExportUpdateRequest(fps=120) def test_none_is_valid(self): req = ExportUpdateRequest() assert req.fps is None class TestExportUpdateRequestBitrate: """码率字段范围验证.""" def test_video_bitrate_valid(self): req = ExportUpdateRequest(video_bitrate=5000) assert req.video_bitrate == 5000 def test_video_bitrate_min(self): req = ExportUpdateRequest(video_bitrate=1000) assert req.video_bitrate == 1000 def test_video_bitrate_max(self): req = ExportUpdateRequest(video_bitrate=20000) assert req.video_bitrate == 20000 def test_video_bitrate_below_min(self): with pytest.raises(ValueError): ExportUpdateRequest(video_bitrate=500) def test_video_bitrate_above_max(self): with pytest.raises(ValueError): ExportUpdateRequest(video_bitrate=50000) def test_audio_bitrate_valid(self): req = ExportUpdateRequest(audio_bitrate=128) assert req.audio_bitrate == 128 def test_audio_bitrate_min(self): req = ExportUpdateRequest(audio_bitrate=64) assert req.audio_bitrate == 64 def test_audio_bitrate_max(self): req = ExportUpdateRequest(audio_bitrate=320) assert req.audio_bitrate == 320 def test_audio_bitrate_below_min(self): with pytest.raises(ValueError): ExportUpdateRequest(audio_bitrate=32) def test_audio_bitrate_above_max(self): with pytest.raises(ValueError): ExportUpdateRequest(audio_bitrate=512) class TestExportUpdateRequestWatermark: """水印字段.""" def test_watermark_enabled(self): req = ExportUpdateRequest(watermark_enabled=True, watermark_text="hello") assert req.watermark_enabled is True assert req.watermark_text == "hello" def test_watermark_disabled(self): req = ExportUpdateRequest(watermark_enabled=False) assert req.watermark_enabled is False def test_watermark_default_none(self): req = ExportUpdateRequest() assert req.watermark_enabled is None assert req.watermark_text is None class TestExportUpdateRequestCombined: """组合字段验证.""" def test_full_valid(self): req = ExportUpdateRequest( resolution="1920x1080", fps=30, video_bitrate=8000, audio_bitrate=128, format="mp4", quality_preset="balanced", watermark_enabled=True, watermark_text="test", ) assert req.resolution == "1920x1080" assert req.fps == 30 assert req.format == "mp4" assert req.quality_preset == "balanced" def test_partial_update(self): """只更新部分字段,其余为 None""" req = ExportUpdateRequest(fps=24) assert req.fps == 24 assert req.resolution is None assert req.format is None assert req.quality_preset is None