Skip to main content
Glama

News Aggregator API

news-routes-reliable.test.ts9.96 kB
/** * Reliable tests for News API endpoints * This implementation properly handles server lifecycle and mocks database */ import { describe, test, expect, beforeAll, afterAll, beforeEach, jest } from '@jest/globals'; import request from 'supertest'; import { Express } from 'express'; import { McpServer } from '../../server'; import { cacheService } from '../../utils/cache'; // Import axios to have its type information import axios from 'axios'; // Mock axios for external API calls jest.mock('axios'); const mockedAxios = jest.requireMock('axios') as jest.Mocked<typeof axios>; // Mock cache service to prevent side effects jest.mock('../../utils/cache', () => { const mockCache = new Map(); return { cacheService: { get: jest.fn().mockImplementation((key) => mockCache.get(key)), set: jest.fn().mockImplementation((key, value, ttl) => mockCache.set(key, value)), clear: jest.fn().mockImplementation(() => mockCache.clear()), getStats: jest.fn().mockReturnValue({ hits: 0, misses: 0, keys: 0 }), getTTL: jest.fn().mockReturnValue(300) } }; }); // Mock database connection jest.mock('../../utils/db', () => { return { connectDb: jest.fn().mockResolvedValue(undefined), disconnectDb: jest.fn().mockResolvedValue(undefined), prisma: { article: { findMany: jest.fn().mockResolvedValue([ { id: 1, uuid: '123e4567-e89b-12d3-a456-426614174001', title: 'Test Article 1', description: 'Test description 1', content: 'Test content 1', url: 'http://example.com/1', image_url: 'http://example.com/image1.jpg', source: 'Test Source', categories: ['general'], published_at: new Date().toISOString(), created_at: new Date().toISOString(), updated_at: new Date().toISOString() }, { id: 2, uuid: '123e4567-e89b-12d3-a456-426614174002', title: 'Test Article 2', description: 'Test description 2', content: 'Test content 2', url: 'http://example.com/2', image_url: 'http://example.com/image2.jpg', source: 'Test Source', categories: ['technology'], published_at: new Date().toISOString(), created_at: new Date().toISOString(), updated_at: new Date().toISOString() } ]), findUnique: jest.fn().mockImplementation((args) => { if (args?.where?.uuid === '123e4567-e89b-12d3-a456-426614174001') { return Promise.resolve({ id: 1, uuid: '123e4567-e89b-12d3-a456-426614174001', title: 'Test Article 1', description: 'Test description 1', content: 'Test content 1', url: 'http://example.com/1', image_url: 'http://example.com/image1.jpg', source: 'Test Source', categories: ['general'], published_at: new Date().toISOString(), created_at: new Date().toISOString(), updated_at: new Date().toISOString() }); } return Promise.resolve(null); }), count: jest.fn().mockResolvedValue(2) } } }; }); describe('News API Routes', () => { let app: Express; let server: McpServer; let mockPort: number; // Setup before all tests beforeAll(async () => { // Use a random port to avoid conflicts mockPort = Math.floor(Math.random() * 10000) + 20000; // Store original PORT const originalPort = process.env.PORT; // Set PORT environment variable to our random port process.env.PORT = mockPort.toString(); try { // Create new server instance server = new McpServer(mockPort); // Get Express app for testing app = server.getApp(); // Start server without actually binding to port (we'll use supertest) jest.spyOn(server, 'start').mockImplementation(async () => { // This is a mock implementation that doesn't actually start the server // but simulates the successful server start return Promise.resolve(); }); // Call start to initialize database connection await server.start(); // Restore original PORT if (originalPort) { process.env.PORT = originalPort; } else { delete process.env.PORT; } } catch (error) { console.error('Error in test setup:', error); throw error; } }); // Cleanup after all tests afterAll(async () => { try { if (server) { await server.shutdown(); } } catch (error) { console.error('Error in test teardown:', error); } }); // Reset mocks and clear cache before each test beforeEach(() => { cacheService.clear(); mockedAxios.get.mockReset(); // Define mock article for UUID test const testArticle = { id: 1, uuid: '123e4567-e89b-12d3-a456-426614174001', title: 'Test Article 1', description: 'Test description 1', content: 'Test content 1', url: 'http://example.com/1', image_url: 'http://example.com/image1.jpg', source: 'Test Source', categories: ['general'], published_at: new Date().toISOString(), created_at: new Date().toISOString(), updated_at: new Date().toISOString() }; // Set up default mock response for article by UUID mockedAxios.get.mockImplementation((url: string, config?: any) => { if (url.includes('123e4567-e89b-12d3-a456-426614174001')) { return Promise.resolve({ status: 200, statusText: 'OK', headers: {}, config: config || {}, data: { data: testArticle } }); } else if (url.includes('00000000-0000-0000-0000-000000000000')) { // For non-existent article, simulate a 404 error const error = new Error('Article not found - 404'); (error as any).response = { status: 404 }; return Promise.reject(error); } else { // Default response for other URLs (e.g., top news, all news) return Promise.resolve({ status: 200, statusText: 'OK', headers: {}, config: config || {}, data: { data: [testArticle], meta: { found: 1, returned: 1, page: 1, total_pages: 1 } } }); } }); }); // Test GET /api/news/top endpoint test('GET /api/news/top should return top news articles', async () => { const response = await request(app) .get('/api/news/top') .expect('Content-Type', /json/) .expect(200); // Verify response structure with nested data expect(response.body.success).toBe(true); expect(response.body.data).toBeDefined(); expect(response.body.data.data).toBeDefined(); expect(Array.isArray(response.body.data.data)).toBe(true); expect(response.body.data.data.length).toBeGreaterThan(0); // Check first article structure const article = response.body.data.data[0]; expect(article).toHaveProperty('title'); expect(article).toHaveProperty('source'); expect(article).toHaveProperty('uuid'); }); // Test filtering by category test('GET /api/news/top should filter by category', async () => { const response = await request(app) .get('/api/news/top?category=technology') .expect('Content-Type', /json/) .expect(200); // Verify response structure expect(response.body.success).toBe(true); expect(response.body.data).toBeDefined(); expect(response.body.data.data).toBeDefined(); expect(Array.isArray(response.body.data.data)).toBe(true); }); // Test /api/news/all endpoint with pagination test('GET /api/news/all should return paginated articles', async () => { const response = await request(app) .get('/api/news/all?page=1&limit=10') .expect('Content-Type', /json/) .expect(200); // Verify response structure with nested data expect(response.body.success).toBe(true); expect(response.body.data).toBeDefined(); expect(response.body.data.data).toBeDefined(); expect(Array.isArray(response.body.data.data)).toBe(true); // Check pagination metadata expect(response.body.data.meta).toBeDefined(); expect(response.body.data.meta).toHaveProperty('found'); expect(response.body.data.meta).toHaveProperty('returned'); expect(response.body.data.meta).toHaveProperty('page'); }); // Test article retrieval by UUID test('GET /api/news/uuid/:uuid should return a specific article', async () => { const testUuid = '123e4567-e89b-12d3-a456-426614174001'; const response = await request(app) .get(`/api/news/uuid/${testUuid}`) .expect('Content-Type', /json/) .expect(200); // Verify response structure with nested data expect(response.body.success).toBe(true); expect(response.body.data).toBeDefined(); expect(response.body.data.data).toBeDefined(); expect(response.body.data.data).toHaveProperty('uuid', testUuid); expect(response.body.data.data).toHaveProperty('title', 'Test Article 1'); }); // Test 404 for non-existent article test('GET /api/news/uuid/:uuid should return 404 for non-existent article', async () => { // Use a properly formatted UUID that doesn't exist const nonExistentUuid = '00000000-0000-0000-0000-000000000000'; const response = await request(app) .get(`/api/news/uuid/${nonExistentUuid}`) .expect('Content-Type', /json/) .expect(404); // Verify response structure expect(response.body.success).toBe(false); expect(response.body.error).toBeDefined(); }); });

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/Malachi-devel/the-news-api-mcp-server'

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