Beyond MCP Server

import { FarcasterProvider } from '../../../../src/providers/farcaster/farcasterProvider'; import { mockNeynarClient } from '../../../mocks/neynarClient.mock'; // Mock the Neynar client jest.mock('@neynar/nodejs-sdk', () => { return { Configuration: jest.fn().mockImplementation(() => ({})), NeynarAPIClient: jest.fn().mockImplementation(() => mockNeynarClient), FeedType: { Filter: 'filter' }, FilterType: { GlobalTrending: 'global_trending' } }; }); // Mock the config jest.mock('../../../../src/config', () => ({ providers: { farcaster: { enabled: true, neynarApiKey: 'test-api-key' } } })); describe('FarcasterProvider', () => { let provider: FarcasterProvider; beforeEach(() => { // Reset all mocks before each test jest.clearAllMocks(); provider = new FarcasterProvider(); }); describe('isAvailable', () => { it('should return true when the API is available', async () => { // Mock a successful API call mockNeynarClient.fetchBulkUsers.mockResolvedValueOnce({ users: [{ fid: 1 }] }); const result = await provider.isAvailable(); expect(result).toBe(true); expect(mockNeynarClient.fetchBulkUsers).toHaveBeenCalledWith({ fids: [1] }); }); it('should return false when the API is not available', async () => { // Mock a failed API call mockNeynarClient.fetchBulkUsers.mockRejectedValueOnce(new Error('API error')); const result = await provider.isAvailable(); expect(result).toBe(false); expect(mockNeynarClient.fetchBulkUsers).toHaveBeenCalledWith({ fids: [1] }); }); }); describe('searchContent', () => { it('should return search results for a regular query', async () => { const query = 'neynar'; const result = await provider.searchContent(query); expect(result).toBeDefined(); expect(result).toHaveLength(1); expect(result[0].text).toContain('Neynar API'); expect(mockNeynarClient.searchCasts).toHaveBeenCalledWith({ q: query, limit: 20 }); }); it('should handle a query with no results', async () => { const query = 'nonexistent'; const result = await provider.searchContent(query); expect(result).toBeDefined(); expect(result).toHaveLength(0); expect(mockNeynarClient.searchCasts).toHaveBeenCalledWith({ q: query, limit: 20 }); }); it('should handle a from: query to search for a specific user', async () => { const query = 'from:rish'; const result = await provider.searchContent(query); expect(result).toBeDefined(); expect(result).toHaveLength(0); // The implementation returns empty array for from: queries expect(mockNeynarClient.searchUser).toHaveBeenCalledWith({ q: 'rish', limit: 1 }); }); it('should handle a from: query with a non-existent user', async () => { const query = 'from:nonexistent'; const result = await provider.searchContent(query); expect(result).toBeDefined(); expect(result).toHaveLength(0); expect(mockNeynarClient.searchUser).toHaveBeenCalledWith({ q: 'nonexistent', limit: 1 }); }); it('should respect the limit option', async () => { const query = 'neynar'; const limit = 5; await provider.searchContent(query, { limit }); expect(mockNeynarClient.searchCasts).toHaveBeenCalledWith({ q: query, limit }); }); }); describe('getThread', () => { it('should return a thread when it exists', async () => { const threadId = '0xfe512114e8a7c6b23c51c66c318f8a9a548cfb07'; // Mock the lookupCastByHashOrWarpcastUrl response mockNeynarClient.lookupCastByHashOrWarpcastUrl.mockResolvedValueOnce({ cast: { hash: '0xfe512114e8a7c6b23c51c66c318f8a9a548cfb07', author: { fid: 194, username: 'rish', display_name: 'rish' }, text: 'adding more v2 frame related tooling', timestamp: '2025-02-12T16:00:21.000Z', reactions: { likes_count: 67, recasts_count: 12 }, replies: { count: 10 } } }); // Mock the conversation response mockNeynarClient.lookupCastConversation.mockResolvedValueOnce({ conversation: { cast: { direct_replies: [] } } }); const result = await provider.getThread(threadId); expect(result).toBeDefined(); expect(result.id).toBe(threadId); expect(result.replies).toHaveLength(0); expect(mockNeynarClient.lookupCastByHashOrWarpcastUrl).toHaveBeenCalledWith({ identifier: threadId, type: 'hash' }); }); it('should handle a thread that does not exist', async () => { const threadId = '0xnonexistent'; // Mock the error for a non-existent thread mockNeynarClient.lookupCastByHashOrWarpcastUrl.mockRejectedValueOnce(new Error('Cast not found')); mockNeynarClient.lookupCastByHashOrWarpcastUrl.mockRejectedValueOnce(new Error('Cast not found')); const result = await provider.getThread(threadId); expect(result).toBeDefined(); expect(result.id).toBe(threadId); expect(result.content.text).toContain('Thread not found'); expect(result.replies).toHaveLength(0); }); }); describe('getUserProfile', () => { it('should return a user profile when the user exists (by username)', async () => { const userId = 'rish'; // Mock the searchUser response mockNeynarClient.searchUser.mockResolvedValueOnce({ result: { users: [ { fid: 194, username: 'rish', display_name: 'rish', profile: { bio: { text: 'building farcaster infra @ /neynar 🪐 casting @ /rish' } }, follower_count: 264665, following_count: 839, verifications: ['0x5a927ac639636e534b678e81768ca19e2c6280b7'] } ] } }); const result = await provider.getUserProfile(userId); expect(result).toBeDefined(); expect(result.username).toBe('rish'); expect(mockNeynarClient.searchUser).toHaveBeenCalledWith({ q: 'rish', limit: 1 }); }); it('should return a user profile when the user exists (by FID)', async () => { const userId = '194'; const result = await provider.getUserProfile(userId); expect(result).toBeDefined(); expect(result.username).toBe('rish'); expect(mockNeynarClient.fetchBulkUsers).toHaveBeenCalledWith({ fids: [194] }); }); it('should handle a user that does not exist', async () => { const userId = 'nonexistent'; // Mock the searchUser response for a non-existent user mockNeynarClient.searchUser.mockResolvedValueOnce({ result: { users: [] } }); await expect(provider.getUserProfile(userId)).rejects.toThrow('Failed to fetch profile'); }); }); describe('getUserContent', () => { it('should return user content when the user exists (by username)', async () => { const userId = 'rish'; // Mock the searchUser response mockNeynarClient.searchUser.mockResolvedValueOnce({ result: { users: [ { fid: 194, username: 'rish', display_name: 'rish' } ] } }); const result = await provider.getUserContent(userId); expect(result).toBeDefined(); expect(result).toHaveLength(0); // The implementation returns empty array expect(mockNeynarClient.searchUser).toHaveBeenCalledWith({ q: 'rish', limit: 1 }); expect(mockNeynarClient.fetchCastsForUser).toHaveBeenCalledWith({ fid: 194, limit: 20, cursor: undefined }); }); it('should return user content when the user exists (by FID)', async () => { const userId = '194'; const result = await provider.getUserContent(userId); expect(result).toBeDefined(); expect(result).toHaveLength(0); // The implementation returns empty array expect(mockNeynarClient.fetchCastsForUser).toHaveBeenCalledWith({ fid: 194, limit: 20, cursor: undefined }); }); it('should throw an error when the user does not exist', async () => { const userId = 'nonexistent'; // Mock the searchUser response for a non-existent user mockNeynarClient.searchUser.mockResolvedValueOnce({ result: { users: [] } }); // The implementation catches the error and returns an empty array const result = await provider.getUserContent(userId); expect(result).toEqual([]); }); it('should respect the limit option', async () => { const userId = '194'; const limit = 5; await provider.getUserContent(userId, { limit }); expect(mockNeynarClient.fetchCastsForUser).toHaveBeenCalledWith({ fid: 194, limit, cursor: undefined }); }); }); describe('getTrendingTopics', () => { it('should return trending topics', async () => { const result = await provider.getTrendingTopics(); expect(result).toBeDefined(); expect(result).toHaveLength(4); // The implementation returns 4 default topics expect(result).toContain('web3'); expect(result).toContain('crypto'); expect(mockNeynarClient.fetchFeed).toHaveBeenCalledWith({ feedType: 'filter', filterType: 'global_trending', limit: 25 }); }); it('should respect the limit option', async () => { const limit = 5; await provider.getTrendingTopics({ limit }); expect(mockNeynarClient.fetchFeed).toHaveBeenCalledWith({ feedType: 'filter', filterType: 'global_trending', limit: 25 }); }); }); });