type: narrow transport and CLI test results
This commit is contained in:
@@ -9,7 +9,12 @@ import httpx
|
|||||||
from typer.testing import CliRunner
|
from typer.testing import CliRunner
|
||||||
|
|
||||||
import wf_cli.context as cli_context
|
import wf_cli.context as cli_context
|
||||||
from wf_api.models import RawWorkflowPlan
|
from wf_api.models import (
|
||||||
|
InspectSourceResult,
|
||||||
|
ListSourcesResult,
|
||||||
|
RawWorkflowPlan,
|
||||||
|
SourceDiagnosisResult,
|
||||||
|
)
|
||||||
from wf_cli.app import app
|
from wf_cli.app import app
|
||||||
from wf_cli.context import CliContext, load_cli_context, load_local_cli_context
|
from wf_cli.context import CliContext, load_cli_context, load_local_cli_context
|
||||||
from wf_core import END
|
from wf_core import END
|
||||||
@@ -27,13 +32,13 @@ class BrokenSourceAdmin:
|
|||||||
*,
|
*,
|
||||||
cursor: str | None = None,
|
cursor: str | None = None,
|
||||||
limit: int = 50,
|
limit: int = 50,
|
||||||
) -> dict[str, Any]:
|
) -> ListSourcesResult:
|
||||||
return {"sources": [], "next_cursor": None, "total": 0}
|
return {"sources": [], "next_cursor": None, "total": 0}
|
||||||
|
|
||||||
async def inspect_source(self, *, source_id: str) -> dict[str, Any]:
|
async def inspect_source(self, *, source_id: str) -> InspectSourceResult:
|
||||||
raise RuntimeError(f"broken source admin for {source_id}")
|
raise RuntimeError(f"broken source admin for {source_id}")
|
||||||
|
|
||||||
async def diagnose_source(self, *, source_id: str) -> dict[str, Any]:
|
async def diagnose_source(self, *, source_id: str) -> SourceDiagnosisResult:
|
||||||
raise RuntimeError(f"broken source admin for {source_id}")
|
raise RuntimeError(f"broken source admin for {source_id}")
|
||||||
|
|
||||||
|
|
||||||
@@ -43,26 +48,31 @@ class InventorySourceAdmin:
|
|||||||
*,
|
*,
|
||||||
cursor: str | None = None,
|
cursor: str | None = None,
|
||||||
limit: int = 50,
|
limit: int = 50,
|
||||||
) -> dict[str, Any]:
|
) -> ListSourcesResult:
|
||||||
return {"sources": [], "next_cursor": None, "total": 0}
|
return {"sources": [], "next_cursor": None, "total": 0}
|
||||||
|
|
||||||
async def inspect_source(self, *, source_id: str) -> dict[str, Any]:
|
async def inspect_source(self, *, source_id: str) -> InspectSourceResult:
|
||||||
return {
|
# These tests exercise only capability names; keep the fake payload narrow
|
||||||
"id": source_id,
|
# while declaring the same result boundary as the production client.
|
||||||
"capabilities": {
|
return cast(
|
||||||
"resources": [
|
InspectSourceResult,
|
||||||
f"{source_id}.architecture.md",
|
{
|
||||||
f"{source_id}.startup.md",
|
"id": source_id,
|
||||||
],
|
"capabilities": {
|
||||||
"prompts": [
|
"resources": [
|
||||||
f"{source_id}.simple-prompt",
|
f"{source_id}.architecture.md",
|
||||||
f"{source_id}.args-prompt",
|
f"{source_id}.startup.md",
|
||||||
],
|
],
|
||||||
|
"prompts": [
|
||||||
|
f"{source_id}.simple-prompt",
|
||||||
|
f"{source_id}.args-prompt",
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
)
|
||||||
|
|
||||||
async def diagnose_source(self, *, source_id: str) -> dict[str, Any]:
|
async def diagnose_source(self, *, source_id: str) -> SourceDiagnosisResult:
|
||||||
return {"source_id": source_id, "status": "ok"}
|
return {"source_id": source_id, "status": "ok", "diagnostics": []}
|
||||||
|
|
||||||
|
|
||||||
def test_load_cli_context_uses_rpc_client_for_rpc_http_target(tmp_path) -> None:
|
def test_load_cli_context_uses_rpc_client_for_rpc_http_target(tmp_path) -> None:
|
||||||
@@ -894,7 +904,8 @@ def test_wf_draft_import_uses_exact_remote_replacement_payload(
|
|||||||
include_draft=True,
|
include_draft=True,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
expected_draft = source["draft"]
|
expected_draft = source.get("draft")
|
||||||
|
assert expected_draft is not None
|
||||||
_patch_rpc_client_to_server(monkeypatch, server)
|
_patch_rpc_client_to_server(monkeypatch, server)
|
||||||
rpc_calls: list[tuple[str, dict[str, Any]]] = []
|
rpc_calls: list[tuple[str, dict[str, Any]]] = []
|
||||||
original_call = RpcClientTransport._call
|
original_call = RpcClientTransport._call
|
||||||
|
|||||||
@@ -192,14 +192,18 @@ async def test_rpc_workflow_client_runs_and_reads_trace(tmp_path) -> None:
|
|||||||
workflow_input={},
|
workflow_input={},
|
||||||
trace_range=TraceRange(start=0, limit=1),
|
trace_range=TraceRange(start=0, limit=1),
|
||||||
)
|
)
|
||||||
inspected = await client.inspect_run(run_id=run["run_id"])
|
run_id = run["run_id"]
|
||||||
|
assert run_id is not None
|
||||||
|
inspected = await client.inspect_run(run_id=run_id)
|
||||||
trace = await client.read_run_trace(
|
trace = await client.read_run_trace(
|
||||||
run_id=run["run_id"],
|
run_id=run_id,
|
||||||
trace_range=TraceRange(start=0, limit=1),
|
trace_range=TraceRange(start=0, limit=1),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert run["status"] == "completed"
|
assert run["status"] == "completed"
|
||||||
assert run["output"]["result"] == "hello from rpc client"
|
output = run["output"]
|
||||||
|
assert output is not None
|
||||||
|
assert output["result"] == "hello from rpc client"
|
||||||
assert inspected["trace_count"] >= 1
|
assert inspected["trace_count"] >= 1
|
||||||
assert len(trace["trace"]) == 1
|
assert len(trace["trace"]) == 1
|
||||||
|
|
||||||
@@ -339,6 +343,7 @@ async def test_rpc_workflow_client_draft_workspace_lifecycle(tmp_path) -> None:
|
|||||||
assert fetched["workspace_id"] == "client_ws"
|
assert fetched["workspace_id"] == "client_ws"
|
||||||
assert validated["status"] in {"valid", "invalid"}
|
assert validated["status"] in {"valid", "invalid"}
|
||||||
assert patched["revision"] == created["revision"] + 1
|
assert patched["revision"] == created["revision"] + 1
|
||||||
|
assert artifact["saved"] is True
|
||||||
assert artifact["artifact_id"] == "client_ws_art"
|
assert artifact["artifact_id"] == "client_ws_art"
|
||||||
|
|
||||||
|
|
||||||
@@ -553,9 +558,12 @@ async def test_rpc_client_builds_capability_free_draft_lifecycle(tmp_path) -> No
|
|||||||
assert stale["status"] == "conflict"
|
assert stale["status"] == "conflict"
|
||||||
assert stale["diagnostics"][0]["code"] == "revision_conflict"
|
assert stale["diagnostics"][0]["code"] == "revision_conflict"
|
||||||
assert validated["status"] == "valid"
|
assert validated["status"] == "valid"
|
||||||
|
assert "compiled_plan" in compiled
|
||||||
assert compiled["compiled_plan"]["start"] == "gate"
|
assert compiled["compiled_plan"]["start"] == "gate"
|
||||||
assert inspected["draft"]["start"] == "gate"
|
draft = inspected.get("draft")
|
||||||
assert inspected["draft"]["steps"] == {
|
assert draft is not None
|
||||||
|
assert draft["start"] == "gate"
|
||||||
|
assert draft["steps"] == {
|
||||||
"gate": {"join": {}},
|
"gate": {"join": {}},
|
||||||
"finish": {"end": {"outcome": "error"}},
|
"finish": {"end": {"outcome": "error"}},
|
||||||
}
|
}
|
||||||
@@ -651,8 +659,10 @@ async def test_rpc_client_lists_runs(tmp_path) -> None:
|
|||||||
)
|
)
|
||||||
listed = await client.list_runs(status="completed", limit=5)
|
listed = await client.list_runs(status="completed", limit=5)
|
||||||
|
|
||||||
|
started_run_id = started["run_id"]
|
||||||
|
assert started_run_id is not None
|
||||||
assert listed["total"] == 1
|
assert listed["total"] == 1
|
||||||
assert listed["runs"][0]["run_id"] == started["run_id"]
|
assert listed["runs"][0]["run_id"] == started_run_id
|
||||||
|
|
||||||
|
|
||||||
async def test_rpc_client_creates_artifact_from_plan(tmp_path) -> None:
|
async def test_rpc_client_creates_artifact_from_plan(tmp_path) -> None:
|
||||||
@@ -713,7 +723,9 @@ async def test_rpc_client_set_workflow_output_map(tmp_path) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
assert result["revision"] == 2
|
assert result["revision"] == 2
|
||||||
assert fetched["draft"]["output"] == [
|
draft = fetched.get("draft")
|
||||||
|
assert draft is not None
|
||||||
|
assert draft["output"] == [
|
||||||
{"path": "state.value", "target": "value"},
|
{"path": "state.value", "target": "value"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -421,11 +421,15 @@ async def test_mcp_backed_rpc_resumes_interrupted_run_after_server_rebuild(
|
|||||||
)
|
)
|
||||||
|
|
||||||
assert started["status"] == "interrupted"
|
assert started["status"] == "interrupted"
|
||||||
assert started["interrupt"]["payload"]["message"] == "approve after restart?"
|
started_run_id = started["run_id"]
|
||||||
assert started["interrupt"]["outcomes"] == ["submitted"]
|
assert started_run_id is not None
|
||||||
assert started["interrupt"]["typed"] is True
|
interrupt = started["interrupt"]
|
||||||
assert started["interrupt"]["request_schema"]["required"] == ["message"]
|
assert interrupt is not None
|
||||||
assert started["interrupt"]["resume_schema"]["required"] == ["approved"]
|
assert interrupt["payload"]["message"] == "approve after restart?"
|
||||||
|
assert interrupt["outcomes"] == ["submitted"]
|
||||||
|
assert interrupt["typed"] is True
|
||||||
|
assert interrupt["request_schema"]["required"] == ["message"]
|
||||||
|
assert interrupt["resume_schema"]["required"] == ["approved"]
|
||||||
|
|
||||||
rebuilt_server = build_workflow_server_from_workflow_config(workflow_config)
|
rebuilt_server = build_workflow_server_from_workflow_config(workflow_config)
|
||||||
async with httpx.AsyncClient(
|
async with httpx.AsyncClient(
|
||||||
@@ -436,15 +440,15 @@ async def test_mcp_backed_rpc_resumes_interrupted_run_after_server_rebuild(
|
|||||||
url="http://test/rpc",
|
url="http://test/rpc",
|
||||||
http_client=http_client,
|
http_client=http_client,
|
||||||
)
|
)
|
||||||
inspected = await rebuilt_client.inspect_run(run_id=started["run_id"])
|
inspected = await rebuilt_client.inspect_run(run_id=started_run_id)
|
||||||
resumed = await rebuilt_client.resume_run(
|
resumed = await rebuilt_client.resume_run(
|
||||||
run_id=started["run_id"],
|
run_id=started_run_id,
|
||||||
resume_payload={"approved": True},
|
resume_payload={"approved": True},
|
||||||
)
|
)
|
||||||
|
|
||||||
assert inspected["status"] == "interrupted"
|
assert inspected["status"] == "interrupted"
|
||||||
assert inspected["run_id"] == started["run_id"]
|
assert inspected["run_id"] == started_run_id
|
||||||
assert resumed["run_id"] == started["run_id"]
|
assert resumed["run_id"] == started_run_id
|
||||||
assert resumed["status"] == "completed"
|
assert resumed["status"] == "completed"
|
||||||
assert resumed["outcome"] == "submitted"
|
assert resumed["outcome"] == "submitted"
|
||||||
|
|
||||||
@@ -507,8 +511,12 @@ async def test_mcp_backed_rpc_workflow_reuses_runtime_session_across_runs(
|
|||||||
|
|
||||||
assert first["status"] == "completed"
|
assert first["status"] == "completed"
|
||||||
assert second["status"] == "completed"
|
assert second["status"] == "completed"
|
||||||
assert first["output"]["count"] == 1
|
first_output = first["output"]
|
||||||
assert second["output"]["count"] == 2
|
second_output = second["output"]
|
||||||
|
assert first_output is not None
|
||||||
|
assert second_output is not None
|
||||||
|
assert first_output["count"] == 1
|
||||||
|
assert second_output["count"] == 2
|
||||||
assert len(factory.clients) == 1
|
assert len(factory.clients) == 1
|
||||||
assert len(factory.created_connections) == 1
|
assert len(factory.created_connections) == 1
|
||||||
assert factory.clients[0].tool_calls == [
|
assert factory.clients[0].tool_calls == [
|
||||||
@@ -605,8 +613,12 @@ async def test_mcp_backed_rpc_workflow_reuses_runtime_session_direct_setup(
|
|||||||
|
|
||||||
assert first["status"] == "completed"
|
assert first["status"] == "completed"
|
||||||
assert second["status"] == "completed"
|
assert second["status"] == "completed"
|
||||||
assert first["output"]["count"] == 1
|
first_output = first["output"]
|
||||||
assert second["output"]["count"] == 2
|
second_output = second["output"]
|
||||||
|
assert first_output is not None
|
||||||
|
assert second_output is not None
|
||||||
|
assert first_output["count"] == 1
|
||||||
|
assert second_output["count"] == 2
|
||||||
assert len(factory.clients) == 1
|
assert len(factory.clients) == 1
|
||||||
assert len(factory.created_connections) == 1
|
assert len(factory.created_connections) == 1
|
||||||
assert factory.clients[0].tool_calls == [
|
assert factory.clients[0].tool_calls == [
|
||||||
@@ -805,8 +817,10 @@ async def test_mcp_backed_rpc_workflow_reuses_real_stdio_fixture_session(
|
|||||||
deployment_id="recall_workflow.default",
|
deployment_id="recall_workflow.default",
|
||||||
workflow_input={},
|
workflow_input={},
|
||||||
)
|
)
|
||||||
|
recalled_run_id = recalled["run_id"]
|
||||||
|
assert recalled_run_id is not None
|
||||||
recall_trace = await client.read_run_trace(
|
recall_trace = await client.read_run_trace(
|
||||||
run_id=recalled["run_id"],
|
run_id=recalled_run_id,
|
||||||
trace_range=TraceRange(start=0, limit=5),
|
trace_range=TraceRange(start=0, limit=5),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -164,8 +164,8 @@ export const WorkflowGraph = ({
|
|||||||
: {})}
|
: {})}
|
||||||
fitView
|
fitView
|
||||||
proOptions={{ hideAttribution: true }}
|
proOptions={{ hideAttribution: true }}
|
||||||
nodesDraggable={false}
|
nodesDraggable={true}
|
||||||
nodesConnectable={false}
|
nodesConnectable={true}
|
||||||
elementsSelectable={Boolean(onNodeSelect || onEdgeSelect)}
|
elementsSelectable={Boolean(onNodeSelect || onEdgeSelect)}
|
||||||
>
|
>
|
||||||
<Background />
|
<Background />
|
||||||
|
|||||||
@@ -1698,7 +1698,7 @@ tbody tr:hover {
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
letter-spacing: 0.02em;
|
letter-spacing: 0.02em;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
text-transform: uppercase;
|
/* text-transform: uppercase; */
|
||||||
}
|
}
|
||||||
|
|
||||||
.graph-node__ref {
|
.graph-node__ref {
|
||||||
|
|||||||
Reference in New Issue
Block a user