test: use real node variable pools and lifecycle events (#43179)

This commit is contained in:
Asuka Minato
2026-10-02 02:54:21 +00:00
committed by GitHub
parent a2b0b2a143
commit 0b9b0db408
3 changed files with 159 additions and 155 deletions

View File

@@ -5,7 +5,12 @@ import pytest
from core.app.entities.app_invoke_entities import DifyRunContext, InvokeFrom, UserFrom
from core.workflow.nodes.agent_v2 import workspace_retirement_layer as layer_module
from core.workflow.nodes.agent_v2.workspace_retirement_layer import WorkflowAgentWorkspaceRetirementLayer
from core.workflow.system_variables import build_system_variables
from graphon.enums import BuiltinNodeTypes
from graphon.graph_engine.command_channels import InMemoryChannel
from graphon.graph_events import GraphRunSucceededEvent, NodeRunStartedEvent
from graphon.runtime import GraphRuntimeState, ReadOnlyGraphRuntimeStateWrapper, VariablePool
from libs.datetime_utils import naive_utc_now
def _run_context() -> DifyRunContext:
@@ -18,7 +23,21 @@ def _run_context() -> DifyRunContext:
)
def test_terminal_event_retires_workflow_workspace(monkeypatch: pytest.MonkeyPatch) -> None:
@pytest.fixture
def runtime_state() -> ReadOnlyGraphRuntimeStateWrapper:
return ReadOnlyGraphRuntimeStateWrapper(
GraphRuntimeState(
variable_pool=VariablePool.from_bootstrap(
system_variables=build_system_variables(workflow_execution_id="workflow-run-1")
),
start_at=0,
)
)
def test_terminal_event_retires_workflow_workspace(
monkeypatch: pytest.MonkeyPatch, runtime_state: ReadOnlyGraphRuntimeStateWrapper
) -> None:
store = MagicMock()
events: list[str] = []
store.retire_workflow_run.side_effect = lambda **_kwargs: events.append("retire") or ["workspace-1"]
@@ -26,10 +45,9 @@ def test_terminal_event_retires_workflow_workspace(monkeypatch: pytest.MonkeyPat
monkeypatch.setattr(layer_module, "WorkflowAgentWorkspaceStore", MagicMock(return_value=store))
monkeypatch.setattr(layer_module, "enqueue_agent_resource_collection", enqueue)
layer = WorkflowAgentWorkspaceRetirementLayer(dify_run_context=_run_context())
layer.initialize(MagicMock(), MagicMock())
monkeypatch.setattr(layer_module, "get_system_text", lambda *_: "workflow-run-1")
layer.initialize(runtime_state, InMemoryChannel())
layer.on_event(MagicMock(spec=GraphRunSucceededEvent))
layer.on_event(GraphRunSucceededEvent())
store.retire_workflow_run.assert_called_once_with(
tenant_id="tenant-1",
@@ -45,12 +63,22 @@ def test_non_terminal_event_does_not_retire_workspace(monkeypatch: pytest.Monkey
monkeypatch.setattr(layer_module, "WorkflowAgentWorkspaceStore", MagicMock(return_value=store))
layer = WorkflowAgentWorkspaceRetirementLayer(dify_run_context=_run_context())
layer.on_event(MagicMock(spec=NodeRunStartedEvent))
layer.on_event(
NodeRunStartedEvent(
id="execution-1",
node_id="node-1",
node_type=BuiltinNodeTypes.START,
node_title="Start",
start_at=naive_utc_now(),
)
)
store.retire_workflow_run.assert_not_called()
def test_terminal_retirement_failure_does_not_replace_terminal_event(monkeypatch: pytest.MonkeyPatch) -> None:
def test_terminal_retirement_failure_does_not_replace_terminal_event(
monkeypatch: pytest.MonkeyPatch, runtime_state: ReadOnlyGraphRuntimeStateWrapper
) -> None:
store = MagicMock()
store.retire_workflow_run.side_effect = RuntimeError("database unavailable")
log_exception = MagicMock()
@@ -59,10 +87,9 @@ def test_terminal_retirement_failure_does_not_replace_terminal_event(monkeypatch
monkeypatch.setattr(layer_module.logger, "exception", log_exception)
monkeypatch.setattr(layer_module, "enqueue_agent_resource_collection", enqueue)
layer = WorkflowAgentWorkspaceRetirementLayer(dify_run_context=_run_context())
layer.initialize(MagicMock(), MagicMock())
monkeypatch.setattr(layer_module, "get_system_text", lambda *_: "workflow-run-1")
layer.initialize(runtime_state, InMemoryChannel())
layer.on_event(MagicMock(spec=GraphRunSucceededEvent))
layer.on_event(GraphRunSucceededEvent())
log_exception.assert_called_once_with(
"Failed to retire Workflow Agent Workspaces",

View File

@@ -1,5 +1,3 @@
from unittest.mock import MagicMock
import pytest
from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY
@@ -7,7 +5,7 @@ from graphon.entities import GraphInitParams
from graphon.enums import WorkflowNodeExecutionStatus
from graphon.nodes.list_operator.entities import ListOperatorNodeData
from graphon.nodes.list_operator.node import ListOperatorNode
from graphon.runtime import GraphRuntimeState
from graphon.runtime import GraphRuntimeState, VariablePool
from graphon.variables import ArrayNumberSegment, ArrayStringSegment
@@ -31,12 +29,9 @@ class TestListOperatorNode:
}
@pytest.fixture
def mock_graph_runtime_state(self):
"""Create mock GraphRuntimeState."""
mock_state = MagicMock(spec=GraphRuntimeState)
mock_variable_pool = MagicMock()
mock_state.variable_pool = mock_variable_pool
return mock_state
def graph_runtime_state(self):
"""Create graph state backed by the real variable pool."""
return GraphRuntimeState(variable_pool=VariablePool(), start_at=0)
@pytest.fixture
def graph_init_params(self):
@@ -57,15 +52,15 @@ class TestListOperatorNode:
)
@pytest.fixture
def list_operator_node_factory(self, graph_init_params, mock_graph_runtime_state):
def list_operator_node_factory(self, graph_init_params, graph_runtime_state):
"""Factory fixture for creating ListOperatorNode instances."""
def _create_node(config, mock_variable):
mock_graph_runtime_state.variable_pool.get.return_value = mock_variable
def _create_node(config, variable):
graph_runtime_state.variable_pool.add(config["variable"], variable)
return self._build_node(
data=config,
graph_init_params=graph_init_params,
graph_runtime_state=mock_graph_runtime_state,
graph_runtime_state=graph_runtime_state,
)
return _create_node
@@ -80,15 +75,15 @@ class TestListOperatorNode:
"limit": {"enabled": False},
}
mock_var = ArrayStringSegment(value=["apple", "banana", "cherry"])
node = list_operator_node_factory(config, mock_var)
variable = ArrayStringSegment(value=["apple", "banana", "cherry"])
node = list_operator_node_factory(config, variable)
result = node._run()
assert result.status == WorkflowNodeExecutionStatus.SUCCEEDED
assert result.outputs["result"].value == ["apple", "banana", "cherry"]
def test_run_with_empty_array(self, mock_graph_runtime_state, graph_init_params):
def test_run_with_empty_array(self, graph_runtime_state, graph_init_params):
"""Test with empty array."""
config = {
"title": "Test",
@@ -98,13 +93,13 @@ class TestListOperatorNode:
"limit": {"enabled": False},
}
mock_var = ArrayStringSegment(value=[])
mock_graph_runtime_state.variable_pool.get.return_value = mock_var
variable = ArrayStringSegment(value=[])
graph_runtime_state.variable_pool.add(config["variable"], variable)
node = self._build_node(
data=config,
graph_init_params=graph_init_params,
graph_runtime_state=mock_graph_runtime_state,
graph_runtime_state=graph_runtime_state,
)
result = node._run()
@@ -114,7 +109,7 @@ class TestListOperatorNode:
assert result.outputs["first_record"] is None
assert result.outputs["last_record"] is None
def test_run_with_filter_contains(self, mock_graph_runtime_state, graph_init_params):
def test_run_with_filter_contains(self, graph_runtime_state, graph_init_params):
"""Test filter with contains condition."""
config = {
"title": "Test",
@@ -124,13 +119,13 @@ class TestListOperatorNode:
"limit": {"enabled": False},
}
mock_var = ArrayStringSegment(value=["apple", "banana", "pineapple", "cherry"])
mock_graph_runtime_state.variable_pool.get.return_value = mock_var
variable = ArrayStringSegment(value=["apple", "banana", "pineapple", "cherry"])
graph_runtime_state.variable_pool.add(config["variable"], variable)
node = self._build_node(
data=config,
graph_init_params=graph_init_params,
graph_runtime_state=mock_graph_runtime_state,
graph_runtime_state=graph_runtime_state,
)
result = node._run()
@@ -138,7 +133,7 @@ class TestListOperatorNode:
assert result.status == WorkflowNodeExecutionStatus.SUCCEEDED
assert result.outputs["result"].value == ["apple", "pineapple"]
def test_run_with_filter_not_contains(self, mock_graph_runtime_state, graph_init_params):
def test_run_with_filter_not_contains(self, graph_runtime_state, graph_init_params):
"""Test filter with not contains condition."""
config = {
"title": "Test",
@@ -148,13 +143,13 @@ class TestListOperatorNode:
"limit": {"enabled": False},
}
mock_var = ArrayStringSegment(value=["apple", "banana", "pineapple", "cherry"])
mock_graph_runtime_state.variable_pool.get.return_value = mock_var
variable = ArrayStringSegment(value=["apple", "banana", "pineapple", "cherry"])
graph_runtime_state.variable_pool.add(config["variable"], variable)
node = self._build_node(
data=config,
graph_init_params=graph_init_params,
graph_runtime_state=mock_graph_runtime_state,
graph_runtime_state=graph_runtime_state,
)
result = node._run()
@@ -162,7 +157,7 @@ class TestListOperatorNode:
assert result.status == WorkflowNodeExecutionStatus.SUCCEEDED
assert result.outputs["result"].value == ["banana", "cherry"]
def test_run_with_number_filter_greater_than(self, mock_graph_runtime_state, graph_init_params):
def test_run_with_number_filter_greater_than(self, graph_runtime_state, graph_init_params):
"""Test filter with greater than condition on numbers."""
config = {
"title": "Test",
@@ -172,13 +167,13 @@ class TestListOperatorNode:
"limit": {"enabled": False},
}
mock_var = ArrayNumberSegment(value=[1, 3, 5, 7, 9, 11])
mock_graph_runtime_state.variable_pool.get.return_value = mock_var
variable = ArrayNumberSegment(value=[1, 3, 5, 7, 9, 11])
graph_runtime_state.variable_pool.add(config["variable"], variable)
node = self._build_node(
data=config,
graph_init_params=graph_init_params,
graph_runtime_state=mock_graph_runtime_state,
graph_runtime_state=graph_runtime_state,
)
result = node._run()
@@ -186,7 +181,7 @@ class TestListOperatorNode:
assert result.status == WorkflowNodeExecutionStatus.SUCCEEDED
assert result.outputs["result"].value == [7, 9, 11]
def test_run_with_order_ascending(self, mock_graph_runtime_state, graph_init_params):
def test_run_with_order_ascending(self, graph_runtime_state, graph_init_params):
"""Test ordering in ascending order."""
config = {
"title": "Test",
@@ -199,13 +194,13 @@ class TestListOperatorNode:
"limit": {"enabled": False},
}
mock_var = ArrayStringSegment(value=["cherry", "apple", "banana"])
mock_graph_runtime_state.variable_pool.get.return_value = mock_var
variable = ArrayStringSegment(value=["cherry", "apple", "banana"])
graph_runtime_state.variable_pool.add(config["variable"], variable)
node = self._build_node(
data=config,
graph_init_params=graph_init_params,
graph_runtime_state=mock_graph_runtime_state,
graph_runtime_state=graph_runtime_state,
)
result = node._run()
@@ -213,7 +208,7 @@ class TestListOperatorNode:
assert result.status == WorkflowNodeExecutionStatus.SUCCEEDED
assert result.outputs["result"].value == ["apple", "banana", "cherry"]
def test_run_with_order_descending(self, mock_graph_runtime_state, graph_init_params):
def test_run_with_order_descending(self, graph_runtime_state, graph_init_params):
"""Test ordering in descending order."""
config = {
"title": "Test",
@@ -226,13 +221,13 @@ class TestListOperatorNode:
"limit": {"enabled": False},
}
mock_var = ArrayStringSegment(value=["cherry", "apple", "banana"])
mock_graph_runtime_state.variable_pool.get.return_value = mock_var
variable = ArrayStringSegment(value=["cherry", "apple", "banana"])
graph_runtime_state.variable_pool.add(config["variable"], variable)
node = self._build_node(
data=config,
graph_init_params=graph_init_params,
graph_runtime_state=mock_graph_runtime_state,
graph_runtime_state=graph_runtime_state,
)
result = node._run()
@@ -240,7 +235,7 @@ class TestListOperatorNode:
assert result.status == WorkflowNodeExecutionStatus.SUCCEEDED
assert result.outputs["result"].value == ["cherry", "banana", "apple"]
def test_run_with_limit(self, mock_graph_runtime_state, graph_init_params):
def test_run_with_limit(self, graph_runtime_state, graph_init_params):
"""Test with limit enabled."""
config = {
"title": "Test",
@@ -253,13 +248,13 @@ class TestListOperatorNode:
},
}
mock_var = ArrayStringSegment(value=["apple", "banana", "cherry", "date"])
mock_graph_runtime_state.variable_pool.get.return_value = mock_var
variable = ArrayStringSegment(value=["apple", "banana", "cherry", "date"])
graph_runtime_state.variable_pool.add(config["variable"], variable)
node = self._build_node(
data=config,
graph_init_params=graph_init_params,
graph_runtime_state=mock_graph_runtime_state,
graph_runtime_state=graph_runtime_state,
)
result = node._run()
@@ -267,7 +262,7 @@ class TestListOperatorNode:
assert result.status == WorkflowNodeExecutionStatus.SUCCEEDED
assert result.outputs["result"].value == ["apple", "banana"]
def test_run_with_filter_order_and_limit(self, mock_graph_runtime_state, graph_init_params):
def test_run_with_filter_order_and_limit(self, graph_runtime_state, graph_init_params):
"""Test with filter, order, and limit combined."""
config = {
"title": "Test",
@@ -283,13 +278,13 @@ class TestListOperatorNode:
},
}
mock_var = ArrayNumberSegment(value=[1, 2, 3, 4, 5, 6, 7, 8, 9])
mock_graph_runtime_state.variable_pool.get.return_value = mock_var
variable = ArrayNumberSegment(value=[1, 2, 3, 4, 5, 6, 7, 8, 9])
graph_runtime_state.variable_pool.add(config["variable"], variable)
node = self._build_node(
data=config,
graph_init_params=graph_init_params,
graph_runtime_state=mock_graph_runtime_state,
graph_runtime_state=graph_runtime_state,
)
result = node._run()
@@ -297,7 +292,7 @@ class TestListOperatorNode:
assert result.status == WorkflowNodeExecutionStatus.SUCCEEDED
assert result.outputs["result"].value == [9, 8, 7]
def test_run_with_variable_not_found(self, mock_graph_runtime_state, graph_init_params):
def test_run_with_variable_not_found(self, graph_runtime_state, graph_init_params):
"""Test when variable is not found."""
config = {
"title": "Test",
@@ -307,12 +302,10 @@ class TestListOperatorNode:
"limit": {"enabled": False},
}
mock_graph_runtime_state.variable_pool.get.return_value = None
node = self._build_node(
data=config,
graph_init_params=graph_init_params,
graph_runtime_state=mock_graph_runtime_state,
graph_runtime_state=graph_runtime_state,
)
result = node._run()
@@ -320,7 +313,7 @@ class TestListOperatorNode:
assert result.status == WorkflowNodeExecutionStatus.FAILED
assert "Variable not found" in result.error
def test_run_with_first_and_last_record(self, mock_graph_runtime_state, graph_init_params):
def test_run_with_first_and_last_record(self, graph_runtime_state, graph_init_params):
"""Test first_record and last_record outputs."""
config = {
"title": "Test",
@@ -330,13 +323,13 @@ class TestListOperatorNode:
"limit": {"enabled": False},
}
mock_var = ArrayStringSegment(value=["first", "middle", "last"])
mock_graph_runtime_state.variable_pool.get.return_value = mock_var
variable = ArrayStringSegment(value=["first", "middle", "last"])
graph_runtime_state.variable_pool.add(config["variable"], variable)
node = self._build_node(
data=config,
graph_init_params=graph_init_params,
graph_runtime_state=mock_graph_runtime_state,
graph_runtime_state=graph_runtime_state,
)
result = node._run()
@@ -345,7 +338,7 @@ class TestListOperatorNode:
assert result.outputs["first_record"] == "first"
assert result.outputs["last_record"] == "last"
def test_run_with_filter_startswith(self, mock_graph_runtime_state, graph_init_params):
def test_run_with_filter_startswith(self, graph_runtime_state, graph_init_params):
"""Test filter with startswith condition."""
config = {
"title": "Test",
@@ -355,13 +348,13 @@ class TestListOperatorNode:
"limit": {"enabled": False},
}
mock_var = ArrayStringSegment(value=["apple", "application", "banana", "apricot"])
mock_graph_runtime_state.variable_pool.get.return_value = mock_var
variable = ArrayStringSegment(value=["apple", "application", "banana", "apricot"])
graph_runtime_state.variable_pool.add(config["variable"], variable)
node = self._build_node(
data=config,
graph_init_params=graph_init_params,
graph_runtime_state=mock_graph_runtime_state,
graph_runtime_state=graph_runtime_state,
)
result = node._run()
@@ -369,7 +362,7 @@ class TestListOperatorNode:
assert result.status == WorkflowNodeExecutionStatus.SUCCEEDED
assert result.outputs["result"].value == ["apple", "application"]
def test_run_with_filter_endswith(self, mock_graph_runtime_state, graph_init_params):
def test_run_with_filter_endswith(self, graph_runtime_state, graph_init_params):
"""Test filter with endswith condition."""
config = {
"title": "Test",
@@ -379,13 +372,13 @@ class TestListOperatorNode:
"limit": {"enabled": False},
}
mock_var = ArrayStringSegment(value=["apple", "banana", "pineapple", "table"])
mock_graph_runtime_state.variable_pool.get.return_value = mock_var
variable = ArrayStringSegment(value=["apple", "banana", "pineapple", "table"])
graph_runtime_state.variable_pool.add(config["variable"], variable)
node = self._build_node(
data=config,
graph_init_params=graph_init_params,
graph_runtime_state=mock_graph_runtime_state,
graph_runtime_state=graph_runtime_state,
)
result = node._run()
@@ -393,7 +386,7 @@ class TestListOperatorNode:
assert result.status == WorkflowNodeExecutionStatus.SUCCEEDED
assert result.outputs["result"].value == ["apple", "pineapple", "table"]
def test_run_with_number_filter_equals(self, mock_graph_runtime_state, graph_init_params):
def test_run_with_number_filter_equals(self, graph_runtime_state, graph_init_params):
"""Test number filter with equals condition."""
config = {
"title": "Test",
@@ -403,13 +396,13 @@ class TestListOperatorNode:
"limit": {"enabled": False},
}
mock_var = ArrayNumberSegment(value=[1, 3, 5, 5, 7, 9])
mock_graph_runtime_state.variable_pool.get.return_value = mock_var
variable = ArrayNumberSegment(value=[1, 3, 5, 5, 7, 9])
graph_runtime_state.variable_pool.add(config["variable"], variable)
node = self._build_node(
data=config,
graph_init_params=graph_init_params,
graph_runtime_state=mock_graph_runtime_state,
graph_runtime_state=graph_runtime_state,
)
result = node._run()
@@ -417,7 +410,7 @@ class TestListOperatorNode:
assert result.status == WorkflowNodeExecutionStatus.SUCCEEDED
assert result.outputs["result"].value == [5, 5]
def test_run_with_number_filter_not_equals(self, mock_graph_runtime_state, graph_init_params):
def test_run_with_number_filter_not_equals(self, graph_runtime_state, graph_init_params):
"""Test number filter with not equals condition."""
config = {
"title": "Test",
@@ -427,13 +420,13 @@ class TestListOperatorNode:
"limit": {"enabled": False},
}
mock_var = ArrayNumberSegment(value=[1, 3, 5, 7, 9])
mock_graph_runtime_state.variable_pool.get.return_value = mock_var
variable = ArrayNumberSegment(value=[1, 3, 5, 7, 9])
graph_runtime_state.variable_pool.add(config["variable"], variable)
node = self._build_node(
data=config,
graph_init_params=graph_init_params,
graph_runtime_state=mock_graph_runtime_state,
graph_runtime_state=graph_runtime_state,
)
result = node._run()
@@ -441,7 +434,7 @@ class TestListOperatorNode:
assert result.status == WorkflowNodeExecutionStatus.SUCCEEDED
assert result.outputs["result"].value == [1, 3, 7, 9]
def test_run_with_number_order_ascending(self, mock_graph_runtime_state, graph_init_params):
def test_run_with_number_order_ascending(self, graph_runtime_state, graph_init_params):
"""Test number ordering in ascending order."""
config = {
"title": "Test",
@@ -454,13 +447,13 @@ class TestListOperatorNode:
"limit": {"enabled": False},
}
mock_var = ArrayNumberSegment(value=[9, 3, 7, 1, 5])
mock_graph_runtime_state.variable_pool.get.return_value = mock_var
variable = ArrayNumberSegment(value=[9, 3, 7, 1, 5])
graph_runtime_state.variable_pool.add(config["variable"], variable)
node = self._build_node(
data=config,
graph_init_params=graph_init_params,
graph_runtime_state=mock_graph_runtime_state,
graph_runtime_state=graph_runtime_state,
)
result = node._run()

