diff --git a/tests/authoring/test_subgraph.py b/tests/authoring/test_subgraph.py index 4070a459..5487d206 100644 --- a/tests/authoring/test_subgraph.py +++ b/tests/authoring/test_subgraph.py @@ -221,13 +221,15 @@ def test_subgraph_ref_accepts_composite_child_input_binding() -> None: id="run_child", workflow=child, input=[ - { - "target": "folder_id", - "expression": { - "kind": "literal", - "value": "demo-folder", - }, - } + InputExpressionBinding.model_validate( + { + "target": "folder_id", + "expression": { + "kind": "literal", + "value": "demo-folder", + }, + } + ) ], ) diff --git a/tests/core/test_input_expression_runtime.py b/tests/core/test_input_expression_runtime.py index 7cb04b9c..63bcde0c 100644 --- a/tests/core/test_input_expression_runtime.py +++ b/tests/core/test_input_expression_runtime.py @@ -15,13 +15,16 @@ from wf_core import ( SchemaRef, StateField, StateSchema, + StepInputBinding, SubgraphNode, Workflow, WorkflowExecutionError, + WorkflowRef, execute_workflow, resume_workflow, ) -from wf_core.models.steps import InterruptNode +from wf_core.models.steps import InterruptNode, Step +from wf_core.paths import GraphSourcePath, LocalPath from wf_core.runtime.input_bindings import ( resolve_input_expression, resolve_step_input_bindings, @@ -45,6 +48,11 @@ COMPOSITE_BINDING = { } +def _composite_binding() -> StepInputBinding: + """Build this raw fixture through the canonical binding model.""" + return InputExpressionBinding.model_validate(COMPOSITE_BINDING) + + def test_resolver_builds_nested_json_with_input_state_and_context_paths() -> None: expression = InputExpressionBinding.model_validate( { @@ -107,7 +115,12 @@ def test_simple_path_binding_preserves_legacy_value_identity() -> None: legacy_value = {"opaque": object()} resolved = resolve_step_input_bindings( - [InputPathBinding(target="request.value", path="state.value")], + [ + InputPathBinding( + target=LocalPath.parse("request.value"), + path=GraphSourcePath.parse("state.value"), + ) + ], state={"value": legacy_value}, workflow_input={}, context={}, @@ -210,7 +223,7 @@ def test_normal_node_execution_resolves_composite_input() -> None: return {"outcome": "ok", "output": {}} workflow = _node_workflow( - input_bindings=[COMPOSITE_BINDING], + input_bindings=[_composite_binding()], state_fields={"foo": StateField(type="string", default="hello")}, ) run = execute_workflow(workflow, {}, {"concat": concat}) @@ -220,7 +233,8 @@ def test_normal_node_execution_resolves_composite_input() -> None: def test_prepared_subgraph_input_resolves_composite_input() -> None: - parent = _parent_subgraph_workflow([COMPOSITE_BINDING]) + parent = _parent_subgraph_workflow([_composite_binding()]) + child_step: Step = EndNode(id="done", type="end", outcome="ok") child = Workflow( name="child", input_schema=_schema({"request": {"type": "object"}}), @@ -228,7 +242,7 @@ def test_prepared_subgraph_input_resolves_composite_input() -> None: output_schema=_schema({}), outcomes=["ok"], start="done", - nodes=[EndNode(id="done", type="end", outcome="ok")], + nodes=[child_step], edges=[], ) @@ -290,9 +304,15 @@ def test_interrupt_request_resolves_composite_input_and_resume_continues() -> No def _node_workflow( *, - input_bindings: list[dict[str, object]], + input_bindings: list[StepInputBinding], state_fields: dict[str, StateField], ) -> Workflow: + node_step: Step = NodeUse( + id="concat", + type="node", + node="concat", + input=input_bindings, + ) return Workflow( name="node_expression", input_schema=_schema({}), @@ -308,19 +328,20 @@ def _node_workflow( outcomes=["ok"], ) ], - nodes=[ - NodeUse( - id="concat", - type="node", - node="concat", - input=input_bindings, - ) - ], + nodes=[node_step], edges=[Edge.model_validate({"from": "concat", "outcome": "ok", "to": END})], ) -def _parent_subgraph_workflow(input_bindings: list[dict[str, object]]) -> Workflow: +def _parent_subgraph_workflow(input_bindings: list[StepInputBinding]) -> Workflow: + child_step: Step = SubgraphNode( + id="child", + type="subgraph", + workflow=WorkflowRef(name="child"), + input_schema=_schema({"request": {"type": "object"}}), + output_schema=_schema({}), + input=input_bindings, + ) return Workflow( name="parent", input_schema=_schema({}), @@ -330,16 +351,7 @@ def _parent_subgraph_workflow(input_bindings: list[dict[str, object]]) -> Workfl output_schema=_schema({}), outcomes=["ok"], start="child", - nodes=[ - SubgraphNode( - id="child", - type="subgraph", - workflow="child", - input_schema=_schema({"request": {"type": "object"}}), - output_schema=_schema({}), - input=input_bindings, - ) - ], + nodes=[child_step], edges=[Edge.model_validate({"from": "child", "outcome": "ok", "to": END})], ) diff --git a/tests/core/test_input_expressions.py b/tests/core/test_input_expressions.py index 5a8f754f..ed5cdc76 100644 --- a/tests/core/test_input_expressions.py +++ b/tests/core/test_input_expressions.py @@ -13,6 +13,7 @@ from wf_artifacts.drafts.models import ( ) from wf_core import Workflow from wf_core.models.input_bindings import ArrayExpression, LiteralExpression +from wf_core.models.json_values import JsonValue from wf_core.models.steps import ( InputExpressionBinding, InputPathBinding, @@ -152,8 +153,8 @@ def _nested_expression_array(depth: int) -> dict[str, object]: return expression -def _nested_json_list(depth: int) -> list[object]: - value: list[object] = ["leaf"] +def _nested_json_list(depth: int) -> list[JsonValue]: + value: list[JsonValue] = ["leaf"] for _ in range(depth - 1): value = [value] return value diff --git a/tests/wf_contract_manifest/test_generate.py b/tests/wf_contract_manifest/test_generate.py index f0f6ed56..5e0378f3 100644 --- a/tests/wf_contract_manifest/test_generate.py +++ b/tests/wf_contract_manifest/test_generate.py @@ -4,6 +4,7 @@ from collections.abc import Iterator, Mapping from typing import Any from wf_contract_manifest import generate_manifest +from wf_contract_manifest.model import ManifestOperation UNION_RESULTS = { "InspectCapabilityResult", @@ -148,7 +149,9 @@ def test_manifest_separates_recursive_step_inputs_from_workflow_outputs() -> Non } <= schemas.keys() input_binding_schema = schemas["InputExpressionBinding"] - assert input_binding_schema["properties"]["expression"] == { + properties = input_binding_schema.get("properties") + assert isinstance(properties, dict) + assert properties["expression"] == { "$ref": "#/components/schemas/InputExpression" } expression_schema = schemas["InputExpression"] @@ -162,7 +165,7 @@ def test_manifest_separates_recursive_step_inputs_from_workflow_outputs() -> Non "propertyName": "kind", } - def operation(method: str) -> dict[str, Any]: + def operation(method: str) -> ManifestOperation: return next(item for item in manifest["operations"] if item["method"] == method) step_input = operation("workflow.draft_workspaces.set_step_input_bindings") 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 {