Skip to content

Commit d430100

Browse files
authored
249 raise doctest v2 (#525)
* Improve doctest coverage Signed-off-by: Mihai Criveti <[email protected]> * Improve doctest coverage Signed-off-by: Mihai Criveti <[email protected]> --------- Signed-off-by: Mihai Criveti <[email protected]>
1 parent f7aa722 commit d430100

File tree

9 files changed

+426
-106
lines changed

9 files changed

+426
-106
lines changed

mcpgateway/cache/resource_cache.py

Lines changed: 64 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -11,28 +11,27 @@
1111
- Maximum size limit with LRU eviction
1212
- Thread-safe operations
1313
14-
Doctest examples
15-
----------------
16-
>>> from mcpgateway.cache.resource_cache import ResourceCache
17-
>>> cache = ResourceCache(max_size=2, ttl=1)
18-
>>> cache.set('a', 1)
19-
>>> cache.get('a')
20-
1
21-
>>> import time
22-
>>> time.sleep(1.2)
23-
>>> cache.get('a') is None
24-
True
25-
>>> cache.set('a', 1)
26-
>>> cache.set('b', 2)
27-
>>> cache.set('c', 3) # LRU eviction
28-
>>> sorted(cache._cache.keys())
29-
['b', 'c']
30-
>>> cache.delete('b')
31-
>>> cache.get('b') is None
32-
True
33-
>>> cache.clear()
34-
>>> cache.get('a') is None
35-
True
14+
Examples:
15+
>>> from mcpgateway.cache.resource_cache import ResourceCache
16+
>>> cache = ResourceCache(max_size=2, ttl=1)
17+
>>> cache.set('a', 1)
18+
>>> cache.get('a')
19+
1
20+
>>> import time
21+
>>> time.sleep(1.2)
22+
>>> cache.get('a') is None
23+
True
24+
>>> cache.set('a', 1)
25+
>>> cache.set('b', 2)
26+
>>> cache.set('c', 3) # LRU eviction
27+
>>> sorted(cache._cache.keys())
28+
['b', 'c']
29+
>>> cache.delete('b')
30+
>>> cache.get('b') is None
31+
True
32+
>>> cache.clear()
33+
>>> cache.get('a') is None
34+
True
3635
"""
3736

3837
# Standard
@@ -64,27 +63,27 @@ class ResourceCache:
6463
_cache: Cache storage
6564
_lock: Async lock for thread safety
6665
67-
Doctest:
68-
>>> from mcpgateway.cache.resource_cache import ResourceCache
69-
>>> cache = ResourceCache(max_size=2, ttl=1)
70-
>>> cache.set('a', 1)
71-
>>> cache.get('a')
72-
1
73-
>>> import time
74-
>>> time.sleep(1.2)
75-
>>> cache.get('a') is None
76-
True
77-
>>> cache.set('a', 1)
78-
>>> cache.set('b', 2)
79-
>>> cache.set('c', 3) # LRU eviction
80-
>>> sorted(cache._cache.keys())
81-
['b', 'c']
82-
>>> cache.delete('b')
83-
>>> cache.get('b') is None
84-
True
85-
>>> cache.clear()
86-
>>> cache.get('a') is None
87-
True
66+
Examples:
67+
>>> from mcpgateway.cache.resource_cache import ResourceCache
68+
>>> cache = ResourceCache(max_size=2, ttl=1)
69+
>>> cache.set('a', 1)
70+
>>> cache.get('a')
71+
1
72+
>>> import time
73+
>>> time.sleep(1.2)
74+
>>> cache.get('a') is None
75+
True
76+
>>> cache.set('a', 1)
77+
>>> cache.set('b', 2)
78+
>>> cache.set('c', 3) # LRU eviction
79+
>>> sorted(cache._cache.keys())
80+
['b', 'c']
81+
>>> cache.delete('b')
82+
>>> cache.get('b') is None
83+
True
84+
>>> cache.clear()
85+
>>> cache.get('a') is None
86+
True
8887
"""
8988

9089
def __init__(self, max_size: int = 1000, ttl: int = 3600):
@@ -120,7 +119,7 @@ def get(self, key: str) -> Optional[Any]:
120119
Returns:
121120
Cached value or None if not found/expired
122121
123-
Example:
122+
Examples:
124123
>>> from mcpgateway.cache.resource_cache import ResourceCache
125124
>>> cache = ResourceCache(max_size=2, ttl=1)
126125
>>> cache.set('a', 1)
@@ -132,7 +131,7 @@ def get(self, key: str) -> Optional[Any]:
132131
>>> short_cache.get('b')
133132
2
134133
>>> import time
135-
>>> time.sleep(1)
134+
>>> time.sleep(1.2)
136135
>>> short_cache.get('b') is None
137136
True
138137
"""
@@ -159,12 +158,12 @@ def set(self, key: str, value: Any) -> None:
159158
key: Cache key
160159
value: Value to cache
161160
162-
Doctest:
163-
>>> from mcpgateway.cache.resource_cache import ResourceCache
164-
>>> cache = ResourceCache(max_size=2, ttl=1)
165-
>>> cache.set('a', 1)
166-
>>> cache.get('a')
167-
1
161+
Examples:
162+
>>> from mcpgateway.cache.resource_cache import ResourceCache
163+
>>> cache = ResourceCache(max_size=2, ttl=1)
164+
>>> cache.set('a', 1)
165+
>>> cache.get('a')
166+
1
168167
"""
169168
now = time.time()
170169

@@ -184,27 +183,27 @@ def delete(self, key: str) -> None:
184183
Args:
185184
key: Cache key to delete
186185
187-
Doctest:
188-
>>> from mcpgateway.cache.resource_cache import ResourceCache
189-
>>> cache = ResourceCache()
190-
>>> cache.set('a', 1)
191-
>>> cache.delete('a')
192-
>>> cache.get('a') is None
193-
True
186+
Examples:
187+
>>> from mcpgateway.cache.resource_cache import ResourceCache
188+
>>> cache = ResourceCache()
189+
>>> cache.set('a', 1)
190+
>>> cache.delete('a')
191+
>>> cache.get('a') is None
192+
True
194193
"""
195194
self._cache.pop(key, None)
196195

197196
def clear(self) -> None:
198197
"""
199198
Clear all cached entries.
200199
201-
Doctest:
202-
>>> from mcpgateway.cache.resource_cache import ResourceCache
203-
>>> cache = ResourceCache()
204-
>>> cache.set('a', 1)
205-
>>> cache.clear()
206-
>>> cache.get('a') is None
207-
True
200+
Examples:
201+
>>> from mcpgateway.cache.resource_cache import ResourceCache
202+
>>> cache = ResourceCache()
203+
>>> cache.set('a', 1)
204+
>>> cache.clear()
205+
>>> cache.get('a') is None
206+
True
208207
"""
209208
self._cache.clear()
210209

mcpgateway/main.py

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -306,13 +306,18 @@ async def database_exception_handler(_request: Request, exc: IntegrityError):
306306
JSONResponse: A 409 Conflict response with formatted database error details.
307307
308308
Examples:
309-
>>> # This handler is automatically invoked when database constraints are violated
310-
>>> # For example, trying to create a duplicate tool name:
311-
>>> # POST /tools with duplicate name would trigger this handler
312-
>>> # Response format:
313-
>>> # {
314-
>>> # "detail": "Unique constraint violation: Key (name)=(existing_tool) already exists"
315-
>>> # }
309+
>>> from sqlalchemy.exc import IntegrityError
310+
>>> from fastapi import Request
311+
>>> import asyncio
312+
>>>
313+
>>> # Create a mock integrity error
314+
>>> mock_error = IntegrityError("statement", {}, Exception("duplicate key"))
315+
>>> result = asyncio.run(database_exception_handler(None, mock_error))
316+
>>> result.status_code
317+
409
318+
>>> # Verify ErrorFormatter.format_database_error is called
319+
>>> hasattr(result, 'body')
320+
True
316321
"""
317322
return JSONResponse(status_code=409, content=ErrorFormatter.format_database_error(exc))
318323

@@ -519,13 +524,19 @@ async def invalidate_resource_cache(uri: Optional[str] = None) -> None:
519524
520525
Examples:
521526
>>> import asyncio
522-
>>> # Test with specific URI
523-
>>> result = asyncio.run(invalidate_resource_cache("/test/uri"))
524-
>>> result is None
527+
>>> # Test clearing specific URI from cache
528+
>>> resource_cache.set("/test/resource", {"content": "test data"})
529+
>>> resource_cache.get("/test/resource") is not None
530+
True
531+
>>> asyncio.run(invalidate_resource_cache("/test/resource"))
532+
>>> resource_cache.get("/test/resource") is None
525533
True
526-
>>> # Test with no URI (clear all)
527-
>>> result = asyncio.run(invalidate_resource_cache())
528-
>>> result is None
534+
>>>
535+
>>> # Test clearing entire cache
536+
>>> resource_cache.set("/resource1", {"content": "data1"})
537+
>>> resource_cache.set("/resource2", {"content": "data2"})
538+
>>> asyncio.run(invalidate_resource_cache())
539+
>>> resource_cache.get("/resource1") is None and resource_cache.get("/resource2") is None
529540
True
530541
"""
531542
if uri:

0 commit comments

Comments
 (0)