135 lines
4.8 KiB
Python
135 lines
4.8 KiB
Python
from __future__ import annotations
|
|
|
|
import asyncio
|
|
|
|
from wf_artifacts import FileDraftWorkspaceStore, FileWorkflowArtifactStore
|
|
from wf_mcp.broker import WfMcpService
|
|
from wf_mcp.models import ConnectionConfig
|
|
from wf_mcp.storage import FileStore
|
|
from wf_mcp.workflow_surface import WorkflowSurfaceHandlers
|
|
|
|
from ..test_support import echo_tool, local_temp_root
|
|
from .conftest import (
|
|
ContentOnlyOutputAdapter,
|
|
failing_tool,
|
|
handlers,
|
|
)
|
|
|
|
|
|
def test_workflow_surface_lists_planner_visible_capabilities() -> None:
|
|
h = handlers(FileWorkflowArtifactStore(local_temp_root() / "surface_caps"))
|
|
|
|
payload = asyncio.run(h.list_capabilities(limit=2))
|
|
first = payload["capabilities"][0]
|
|
|
|
assert len(payload["capabilities"]) == 2
|
|
assert payload["total"] >= 2
|
|
assert payload["next_cursor"] == "2"
|
|
assert first["kind"] == "node_spec"
|
|
assert "input_schema" not in first
|
|
|
|
|
|
def test_workflow_surface_call_capability_returns_structured_error() -> None:
|
|
service = WfMcpService(
|
|
store=FileStore(local_temp_root() / "surface_call_capability_error_mcp"),
|
|
artifact_store=FileWorkflowArtifactStore(
|
|
local_temp_root() / "surface_call_capability_error"
|
|
),
|
|
)
|
|
service.register_connection(
|
|
ConnectionConfig(id="demo.personal", server="demo", account="personal")
|
|
)
|
|
service.register_specs("demo.personal", failing_tool)
|
|
h = WorkflowSurfaceHandlers(service)
|
|
|
|
payload = asyncio.run(
|
|
h.call_capability(
|
|
qualified_name="demo.personal.failing_tool",
|
|
payload={"message": "hello"},
|
|
)
|
|
)
|
|
|
|
assert payload["qualified_name"] == "demo.personal.failing_tool"
|
|
assert payload["source_id"] == "demo.personal"
|
|
assert payload["kind"] == "node_spec"
|
|
assert payload["outcome"] == "runtime_error"
|
|
assert payload["output"] is None
|
|
assert payload["diagnostics"][0]["code"] == "capability_call_failed"
|
|
assert payload["diagnostics"][0]["severity"] == "error"
|
|
assert "demo.personal.failing_tool" in payload["diagnostics"][0]["message"]
|
|
assert "upstream exploded" in payload["diagnostics"][0]["message"]
|
|
|
|
|
|
def test_workflow_surface_inspects_one_capability() -> None:
|
|
h = handlers(FileWorkflowArtifactStore(local_temp_root() / "surface_inspect_cap"))
|
|
|
|
payload = asyncio.run(h.inspect_capability(qualified_name="wf.std.runtime_error"))
|
|
|
|
assert payload["name"] == "wf.std.runtime_error"
|
|
assert payload["outcomes"] == ["ok"]
|
|
assert "input_schema" in payload
|
|
|
|
|
|
def test_workflow_surface_inspect_capability_includes_wrapper_hints() -> None:
|
|
service = WfMcpService(
|
|
store=FileStore(local_temp_root() / "surface_wrapper_hints_mcp"),
|
|
artifact_store=FileWorkflowArtifactStore(
|
|
local_temp_root() / "surface_wrapper_hints_artifacts"
|
|
),
|
|
)
|
|
service.register_connection(
|
|
ConnectionConfig(id="demo.personal", server="demo", account="personal")
|
|
)
|
|
service.register_specs("demo.personal", echo_tool)
|
|
h = WorkflowSurfaceHandlers(service)
|
|
|
|
payload = asyncio.run(
|
|
h.inspect_capability(qualified_name="demo.personal.echo_tool")
|
|
)
|
|
|
|
hints = payload["wrapper_hints"]
|
|
assert hints["capability_name"] == "demo.personal.echo_tool"
|
|
assert hints["declared_outcomes"] == ["ok"]
|
|
assert hints["input_map"] == {"input.text": "text"}
|
|
assert hints["output_map"] == {"echoed": "state.echoed"}
|
|
assert hints["outcome_policy"] == "preserve_declared"
|
|
|
|
|
|
def test_workflow_surface_does_not_auto_map_raw_mcp_content_blocks() -> None:
|
|
artifact_store = FileWorkflowArtifactStore(
|
|
local_temp_root() / "surface_content_only_content_hint"
|
|
)
|
|
service = WfMcpService(
|
|
store=FileStore(local_temp_root() / "surface_content_only_content_hint_mcp"),
|
|
artifact_store=artifact_store,
|
|
draft_workspace_store=FileDraftWorkspaceStore(
|
|
local_temp_root() / "surface_content_only_content_hint_mcp"
|
|
),
|
|
)
|
|
service.register_connection(
|
|
ConnectionConfig(
|
|
id="everything.default", server="everything", account="default"
|
|
)
|
|
)
|
|
service.register_adapter("everything", ContentOnlyOutputAdapter())
|
|
asyncio.run(service.refresh_connection_catalog("everything.default"))
|
|
h = WorkflowSurfaceHandlers(service)
|
|
|
|
inspected = asyncio.run(
|
|
h.inspect_capability(qualified_name="everything.default.echo")
|
|
)
|
|
created = asyncio.run(
|
|
h.create_draft_workspace_from_capability(
|
|
workspace_id="content_blocks",
|
|
capability_name="everything.default.echo",
|
|
name="content_blocks",
|
|
)
|
|
)
|
|
|
|
assert inspected["wrapper_hints"]["output_map"] == {}
|
|
assert created["wrapper_hints"]["output_map"] == {}
|
|
assert inspected["wrapper_hints"]["missing_decisions"][0]["kind"] == (
|
|
"review_nested_output"
|
|
)
|
|
assert "Raw MCP content blocks" in inspected["wrapper_hints"]["notes"][2]
|