Skip to main content
Glama

LLM Researcher

by Code-Hex
search-exceptions.test.ts3.62 kB
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { DuckDuckGoSearcher } from '@/search.js'; import { RatelimitException, DuckDuckGoSearchException } from '@/types.js'; // Mock fetch globally const mockFetch = vi.fn(); global.fetch = mockFetch; describe('DuckDuckGoSearcher Exception Handling', () => { let searcher: DuckDuckGoSearcher; beforeEach(() => { searcher = new DuckDuckGoSearcher(); vi.clearAllMocks(); }); afterEach(() => { vi.restoreAllMocks(); }); describe('Rate limit exceptions', () => { const rateLimitCodes = [202, 301, 403, 400, 429, 418]; rateLimitCodes.forEach(statusCode => { it(`should throw RatelimitException for status code ${statusCode}`, async () => { mockFetch.mockResolvedValueOnce({ status: statusCode, statusText: `Status ${statusCode}`, text: vi.fn().mockResolvedValue('<html></html>'), ok: false, }); await expect(searcher.search('test query')).rejects.toThrow(RatelimitException); }); }); }); describe('DuckDuckGo search exceptions', () => { const errorCodes = [404, 500, 502, 503, 504, 422, 401]; errorCodes.forEach(statusCode => { it(`should retry and eventually throw for status code ${statusCode}`, async () => { // Mock multiple failed responses (will be retried) mockFetch.mockResolvedValue({ status: statusCode, statusText: `Status ${statusCode}`, text: vi.fn().mockResolvedValue('<html></html>'), ok: false, }); await expect(searcher.search('test query')).rejects.toThrow('Search failed after'); }); }); }); describe('Successful requests', () => { it('should not throw exceptions for status code 200', async () => { const mockHtml = ` <html> <body> <div id="links" class="results"> <div class="result"> <h2 class="result__title"> <a class="result__a" href="/l/?uddg=https%3A//example.com">Test Result</a> </h2> <div class="result__snippet">Test snippet</div> </div> </div> </body> </html> `; mockFetch.mockResolvedValueOnce({ status: 200, statusText: 'OK', text: vi.fn().mockResolvedValue(mockHtml), ok: true, }); const result = await searcher.search('test query'); expect(result).toBeDefined(); expect(result.results).toBeDefined(); expect(Array.isArray(result.results)).toBe(true); }); }); describe('Exception inheritance', () => { it('RatelimitException should inherit from Error', () => { const exception = new RatelimitException(429); expect(exception).toBeInstanceOf(Error); expect(exception.name).toBe('RatelimitException'); expect(exception.statusCode).toBe(429); }); it('DuckDuckGoSearchException should inherit from Error', () => { const exception = new DuckDuckGoSearchException(500); expect(exception).toBeInstanceOf(Error); expect(exception.name).toBe('DuckDuckGoSearchException'); expect(exception.statusCode).toBe(500); }); it('should allow custom error messages', () => { const rateLimit = new RatelimitException(429, 'Custom rate limit message'); expect(rateLimit.message).toBe('Custom rate limit message'); const searchError = new DuckDuckGoSearchException(500, 'Custom search error'); expect(searchError.message).toBe('Custom search 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/Code-Hex/light-research-mcp'

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