From dfbd1418958fb57828304a63f760f047fb729b59 Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Sat, 25 Jul 2026 22:33:26 +0800 Subject: [PATCH] =?UTF-8?q?test(wave83):=20generated=5Fvideo/verification/?= =?UTF-8?q?video=5Fshare=E5=8D=95=E6=B5=8B=20+55=20(#891)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/unit/test_generated_video_domain.py | 114 ++++++++++++++++++++ tests/unit/test_verification_code_domain.py | 85 +++++++++++++++ tests/unit/test_video_share_domain.py | 97 +++++++++++++++++ 3 files changed, 296 insertions(+) mode change 100644 => 100755 tests/unit/test_verification_code_domain.py diff --git a/tests/unit/test_generated_video_domain.py b/tests/unit/test_generated_video_domain.py index d8e9fc42b..204cf4dfc 100755 --- a/tests/unit/test_generated_video_domain.py +++ b/tests/unit/test_generated_video_domain.py @@ -194,3 +194,117 @@ class TestGeneratedVideoProperties: fingerprint = {"phash": "abc123", "md5": "def456"} gv.video_fingerprint = fingerprint assert gv.video_fingerprint == fingerprint + + +class TestGeneratedVideoExtended: + """GeneratedVideo 深度补充测试""" + + def test_id_is_hex(self): + v = GeneratedVideo.create(project_id="p", generation_task_id="t", name="n", file_url="u") + int(v.id, 16) + + def test_zero_file_size(self): + v = GeneratedVideo.create(project_id="p", generation_task_id="t", name="n", file_url="u", file_size=0) + assert v.file_size == 0 + + def test_large_file_size(self): + large = 1024 * 1024 * 1024 # 1GB + v = GeneratedVideo.create(project_id="p", generation_task_id="t", name="n", file_url="u", file_size=large) + assert v.file_size == large + + def test_zero_duration(self): + v = GeneratedVideo.create(project_id="p", generation_task_id="t", name="n", file_url="u", duration=0.0) + assert v.duration == 0.0 + + def test_large_duration(self): + v = GeneratedVideo.create(project_id="p", generation_task_id="t", name="n", file_url="u", duration=9999.99) + assert v.duration == 9999.99 + + def test_zero_dimensions(self): + v = GeneratedVideo.create(project_id="p", generation_task_id="t", name="n", file_url="u", width=0, height=0) + assert v.width == 0 + assert v.height == 0 + + def test_4k_dimensions(self): + v = GeneratedVideo.create( + project_id="p", generation_task_id="t", name="n", file_url="u", width=3840, height=2160 + ) + assert v.width == 3840 + assert v.height == 2160 + + def test_zero_fps(self): + v = GeneratedVideo.create(project_id="p", generation_task_id="t", name="n", file_url="u", fps=0.0) + assert v.fps == 0.0 + + def test_high_fps(self): + v = GeneratedVideo.create(project_id="p", generation_task_id="t", name="n", file_url="u", fps=120.0) + assert v.fps == 120.0 + + def test_empty_user_id(self): + v = GeneratedVideo.create(project_id="p", generation_task_id="t", name="n", file_url="u", user_id="") + assert v.user_id == "" + + def test_long_name(self): + long_name = "视频" * 100 + v = GeneratedVideo.create(project_id="p", generation_task_id="t", name=long_name, file_url="u") + assert v.name == long_name + assert len(v.name) == 200 + + def test_unicode_name(self): + v = GeneratedVideo.create( + project_id="p", generation_task_id="t", name="🎬 我的精彩视频 · 旅行vlog", file_url="u" + ) + assert "🎬" in v.name + assert "旅行vlog" in v.name + + def test_special_characters_name(self): + special = "视!@#$%^&*()频" + v = GeneratedVideo.create(project_id="p", generation_task_id="t", name=special, file_url="u") + assert v.name == special + + def test_thumbnail_url_none(self): + v = GeneratedVideo.create(project_id="p", generation_task_id="t", name="n", file_url="u", thumbnail_url=None) + assert v.thumbnail_url is None + + def test_generation_params_complex(self): + params = { + "mode": "voice_over", + "quality": "high", + "resolution": {"width": 1920, "height": 1080}, + "effects": ["filter", "transition"], + } + v = GeneratedVideo.create( + project_id="p", generation_task_id="t", name="n", file_url="u", generation_params=params + ) + assert v.generation_params["mode"] == "voice_over" + assert v.generation_params["resolution"]["width"] == 1920 + assert len(v.generation_params["effects"]) == 2 + + def test_generation_params_independence(self): + v1 = GeneratedVideo.create(project_id="p", generation_task_id="t1", name="n1", file_url="u1") + v2 = GeneratedVideo.create(project_id="p", generation_task_id="t2", name="n2", file_url="u2") + v1.generation_params["key"] = "val" + assert "key" not in v2.generation_params + + def test_status_failed(self): + v = GeneratedVideo.create(project_id="p", generation_task_id="t", name="n", file_url="u") + v.status = "failed" + assert v.status == "failed" + + def test_review_status_approved(self): + v = GeneratedVideo.create(project_id="p", generation_task_id="t", name="n", file_url="u") + v.review_status = "approved" + assert v.review_status == "approved" + + def test_is_duplicate_default_false(self): + v = GeneratedVideo.create(project_id="p", generation_task_id="t", name="n", file_url="u") + assert v.is_duplicate is False + assert v.duplicate_of is None + + def test_fingerprint_none_default(self): + v = GeneratedVideo.create(project_id="p", generation_task_id="t", name="n", file_url="u") + assert v.video_fingerprint is None + + def test_empty_file_url_raises(self): + with pytest.raises(ValueError): + GeneratedVideo.create(project_id="p", generation_task_id="t", name="n", file_url=" ") diff --git a/tests/unit/test_verification_code_domain.py b/tests/unit/test_verification_code_domain.py old mode 100644 new mode 100755 index 97a871ac5..d4a01363d --- a/tests/unit/test_verification_code_domain.py +++ b/tests/unit/test_verification_code_domain.py @@ -166,3 +166,88 @@ class TestVerificationCodeTypes: vc = VerificationCode.create("test@example.com", code_type) assert vc.code_type == code_type assert vc.is_valid is True + + +class TestVerificationCodeExtended: + """VerificationCode 深度补充测试""" + + def test_id_is_hex(self): + vc = VerificationCode.create("test@example.com", "email_bind") + int(vc.id, 16) + + def test_ids_are_unique(self): + vc1 = VerificationCode.create("test@example.com", "email_bind") + vc2 = VerificationCode.create("test@example.com", "email_bind") + assert vc1.id != vc2.id + + def test_slots_no_extra_attrs(self): + import pytest + + vc = VerificationCode.create("test@example.com", "email_bind") + with pytest.raises(AttributeError): + vc.new_field = "value" # type: ignore[attr-defined] + + def test_custom_code_non_numeric(self): + """自定义 code 可以是非数字""" + vc = VerificationCode.create("test@example.com", "email_bind", custom_code="abcdef") + assert vc.code == "abcdef" + + def test_custom_code_short(self): + vc = VerificationCode.create("test@example.com", "email_bind", custom_code="12") + assert vc.code == "12" + + def test_custom_code_long(self): + long_code = "1" * 20 + vc = VerificationCode.create("test@example.com", "email_bind", custom_code=long_code) + assert vc.code == long_code + assert len(vc.code) == 20 + + def test_ttl_zero_expires_immediately(self): + vc = VerificationCode.create("test@example.com", "email_bind", ttl_seconds=0) + # ttl=0 时 expires_at = now,可能已过期或刚好 + assert vc.expires_at is not None + + def test_very_long_ttl(self): + vc = VerificationCode.create("test@example.com", "email_bind", ttl_seconds=86400) + assert vc.is_expired is False + + def test_negative_ttl_expired(self): + vc = VerificationCode.create("test@example.com", "email_bind", ttl_seconds=-100) + assert vc.is_expired is True + + def test_attempts_starts_at_zero(self): + vc = VerificationCode.create("test@example.com", "email_bind") + assert vc.attempts == 0 + + def test_increment_attempts_returns_none(self): + vc = VerificationCode.create("test@example.com", "email_bind") + result = vc.increment_attempts() + assert result is None + assert vc.attempts == 1 + + def test_mark_used_returns_none(self): + vc = VerificationCode.create("test@example.com", "email_bind") + result = vc.mark_used() + assert result is None + assert vc.is_used is True + + def test_code_type_empty_string(self): + vc = VerificationCode.create("test@example.com", "") + assert vc.code_type == "" + + def test_recipient_empty_string(self): + vc = VerificationCode.create("", "email_bind") + assert vc.recipient == "" + + def test_created_at_equals_expires_minus_ttl(self): + vc = VerificationCode.create("test@example.com", "email_bind", ttl_seconds=60) + diff = (vc.expires_at - vc.created_at).total_seconds() + assert abs(diff - 60) < 2 + + def test_multiple_mark_used_updates_time(self): + vc = VerificationCode.create("test@example.com", "email_bind") + vc.mark_used() + first = vc.used_at + vc.mark_used() + second = vc.used_at + assert second >= first diff --git a/tests/unit/test_video_share_domain.py b/tests/unit/test_video_share_domain.py index 5aa697392..8eef9b85a 100755 --- a/tests/unit/test_video_share_domain.py +++ b/tests/unit/test_video_share_domain.py @@ -223,3 +223,100 @@ class TestVideoShareCounters: share.revoke() assert share.is_active is False assert share.is_accessible is False + + +class TestGenerateShareTokenExtended: + """generate_share_token 深度补充测试""" + + def test_zero_length(self): + token = generate_share_token(0) + assert token == "" + + def test_length_one(self): + token = generate_share_token(1) + assert len(token) == 1 + + def test_very_long_token(self): + token = generate_share_token(100) + assert len(token) == 100 + + def test_no_special_characters(self): + token = generate_share_token(50) + assert token.isalnum() + + def test_all_characters_from_alphabet(self): + alphabet = set("abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ23456789") + token = generate_share_token(200) + for c in token: + assert c in alphabet + + +class TestVideoShareExtended: + """VideoShare 深度补充测试""" + + def test_zero_view_count(self): + share = VideoShare.create(video_id="v1", user_id="u1") + assert share.view_count == 0 + + def test_zero_download_count(self): + share = VideoShare.create(video_id="v1", user_id="u1") + assert share.download_count == 0 + + def test_large_view_count(self): + share = VideoShare.create(video_id="v1", user_id="u1") + for _ in range(1000): + share.increment_view_count() + assert share.view_count == 1000 + + def test_revoke_idempotent(self): + share = VideoShare.create(video_id="v1", user_id="u1") + share.revoke() + assert share.is_active is False + share.revoke() + assert share.is_active is False + + def test_revoke_returns_none(self): + share = VideoShare.create(video_id="v1", user_id="u1") + result = share.revoke() + assert result is None + + def test_increment_view_returns_none(self): + share = VideoShare.create(video_id="v1", user_id="u1") + result = share.increment_view_count() + assert result is None + + def test_increment_download_returns_none(self): + share = VideoShare.create(video_id="v1", user_id="u1") + result = share.increment_download_count() + assert result is None + + def test_id_is_hex(self): + share = VideoShare.create(video_id="v1", user_id="u1") + int(share.id, 16) + + def test_ids_are_unique(self): + s1 = VideoShare.create(video_id="v1", user_id="u1") + s2 = VideoShare.create(video_id="v1", user_id="u1") + assert s1.id != s2.id + + def test_expires_at_boundary_exact_now(self): + """expires_at 恰好是现在,应该被认为过期""" + share = VideoShare.create(video_id="v1", user_id="u1") + share.expires_at = datetime.now(timezone.utc) - timedelta(microseconds=1) + assert share.is_expired is True + + def test_expires_at_boundary_one_second_future(self): + share = VideoShare.create(video_id="v1", user_id="u1") + share.expires_at = datetime.now(timezone.utc) + timedelta(seconds=1) + assert share.is_expired is False + + def test_password_with_special_characters(self): + special_pass = "pass!@#$%^&*()" + share = VideoShare.create(video_id="v1", user_id="u1", password=special_pass) + assert share.verify_password(special_pass) is True + assert share.verify_password("wrong") is False + + def test_password_unicode(self): + unicode_pass = "密码🔐测试" + share = VideoShare.create(video_id="v1", user_id="u1", password=unicode_pass) + assert share.verify_password(unicode_pass) is True