Skip to content

Commit 9cb6c11

Browse files
committed
get test coverage to 100%
Signed-off-by: phernandez <paul@basicmachines.co>
1 parent d24e54f commit 9cb6c11

2 files changed

Lines changed: 177 additions & 0 deletions

File tree

tests/mcp/test_auth_provider.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,3 +278,36 @@ async def test_invalid_client(self, provider):
278278

279279
code = await provider.load_authorization_code(fake_client, "some-code")
280280
assert code is None
281+
282+
@pytest.mark.asyncio
283+
async def test_expired_access_token_in_memory(self, provider):
284+
"""Test that expired access tokens in memory are removed."""
285+
# Create an expired token directly in memory
286+
expired_token_str = "expired-access-token"
287+
expired_access_token = BasicMemoryAccessToken(
288+
token=expired_token_str,
289+
client_id="test-client",
290+
scopes=["read"],
291+
expires_at=int((datetime.utcnow() - timedelta(minutes=1)).timestamp()), # Expired
292+
)
293+
provider.access_tokens[expired_token_str] = expired_access_token
294+
295+
# Try to load the expired token - should return None and remove from memory
296+
result = await provider.load_access_token(expired_token_str)
297+
assert result is None
298+
assert expired_token_str not in provider.access_tokens
299+
300+
@pytest.mark.asyncio
301+
async def test_jwt_decode_success_path(self, provider):
302+
"""Test successful JWT decode path when token not in memory."""
303+
# Generate a valid JWT token
304+
jwt_token = provider._generate_access_token("test-client", ["read", "write"])
305+
306+
# Make sure it's not in memory cache
307+
assert jwt_token not in provider.access_tokens
308+
309+
# Load the token - should decode successfully
310+
result = await provider.load_access_token(jwt_token)
311+
assert result is not None
312+
assert result.client_id == "test-client"
313+
assert result.scopes == ["read", "write"]

