test(wave83): generated_video/verification/video_share单测 +55 (#891)
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m54s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m1s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m14s
CI/CD Pipeline / Frontend Lint (push) Successful in 2m22s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 1m8s
CI/CD Pipeline / Validate - Code Quality (push) Failing after 3m33s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m25s
CI/CD Pipeline / Integration Tests (push) Successful in 2m38s
CI/CD Pipeline / Unit Tests (push) Failing after 6m7s
CI/CD Pipeline / Build Staging API Image (push) Successful in 12m16s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 3m12s
CI/CD Pipeline / ACR Image Cleanup (push) Failing after 13s
CI/CD Pipeline / Staging E2E Tests (push) Successful in 5m30s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 6m24s
CI/CD Pipeline / Canary Release to Production (push) Failing after 1264h24m52s
CI/CD Pipeline / Production Browser E2E (push) Failing after 1264h44m33s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 1264h44m53s
CI/CD Pipeline / Build Production Web Image (push) Failing after 1264h44m55s
CI/CD Pipeline / Build Production API Image (push) Failing after 1264h44m57s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 1264h46m51s
CI/CD Pipeline / PR Build Web Image (push) Failing after 1264h46m51s
CI/CD Pipeline / PR Build API Image (push) Failing after 1264h46m51s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 1264h46m53s
CI/CD Pipeline / Deploy Production (push) Failing after 1265h17m12s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m54s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m1s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m14s
CI/CD Pipeline / Frontend Lint (push) Successful in 2m22s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 1m8s
CI/CD Pipeline / Validate - Code Quality (push) Failing after 3m33s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m25s
CI/CD Pipeline / Integration Tests (push) Successful in 2m38s
CI/CD Pipeline / Unit Tests (push) Failing after 6m7s
CI/CD Pipeline / Build Staging API Image (push) Successful in 12m16s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 3m12s
CI/CD Pipeline / ACR Image Cleanup (push) Failing after 13s
CI/CD Pipeline / Staging E2E Tests (push) Successful in 5m30s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 6m24s
CI/CD Pipeline / Canary Release to Production (push) Failing after 1264h24m52s
CI/CD Pipeline / Production Browser E2E (push) Failing after 1264h44m33s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 1264h44m53s
CI/CD Pipeline / Build Production Web Image (push) Failing after 1264h44m55s
CI/CD Pipeline / Build Production API Image (push) Failing after 1264h44m57s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 1264h46m51s
CI/CD Pipeline / PR Build Web Image (push) Failing after 1264h46m51s
CI/CD Pipeline / PR Build API Image (push) Failing after 1264h46m51s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 1264h46m53s
CI/CD Pipeline / Deploy Production (push) Failing after 1265h17m12s
This commit is contained in:
@@ -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=" ")
|
||||
|
||||
Regular → Executable
+85
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user