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"