tests/mcp/test_server.py

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
"""Tests for MCP server configuration."""
2+
3+
import os
4+
import pytest
5+
from unittest.mock import patch, MagicMock
6+
7+
from basic_memory.mcp.server import create_auth_config
8+
9+
10+
class TestMCPServer:
11+
"""Test MCP server configuration."""
12+
13+
def test_create_auth_config_no_provider(self):
14+
"""Test auth config creation when no provider is specified."""
15+
with patch.dict(os.environ, {}, clear=True):
16+
auth_settings, auth_provider = create_auth_config()
17+
assert auth_settings is None
18+
assert auth_provider is None
19+
20+
def test_create_auth_config_github_provider(self):
21+
"""Test auth config creation with GitHub provider."""
22+
env_vars = {"FASTMCP_AUTH_ENABLED": "true", "FASTMCP_AUTH_PROVIDER": "github"}
23+
with patch.dict(os.environ, env_vars):
24+
with patch("basic_memory.mcp.server.create_github_provider") as mock_create_github:
25+
mock_github_provider = MagicMock()
26+
mock_create_github.return_value = mock_github_provider
27+
28+
auth_settings, auth_provider = create_auth_config()
29+
30+
assert auth_settings is not None
31+
assert auth_provider == mock_github_provider
32+
mock_create_github.assert_called_once()
33+
34+
def test_create_auth_config_google_provider(self):
35+
"""Test auth config creation with Google provider."""
36+
env_vars = {"FASTMCP_AUTH_ENABLED": "true", "FASTMCP_AUTH_PROVIDER": "google"}
37+
with patch.dict(os.environ, env_vars):
38+
with patch("basic_memory.mcp.server.create_google_provider") as mock_create_google:
39+
mock_google_provider = MagicMock()
40+
mock_create_google.return_value = mock_google_provider
41+
42+
auth_settings, auth_provider = create_auth_config()
43+
44+
assert auth_settings is not None
45+
assert auth_provider == mock_google_provider
46+
mock_create_google.assert_called_once()
47+
48+
def test_create_auth_config_supabase_provider_success(self):
49+
"""Test auth config creation with Supabase provider (success case)."""
50+
env_vars = {
51+
"FASTMCP_AUTH_ENABLED": "true",
52+
"FASTMCP_AUTH_PROVIDER": "supabase",
53+
"SUPABASE_URL": "https://test.supabase.co",
54+
"SUPABASE_ANON_KEY": "anon-key-123",
55+
"SUPABASE_SERVICE_KEY": "service-key-456",
56+
}
57+
58+
with patch.dict(os.environ, env_vars):
59+
with patch("basic_memory.mcp.server.SupabaseOAuthProvider") as mock_supabase_class:
60+
mock_supabase_provider = MagicMock()
61+
mock_supabase_class.return_value = mock_supabase_provider
62+
63+
auth_settings, auth_provider = create_auth_config()
64+
65+
assert auth_settings is not None
66+
assert auth_provider == mock_supabase_provider
67+
mock_supabase_class.assert_called_once_with(
68+
supabase_url="https://test.supabase.co",
69+
supabase_anon_key="anon-key-123",
70+
supabase_service_key="service-key-456",
71+
issuer_url="http://localhost:8000", # Default issuer URL is added
72+
)
73+
74+
def test_create_auth_config_supabase_provider_missing_url(self):
75+
"""Test auth config creation with Supabase provider missing URL."""
76+
env_vars = {
77+
"FASTMCP_AUTH_ENABLED": "true",
78+
"FASTMCP_AUTH_PROVIDER": "supabase",
79+
"SUPABASE_ANON_KEY": "anon-key-123",
80+
# Missing SUPABASE_URL
81+
}
82+
83+
with patch.dict(os.environ, env_vars):
84+
with pytest.raises(ValueError, match="SUPABASE_URL and SUPABASE_ANON_KEY must be set"):
85+
create_auth_config()
86+
87+
def test_create_auth_config_supabase_provider_missing_anon_key(self):
88+
"""Test auth config creation with Supabase provider missing anon key."""
89+
env_vars = {
90+
"FASTMCP_AUTH_ENABLED": "true",
91+
"FASTMCP_AUTH_PROVIDER": "supabase",
92+
"SUPABASE_URL": "https://test.supabase.co",
93+
# Missing SUPABASE_ANON_KEY
94+
}
95+
96+
with patch.dict(os.environ, env_vars):
97+
with pytest.raises(ValueError, match="SUPABASE_URL and SUPABASE_ANON_KEY must be set"):
98+
create_auth_config()
99+
100+
def test_create_auth_config_basic_memory_provider(self):
101+
"""Test auth config creation with basic-memory provider."""
102+
env_vars = {
103+
"FASTMCP_AUTH_ENABLED": "true",
104+
"FASTMCP_AUTH_PROVIDER": "basic-memory",
105+
"FASTMCP_AUTH_SECRET_KEY": "test-secret-key",
106+
"FASTMCP_AUTH_ISSUER_URL": "https://custom-issuer.com",
107+
}
108+
109+
with patch.dict(os.environ, env_vars):
110+
with patch(
111+
"basic_memory.mcp.server.BasicMemoryOAuthProvider"
112+
) as mock_basic_memory_class:
113+
mock_basic_memory_provider = MagicMock()
114+
mock_basic_memory_class.return_value = mock_basic_memory_provider
115+
116+
auth_settings, auth_provider = create_auth_config()
117+
118+
assert auth_settings is not None
119+
assert auth_provider == mock_basic_memory_provider
120+
mock_basic_memory_class.assert_called_once_with(
121+
issuer_url="https://custom-issuer.com"
122+
)
123+
124+
def test_create_auth_config_basic_memory_provider_default_issuer(self):
125+
"""Test auth config creation with basic-memory provider using default issuer."""
126+
env_vars = {
127+
"FASTMCP_AUTH_ENABLED": "true",
128+
"FASTMCP_AUTH_PROVIDER": "basic-memory",
129+
"FASTMCP_AUTH_SECRET_KEY": "test-secret-key",
130+
# No FASTMCP_AUTH_ISSUER_URL - should use default
131+
}
132+
133+
with patch.dict(os.environ, env_vars):
134+
with patch(
135+
"basic_memory.mcp.server.BasicMemoryOAuthProvider"
136+
) as mock_basic_memory_class:
137+
mock_basic_memory_provider = MagicMock()
138+
mock_basic_memory_class.return_value = mock_basic_memory_provider
139+
140+
auth_settings, auth_provider = create_auth_config()
141+
142+
assert auth_settings is not None
143+
assert auth_provider == mock_basic_memory_provider
144+
mock_basic_memory_class.assert_called_once_with(issuer_url="http://localhost:8000")

0 commit comments

Comments
 (0)