Files
hive/tools/tests/test_health_checks.py
SAURABH KUMAR ea707438f2 feat(tools): add SimilarWeb V5 API integration (#7066)
Adds 29 MCP tools for SimilarWeb V5 covering traffic and engagement,
competitor intelligence, keywords/SERP, audience demographics, and
segment analysis. Includes credential spec, health checker, README,
and tests on ubuntu and windows.

Closes #7022
2026-04-26 20:37:44 +08:00

600 lines
23 KiB
Python

"""Tests for credential health checkers."""
from unittest.mock import MagicMock, patch
import httpx
from aden_tools.credentials.health_check import (
HEALTH_CHECKERS,
DiscordHealthChecker,
GitHubHealthChecker,
GoogleHealthChecker,
GoogleMapsHealthChecker,
GoogleSearchHealthChecker,
LushaHealthChecker,
ResendHealthChecker,
check_credential_health,
)
class TestHealthCheckerRegistry:
"""Tests for the HEALTH_CHECKERS registry."""
def test_google_search_registered(self):
"""GoogleSearchHealthChecker is registered in HEALTH_CHECKERS."""
assert "google_search" in HEALTH_CHECKERS
assert isinstance(HEALTH_CHECKERS["google_search"], GoogleSearchHealthChecker)
def test_github_registered(self):
"""GitHubHealthChecker is registered in HEALTH_CHECKERS."""
assert "github" in HEALTH_CHECKERS
assert isinstance(HEALTH_CHECKERS["github"], GitHubHealthChecker)
def test_resend_registered(self):
"""ResendHealthChecker is registered in HEALTH_CHECKERS."""
assert "resend" in HEALTH_CHECKERS
assert isinstance(HEALTH_CHECKERS["resend"], ResendHealthChecker)
def test_google_maps_registered(self):
"""GoogleMapsHealthChecker is registered in HEALTH_CHECKERS."""
assert "google_maps" in HEALTH_CHECKERS
assert isinstance(HEALTH_CHECKERS["google_maps"], GoogleMapsHealthChecker)
def test_google_registered(self):
"""GoogleHealthChecker is registered in HEALTH_CHECKERS under 'google'."""
assert "google" in HEALTH_CHECKERS
assert isinstance(HEALTH_CHECKERS["google"], GoogleHealthChecker)
def test_lusha_registered(self):
"""LushaHealthChecker is registered in HEALTH_CHECKERS."""
assert "lusha_api_key" in HEALTH_CHECKERS
assert isinstance(HEALTH_CHECKERS["lusha_api_key"], LushaHealthChecker)
def test_discord_registered(self):
"""DiscordHealthChecker is registered in HEALTH_CHECKERS."""
assert "discord" in HEALTH_CHECKERS
assert isinstance(HEALTH_CHECKERS["discord"], DiscordHealthChecker)
def test_all_expected_checkers_registered(self):
"""All expected health checkers are in the registry."""
expected = {
"apify",
"apollo",
"asana",
"attio",
"brave_search",
"brevo",
"calcom",
"calendly_pat",
"cloudflare",
"discord",
"docker_hub",
"exa_search",
"finlight",
"github",
"gitlab_token",
"google",
"google_maps",
"google_search",
"google_search_console",
"greenhouse_token",
"hubspot",
"huggingface",
"intercom",
"linear",
"lusha_api_key",
"microsoft_graph",
"newsdata",
"notion_token",
"pinecone",
"pipedrive",
"prometheus",
"resend",
"serpapi",
"similarweb",
"slack",
"stripe",
"telegram",
"trello_key",
"trello_token",
"vercel",
"youtube",
"zoho_crm",
}
assert set(HEALTH_CHECKERS.keys()) == expected
class TestGitHubHealthChecker:
"""Tests for GitHubHealthChecker."""
def _mock_response(self, status_code, json_data=None):
response = MagicMock(spec=httpx.Response)
response.status_code = status_code
if json_data:
response.json.return_value = json_data
return response
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_valid_token_200(self, mock_client_cls):
mock_client = MagicMock()
mock_client_cls.return_value.__enter__ = MagicMock(return_value=mock_client)
mock_client_cls.return_value.__exit__ = MagicMock(return_value=False)
mock_client.get.return_value = self._mock_response(200, {"login": "testuser"})
checker = GitHubHealthChecker()
result = checker.check("ghp_test-token")
assert result.valid is True
assert "testuser" in result.message
assert result.details["username"] == "testuser"
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_invalid_token_401(self, mock_client_cls):
mock_client = MagicMock()
mock_client_cls.return_value.__enter__ = MagicMock(return_value=mock_client)
mock_client_cls.return_value.__exit__ = MagicMock(return_value=False)
mock_client.get.return_value = self._mock_response(401)
checker = GitHubHealthChecker()
result = checker.check("invalid-token")
assert result.valid is False
assert result.details["status_code"] == 401
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_forbidden_403(self, mock_client_cls):
mock_client = MagicMock()
mock_client_cls.return_value.__enter__ = MagicMock(return_value=mock_client)
mock_client_cls.return_value.__exit__ = MagicMock(return_value=False)
mock_client.get.return_value = self._mock_response(403)
checker = GitHubHealthChecker()
result = checker.check("ghp_test-token")
assert result.valid is False
assert result.details["status_code"] == 403
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_timeout(self, mock_client_cls):
mock_client = MagicMock()
mock_client_cls.return_value.__enter__ = MagicMock(return_value=mock_client)
mock_client_cls.return_value.__exit__ = MagicMock(return_value=False)
mock_client.get.side_effect = httpx.TimeoutException("timed out")
checker = GitHubHealthChecker()
result = checker.check("ghp_test-token")
assert result.valid is False
assert result.details["error"] == "timeout"
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_request_error(self, mock_client_cls):
mock_client = MagicMock()
mock_client_cls.return_value.__enter__ = MagicMock(return_value=mock_client)
mock_client_cls.return_value.__exit__ = MagicMock(return_value=False)
mock_client.get.side_effect = httpx.RequestError("connection failed")
checker = GitHubHealthChecker()
result = checker.check("ghp_test-token")
assert result.valid is False
assert "connection failed" in result.details["error"]
class TestResendHealthChecker:
"""Tests for ResendHealthChecker."""
def _mock_response(self, status_code, json_data=None):
response = MagicMock(spec=httpx.Response)
response.status_code = status_code
if json_data:
response.json.return_value = json_data
return response
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_valid_key_200(self, mock_client_cls):
mock_client = MagicMock()
mock_client_cls.return_value.__enter__ = MagicMock(return_value=mock_client)
mock_client_cls.return_value.__exit__ = MagicMock(return_value=False)
mock_client.get.return_value = self._mock_response(200)
checker = ResendHealthChecker()
result = checker.check("re_test-key")
assert result.valid is True
assert "valid" in result.message.lower()
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_invalid_key_401(self, mock_client_cls):
mock_client = MagicMock()
mock_client_cls.return_value.__enter__ = MagicMock(return_value=mock_client)
mock_client_cls.return_value.__exit__ = MagicMock(return_value=False)
mock_client.get.return_value = self._mock_response(401)
checker = ResendHealthChecker()
result = checker.check("invalid-key")
assert result.valid is False
assert result.details["status_code"] == 401
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_forbidden_403(self, mock_client_cls):
mock_client = MagicMock()
mock_client_cls.return_value.__enter__ = MagicMock(return_value=mock_client)
mock_client_cls.return_value.__exit__ = MagicMock(return_value=False)
mock_client.get.return_value = self._mock_response(403)
checker = ResendHealthChecker()
result = checker.check("re_test-key")
assert result.valid is False
assert result.details["status_code"] == 403
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_timeout(self, mock_client_cls):
mock_client = MagicMock()
mock_client_cls.return_value.__enter__ = MagicMock(return_value=mock_client)
mock_client_cls.return_value.__exit__ = MagicMock(return_value=False)
mock_client.get.side_effect = httpx.TimeoutException("timed out")
checker = ResendHealthChecker()
result = checker.check("re_test-key")
assert result.valid is False
assert result.details["error"] == "timeout"
class TestGoogleMapsHealthChecker:
"""Tests for GoogleMapsHealthChecker."""
def _mock_response(self, status_code, json_data=None):
response = MagicMock(spec=httpx.Response)
response.status_code = status_code
if json_data:
response.json.return_value = json_data
return response
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_valid_key_ok_status(self, mock_client_cls):
mock_client = MagicMock()
mock_client_cls.return_value.__enter__ = MagicMock(return_value=mock_client)
mock_client_cls.return_value.__exit__ = MagicMock(return_value=False)
mock_client.get.return_value = self._mock_response(200, {"status": "OK", "results": []})
checker = GoogleMapsHealthChecker()
result = checker.check("test-api-key")
assert result.valid is True
assert "valid" in result.message.lower()
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_invalid_key_request_denied(self, mock_client_cls):
mock_client = MagicMock()
mock_client_cls.return_value.__enter__ = MagicMock(return_value=mock_client)
mock_client_cls.return_value.__exit__ = MagicMock(return_value=False)
mock_client.get.return_value = self._mock_response(200, {"status": "REQUEST_DENIED", "results": []})
checker = GoogleMapsHealthChecker()
result = checker.check("invalid-key")
assert result.valid is False
assert result.details["status"] == "REQUEST_DENIED"
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_quota_exceeded_still_valid(self, mock_client_cls):
mock_client = MagicMock()
mock_client_cls.return_value.__enter__ = MagicMock(return_value=mock_client)
mock_client_cls.return_value.__exit__ = MagicMock(return_value=False)
mock_client.get.return_value = self._mock_response(200, {"status": "OVER_QUERY_LIMIT", "results": []})
checker = GoogleMapsHealthChecker()
result = checker.check("test-api-key")
assert result.valid is True
assert result.details.get("rate_limited") is True
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_http_error(self, mock_client_cls):
mock_client = MagicMock()
mock_client_cls.return_value.__enter__ = MagicMock(return_value=mock_client)
mock_client_cls.return_value.__exit__ = MagicMock(return_value=False)
mock_client.get.return_value = self._mock_response(500)
checker = GoogleMapsHealthChecker()
result = checker.check("test-api-key")
assert result.valid is False
assert result.details["status_code"] == 500
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_timeout(self, mock_client_cls):
mock_client = MagicMock()
mock_client_cls.return_value.__enter__ = MagicMock(return_value=mock_client)
mock_client_cls.return_value.__exit__ = MagicMock(return_value=False)
mock_client.get.side_effect = httpx.TimeoutException("timed out")
checker = GoogleMapsHealthChecker()
result = checker.check("test-api-key")
assert result.valid is False
assert result.details["error"] == "timeout"
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_request_error(self, mock_client_cls):
mock_client = MagicMock()
mock_client_cls.return_value.__enter__ = MagicMock(return_value=mock_client)
mock_client_cls.return_value.__exit__ = MagicMock(return_value=False)
mock_client.get.side_effect = httpx.RequestError("connection failed")
checker = GoogleMapsHealthChecker()
result = checker.check("test-api-key")
assert result.valid is False
assert "connection failed" in result.details["error"]
class TestLushaHealthChecker:
"""Tests for LushaHealthChecker."""
def _mock_response(self, status_code, json_data=None):
response = MagicMock(spec=httpx.Response)
response.status_code = status_code
if json_data:
response.json.return_value = json_data
return response
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_valid_key_200(self, mock_client_cls):
mock_client = MagicMock()
mock_client_cls.return_value.__enter__ = MagicMock(return_value=mock_client)
mock_client_cls.return_value.__exit__ = MagicMock(return_value=False)
mock_client.get.return_value = self._mock_response(200)
checker = LushaHealthChecker()
result = checker.check("lusha_test_key")
assert result.valid is True
assert "valid" in result.message.lower()
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_invalid_key_401(self, mock_client_cls):
mock_client = MagicMock()
mock_client_cls.return_value.__enter__ = MagicMock(return_value=mock_client)
mock_client_cls.return_value.__exit__ = MagicMock(return_value=False)
mock_client.get.return_value = self._mock_response(401)
checker = LushaHealthChecker()
result = checker.check("invalid")
assert result.valid is False
assert result.details["status_code"] == 401
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_rate_limited_429_still_valid(self, mock_client_cls):
mock_client = MagicMock()
mock_client_cls.return_value.__enter__ = MagicMock(return_value=mock_client)
mock_client_cls.return_value.__exit__ = MagicMock(return_value=False)
mock_client.get.return_value = self._mock_response(429)
checker = LushaHealthChecker()
result = checker.check("lusha_test_key")
assert result.valid is True
assert result.details.get("rate_limited") is True
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_timeout(self, mock_client_cls):
mock_client = MagicMock()
mock_client_cls.return_value.__enter__ = MagicMock(return_value=mock_client)
mock_client_cls.return_value.__exit__ = MagicMock(return_value=False)
mock_client.get.side_effect = httpx.TimeoutException("timed out")
checker = LushaHealthChecker()
result = checker.check("lusha_test_key")
assert result.valid is False
assert result.details["error"] == "timeout"
class TestCheckCredentialHealthDispatcher:
"""Tests for the check_credential_health() top-level dispatcher."""
def test_unknown_credential_returns_valid(self):
"""Unregistered credential names are assumed valid."""
result = check_credential_health("nonexistent_service", "some-key")
assert result.valid is True
assert result.details.get("no_checker") is True
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_dispatches_to_registered_checker(self, mock_client_cls):
"""Normal dispatch calls the registered checker."""
mock_client = MagicMock()
mock_client_cls.return_value.__enter__ = MagicMock(return_value=mock_client)
mock_client_cls.return_value.__exit__ = MagicMock(return_value=False)
response = MagicMock(spec=httpx.Response)
response.status_code = 200
mock_client.get.return_value = response
result = check_credential_health("brave_search", "test-key")
assert result.valid is True
mock_client.get.assert_called_once()
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_google_search_with_cse_id(self, mock_client_cls):
"""google_search special case passes cse_id to checker."""
mock_client = MagicMock()
mock_client_cls.return_value.__enter__ = MagicMock(return_value=mock_client)
mock_client_cls.return_value.__exit__ = MagicMock(return_value=False)
response = MagicMock(spec=httpx.Response)
response.status_code = 200
mock_client.get.return_value = response
result = check_credential_health("google_search", "api-key", cse_id="cse-123")
assert result.valid is True
# Verify the request included the cse_id as the cx param
call_kwargs = mock_client.get.call_args
assert call_kwargs[1]["params"]["cx"] == "cse-123"
def test_google_search_without_cse_id(self):
"""google_search without cse_id does partial check (no HTTP call)."""
result = check_credential_health("google_search", "api-key")
assert result.valid is True
assert result.details.get("partial_check") is True
class TestGoogleHealthChecker:
"""Tests for GoogleHealthChecker (Gmail, Calendar, Sheets)."""
def _setup_mock_client(self, mock_client_cls):
mock_client = MagicMock()
mock_client_cls.return_value.__enter__ = MagicMock(return_value=mock_client)
mock_client_cls.return_value.__exit__ = MagicMock(return_value=False)
return mock_client
def _mock_response(self, status_code):
response = MagicMock(spec=httpx.Response)
response.status_code = status_code
return response
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_all_scopes_valid(self, mock_client_cls):
"""All three endpoints return 200/404 → valid."""
mock_client = self._setup_mock_client(mock_client_cls)
# Gmail 200, Calendar 200, Sheets 404 (no spreadsheet, but scope works)
mock_client.get.side_effect = [
self._mock_response(200),
self._mock_response(200),
self._mock_response(404),
]
checker = GoogleHealthChecker()
result = checker.check("test-token")
assert result.valid is True
assert "Gmail" in result.message
assert "Calendar" in result.message
assert "Sheets" in result.message
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_invalid_token_401_fails_fast(self, mock_client_cls):
"""401 on the first endpoint → token invalid, no further calls."""
mock_client = self._setup_mock_client(mock_client_cls)
mock_client.get.return_value = self._mock_response(401)
checker = GoogleHealthChecker()
result = checker.check("expired-token")
assert result.valid is False
assert result.details["status_code"] == 401
# Should fail fast — only one call made
assert mock_client.get.call_count == 1
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_missing_calendar_scope(self, mock_client_cls):
"""Gmail OK, Calendar 403, Sheets OK → reports missing calendar scope."""
mock_client = self._setup_mock_client(mock_client_cls)
mock_client.get.side_effect = [
self._mock_response(200), # gmail
self._mock_response(403), # calendar
self._mock_response(404), # sheets (404 = scope OK)
]
checker = GoogleHealthChecker()
result = checker.check("test-token")
assert result.valid is False
assert "calendar" in result.details["missing_scopes"]
assert "gmail" not in result.details["missing_scopes"]
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_missing_gmail_and_sheets_scopes(self, mock_client_cls):
"""Gmail 403, Calendar OK, Sheets 403 → reports both missing."""
mock_client = self._setup_mock_client(mock_client_cls)
mock_client.get.side_effect = [
self._mock_response(403), # gmail
self._mock_response(200), # calendar
self._mock_response(403), # sheets
]
checker = GoogleHealthChecker()
result = checker.check("test-token")
assert result.valid is False
assert "gmail" in result.details["missing_scopes"]
assert "sheets" in result.details["missing_scopes"]
assert len(result.details["missing_scopes"]) == 2
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_sheets_404_is_success(self, mock_client_cls):
"""Sheets returns 404 for non-existent spreadsheet — that's valid."""
mock_client = self._setup_mock_client(mock_client_cls)
mock_client.get.side_effect = [
self._mock_response(200),
self._mock_response(200),
self._mock_response(404),
]
checker = GoogleHealthChecker()
result = checker.check("test-token")
assert result.valid is True
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_unexpected_status_code(self, mock_client_cls):
"""500 on any endpoint → reports failure with scope name."""
mock_client = self._setup_mock_client(mock_client_cls)
mock_client.get.side_effect = [
self._mock_response(200), # gmail
self._mock_response(500), # calendar
]
checker = GoogleHealthChecker()
result = checker.check("test-token")
assert result.valid is False
assert result.details["status_code"] == 500
assert result.details["scope"] == "calendar"
@patch("aden_tools.credentials.health_check.httpx.Client")
def test_timeout(self, mock_client_cls):
mock_client = self._setup_mock_client(mock_client_cls)
mock_client.get.side_effect = httpx.TimeoutException("timed out")
checker = GoogleHealthChecker()
result = checker.check("test-token")
assert result.valid is False
assert result.details["error"] == "timeout"
def test_request_error_with_bearer_token_sanitized(self):
"""Sanitizes Bearer tokens in error messages."""
checker = GoogleHealthChecker()
with patch("aden_tools.credentials.health_check.httpx.Client") as mock_client_cls:
mock_client = self._setup_mock_client(mock_client_cls)
mock_client.get.side_effect = httpx.RequestError("Connection failed with Bearer ya29.secret-token-here")
result = checker.check("ya29.secret-token-here")
assert not result.valid
assert "Bearer" not in result.message
assert "ya29" not in result.message
assert "redacted" in result.message
def test_request_error_without_sensitive_data_passes_through(self):
"""Non-sensitive error messages pass through unchanged."""
checker = GoogleHealthChecker()
with patch("aden_tools.credentials.health_check.httpx.Client") as mock_client_cls:
mock_client = self._setup_mock_client(mock_client_cls)
mock_client.get.side_effect = httpx.RequestError("Connection refused")
result = checker.check("token123")
assert not result.valid
assert "Connection refused" in result.message