Files
xiaoxia-saas/tests/unit/test_duplication.py
T
CI Bot 68e55df83b
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
style: auto-format with black + isort + prettier
2026-07-24 10:59:46 +00:00

306 lines
11 KiB
Python
Executable File

"""Duplication 查重记录领域实体单测."""
import pytest
from packages.domain.duplication import DuplicateSegment, DuplicationRecord
class TestDuplicateSegmentCreate:
def test_create_normal(self):
seg = DuplicateSegment.create(
source_start=10.0,
source_end=20.0,
matched_video_id="vid123",
matched_video_name="测试视频",
matched_start=5.0,
matched_end=15.0,
similarity=85.5,
)
assert seg.id
assert seg.source_start == 10.0
assert seg.source_end == 20.0
assert seg.matched_video_id == "vid123"
assert seg.matched_video_name == "测试视频"
assert seg.matched_start == 5.0
assert seg.matched_end == 15.0
assert seg.similarity == 85.5
def test_create_negative_source_start_raises(self):
with pytest.raises(ValueError, match="invalid source segment range"):
DuplicateSegment.create(
source_start=-1.0,
source_end=10.0,
matched_video_id="v1",
matched_video_name="v",
matched_start=0.0,
matched_end=10.0,
similarity=50.0,
)
def test_create_zero_duration_source_raises(self):
with pytest.raises(ValueError, match="invalid source segment range"):
DuplicateSegment.create(
source_start=10.0,
source_end=10.0,
matched_video_id="v1",
matched_video_name="v",
matched_start=0.0,
matched_end=10.0,
similarity=50.0,
)
def test_create_reversed_source_range_raises(self):
with pytest.raises(ValueError, match="invalid source segment range"):
DuplicateSegment.create(
source_start=20.0,
source_end=10.0,
matched_video_id="v1",
matched_video_name="v",
matched_start=0.0,
matched_end=10.0,
similarity=50.0,
)
def test_create_negative_matched_start_raises(self):
with pytest.raises(ValueError, match="invalid matched segment range"):
DuplicateSegment.create(
source_start=0.0,
source_end=10.0,
matched_video_id="v1",
matched_video_name="v",
matched_start=-1.0,
matched_end=10.0,
similarity=50.0,
)
def test_create_zero_duration_matched_raises(self):
with pytest.raises(ValueError, match="invalid matched segment range"):
DuplicateSegment.create(
source_start=0.0,
source_end=10.0,
matched_video_id="v1",
matched_video_name="v",
matched_start=5.0,
matched_end=5.0,
similarity=50.0,
)
def test_create_similarity_negative_raises(self):
with pytest.raises(ValueError, match="similarity must be between 0 and 100"):
DuplicateSegment.create(
source_start=0.0,
source_end=10.0,
matched_video_id="v1",
matched_video_name="v",
matched_start=0.0,
matched_end=10.0,
similarity=-1.0,
)
def test_create_similarity_over_100_raises(self):
with pytest.raises(ValueError, match="similarity must be between 0 and 100"):
DuplicateSegment.create(
source_start=0.0,
source_end=10.0,
matched_video_id="v1",
matched_video_name="v",
matched_start=0.0,
matched_end=10.0,
similarity=101.0,
)
def test_create_similarity_boundary_values(self):
# 0 和 100 都是合法的
seg0 = DuplicateSegment.create(
source_start=0.0,
source_end=1.0,
matched_video_id="v1",
matched_video_name="v",
matched_start=0.0,
matched_end=1.0,
similarity=0.0,
)
assert seg0.similarity == 0.0
seg100 = DuplicateSegment.create(
source_start=0.0,
source_end=1.0,
matched_video_id="v1",
matched_video_name="v",
matched_start=0.0,
matched_end=1.0,
similarity=100.0,
)
assert seg100.similarity == 100.0
def test_create_unique_ids(self):
seg1 = DuplicateSegment.create(
source_start=0.0,
source_end=1.0,
matched_video_id="v1",
matched_video_name="v",
matched_start=0.0,
matched_end=1.0,
similarity=50.0,
)
seg2 = DuplicateSegment.create(
source_start=0.0,
source_end=1.0,
matched_video_id="v1",
matched_video_name="v",
matched_start=0.0,
matched_end=1.0,
similarity=50.0,
)
assert seg1.id != seg2.id
class TestDuplicationRecordCreate:
def test_create_normal(self):
record = DuplicationRecord.create(
user_id="user1",
filename="test.mp4",
file_size=1024000,
storage_key="videos/test.mp4",
duration_seconds=30.5,
)
assert record.id
assert record.user_id == "user1"
assert record.filename == "test.mp4"
assert record.file_size == 1024000
assert record.storage_key == "videos/test.mp4"
assert record.duration_seconds == 30.5
assert record.status == "pending"
assert record.duplicate_rate is None
assert record.duplicate_count == 0
assert record.segments == []
assert record.error_message == ""
def test_create_strips_whitespace(self):
record = DuplicationRecord.create(
user_id=" user1 ",
filename=" test.mp4 ",
file_size=100,
storage_key="key1",
)
assert record.user_id == "user1"
assert record.filename == "test.mp4"
def test_create_empty_user_id_raises(self):
with pytest.raises(ValueError, match="user_id cannot be empty"):
DuplicationRecord.create(
user_id="",
filename="test.mp4",
file_size=100,
storage_key="key1",
)
def test_create_empty_filename_raises(self):
with pytest.raises(ValueError, match="filename cannot be empty"):
DuplicationRecord.create(
user_id="u1",
filename="",
file_size=100,
storage_key="key1",
)
def test_create_zero_file_size_raises(self):
with pytest.raises(ValueError, match="file_size must be positive"):
DuplicationRecord.create(
user_id="u1",
filename="test.mp4",
file_size=0,
storage_key="key1",
)
def test_create_negative_file_size_raises(self):
with pytest.raises(ValueError, match="file_size must be positive"):
DuplicationRecord.create(
user_id="u1",
filename="test.mp4",
file_size=-100,
storage_key="key1",
)
class TestDuplicationRecordStatus:
def test_mark_processing(self):
record = DuplicationRecord.create(user_id="u1", filename="t.mp4", file_size=100, storage_key="k1")
record.mark_processing()
assert record.status == "processing"
def test_mark_completed(self):
record = DuplicationRecord.create(user_id="u1", filename="t.mp4", file_size=100, storage_key="k1")
seg = DuplicateSegment.create(
source_start=0.0,
source_end=5.0,
matched_video_id="v1",
matched_video_name="v",
matched_start=0.0,
matched_end=5.0,
similarity=90.0,
)
record.mark_completed(duplicate_rate=25.5, duplicate_count=3, segments=[seg])
assert record.status == "completed"
assert record.duplicate_rate == 25.5
assert record.duplicate_count == 3
assert len(record.segments) == 1
def test_mark_completed_invalid_rate_raises(self):
record = DuplicationRecord.create(user_id="u1", filename="t.mp4", file_size=100, storage_key="k1")
with pytest.raises(ValueError, match="duplicate_rate must be between 0 and 100"):
record.mark_completed(duplicate_rate=-1, duplicate_count=0, segments=[])
with pytest.raises(ValueError, match="duplicate_rate must be between 0 and 100"):
record.mark_completed(duplicate_rate=101, duplicate_count=0, segments=[])
def test_mark_failed(self):
record = DuplicationRecord.create(user_id="u1", filename="t.mp4", file_size=100, storage_key="k1")
record.mark_failed("网络超时")
assert record.status == "failed"
assert record.error_message == "网络超时"
def test_can_retry_only_failed(self):
record = DuplicationRecord.create(user_id="u1", filename="t.mp4", file_size=100, storage_key="k1")
assert record.can_retry() is False # pending
record.mark_processing()
assert record.can_retry() is False # processing
record.mark_failed("error")
assert record.can_retry() is True # failed
seg = DuplicateSegment.create(
source_start=0.0,
source_end=1.0,
matched_video_id="v1",
matched_video_name="v",
matched_start=0.0,
matched_end=1.0,
similarity=50.0,
)
record2 = DuplicationRecord.create(user_id="u1", filename="t.mp4", file_size=100, storage_key="k1")
record2.mark_completed(10.0, 1, [seg])
assert record2.can_retry() is False # completed
def test_reset_for_retry(self):
record = DuplicationRecord.create(user_id="u1", filename="t.mp4", file_size=100, storage_key="k1")
seg = DuplicateSegment.create(
source_start=0.0,
source_end=1.0,
matched_video_id="v1",
matched_video_name="v",
matched_start=0.0,
matched_end=1.0,
similarity=50.0,
)
record.mark_completed(50.0, 2, [seg])
record.video_fingerprint = {"hash": "abc"}
record.reset_for_retry()
assert record.status == "pending"
assert record.duplicate_rate is None
assert record.duplicate_count == 0
assert record.error_message == ""
assert record.segments == []
assert record.video_fingerprint is None