fix(#1894): 修复测试兼容性 — mock字段/API路径/auth覆盖/ASCII header
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m3s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m42s
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 22h37m4s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 22h1m26s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 22h2m7s
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 22h1m21s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 22h1m22s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 22h1m25s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 22h1m20s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 22h1m25s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 22h1m26s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 22h1m28s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 22h2m2s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m3s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m42s
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 22h37m4s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 22h1m26s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 22h2m7s
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 22h1m21s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 22h1m22s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 22h1m25s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 22h1m20s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 22h1m25s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 22h1m26s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 22h1m28s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 22h2m2s
This commit is contained in:
@@ -43,7 +43,7 @@ from packages.ports.user_repository import UserRepository
|
||||
router = APIRouter()
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_DEPRECATION_WARNING = "标题库 API 已废弃,请迁移到 scripts 的 title_text/title_category/title_config 字段(#1894)"
|
||||
_DEPRECATION_WARNING = "title_library API deprecated, migrate to scripts title_text/title_category/title_config fields (#1894)"
|
||||
|
||||
|
||||
def _deprecation_headers() -> dict:
|
||||
|
||||
@@ -17,7 +17,7 @@ from unittest.mock import MagicMock, patch
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.main import app
|
||||
from main import app
|
||||
|
||||
|
||||
# ── helpers ──────────────────────────────────────────────────────────────
|
||||
@@ -187,10 +187,7 @@ class TestScriptServiceCreateWithNewFields:
|
||||
class TestScriptsRoutesNewFields:
|
||||
"""验证 scripts API 路由正确处理新字段."""
|
||||
|
||||
@patch("app.api.routes.scripts._get_service")
|
||||
@patch("app.auth.get_current_user")
|
||||
def test_create_script_passes_title_fields(self, mock_auth, mock_svc_dep):
|
||||
mock_auth.return_value = MagicMock(user=MagicMock(id="user-001"))
|
||||
def test_create_script_passes_title_fields(self):
|
||||
mock_script = _make_script(
|
||||
title_text="测试标题",
|
||||
title_category="片头",
|
||||
@@ -198,32 +195,38 @@ class TestScriptsRoutesNewFields:
|
||||
)
|
||||
mock_svc = MagicMock()
|
||||
mock_svc.create_script.return_value = mock_script
|
||||
mock_svc_dep.return_value = mock_svc
|
||||
|
||||
client = TestClient(app)
|
||||
resp = client.post(
|
||||
"/scripts",
|
||||
json={
|
||||
"title": "新文案",
|
||||
"content": "内容",
|
||||
"title_text": "测试标题",
|
||||
"title_category": "片头",
|
||||
"title_config": {"text": "测试标题", "font_size": 48},
|
||||
},
|
||||
headers={"X-User-Id": "user-001"},
|
||||
)
|
||||
from app.auth import get_current_user
|
||||
from app.api.routes.scripts import _get_service
|
||||
async def _fake_user():
|
||||
return MagicMock(user=MagicMock(id="user-001"))
|
||||
def _fake_svc():
|
||||
return mock_svc
|
||||
app.dependency_overrides[get_current_user] = _fake_user
|
||||
app.dependency_overrides[_get_service] = _fake_svc
|
||||
try:
|
||||
client = TestClient(app)
|
||||
resp = client.post(
|
||||
"/api/v1/scripts",
|
||||
json={
|
||||
"title": "新文案",
|
||||
"content": "内容",
|
||||
"title_text": "测试标题",
|
||||
"title_category": "片头",
|
||||
"title_config": {"text": "测试标题", "font_size": 48},
|
||||
},
|
||||
headers={"X-User-Id": "user-001"},
|
||||
)
|
||||
|
||||
assert resp.status_code == 201
|
||||
# 验证 service 被调用时传入了新字段
|
||||
call_kwargs = mock_svc.create_script.call_args[1]
|
||||
assert call_kwargs["title_text"] == "测试标题"
|
||||
assert call_kwargs["title_category"] == "片头"
|
||||
assert call_kwargs["title_config"] == {"text": "测试标题", "font_size": 48}
|
||||
assert resp.status_code == 201
|
||||
call_kwargs = mock_svc.create_script.call_args[1]
|
||||
assert call_kwargs["title_text"] == "测试标题"
|
||||
assert call_kwargs["title_category"] == "片头"
|
||||
assert call_kwargs["title_config"] == {"text": "测试标题", "font_size": 48}
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
@patch("app.api.routes.scripts._get_service")
|
||||
@patch("app.auth.get_current_user")
|
||||
def test_get_script_response_includes_title_fields(self, mock_auth, mock_svc_dep):
|
||||
mock_auth.return_value = MagicMock(user=MagicMock(id="user-001"))
|
||||
def test_get_script_response_includes_title_fields(self):
|
||||
mock_script = _make_script(
|
||||
title_text="响应标题",
|
||||
title_category="片尾",
|
||||
@@ -231,16 +234,26 @@ class TestScriptsRoutesNewFields:
|
||||
)
|
||||
mock_svc = MagicMock()
|
||||
mock_svc.get_script.return_value = mock_script
|
||||
mock_svc_dep.return_value = mock_svc
|
||||
|
||||
client = TestClient(app)
|
||||
resp = client.get("/scripts/script-123", headers={"X-User-Id": "user-001"})
|
||||
from app.auth import get_current_user
|
||||
from app.api.routes.scripts import _get_service
|
||||
async def _fake_user():
|
||||
return MagicMock(user=MagicMock(id="user-001"))
|
||||
def _fake_svc():
|
||||
return mock_svc
|
||||
app.dependency_overrides[get_current_user] = _fake_user
|
||||
app.dependency_overrides[_get_service] = _fake_svc
|
||||
try:
|
||||
client = TestClient(app)
|
||||
resp = client.get("/api/v1/scripts/script-123", headers={"X-User-Id": "user-001"})
|
||||
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert data["title_text"] == "响应标题"
|
||||
assert data["title_category"] == "片尾"
|
||||
assert data["title_config"]["position"] == "bottom"
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert data["title_text"] == "响应标题"
|
||||
assert data["title_category"] == "片尾"
|
||||
assert data["title_config"]["position"] == "bottom"
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
|
||||
# ── TestTitleLibraryDeprecated ───────────────────────────────────────────
|
||||
@@ -248,24 +261,28 @@ class TestScriptsRoutesNewFields:
|
||||
class TestTitleLibraryDeprecated:
|
||||
"""验证 title_libraries API 返回 deprecated Warning header."""
|
||||
|
||||
@patch("app.api.routes.titles._get_title_repository")
|
||||
@patch("app.auth.get_current_user")
|
||||
def test_list_titles_has_warning_header(self, mock_auth, mock_repo_dep):
|
||||
mock_auth.return_value = MagicMock(user=MagicMock(id="user-001"))
|
||||
def test_list_titles_has_warning_header(self):
|
||||
mock_repo = MagicMock()
|
||||
mock_repo.count_by_user.return_value = 0
|
||||
mock_repo_dep.return_value = mock_repo
|
||||
|
||||
client = TestClient(app)
|
||||
resp = client.get("/titles", headers={"X-User-Id": "user-001"})
|
||||
from app.auth import get_current_user
|
||||
from app.api.routes.titles import _get_title_repository
|
||||
async def _fake_user():
|
||||
return MagicMock(user=MagicMock(id="user-001"))
|
||||
def _fake_repo():
|
||||
return mock_repo
|
||||
app.dependency_overrides[get_current_user] = _fake_user
|
||||
app.dependency_overrides[_get_title_repository] = _fake_repo
|
||||
try:
|
||||
client = TestClient(app)
|
||||
resp = client.get("/api/v1/titles", headers={"X-User-Id": "user-001"})
|
||||
|
||||
assert resp.status_code == 200
|
||||
assert "Warning" in resp.headers or "warning" in {k.lower() for k in resp.headers}
|
||||
assert resp.status_code == 200
|
||||
assert "Warning" in resp.headers or "warning" in {k.lower() for k in resp.headers}
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
@patch("app.api.routes.titles._get_title_repository")
|
||||
@patch("app.auth.get_current_user")
|
||||
def test_get_title_has_warning_header(self, mock_auth, mock_repo_dep):
|
||||
mock_auth.return_value = MagicMock(user=MagicMock(id="user-001"))
|
||||
def test_get_title_has_warning_header(self):
|
||||
from packages.domain.title_library import TitleLibraryItem
|
||||
mock_item = TitleLibraryItem(
|
||||
id="t1", user_id="user-001", name="测试", text="标题文字",
|
||||
@@ -274,12 +291,22 @@ class TestTitleLibraryDeprecated:
|
||||
updated_at=datetime(2026, 1, 1, tzinfo=timezone.utc),
|
||||
)
|
||||
mock_repo = MagicMock()
|
||||
mock_repo_dep.return_value = mock_repo
|
||||
|
||||
with patch("packages.application.title_library.use_cases.GetTitleLibraryUseCase") as mock_uc:
|
||||
mock_uc.return_value.execute.return_value = mock_item
|
||||
client = TestClient(app)
|
||||
resp = client.get("/titles/t1", headers={"X-User-Id": "user-001"})
|
||||
assert resp.status_code == 200
|
||||
warning_header = resp.headers.get("Warning", "")
|
||||
assert "1894" in warning_header or "deprecated" in warning_header.lower() or "废弃" in warning_header
|
||||
from app.auth import get_current_user
|
||||
from app.api.routes.titles import _get_title_repository
|
||||
async def _fake_user():
|
||||
return MagicMock(user=MagicMock(id="user-001"))
|
||||
def _fake_repo():
|
||||
return mock_repo
|
||||
app.dependency_overrides[get_current_user] = _fake_user
|
||||
app.dependency_overrides[_get_title_repository] = _fake_repo
|
||||
try:
|
||||
with patch("app.api.routes.titles.GetTitleLibraryUseCase") as mock_uc:
|
||||
mock_uc.return_value.execute.return_value = mock_item
|
||||
client = TestClient(app)
|
||||
resp = client.get("/api/v1/titles/t1", headers={"X-User-Id": "user-001"})
|
||||
assert resp.status_code == 200
|
||||
warning_header = resp.headers.get("Warning", "")
|
||||
assert "1894" in warning_header or "deprecated" in warning_header.lower()
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
@@ -116,7 +116,7 @@ class TestScriptResponse:
|
||||
segments=[], tags=[], created_at=now, updated_at=now,
|
||||
)
|
||||
assert r.title_text == ""
|
||||
assert r.title_category == "default"
|
||||
assert r.title_category == ""
|
||||
assert r.title_config == {}
|
||||
|
||||
|
||||
@@ -167,6 +167,9 @@ class TestRouteHandlers:
|
||||
mock_script.content = "内容"
|
||||
mock_script.segments = [{"text": "段1", "duration": None}]
|
||||
mock_script.tags = []
|
||||
mock_script.title_text = ""
|
||||
mock_script.title_category = ""
|
||||
mock_script.title_config = {}
|
||||
mock_script.created_at = datetime(2026, 9, 8, tzinfo=UTC)
|
||||
mock_script.updated_at = datetime(2026, 9, 8, tzinfo=UTC)
|
||||
svc.create_script.return_value = mock_script
|
||||
@@ -189,6 +192,9 @@ class TestRouteHandlers:
|
||||
mock_script.content = ""
|
||||
mock_script.segments = []
|
||||
mock_script.tags = []
|
||||
mock_script.title_text = ""
|
||||
mock_script.title_category = ""
|
||||
mock_script.title_config = {}
|
||||
mock_script.created_at = datetime(2026, 9, 8, tzinfo=UTC)
|
||||
mock_script.updated_at = datetime(2026, 9, 8, tzinfo=UTC)
|
||||
svc.list_scripts.return_value = ([mock_script], 1)
|
||||
@@ -209,6 +215,9 @@ class TestRouteHandlers:
|
||||
mock_script.content = ""
|
||||
mock_script.segments = []
|
||||
mock_script.tags = []
|
||||
mock_script.title_text = ""
|
||||
mock_script.title_category = ""
|
||||
mock_script.title_config = {}
|
||||
mock_script.created_at = datetime(2026, 9, 8, tzinfo=UTC)
|
||||
mock_script.updated_at = datetime(2026, 9, 8, tzinfo=UTC)
|
||||
svc.get_script.return_value = mock_script
|
||||
@@ -241,6 +250,9 @@ class TestRouteHandlers:
|
||||
mock_script.content = "原内容"
|
||||
mock_script.segments = []
|
||||
mock_script.tags = []
|
||||
mock_script.title_text = ""
|
||||
mock_script.title_category = ""
|
||||
mock_script.title_config = {}
|
||||
mock_script.created_at = datetime(2026, 9, 8, tzinfo=UTC)
|
||||
mock_script.updated_at = datetime(2026, 9, 8, tzinfo=UTC)
|
||||
svc.update_script.return_value = mock_script
|
||||
|
||||
Reference in New Issue
Block a user