mirror of
https://gitee.com/wanwujie/deer-flow
synced 2026-04-28 08:14:48 +08:00
test:unit tests for configuration (#291)
* test:unit tests for configuration * test: update the test_configuration.py file * test: reformate the test codes
This commit is contained in:
@@ -12,7 +12,7 @@ def replace_env_vars(value: str) -> str:
|
|||||||
return value
|
return value
|
||||||
if value.startswith("$"):
|
if value.startswith("$"):
|
||||||
env_var = value[1:]
|
env_var = value[1:]
|
||||||
return os.getenv(env_var, value)
|
return os.getenv(env_var, env_var)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
|||||||
95
tests/unit/config/test_configuration.py
Normal file
95
tests/unit/config/test_configuration.py
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import os
|
||||||
|
import pytest
|
||||||
|
import sys
|
||||||
|
import types
|
||||||
|
from pathlib import Path
|
||||||
|
import builtins
|
||||||
|
import importlib
|
||||||
|
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):
|
||||||
|
config_dict = {
|
||||||
|
"configurable": {
|
||||||
|
"max_plan_iterations": None,
|
||||||
|
"max_step_num": 0, # falsy, should be skipped
|
||||||
|
"max_search_results": "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config = Configuration.from_runnable_config(config_dict)
|
||||||
|
# Should fall back to defaults for skipped/falsy values
|
||||||
|
assert config.max_plan_iterations == 1
|
||||||
|
assert config.max_step_num == 3
|
||||||
|
assert config.max_search_results == 3
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
83
tests/unit/config/test_loader.py
Normal file
83
tests/unit/config/test_loader.py
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
import yaml
|
||||||
|
import pytest
|
||||||
|
from src.config.loader import load_yaml_config, process_dict, replace_env_vars
|
||||||
|
|
||||||
|
|
||||||
|
def test_replace_env_vars_with_env(monkeypatch):
|
||||||
|
monkeypatch.setenv("TEST_ENV", "env_value")
|
||||||
|
assert replace_env_vars("$TEST_ENV") == "env_value"
|
||||||
|
|
||||||
|
|
||||||
|
def test_replace_env_vars_without_env(monkeypatch):
|
||||||
|
monkeypatch.delenv("NOT_SET_ENV", raising=False)
|
||||||
|
assert replace_env_vars("$NOT_SET_ENV") == "NOT_SET_ENV"
|
||||||
|
|
||||||
|
|
||||||
|
def test_replace_env_vars_non_string():
|
||||||
|
assert replace_env_vars(123) == 123
|
||||||
|
|
||||||
|
|
||||||
|
def test_replace_env_vars_regular_string():
|
||||||
|
assert replace_env_vars("no_env") == "no_env"
|
||||||
|
|
||||||
|
|
||||||
|
def test_process_dict_nested(monkeypatch):
|
||||||
|
monkeypatch.setenv("FOO", "bar")
|
||||||
|
config = {"a": "$FOO", "b": {"c": "$FOO", "d": 42, "e": "$NOT_SET_ENV"}}
|
||||||
|
processed = process_dict(config)
|
||||||
|
assert processed["a"] == "bar"
|
||||||
|
assert processed["b"]["c"] == "bar"
|
||||||
|
assert processed["b"]["d"] == 42
|
||||||
|
assert processed["b"]["e"] == "NOT_SET_ENV"
|
||||||
|
|
||||||
|
|
||||||
|
def test_process_dict_empty():
|
||||||
|
assert process_dict({}) == {}
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_yaml_config_file_not_exist():
|
||||||
|
assert load_yaml_config("non_existent_file.yaml") == {}
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_yaml_config(monkeypatch):
|
||||||
|
monkeypatch.setenv("MY_ENV", "my_value")
|
||||||
|
yaml_content = """
|
||||||
|
key1: value1
|
||||||
|
key2: $MY_ENV
|
||||||
|
nested:
|
||||||
|
key3: $MY_ENV
|
||||||
|
key4: 123
|
||||||
|
"""
|
||||||
|
with tempfile.NamedTemporaryFile("w+", delete=False) as tmp:
|
||||||
|
tmp.write(yaml_content)
|
||||||
|
tmp_path = tmp.name
|
||||||
|
|
||||||
|
try:
|
||||||
|
config = load_yaml_config(tmp_path)
|
||||||
|
assert config["key1"] == "value1"
|
||||||
|
assert config["key2"] == "my_value"
|
||||||
|
assert config["nested"]["key3"] == "my_value"
|
||||||
|
assert config["nested"]["key4"] == 123
|
||||||
|
finally:
|
||||||
|
os.remove(tmp_path)
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_yaml_config_cache(monkeypatch):
|
||||||
|
monkeypatch.setenv("CACHE_ENV", "cache_value")
|
||||||
|
yaml_content = "foo: $CACHE_ENV"
|
||||||
|
with tempfile.NamedTemporaryFile("w+", delete=False) as tmp:
|
||||||
|
tmp.write(yaml_content)
|
||||||
|
tmp_path = tmp.name
|
||||||
|
|
||||||
|
try:
|
||||||
|
config1 = load_yaml_config(tmp_path)
|
||||||
|
config2 = load_yaml_config(tmp_path)
|
||||||
|
assert config1 is config2 # Should be cached (same object)
|
||||||
|
assert config1["foo"] == "cache_value"
|
||||||
|
finally:
|
||||||
|
os.remove(tmp_path)
|
||||||
Reference in New Issue
Block a user