"""查重应用层用例单元测试。 覆盖: - UploadForDuplicationUseCase — 创建查重记录 - ListDuplicationRecordsUseCase — 列表查询(含分页) - GetDuplicationDetailUseCase — 详情查询 - DeleteDuplicationRecordUseCase — 删除记录 - RetryDuplicationUseCase — 重试查重(含状态校验) """ from __future__ import annotations from unittest.mock import MagicMock import pytest from packages.application.duplication import ( DeleteDuplicationRecordUseCase, GetDuplicationDetailUseCase, ListDuplicationRecordsUseCase, RetryDuplicationUseCase, UploadForDuplicationCommand, UploadForDuplicationUseCase, ) from packages.domain.duplication import DuplicateSegment, DuplicationRecord def _make_record(status="pending", **kwargs): """创建测试用 DuplicationRecord。""" record = DuplicationRecord.create( user_id=kwargs.get("user_id", "user-1"), filename=kwargs.get("filename", "test.mp4"), file_size=kwargs.get("file_size", 1024), storage_key=kwargs.get("storage_key", "oss/key"), duration_seconds=kwargs.get("duration", 30.0), ) if status != "pending": record.mark_processing() if status == "completed": record.mark_completed(duplicate_rate=15.0, duplicate_count=1, segments=[]) elif status == "failed": record.mark_failed("处理失败") return record class TestUploadForDuplicationUseCase: """上传查重用例测试。""" def test_execute_creates_and_persists_record(self): mock_repo = MagicMock() mock_repo.create.side_effect = lambda r: r use_case = UploadForDuplicationUseCase(mock_repo) command = UploadForDuplicationCommand( user_id="user-1", filename="video.mp4", file_size=2048, storage_key="oss/video.mp4", duration_seconds=60.0, ) result = use_case.execute(command) assert result.user_id == "user-1" assert result.filename == "video.mp4" assert result.file_size == 2048 assert result.status == "pending" mock_repo.create.assert_called_once() def test_execute_with_default_duration(self): mock_repo = MagicMock() mock_repo.create.side_effect = lambda r: r use_case = UploadForDuplicationUseCase(mock_repo) command = UploadForDuplicationCommand( user_id="user-1", filename="video.mp4", file_size=1024, storage_key="oss/key", ) result = use_case.execute(command) assert result.duration_seconds == 0.0 def test_execute_invalid_user_id_raises(self): mock_repo = MagicMock() use_case = UploadForDuplicationUseCase(mock_repo) command = UploadForDuplicationCommand( user_id="", filename="video.mp4", file_size=1024, storage_key="oss/key", ) with pytest.raises(ValueError, match="user_id"): use_case.execute(command) mock_repo.create.assert_not_called() class TestListDuplicationRecordsUseCase: """列表查询用例测试。""" def test_execute_returns_records(self): records = [_make_record(), _make_record(filename="b.mp4")] mock_repo = MagicMock() mock_repo.list_by_user.return_value = records use_case = ListDuplicationRecordsUseCase(mock_repo) result = use_case.execute("user-1") assert len(result) == 2 mock_repo.list_by_user.assert_called_once_with("user-1", offset=0, limit=50) def test_execute_with_pagination(self): mock_repo = MagicMock() mock_repo.list_by_user.return_value = [] use_case = ListDuplicationRecordsUseCase(mock_repo) use_case.execute("user-1", offset=10, limit=20) mock_repo.list_by_user.assert_called_once_with("user-1", offset=10, limit=20) def test_execute_empty_user_id_raises(self): mock_repo = MagicMock() use_case = ListDuplicationRecordsUseCase(mock_repo) with pytest.raises(ValueError): use_case.execute("") mock_repo.list_by_user.assert_not_called() def test_execute_whitespace_user_id_raises(self): mock_repo = MagicMock() use_case = ListDuplicationRecordsUseCase(mock_repo) with pytest.raises(ValueError): use_case.execute(" ") def test_execute_strips_user_id(self): mock_repo = MagicMock() mock_repo.list_by_user.return_value = [] use_case = ListDuplicationRecordsUseCase(mock_repo) use_case.execute(" user-1 ") mock_repo.list_by_user.assert_called_once_with("user-1", offset=0, limit=50) class TestGetDuplicationDetailUseCase: """详情查询用例测试。""" def test_execute_returns_record(self): record = _make_record() mock_repo = MagicMock() mock_repo.get.return_value = record use_case = GetDuplicationDetailUseCase(mock_repo) result = use_case.execute(record.id) assert result is record mock_repo.get.assert_called_once_with(record.id) def test_execute_returns_none_for_missing(self): mock_repo = MagicMock() mock_repo.get.return_value = None use_case = GetDuplicationDetailUseCase(mock_repo) result = use_case.execute("nonexistent") assert result is None class TestDeleteDuplicationRecordUseCase: """删除用例测试。""" def test_execute_deletes_record(self): mock_repo = MagicMock() mock_repo.delete.return_value = True use_case = DeleteDuplicationRecordUseCase(mock_repo) result = use_case.execute("record-1") assert result is True mock_repo.delete.assert_called_once_with("record-1") def test_execute_returns_false_for_missing(self): mock_repo = MagicMock() mock_repo.delete.return_value = False use_case = DeleteDuplicationRecordUseCase(mock_repo) result = use_case.execute("nonexistent") assert result is False class TestRetryDuplicationUseCase: """重试用例测试。""" def test_execute_resets_failed_record(self): record = _make_record(status="failed") mock_repo = MagicMock() mock_repo.get.return_value = record mock_repo.update.side_effect = lambda r: r use_case = RetryDuplicationUseCase(mock_repo) result = use_case.execute(record.id) assert result is not None assert result.status == "pending" assert result.error_message == "" assert result.duplicate_rate is None assert result.duplicate_count == 0 assert result.segments == [] mock_repo.update.assert_called_once() def test_execute_returns_none_for_missing(self): mock_repo = MagicMock() mock_repo.get.return_value = None use_case = RetryDuplicationUseCase(mock_repo) result = use_case.execute("nonexistent") assert result is None mock_repo.update.assert_not_called() def test_execute_calls_repo_get_and_update(self): record = _make_record(status="failed") mock_repo = MagicMock() mock_repo.get.return_value = record mock_repo.update.side_effect = lambda r: r use_case = RetryDuplicationUseCase(mock_repo) use_case.execute(record.id) mock_repo.get.assert_called_once_with(record.id) mock_repo.update.assert_called_once()