Skip to main content
Glama
2389-research

MCP Agent Social Media Server

api-client.test.ts7.92 kB
// ABOUTME: Tests for ApiClient functionality with mocked fetch // ABOUTME: Uses dependency injection to mock HTTP requests in tests only import { jest } from '@jest/globals'; import { ApiClient, type FetchFunction } from '../src/api-client'; import type { PostData, PostQueryOptions } from '../src/types'; // Mock Response type for testing type MockResponse = { ok: boolean; status: number; statusText: string; json: jest.MockedFunction<() => Promise<unknown>>; }; describe('ApiClient', () => { let apiClient: ApiClient; let mockFetch: jest.MockedFunction<FetchFunction>; const baseUrl = 'https://api.test.com'; const apiKey = 'test-api-key'; beforeEach(() => { // Create a mocked fetch function mockFetch = jest.fn() as jest.MockedFunction<FetchFunction>; // Create API client with mocked fetch apiClient = new ApiClient(baseUrl, apiKey, 30000, mockFetch); }); afterEach(() => { jest.clearAllMocks(); }); describe('fetchPosts', () => { it('should fetch posts successfully', async () => { // Mock response in remote API format const mockRemoteResponse = { posts: [ { postId: 'post-1', teamId: 'test-team', author: 'test-user', content: 'Test content', tags: ['test'], createdAt: { _seconds: 1672531200, _nanoseconds: 0 }, // 2023-01-01T00:00:00Z parentPostId: null, }, ], nextOffset: null, }; // Expected result after schema adaptation const expectedResult = { posts: [ { id: 'post-1', team_name: 'test-team', author_name: 'test-user', content: 'Test content', tags: ['test'], timestamp: '2023-01-01T00:00:00.000Z', }, ], total: 1, has_more: false, }; mockFetch.mockResolvedValueOnce({ ok: true, status: 200, statusText: 'OK', json: jest.fn().mockResolvedValue(mockRemoteResponse), } as MockResponse); const result = await apiClient.fetchPosts('test-team'); expect(result).toEqual(expectedResult); expect(mockFetch).toHaveBeenCalledWith( `${baseUrl}/teams/test-team/posts`, expect.objectContaining({ method: 'GET', headers: expect.objectContaining({ 'x-api-key': apiKey, }), }), ); }); it('should include query parameters when provided', async () => { const options: PostQueryOptions = { limit: 5, agent_filter: 'test-agent', tag_filter: 'test-tag', }; mockFetch.mockResolvedValueOnce({ ok: true, status: 200, statusText: 'OK', json: jest.fn().mockResolvedValue({ posts: [], total: 0, has_more: false }), } as MockResponse); await apiClient.fetchPosts('test-team', options); expect(mockFetch).toHaveBeenCalledWith( expect.stringContaining('limit=5&agent=test-agent&tag=test-tag'), expect.any(Object), ); }); it('should handle authentication errors', async () => { mockFetch.mockResolvedValueOnce({ ok: false, status: 401, statusText: 'Unauthorized', json: jest.fn().mockResolvedValue({ error: 'Invalid API key' }), } as MockResponse); await expect(apiClient.fetchPosts('test-team')).rejects.toThrow( 'Authentication failed: Invalid API key', ); }); it('should handle network errors', async () => { mockFetch.mockRejectedValueOnce(new Error('Network error')); await expect(apiClient.fetchPosts('test-team')).rejects.toThrow('Network error'); }); }); describe('createPost', () => { it('should create post successfully', async () => { const postData: PostData = { author_name: 'test-user', content: 'Test post content', tags: ['test'], }; // Mock response in remote API format const mockRemoteResponse = { postId: 'new-post-1', teamId: 'test-team', author: postData.author_name, content: postData.content, tags: postData.tags, createdAt: { _seconds: 1672531200, _nanoseconds: 0 }, // 2023-01-01T00:00:00Z parentPostId: null, }; // Expected result after schema adaptation const expectedResult = { post: { id: 'new-post-1', team_name: 'test-team', author_name: 'test-user', content: 'Test post content', tags: ['test'], timestamp: '2023-01-01T00:00:00.000Z', }, }; mockFetch.mockResolvedValueOnce({ ok: true, status: 201, statusText: 'Created', json: jest.fn().mockResolvedValue(mockRemoteResponse), } as MockResponse); const result = await apiClient.createPost('test-team', postData); expect(result).toEqual(expectedResult); expect(mockFetch).toHaveBeenCalledWith( `${baseUrl}/teams/test-team/posts`, expect.objectContaining({ method: 'POST', headers: expect.objectContaining({ 'x-api-key': apiKey, 'Content-Type': 'application/json', }), body: JSON.stringify({ author: postData.author_name, content: postData.content, tags: postData.tags, parentPostId: postData.parent_post_id, }), }), ); }); it('should handle validation errors', async () => { const postData: PostData = { author_name: '', content: '', tags: [], }; mockFetch.mockResolvedValueOnce({ ok: false, status: 422, statusText: 'Unprocessable Entity', json: jest.fn().mockResolvedValue({ error: 'Validation failed' }), } as MockResponse); await expect(apiClient.createPost('test-team', postData)).rejects.toThrow( 'Validation failed', ); }); it('should handle rate limiting', async () => { const postData: PostData = { author_name: 'test-user', content: 'Test content', tags: [], }; mockFetch.mockResolvedValueOnce({ ok: false, status: 429, statusText: 'Too Many Requests', json: jest.fn().mockResolvedValue({ error: 'Rate limit exceeded' }), } as MockResponse); await expect(apiClient.createPost('test-team', postData)).rejects.toThrow( 'Rate limit exceeded: Rate limit exceeded', ); }); }); describe('error handling', () => { it('should handle server errors', async () => { mockFetch.mockResolvedValueOnce({ ok: false, status: 500, statusText: 'Internal Server Error', json: jest.fn().mockResolvedValue({ error: 'Server error' }), } as MockResponse); await expect(apiClient.fetchPosts('test-team')).rejects.toThrow('Server error: Server error'); }); it('should handle malformed JSON responses', async () => { mockFetch.mockResolvedValueOnce({ ok: false, status: 400, statusText: 'Bad Request', json: jest.fn().mockRejectedValue(new Error('Invalid JSON')), } as MockResponse); await expect(apiClient.fetchPosts('test-team')).rejects.toThrow( 'API request failed: 400 Bad Request', ); }); }); describe('constructor', () => { it('should create an ApiClient with default fetch', () => { // Test that we can create without providing fetch (uses real fetch in production) const defaultClient = new ApiClient(baseUrl, apiKey, 30000); expect(defaultClient).toBeInstanceOf(ApiClient); }); it('should create an ApiClient with custom fetch for testing', () => { expect(apiClient).toBeInstanceOf(ApiClient); }); }); });

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/2389-research/mcp-socialmedia'

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