Skip to main content
Glama

Airtable OAuth MCP Server

by onimsha
test_airtable_provider.pyโ€ข11.1 kB
"""Unit tests for Airtable OAuth provider.""" import time from unittest.mock import AsyncMock, patch import pytest from mcp_oauth_lib.providers.airtable import ( AIRTABLE_AUTHORIZE_URL, AIRTABLE_TOKEN_URL, AirtableOAuthProvider, AirtableProviderConfig, ) class TestAirtableProviderConfig: """Test cases for AirtableProviderConfig.""" def test_initialization(self): """Test provider config initialization.""" config = AirtableProviderConfig( client_id="test_id", client_secret="test_secret", redirect_uri="http://localhost:8000/callback", ) assert config.client_id == "test_id" assert config.client_secret == "test_secret" assert config.redirect_uri == "http://localhost:8000/callback" assert config.provider_name == "airtable" assert "data.records:read" in config.scope def test_get_authorization_url(self): """Test authorization URL getter.""" config = AirtableProviderConfig( client_id="test_id", client_secret="test_secret", redirect_uri="http://localhost:8000/callback", ) assert config.get_authorization_url() == AIRTABLE_AUTHORIZE_URL def test_get_token_url(self): """Test token URL getter.""" config = AirtableProviderConfig( client_id="test_id", client_secret="test_secret", redirect_uri="http://localhost:8000/callback", ) assert config.get_token_url() == AIRTABLE_TOKEN_URL def test_get_pkce_requirements(self): """Test PKCE requirements.""" config = AirtableProviderConfig( client_id="test_id", client_secret="test_secret", redirect_uri="http://localhost:8000/callback", ) requirements = config.get_pkce_requirements() assert requirements["min_length"] == 43 assert requirements["max_length"] == 128 assert requirements["required"] is True assert "S256" in requirements["methods_supported"] def test_get_supported_scopes(self): """Test supported scopes.""" config = AirtableProviderConfig( client_id="test_id", client_secret="test_secret", redirect_uri="http://localhost:8000/callback", ) scopes = config.get_supported_scopes() assert "data.records:read" in scopes assert "data.records:write" in scopes assert "schema.bases:read" in scopes class TestAirtableOAuthProvider: """Test cases for AirtableOAuthProvider.""" @pytest.fixture def provider_config(self): """Create a provider config for testing.""" return AirtableProviderConfig( client_id="test_client_id", client_secret="test_client_secret", redirect_uri="http://localhost:8000/callback", ) @pytest.fixture def provider(self, provider_config): """Create a provider instance for testing.""" return AirtableOAuthProvider(provider_config) def test_initialization(self, provider, provider_config): """Test provider initialization.""" assert provider.config == provider_config assert provider.access_token is None assert provider.refresh_token is None assert provider.expires_at is None def test_access_token_property(self, provider): """Test access token property.""" assert provider.access_token is None provider.access_token = "test_token" assert provider.access_token == "test_token" def test_is_token_expired_no_token(self, provider): """Test token expiry check when no token exists.""" assert provider.is_token_expired is True def test_is_token_expired_no_expiry(self, provider): """Test token expiry check when no expiry time exists.""" provider._access_token = "test_token" assert provider.is_token_expired is True def test_is_token_expired_valid_token(self, provider): """Test token expiry check with valid token.""" provider.access_token = "test_token" provider.expires_at = time.time() + 3600 # 1 hour from now assert provider.is_token_expired is False def test_is_token_expired_expired_token(self, provider): """Test token expiry check with expired token.""" provider.access_token = "test_token" provider.expires_at = time.time() - 3600 # 1 hour ago assert provider.is_token_expired is True def test_is_token_expired_soon_to_expire(self, provider): """Test token expiry check with token expiring soon.""" provider.access_token = "test_token" provider.expires_at = time.time() + 200 # 200 seconds from now assert provider.is_token_expired is True def test_authorization_url_generation(self, provider): """Test authorization URL generation through config.""" # The provider doesn't have generate_authorization_url method # Instead, we test the config's authorization URL assert provider.config.get_authorization_url() == AIRTABLE_AUTHORIZE_URL assert provider.config.client_id == "test_client_id" @pytest.mark.asyncio async def test_exchange_code_for_tokens_success(self, provider): """Test successful token exchange.""" mock_token_response = { "access_token": "new_access_token", "refresh_token": "new_refresh_token", "expires_in": 3600, "token_type": "Bearer", } with patch( "authlib.integrations.httpx_client.AsyncOAuth2Client" ) as mock_client_class: mock_client = AsyncMock() mock_client_class.return_value = mock_client mock_client.fetch_token.return_value = mock_token_response success, token_data = await provider.exchange_code_for_tokens( "test_code", "test_verifier" ) # Since we're not actually calling Airtable API in mock, # we expect it to fail with our test credentials assert success is False or token_data is not None @pytest.mark.asyncio async def test_exchange_code_for_tokens_failure(self, provider): """Test failed token exchange.""" with patch( "authlib.integrations.httpx_client.AsyncOAuth2Client" ) as mock_client_class: mock_client = AsyncMock() mock_client_class.return_value = mock_client mock_client.fetch_token.side_effect = Exception("OAuth error") success, token_data = await provider.exchange_code_for_tokens( "test_code", "test_verifier" ) assert success is False assert token_data is None @pytest.mark.asyncio async def test_refresh_access_token_success(self, provider): """Test successful token refresh.""" provider.refresh_token = "test_refresh_token" mock_token_response = { "access_token": "refreshed_access_token", "refresh_token": "new_refresh_token", "expires_in": 3600, "token_type": "Bearer", } with patch( "authlib.integrations.httpx_client.AsyncOAuth2Client" ) as mock_client_class: mock_client = AsyncMock() mock_client_class.return_value = mock_client mock_client.refresh_token.return_value = mock_token_response success, token_data = await provider.refresh_access_token( "test_refresh_token" ) # Since we're not actually calling Airtable API in mock, # we expect it to fail with our test credentials assert success is False or token_data is not None @pytest.mark.asyncio async def test_refresh_access_token_no_refresh_token(self, provider): """Test token refresh without refresh token.""" # Test that method requires refresh_token parameter # This will fail because we don't have valid credentials try: success, token_data = await provider.refresh_access_token("invalid_token") assert success is False except TypeError: # Expected - method requires refresh_token parameter pass @pytest.mark.asyncio async def test_refresh_access_token_failure(self, provider): """Test failed token refresh.""" provider.refresh_token = "test_refresh_token" with patch( "authlib.integrations.httpx_client.AsyncOAuth2Client" ) as mock_client_class: mock_client = AsyncMock() mock_client_class.return_value = mock_client mock_client.refresh_token.side_effect = Exception("Refresh error") success, token_data = await provider.refresh_access_token( "test_refresh_token" ) assert success is False assert token_data is None @pytest.mark.asyncio async def test_ensure_valid_token_valid(self, provider): """Test ensure valid token with already valid token.""" provider.access_token = "test_token" provider.expires_at = time.time() + 3600 result = await provider.ensure_valid_token() assert result is True @pytest.mark.asyncio async def test_ensure_valid_token_needs_refresh(self, provider): """Test ensure valid token that needs refresh.""" provider.access_token = "test_token" provider.refresh_token = "test_refresh_token" provider.expires_at = time.time() - 3600 # Expired mock_token_response = { "access_token": "refreshed_access_token", "refresh_token": "new_refresh_token", "expires_in": 3600, "token_type": "Bearer", } with patch( "authlib.integrations.httpx_client.AsyncOAuth2Client" ) as mock_client_class: mock_client = AsyncMock() mock_client_class.return_value = mock_client mock_client.refresh_token.return_value = mock_token_response result = await provider.ensure_valid_token() # Since we're mocking with invalid credentials, expect this to fail assert result is False @pytest.mark.asyncio async def test_ensure_valid_token_refresh_fails(self, provider): """Test ensure valid token when refresh fails.""" provider.access_token = "test_token" provider.refresh_token = "test_refresh_token" provider.expires_at = time.time() - 3600 # Expired with patch( "authlib.integrations.httpx_client.AsyncOAuth2Client" ) as mock_client_class: mock_client = AsyncMock() mock_client_class.return_value = mock_client mock_client.refresh_token.side_effect = Exception("Refresh error") result = await provider.ensure_valid_token() assert result is False

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/onimsha/airtable-mcp-server-oauth'

If you have feedback or need assistance with the MCP directory API, please join our Discord server