Skip to content

Commit 2481b1a

Browse files
committed
doctest
Signed-off-by: RAKHI DUTTA <[email protected]>
1 parent 3741895 commit 2481b1a

File tree

2 files changed

+69
-66
lines changed

2 files changed

+69
-66
lines changed

mcpgateway/admin.py

Lines changed: 67 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,7 @@ async def admin_edit_server(
495495
>>> import asyncio
496496
>>> from unittest.mock import AsyncMock, MagicMock
497497
>>> from fastapi import Request
498-
>>> from fastapi.responses import RedirectResponse
498+
>>> from fastapi.responses import JSONResponse
499499
>>> from starlette.datastructures import FormData
500500
>>>
501501
>>> mock_db = MagicMock()
@@ -511,36 +511,67 @@ async def admin_edit_server(
511511
>>>
512512
>>> async def test_admin_edit_server_success():
513513
... result = await admin_edit_server(server_id, mock_request_edit, mock_db, mock_user)
514-
... return isinstance(result, RedirectResponse) and result.status_code == 303 and "/admin#catalog" in result.headers["location"]
514+
... return isinstance(result, JSONResponse) and result.status_code == 200 and result.body == b'{"message":"Server updated successfully!","success":true}'
515515
>>>
516516
>>> asyncio.run(test_admin_edit_server_success())
517517
True
518518
>>>
519-
>>> # Edge case: Edit server and include inactive checkbox
520-
>>> form_data_inactive = FormData([("name", "Inactive Server Edit"), ("is_inactive_checked", "true")])
521-
>>> mock_request_inactive = MagicMock(spec=Request, scope={"root_path": "/api"})
522-
>>> mock_request_inactive.form = AsyncMock(return_value=form_data_inactive)
523-
>>>
524-
>>> async def test_admin_edit_server_inactive_checked():
525-
... result = await admin_edit_server(server_id, mock_request_inactive, mock_db, mock_user)
526-
... return isinstance(result, RedirectResponse) and result.status_code == 303 and "/api/admin/?include_inactive=true#catalog" in result.headers["location"]
527-
>>>
528-
>>> asyncio.run(test_admin_edit_server_inactive_checked())
529-
True
530-
>>>
531519
>>> # Error path: Simulate an exception during update
532520
>>> form_data_error = FormData([("name", "Error Server")])
533521
>>> mock_request_error = MagicMock(spec=Request, scope={"root_path": ""})
534522
>>> mock_request_error.form = AsyncMock(return_value=form_data_error)
535523
>>> server_service.update_server = AsyncMock(side_effect=Exception("Update failed"))
536524
>>>
537-
>>> async def test_admin_edit_server_exception():
525+
>>> # Restore original method
526+
>>> server_service.update_server = original_update_server
527+
>>> # 409 Conflict: ServerNameConflictError
528+
>>> server_service.update_server = AsyncMock(side_effect=ServerNameConflictError("Name conflict"))
529+
>>> async def test_admin_edit_server_conflict():
538530
... result = await admin_edit_server(server_id, mock_request_error, mock_db, mock_user)
539-
... return isinstance(result, RedirectResponse) and result.status_code == 303 and "/admin#catalog" in result.headers["location"]
540-
>>>
541-
>>> asyncio.run(test_admin_edit_server_exception())
531+
... return isinstance(result, JSONResponse) and result.status_code == 409 and b'Name conflict' in result.body
532+
>>> asyncio.run(test_admin_edit_server_conflict())
533+
True
534+
>>> # 409 Conflict: IntegrityError
535+
>>> from sqlalchemy.exc import IntegrityError
536+
>>> server_service.update_server = AsyncMock(side_effect=IntegrityError("Integrity error", None, None))
537+
>>> async def test_admin_edit_server_integrity():
538+
... result = await admin_edit_server(server_id, mock_request_error, mock_db, mock_user)
539+
... return isinstance(result, JSONResponse) and result.status_code == 409
540+
>>> asyncio.run(test_admin_edit_server_integrity())
541+
True
542+
>>> # 422 Unprocessable Entity: ValidationError
543+
>>> from pydantic import ValidationError, BaseModel
544+
>>> from mcpgateway.schemas import ServerUpdate
545+
>>> validation_error = ValidationError.from_exception_data("ServerUpdate validation error", [
546+
... {"loc": ("name",), "msg": "Field required", "type": "missing"}
547+
... ])
548+
>>> server_service.update_server = AsyncMock(side_effect=validation_error)
549+
>>> async def test_admin_edit_server_validation():
550+
... result = await admin_edit_server(server_id, mock_request_error, mock_db, mock_user)
551+
... return isinstance(result, JSONResponse) and result.status_code == 422
552+
>>> asyncio.run(test_admin_edit_server_validation())
553+
True
554+
>>> # 400 Bad Request: ValueError
555+
>>> server_service.update_server = AsyncMock(side_effect=ValueError("Bad value"))
556+
>>> async def test_admin_edit_server_valueerror():
557+
... result = await admin_edit_server(server_id, mock_request_error, mock_db, mock_user)
558+
... return isinstance(result, JSONResponse) and result.status_code == 400 and b'Bad value' in result.body
559+
>>> asyncio.run(test_admin_edit_server_valueerror())
560+
True
561+
>>> # 500 Internal Server Error: ServerError
562+
>>> server_service.update_server = AsyncMock(side_effect=ServerError("Server error"))
563+
>>> async def test_admin_edit_server_servererror():
564+
... result = await admin_edit_server(server_id, mock_request_error, mock_db, mock_user)
565+
... return isinstance(result, JSONResponse) and result.status_code == 500 and b'Server error' in result.body
566+
>>> asyncio.run(test_admin_edit_server_servererror())
567+
True
568+
>>> # 500 Internal Server Error: RuntimeError
569+
>>> server_service.update_server = AsyncMock(side_effect=RuntimeError("Runtime error"))
570+
>>> async def test_admin_edit_server_runtimeerror():
571+
... result = await admin_edit_server(server_id, mock_request_error, mock_db, mock_user)
572+
... return isinstance(result, JSONResponse) and result.status_code == 500 and b'Runtime error' in result.body
573+
>>> asyncio.run(test_admin_edit_server_runtimeerror())
542574
True
543-
>>>
544575
>>> # Restore original method
545576
>>> server_service.update_server = original_update_server
546577
"""
@@ -558,35 +589,23 @@ async def admin_edit_server(
558589
await server_service.update_server(db, server_id, server)
559590

560591
return JSONResponse(
561-
content={"message": "Server update successfully!", "success": True},
592+
content={"message": "Server updated successfully!", "success": True},
562593
status_code=200,
563594
)
595+
except (ValidationError, CoreValidationError) as ex:
596+
# Catch both Pydantic and pydantic_core validation errors
597+
return JSONResponse(content=ErrorFormatter.format_validation_error(ex), status_code=422)
598+
except ServerNameConflictError as ex:
599+
return JSONResponse(content={"message": str(ex), "success": False}, status_code=409)
600+
except ServerError as ex:
601+
return JSONResponse(content={"message": str(ex), "success": False}, status_code=500)
602+
except ValueError as ex:
603+
return JSONResponse(content={"message": str(ex), "success": False}, status_code=400)
604+
except RuntimeError as ex:
605+
return JSONResponse(content={"message": str(ex), "success": False}, status_code=500)
606+
except IntegrityError as ex:
607+
return JSONResponse(content=ErrorFormatter.format_database_error(ex), status_code=409)
564608
except Exception as ex:
565-
if isinstance(ex, ServerNameConflictError):
566-
# Custom server name conflict error — 409 Conflict is appropriate
567-
return JSONResponse(content={"message": str(ex), "success": False}, status_code=409)
568-
569-
if isinstance(ex, ServerError):
570-
# Custom server logic error — 500 Internal Server Error makes sense
571-
return JSONResponse(content={"message": str(ex), "success": False}, status_code=500)
572-
573-
if isinstance(ex, ValueError):
574-
# Invalid input — 400 Bad Request is appropriate
575-
return JSONResponse(content={"message": str(ex), "success": False}, status_code=400)
576-
577-
if isinstance(ex, RuntimeError):
578-
# Unexpected error during runtime — 500 is suitable
579-
return JSONResponse(content={"message": str(ex), "success": False}, status_code=500)
580-
581-
if isinstance(ex, ValidationError):
582-
# Pydantic or input validation failure — 422 Unprocessable Entity is correct
583-
return JSONResponse(content=ErrorFormatter.format_validation_error(ex), status_code=422)
584-
585-
if isinstance(ex, IntegrityError):
586-
# DB constraint violation — 409 Conflict is appropriate
587-
return JSONResponse(content=ErrorFormatter.format_database_error(ex), status_code=409)
588-
589-
# For any other unhandled error, default to 500
590609
return JSONResponse(content={"message": str(ex), "success": False}, status_code=500)
591610

592611

@@ -2831,27 +2850,11 @@ async def admin_edit_resource(
28312850
>>>
28322851
>>> async def test_admin_edit_resource():
28332852
... response = await admin_edit_resource("test://resource1", mock_request, mock_db, mock_user)
2834-
... return isinstance(response, RedirectResponse) and response.status_code == 303
2853+
... return isinstance(response, JSONResponse) and response.status_code == 200 and response.body == b'{"message":"Resource update successfully!","success":true}'
28352854
>>>
28362855
>>> import asyncio; asyncio.run(test_admin_edit_resource())
28372856
True
28382857
>>>
2839-
>>> # Test with inactive checkbox checked
2840-
>>> form_data_inactive = FormData([
2841-
... ("name", "Updated Resource"),
2842-
... ("description", "Updated description"),
2843-
... ("mimeType", "text/plain"),
2844-
... ("content", "Updated content"),
2845-
... ("is_inactive_checked", "true")
2846-
... ])
2847-
>>> mock_request.form = AsyncMock(return_value=form_data_inactive)
2848-
>>>
2849-
>>> async def test_admin_edit_resource_inactive():
2850-
... response = await admin_edit_resource("test://resource1", mock_request, mock_db, mock_user)
2851-
... return isinstance(response, RedirectResponse) and "include_inactive=true" in response.headers["location"]
2852-
>>>
2853-
>>> asyncio.run(test_admin_edit_resource_inactive())
2854-
True
28552858
>>> resource_service.update_resource = original_update_resource
28562859
"""
28572860
logger.debug(f"User {user} is editing resource URI {uri}")
@@ -2865,7 +2868,7 @@ async def admin_edit_resource(
28652868
)
28662869
await resource_service.update_resource(db, uri, resource)
28672870
return JSONResponse(
2868-
content={"message": "Resource updated successfully!", "success": True},
2871+
content={"message": "Resource update successfully!", "success": True},
28692872
status_code=200,
28702873
)
28712874
except Exception as ex:
@@ -3254,7 +3257,7 @@ async def admin_edit_prompt(
32543257
user: Authenticated user.
32553258
32563259
Returns:
3257-
A redirect response to the admin dashboard.
3260+
JSONResponse: A JSON response indicating success or failure of the server update operation.
32583261
32593262
Examples:
32603263
>>> import asyncio

mcpgateway/main.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1426,10 +1426,10 @@ async def create_resource(
14261426
except ResourceError as e:
14271427
raise HTTPException(status_code=400, detail=str(e))
14281428
except ValidationError as e:
1429-
logger.error(f"Validation error while creating resource {uri}: {e}")
1429+
logger.error(f"Validation error while creating resource: {e}")
14301430
raise HTTPException(status_code=422, detail=ErrorFormatter.format_validation_error(e))
14311431
except IntegrityError as e:
1432-
logger.error(f"Integrity error while creating resource {uri}: {e}")
1432+
logger.error(f"Integrity error while creating resource: {e}")
14331433
raise HTTPException(status_code=409, detail=ErrorFormatter.format_database_error(e))
14341434

14351435
@resource_router.get("/{uri:path}")

0 commit comments

Comments
 (0)