test(unit): P3-1 第四波 新增3个领域模块单元测试(65个用例) #682

Merged
auto-approve-bot merged 1 commits from test/p3-1-wave4-domain-tests into develop 2026-07-21 19:38:28 +08:00
3 changed files with 258 additions and 0 deletions
+35
View File
@@ -102,3 +102,38 @@ class TestClassificationJobCreate:
job = ClassificationJob.create(project_id="p", asset_id="a")
assert job.created_at.tzinfo is not None
assert job.updated_at.tzinfo is not None
class TestClassificationJobState:
"""ClassificationJob 状态操作测试"""
def test_set_processing(self):
job = ClassificationJob.create(project_id="proj-1", asset_id="asset-1")
job.status = ClassificationJobStatus.PROCESSING
assert job.status == ClassificationJobStatus.PROCESSING
def test_set_completed_with_result(self):
job = ClassificationJob.create(project_id="proj-1", asset_id="asset-1")
job.status = ClassificationJobStatus.COMPLETED
job.classification = AssetClassification.SCENIC
job.confidence = 0.95
assert job.status == ClassificationJobStatus.COMPLETED
assert job.classification == "scenic"
assert job.confidence == pytest.approx(0.95)
def test_set_failed_with_error(self):
job = ClassificationJob.create(project_id="proj-1", asset_id="asset-1")
job.status = ClassificationJobStatus.FAILED
job.error_message = "model timeout"
assert job.status == ClassificationJobStatus.FAILED
assert job.error_message == "model timeout"
def test_confidence_range_zero(self):
job = ClassificationJob.create(project_id="proj-1", asset_id="asset-1")
job.confidence = 0.0
assert job.confidence == 0.0
def test_confidence_range_one(self):
job = ClassificationJob.create(project_id="proj-1", asset_id="asset-1")
job.confidence = 1.0
assert job.confidence == 1.0
+55
View File
@@ -139,3 +139,58 @@ class TestGeneratedVideoCreate:
video = GeneratedVideo.create(project_id="p", generation_task_id="t", name="n", file_url="u")
assert video.created_at.tzinfo is not None
assert video.generated_at.tzinfo is not None
class TestGeneratedVideoProperties:
"""GeneratedVideo 属性测试"""
def test_default_status_completed(self):
gv = GeneratedVideo.create(
project_id="proj-1",
generation_task_id="task-1",
name="测试视频",
file_url="https://example.com/video.mp4",
)
assert gv.status == "completed"
def test_default_review_status(self):
gv = GeneratedVideo.create(
project_id="proj-1",
generation_task_id="task-1",
name="测试视频",
file_url="https://example.com/video.mp4",
)
assert gv.review_status == "pending_review"
def test_set_status(self):
gv = GeneratedVideo.create(
project_id="proj-1",
generation_task_id="task-1",
name="测试视频",
file_url="https://example.com/video.mp4",
)
gv.status = "failed"
assert gv.status == "failed"
def test_mark_as_duplicate(self):
gv = GeneratedVideo.create(
project_id="proj-1",
generation_task_id="task-1",
name="测试视频",
file_url="https://example.com/video.mp4",
)
gv.is_duplicate = True
gv.duplicate_of = "video-original"
assert gv.is_duplicate is True
assert gv.duplicate_of == "video-original"
def test_set_fingerprint(self):
gv = GeneratedVideo.create(
project_id="proj-1",
generation_task_id="task-1",
name="测试视频",
file_url="https://example.com/video.mp4",
)
fingerprint = {"phash": "abc123", "md5": "def456"}
gv.video_fingerprint = fingerprint
assert gv.video_fingerprint == fingerprint
+168
View File
@@ -0,0 +1,168 @@
"""
VerificationCode 验证码领域模型单元测试
"""
from datetime import datetime, timedelta, timezone
import pytest
from domain.verification_code import VerificationCode
class TestVerificationCodeCreate:
"""创建验证码测试"""
def test_create_default_6digit_code(self):
vc = VerificationCode.create("test@example.com", "email_bind")
assert vc.id is not None
assert len(vc.id) == 32 # uuid4 hex
assert vc.recipient == "test@example.com"
assert vc.code_type == "email_bind"
assert len(vc.code) == 6
assert vc.code.isdigit()
assert vc.used_at is None
assert vc.attempts == 0
def test_create_custom_code(self):
vc = VerificationCode.create("13800138000", "phone_bind", custom_code="123456")
assert vc.code == "123456"
def test_create_default_ttl_300s(self):
before = datetime.now(timezone.utc)
vc = VerificationCode.create("test@example.com", "email_login")
after = datetime.now(timezone.utc)
expected_expiry_min = before + timedelta(seconds=300)
expected_expiry_max = after + timedelta(seconds=300)
assert expected_expiry_min <= vc.expires_at <= expected_expiry_max
def test_create_custom_ttl(self):
vc = VerificationCode.create("test@example.com", "reset_password", ttl_seconds=60)
expected = datetime.now(timezone.utc) + timedelta(seconds=60)
diff = abs((vc.expires_at - expected).total_seconds())
assert diff < 2
def test_create_recipient_stripped(self):
vc = VerificationCode.create(" test@example.com ", "email_bind")
assert vc.recipient == "test@example.com"
def test_create_phone_recipient(self):
vc = VerificationCode.create("13800138000", "phone_login")
assert vc.recipient == "13800138000"
assert vc.code_type == "phone_login"
def test_create_sets_created_at(self):
vc = VerificationCode.create("test@example.com", "email_bind")
assert vc.created_at is not None
assert isinstance(vc.created_at, datetime)
class TestVerificationCodeExpiry:
"""过期状态测试"""
def test_fresh_code_not_expired(self):
vc = VerificationCode.create("test@example.com", "email_bind")
assert vc.is_expired is False
def test_expired_code_is_expired(self):
vc = VerificationCode.create("test@example.com", "email_bind", ttl_seconds=-60)
assert vc.is_expired is True
def test_boundary_not_expired_at_expiry_time(self):
now = datetime.now(timezone.utc)
vc = VerificationCode.create("test@example.com", "email_bind")
vc.expires_at = now + timedelta(seconds=1)
assert vc.is_expired is False
def test_boundary_expired_right_after(self):
vc = VerificationCode.create("test@example.com", "email_bind")
vc.expires_at = datetime.now(timezone.utc) - timedelta(microseconds=1)
assert vc.is_expired is True
class TestVerificationCodeUsed:
"""使用状态测试"""
def test_fresh_code_not_used(self):
vc = VerificationCode.create("test@example.com", "email_bind")
assert vc.is_used is False
def test_mark_used(self):
vc = VerificationCode.create("test@example.com", "email_bind")
vc.mark_used()
assert vc.is_used is True
assert vc.used_at is not None
assert isinstance(vc.used_at, datetime)
def test_mark_used_sets_recent_time(self):
vc = VerificationCode.create("test@example.com", "email_bind")
before = datetime.now(timezone.utc)
vc.mark_used()
after = datetime.now(timezone.utc)
assert before <= vc.used_at <= after
def test_mark_used_idempotent(self):
vc = VerificationCode.create("test@example.com", "email_bind")
vc.mark_used()
first_used_at = vc.used_at
vc.mark_used()
# 第二次会更新时间
assert vc.used_at >= first_used_at
class TestVerificationCodeValidity:
"""有效性(未过期+未使用)测试"""
def test_fresh_code_is_valid(self):
vc = VerificationCode.create("test@example.com", "email_bind")
assert vc.is_valid is True
def test_expired_code_not_valid(self):
vc = VerificationCode.create("test@example.com", "email_bind", ttl_seconds=-10)
assert vc.is_valid is False
def test_used_code_not_valid(self):
vc = VerificationCode.create("test@example.com", "email_bind")
vc.mark_used()
assert vc.is_valid is False
def test_expired_and_used_not_valid(self):
vc = VerificationCode.create("test@example.com", "email_bind", ttl_seconds=-10)
vc.mark_used()
assert vc.is_valid is False
class TestVerificationCodeAttempts:
"""尝试次数测试"""
def test_initial_attempts_zero(self):
vc = VerificationCode.create("test@example.com", "email_bind")
assert vc.attempts == 0
def test_increment_attempts(self):
vc = VerificationCode.create("test@example.com", "email_bind")
vc.increment_attempts()
assert vc.attempts == 1
def test_increment_attempts_multiple(self):
vc = VerificationCode.create("test@example.com", "email_bind")
for _ in range(5):
vc.increment_attempts()
assert vc.attempts == 5
class TestVerificationCodeTypes:
"""不同验证码类型测试"""
@pytest.mark.parametrize(
"code_type",
[
"email_bind",
"phone_bind",
"email_login",
"phone_login",
"reset_password",
],
)
def test_all_supported_types(self, code_type):
vc = VerificationCode.create("test@example.com", code_type)
assert vc.code_type == code_type
assert vc.is_valid is True