test(unit): P3-1 第四波 新增3个领域模块单元测试(65个用例) #682
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user