Skip to main content
Glama
cameronsjo

MCP Server Template

by cameronsjo
retry.test.ts3.36 kB
/** * Tests for retry handler */ import { describe, it, expect, vi } from 'vitest'; import { withRetry, createRetryHandler, RetryableErrors } from './retry.js'; describe('withRetry', () => { it('should return result on first success', async () => { const fn = vi.fn().mockResolvedValue('success'); const result = await withRetry(fn); expect(result).toBe('success'); expect(fn).toHaveBeenCalledTimes(1); }); it('should retry on failure and succeed', async () => { const fn = vi .fn() .mockRejectedValueOnce(new Error('fail')) .mockResolvedValue('success'); const result = await withRetry(fn, { maxAttempts: 3, initialDelayMs: 10, }); expect(result).toBe('success'); expect(fn).toHaveBeenCalledTimes(2); }); it('should throw after max attempts', async () => { const fn = vi.fn().mockRejectedValue(new Error('always fail')); await expect( withRetry(fn, { maxAttempts: 3, initialDelayMs: 10, }) ).rejects.toThrow('always fail'); expect(fn).toHaveBeenCalledTimes(3); }); it('should not retry non-retryable errors', async () => { const fn = vi.fn().mockRejectedValue(new Error('not retryable')); await expect( withRetry(fn, { maxAttempts: 3, isRetryable: () => false, }) ).rejects.toThrow('not retryable'); expect(fn).toHaveBeenCalledTimes(1); }); it('should call onRetry callback', async () => { const fn = vi .fn() .mockRejectedValueOnce(new Error('fail')) .mockResolvedValue('success'); const onRetry = vi.fn(); await withRetry(fn, { maxAttempts: 3, initialDelayMs: 10, onRetry, }); expect(onRetry).toHaveBeenCalledTimes(1); expect(onRetry).toHaveBeenCalledWith(expect.any(Error), 1, expect.any(Number)); }); }); describe('createRetryHandler', () => { it('should create a reusable retry wrapper', async () => { const handler = createRetryHandler({ maxAttempts: 2, initialDelayMs: 10, }); const fn = vi.fn().mockResolvedValue('result'); const result = await handler(fn); expect(result).toBe('result'); }); }); describe('RetryableErrors', () => { describe('isNetworkError', () => { it('should detect network errors', () => { expect(RetryableErrors.isNetworkError(new Error('ECONNRESET'))).toBe(true); expect(RetryableErrors.isNetworkError(new Error('ETIMEDOUT'))).toBe(true); expect(RetryableErrors.isNetworkError(new Error('regular error'))).toBe(false); }); }); describe('isServerError', () => { it('should detect 5xx errors', () => { const err500 = Object.assign(new Error('Server error'), { status: 500 }); const err404 = Object.assign(new Error('Not found'), { status: 404 }); expect(RetryableErrors.isServerError(err500)).toBe(true); expect(RetryableErrors.isServerError(err404)).toBe(false); }); }); describe('isRateLimitError', () => { it('should detect 429 errors', () => { const err429 = Object.assign(new Error('Rate limited'), { status: 429 }); const err500 = Object.assign(new Error('Server error'), { status: 500 }); expect(RetryableErrors.isRateLimitError(err429)).toBe(true); expect(RetryableErrors.isRateLimitError(err500)).toBe(false); }); }); });

Latest Blog Posts

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/cameronsjo/mcp-server-template'

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