diff --git a/hermes_cli/gateway.py b/hermes_cli/gateway.py index ccac2220a6b9..e3fa8d6339b7 100644 --- a/hermes_cli/gateway.py +++ b/hermes_cli/gateway.py @@ -5814,17 +5814,21 @@ def _builtin_setup_fn(key: str): from hermes_cli import setup as _s return { - # telegram moved into the plugin: setup_fn registered by - # plugins/platforms/telegram/adapter.py::register(). #41112. # discord moved into the plugin: setup_fn is registered by # plugins/platforms/discord/adapter.py::register() and dispatched # via the plugin path in _configure_platform(). - # slack moved into the plugin: setup_fn is registered by - # plugins/platforms/slack/adapter.py::register() and dispatched - # via the plugin path in _configure_platform(). #41112. - # matrix moved into the plugin: setup_fn registered by - # plugins/platforms/matrix/adapter.py::register() and dispatched via - # the plugin path in _configure_platform(). #41112. + # + # telegram, slack, and matrix are built-in (not plugin-migrated — + # see the _PLATFORMS NOTE above). They keep their bespoke setup + # flows here because the generic _setup_standard_platform fallback + # treats the first `vars` entry (token_var) as mandatory and aborts + # the whole wizard if it's left empty. That breaks Matrix's + # documented "leave the access token empty for password login" + # path outright, and loses Telegram's token-format validation and + # Slack's manifest-regeneration prompt. + "telegram": _s._setup_telegram, + "slack": _s._setup_slack, + "matrix": _s._setup_matrix, # mattermost moved into the plugin: setup_fn is registered by # plugins/platforms/mattermost/adapter.py::register() and dispatched # via the plugin path in _configure_platform(). diff --git a/tests/hermes_cli/test_gateway_platform_gating.py b/tests/hermes_cli/test_gateway_platform_gating.py index c16875687ce4..ebf648d03a61 100644 --- a/tests/hermes_cli/test_gateway_platform_gating.py +++ b/tests/hermes_cli/test_gateway_platform_gating.py @@ -15,6 +15,67 @@ import sys +# Every built-in adapter under gateway/platforms/ that is expected to have +# a picker entry today. This intentionally excludes dingtalk/feishu/wecom/ +# wecom_callback — those were dropped by the same e39b468 refactor but are +# tracked and restored separately (see .plans/missing-platform-plugin-shims.md +# and the PR that follows it); asserting them here would make this test +# depend on merge order between unrelated PRs. +BUILTIN_ADAPTER_KEYS = frozenset( + { + "telegram", + "slack", + "matrix", + "whatsapp", + "email", + "sms", + "mattermost", + "signal", + "weixin", + "bluebubbles", + "qqbot", + "yuanbao", + } +) + + +class TestBuiltinAdaptersStayInPicker: + def test_every_builtin_adapter_key_is_in_all_platforms(self, monkeypatch): + """Regression guard for e39b468-style silent deletions. + + A future refactor that deletes a _PLATFORMS entry (or claims a + built-in "moved to plugins" without actually creating the plugin) + should fail CI here instead of shipping a picker that's silently + missing a platform. + """ + import hermes_cli.gateway as gateway_mod + + monkeypatch.setattr(gateway_mod.sys, "platform", "linux") + keys = {p["key"] for p in gateway_mod._all_platforms()} + missing = BUILTIN_ADAPTER_KEYS - keys + assert not missing, ( + f"Built-in adapter(s) {sorted(missing)} have no _all_platforms() " + "entry — they exist in gateway/platforms/ but are invisible to " + "`hermes setup gateway`." + ) + + def test_telegram_slack_matrix_use_bespoke_setup_fn(self, monkeypatch): + """Matrix's "leave the token empty for password login" path is real. + + The generic _setup_standard_platform() fallback treats the first + `vars` entry (token_var) as mandatory and aborts the whole wizard + if left empty — which would silently break Matrix's documented + password-login path. These three must resolve to their bespoke + hermes_cli.setup functions, not fall through to the generic flow. + """ + import hermes_cli.gateway as gateway_mod + from hermes_cli import setup as _s + + assert gateway_mod._builtin_setup_fn("telegram") is _s._setup_telegram + assert gateway_mod._builtin_setup_fn("slack") is _s._setup_slack + assert gateway_mod._builtin_setup_fn("matrix") is _s._setup_matrix + + class TestMatrixHiddenOnWindows: def test_matrix_present_on_linux(self, monkeypatch): """Sanity: matrix is still in the picker on Linux/macOS."""