diff --git a/tests/wf_cli/test_remote_target.py b/tests/wf_cli/test_remote_target.py index a4bf7b87..2eb1c858 100644 --- a/tests/wf_cli/test_remote_target.py +++ b/tests/wf_cli/test_remote_target.py @@ -9,7 +9,12 @@ import httpx from typer.testing import CliRunner 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.context import CliContext, load_cli_context, load_local_cli_context from wf_core import END @@ -27,13 +32,13 @@ class BrokenSourceAdmin: *, cursor: str | None = None, limit: int = 50, - ) -> dict[str, Any]: + ) -> ListSourcesResult: 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}") - 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}") @@ -43,26 +48,31 @@ class InventorySourceAdmin: *, cursor: str | None = None, limit: int = 50, - ) -> dict[str, Any]: + ) -> ListSourcesResult: return {"sources": [], "next_cursor": None, "total": 0} - async def inspect_source(self, *, source_id: str) -> dict[str, Any]: - return { - "id": source_id, - "capabilities": { - "resources": [ - f"{source_id}.architecture.md", - f"{source_id}.startup.md", - ], - "prompts": [ - f"{source_id}.simple-prompt", - f"{source_id}.args-prompt", - ], + async def inspect_source(self, *, source_id: str) -> InspectSourceResult: + # These tests exercise only capability names; keep the fake payload narrow + # while declaring the same result boundary as the production client. + return cast( + InspectSourceResult, + { + "id": source_id, + "capabilities": { + "resources": [ + f"{source_id}.architecture.md", + 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]: - return {"source_id": source_id, "status": "ok"} + async def diagnose_source(self, *, source_id: str) -> SourceDiagnosisResult: + return {"source_id": source_id, "status": "ok", "diagnostics": []} 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, ) ) - expected_draft = source["draft"] + expected_draft = source.get("draft") + assert expected_draft is not None _patch_rpc_client_to_server(monkeypatch, server) rpc_calls: list[tuple[str, dict[str, Any]]] = [] original_call = RpcClientTransport._call diff --git a/tests/wf_transport_rpc_http/test_client.py b/tests/wf_transport_rpc_http/test_client.py index 5ab6ed55..65b8fb31 100644 --- a/tests/wf_transport_rpc_http/test_client.py +++ b/tests/wf_transport_rpc_http/test_client.py @@ -192,14 +192,18 @@ async def test_rpc_workflow_client_runs_and_reads_trace(tmp_path) -> None: workflow_input={}, 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( - run_id=run["run_id"], + run_id=run_id, trace_range=TraceRange(start=0, limit=1), ) 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 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 validated["status"] in {"valid", "invalid"} assert patched["revision"] == created["revision"] + 1 + assert artifact["saved"] is True 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["diagnostics"][0]["code"] == "revision_conflict" assert validated["status"] == "valid" + assert "compiled_plan" in compiled assert compiled["compiled_plan"]["start"] == "gate" - assert inspected["draft"]["start"] == "gate" - assert inspected["draft"]["steps"] == { + draft = inspected.get("draft") + assert draft is not None + assert draft["start"] == "gate" + assert draft["steps"] == { "gate": {"join": {}}, "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) + started_run_id = started["run_id"] + assert started_run_id is not None 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: @@ -713,7 +723,9 @@ async def test_rpc_client_set_workflow_output_map(tmp_path) -> None: ) 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"}, ] diff --git a/tests/wf_transport_rpc_http/test_mcp_backed_server_rpc.py b/tests/wf_transport_rpc_http/test_mcp_backed_server_rpc.py index 436078e1..dab971fc 100644 --- a/tests/wf_transport_rpc_http/test_mcp_backed_server_rpc.py +++ b/tests/wf_transport_rpc_http/test_mcp_backed_server_rpc.py @@ -421,11 +421,15 @@ async def test_mcp_backed_rpc_resumes_interrupted_run_after_server_rebuild( ) assert started["status"] == "interrupted" - assert started["interrupt"]["payload"]["message"] == "approve after restart?" - assert started["interrupt"]["outcomes"] == ["submitted"] - assert started["interrupt"]["typed"] is True - assert started["interrupt"]["request_schema"]["required"] == ["message"] - assert started["interrupt"]["resume_schema"]["required"] == ["approved"] + started_run_id = started["run_id"] + assert started_run_id is not None + interrupt = started["interrupt"] + assert interrupt is not None + 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) async with httpx.AsyncClient( @@ -436,15 +440,15 @@ async def test_mcp_backed_rpc_resumes_interrupted_run_after_server_rebuild( url="http://test/rpc", 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( - run_id=started["run_id"], + run_id=started_run_id, resume_payload={"approved": True}, ) assert inspected["status"] == "interrupted" - assert inspected["run_id"] == started["run_id"] - assert resumed["run_id"] == started["run_id"] + assert inspected["run_id"] == started_run_id + assert resumed["run_id"] == started_run_id assert resumed["status"] == "completed" 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 second["status"] == "completed" - assert first["output"]["count"] == 1 - assert second["output"]["count"] == 2 + first_output = first["output"] + 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.created_connections) == 1 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 second["status"] == "completed" - assert first["output"]["count"] == 1 - assert second["output"]["count"] == 2 + first_output = first["output"] + 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.created_connections) == 1 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", workflow_input={}, ) + recalled_run_id = recalled["run_id"] + assert recalled_run_id is not None recall_trace = await client.read_run_trace( - run_id=recalled["run_id"], + run_id=recalled_run_id, trace_range=TraceRange(start=0, limit=5), ) diff --git a/web/apps/console/src/graph/WorkflowGraph.tsx b/web/apps/console/src/graph/WorkflowGraph.tsx index 6e2013d1..bdb3efc4 100644 --- a/web/apps/console/src/graph/WorkflowGraph.tsx +++ b/web/apps/console/src/graph/WorkflowGraph.tsx @@ -164,8 +164,8 @@ export const WorkflowGraph = ({ : {})} fitView proOptions={{ hideAttribution: true }} - nodesDraggable={false} - nodesConnectable={false} + nodesDraggable={true} + nodesConnectable={true} elementsSelectable={Boolean(onNodeSelect || onEdgeSelect)} > diff --git a/web/apps/console/src/styles/global.css b/web/apps/console/src/styles/global.css index a617e71c..4d37018d 100644 --- a/web/apps/console/src/styles/global.css +++ b/web/apps/console/src/styles/global.css @@ -1698,7 +1698,7 @@ tbody tr:hover { font-weight: 700; letter-spacing: 0.02em; line-height: 1; - text-transform: uppercase; + /* text-transform: uppercase; */ } .graph-node__ref {