b780bf1563
- Extract safe_parse_fps, is_valid_media, and constants from ingest.py - New module: packages/domain/media_validation.py - 70 new unit tests: safe_parse_fps(18) + constants(11) + is_valid_media(41) - ingest.py: 424 -> 340 lines (-84, -20%) - Backward compatible: private aliases preserved, original 25 tests pass
300 lines
10 KiB
Python
Executable File
300 lines
10 KiB
Python
Executable File
"""media_validation 领域模块单元测试。"""
|
|
|
|
import pytest
|
|
|
|
from packages.domain.media_validation import (
|
|
MIN_AUDIO_FILE_SIZE,
|
|
MIN_IMAGE_FILE_SIZE,
|
|
MIN_VIDEO_FILE_SIZE,
|
|
SUPPORTED_VIDEO_CODECS,
|
|
is_valid_media,
|
|
safe_parse_fps,
|
|
)
|
|
|
|
|
|
class TestSafeParseFpsBasic:
|
|
def test_integer_fps(self):
|
|
assert safe_parse_fps("30") == 30.0
|
|
|
|
def test_decimal_fps(self):
|
|
assert safe_parse_fps("29.97") == pytest.approx(29.97)
|
|
|
|
def test_fraction_simple(self):
|
|
assert safe_parse_fps("30/1") == 30.0
|
|
|
|
def test_fraction_ntsc(self):
|
|
assert safe_parse_fps("30000/1001") == pytest.approx(29.97002997)
|
|
|
|
def test_fraction_pal(self):
|
|
assert safe_parse_fps("25/1") == 25.0
|
|
|
|
def test_fraction_24fps_cine(self):
|
|
assert safe_parse_fps("24000/1001") == pytest.approx(23.976023976)
|
|
|
|
def test_zero_fps(self):
|
|
assert safe_parse_fps("0") == 0.0
|
|
|
|
def test_zero_fraction(self):
|
|
assert safe_parse_fps("0/1") == 0.0
|
|
|
|
|
|
class TestSafeParseFpsEdgeCases:
|
|
def test_zero_denominator(self):
|
|
assert safe_parse_fps("30/0") == 0.0
|
|
|
|
def test_empty_string(self):
|
|
assert safe_parse_fps("") == 0.0
|
|
|
|
def test_garbage_string(self):
|
|
assert safe_parse_fps("not_a_number") == 0.0
|
|
|
|
def test_multiple_slashes(self):
|
|
# split("/", 1) 只切第一个,后面的作为 den 的一部分会解析失败
|
|
assert safe_parse_fps("30/1/2") == 0.0
|
|
|
|
def test_negative_fps(self):
|
|
assert safe_parse_fps("-30") == -30.0
|
|
|
|
def test_negative_fraction(self):
|
|
assert safe_parse_fps("-30/1") == -30.0
|
|
|
|
def test_very_high_fps(self):
|
|
assert safe_parse_fps("240/1") == 240.0
|
|
|
|
def test_fraction_float_num(self):
|
|
assert safe_parse_fps("29.97/1") == pytest.approx(29.97)
|
|
|
|
def test_fraction_float_den(self):
|
|
assert safe_parse_fps("30/1.001") == pytest.approx(29.97002997)
|
|
|
|
def test_whitespace_in_string(self):
|
|
# float(" 30 ") 能解析,所以应该返回 30.0
|
|
assert safe_parse_fps(" 30 ") == 30.0
|
|
|
|
|
|
class TestMinFileSizeConstants:
|
|
def test_min_video_size_is_1kb(self):
|
|
assert MIN_VIDEO_FILE_SIZE == 1024
|
|
|
|
def test_min_audio_size(self):
|
|
assert MIN_AUDIO_FILE_SIZE == 100
|
|
|
|
def test_min_image_size(self):
|
|
assert MIN_IMAGE_FILE_SIZE == 100
|
|
|
|
|
|
class TestSupportedVideoCodecs:
|
|
def test_h264_family_present(self):
|
|
assert "h264" in SUPPORTED_VIDEO_CODECS
|
|
assert "avc1" in SUPPORTED_VIDEO_CODECS
|
|
assert "avc" in SUPPORTED_VIDEO_CODECS
|
|
|
|
def test_h265_family_present(self):
|
|
assert "hevc" in SUPPORTED_VIDEO_CODECS
|
|
assert "h265" in SUPPORTED_VIDEO_CODECS
|
|
assert "hev1" in SUPPORTED_VIDEO_CODECS
|
|
assert "hvc1" in SUPPORTED_VIDEO_CODECS
|
|
|
|
def test_vp9_av1_present(self):
|
|
assert "vp9" in SUPPORTED_VIDEO_CODECS
|
|
assert "vp09" in SUPPORTED_VIDEO_CODECS
|
|
assert "av1" in SUPPORTED_VIDEO_CODECS
|
|
assert "av01" in SUPPORTED_VIDEO_CODECS
|
|
|
|
def test_vp8_present(self):
|
|
assert "vp8" in SUPPORTED_VIDEO_CODECS
|
|
assert "vp08" in SUPPORTED_VIDEO_CODECS
|
|
|
|
def test_mpeg_family_present(self):
|
|
assert "mpeg4" in SUPPORTED_VIDEO_CODECS
|
|
assert "mp4v" in SUPPORTED_VIDEO_CODECS
|
|
assert "mpeg2video" in SUPPORTED_VIDEO_CODECS
|
|
|
|
def test_prores_family_present(self):
|
|
assert "prores" in SUPPORTED_VIDEO_CODECS
|
|
assert "apcn" in SUPPORTED_VIDEO_CODECS
|
|
assert "apch" in SUPPORTED_VIDEO_CODECS
|
|
|
|
def test_unknown_codec_not_present(self):
|
|
assert "unknown_codec_xyz" not in SUPPORTED_VIDEO_CODECS
|
|
|
|
def test_codecs_count_reasonable(self):
|
|
# 白名单应该有足够多的编码格式
|
|
assert len(SUPPORTED_VIDEO_CODECS) >= 30
|
|
|
|
|
|
class TestIsValidMediaVideo:
|
|
def test_valid_video(self):
|
|
metadata = {"size_bytes": 5000, "duration": 10.0, "codec": "h264"}
|
|
assert is_valid_media(metadata, "video") is True
|
|
|
|
def test_video_too_small(self):
|
|
metadata = {"size_bytes": 500, "duration": 10.0, "codec": "h264"}
|
|
assert is_valid_media(metadata, "video") is False
|
|
|
|
def test_video_exact_min_size(self):
|
|
metadata = {"size_bytes": 1024, "duration": 10.0, "codec": "h264"}
|
|
assert is_valid_media(metadata, "video") is True
|
|
|
|
def test_video_zero_duration(self):
|
|
metadata = {"size_bytes": 5000, "duration": 0, "codec": "h264"}
|
|
assert is_valid_media(metadata, "video") is False
|
|
|
|
def test_video_negative_duration(self):
|
|
metadata = {"size_bytes": 5000, "duration": -1.0, "codec": "h264"}
|
|
assert is_valid_media(metadata, "video") is False
|
|
|
|
def test_video_missing_size_default_zero(self):
|
|
metadata = {"duration": 10.0, "codec": "h264"}
|
|
assert is_valid_media(metadata, "video") is False
|
|
|
|
def test_video_missing_duration_default_zero(self):
|
|
metadata = {"size_bytes": 5000, "codec": "h264"}
|
|
assert is_valid_media(metadata, "video") is False
|
|
|
|
def test_video_unknown_codec_still_valid(self):
|
|
# 非白名单编码仍允许通过(渲染层统一转码)
|
|
metadata = {"size_bytes": 5000, "duration": 10.0, "codec": "some_unknown_codec"}
|
|
assert is_valid_media(metadata, "video") is True
|
|
|
|
def test_video_missing_codec_still_valid(self):
|
|
metadata = {"size_bytes": 5000, "duration": 10.0}
|
|
assert is_valid_media(metadata, "video") is True
|
|
|
|
def test_video_codec_case_insensitive(self):
|
|
metadata = {"size_bytes": 5000, "duration": 10.0, "codec": "H264"}
|
|
assert is_valid_media(metadata, "video") is True
|
|
|
|
def test_video_empty_codec(self):
|
|
metadata = {"size_bytes": 5000, "duration": 10.0, "codec": ""}
|
|
assert is_valid_media(metadata, "video") is True
|
|
|
|
def test_video_hevc_codec(self):
|
|
metadata = {"size_bytes": 5000, "duration": 10.0, "codec": "hevc"}
|
|
assert is_valid_media(metadata, "video") is True
|
|
|
|
def test_video_vp9_codec(self):
|
|
metadata = {"size_bytes": 5000, "duration": 10.0, "codec": "vp9"}
|
|
assert is_valid_media(metadata, "video") is True
|
|
|
|
def test_video_av1_codec(self):
|
|
metadata = {"size_bytes": 5000, "duration": 10.0, "codec": "av1"}
|
|
assert is_valid_media(metadata, "video") is True
|
|
|
|
def test_video_prores_codec(self):
|
|
metadata = {"size_bytes": 5000, "duration": 10.0, "codec": "prores"}
|
|
assert is_valid_media(metadata, "video") is True
|
|
|
|
def test_video_empty_metadata(self):
|
|
assert is_valid_media({}, "video") is False
|
|
|
|
|
|
class TestIsValidMediaAudio:
|
|
def test_valid_audio(self):
|
|
metadata = {"size_bytes": 5000, "duration": 30.0}
|
|
assert is_valid_media(metadata, "audio") is True
|
|
|
|
def test_audio_too_small(self):
|
|
metadata = {"size_bytes": 50, "duration": 30.0}
|
|
assert is_valid_media(metadata, "audio") is False
|
|
|
|
def test_audio_exact_min_size(self):
|
|
metadata = {"size_bytes": 100, "duration": 10.0}
|
|
assert is_valid_media(metadata, "audio") is True
|
|
|
|
def test_audio_zero_duration(self):
|
|
metadata = {"size_bytes": 5000, "duration": 0}
|
|
assert is_valid_media(metadata, "audio") is False
|
|
|
|
def test_audio_negative_duration(self):
|
|
metadata = {"size_bytes": 5000, "duration": -1.0}
|
|
assert is_valid_media(metadata, "audio") is False
|
|
|
|
def test_audio_missing_size(self):
|
|
metadata = {"duration": 10.0}
|
|
assert is_valid_media(metadata, "audio") is False
|
|
|
|
def test_audio_missing_duration(self):
|
|
metadata = {"size_bytes": 5000}
|
|
assert is_valid_media(metadata, "audio") is False
|
|
|
|
def test_audio_with_codec_info(self):
|
|
metadata = {"size_bytes": 5000, "duration": 30.0, "codec": "aac"}
|
|
assert is_valid_media(metadata, "audio") is True
|
|
|
|
def test_audio_empty_metadata(self):
|
|
assert is_valid_media({}, "audio") is False
|
|
|
|
|
|
class TestIsValidMediaImage:
|
|
def test_valid_image(self):
|
|
metadata = {"size_bytes": 5000, "width": 1920, "height": 1080}
|
|
assert is_valid_media(metadata, "image") is True
|
|
|
|
def test_image_too_small(self):
|
|
metadata = {"size_bytes": 50, "width": 1920, "height": 1080}
|
|
assert is_valid_media(metadata, "image") is False
|
|
|
|
def test_image_exact_min_size(self):
|
|
metadata = {"size_bytes": 100, "width": 100, "height": 100}
|
|
assert is_valid_media(metadata, "image") is True
|
|
|
|
def test_image_zero_width(self):
|
|
metadata = {"size_bytes": 5000, "width": 0, "height": 1080}
|
|
assert is_valid_media(metadata, "image") is False
|
|
|
|
def test_image_zero_height(self):
|
|
metadata = {"size_bytes": 5000, "width": 1920, "height": 0}
|
|
assert is_valid_media(metadata, "image") is False
|
|
|
|
def test_image_negative_dimensions(self):
|
|
metadata = {"size_bytes": 5000, "width": -1, "height": 1080}
|
|
assert is_valid_media(metadata, "image") is False
|
|
|
|
def test_image_missing_width(self):
|
|
metadata = {"size_bytes": 5000, "height": 1080}
|
|
assert is_valid_media(metadata, "image") is False
|
|
|
|
def test_image_missing_height(self):
|
|
metadata = {"size_bytes": 5000, "width": 1920}
|
|
assert is_valid_media(metadata, "image") is False
|
|
|
|
def test_image_missing_size(self):
|
|
metadata = {"width": 1920, "height": 1080}
|
|
assert is_valid_media(metadata, "image") is False
|
|
|
|
def test_image_small_but_valid(self):
|
|
metadata = {"size_bytes": 100, "width": 1, "height": 1}
|
|
assert is_valid_media(metadata, "image") is True
|
|
|
|
def test_image_empty_metadata(self):
|
|
assert is_valid_media({}, "image") is False
|
|
|
|
|
|
class TestIsValidMediaUnknownType:
|
|
def test_unknown_type_returns_false(self):
|
|
metadata = {"size_bytes": 5000, "duration": 10.0}
|
|
assert is_valid_media(metadata, "unknown") is False
|
|
|
|
def test_empty_type_returns_false(self):
|
|
metadata = {"size_bytes": 5000, "duration": 10.0}
|
|
assert is_valid_media(metadata, "") is False
|
|
|
|
def test_text_type_returns_false(self):
|
|
metadata = {"size_bytes": 5000}
|
|
assert is_valid_media(metadata, "text") is False
|
|
|
|
|
|
class TestIsValidMediaSizeTypes:
|
|
def test_size_as_string(self):
|
|
# int("5000") 能解析
|
|
metadata = {"size_bytes": "5000", "duration": 10.0, "codec": "h264"}
|
|
assert is_valid_media(metadata, "video") is True
|
|
|
|
def test_size_as_none(self):
|
|
# int(None) 会 TypeError,但 metadata.get 返回 0 默认值
|
|
metadata = {"size_bytes": None, "duration": 10.0, "codec": "h264"}
|
|
# int(None) 会抛 TypeError
|
|
with pytest.raises(TypeError):
|
|
is_valid_media(metadata, "video")
|