Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion agent_reach/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,13 @@ def load(self):
self.data = {}
return

loaded = yaml.safe_load(payload) or {}
loaded = yaml.safe_load(payload)
if loaded is None:
loaded = {}
if not isinstance(loaded, dict):
raise ConfigError("配置文件顶层必须是对象")
if not all(isinstance(key, str) for key in loaded):
raise ConfigError("配置文件的键必须是字符串")
self.data = loaded

def save(self):
Expand Down
30 changes: 30 additions & 0 deletions tests/test_config_shape.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""Malformed config shapes fail before they can be mistaken for empty settings."""

import pytest

from agent_reach.config import Config, ConfigError


@pytest.mark.parametrize("payload", ["[]", "false", "0", "''"])
def test_falsy_non_mapping_config_is_rejected_without_changing_file(tmp_path, payload):
path = tmp_path / "config.yaml"
path.write_text(payload, encoding="utf-8")
with pytest.raises(ConfigError):
Config(path)
assert path.read_text(encoding="utf-8") == payload


@pytest.mark.parametrize("payload", ["1: value", "true: value", "null: value"])
def test_non_string_config_keys_are_rejected_at_load(tmp_path, payload):
path = tmp_path / "config.yaml"
path.write_text(payload, encoding="utf-8")
with pytest.raises(ConfigError):
Config(path)
assert path.read_text(encoding="utf-8") == payload


@pytest.mark.parametrize("payload", ["", "# only a comment\n", "null", "{}"])
def test_empty_config_still_loads_as_an_empty_mapping(tmp_path, payload):
path = tmp_path / "config.yaml"
path.write_text(payload, encoding="utf-8")
assert Config(path).data == {}