From 8ea389a7f815c0e37ea382c55a38cf5e4e640ca5 Mon Sep 17 00:00:00 2001 From: Yukipukii1 Date: Sat, 25 Apr 2026 00:00:54 +0300 Subject: [PATCH] fix(gateway/config): coerce quoted boolean values in config parsing --- gateway/config.py | 6 ++-- tests/gateway/test_config.py | 61 ++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/gateway/config.py b/gateway/config.py index a156e79c5..509737279 100644 --- a/gateway/config.py +++ b/gateway/config.py @@ -135,7 +135,7 @@ class SessionResetPolicy: mode=mode if mode is not None else "both", at_hour=at_hour if at_hour is not None else 4, 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"), ) @@ -178,7 +178,7 @@ class PlatformConfig: home_channel = HomeChannel.from_dict(data["home_channel"]) return cls( - enabled=data.get("enabled", False), + enabled=_coerce_bool(data.get("enabled"), False), token=data.get("token"), api_key=data.get("api_key"), home_channel=home_channel, @@ -435,7 +435,7 @@ class GatewayConfig: reset_triggers=data.get("reset_triggers", ["/new", "/reset"]), quick_commands=quick_commands, 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), group_sessions_per_user=_coerce_bool(group_sessions_per_user, True), thread_sessions_per_user=_coerce_bool(thread_sessions_per_user, False), diff --git a/tests/gateway/test_config.py b/tests/gateway/test_config.py index 41a7a49fe..9e82a5da7 100644 --- a/tests/gateway/test_config.py +++ b/tests/gateway/test_config.py @@ -52,6 +52,10 @@ class TestPlatformConfigRoundtrip: assert restored.enabled is False 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: def test_returns_enabled_with_token(self): @@ -140,6 +144,10 @@ class TestSessionResetPolicy: assert restored.at_hour == 4 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: def test_full_roundtrip(self): @@ -182,6 +190,10 @@ class TestGatewayConfigRoundtrip: assert restored.unauthorized_dm_behavior == "ignore" 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: 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 + 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): hermes_home = tmp_path / ".hermes" hermes_home.mkdir()