Files
KiloStar/tests/unit/test_workflow_engine.py
T

116 lines
4.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""``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 推送方向必须是 pendingput_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)