Files
deer-flow/tests/unit/config/test_configuration.py
Xun 6ec170cde5 fix: handle false values correctly in (#823)
Fixes a critical bug in the from_runnable_config() method where falsy values (like False, 0, and empty strings) were being incorrectly filtered out, causing configuration fields to revert to their default values. The fix changes the filter condition from if v to if v is not None, ensuring only None values are skipped.
2026-01-21 09:33:20 +08:00

183 lines
6.1 KiB
Python

# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
# SPDX-License-Identifier: MIT
import sys
import types
from src.config.configuration import Configuration
# Patch sys.path so relative import works
# Patch Resource for import
mock_resource = type("Resource", (), {})
# Patch src.rag.retriever.Resource for import
module_name = "src.rag.retriever"
if module_name not in sys.modules:
retriever_mod = types.ModuleType(module_name)
retriever_mod.Resource = mock_resource
sys.modules[module_name] = retriever_mod
# Relative import of Configuration
def test_default_configuration():
config = Configuration()
assert config.resources == []
assert config.max_plan_iterations == 1
assert config.max_step_num == 3
assert config.max_search_results == 3
assert config.mcp_settings is None
def test_from_runnable_config_with_config_dict(monkeypatch):
config_dict = {
"configurable": {
"max_plan_iterations": 5,
"max_step_num": 7,
"max_search_results": 10,
"mcp_settings": {"foo": "bar"},
}
}
config = Configuration.from_runnable_config(config_dict)
assert config.max_plan_iterations == 5
assert config.max_step_num == 7
assert config.max_search_results == 10
assert config.mcp_settings == {"foo": "bar"}
def test_from_runnable_config_with_env_override(monkeypatch):
monkeypatch.setenv("MAX_PLAN_ITERATIONS", "9")
monkeypatch.setenv("MAX_STEP_NUM", "11")
config_dict = {
"configurable": {
"max_plan_iterations": 2,
"max_step_num": 3,
"max_search_results": 4,
}
}
config = Configuration.from_runnable_config(config_dict)
# Environment variables take precedence and are strings
assert config.max_plan_iterations == "9"
assert config.max_step_num == "11"
assert config.max_search_results == 4 # not overridden
# Clean up
monkeypatch.delenv("MAX_PLAN_ITERATIONS")
monkeypatch.delenv("MAX_STEP_NUM")
def test_from_runnable_config_with_none_and_falsy(monkeypatch):
"""Test that None values are skipped but falsy values (0, False, empty string) are preserved."""
config_dict = {
"configurable": {
"max_plan_iterations": None, # None should be skipped, use default
"max_step_num": 0, # 0 is valid, should be preserved
"max_search_results": "", # Empty string should be preserved
}
}
config = Configuration.from_runnable_config(config_dict)
# None values should fall back to defaults
assert config.max_plan_iterations == 1
# Falsy but valid values should be preserved
assert config.max_step_num == 0
assert config.max_search_results == ""
def test_from_runnable_config_with_no_config():
config = Configuration.from_runnable_config()
assert config.max_plan_iterations == 1
assert config.max_step_num == 3
assert config.max_search_results == 3
assert config.resources == []
assert config.mcp_settings is None
def test_from_runnable_config_with_boolean_false_values():
"""Test that boolean False values are correctly preserved and not filtered out.
This is a regression test for the bug where False values were treated as falsy
and filtered out, causing fields to revert to their default values.
"""
config_dict = {
"configurable": {
"enable_web_search": False, # Should be preserved as False, not revert to True
"enable_deep_thinking": False, # Should be preserved as False
"enforce_web_search": False, # Should be preserved as False
"enforce_researcher_search": False, # Should be preserved as False
"max_plan_iterations": 5, # Control: non-falsy value
}
}
config = Configuration.from_runnable_config(config_dict)
# Assert that False values are preserved
assert config.enable_web_search is False, "enable_web_search should be False, not default True"
assert config.enable_deep_thinking is False, "enable_deep_thinking should be False"
assert config.enforce_web_search is False, "enforce_web_search should be False"
assert config.enforce_researcher_search is False, "enforce_researcher_search should be False, not default True"
# Control: verify non-falsy values still work
assert config.max_plan_iterations == 5
def test_from_runnable_config_with_boolean_true_values():
"""Test that boolean True values work correctly (control test)."""
config_dict = {
"configurable": {
"enable_web_search": True,
"enable_deep_thinking": True,
"enforce_web_search": True,
}
}
config = Configuration.from_runnable_config(config_dict)
assert config.enable_web_search is True
assert config.enable_deep_thinking is True
assert config.enforce_web_search is True
def test_get_recursion_limit_default():
from src.config.configuration import get_recursion_limit
result = get_recursion_limit()
assert result == 25
def test_get_recursion_limit_custom_default():
from src.config.configuration import get_recursion_limit
result = get_recursion_limit(50)
assert result == 50
def test_get_recursion_limit_from_env(monkeypatch):
from src.config.configuration import get_recursion_limit
monkeypatch.setenv("AGENT_RECURSION_LIMIT", "100")
result = get_recursion_limit()
assert result == 100
def test_get_recursion_limit_invalid_env_value(monkeypatch):
from src.config.configuration import get_recursion_limit
monkeypatch.setenv("AGENT_RECURSION_LIMIT", "invalid")
result = get_recursion_limit()
assert result == 25
def test_get_recursion_limit_negative_env_value(monkeypatch):
from src.config.configuration import get_recursion_limit
monkeypatch.setenv("AGENT_RECURSION_LIMIT", "-5")
result = get_recursion_limit()
assert result == 25
def test_get_recursion_limit_zero_env_value(monkeypatch):
from src.config.configuration import get_recursion_limit
monkeypatch.setenv("AGENT_RECURSION_LIMIT", "0")
result = get_recursion_limit()
assert result == 25