Skip to main content
Glama

OpenAPI MCP Server

MIT License
2,130
150
  • Apple
import { describe, it, expect } from "vitest" import { AxiosError } from "axios" import { AuthProvider, StaticAuthProvider, isAuthError } from "../src/auth-provider" describe("AuthProvider", () => { describe("isAuthError", () => { it("should return true for 401 errors", () => { const error = { response: { status: 401 }, } as AxiosError expect(isAuthError(error)).toBe(true) }) it("should return true for 403 errors", () => { const error = { response: { status: 403 }, } as AxiosError expect(isAuthError(error)).toBe(true) }) it("should return false for other error statuses", () => { const error404 = { response: { status: 404 }, } as AxiosError const error500 = { response: { status: 500 }, } as AxiosError expect(isAuthError(error404)).toBe(false) expect(isAuthError(error500)).toBe(false) }) it("should return false for errors without response", () => { const error = {} as AxiosError expect(isAuthError(error)).toBe(false) }) // Edge case tests for isAuthError describe("Edge Cases", () => { it("should return false for non-Axios plain error objects", () => { // Plain Error object const plainError = new Error("Network error") as unknown as AxiosError expect(isAuthError(plainError)).toBe(false) // Generic object with error-like properties but no response const genericError = { message: "Something went wrong", code: "NETWORK_ERROR", } as unknown as AxiosError expect(isAuthError(genericError)).toBe(false) // Object with response but no status const errorWithoutStatus = { response: { data: "Unauthorized" }, } as unknown as AxiosError expect(isAuthError(errorWithoutStatus)).toBe(false) }) it("should return false for Axios-like errors with response but undefined status", () => { const errorWithUndefinedStatus = { response: { status: undefined, data: "Some error data", }, } as unknown as AxiosError expect(isAuthError(errorWithUndefinedStatus)).toBe(false) }) it("should return false for Axios-like errors with response but non-number status", () => { const errorWithStringStatus = { response: { status: "401" as unknown as number, data: "Unauthorized", }, } as AxiosError expect(isAuthError(errorWithStringStatus)).toBe(false) const errorWithNullStatus = { response: { status: null as unknown as number, data: "Forbidden", }, } as AxiosError expect(isAuthError(errorWithNullStatus)).toBe(false) const errorWithObjectStatus = { response: { status: { code: 401 } as unknown as number, data: "Unauthorized", }, } as AxiosError expect(isAuthError(errorWithObjectStatus)).toBe(false) }) it("should return false for errors with response property but response is null", () => { const errorWithNullResponse = { response: null, } as unknown as AxiosError expect(isAuthError(errorWithNullResponse)).toBe(false) }) it("should return false for errors with response property but response is undefined", () => { const errorWithUndefinedResponse = { response: undefined, } as unknown as AxiosError expect(isAuthError(errorWithUndefinedResponse)).toBe(false) }) it("should handle edge case status codes correctly", () => { // Status 0 (network error) const networkError = { response: { status: 0 }, } as AxiosError expect(isAuthError(networkError)).toBe(false) // Negative status const negativeStatusError = { response: { status: -1 }, } as AxiosError expect(isAuthError(negativeStatusError)).toBe(false) // Very large status code const largeStatusError = { response: { status: 999999 }, } as AxiosError expect(isAuthError(largeStatusError)).toBe(false) // Float status code (should still work if it's 401.0 or 403.0) const floatStatus401 = { response: { status: 401.0 }, } as AxiosError expect(isAuthError(floatStatus401)).toBe(true) const floatStatus403 = { response: { status: 403.0 }, } as AxiosError expect(isAuthError(floatStatus403)).toBe(true) // Float status code that's not exactly 401 or 403 const floatStatusOther = { response: { status: 401.5 }, } as AxiosError expect(isAuthError(floatStatusOther)).toBe(false) }) }) }) describe("StaticAuthProvider", () => { it("should return provided headers", async () => { const headers = { Authorization: "Bearer token123", "X-API-Key": "key456" } const provider = new StaticAuthProvider(headers) const result = await provider.getAuthHeaders() expect(result).toEqual(headers) }) it("should return empty object when no headers provided", async () => { const provider = new StaticAuthProvider() const result = await provider.getAuthHeaders() expect(result).toEqual({}) }) it("should return copy of headers (not reference)", async () => { const headers = { Authorization: "Bearer token123" } const provider = new StaticAuthProvider(headers) const result = await provider.getAuthHeaders() result["X-Modified"] = "test" const result2 = await provider.getAuthHeaders() expect(result2).toEqual(headers) expect(result2).not.toHaveProperty("X-Modified") }) it("should always return false for handleAuthError", async () => { const provider = new StaticAuthProvider() const error = { response: { status: 401 } } as AxiosError const result = await provider.handleAuthError(error) expect(result).toBe(false) }) // Edge case tests for StaticAuthProvider constructor describe("Constructor Edge Cases", () => { it("should handle null passed to constructor", () => { // Note: TypeScript may prevent this, but testing runtime behavior const provider = new StaticAuthProvider(null as unknown as Record<string, string>) // Should not throw during construction expect(provider).toBeInstanceOf(StaticAuthProvider) }) it("should handle null headers gracefully in getAuthHeaders", async () => { const provider = new StaticAuthProvider(null as unknown as Record<string, string>) // Should handle null gracefully and return empty object or throw meaningful error await expect(async () => { const result = await provider.getAuthHeaders() // If it doesn't throw, it should return an object (likely empty) expect(typeof result).toBe("object") expect(result).not.toBeNull() }).not.toThrow() }) it("should handle undefined passed to constructor", () => { const provider = new StaticAuthProvider(undefined as unknown as Record<string, string>) expect(provider).toBeInstanceOf(StaticAuthProvider) }) it("should handle undefined headers gracefully in getAuthHeaders", async () => { const provider = new StaticAuthProvider(undefined as unknown as Record<string, string>) const result = await provider.getAuthHeaders() expect(typeof result).toBe("object") expect(result).not.toBeNull() }) it("should handle non-object values passed to constructor", () => { // Testing with various non-object types const stringProvider = new StaticAuthProvider( "not an object" as unknown as Record<string, string>, ) expect(stringProvider).toBeInstanceOf(StaticAuthProvider) const numberProvider = new StaticAuthProvider(123 as unknown as Record<string, string>) expect(numberProvider).toBeInstanceOf(StaticAuthProvider) const booleanProvider = new StaticAuthProvider(true as unknown as Record<string, string>) expect(booleanProvider).toBeInstanceOf(StaticAuthProvider) }) it("should handle non-object headers gracefully in getAuthHeaders", async () => { const stringProvider = new StaticAuthProvider( "not an object" as unknown as Record<string, string>, ) // Should either throw a meaningful error or handle gracefully await expect(async () => { const result = await stringProvider.getAuthHeaders() expect(typeof result).toBe("object") }).not.toThrow() }) it("should handle headers with non-string values", async () => { const headersWithMixedTypes = { Authorization: "Bearer token123", "X-Numeric": 123, "X-Boolean": true, "X-Null": null, "X-Undefined": undefined, "X-Object": { nested: "value" }, } as unknown as Record<string, string> const provider = new StaticAuthProvider(headersWithMixedTypes) const result = await provider.getAuthHeaders() expect(typeof result).toBe("object") expect(result).not.toBeNull() // Should preserve string values expect(result["Authorization"]).toBe("Bearer token123") // Behavior for non-string values depends on implementation // At minimum, it should not crash }) it("should handle empty object as headers", async () => { const provider = new StaticAuthProvider({}) const result = await provider.getAuthHeaders() expect(result).toEqual({}) }) it("should handle headers with special characters and unicode", async () => { const headersWithSpecialChars = { "X-Special-Chars": "!@#$%^&*()_+-=[]{}|;':\",./<>?", "X-Unicode": "🚀 Hello 世界 café naïve résumé", "X-Empty-String": "", "X-Whitespace": " \t\n\r ", } const provider = new StaticAuthProvider(headersWithSpecialChars) const result = await provider.getAuthHeaders() expect(result).toEqual(headersWithSpecialChars) }) }) }) describe("Custom AuthProvider Implementation", () => { class MockAuthProvider implements AuthProvider { private isValid = true private retryCount = 0 async getAuthHeaders(): Promise<Record<string, string>> { if (!this.isValid) { throw new Error("Token expired") } return { Authorization: "Bearer valid-token" } } // eslint-disable-next-line @typescript-eslint/no-unused-vars async handleAuthError(_error: AxiosError): Promise<boolean> { this.retryCount++ if (this.retryCount === 1) { // First auth error - refresh token and retry this.isValid = true return true } // Second auth error - give up return false } // Test helper methods expireToken(): void { this.isValid = false } getRetryCount(): number { return this.retryCount } } it("should provide valid headers when token is valid", async () => { const provider = new MockAuthProvider() const headers = await provider.getAuthHeaders() expect(headers).toEqual({ Authorization: "Bearer valid-token" }) }) it("should throw error when token is expired", async () => { const provider = new MockAuthProvider() provider.expireToken() await expect(provider.getAuthHeaders()).rejects.toThrow("Token expired") }) it("should handle auth errors with retry logic", async () => { const provider = new MockAuthProvider() const error = { response: { status: 401 } } as AxiosError // First call should return true (retry) const shouldRetry1 = await provider.handleAuthError(error) expect(shouldRetry1).toBe(true) expect(provider.getRetryCount()).toBe(1) // Second call should return false (don't retry) const shouldRetry2 = await provider.handleAuthError(error) expect(shouldRetry2).toBe(false) expect(provider.getRetryCount()).toBe(2) }) }) })

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/ivo-toby/mcp-openapi-server'

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