UNPKG

@supadata/mcp

Version:

MCP server for Supadata video & web scraping integration. Features include YouTube, TikTok, Instagram, Twitter, and file video transcription, web scraping, batch processing and structured data extraction.

296 lines (295 loc) 11.4 kB
import { describe, expect, jest, test, beforeEach, afterEach, } from '@jest/globals'; // Mock Supadata jest.mock('@supadata/js'); describe('Supadata Tool Tests', () => { let mockClient; let requestHandler; beforeEach(() => { jest.clearAllMocks(); // Create mock client with Jest mock functions const mockTranscriptFn = jest.fn(); mockTranscriptFn.getJobStatus = jest.fn(); mockClient = { transcript: mockTranscriptFn, web: { scrape: jest.fn(), map: jest.fn(), crawl: jest.fn(), getCrawlResults: jest.fn(), } }; // Create request handler requestHandler = async (request) => { const { name, arguments: args } = request.params; if (!args) { throw new Error('No arguments provided'); } return handleRequest(name, args, mockClient); }; }); afterEach(() => { jest.clearAllMocks(); }); // Test transcript functionality test('should handle transcript request', async () => { const url = 'https://youtube.com/watch?v=example'; const options = { lang: 'en', text: false }; const mockResponse = 'Transcript content here'; mockClient.transcript.mockResolvedValueOnce(mockResponse); const response = await requestHandler({ method: 'call_tool', params: { name: 'supadata_transcript', arguments: { url, ...options }, }, }); expect(response).toEqual({ content: [{ type: 'text', text: 'Transcript content here' }], isError: false, }); expect(mockClient.transcript).toHaveBeenCalledWith({ url, lang: 'en', text: false }); }); // Test transcript with job ID response test('should handle transcript request with job ID', async () => { const url = 'https://youtube.com/watch?v=example'; const mockResponse = { jobId: 'test-transcript-job-id' }; mockClient.transcript.mockResolvedValueOnce(mockResponse); const response = await requestHandler({ method: 'call_tool', params: { name: 'supadata_transcript', arguments: { url }, }, }); expect(response.isError).toBe(false); expect(response.content[0].text).toContain('test-transcript-job-id'); expect(mockClient.transcript).toHaveBeenCalledWith({ url }); }); // Test check transcript status functionality test('should handle transcript status request', async () => { const id = 'test-transcript-id'; const mockStatusResponse = { status: 'completed', result: 'Full transcript content here' }; mockClient.transcript.getJobStatus.mockResolvedValueOnce(mockStatusResponse); const response = await requestHandler({ method: 'call_tool', params: { name: 'supadata_check_transcript_status', arguments: { id }, }, }); expect(response.isError).toBe(false); expect(response.content[0].text).toContain('completed'); expect(mockClient.transcript.getJobStatus).toHaveBeenCalledWith(id); }); // Test scrape functionality test('should handle scrape request', async () => { const url = 'https://example.com'; const options = { formats: ['markdown'] }; const mockResponse = '# Test Content'; mockClient.web.scrape.mockResolvedValueOnce(mockResponse); const response = await requestHandler({ method: 'call_tool', params: { name: 'supadata_scrape', arguments: { url, ...options }, }, }); expect(response).toEqual({ content: [{ type: 'text', text: '# Test Content' }], isError: false, }); expect(mockClient.web.scrape).toHaveBeenCalledWith(url); }); // Test map functionality test('should handle map request', async () => { const url = 'https://example.com'; const mockMapResponse = ['https://example.com/page1', 'https://example.com/page2']; mockClient.web.map.mockResolvedValueOnce(mockMapResponse); const response = await requestHandler({ method: 'call_tool', params: { name: 'supadata_map', arguments: { url }, }, }); expect(response.isError).toBe(false); expect(response.content[0].text).toContain('https://example.com/page1'); expect(mockClient.web.map).toHaveBeenCalledWith(url); }); // Test check crawl status functionality test('should handle crawl status request', async () => { const id = 'test-crawl-id'; const mockStatusResponse = { status: 'completed', data: ['# Page 1 Content', '# Page 2 Content'] }; mockClient.web.getCrawlResults.mockResolvedValueOnce(mockStatusResponse); const response = await requestHandler({ method: 'call_tool', params: { name: 'supadata_check_crawl_status', arguments: { id }, }, }); expect(response.isError).toBe(false); expect(response.content[0].text).toContain('completed'); expect(mockClient.web.getCrawlResults).toHaveBeenCalledWith(id); }); // Test crawl functionality test('should handle crawl request', async () => { const url = 'https://example.com'; const options = { maxDepth: 2 }; mockClient.web.crawl.mockResolvedValueOnce({ jobId: 'test-crawl-id', }); const response = await requestHandler({ method: 'call_tool', params: { name: 'supadata_crawl', arguments: { url, ...options }, }, }); expect(response.isError).toBe(false); expect(response.content[0].text).toContain('test-crawl-id'); expect(mockClient.web.crawl).toHaveBeenCalledWith({ url, limit: 10, }); }); // Test error handling test('should handle API errors', async () => { const url = 'https://example.com'; mockClient.web.scrape.mockRejectedValueOnce(new Error('API Error')); const response = await requestHandler({ method: 'call_tool', params: { name: 'supadata_scrape', arguments: { url }, }, }); expect(response.isError).toBe(true); expect(response.content[0].text).toContain('API Error'); }); // Test rate limiting test('should handle rate limits', async () => { const url = 'https://example.com'; // Mock rate limit error mockClient.web.scrape.mockRejectedValueOnce(new Error('rate limit exceeded')); const response = await requestHandler({ method: 'call_tool', params: { name: 'supadata_scrape', arguments: { url }, }, }); expect(response.isError).toBe(true); expect(response.content[0].text).toContain('rate limit exceeded'); }); }); // Helper function to simulate request handling async function handleRequest(name, args, client) { try { switch (name) { case 'supadata_transcript': { const options = { url: args.url }; if (args.lang) options.lang = args.lang; if (args.text !== undefined) options.text = args.text; if (args.chunkSize) options.chunkSize = args.chunkSize; if (args.mode) options.mode = args.mode; const response = await client.transcript(options); // Check if response contains a job ID (async processing) if (typeof response === 'object' && response !== null && 'jobId' in response) { const jobId = response.jobId; return { content: [ { type: 'text', text: `Started transcript job for ${args.url} with job ID: ${jobId}. Use supadata_check_transcript_status to check progress.`, }, ], isError: false, }; } return { content: [ { type: 'text', text: typeof response === 'string' ? response : JSON.stringify(response) }, ], isError: false, }; } case 'supadata_check_transcript_status': { const response = await client.transcript.getJobStatus(args.id); return { content: [ { type: 'text', text: JSON.stringify(response, null, 2) }, ], isError: false, }; } case 'supadata_scrape': { const response = await client.web.scrape(args.url); return { content: [ { type: 'text', text: typeof response === 'string' ? response : JSON.stringify(response) }, ], isError: false, }; } case 'supadata_map': { const response = await client.web.map(args.url); const urls = Array.isArray(response) ? response : [response]; return { content: [ { type: 'text', text: urls.join('\n') }, ], isError: false, }; } case 'supadata_crawl': { const response = await client.web.crawl({ url: args.url, limit: args.limit || 10, }); const jobId = response.jobId || response; return { content: [ { type: 'text', text: `Started crawl for ${args.url} with job ID: ${jobId}. Use supadata_check_crawl_status to check progress.`, }, ], isError: false, }; } case 'supadata_check_crawl_status': { const response = await client.web.getCrawlResults(args.id); return { content: [ { type: 'text', text: JSON.stringify(response, null, 2) }, ], isError: false, }; } default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { return { content: [ { type: 'text', text: error instanceof Error ? error.message : String(error), }, ], isError: true, }; } }