"""素材库 UseCase 单元测试.""" from __future__ import annotations from unittest.mock import MagicMock import pytest from packages.application.asset_libraries import ( CreateAssetLibraryCommand, CreateAssetLibraryUseCase, ListAssetLibrariesUseCase, ) from packages.domain import AssetLibrary, AssetLibraryKind @pytest.fixture def mock_repo(): repo = MagicMock() return repo @pytest.fixture def sample_library(): lib = AssetLibrary.create( project_id="proj_456", name="测试视频库", kind=AssetLibraryKind.VIDEO, ) lib.id = "lib_123" return lib class TestListAssetLibrariesUseCase: """ListAssetLibrariesUseCase 测试""" def test_list_returns_repo_results(self, mock_repo, sample_library): """正常返回 repository 的查询结果""" mock_repo.find_by_project.return_value = [sample_library] use_case = ListAssetLibrariesUseCase(mock_repo) result = use_case.execute("proj_456") assert len(result) == 1 assert result[0].id == "lib_123" mock_repo.find_by_project.assert_called_once_with("proj_456") def test_empty_project_raises_value_error(self, mock_repo): """空 project_id 抛出 ValueError""" use_case = ListAssetLibrariesUseCase(mock_repo) with pytest.raises(ValueError, match="project_id 不能为空"): use_case.execute("") mock_repo.find_by_project.assert_not_called() def test_whitespace_project_raises_value_error(self, mock_repo): """纯空格 project_id 也抛出 ValueError""" use_case = ListAssetLibrariesUseCase(mock_repo) with pytest.raises(ValueError, match="project_id 不能为空"): use_case.execute(" ") mock_repo.find_by_project.assert_not_called() def test_project_id_stripped_before_query(self, mock_repo, sample_library): """project_id 会被 strip 后再查询""" mock_repo.find_by_project.return_value = [sample_library] use_case = ListAssetLibrariesUseCase(mock_repo) use_case.execute(" proj_456 ") mock_repo.find_by_project.assert_called_once_with("proj_456") def test_empty_list(self, mock_repo): """项目没有素材库时返回空列表""" mock_repo.find_by_project.return_value = [] use_case = ListAssetLibrariesUseCase(mock_repo) result = use_case.execute("proj_456") assert result == [] class TestCreateAssetLibraryUseCase: """CreateAssetLibraryUseCase 测试""" def test_create_success(self, mock_repo, sample_library): """创建成功返回 AssetLibrary""" mock_repo.create.return_value = sample_library use_case = CreateAssetLibraryUseCase(mock_repo) command = CreateAssetLibraryCommand( project_id="proj_456", name="新素材库", kind=AssetLibraryKind.IMAGE, ) result = use_case.execute(command) assert result.id == "lib_123" mock_repo.create.assert_called_once() # 验证传入 repository 的是一个 AssetLibrary 对象 created = mock_repo.create.call_args[0][0] assert isinstance(created, AssetLibrary) assert created.project_id == "proj_456" assert created.name == "新素材库" assert created.kind == AssetLibraryKind.IMAGE def test_create_with_video_kind(self, mock_repo): """创建视频类型素材库""" mock_repo.create.side_effect = lambda x: x use_case = CreateAssetLibraryUseCase(mock_repo) command = CreateAssetLibraryCommand( project_id="proj_1", name="视频库", kind=AssetLibraryKind.VIDEO, ) result = use_case.execute(command) assert result.kind == AssetLibraryKind.VIDEO assert result.name == "视频库" def test_create_with_voice_kind(self, mock_repo): """创建音色类型素材库""" mock_repo.create.side_effect = lambda x: x use_case = CreateAssetLibraryUseCase(mock_repo) command = CreateAssetLibraryCommand( project_id="proj_1", name="音色库", kind=AssetLibraryKind.VOICE, ) result = use_case.execute(command) assert result.kind == AssetLibraryKind.VOICE class TestCreateAssetLibraryCommand: """CreateAssetLibraryCommand 数据类测试""" def test_command_fields(self): """命令对象字段正确""" cmd = CreateAssetLibraryCommand( project_id="proj_1", name="test", kind=AssetLibraryKind.VIDEO, ) assert cmd.project_id == "proj_1" assert cmd.name == "test" assert cmd.kind == AssetLibraryKind.VIDEO def test_command_is_dataclass(self): """命令是 dataclass""" from dataclasses import is_dataclass assert is_dataclass(CreateAssetLibraryCommand)