116 lines
4.0 KiB
Python
116 lines
4.0 KiB
Python
"""``ConsciousnessNode.start_workflow_design`` fire workflow task 的提交逻辑。
|
||
|
||
历史上这里有一个常驻的 ``WorkflowRunningEngine`` actor 做中转,现已删除:
|
||
workflow 是一次性、有头有尾的执行,直接以 ``asyncio.create_task`` 触发
|
||
``run_workflow_task``。本测试保证 ConsciousnessNode 在工作流生成后正确 fire,
|
||
并通过 ``put_pending`` 推送 SSE 进度(节点端写 pending → API 端 SSE 读 pending)。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import asyncio
|
||
from unittest.mock import AsyncMock, MagicMock
|
||
|
||
import pytest
|
||
|
||
from kilostar.core.work.workflow import workflow_engine as engine_module
|
||
from kilostar.core.work.workflow.workflow import KiloStarWorkflow
|
||
from kilostar.core.work.workflow.model import WorkflowMetadata
|
||
|
||
|
||
@pytest.fixture
|
||
def consciousness_instance():
|
||
from kilostar.core.individual.consciousness_node.consciousness_node import (
|
||
ConsciousnessNode,
|
||
)
|
||
from kilostar.utils.logger import get_logger
|
||
|
||
obj = ConsciousnessNode.__new__(ConsciousnessNode)
|
||
obj.logger = get_logger("consciousness_node")
|
||
obj.agent = None
|
||
return obj
|
||
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_start_workflow_design_fires_run_workflow_task(
|
||
consciousness_instance, fake_actors, monkeypatch
|
||
):
|
||
"""快乐路径:working 返回 ForWorkflowEngine,应 fire run_workflow_task 且推送 pending。"""
|
||
from kilostar.core.individual.consciousness_node.template import (
|
||
ForWorkflowEngine,
|
||
)
|
||
|
||
wf = KiloStarWorkflow(
|
||
title="t",
|
||
work_link=[],
|
||
workflow_metadata=WorkflowMetadata(),
|
||
)
|
||
|
||
consciousness_instance.working = AsyncMock(
|
||
return_value=ForWorkflowEngine(workflow=wf, reasoning="r")
|
||
)
|
||
|
||
postgres = MagicMock()
|
||
postgres.get_all_worker_individual = AsyncMock(return_value=[])
|
||
postgres.update_workflow_status = AsyncMock()
|
||
fake_actors.register("postgres_database", postgres)
|
||
|
||
pending_writes: list[tuple[str, str]] = []
|
||
gwm = MagicMock()
|
||
gwm.put_pending = AsyncMock(
|
||
side_effect=lambda tid, msg: pending_writes.append((tid, msg))
|
||
)
|
||
fake_actors.register("global_workflow_manager", gwm)
|
||
|
||
captured: dict = {}
|
||
|
||
async def _fake_task(workflow_dict, trace_id):
|
||
captured["workflow_dict"] = workflow_dict
|
||
captured["trace_id"] = trace_id
|
||
|
||
monkeypatch.setattr(engine_module, "run_workflow_task", _fake_task)
|
||
|
||
await consciousness_instance.start_workflow_design("trace-123", "do something")
|
||
# 让 create_task 调度的协程跑完
|
||
await asyncio.sleep(0)
|
||
|
||
assert captured["trace_id"] == "trace-123"
|
||
assert captured["workflow_dict"]["title"] == "t"
|
||
# SSE 推送方向必须是 pending(put_pending)
|
||
assert any("正在为您构建" in msg for _, msg in pending_writes)
|
||
assert any("即将开始执行" in msg for _, msg in pending_writes)
|
||
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_start_workflow_design_failed_path_marks_failed(
|
||
consciousness_instance, fake_actors, monkeypatch
|
||
):
|
||
"""working 返回 None / 不匹配类型时应推送失败提示并把 workflow 状态置为 failed。"""
|
||
consciousness_instance.working = AsyncMock(return_value=None)
|
||
|
||
postgres = MagicMock()
|
||
postgres.get_all_worker_individual = AsyncMock(return_value=[])
|
||
postgres.update_workflow_status = AsyncMock()
|
||
fake_actors.register("postgres_database", postgres)
|
||
|
||
pending_writes: list[tuple[str, str]] = []
|
||
gwm = MagicMock()
|
||
gwm.put_pending = AsyncMock(
|
||
side_effect=lambda tid, msg: pending_writes.append((tid, msg))
|
||
)
|
||
fake_actors.register("global_workflow_manager", gwm)
|
||
|
||
fired: list = []
|
||
|
||
async def _fake_task(*a, **kw):
|
||
fired.append((a, kw))
|
||
|
||
monkeypatch.setattr(engine_module, "run_workflow_task", _fake_task)
|
||
|
||
await consciousness_instance.start_workflow_design("trace-x", "cmd")
|
||
await asyncio.sleep(0)
|
||
|
||
assert fired == [] # 没有 fire workflow task
|
||
postgres.update_workflow_status.assert_awaited_with("trace-x", "failed")
|
||
assert any("生成失败" in msg for _, msg in pending_writes)
|