4f323b394b
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m1s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m20s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 53s
CI/CD Pipeline / Build Staging Web Image (push) Failing after 2m23s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 6m12s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 6m2s
CI/CD Pipeline / Integration Tests (push) Successful in 4m7s
CI/CD Pipeline / Unit Tests (push) Failing after 11m15s
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Build Staging API Image (push) Successful in 15m12s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
191 lines
6.1 KiB
Python
191 lines
6.1 KiB
Python
"""测试 create_asset 端点:project_id 可选,从 library 自动推导。"""
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
from app.api.routes.assets import create_asset
|
|
from app.auth import AuthenticatedUser
|
|
from app.schemas.asset import CreateAssetRequest
|
|
from fastapi import HTTPException
|
|
|
|
from packages.domain import AssetStatus, ClassificationStatus
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_user():
|
|
user = MagicMock(spec=AuthenticatedUser)
|
|
user.user.id = "user-123"
|
|
return user
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_library():
|
|
lib = MagicMock()
|
|
lib.id = "lib-abc"
|
|
lib.project_id = "proj-from-library"
|
|
return lib
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_project():
|
|
proj = MagicMock()
|
|
proj.id = "proj-from-library"
|
|
proj.can_access.return_value = True
|
|
return proj
|
|
|
|
|
|
def _make_request(**overrides):
|
|
defaults = dict(
|
|
library_id="lib-abc",
|
|
name="test-audio.mp3",
|
|
storage_key="uploads/test.mp3",
|
|
mime_type="audio/mpeg",
|
|
file_size=1024,
|
|
status="uploading",
|
|
)
|
|
defaults.update(overrides)
|
|
return CreateAssetRequest(**defaults)
|
|
|
|
|
|
def test_project_id_derived_from_library_when_not_provided(mock_user, mock_library, mock_project):
|
|
"""前端不传 project_id 时,从 library.project_id 自动推导。"""
|
|
request = _make_request() # project_id 默认 None
|
|
|
|
asset_repo = MagicMock()
|
|
lib_repo = MagicMock()
|
|
lib_repo.get.return_value = mock_library
|
|
proj_repo = MagicMock()
|
|
proj_repo.find_by_id.return_value = mock_project
|
|
|
|
expected_asset = MagicMock()
|
|
expected_asset.id = "asset-1"
|
|
expected_asset.project_id = "proj-from-library"
|
|
expected_asset.library_id = "lib-abc"
|
|
expected_asset.name = "test-audio.mp3"
|
|
expected_asset.storage_key = ""
|
|
expected_asset.mime_type = "audio/mpeg"
|
|
expected_asset.metadata = {}
|
|
expected_asset.file_size = 1024
|
|
expected_asset.thumbnail_url = None
|
|
expected_asset.duration = None
|
|
expected_asset.width = None
|
|
expected_asset.height = None
|
|
expected_asset.fps = None
|
|
expected_asset.codec = None
|
|
expected_asset.status = AssetStatus.UPLOADING
|
|
expected_asset.classification_status = ClassificationStatus.PENDING
|
|
expected_asset.quality_score = None
|
|
expected_asset.created_at = None
|
|
expected_asset.uploaded_by_user_id = "user-123"
|
|
expected_asset.tag_ids = []
|
|
with patch("app.api.routes.assets.CreateAssetUseCase") as mock_uc:
|
|
mock_uc.return_value.execute.return_value = expected_asset
|
|
result = create_asset(
|
|
request=request,
|
|
authenticated_user=mock_user,
|
|
asset_repository=asset_repo,
|
|
asset_library_repository=lib_repo,
|
|
project_repository=proj_repo,
|
|
)
|
|
|
|
# 验证 project_id 被正确推导
|
|
proj_repo.find_by_id.assert_called_once_with("proj-from-library")
|
|
# 验证 use case 使用的是推导出的 project_id
|
|
cmd = mock_uc.return_value.execute.call_args[0][0]
|
|
assert cmd.project_id == "proj-from-library"
|
|
|
|
|
|
def test_explicit_project_id_used_when_provided(mock_user, mock_library, mock_project):
|
|
"""前端显式传 project_id 时,优先使用请求值。"""
|
|
mock_project.id = "proj-explicit"
|
|
mock_project.can_access.return_value = True
|
|
mock_library.project_id = "proj-explicit" # 匹配
|
|
|
|
request = _make_request(project_id="proj-explicit")
|
|
|
|
asset_repo = MagicMock()
|
|
lib_repo = MagicMock()
|
|
lib_repo.get.return_value = mock_library
|
|
proj_repo = MagicMock()
|
|
proj_repo.find_by_id.return_value = mock_project
|
|
|
|
mock_asset = MagicMock()
|
|
mock_asset.id = "asset-1"
|
|
mock_asset.storage_key = ""
|
|
mock_asset.mime_type = "audio/mpeg"
|
|
mock_asset.project_id = "proj-explicit"
|
|
mock_asset.library_id = "lib-abc"
|
|
mock_asset.name = "test"
|
|
mock_asset.metadata = {}
|
|
mock_asset.file_size = 0
|
|
mock_asset.thumbnail_url = None
|
|
mock_asset.duration = None
|
|
mock_asset.width = None
|
|
mock_asset.height = None
|
|
mock_asset.fps = None
|
|
mock_asset.codec = None
|
|
mock_asset.status = AssetStatus.UPLOADING
|
|
mock_asset.classification_status = ClassificationStatus.PENDING
|
|
mock_asset.quality_score = None
|
|
mock_asset.created_at = None
|
|
mock_asset.uploaded_by_user_id = "user-123"
|
|
mock_asset.tag_ids = []
|
|
|
|
with patch("app.api.routes.assets.CreateAssetUseCase") as mock_uc:
|
|
mock_uc.return_value.execute.return_value = mock_asset
|
|
create_asset(
|
|
request=request,
|
|
authenticated_user=mock_user,
|
|
asset_repository=asset_repo,
|
|
asset_library_repository=lib_repo,
|
|
project_repository=proj_repo,
|
|
)
|
|
|
|
proj_repo.find_by_id.assert_called_once_with("proj-explicit")
|
|
cmd = mock_uc.return_value.execute.call_args[0][0]
|
|
assert cmd.project_id == "proj-explicit"
|
|
|
|
|
|
def test_library_not_found_returns_404(mock_user):
|
|
"""素材库不存在时返回 404。"""
|
|
request = _make_request()
|
|
|
|
lib_repo = MagicMock()
|
|
lib_repo.get.return_value = None
|
|
proj_repo = MagicMock()
|
|
asset_repo = MagicMock()
|
|
|
|
with pytest.raises(HTTPException) as exc_info:
|
|
create_asset(
|
|
request=request,
|
|
authenticated_user=mock_user,
|
|
asset_repository=asset_repo,
|
|
asset_library_repository=lib_repo,
|
|
project_repository=proj_repo,
|
|
)
|
|
assert exc_info.value.status_code == 404
|
|
|
|
|
|
def test_library_project_mismatch_returns_400(mock_user, mock_library, mock_project):
|
|
"""当 library.project_id 与请求的 project_id 不一致时返回 400。"""
|
|
mock_library.project_id = "proj-A"
|
|
mock_project.id = "proj-B"
|
|
|
|
request = _make_request(project_id="proj-B")
|
|
|
|
lib_repo = MagicMock()
|
|
lib_repo.get.return_value = mock_library
|
|
proj_repo = MagicMock()
|
|
proj_repo.find_by_id.return_value = mock_project
|
|
asset_repo = MagicMock()
|
|
|
|
with pytest.raises(HTTPException) as exc_info:
|
|
create_asset(
|
|
request=request,
|
|
authenticated_user=mock_user,
|
|
asset_repository=asset_repo,
|
|
asset_library_repository=lib_repo,
|
|
project_repository=proj_repo,
|
|
)
|
|
assert exc_info.value.status_code == 400
|