mirror of
https://github.com/langgenius/dify.git
synced 2026-04-28 06:28:05 +08:00
Merge remote-tracking branch 'origin/main' into feat/trigger
This commit is contained in:
@ -0,0 +1,113 @@
|
||||
from core.variables.segments import (
|
||||
BooleanSegment,
|
||||
IntegerSegment,
|
||||
NoneSegment,
|
||||
StringSegment,
|
||||
)
|
||||
from core.workflow.entities.variable_pool import VariablePool
|
||||
|
||||
|
||||
class TestVariablePoolGetAndNestedAttribute:
|
||||
#
|
||||
# _get_nested_attribute tests
|
||||
#
|
||||
def test__get_nested_attribute_existing_key(self):
|
||||
pool = VariablePool.empty()
|
||||
obj = {"a": 123}
|
||||
segment = pool._get_nested_attribute(obj, "a")
|
||||
assert segment is not None
|
||||
assert segment.value == 123
|
||||
|
||||
def test__get_nested_attribute_missing_key(self):
|
||||
pool = VariablePool.empty()
|
||||
obj = {"a": 123}
|
||||
segment = pool._get_nested_attribute(obj, "b")
|
||||
assert segment is None
|
||||
|
||||
def test__get_nested_attribute_non_dict(self):
|
||||
pool = VariablePool.empty()
|
||||
obj = ["not", "a", "dict"]
|
||||
segment = pool._get_nested_attribute(obj, "a")
|
||||
assert segment is None
|
||||
|
||||
def test__get_nested_attribute_with_none_value(self):
|
||||
pool = VariablePool.empty()
|
||||
obj = {"a": None}
|
||||
segment = pool._get_nested_attribute(obj, "a")
|
||||
assert segment is not None
|
||||
assert isinstance(segment, NoneSegment)
|
||||
|
||||
def test__get_nested_attribute_with_empty_string(self):
|
||||
pool = VariablePool.empty()
|
||||
obj = {"a": ""}
|
||||
segment = pool._get_nested_attribute(obj, "a")
|
||||
assert segment is not None
|
||||
assert isinstance(segment, StringSegment)
|
||||
assert segment.value == ""
|
||||
|
||||
#
|
||||
# get tests
|
||||
#
|
||||
def test_get_simple_variable(self):
|
||||
pool = VariablePool.empty()
|
||||
pool.add(("node1", "var1"), "value1")
|
||||
segment = pool.get(("node1", "var1"))
|
||||
assert segment is not None
|
||||
assert segment.value == "value1"
|
||||
|
||||
def test_get_missing_variable(self):
|
||||
pool = VariablePool.empty()
|
||||
result = pool.get(("node1", "unknown"))
|
||||
assert result is None
|
||||
|
||||
def test_get_with_too_short_selector(self):
|
||||
pool = VariablePool.empty()
|
||||
result = pool.get(("only_node",))
|
||||
assert result is None
|
||||
|
||||
def test_get_nested_object_attribute(self):
|
||||
pool = VariablePool.empty()
|
||||
obj_value = {"inner": "hello"}
|
||||
pool.add(("node1", "obj"), obj_value)
|
||||
|
||||
# simulate selector with nested attr
|
||||
segment = pool.get(("node1", "obj", "inner"))
|
||||
assert segment is not None
|
||||
assert segment.value == "hello"
|
||||
|
||||
def test_get_nested_object_missing_attribute(self):
|
||||
pool = VariablePool.empty()
|
||||
obj_value = {"inner": "hello"}
|
||||
pool.add(("node1", "obj"), obj_value)
|
||||
|
||||
result = pool.get(("node1", "obj", "not_exist"))
|
||||
assert result is None
|
||||
|
||||
def test_get_nested_object_attribute_with_falsy_values(self):
|
||||
pool = VariablePool.empty()
|
||||
obj_value = {
|
||||
"inner_none": None,
|
||||
"inner_empty": "",
|
||||
"inner_zero": 0,
|
||||
"inner_false": False,
|
||||
}
|
||||
pool.add(("node1", "obj"), obj_value)
|
||||
|
||||
segment_none = pool.get(("node1", "obj", "inner_none"))
|
||||
assert segment_none is not None
|
||||
assert isinstance(segment_none, NoneSegment)
|
||||
|
||||
segment_empty = pool.get(("node1", "obj", "inner_empty"))
|
||||
assert segment_empty is not None
|
||||
assert isinstance(segment_empty, StringSegment)
|
||||
assert segment_empty.value == ""
|
||||
|
||||
segment_zero = pool.get(("node1", "obj", "inner_zero"))
|
||||
assert segment_zero is not None
|
||||
assert isinstance(segment_zero, IntegerSegment)
|
||||
assert segment_zero.value == 0
|
||||
|
||||
segment_false = pool.get(("node1", "obj", "inner_false"))
|
||||
assert segment_false is not None
|
||||
assert isinstance(segment_false, BooleanSegment)
|
||||
assert segment_false.value is False
|
||||
@ -0,0 +1,41 @@
|
||||
"""Validate conversation variable updates inside an iteration workflow.
|
||||
|
||||
This test uses the ``update-conversation-variable-in-iteration`` fixture, which
|
||||
routes ``sys.query`` into the conversation variable ``answer`` from within an
|
||||
iteration container. The workflow should surface that updated conversation
|
||||
variable in the final answer output.
|
||||
|
||||
Code nodes in the fixture are mocked because their concrete outputs are not
|
||||
relevant to verifying variable propagation semantics.
|
||||
"""
|
||||
|
||||
from .test_mock_config import MockConfigBuilder
|
||||
from .test_table_runner import TableTestRunner, WorkflowTestCase
|
||||
|
||||
|
||||
def test_update_conversation_variable_in_iteration():
|
||||
fixture_name = "update-conversation-variable-in-iteration"
|
||||
user_query = "ensure conversation variable syncs"
|
||||
|
||||
mock_config = (
|
||||
MockConfigBuilder()
|
||||
.with_node_output("1759032363865", {"result": [1]})
|
||||
.with_node_output("1759032476318", {"result": ""})
|
||||
.build()
|
||||
)
|
||||
|
||||
case = WorkflowTestCase(
|
||||
fixture_path=fixture_name,
|
||||
use_auto_mock=True,
|
||||
mock_config=mock_config,
|
||||
query=user_query,
|
||||
expected_outputs={"answer": user_query},
|
||||
description="Conversation variable updated within iteration should flow to answer output.",
|
||||
)
|
||||
|
||||
runner = TableTestRunner()
|
||||
result = runner.run_test_case(case)
|
||||
|
||||
assert result.success, f"Workflow execution failed: {result.error}"
|
||||
assert result.actual_outputs is not None
|
||||
assert result.actual_outputs.get("answer") == user_query
|
||||
Reference in New Issue
Block a user