/ tests / gateway / test_platform_connected_checkers.py
test_platform_connected_checkers.py
 1  """
 2  Verify that every gateway platform — built-in and plugin — has a connection
 3  checker so ``GatewayConfig.get_connected_platforms()`` doesn't silently drop
 4  platforms with bespoke auth requirements.
 5  """
 6  
 7  from unittest.mock import MagicMock
 8  
 9  import pytest
10  
11  from gateway.config import Platform, _PLATFORM_CONNECTED_CHECKERS, _BUILTIN_PLATFORM_VALUES
12  
13  
14  def test_all_builtins_have_checker_or_generic_token_path():
15      """Every built-in Platform member must be reachable by either:
16  
17      1. The generic ``config.token or config.api_key`` check, OR
18      2. A platform-specific entry in ``_PLATFORM_CONNECTED_CHECKERS``.
19  
20      This guarantees ``get_connected_platforms()`` doesn't silently ignore
21      a built-in just because nobody added it to the checker dict.
22      """
23      # Platforms covered by the generic token/api_key branch
24      generic_token_values = {p.value for p in {
25          Platform.TELEGRAM,
26          Platform.DISCORD,
27          Platform.SLACK,
28          Platform.MATRIX,
29          Platform.MATTERMOST,
30          Platform.HOMEASSISTANT,
31      }}
32  
33      # Platforms with a bespoke checker
34      checker_values = {p.value for p in set(_PLATFORM_CONNECTED_CHECKERS.keys())}
35  
36      # Every built-in should be in one of the two sets
37      all_builtins = set(_BUILTIN_PLATFORM_VALUES)
38      missing = all_builtins - generic_token_values - checker_values - {"local"}
39  
40      assert not missing, (
41          f"Built-in platforms missing a connection checker: "
42          f"{sorted(missing)}.  "
43          f"Add them to _PLATFORM_CONNECTED_CHECKERS or generic_token_platforms."
44      )
45  
46  
47  @pytest.mark.parametrize("platform, checker", list(_PLATFORM_CONNECTED_CHECKERS.items()))
48  def test_checker_handles_minimal_config(platform, checker):
49      """Each bespoke checker must not crash on a minimal PlatformConfig."""
50      mock_config = MagicMock()
51      mock_config.extra = {}
52      mock_config.token = None
53      mock_config.api_key = None
54      mock_config.enabled = True
55  
56      # Should return a bool without raising
57      result = checker(mock_config)
58      assert isinstance(result, bool)
59  
60  
61  @pytest.mark.parametrize("platform, checker", list(_PLATFORM_CONNECTED_CHECKERS.items()))
62  def test_checker_returns_true_when_configured(platform, checker, monkeypatch):
63      """Each bespoke checker must return True when the config looks valid."""
64      mock_config = MagicMock()
65      mock_config.token = None
66      mock_config.api_key = None
67      mock_config.enabled = True
68  
69      # Set up platform-specific mock extra fields so the checker succeeds
70      if platform == Platform.WEIXIN:
71          mock_config.extra = {"account_id": "123", "token": "***"}
72      elif platform == Platform.SIGNAL:
73          mock_config.extra = {"http_url": "http://signal:8080"}
74      elif platform == Platform.EMAIL:
75          mock_config.extra = {"address": "hermes@example.com"}
76      elif platform == Platform.SMS:
77          monkeypatch.setenv("TWILIO_ACCOUNT_SID", "ACtest")
78          mock_config.extra = {}
79      elif platform in (Platform.API_SERVER, Platform.WEBHOOK, Platform.WHATSAPP):
80          mock_config.extra = {}
81      elif platform == Platform.FEISHU:
82          mock_config.extra = {"app_id": "app"}
83      elif platform == Platform.WECOM:
84          mock_config.extra = {"bot_id": "bot"}
85      elif platform == Platform.WECOM_CALLBACK:
86          mock_config.extra = {"corp_id": "corp"}
87      elif platform == Platform.BLUEBUBBLES:
88          mock_config.extra = {"server_url": "http://bb:1234", "password": "pw"}
89      elif platform == Platform.QQBOT:
90          mock_config.extra = {"app_id": "app", "client_secret": "sec"}
91      elif platform == Platform.YUANBAO:
92          mock_config.extra = {"app_id": "app", "app_secret": "sec"}
93      elif platform == Platform.DINGTALK:
94          mock_config.extra = {"client_id": "id", "client_secret": "sec"}
95      else:
96          pytest.skip(f"No synthetic config defined for {platform.value}")
97  
98      result = checker(mock_config)
99      assert result is True, f"{platform.value} checker should return True with valid-looking config"