mirror of
https://github.com/alibaba/higress.git
synced 2026-02-27 22:20:57 +08:00
303 lines
8.7 KiB
Go
303 lines
8.7 KiB
Go
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package server
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// MockHttpContext is a mock implementation for testing - skipping interface implementation for now
|
|
// Tests that require full HttpContext will be tested in integration tests with real host
|
|
type MockHttpContext struct {
|
|
responseBody []byte
|
|
responseStatus int
|
|
headers map[string]string
|
|
}
|
|
|
|
// TestMcpProtocolInitialization tests the MCP protocol initialization flow
|
|
func TestMcpProtocolInitialization(t *testing.T) {
|
|
// Create proxy server
|
|
server := NewMcpProxyServer("test-proxy")
|
|
|
|
// Set server fields directly
|
|
server.SetMcpServerURL("http://mock-backend.example.com/mcp")
|
|
server.SetTimeout(5000)
|
|
|
|
// Create proxy tool
|
|
toolConfig := McpProxyToolConfig{
|
|
Name: "test-tool",
|
|
Description: "Test tool for initialization",
|
|
Args: []ToolArg{
|
|
{
|
|
Name: "input",
|
|
Description: "Test input",
|
|
Type: "string",
|
|
Required: true,
|
|
},
|
|
},
|
|
}
|
|
|
|
err := server.AddProxyTool(toolConfig)
|
|
require.NoError(t, err)
|
|
|
|
tool, exists := server.GetMCPTools()["test-tool"]
|
|
require.True(t, exists)
|
|
|
|
// Create tool instance with parameters
|
|
params := map[string]interface{}{
|
|
"input": "test value",
|
|
}
|
|
paramsBytes, err := json.Marshal(params)
|
|
require.NoError(t, err)
|
|
|
|
toolInstance := tool.Create(paramsBytes)
|
|
require.NotNil(t, toolInstance)
|
|
|
|
// Skip HttpContext-dependent test for now - will be tested in integration
|
|
// mockCtx := &MockHttpContext{}
|
|
// err = toolInstance.Call(mockCtx, server)
|
|
// assert.NoError(t, err)
|
|
|
|
// Test the tool creation was successful
|
|
assert.NotNil(t, toolInstance)
|
|
}
|
|
|
|
// TestMcpSessionManagement tests temporary session creation and cleanup
|
|
func TestMcpSessionManagement(t *testing.T) {
|
|
_ = NewMcpProxyServer("session-test")
|
|
|
|
// Skip session management test until implemented
|
|
t.Skip("Session management not implemented yet")
|
|
|
|
// Test session creation
|
|
sessionManager := NewMcpSessionManager()
|
|
sessionID, err := sessionManager.CreateSession("http://backend.example.com/mcp")
|
|
|
|
// This will fail until session management is implemented
|
|
assert.NoError(t, err)
|
|
assert.NotEmpty(t, sessionID)
|
|
|
|
// Test session retrieval
|
|
session, exists := sessionManager.GetSession(sessionID)
|
|
assert.True(t, exists)
|
|
assert.NotNil(t, session)
|
|
|
|
// Test session cleanup
|
|
sessionManager.CleanupSession(sessionID)
|
|
_, exists = sessionManager.GetSession(sessionID)
|
|
assert.False(t, exists)
|
|
}
|
|
|
|
// TestMcpProtocolVersionNegotiation tests protocol version handling
|
|
func TestMcpProtocolVersionNegotiation(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
requestedVersion string
|
|
supportedVersions []string
|
|
shouldSucceed bool
|
|
expectedVersion string
|
|
}{
|
|
{
|
|
name: "supported version 2025-03-26",
|
|
requestedVersion: "2025-03-26",
|
|
supportedVersions: []string{"2024-11-05", "2025-03-26"},
|
|
shouldSucceed: true,
|
|
expectedVersion: "2025-03-26",
|
|
},
|
|
{
|
|
name: "unsupported version",
|
|
requestedVersion: "2026-01-01",
|
|
supportedVersions: []string{"2024-11-05", "2025-03-26"},
|
|
shouldSucceed: false,
|
|
expectedVersion: "",
|
|
},
|
|
{
|
|
name: "fallback to supported version",
|
|
requestedVersion: "2025-06-18",
|
|
supportedVersions: []string{"2024-11-05", "2025-03-26"},
|
|
shouldSucceed: false,
|
|
expectedVersion: "",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Skip until NewMcpVersionNegotiator is implemented
|
|
t.Skip("Version negotiation not implemented yet")
|
|
|
|
negotiator := NewMcpVersionNegotiator(tt.supportedVersions)
|
|
version, err := negotiator.NegotiateVersion(tt.requestedVersion)
|
|
|
|
if tt.shouldSucceed {
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tt.expectedVersion, version)
|
|
} else {
|
|
assert.Error(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestMcpInitializeRequest tests the initialize request format and handling
|
|
func TestMcpInitializeRequest(t *testing.T) {
|
|
_ = NewMcpProxyServer("init-test")
|
|
|
|
// Skip until CreateInitializeRequest is implemented
|
|
t.Skip("MCP protocol initialization not implemented yet")
|
|
|
|
// Test initialize request creation
|
|
initRequest := CreateInitializeRequest()
|
|
|
|
assert.Equal(t, "2.0", initRequest.JsonRPC)
|
|
assert.Equal(t, "initialize", initRequest.Method)
|
|
assert.NotNil(t, initRequest.Params)
|
|
|
|
// Validate client info
|
|
params := initRequest.Params.(map[string]interface{})
|
|
clientInfo := params["clientInfo"].(map[string]interface{})
|
|
assert.Equal(t, "Higress-mcp-proxy", clientInfo["name"])
|
|
assert.Equal(t, "1.0.0", clientInfo["version"])
|
|
|
|
// Test protocol version
|
|
assert.Equal(t, "2025-03-26", params["protocolVersion"])
|
|
}
|
|
|
|
// TestMcpNotificationsInitialized tests the notifications/initialized message
|
|
func TestMcpNotificationsInitialized(t *testing.T) {
|
|
// Skip until CreateInitializedNotification is implemented
|
|
t.Skip("MCP notifications not implemented yet")
|
|
|
|
// Test notifications/initialized request creation
|
|
notification := CreateInitializedNotification()
|
|
|
|
assert.Equal(t, "2.0", notification.JsonRPC)
|
|
assert.Equal(t, "notifications/initialized", notification.Method)
|
|
assert.Nil(t, notification.ID) // Notifications don't have IDs
|
|
}
|
|
|
|
// TestMcpErrorHandling tests error response handling and source identification
|
|
func TestMcpErrorHandling(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
errorType string
|
|
originalError error
|
|
expectedSource string
|
|
expectedCode int
|
|
}{
|
|
{
|
|
name: "backend connection error",
|
|
errorType: "connection",
|
|
originalError: assert.AnError,
|
|
expectedSource: "mcp-proxy",
|
|
expectedCode: -32603,
|
|
},
|
|
{
|
|
name: "backend timeout error",
|
|
errorType: "timeout",
|
|
originalError: assert.AnError,
|
|
expectedSource: "mcp-proxy",
|
|
expectedCode: -32000,
|
|
},
|
|
{
|
|
name: "protocol version error",
|
|
errorType: "version",
|
|
originalError: assert.AnError,
|
|
expectedSource: "mcp-proxy",
|
|
expectedCode: -32602,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Skip until CreateMcpErrorResponse is implemented
|
|
t.Skip("MCP error handling not implemented yet")
|
|
|
|
errorResponse := CreateMcpErrorResponse(tt.errorType, tt.originalError, "http://backend.example.com/mcp")
|
|
|
|
assert.Equal(t, "2.0", errorResponse.JsonRPC)
|
|
assert.NotNil(t, errorResponse.Error)
|
|
assert.Equal(t, tt.expectedCode, errorResponse.Error.Code)
|
|
assert.Equal(t, tt.expectedSource, errorResponse.Error.Data["source"])
|
|
})
|
|
}
|
|
}
|
|
|
|
// Helper types and functions that will fail until implemented
|
|
|
|
type McpSessionManager struct{}
|
|
|
|
func NewMcpSessionManager() *McpSessionManager {
|
|
panic("McpSessionManager not implemented yet")
|
|
}
|
|
|
|
func (m *McpSessionManager) CreateSession(backendURL string) (string, error) {
|
|
panic("CreateSession not implemented yet")
|
|
}
|
|
|
|
func (m *McpSessionManager) GetSession(sessionID string) (interface{}, bool) {
|
|
panic("GetSession not implemented yet")
|
|
}
|
|
|
|
func (m *McpSessionManager) CleanupSession(sessionID string) {
|
|
panic("CleanupSession not implemented yet")
|
|
}
|
|
|
|
type McpVersionNegotiator struct {
|
|
supportedVersions []string
|
|
}
|
|
|
|
func NewMcpVersionNegotiator(versions []string) *McpVersionNegotiator {
|
|
panic("McpVersionNegotiator not implemented yet")
|
|
}
|
|
|
|
func (n *McpVersionNegotiator) NegotiateVersion(requested string) (string, error) {
|
|
panic("NegotiateVersion not implemented yet")
|
|
}
|
|
|
|
type McpRequest struct {
|
|
JsonRPC string `json:"jsonrpc"`
|
|
ID interface{} `json:"id,omitempty"`
|
|
Method string `json:"method"`
|
|
Params interface{} `json:"params,omitempty"`
|
|
}
|
|
|
|
type McpErrorResponse struct {
|
|
JsonRPC string `json:"jsonrpc"`
|
|
ID interface{} `json:"id,omitempty"`
|
|
Error *McpError `json:"error"`
|
|
}
|
|
|
|
type McpError struct {
|
|
Code int `json:"code"`
|
|
Message string `json:"message"`
|
|
Data map[string]interface{} `json:"data,omitempty"`
|
|
}
|
|
|
|
func CreateInitializeRequest() *McpRequest {
|
|
panic("CreateInitializeRequest not implemented yet")
|
|
}
|
|
|
|
func CreateInitializedNotification() *McpRequest {
|
|
panic("CreateInitializedNotification not implemented yet")
|
|
}
|
|
|
|
func CreateMcpErrorResponse(errorType string, originalError error, backendURL string) *McpErrorResponse {
|
|
panic("CreateMcpErrorResponse not implemented yet")
|
|
}
|