Files
xiaoxia-saas/tests/unit/test_writeback_edit_plan_config.py
T
CI Bot 0b000f96a6
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 / 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 / Check if frontend-only change (pull_request) Successful in 45s
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 / PR Build Worker Image (pull_request) Successful in 41s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m46s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m50s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m53s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m26s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 1m46s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m42s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 5m18s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 3m17s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 10m6s
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 / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 6s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Waiting to run
AI Code Review / AI Code Review (pull_request) Waiting for AI Code Review to complete
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 1m13s
style: auto-format with black + isort + prettier [skip ci-format-check]
2026-08-23 05:39:27 +00:00

127 lines
4.7 KiB
Python

"""Tests for _writeback_edit_plan_config in generation_tasks route.
覆盖 CI 增量覆盖率不足的代码:
- generation_tasks.py 行 160-193 (_writeback_edit_plan_config 函数体)
- generation_tasks.py 行 371-372 (路由中调用该函数)
"""
from __future__ import annotations
import sys
from pathlib import Path
from unittest.mock import MagicMock, patch
import pytest
sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "apps" / "api"))
from app.api.routes.generation_tasks import _writeback_edit_plan_config
@pytest.fixture
def mock_db():
"""Mock SQLAlchemy Session."""
db = MagicMock()
db.query.return_value = db
db.filter.return_value = db
return db
@pytest.fixture
def mock_plan():
"""Mock EditPlanModel instance."""
plan = MagicMock()
plan.config = {"existing_key": "existing_value"}
return plan
class TestWritebackEditPlanConfig:
"""_writeback_edit_plan_config 全分支覆盖"""
# ---- 行 160-161: plan_id 为空直接返回 ----
def test_empty_plan_id_returns_immediately(self, mock_db):
_writeback_edit_plan_config(plan_id="", task_id="task_1", title_config={"text": "hi"}, db=mock_db)
mock_db.query.assert_not_called()
mock_db.commit.assert_not_called()
def test_none_plan_id_returns_immediately(self, mock_db):
_writeback_edit_plan_config(plan_id=None, task_id="task_1", title_config=None, db=mock_db)
mock_db.query.assert_not_called()
# ---- 行 165-168: plan 不存在 → warning + 不 commit ----
def test_plan_not_found_no_commit(self, mock_db):
mock_db.first.return_value = None
_writeback_edit_plan_config(plan_id="plan_999", task_id="task_1", title_config=None, db=mock_db)
mock_db.query.assert_called_once()
mock_db.commit.assert_not_called()
# ---- 行 170-182: 正常写入 + title_config ----
def test_success_with_title_config(self, mock_db, mock_plan):
mock_db.first.return_value = mock_plan
_writeback_edit_plan_config(
plan_id="plan_123",
task_id="task_456",
title_config={"text": "标题", "font_size": 36},
db=mock_db,
)
assert mock_plan.config["generation_task_id"] == "task_456"
assert mock_plan.config["title_config"] == {"text": "标题", "font_size": 36}
assert mock_plan.config["existing_key"] == "existing_value"
mock_db.commit.assert_called_once()
# ---- 行 170-175: 正常写入、无 title_config ----
def test_success_without_title_config(self, mock_db, mock_plan):
mock_db.first.return_value = mock_plan
_writeback_edit_plan_config(plan_id="plan_123", task_id="task_789", title_config=None, db=mock_db)
assert mock_plan.config["generation_task_id"] == "task_789"
assert "title_config" not in mock_plan.config
mock_db.commit.assert_called_once()
# ---- 行 170: config 不是 dict → 兜底空 dict ----
def test_config_not_dict_uses_empty_dict(self, mock_db):
bad_plan = MagicMock()
bad_plan.config = "not_a_dict"
mock_db.first.return_value = bad_plan
_writeback_edit_plan_config(plan_id="plan_123", task_id="task_1", title_config=None, db=mock_db)
assert isinstance(bad_plan.config, dict)
assert bad_plan.config["generation_task_id"] == "task_1"
mock_db.commit.assert_called_once()
# ---- 行 183-189: DB 异常 → warning + rollback ----
def test_db_exception_triggers_rollback(self, mock_db, mock_plan):
mock_db.first.return_value = mock_plan
mock_db.commit.side_effect = RuntimeError("DB connection lost")
# 不应抛异常
_writeback_edit_plan_config(plan_id="plan_123", task_id="task_1", title_config=None, db=mock_db)
mock_db.rollback.assert_called_once()
# ---- 行 190-193: rollback 也失败 → 静默 ----
def test_rollback_failure_silent(self, mock_db, mock_plan):
mock_db.first.return_value = mock_plan
mock_db.commit.side_effect = RuntimeError("commit failed")
mock_db.rollback.side_effect = RuntimeError("rollback also failed")
# 两个异常都不应抛出
_writeback_edit_plan_config(plan_id="plan_123", task_id="task_1", title_config=None, db=mock_db)
mock_db.rollback.assert_called_once()
# ---- 行 173: title_config 为空 dict → 不写入 title_config ----
def test_empty_title_config_not_written(self, mock_db, mock_plan):
mock_db.first.return_value = mock_plan
_writeback_edit_plan_config(plan_id="plan_123", task_id="task_1", title_config={}, db=mock_db)
# 空 dict 为 falsy,不写入
assert "title_config" not in mock_plan.config
assert mock_plan.config["generation_task_id"] == "task_1"