From 2df7bc9dc892bab27b7397fe263a57e63f08ff92 Mon Sep 17 00:00:00 2001 From: CI Bot Date: Fri, 24 Jul 2026 13:35:05 +0000 Subject: [PATCH] style: auto-format with black + isort + prettier --- tests/unit/test_concat_engine.py | 227 +++++----- tests/unit/test_config_schemas.py | 18 +- .../test_domain_remaining_small_modules.py | 1 - tests/unit/test_entities_domain.py | 2 + tests/unit/test_intro_outro_engine.py | 1 - tests/unit/test_job_domain.py | 2 + tests/unit/test_multi_track_mixer.py | 387 ++++++++++-------- tests/unit/test_quota_domain.py | 2 +- tests/unit/test_render_subtitles.py | 3 +- tests/unit/test_thumbnail_generator.py | 1 - tests/unit/test_transition_presets.py | 6 +- tests/unit/test_tts_job_domain.py | 1 + tests/unit/test_video_share_domain.py | 1 + tests/unit/test_voice_clone_profile_domain.py | 9 +- tests/unit/test_watermark_engine.py | 127 +++--- 15 files changed, 449 insertions(+), 339 deletions(-) diff --git a/tests/unit/test_concat_engine.py b/tests/unit/test_concat_engine.py index 6aff69220..2f122f385 100755 --- a/tests/unit/test_concat_engine.py +++ b/tests/unit/test_concat_engine.py @@ -8,7 +8,6 @@ from __future__ import annotations import pytest - from video_processing.concat_engine import ConcatConfig, ConcatSegment @@ -23,50 +22,62 @@ class TestConcatSegmentFromDict: assert seg.has_audio is True def test_full_fields(self): - seg = ConcatSegment.from_dict({ - "video_path": "/tmp/b.mp4", - "start_time": 5.5, - "duration": 10.0, - "has_audio": False, - }) + seg = ConcatSegment.from_dict( + { + "video_path": "/tmp/b.mp4", + "start_time": 5.5, + "duration": 10.0, + "has_audio": False, + } + ) assert seg.video_path == "/tmp/b.mp4" assert seg.start_time == 5.5 assert seg.duration == 10.0 assert seg.has_audio is False def test_negative_start_time_clamped(self): - seg = ConcatSegment.from_dict({ - "video_path": "/tmp/a.mp4", - "start_time": -1.0, - }) + seg = ConcatSegment.from_dict( + { + "video_path": "/tmp/a.mp4", + "start_time": -1.0, + } + ) assert seg.start_time == 0.0 def test_negative_duration_clamped(self): - seg = ConcatSegment.from_dict({ - "video_path": "/tmp/a.mp4", - "duration": -5.0, - }) + seg = ConcatSegment.from_dict( + { + "video_path": "/tmp/a.mp4", + "duration": -5.0, + } + ) assert seg.duration == 0.0 def test_invalid_start_time_type_falls_back(self): - seg = ConcatSegment.from_dict({ - "video_path": "/tmp/a.mp4", - "start_time": "not_a_number", - }) + seg = ConcatSegment.from_dict( + { + "video_path": "/tmp/a.mp4", + "start_time": "not_a_number", + } + ) assert seg.start_time == 0.0 def test_invalid_duration_type_falls_back(self): - seg = ConcatSegment.from_dict({ - "video_path": "/tmp/a.mp4", - "duration": "abc", - }) + seg = ConcatSegment.from_dict( + { + "video_path": "/tmp/a.mp4", + "duration": "abc", + } + ) assert seg.duration == 0.0 def test_start_time_none_falls_back(self): - seg = ConcatSegment.from_dict({ - "video_path": "/tmp/a.mp4", - "start_time": None, - }) + seg = ConcatSegment.from_dict( + { + "video_path": "/tmp/a.mp4", + "start_time": None, + } + ) assert seg.start_time == 0.0 def test_empty_video_path_stored(self): @@ -94,97 +105,115 @@ class TestConcatConfigFromConfigDict: assert cfg.segments == [] def test_single_segment(self): - cfg = ConcatConfig.from_config_dict({ - "segments": [ - {"video_path": "/tmp/a.mp4", "duration": 5.0}, - ], - }) + cfg = ConcatConfig.from_config_dict( + { + "segments": [ + {"video_path": "/tmp/a.mp4", "duration": 5.0}, + ], + } + ) assert len(cfg.segments) == 1 assert cfg.segments[0].video_path == "/tmp/a.mp4" assert cfg.segments[0].duration == 5.0 def test_multiple_segments(self): - cfg = ConcatConfig.from_config_dict({ - "segments": [ - {"video_path": "/tmp/a.mp4"}, - {"video_path": "/tmp/b.mp4", "start_time": 2.0}, - {"video_path": "/tmp/c.mp4", "duration": 3.0, "has_audio": False}, - ], - }) + cfg = ConcatConfig.from_config_dict( + { + "segments": [ + {"video_path": "/tmp/a.mp4"}, + {"video_path": "/tmp/b.mp4", "start_time": 2.0}, + {"video_path": "/tmp/c.mp4", "duration": 3.0, "has_audio": False}, + ], + } + ) assert len(cfg.segments) == 3 assert cfg.segments[0].video_path == "/tmp/a.mp4" assert cfg.segments[1].start_time == 2.0 assert cfg.segments[2].has_audio is False def test_invalid_segments_filtered(self): - cfg = ConcatConfig.from_config_dict({ - "segments": [ - {"video_path": "/tmp/valid.mp4"}, - {"video_path": ""}, # 空路径被过滤 - {"not_video_path": "xxx"}, # 没有video_path被过滤 - "not_a_dict", # 不是dict被过滤 - None, # None被过滤 - ], - }) + cfg = ConcatConfig.from_config_dict( + { + "segments": [ + {"video_path": "/tmp/valid.mp4"}, + {"video_path": ""}, # 空路径被过滤 + {"not_video_path": "xxx"}, # 没有video_path被过滤 + "not_a_dict", # 不是dict被过滤 + None, # None被过滤 + ], + } + ) assert len(cfg.segments) == 1 assert cfg.segments[0].video_path == "/tmp/valid.mp4" def test_segments_not_a_list(self): - cfg = ConcatConfig.from_config_dict({ - "segments": "not_a_list", - }) + cfg = ConcatConfig.from_config_dict( + { + "segments": "not_a_list", + } + ) assert cfg.segments == [] def test_output_params(self): - cfg = ConcatConfig.from_config_dict({ - "segments": [], - "output_width": 1920, - "output_height": 1080, - "output_fps": 30.0, - "force_reencode": True, - }) + cfg = ConcatConfig.from_config_dict( + { + "segments": [], + "output_width": 1920, + "output_height": 1080, + "output_fps": 30.0, + "force_reencode": True, + } + ) assert cfg.output_width == 1920 assert cfg.output_height == 1080 assert cfg.output_fps == 30.0 assert cfg.force_reencode is True def test_negative_output_params_clamped(self): - cfg = ConcatConfig.from_config_dict({ - "segments": [], - "output_width": -100, - "output_height": -50, - "output_fps": -1.0, - }) + cfg = ConcatConfig.from_config_dict( + { + "segments": [], + "output_width": -100, + "output_height": -50, + "output_fps": -1.0, + } + ) assert cfg.output_width == 0 assert cfg.output_height == 0 assert cfg.output_fps == 0.0 def test_invalid_output_params_fall_back(self): - cfg = ConcatConfig.from_config_dict({ - "segments": [], - "output_width": "abc", - "output_height": None, - "output_fps": "xyz", - }) + cfg = ConcatConfig.from_config_dict( + { + "segments": [], + "output_width": "abc", + "output_height": None, + "output_fps": "xyz", + } + ) assert cfg.output_width == 0 assert cfg.output_height == 0 assert cfg.output_fps == 0.0 def test_transition_config(self): - cfg = ConcatConfig.from_config_dict({ - "segments": [], - "transition": "crossfade", - "transition_duration": 1.0, - }) + cfg = ConcatConfig.from_config_dict( + { + "segments": [], + "transition": "crossfade", + "transition_duration": 1.0, + } + ) assert cfg.transition == "crossfade" assert cfg.transition_duration == 1.0 def test_transition_duration_minimum(self): """transition_duration 不能小于 0.1.""" - cfg = ConcatConfig.from_config_dict({ - "segments": [], - "transition_duration": 0.01, - }) + cfg = ConcatConfig.from_config_dict( + { + "segments": [], + "transition_duration": 0.01, + } + ) assert cfg.transition_duration >= 0.1 def test_default_values(self): @@ -198,16 +227,20 @@ class TestConcatConfigProperties: """has_effect / total_segments 属性.""" def test_has_effect_two_or_more_valid(self): - cfg = ConcatConfig(segments=[ - ConcatSegment(video_path="/tmp/a.mp4"), - ConcatSegment(video_path="/tmp/b.mp4"), - ]) + cfg = ConcatConfig( + segments=[ + ConcatSegment(video_path="/tmp/a.mp4"), + ConcatSegment(video_path="/tmp/b.mp4"), + ] + ) assert cfg.has_effect is True def test_no_effect_one_segment(self): - cfg = ConcatConfig(segments=[ - ConcatSegment(video_path="/tmp/a.mp4"), - ]) + cfg = ConcatConfig( + segments=[ + ConcatSegment(video_path="/tmp/a.mp4"), + ] + ) assert cfg.has_effect is False def test_no_effect_zero_segments(self): @@ -215,18 +248,22 @@ class TestConcatConfigProperties: assert cfg.has_effect is False def test_no_effect_empty_paths(self): - cfg = ConcatConfig(segments=[ - ConcatSegment(video_path=""), - ConcatSegment(video_path=""), - ]) + cfg = ConcatConfig( + segments=[ + ConcatSegment(video_path=""), + ConcatSegment(video_path=""), + ] + ) assert cfg.has_effect is False def test_total_segments(self): - cfg = ConcatConfig(segments=[ - ConcatSegment(video_path="/tmp/a.mp4"), - ConcatSegment(video_path=""), - ConcatSegment(video_path="/tmp/b.mp4"), - ]) + cfg = ConcatConfig( + segments=[ + ConcatSegment(video_path="/tmp/a.mp4"), + ConcatSegment(video_path=""), + ConcatSegment(video_path="/tmp/b.mp4"), + ] + ) assert cfg.total_segments == 2 def test_total_segments_empty(self): diff --git a/tests/unit/test_config_schemas.py b/tests/unit/test_config_schemas.py index addac1f80..93e9a31be 100755 --- a/tests/unit/test_config_schemas.py +++ b/tests/unit/test_config_schemas.py @@ -6,12 +6,12 @@ import pytest from pydantic import ValidationError from packages.domain.config_schemas import ( - BGMSource, - BGMConfig, - CoverConfig, - CoverType, DEFAULT_EDIT_PLAN_CONFIG, DEFAULT_EDIT_TEMPLATE_CONFIG, + BGMConfig, + BGMSource, + CoverConfig, + CoverType, EditPlanConfigSchema, EditTemplateConfigSchema, ExportConfig, @@ -491,10 +491,12 @@ class TestNormalizeTemplateConfig: assert result["transition_enabled"] is True def test_updates_sections(self): - result = normalize_template_config({ - "title": {"text": "模板标题"}, - "bgm": {"enabled": True}, - }) + result = normalize_template_config( + { + "title": {"text": "模板标题"}, + "bgm": {"enabled": True}, + } + ) assert result["title"]["text"] == "模板标题" assert result["bgm"]["enabled"] is True diff --git a/tests/unit/test_domain_remaining_small_modules.py b/tests/unit/test_domain_remaining_small_modules.py index b312958c4..e77f586cb 100755 --- a/tests/unit/test_domain_remaining_small_modules.py +++ b/tests/unit/test_domain_remaining_small_modules.py @@ -35,7 +35,6 @@ from packages.domain.template_version import EditTemplateVersion from packages.domain.title_library import TitleLibraryItem from packages.domain.voice_library import VoiceLibraryItem - # ── EditingMode ─────────────────────────────────────────────────────────────── diff --git a/tests/unit/test_entities_domain.py b/tests/unit/test_entities_domain.py index 12d9c9c45..091537ec5 100755 --- a/tests/unit/test_entities_domain.py +++ b/tests/unit/test_entities_domain.py @@ -435,6 +435,7 @@ class TestAsset: ) old_updated = asset.updated_at import time + time.sleep(0.001) asset.add_tag("tag-1") assert asset.updated_at >= old_updated @@ -450,6 +451,7 @@ class TestAsset: asset.add_tag("tag-1") old_updated = asset.updated_at import time + time.sleep(0.001) asset.remove_tag("tag-1") assert asset.updated_at >= old_updated diff --git a/tests/unit/test_intro_outro_engine.py b/tests/unit/test_intro_outro_engine.py index f9112eca4..83ca48c21 100755 --- a/tests/unit/test_intro_outro_engine.py +++ b/tests/unit/test_intro_outro_engine.py @@ -6,7 +6,6 @@ """ import pytest - from video_processing.intro_outro_engine import IntroOutroConfig diff --git a/tests/unit/test_job_domain.py b/tests/unit/test_job_domain.py index d9d947f01..ba0cc9aa1 100755 --- a/tests/unit/test_job_domain.py +++ b/tests/unit/test_job_domain.py @@ -280,6 +280,7 @@ class TestJobTransitions: job = Job.create(project_id="p1", job_type=JobType.VIDEO_COMPOSE) old_updated = job.updated_at import time + time.sleep(0.001) job.transition_to(JobStatus.RUNNING) assert job.updated_at >= old_updated @@ -382,6 +383,7 @@ class TestJobProgress: job = Job.create(project_id="p1", job_type=JobType.VIDEO_COMPOSE) old_updated = job.updated_at import time + time.sleep(0.001) job.update_progress(50.0) assert job.updated_at >= old_updated diff --git a/tests/unit/test_multi_track_mixer.py b/tests/unit/test_multi_track_mixer.py index 244141413..67c7b3a04 100755 --- a/tests/unit/test_multi_track_mixer.py +++ b/tests/unit/test_multi_track_mixer.py @@ -8,7 +8,6 @@ from __future__ import annotations import pytest - from video_processing.multi_track_mixer import ( DEFAULT_VOLUMES, MAX_AUDIO_TRACKS, @@ -47,150 +46,180 @@ class TestAudioTrackFromDict: """AudioTrack.from_dict 构造逻辑.""" def test_basic(self): - track = AudioTrack.from_dict({ - "track_id": "t1", - "track_type": "bgm", - "audio_path": "/tmp/bgm.mp3", - }) + track = AudioTrack.from_dict( + { + "track_id": "t1", + "track_type": "bgm", + "audio_path": "/tmp/bgm.mp3", + } + ) assert track.track_id == "t1" assert track.track_type == "bgm" assert track.audio_path == "/tmp/bgm.mp3" assert track.volume == 0.3 # bgm 默认音量 def test_custom_volume(self): - track = AudioTrack.from_dict({ - "track_id": "t1", - "track_type": "main", - "audio_path": "/tmp/main.wav", - "volume": 0.8, - }) + track = AudioTrack.from_dict( + { + "track_id": "t1", + "track_type": "main", + "audio_path": "/tmp/main.wav", + "volume": 0.8, + } + ) assert track.volume == 0.8 def test_volume_clamped_to_zero(self): - track = AudioTrack.from_dict({ - "track_id": "t1", - "track_type": "sfx", - "audio_path": "/tmp/sfx.wav", - "volume": -1.0, - }) + track = AudioTrack.from_dict( + { + "track_id": "t1", + "track_type": "sfx", + "audio_path": "/tmp/sfx.wav", + "volume": -1.0, + } + ) assert track.volume == 0.0 def test_volume_clamped_to_max(self): - track = AudioTrack.from_dict({ - "track_id": "t1", - "track_type": "sfx", - "audio_path": "/tmp/sfx.wav", - "volume": 3.0, - }) + track = AudioTrack.from_dict( + { + "track_id": "t1", + "track_type": "sfx", + "audio_path": "/tmp/sfx.wav", + "volume": 3.0, + } + ) assert track.volume == 2.0 def test_invalid_volume_falls_back_to_default(self): - track = AudioTrack.from_dict({ - "track_id": "t1", - "track_type": "bgm", - "audio_path": "/tmp/bgm.mp3", - "volume": "not_a_number", - }) + track = AudioTrack.from_dict( + { + "track_id": "t1", + "track_type": "bgm", + "audio_path": "/tmp/bgm.mp3", + "volume": "not_a_number", + } + ) assert track.volume == 0.3 # bgm 默认 def test_none_volume_falls_back(self): - track = AudioTrack.from_dict({ - "track_id": "t1", - "track_type": "voiceover", - "audio_path": "/tmp/vo.wav", - "volume": None, - }) + track = AudioTrack.from_dict( + { + "track_id": "t1", + "track_type": "voiceover", + "audio_path": "/tmp/vo.wav", + "volume": None, + } + ) assert track.volume == 1.0 # voiceover 默认 def test_unknown_track_type_default_volume(self): - track = AudioTrack.from_dict({ - "track_id": "t1", - "track_type": "unknown_type", - "audio_path": "/tmp/a.wav", - }) + track = AudioTrack.from_dict( + { + "track_id": "t1", + "track_type": "unknown_type", + "audio_path": "/tmp/a.wav", + } + ) assert track.volume == 1.0 # 未知类型默认 1.0 def test_fade_in_fade_out(self): - track = AudioTrack.from_dict({ - "track_id": "t1", - "track_type": "bgm", - "audio_path": "/tmp/bgm.mp3", - "fade_in": 1.5, - "fade_out": 2.0, - }) + track = AudioTrack.from_dict( + { + "track_id": "t1", + "track_type": "bgm", + "audio_path": "/tmp/bgm.mp3", + "fade_in": 1.5, + "fade_out": 2.0, + } + ) assert track.fade_in == 1.5 assert track.fade_out == 2.0 def test_negative_fade_clamped(self): - track = AudioTrack.from_dict({ - "track_id": "t1", - "track_type": "bgm", - "audio_path": "/tmp/bgm.mp3", - "fade_in": -0.5, - "fade_out": -1.0, - }) + track = AudioTrack.from_dict( + { + "track_id": "t1", + "track_type": "bgm", + "audio_path": "/tmp/bgm.mp3", + "fade_in": -0.5, + "fade_out": -1.0, + } + ) assert track.fade_in == 0.0 assert track.fade_out == 0.0 def test_invalid_fade_falls_back(self): - track = AudioTrack.from_dict({ - "track_id": "t1", - "track_type": "bgm", - "audio_path": "/tmp/bgm.mp3", - "fade_in": "abc", - "fade_out": None, - }) + track = AudioTrack.from_dict( + { + "track_id": "t1", + "track_type": "bgm", + "audio_path": "/tmp/bgm.mp3", + "fade_in": "abc", + "fade_out": None, + } + ) assert track.fade_in == 0.0 assert track.fade_out == 0.0 def test_start_time_and_duration(self): - track = AudioTrack.from_dict({ - "track_id": "t1", - "track_type": "sfx", - "audio_path": "/tmp/sfx.wav", - "start_time": 5.0, - "duration": 3.0, - }) + track = AudioTrack.from_dict( + { + "track_id": "t1", + "track_type": "sfx", + "audio_path": "/tmp/sfx.wav", + "start_time": 5.0, + "duration": 3.0, + } + ) assert track.start_time == 5.0 assert track.duration == 3.0 def test_negative_start_time_clamped(self): - track = AudioTrack.from_dict({ - "track_id": "t1", - "track_type": "bgm", - "audio_path": "/tmp/bgm.mp3", - "start_time": -10.0, - "duration": -2.0, - }) + track = AudioTrack.from_dict( + { + "track_id": "t1", + "track_type": "bgm", + "audio_path": "/tmp/bgm.mp3", + "start_time": -10.0, + "duration": -2.0, + } + ) assert track.start_time == 0.0 assert track.duration == 0.0 def test_invalid_time_values_fall_back(self): - track = AudioTrack.from_dict({ - "track_id": "t1", - "track_type": "bgm", - "audio_path": "/tmp/bgm.mp3", - "start_time": "invalid", - "duration": "bad", - }) + track = AudioTrack.from_dict( + { + "track_id": "t1", + "track_type": "bgm", + "audio_path": "/tmp/bgm.mp3", + "start_time": "invalid", + "duration": "bad", + } + ) assert track.start_time == 0.0 assert track.duration == 0.0 def test_enabled_default_true(self): - track = AudioTrack.from_dict({ - "track_id": "t1", - "track_type": "bgm", - "audio_path": "/tmp/bgm.mp3", - }) + track = AudioTrack.from_dict( + { + "track_id": "t1", + "track_type": "bgm", + "audio_path": "/tmp/bgm.mp3", + } + ) assert track.enabled is True def test_enabled_can_be_false(self): - track = AudioTrack.from_dict({ - "track_id": "t1", - "track_type": "bgm", - "audio_path": "/tmp/bgm.mp3", - "enabled": False, - }) + track = AudioTrack.from_dict( + { + "track_id": "t1", + "track_type": "bgm", + "audio_path": "/tmp/bgm.mp3", + "enabled": False, + } + ) assert track.enabled is False @@ -212,104 +241,126 @@ class TestMultiTrackMixConfigFromDict: assert cfg.tracks == [] def test_single_track(self): - cfg = MultiTrackMixConfig.from_config_dict({ - "tracks": [ - { - "track_id": "bgm1", - "track_type": "bgm", - "audio_path": "/tmp/bgm.mp3", - "volume": 0.5, - }, - ], - }) + cfg = MultiTrackMixConfig.from_config_dict( + { + "tracks": [ + { + "track_id": "bgm1", + "track_type": "bgm", + "audio_path": "/tmp/bgm.mp3", + "volume": 0.5, + }, + ], + } + ) assert len(cfg.tracks) == 1 assert cfg.tracks[0].track_id == "bgm1" assert cfg.tracks[0].volume == 0.5 def test_multiple_tracks(self): - cfg = MultiTrackMixConfig.from_config_dict({ - "tracks": [ - {"track_id": "m", "track_type": "main", "audio_path": "/tmp/m.wav"}, - {"track_id": "b", "track_type": "bgm", "audio_path": "/tmp/b.mp3"}, - {"track_id": "v", "track_type": "voiceover", "audio_path": "/tmp/v.wav"}, - ], - }) + cfg = MultiTrackMixConfig.from_config_dict( + { + "tracks": [ + {"track_id": "m", "track_type": "main", "audio_path": "/tmp/m.wav"}, + {"track_id": "b", "track_type": "bgm", "audio_path": "/tmp/b.mp3"}, + {"track_id": "v", "track_type": "voiceover", "audio_path": "/tmp/v.wav"}, + ], + } + ) assert len(cfg.tracks) == 3 assert cfg.tracks[0].track_type == "main" assert cfg.tracks[1].track_type == "bgm" assert cfg.tracks[2].track_type == "voiceover" def test_disabled_tracks_filtered(self): - cfg = MultiTrackMixConfig.from_config_dict({ - "tracks": [ - {"track_id": "a", "track_type": "sfx", "audio_path": "/tmp/a.wav"}, - {"track_id": "b", "track_type": "sfx", "audio_path": "/tmp/b.wav", "enabled": False}, - {"track_id": "c", "track_type": "sfx", "audio_path": "/tmp/c.wav"}, - ], - }) + cfg = MultiTrackMixConfig.from_config_dict( + { + "tracks": [ + {"track_id": "a", "track_type": "sfx", "audio_path": "/tmp/a.wav"}, + {"track_id": "b", "track_type": "sfx", "audio_path": "/tmp/b.wav", "enabled": False}, + {"track_id": "c", "track_type": "sfx", "audio_path": "/tmp/c.wav"}, + ], + } + ) assert len(cfg.tracks) == 2 assert all(t.track_id != "b" for t in cfg.tracks) def test_empty_audio_path_filtered(self): - cfg = MultiTrackMixConfig.from_config_dict({ - "tracks": [ - {"track_id": "valid", "track_type": "sfx", "audio_path": "/tmp/a.wav"}, - {"track_id": "empty", "track_type": "sfx", "audio_path": ""}, - ], - }) + cfg = MultiTrackMixConfig.from_config_dict( + { + "tracks": [ + {"track_id": "valid", "track_type": "sfx", "audio_path": "/tmp/a.wav"}, + {"track_id": "empty", "track_type": "sfx", "audio_path": ""}, + ], + } + ) assert len(cfg.tracks) == 1 assert cfg.tracks[0].track_id == "valid" def test_invalid_tracks_skipped(self): - cfg = MultiTrackMixConfig.from_config_dict({ - "tracks": [ - {"track_id": "ok", "track_type": "sfx", "audio_path": "/tmp/a.wav"}, - "not_a_dict", - None, - {"no_audio_path": "xxx"}, - ], - }) + cfg = MultiTrackMixConfig.from_config_dict( + { + "tracks": [ + {"track_id": "ok", "track_type": "sfx", "audio_path": "/tmp/a.wav"}, + "not_a_dict", + None, + {"no_audio_path": "xxx"}, + ], + } + ) assert len(cfg.tracks) == 1 def test_tracks_not_a_list(self): - cfg = MultiTrackMixConfig.from_config_dict({ - "tracks": "not_a_list", - }) + cfg = MultiTrackMixConfig.from_config_dict( + { + "tracks": "not_a_list", + } + ) assert cfg.tracks == [] def test_master_volume(self): - cfg = MultiTrackMixConfig.from_config_dict({ - "tracks": [], - "master_volume": 0.8, - }) + cfg = MultiTrackMixConfig.from_config_dict( + { + "tracks": [], + "master_volume": 0.8, + } + ) assert cfg.master_volume == 0.8 def test_master_volume_clamped(self): - cfg = MultiTrackMixConfig.from_config_dict({ - "tracks": [], - "master_volume": 3.0, - }) + cfg = MultiTrackMixConfig.from_config_dict( + { + "tracks": [], + "master_volume": 3.0, + } + ) assert cfg.master_volume == 2.0 - cfg2 = MultiTrackMixConfig.from_config_dict({ - "tracks": [], - "master_volume": -1.0, - }) + cfg2 = MultiTrackMixConfig.from_config_dict( + { + "tracks": [], + "master_volume": -1.0, + } + ) assert cfg2.master_volume == 0.0 def test_invalid_master_volume_falls_back(self): - cfg = MultiTrackMixConfig.from_config_dict({ - "tracks": [], - "master_volume": "abc", - }) + cfg = MultiTrackMixConfig.from_config_dict( + { + "tracks": [], + "master_volume": "abc", + } + ) assert cfg.master_volume == 1.0 def test_normalize_and_max_output(self): - cfg = MultiTrackMixConfig.from_config_dict({ - "tracks": [], - "normalize": False, - "max_output_volume": 2.0, - }) + cfg = MultiTrackMixConfig.from_config_dict( + { + "tracks": [], + "normalize": False, + "max_output_volume": 2.0, + } + ) assert cfg.normalize is False assert cfg.max_output_volume == 2.0 @@ -324,9 +375,11 @@ class TestMultiTrackMixConfigProperties: """has_effect 属性.""" def test_has_effect_with_tracks(self): - cfg = MultiTrackMixConfig(tracks=[ - AudioTrack(track_id="t1", track_type="bgm", audio_path="/tmp/a.mp3"), - ]) + cfg = MultiTrackMixConfig( + tracks=[ + AudioTrack(track_id="t1", track_type="bgm", audio_path="/tmp/a.mp3"), + ] + ) assert cfg.has_effect is True def test_no_effect_empty(self): @@ -334,13 +387,17 @@ class TestMultiTrackMixConfigProperties: assert cfg.has_effect is False def test_no_effect_all_disabled(self): - cfg = MultiTrackMixConfig(tracks=[ - AudioTrack(track_id="t1", track_type="bgm", audio_path="/tmp/a.mp3", enabled=False), - ]) + cfg = MultiTrackMixConfig( + tracks=[ + AudioTrack(track_id="t1", track_type="bgm", audio_path="/tmp/a.mp3", enabled=False), + ] + ) assert cfg.has_effect is False def test_no_effect_empty_paths(self): - cfg = MultiTrackMixConfig(tracks=[ - AudioTrack(track_id="t1", track_type="bgm", audio_path=""), - ]) + cfg = MultiTrackMixConfig( + tracks=[ + AudioTrack(track_id="t1", track_type="bgm", audio_path=""), + ] + ) assert cfg.has_effect is False diff --git a/tests/unit/test_quota_domain.py b/tests/unit/test_quota_domain.py index e282a71ae..c00023f17 100755 --- a/tests/unit/test_quota_domain.py +++ b/tests/unit/test_quota_domain.py @@ -6,8 +6,8 @@ import pytest from packages.domain.quota import ( QUOTA_TIERS, - QuotaCheckResult, QuotaChecker, + QuotaCheckResult, QuotaDimension, QuotaRegistry, QuotaTier, diff --git a/tests/unit/test_render_subtitles.py b/tests/unit/test_render_subtitles.py index 16cb5bc71..bef01d8ce 100755 --- a/tests/unit/test_render_subtitles.py +++ b/tests/unit/test_render_subtitles.py @@ -10,7 +10,6 @@ from __future__ import annotations from pathlib import Path import pytest - from video_processing.render_subtitles import ( _build_ass_style, _escape_ass_text, @@ -49,7 +48,7 @@ class TestHexToAssColor: def test_invalid_length_returns_default(self): assert _hex_to_ass_color("#FFF") == "&H000000" # 3位 - assert _hex_to_ass_color("") == "&H000000" # 空 + assert _hex_to_ass_color("") == "&H000000" # 空 def test_mixed_case(self): result = _hex_to_ass_color("#aBcDeF") diff --git a/tests/unit/test_thumbnail_generator.py b/tests/unit/test_thumbnail_generator.py index a32b7f442..91c5f53c4 100755 --- a/tests/unit/test_thumbnail_generator.py +++ b/tests/unit/test_thumbnail_generator.py @@ -8,7 +8,6 @@ FFmpeg 抽帧与 OSS 上传由集成测试覆盖. from __future__ import annotations import pytest - from video_processing.thumbnail_generator import _format_seek_time diff --git a/tests/unit/test_transition_presets.py b/tests/unit/test_transition_presets.py index 59ca70434..d2c2d2d9a 100755 --- a/tests/unit/test_transition_presets.py +++ b/tests/unit/test_transition_presets.py @@ -104,10 +104,8 @@ class TestTransitionPresetLibrary: def test_duration_constraints_valid(self): """时长约束:min <= default <= max""" for preset in TRANSITION_PRESET_LIBRARY: - assert preset.min_duration <= preset.default_duration, \ - f"{preset.id}: min > default" - assert preset.default_duration <= preset.max_duration, \ - f"{preset.id}: default > max" + assert preset.min_duration <= preset.default_duration, f"{preset.id}: min > default" + assert preset.default_duration <= preset.max_duration, f"{preset.id}: default > max" assert preset.min_duration >= 0, f"{preset.id}: min < 0" def test_known_categories_exist(self): diff --git a/tests/unit/test_tts_job_domain.py b/tests/unit/test_tts_job_domain.py index de26cb11b..5e877f14a 100755 --- a/tests/unit/test_tts_job_domain.py +++ b/tests/unit/test_tts_job_domain.py @@ -216,6 +216,7 @@ class TestTTSJobTransitions: job = TTSJob.create(user_id="u1", input_text="test") old = job.updated_at import time + time.sleep(0.001) job.transition_to(TTSJobStatus.PROCESSING) assert job.updated_at >= old diff --git a/tests/unit/test_video_share_domain.py b/tests/unit/test_video_share_domain.py index 2d3d36753..5aa697392 100755 --- a/tests/unit/test_video_share_domain.py +++ b/tests/unit/test_video_share_domain.py @@ -69,6 +69,7 @@ class TestHashPassword: def test_hash_includes_salt(self): """加盐后与直接 SHA-256 不同""" from hashlib import sha256 + direct = sha256("mypass".encode()).hexdigest() salted = _hash_password("mypass") assert direct != salted diff --git a/tests/unit/test_voice_clone_profile_domain.py b/tests/unit/test_voice_clone_profile_domain.py index 55c531a52..db3faadb2 100755 --- a/tests/unit/test_voice_clone_profile_domain.py +++ b/tests/unit/test_voice_clone_profile_domain.py @@ -69,15 +69,11 @@ class TestVoiceCloneProfileCreate: assert profile.name == name def test_create_with_description(self): - profile = VoiceCloneProfile.create( - user_id="u1", name="test", description="温暖男声" - ) + profile = VoiceCloneProfile.create(user_id="u1", name="test", description="温暖男声") assert profile.description == "温暖男声" def test_create_with_source_audio(self): - profile = VoiceCloneProfile.create( - user_id="u1", name="test", source_audio_url="http://audio.com/source.wav" - ) + profile = VoiceCloneProfile.create(user_id="u1", name="test", source_audio_url="http://audio.com/source.wav") assert profile.source_audio_url == "http://audio.com/source.wav" def test_create_with_language(self): @@ -251,6 +247,7 @@ class TestVoiceCloneProfileTransitions: p = VoiceCloneProfile.create(user_id="u1", name="test") old = p.updated_at import time + time.sleep(0.001) p.transition_to(VoiceCloneStatus.PROCESSING) assert p.updated_at >= old diff --git a/tests/unit/test_watermark_engine.py b/tests/unit/test_watermark_engine.py index 5a8711d0d..a3c0ae293 100755 --- a/tests/unit/test_watermark_engine.py +++ b/tests/unit/test_watermark_engine.py @@ -6,7 +6,6 @@ """ import pytest - from video_processing.watermark_engine import WATERMARK_POSITIONS, WatermarkConfig @@ -44,46 +43,56 @@ class TestWatermarkConfigFromDict: assert WatermarkConfig.from_dict({"enabled": False}) is None def test_image_mode_without_path_returns_none(self): - result = WatermarkConfig.from_dict({ - "enabled": True, - "mode": "image", - }) + result = WatermarkConfig.from_dict( + { + "enabled": True, + "mode": "image", + } + ) assert result is None def test_image_mode_with_empty_path_returns_none(self): - result = WatermarkConfig.from_dict({ - "enabled": True, - "mode": "image", - "image_path": "", - }) + result = WatermarkConfig.from_dict( + { + "enabled": True, + "mode": "image", + "image_path": "", + } + ) assert result is None def test_text_mode_without_text_returns_none(self): - result = WatermarkConfig.from_dict({ - "enabled": True, - "mode": "text", - }) + result = WatermarkConfig.from_dict( + { + "enabled": True, + "mode": "text", + } + ) assert result is None def test_text_mode_with_empty_text_returns_none(self): - result = WatermarkConfig.from_dict({ - "enabled": True, - "mode": "text", - "text": "", - }) + result = WatermarkConfig.from_dict( + { + "enabled": True, + "mode": "text", + "text": "", + } + ) assert result is None def test_image_mode_success(self): - cfg = WatermarkConfig.from_dict({ - "enabled": True, - "mode": "image", - "image_path": "/tmp/logo.png", - "scale": 0.3, - "opacity": 0.9, - "position": "top_left", - "margin_x": 30, - "margin_y": 30, - }) + cfg = WatermarkConfig.from_dict( + { + "enabled": True, + "mode": "image", + "image_path": "/tmp/logo.png", + "scale": 0.3, + "opacity": 0.9, + "position": "top_left", + "margin_x": 30, + "margin_y": 30, + } + ) assert cfg is not None assert cfg.mode == "image" assert cfg.image_path == "/tmp/logo.png" @@ -95,25 +104,29 @@ class TestWatermarkConfigFromDict: def test_image_mode_image_key_fallback(self): """image 字段作为 image_path 的 fallback.""" - cfg = WatermarkConfig.from_dict({ - "enabled": True, - "mode": "image", - "image": "/tmp/fallback.png", - }) + cfg = WatermarkConfig.from_dict( + { + "enabled": True, + "mode": "image", + "image": "/tmp/fallback.png", + } + ) assert cfg is not None assert cfg.image_path == "/tmp/fallback.png" def test_text_mode_success(self): - cfg = WatermarkConfig.from_dict({ - "enabled": True, - "mode": "text", - "text": "hello world", - "font_size": 32, - "font_color": "red", - "position": "bottom_left", - "scroll": True, - "scroll_speed": 100, - }) + cfg = WatermarkConfig.from_dict( + { + "enabled": True, + "mode": "text", + "text": "hello world", + "font_size": 32, + "font_color": "red", + "position": "bottom_left", + "scroll": True, + "scroll_speed": 100, + } + ) assert cfg is not None assert cfg.mode == "text" assert cfg.text == "hello world" @@ -124,21 +137,25 @@ class TestWatermarkConfigFromDict: assert cfg.scroll_speed == 100 def test_invalid_position_falls_back_to_bottom_right(self): - cfg = WatermarkConfig.from_dict({ - "enabled": True, - "mode": "text", - "text": "test", - "position": "invalid_position", - }) + cfg = WatermarkConfig.from_dict( + { + "enabled": True, + "mode": "text", + "text": "test", + "position": "invalid_position", + } + ) assert cfg is not None assert cfg.position == "bottom_right" def test_default_values_applied(self): - cfg = WatermarkConfig.from_dict({ - "enabled": True, - "mode": "text", - "text": "test", - }) + cfg = WatermarkConfig.from_dict( + { + "enabled": True, + "mode": "text", + "text": "test", + } + ) assert cfg is not None assert cfg.position == "bottom_right" assert cfg.opacity == 0.8