mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-11-04 06:15:14 +08:00
refactor(types): govern workflow oper session contracts
This commit is contained in:
@@ -2,6 +2,7 @@ from typing import Any, List, Mapping, Optional, Tuple
|
||||
|
||||
from sqlalchemy import delete as sqlalchemy_delete
|
||||
from sqlalchemy import func, or_, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.db.base import DbOper
|
||||
@@ -47,12 +48,12 @@ class WorkflowOper(DbOper):
|
||||
工作流管理
|
||||
"""
|
||||
|
||||
def add(self, **kwargs) -> Tuple[bool, str]:
|
||||
def add(self, **kwargs: Any) -> Tuple[bool, str]:
|
||||
"""
|
||||
新增工作流
|
||||
"""
|
||||
wf = Workflow(**kwargs)
|
||||
if not self.get_by_name(kwargs.get("name")):
|
||||
if not self.get_by_name(wf.name):
|
||||
self._stage_create(wf)
|
||||
return True, "新增工作流成功"
|
||||
return False, "工作流已存在"
|
||||
@@ -87,6 +88,8 @@ class WorkflowOper(DbOper):
|
||||
|
||||
def stage_delete(self, workflow_id: int) -> None:
|
||||
"""暂存工作流删除,由请求级 UnitOfWork 统一提交。"""
|
||||
if not isinstance(self._db, Session):
|
||||
raise RuntimeError("工作流暂存写入需要调用方提供同步 Session")
|
||||
self._db.execute(
|
||||
sqlalchemy_delete(Workflow).where(Workflow.id == workflow_id)
|
||||
)
|
||||
@@ -189,6 +192,8 @@ class WorkflowOper(DbOper):
|
||||
|
||||
async def stage_create(self, payload: Mapping[str, Any]) -> Workflow:
|
||||
"""暂存新工作流,不在操作器内提交事务。"""
|
||||
if not isinstance(self._db, AsyncSession):
|
||||
raise RuntimeError("工作流暂存写入需要调用方提供异步 Session")
|
||||
workflow = Workflow(**dict(payload))
|
||||
self._db.add(workflow)
|
||||
await self._db.flush()
|
||||
|
||||
1
mypy.ini
1
mypy.ini
@@ -116,6 +116,7 @@ files =
|
||||
app/db/oper/user.py,
|
||||
app/db/oper/systemconfig.py,
|
||||
app/db/oper/site.py,
|
||||
app/db/oper/workflow.py,
|
||||
app/db/models/agentchat.py,
|
||||
app/db/models/agenttask.py,
|
||||
app/db/models/agenttaskrun.py,
|
||||
|
||||
@@ -94,6 +94,23 @@ def test_workflow_oper_reuses_explicit_query_sessions(db, monkeypatch):
|
||||
asyncio.run(check())
|
||||
|
||||
|
||||
def test_workflow_staged_writes_require_matching_caller_sessions(db) -> None:
|
||||
"""暂存入口拒绝缺失或错配会话,不能静默丢失删除或另开事务。"""
|
||||
with pytest.raises(RuntimeError, match="同步 Session"):
|
||||
WorkflowOper().stage_delete(1)
|
||||
|
||||
async def check() -> None:
|
||||
"""分别覆盖同步删除和异步创建的会话类型边界。"""
|
||||
async with async_session_scope() as session:
|
||||
with pytest.raises(RuntimeError, match="同步 Session"):
|
||||
WorkflowOper(session).stage_delete(1)
|
||||
for oper in (WorkflowOper(), WorkflowOper(db.session)):
|
||||
with pytest.raises(RuntimeError, match="异步 Session"):
|
||||
await oper.stage_create({"name": "wrong-session"})
|
||||
|
||||
asyncio.run(check())
|
||||
|
||||
|
||||
def test_query_repository_returns_detached_deep_copied_snapshot(db):
|
||||
"""查询仓储必须在关闭短 Session 前投影,且 JSON 不与 ORM 记录共享。"""
|
||||
workflow = _flow("wf-snapshot")
|
||||
|
||||
Reference in New Issue
Block a user