test(#1714): 补 prepare 去重边界分支——预建失败降级、update异常吞掉、PROCESSING hash不同跳过/相同命中
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 30s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 30s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 1m35s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m48s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m53s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 2m20s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m5s
AI Code Review / AI Code Review (pull_request) Successful in 4m11s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 5m28s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 6m49s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 4s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 4m31s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 9s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 17s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 30s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 30s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 1m35s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m48s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m53s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 2m20s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m5s
AI Code Review / AI Code Review (pull_request) Successful in 4m11s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 5m28s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 6m49s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 4s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 4m31s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 9s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 17s
This commit is contained in:
@@ -369,3 +369,103 @@ def test_create_pending_asset_creates_when_no_match():
|
||||
assert result.file_hash == "newhash"
|
||||
assert result.client_upload_id == "newcuid"
|
||||
assert repo.saved == 1
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 兜底去重:PROCESSING 占位 hash 不同时跳过
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_filename_fallback_skips_processing_with_different_hash():
|
||||
"""PROCESSING/UPLOADING 占位记录仅当 hash 一致(或占位无 hash)才命中;hash 不同跳过。"""
|
||||
repo = _FakeAssetRepo()
|
||||
repo.create(_make_pending(id="p1", file_hash="oldhash"))
|
||||
repo.find_recent_active_by_library_and_name = lambda **kw: repo.assets["p1"]
|
||||
result = upload_route._find_duplicate_asset(
|
||||
repo,
|
||||
library_id="lib-1",
|
||||
file_hash="differenthash", # 新上传内容不同
|
||||
client_upload_id="",
|
||||
filename="test.mp4",
|
||||
file_size=1024,
|
||||
)
|
||||
assert result is None
|
||||
|
||||
|
||||
def test_filename_fallback_matches_processing_with_same_hash():
|
||||
"""PROCESSING 占位 hash 与请求一致时命中(重试场景)。"""
|
||||
repo = _FakeAssetRepo()
|
||||
repo.create(_make_pending(id="p1", file_hash="samehash"))
|
||||
repo.find_recent_active_by_library_and_name = lambda **kw: repo.assets["p1"]
|
||||
result = upload_route._find_duplicate_asset(
|
||||
repo,
|
||||
library_id="lib-1",
|
||||
file_hash="samehash",
|
||||
client_upload_id="",
|
||||
filename="test.mp4",
|
||||
file_size=1024,
|
||||
)
|
||||
assert result is not None
|
||||
assert result.id == "p1"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# prepare 预建失败降级:不阻塞签名
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_prepare_pending_asset_create_failure_degrades_gracefully():
|
||||
"""预建 asset 抛异常时,prepare 仍正常返回签名(duplicated=False, asset_id 空)。"""
|
||||
|
||||
class _BrokenRepo(_FakeAssetRepo):
|
||||
def create(self, asset):
|
||||
raise RuntimeError("db down")
|
||||
|
||||
repo = _BrokenRepo()
|
||||
req = SimpleNamespace(
|
||||
project_id="p-1",
|
||||
library_id="lib-1",
|
||||
filename="test.mp4",
|
||||
content_type="video/mp4",
|
||||
file_size=1024,
|
||||
file_hash="abc123",
|
||||
client_upload_id="cuid-1",
|
||||
)
|
||||
resp = await upload_route.prepare_direct_upload(
|
||||
request=req,
|
||||
authenticated_user=_user(),
|
||||
project_repository=_StubProjectRepo(_FIXTURE_PROJECT),
|
||||
asset_library_repository=_StubLibraryRepo(_FIXTURE_LIBRARY),
|
||||
asset_repository=repo,
|
||||
storage_service=_storage(),
|
||||
)
|
||||
assert resp.duplicated is False
|
||||
assert resp.skip_transfer is False
|
||||
assert resp.asset_id == "" # 预建失败,降级无 asset_id
|
||||
assert resp.upload_url != "" # 签名仍正常返回
|
||||
|
||||
|
||||
def test_create_pending_asset_update_failure_swallowed():
|
||||
"""复用占位记录时字段补齐 update 抛异常被吞掉,不阻塞返回。"""
|
||||
|
||||
class _UpdateBrokenRepo(_FakeAssetRepo):
|
||||
def update(self, asset):
|
||||
raise RuntimeError("db down")
|
||||
|
||||
repo = _UpdateBrokenRepo()
|
||||
repo.create(_make_pending(file_hash="abc123", client_upload_id="", id="p1"))
|
||||
result = upload_route._create_pending_asset(
|
||||
asset_repository=repo,
|
||||
project_id="p-1",
|
||||
library_id="lib-1",
|
||||
storage_key="uploads/new/test.mp4",
|
||||
filename="test.mp4",
|
||||
mime_type="video/mp4",
|
||||
user_id="user-1",
|
||||
file_hash="abc123",
|
||||
client_upload_id="cuid-new",
|
||||
file_size=1024,
|
||||
)
|
||||
assert result.id == "p1" # 仍复用,不抛异常
|
||||
assert repo.saved == 1
|
||||
|
||||
Reference in New Issue
Block a user