style: auto-format with black + isort + ruff + prettier [skip ci-format-check]
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 1s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 47s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 47s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m29s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m1s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 3m20s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 4m7s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Failing after 5m24s
AI Code Review / AI Code Review (pull_request) Successful in 6m50s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 9m38s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 11m37s
CI/CD Pipeline / CI Gate (pull_request) Failing after 3s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 8m47s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 21h33m14s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 21h44m45s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 21h44m52s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 21h44m54s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 21h44m9s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 21h44m9s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 21h44m9s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 21h44m10s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 21h44m12s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 21h44m13s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 21h44m15s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 21h44m4s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 21h44m4s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 21h32m33s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 21h32m35s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 21h32m35s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 21h44m11s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 21h44m12s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 22h8m14s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 1s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 47s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 47s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m29s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m1s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 3m20s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 4m7s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Failing after 5m24s
AI Code Review / AI Code Review (pull_request) Successful in 6m50s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 9m38s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 11m37s
CI/CD Pipeline / CI Gate (pull_request) Failing after 3s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 8m47s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 21h33m14s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 21h44m45s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 21h44m52s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 21h44m54s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 21h44m9s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 21h44m9s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 21h44m9s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 21h44m10s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 21h44m12s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 21h44m13s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 21h44m15s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 21h44m4s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 21h44m4s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 21h32m33s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 21h32m35s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 21h32m35s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 21h44m11s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 21h44m12s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 22h8m14s
This commit is contained in:
@@ -6,6 +6,7 @@ Create Date: 2026-09-15
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision = "077_merge_title_library_into_scripts"
|
||||
@@ -29,9 +30,7 @@ def upgrade() -> None:
|
||||
|
||||
# 从 title_libraries 迁移活跃数据到 scripts(若 title_libraries 表存在)
|
||||
conn = op.get_bind()
|
||||
exists = conn.execute(
|
||||
sa.text("SELECT to_regclass('public.title_libraries') IS NOT NULL")
|
||||
).scalar()
|
||||
exists = conn.execute(sa.text("SELECT to_regclass('public.title_libraries') IS NOT NULL")).scalar()
|
||||
if exists:
|
||||
conn.execute(sa.text("""
|
||||
INSERT INTO scripts (id, user_id, title, content, segments, tags, title_text, title_category, title_config, created_at, updated_at)
|
||||
|
||||
@@ -16,12 +16,11 @@ from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from main import app
|
||||
|
||||
|
||||
# ── helpers ──────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def _make_script(**overrides):
|
||||
"""构造一个模拟 ScriptModel 对象."""
|
||||
defaults = dict(
|
||||
@@ -33,7 +32,13 @@ def _make_script(**overrides):
|
||||
tags=["测试"],
|
||||
title_text="开场大标题",
|
||||
title_category="片头",
|
||||
title_config={"text": "开场大标题", "font": "思源黑体", "font_size": 48, "font_color": "#FFFFFF", "position": "top"},
|
||||
title_config={
|
||||
"text": "开场大标题",
|
||||
"font": "思源黑体",
|
||||
"font_size": 48,
|
||||
"font_color": "#FFFFFF",
|
||||
"position": "top",
|
||||
},
|
||||
created_at=datetime(2026, 9, 1, tzinfo=timezone.utc),
|
||||
updated_at=datetime(2026, 9, 1, tzinfo=timezone.utc),
|
||||
)
|
||||
@@ -43,11 +48,13 @@ def _make_script(**overrides):
|
||||
|
||||
# ── TestScriptModelNewFields ─────────────────────────────────────────────
|
||||
|
||||
|
||||
class TestScriptModelNewFields:
|
||||
"""验证 ScriptModel 新增字段的定义."""
|
||||
|
||||
def test_model_has_title_text_column(self):
|
||||
from packages.adapters.sqlalchemy_impl.models import ScriptModel
|
||||
|
||||
assert hasattr(ScriptModel, "title_text")
|
||||
col = ScriptModel.__table__.columns["title_text"]
|
||||
assert col is not None
|
||||
@@ -55,6 +62,7 @@ class TestScriptModelNewFields:
|
||||
|
||||
def test_model_has_title_category_column(self):
|
||||
from packages.adapters.sqlalchemy_impl.models import ScriptModel
|
||||
|
||||
assert hasattr(ScriptModel, "title_category")
|
||||
col = ScriptModel.__table__.columns["title_category"]
|
||||
assert col is not None
|
||||
@@ -62,6 +70,7 @@ class TestScriptModelNewFields:
|
||||
|
||||
def test_model_has_title_config_column(self):
|
||||
from packages.adapters.sqlalchemy_impl.models import ScriptModel
|
||||
|
||||
assert hasattr(ScriptModel, "title_config")
|
||||
col = ScriptModel.__table__.columns["title_config"]
|
||||
assert col is not None
|
||||
@@ -69,6 +78,7 @@ class TestScriptModelNewFields:
|
||||
def test_model_defaults(self):
|
||||
"""新字段默认值为空字符串/空 dict."""
|
||||
from packages.adapters.sqlalchemy_impl.models import ScriptModel
|
||||
|
||||
s = ScriptModel(id="x", user_id="u", title="t")
|
||||
# 检查 default 值
|
||||
assert ScriptModel.__table__.columns["title_text"].default.arg == ""
|
||||
@@ -77,6 +87,7 @@ class TestScriptModelNewFields:
|
||||
|
||||
# ── TestScriptServiceTitleConfig ─────────────────────────────────────────
|
||||
|
||||
|
||||
class TestScriptServiceTitleConfig:
|
||||
"""验证 ScriptService 新方法 get_title_config_for_script."""
|
||||
|
||||
@@ -137,6 +148,7 @@ class TestScriptServiceTitleConfig:
|
||||
|
||||
# ── TestScriptServiceCreateWithNewFields ─────────────────────────────────
|
||||
|
||||
|
||||
class TestScriptServiceCreateWithNewFields:
|
||||
"""验证 create_script 和 update_script 支持新字段."""
|
||||
|
||||
@@ -184,6 +196,7 @@ class TestScriptServiceCreateWithNewFields:
|
||||
|
||||
# ── TestScriptsRoutesNewFields ───────────────────────────────────────────
|
||||
|
||||
|
||||
class TestScriptsRoutesNewFields:
|
||||
"""验证 scripts API 路由正确处理新字段."""
|
||||
|
||||
@@ -196,12 +209,15 @@ class TestScriptsRoutesNewFields:
|
||||
mock_svc = MagicMock()
|
||||
mock_svc.create_script.return_value = mock_script
|
||||
|
||||
from app.auth import get_current_user
|
||||
from app.api.routes.scripts import _get_service
|
||||
from app.auth import get_current_user
|
||||
|
||||
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:
|
||||
@@ -235,12 +251,15 @@ class TestScriptsRoutesNewFields:
|
||||
mock_svc = MagicMock()
|
||||
mock_svc.get_script.return_value = mock_script
|
||||
|
||||
from app.auth import get_current_user
|
||||
from app.api.routes.scripts import _get_service
|
||||
from app.auth import get_current_user
|
||||
|
||||
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:
|
||||
@@ -258,6 +277,7 @@ class TestScriptsRoutesNewFields:
|
||||
|
||||
# ── TestTitleLibraryDeprecated ───────────────────────────────────────────
|
||||
|
||||
|
||||
class TestTitleLibraryDeprecated:
|
||||
"""验证 title_libraries API 返回 deprecated Warning header."""
|
||||
|
||||
@@ -265,12 +285,15 @@ class TestTitleLibraryDeprecated:
|
||||
mock_repo = MagicMock()
|
||||
mock_repo.count_by_user.return_value = 0
|
||||
|
||||
from app.auth import get_current_user
|
||||
from app.api.routes.titles import _get_title_repository
|
||||
from app.auth import get_current_user
|
||||
|
||||
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:
|
||||
@@ -284,20 +307,31 @@ class TestTitleLibraryDeprecated:
|
||||
|
||||
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="标题文字",
|
||||
category="通用", description="", tags=[], usage_count=0,
|
||||
is_active=True, created_at=datetime(2026, 1, 1, tzinfo=timezone.utc),
|
||||
id="t1",
|
||||
user_id="user-001",
|
||||
name="测试",
|
||||
text="标题文字",
|
||||
category="通用",
|
||||
description="",
|
||||
tags=[],
|
||||
usage_count=0,
|
||||
is_active=True,
|
||||
created_at=datetime(2026, 1, 1, tzinfo=timezone.utc),
|
||||
updated_at=datetime(2026, 1, 1, tzinfo=timezone.utc),
|
||||
)
|
||||
mock_repo = MagicMock()
|
||||
|
||||
from app.auth import get_current_user
|
||||
from app.api.routes.titles import _get_title_repository
|
||||
from app.auth import get_current_user
|
||||
|
||||
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:
|
||||
|
||||
Reference in New Issue
Block a user