Skip to main content
Glama

MCPJungle mcp gateway

by mcpjungle
Mozilla Public License 2.0
638
  • Apple
client_test.go•9.51 kB
package client import ( "io" "net/http" "net/http/httptest" "strings" "testing" ) func TestNewClient(t *testing.T) { t.Parallel() baseURL := "https://api.example.com" accessToken := "test-token" httpClient := &http.Client{} client := NewClient(baseURL, accessToken, httpClient) if client.baseURL != baseURL { t.Errorf("Expected baseURL %s, got %s", baseURL, client.baseURL) } if client.accessToken != accessToken { t.Errorf("Expected accessToken %s, got %s", accessToken, client.accessToken) } if client.httpClient != httpClient { t.Error("Expected httpClient to match provided client") } } func TestNewClientWithEmptyToken(t *testing.T) { t.Parallel() client := NewClient("https://api.example.com", "", &http.Client{}) if client.accessToken != "" { t.Errorf("Expected empty accessToken, got %s", client.accessToken) } } func TestConstructAPIEndpoint(t *testing.T) { t.Parallel() client := NewClient("https://api.example.com", "token", &http.Client{}) tests := []struct { name string suffixPath string expectedPath string }{ { name: "simple path", suffixPath: "servers", expectedPath: "https://api.example.com/api/v0/servers", }, { name: "nested path", suffixPath: "servers/test-server", expectedPath: "https://api.example.com/api/v0/servers/test-server", }, { name: "empty suffix", suffixPath: "", expectedPath: "https://api.example.com/api/v0", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := client.constructAPIEndpoint(tt.suffixPath) if err != nil { t.Fatalf("Unexpected error: %v", err) } if result != tt.expectedPath { t.Errorf("Expected %s, got %s", tt.expectedPath, result) } }) } } func TestNewRequest(t *testing.T) { t.Parallel() client := NewClient("https://api.example.com", "test-token", &http.Client{}) t.Run("request with access token", func(t *testing.T) { body := strings.NewReader("test body") req, err := client.newRequest(http.MethodPost, "https://api.example.com/test", body) if err != nil { t.Fatalf("Unexpected error: %v", err) } if req.Method != http.MethodPost { t.Errorf("Expected method POST, got %s", req.Method) } if req.URL.String() != "https://api.example.com/test" { t.Errorf("Expected URL https://api.example.com/test, got %s", req.URL.String()) } authHeader := req.Header.Get("Authorization") if authHeader != "Bearer test-token" { t.Errorf("Expected Authorization header 'Bearer test-token', got %s", authHeader) } }) t.Run("request without access token", func(t *testing.T) { clientNoToken := NewClient("https://api.example.com", "", &http.Client{}) req, err := clientNoToken.newRequest(http.MethodGet, "https://api.example.com/test", nil) if err != nil { t.Fatalf("Unexpected error: %v", err) } authHeader := req.Header.Get("Authorization") if authHeader != "" { t.Errorf("Expected empty Authorization header, got %s", authHeader) } }) t.Run("request with nil body", func(t *testing.T) { req, err := client.newRequest(http.MethodGet, "https://api.example.com/test", nil) if err != nil { t.Fatalf("Unexpected error: %v", err) } if req.Body != nil { t.Error("Expected nil body, got non-nil") } }) } func TestNewRequestWithInvalidURL(t *testing.T) { t.Parallel() client := NewClient("https://api.example.com", "token", &http.Client{}) // Test with invalid URL req, err := client.newRequest(http.MethodGet, "://invalid-url", nil) if err == nil { t.Error("Expected error for invalid URL, got nil") } if req != nil { t.Error("Expected nil request for invalid URL") } } func TestClientIntegration(t *testing.T) { t.Parallel() // Test that client can make actual HTTP requests server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Verify request method and headers if r.Method != http.MethodGet { t.Errorf("Expected GET method, got %s", r.Method) } authHeader := r.Header.Get("Authorization") if authHeader != "Bearer test-token" { t.Errorf("Expected Authorization header 'Bearer test-token', got %s", authHeader) } // Return success response w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte(`{"message": "success"}`)) })) defer server.Close() client := NewClient(server.URL, "test-token", &http.Client{}) // Test constructAPIEndpoint + newRequest integration endpoint, err := client.constructAPIEndpoint("test") if err != nil { t.Fatalf("Failed to construct endpoint: %v", err) } req, err := client.newRequest(http.MethodGet, endpoint, nil) if err != nil { t.Fatalf("Failed to create request: %v", err) } // Make the actual request resp, err := client.httpClient.Do(req) if err != nil { t.Fatalf("Failed to make request: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Errorf("Expected status 200, got %d", resp.StatusCode) } // Verify response body body, err := io.ReadAll(resp.Body) if err != nil { t.Fatalf("Failed to read response body: %v", err) } expectedBody := `{"message": "success"}` if string(body) != expectedBody { t.Errorf("Expected body %s, got %s", expectedBody, string(body)) } } func TestClientWithDifferentHTTPClients(t *testing.T) { t.Parallel() baseURL := "https://api.example.com" accessToken := "test-token" // Test with default HTTP client client1 := NewClient(baseURL, accessToken, &http.Client{}) if client1.httpClient == nil { t.Error("Expected non-nil httpClient") } // Test with custom HTTP client customClient := &http.Client{ Timeout: 30, // This would be a proper timeout in real usage } client2 := NewClient(baseURL, accessToken, customClient) if client2.httpClient != customClient { t.Error("Expected httpClient to match custom client") } } func TestConstructAPIEndpointWithComplexPaths(t *testing.T) { t.Parallel() client := NewClient("https://api.example.com", "token", &http.Client{}) tests := []struct { name string suffixPath string expectedPath string }{ { name: "path with query params", suffixPath: "tools?server=test", expectedPath: "https://api.example.com/api/v0/tools%3Fserver=test", }, { name: "path with special characters", suffixPath: "servers/test-server-123", expectedPath: "https://api.example.com/api/v0/servers/test-server-123", }, { name: "path with multiple segments", suffixPath: "tool-groups/my-group/tools", expectedPath: "https://api.example.com/api/v0/tool-groups/my-group/tools", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := client.constructAPIEndpoint(tt.suffixPath) if err != nil { t.Fatalf("Unexpected error: %v", err) } if result != tt.expectedPath { t.Errorf("Expected %s, got %s", tt.expectedPath, result) } }) } } // badReader simulates a Read error type badReader struct{} func (badReader) Read([]byte) (int, error) { return 0, io.ErrUnexpectedEOF } func (badReader) Close() error { return nil } func TestParseErrorResponse(t *testing.T) { t.Parallel() client := NewClient("https://api.example.com", "token", &http.Client{}) tests := []struct { name string statusCode int body string expectContains string expectErr bool readErr bool }{ { name: "valid JSON error", statusCode: 400, body: `{"error":"something went wrong"}`, expectContains: "something went wrong", expectErr: true, }, { name: "valid JSON error but empty message", statusCode: 404, body: `{"error":""}`, expectContains: "request failed with status: 404", expectErr: true, }, { name: "invalid JSON", statusCode: 500, body: `not a json`, expectContains: "request failed with status: 500, message: not a json", expectErr: true, }, { name: "non-JSON, non-error status", statusCode: 200, body: `all good`, expectContains: "unexpected response with status: 200, body: all good", expectErr: true, }, { name: "valid JSON, non-error status", statusCode: 201, body: `{"error":"should not parse"}`, expectContains: "unexpected response with status: 201, body: {\"error\":\"should not parse\"}", expectErr: true, }, { name: "read error from body", statusCode: 500, body: "", expectContains: "request failed with status: 500 (unable to read error details)", expectErr: true, readErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var body io.ReadCloser if tt.readErr { // Simulate read error body = io.NopCloser(badReader{}) } else { body = io.NopCloser(strings.NewReader(tt.body)) } resp := &http.Response{ StatusCode: tt.statusCode, Body: body, } err := client.parseErrorResponse(resp) if tt.expectErr && err == nil { t.Fatalf("Expected error, got nil") } if !tt.expectErr && err != nil { t.Fatalf("Did not expect error, got %v", err) } if err != nil && !strings.Contains(err.Error(), tt.expectContains) { t.Errorf("Expected error to contain %q, got %q", tt.expectContains, err.Error()) } }) } }

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/mcpjungle/MCPJungle'

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