fix: 拒绝环形 DAG 并避免无效工作流堵塞任务队列
This commit is contained in:
@@ -26,6 +26,91 @@ def definition() -> dict:
|
||||
}
|
||||
|
||||
|
||||
def cyclic_definition() -> dict:
|
||||
"""构造带环的 DAG 定义(a → b → a),用于验证保存/发布拒绝无效工作流。
|
||||
|
||||
环上的两个节点互相引用对方输出,形成了无法拓扑排序的依赖:节点 ID 唯一、
|
||||
边引用的节点都存在,因此只有环检测能拦住它(R04 复现定义)。
|
||||
"""
|
||||
return {
|
||||
"name": "cyclic",
|
||||
"version": 1,
|
||||
"nodes": [
|
||||
{"id": "a", "node_type": "echo", "inputs": {"file_uri": "b.file_uri"}},
|
||||
{"id": "b", "node_type": "echo", "inputs": {"file_uri": "a.file_uri"}},
|
||||
],
|
||||
"edges": [{"from": "a", "to": "b"}, {"from": "b", "to": "a"}],
|
||||
"entry_inputs": {"video_uri": "file"},
|
||||
"final_outputs": {"result": "a.file_uri"},
|
||||
}
|
||||
|
||||
|
||||
def test_create_workflow_rejects_cycle() -> None:
|
||||
"""验证保存环形 DAG 返回 422,不把无效版本写入版本表。"""
|
||||
with TestClient(app) as client:
|
||||
response = client.post(
|
||||
"/api/admin/workflows",
|
||||
json={
|
||||
"id": "cyclic-flow",
|
||||
"name": "Cyclic Flow",
|
||||
"definition": cyclic_definition(),
|
||||
},
|
||||
)
|
||||
assert response.status_code == 422
|
||||
assert "cycle" in response.json()["detail"]
|
||||
# 拒绝保存时不得留下半成品工作流/版本记录。
|
||||
db = app.state.db
|
||||
assert db.get_workflow("cyclic-flow") is None
|
||||
assert db.list_workflow_versions("cyclic-flow") == []
|
||||
|
||||
# 已有工作流重新提交带环定义:同样拒绝,不追加新版本。
|
||||
assert client.post(
|
||||
"/api/admin/workflows",
|
||||
json={"id": "cycle-check", "name": "Cycle Check", "definition": definition()},
|
||||
).status_code == 200
|
||||
assert client.post(
|
||||
"/api/admin/workflows",
|
||||
json={"id": "cycle-check", "name": "Cycle Check", "definition": cyclic_definition()},
|
||||
).status_code == 422
|
||||
assert db.get_workflow("cycle-check")["latest_version"] == 1
|
||||
assert len(db.list_workflow_versions("cycle-check")) == 1
|
||||
db.delete_workflow("cycle-check")
|
||||
|
||||
|
||||
def test_validate_workflow_rejects_cycle() -> None:
|
||||
"""验证只校验不保存的接口同样拒绝环形 DAG。"""
|
||||
with TestClient(app) as client:
|
||||
db = app.state.db
|
||||
# 独立工作流 ID 并显式清理,避免与其他用例(共用同一个测试数据库)互相影响。
|
||||
assert client.post(
|
||||
"/api/admin/workflows",
|
||||
json={"id": "cycle-check", "name": "Cycle Check", "definition": definition()},
|
||||
).status_code == 200
|
||||
response = client.post(
|
||||
"/api/admin/workflows/cycle-check/validate",
|
||||
json=cyclic_definition(),
|
||||
)
|
||||
assert response.status_code == 422
|
||||
assert "cycle" in response.json()["detail"]
|
||||
db.delete_workflow("cycle-check")
|
||||
|
||||
|
||||
def test_publish_rejects_cycle_in_latest_version() -> None:
|
||||
"""验证历史遗留的环形版本不能被发布(发布前重新校验最新版本定义)。"""
|
||||
with TestClient(app) as client:
|
||||
db = app.state.db
|
||||
# 直接写库模拟修复前已保存的无效版本(保存接口现在会拒绝,只能这样构造)。
|
||||
db.upsert_workflow(
|
||||
{"id": "legacy-cyclic", "name": "Legacy", "published": 0, "latest_version": 1}
|
||||
)
|
||||
db.create_workflow_version("legacy-cyclic", 1, cyclic_definition())
|
||||
response = client.post("/api/admin/workflows/legacy-cyclic/publish")
|
||||
assert response.status_code == 422
|
||||
assert "cycle" in response.json()["detail"]
|
||||
assert db.get_workflow("legacy-cyclic")["published"] == 0
|
||||
db.delete_workflow("legacy-cyclic")
|
||||
|
||||
|
||||
def test_workflow_crud_and_publish() -> None:
|
||||
"""验证工作流 CRUD、校验、发布与版本列表的完整流程。"""
|
||||
with TestClient(app) as client:
|
||||
|
||||
Reference in New Issue
Block a user