View File

@@ -4,11 +4,10 @@ import pytest
from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom
from graphon.enums import WorkflowNodeExecutionStatus
from graphon.graph import Graph
from graphon.nodes.base.entities import VariableSelector
from graphon.nodes.template_transform.entities import TemplateTransformNodeData
from graphon.nodes.template_transform.template_transform_node import TemplateTransformNode
from graphon.runtime import GraphRuntimeState
from graphon.runtime import GraphRuntimeState, VariablePool
from graphon.template_rendering import TemplateRenderError
from tests.workflow_test_utils import build_test_graph_init_params
@@ -39,21 +38,13 @@ class TestTemplateTransformNode:
"""Comprehensive test suite for TemplateTransformNode."""
@pytest.fixture
def mock_graph_runtime_state(self):
"""Create a mock GraphRuntimeState with variable pool."""
mock_state = MagicMock(spec=GraphRuntimeState)
mock_variable_pool = MagicMock()
mock_state.variable_pool = mock_variable_pool
return mock_state
@pytest.fixture
def mock_graph(self):
"""Create a mock Graph (kept for backward compat in other tests)."""
return MagicMock(spec=Graph)
def graph_runtime_state(self) -> GraphRuntimeState:
"""Create graph state with real variable storage and conversion."""
return GraphRuntimeState(variable_pool=VariablePool(), start_at=0)
@pytest.fixture
def graph_init_params(self):
"""Create a mock GraphInitParams."""
"""Create validated graph initialization parameters."""
return build_test_graph_init_params(
workflow_id="test_workflow",
graph_config={},
@@ -82,7 +73,7 @@ class TestTemplateTransformNode:
def test_node_initialization_rejects_non_positive_max_output_length(
self,
basic_node_data,
mock_graph_runtime_state,
graph_runtime_state,
graph_init_params,
max_output_length,
):
@@ -92,24 +83,22 @@ class TestTemplateTransformNode:
_build_template_transform_node(
node_data=basic_node_data,
graph_init_params=graph_init_params,
graph_runtime_state=mock_graph_runtime_state,
graph_runtime_state=graph_runtime_state,
jinja2_template_renderer=mock_renderer,
max_output_length=max_output_length,
)
def test_run_simple_template(self, basic_node_data, mock_graph_runtime_state, graph_init_params):
def test_run_simple_template(self, basic_node_data, graph_runtime_state, graph_init_params):
"""Test _run with simple template transformation using injected renderer."""
# Setup mock variable pool
mock_name_value = MagicMock()
mock_name_value.to_object.return_value = "Alice"
mock_age_value = MagicMock()
mock_age_value.to_object.return_value = 30
name_value = "Alice"
age_value = 30
variable_map = {
("sys", "user_name"): mock_name_value,
("sys", "user_age"): mock_age_value,
("sys", "user_name"): name_value,
("sys", "user_age"): age_value,
}
mock_graph_runtime_state.variable_pool.get.side_effect = lambda selector: variable_map.get(tuple(selector))
for selector, value in variable_map.items():
graph_runtime_state.variable_pool.add(selector, value)
# Setup mock renderer
mock_renderer = MagicMock()
@@ -118,7 +107,7 @@ class TestTemplateTransformNode:
node = _build_template_transform_node(
node_data=basic_node_data,
graph_init_params=graph_init_params,
graph_runtime_state=mock_graph_runtime_state,
graph_runtime_state=graph_runtime_state,
jinja2_template_renderer=mock_renderer,
)
@@ -129,7 +118,7 @@ class TestTemplateTransformNode:
assert result.inputs["name"] == "Alice"
assert result.inputs["age"] == 30
def test_run_with_none_values(self, mock_graph_runtime_state, graph_init_params):
def test_run_with_none_values(self, graph_runtime_state, graph_init_params):
"""Test _run with None variable values."""
node_data = {
"title": "Test",
@@ -137,15 +126,13 @@ class TestTemplateTransformNode:
"template": "Value: {{ value }}",
}
mock_graph_runtime_state.variable_pool.get.return_value = None
mock_renderer = MagicMock()
mock_renderer.render_template.return_value = "Value: "
node = _build_template_transform_node(
node_data=node_data,
graph_init_params=graph_init_params,
graph_runtime_state=mock_graph_runtime_state,
graph_runtime_state=graph_runtime_state,
jinja2_template_renderer=mock_renderer,
)
@@ -154,9 +141,10 @@ class TestTemplateTransformNode:
assert result.status == WorkflowNodeExecutionStatus.SUCCEEDED
assert result.inputs["value"] is None
def test_run_with_render_error(self, basic_node_data, mock_graph_runtime_state, graph_init_params):
def test_run_with_render_error(self, basic_node_data, graph_runtime_state, graph_init_params):
"""Test _run when template rendering fails."""
mock_graph_runtime_state.variable_pool.get.return_value = MagicMock()
graph_runtime_state.variable_pool.add(["sys", "user_name"], "Alice")
graph_runtime_state.variable_pool.add(["sys", "user_age"], 30)
mock_renderer = MagicMock()
mock_renderer.render_template.side_effect = TemplateRenderError("Template syntax error")
@@ -164,7 +152,7 @@ class TestTemplateTransformNode:
node = _build_template_transform_node(
node_data=basic_node_data,
graph_init_params=graph_init_params,
graph_runtime_state=mock_graph_runtime_state,
graph_runtime_state=graph_runtime_state,
jinja2_template_renderer=mock_renderer,
)
@@ -173,9 +161,10 @@ class TestTemplateTransformNode:
assert result.status == WorkflowNodeExecutionStatus.FAILED
assert "Template syntax error" in result.error
def test_run_output_length_exceeds_limit(self, basic_node_data, mock_graph_runtime_state, graph_init_params):
def test_run_output_length_exceeds_limit(self, basic_node_data, graph_runtime_state, graph_init_params):
"""Test _run when output exceeds maximum length."""
mock_graph_runtime_state.variable_pool.get.return_value = MagicMock()
graph_runtime_state.variable_pool.add(["sys", "user_name"], "Alice")
graph_runtime_state.variable_pool.add(["sys", "user_age"], 30)
mock_renderer = MagicMock()
mock_renderer.render_template.return_value = "This is a very long output that exceeds the limit"
@@ -183,7 +172,7 @@ class TestTemplateTransformNode:
node = _build_template_transform_node(
node_data=basic_node_data,
graph_init_params=graph_init_params,
graph_runtime_state=mock_graph_runtime_state,
graph_runtime_state=graph_runtime_state,
jinja2_template_renderer=mock_renderer,
max_output_length=10,
)
@@ -193,10 +182,9 @@ class TestTemplateTransformNode:
assert result.status == WorkflowNodeExecutionStatus.FAILED
assert "Output length exceeds" in result.error
def test_run_output_length_equal_to_limit_succeeds(
self, basic_node_data, mock_graph_runtime_state, graph_init_params
):
mock_graph_runtime_state.variable_pool.get.return_value = MagicMock()
def test_run_output_length_equal_to_limit_succeeds(self, basic_node_data, graph_runtime_state, graph_init_params):
graph_runtime_state.variable_pool.add(["sys", "user_name"], "Alice")
graph_runtime_state.variable_pool.add(["sys", "user_age"], 30)
mock_renderer = MagicMock()
mock_renderer.render_template.return_value = "1234567890"
@@ -204,7 +192,7 @@ class TestTemplateTransformNode:
node = _build_template_transform_node(
node_data=basic_node_data,
graph_init_params=graph_init_params,
graph_runtime_state=mock_graph_runtime_state,
graph_runtime_state=graph_runtime_state,
jinja2_template_renderer=mock_renderer,
max_output_length=10,
)
@@ -214,7 +202,7 @@ class TestTemplateTransformNode:
assert result.status == WorkflowNodeExecutionStatus.SUCCEEDED
assert result.outputs["output"] == "1234567890"
def test_run_with_complex_jinja2_template(self, mock_graph_runtime_state, graph_init_params):
def test_run_with_complex_jinja2_template(self, graph_runtime_state, graph_init_params):
"""Test _run with complex Jinja2 template including loops and conditions."""
node_data = {
"title": "Complex Template",
@@ -228,16 +216,15 @@ class TestTemplateTransformNode:
),
}
mock_items = MagicMock()
mock_items.to_object.return_value = ["apple", "banana", "orange"]
mock_show_total = MagicMock()
mock_show_total.to_object.return_value = True
items = ["apple", "banana", "orange"]
show_total = True
variable_map = {
("sys", "items"): mock_items,
("sys", "show_total"): mock_show_total,
("sys", "items"): items,
("sys", "show_total"): show_total,
}
mock_graph_runtime_state.variable_pool.get.side_effect = lambda selector: variable_map.get(tuple(selector))
for selector, value in variable_map.items():
graph_runtime_state.variable_pool.add(selector, value)
mock_renderer = MagicMock()
mock_renderer.render_template.return_value = "apple, banana, orange (Total: 3)"
@@ -245,7 +232,7 @@ class TestTemplateTransformNode:
node = _build_template_transform_node(
node_data=node_data,
graph_init_params=graph_init_params,
graph_runtime_state=mock_graph_runtime_state,
graph_runtime_state=graph_runtime_state,
jinja2_template_renderer=mock_renderer,
)
@@ -337,7 +324,7 @@ class TestTemplateTransformNode:
assert mapping == {"node_123.var1": ["sys", "input1"]}
def test_run_with_empty_variables(self, mock_graph_runtime_state, graph_init_params):
def test_run_with_empty_variables(self, graph_runtime_state, graph_init_params):
"""Test _run with no variables (static template)."""
node_data = {
"title": "Static Template",
@@ -351,7 +338,7 @@ class TestTemplateTransformNode:
node = _build_template_transform_node(
node_data=node_data,
graph_init_params=graph_init_params,
graph_runtime_state=mock_graph_runtime_state,
graph_runtime_state=graph_runtime_state,
jinja2_template_renderer=mock_renderer,
)
@@ -361,7 +348,7 @@ class TestTemplateTransformNode:
assert result.outputs["output"] == "This is a static message."
assert result.inputs == {}
def test_run_with_numeric_values(self, mock_graph_runtime_state, graph_init_params):
def test_run_with_numeric_values(self, graph_runtime_state, graph_init_params):
"""Test _run with numeric variable values."""
node_data = {
"title": "Numeric Template",
@@ -372,16 +359,15 @@ class TestTemplateTransformNode:
"template": "Total: ${{ price * quantity }}",
}
mock_price = MagicMock()
mock_price.to_object.return_value = 10.5
mock_quantity = MagicMock()
mock_quantity.to_object.return_value = 3
price = 10.5
quantity = 3
variable_map = {
("sys", "price"): mock_price,
("sys", "quantity"): mock_quantity,
("sys", "price"): price,
("sys", "quantity"): quantity,
}
mock_graph_runtime_state.variable_pool.get.side_effect = lambda selector: variable_map.get(tuple(selector))
for selector, value in variable_map.items():
graph_runtime_state.variable_pool.add(selector, value)
mock_renderer = MagicMock()
mock_renderer.render_template.return_value = "Total: $31.5"
@@ -389,7 +375,7 @@ class TestTemplateTransformNode:
node = _build_template_transform_node(
node_data=node_data,
graph_init_params=graph_init_params,
graph_runtime_state=mock_graph_runtime_state,
graph_runtime_state=graph_runtime_state,
jinja2_template_renderer=mock_renderer,
)
@@ -398,7 +384,7 @@ class TestTemplateTransformNode:
assert result.status == WorkflowNodeExecutionStatus.SUCCEEDED
assert result.outputs["output"] == "Total: $31.5"
def test_run_with_dict_values(self, mock_graph_runtime_state, graph_init_params):
def test_run_with_dict_values(self, graph_runtime_state, graph_init_params):
"""Test _run with dictionary variable values."""
node_data = {
"title": "Dict Template",
@@ -406,10 +392,9 @@ class TestTemplateTransformNode:
"template": "Name: {{ user.name }}, Email: {{ user.email }}",
}
mock_user = MagicMock()
mock_user.to_object.return_value = {"name": "John Doe", "email": "john@example.com"}
user = {"name": "John Doe", "email": "john@example.com"}
mock_graph_runtime_state.variable_pool.get.return_value = mock_user
graph_runtime_state.variable_pool.add(["sys", "user_data"], user)
mock_renderer = MagicMock()
mock_renderer.render_template.return_value = "Name: John Doe, Email: john@example.com"
@@ -417,7 +402,7 @@ class TestTemplateTransformNode:
node = _build_template_transform_node(
node_data=node_data,
graph_init_params=graph_init_params,
graph_runtime_state=mock_graph_runtime_state,
graph_runtime_state=graph_runtime_state,
jinja2_template_renderer=mock_renderer,
)
@@ -427,7 +412,7 @@ class TestTemplateTransformNode:
assert "John Doe" in result.outputs["output"]
assert "john@example.com" in result.outputs["output"]
def test_run_with_list_values(self, mock_graph_runtime_state, graph_init_params):
def test_run_with_list_values(self, graph_runtime_state, graph_init_params):
"""Test _run with list variable values."""
node_data = {
"title": "List Template",
@@ -435,10 +420,9 @@ class TestTemplateTransformNode:
"template": "Tags: {% for tag in tags %}#{{ tag }} {% endfor %}",
}
mock_tags = MagicMock()
mock_tags.to_object.return_value = ["python", "ai", "workflow"]
tags = ["python", "ai", "workflow"]
mock_graph_runtime_state.variable_pool.get.return_value = mock_tags
graph_runtime_state.variable_pool.add(["sys", "tags"], tags)
mock_renderer = MagicMock()
mock_renderer.render_template.return_value = "Tags: #python #ai #workflow "
@@ -446,7 +430,7 @@ class TestTemplateTransformNode:
node = _build_template_transform_node(
node_data=node_data,
graph_init_params=graph_init_params,
graph_runtime_state=mock_graph_runtime_state,
graph_runtime_state=graph_runtime_state,
jinja2_template_renderer=mock_renderer,
)