"""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"