fix(gateway/config): coerce quoted boolean values in config parsing

This commit is contained in:
Yukipukii1 2026-04-25 00:00:54 +03:00 committed by Teknium
parent 3e6c108565
commit 8ea389a7f8
2 changed files with 64 additions and 3 deletions

View file

@ -135,7 +135,7 @@ class SessionResetPolicy:
mode=mode if mode is not None else "both", mode=mode if mode is not None else "both",
at_hour=at_hour if at_hour is not None else 4, at_hour=at_hour if at_hour is not None else 4,
idle_minutes=idle_minutes if idle_minutes is not None else 1440, idle_minutes=idle_minutes if idle_minutes is not None else 1440,
notify=notify if notify is not None else True, notify=_coerce_bool(notify, True),
notify_exclude_platforms=tuple(exclude) if exclude is not None else ("api_server", "webhook"), notify_exclude_platforms=tuple(exclude) if exclude is not None else ("api_server", "webhook"),
) )
@ -178,7 +178,7 @@ class PlatformConfig:
home_channel = HomeChannel.from_dict(data["home_channel"]) home_channel = HomeChannel.from_dict(data["home_channel"])
return cls( return cls(
enabled=data.get("enabled", False), enabled=_coerce_bool(data.get("enabled"), False),
token=data.get("token"), token=data.get("token"),
api_key=data.get("api_key"), api_key=data.get("api_key"),
home_channel=home_channel, home_channel=home_channel,
@ -435,7 +435,7 @@ class GatewayConfig:
reset_triggers=data.get("reset_triggers", ["/new", "/reset"]), reset_triggers=data.get("reset_triggers", ["/new", "/reset"]),
quick_commands=quick_commands, quick_commands=quick_commands,
sessions_dir=sessions_dir, sessions_dir=sessions_dir,
always_log_local=data.get("always_log_local", True), always_log_local=_coerce_bool(data.get("always_log_local"), True),
stt_enabled=_coerce_bool(stt_enabled, True), stt_enabled=_coerce_bool(stt_enabled, True),
group_sessions_per_user=_coerce_bool(group_sessions_per_user, True), group_sessions_per_user=_coerce_bool(group_sessions_per_user, True),
thread_sessions_per_user=_coerce_bool(thread_sessions_per_user, False), thread_sessions_per_user=_coerce_bool(thread_sessions_per_user, False),

View file

@ -52,6 +52,10 @@ class TestPlatformConfigRoundtrip:
assert restored.enabled is False assert restored.enabled is False
assert restored.token is None assert restored.token is None
def test_from_dict_coerces_quoted_false_enabled(self):
restored = PlatformConfig.from_dict({"enabled": "false"})
assert restored.enabled is False
class TestGetConnectedPlatforms: class TestGetConnectedPlatforms:
def test_returns_enabled_with_token(self): def test_returns_enabled_with_token(self):
@ -140,6 +144,10 @@ class TestSessionResetPolicy:
assert restored.at_hour == 4 assert restored.at_hour == 4
assert restored.idle_minutes == 1440 assert restored.idle_minutes == 1440
def test_from_dict_coerces_quoted_false_notify(self):
restored = SessionResetPolicy.from_dict({"notify": "false"})
assert restored.notify is False
class TestGatewayConfigRoundtrip: class TestGatewayConfigRoundtrip:
def test_full_roundtrip(self): def test_full_roundtrip(self):
@ -182,6 +190,10 @@ class TestGatewayConfigRoundtrip:
assert restored.unauthorized_dm_behavior == "ignore" assert restored.unauthorized_dm_behavior == "ignore"
assert restored.platforms[Platform.WHATSAPP].extra["unauthorized_dm_behavior"] == "pair" assert restored.platforms[Platform.WHATSAPP].extra["unauthorized_dm_behavior"] == "pair"
def test_from_dict_coerces_quoted_false_always_log_local(self):
restored = GatewayConfig.from_dict({"always_log_local": "false"})
assert restored.always_log_local is False
class TestLoadGatewayConfig: class TestLoadGatewayConfig:
def test_bridges_quick_commands_from_config_yaml(self, tmp_path, monkeypatch): def test_bridges_quick_commands_from_config_yaml(self, tmp_path, monkeypatch):
@ -238,6 +250,55 @@ class TestLoadGatewayConfig:
assert config.thread_sessions_per_user is False assert config.thread_sessions_per_user is False
def test_bridges_quoted_false_platform_enabled_from_config_yaml(self, tmp_path, monkeypatch):
hermes_home = tmp_path / ".hermes"
hermes_home.mkdir()
config_path = hermes_home / "config.yaml"
config_path.write_text(
"platforms:\n"
" api_server:\n"
" enabled: \"false\"\n",
encoding="utf-8",
)
monkeypatch.setenv("HERMES_HOME", str(hermes_home))
config = load_gateway_config()
assert config.platforms[Platform.API_SERVER].enabled is False
assert Platform.API_SERVER not in config.get_connected_platforms()
def test_bridges_quoted_false_session_notify_from_config_yaml(self, tmp_path, monkeypatch):
hermes_home = tmp_path / ".hermes"
hermes_home.mkdir()
config_path = hermes_home / "config.yaml"
config_path.write_text(
"session_reset:\n"
" notify: \"false\"\n",
encoding="utf-8",
)
monkeypatch.setenv("HERMES_HOME", str(hermes_home))
config = load_gateway_config()
assert config.default_reset_policy.notify is False
def test_bridges_quoted_false_always_log_local_from_config_yaml(self, tmp_path, monkeypatch):
hermes_home = tmp_path / ".hermes"
hermes_home.mkdir()
config_path = hermes_home / "config.yaml"
config_path.write_text(
"always_log_local: \"false\"\n",
encoding="utf-8",
)
monkeypatch.setenv("HERMES_HOME", str(hermes_home))
config = load_gateway_config()
assert config.always_log_local is False
def test_bridges_discord_channel_prompts_from_config_yaml(self, tmp_path, monkeypatch): def test_bridges_discord_channel_prompts_from_config_yaml(self, tmp_path, monkeypatch):
hermes_home = tmp_path / ".hermes" hermes_home = tmp_path / ".hermes"
hermes_home.mkdir() hermes_home.mkdir()