Skip to main content
Glama

News Aggregator API

news.controller.test.ts11.7 kB
/** * Unit tests for the NewsController * * These tests focus on controller behavior in isolation by properly * mocking dependencies and verifying correct request handling. */ import { describe, test, expect, beforeEach, jest, afterEach } from '@jest/globals'; import { Request, Response } from 'express'; import { cacheService } from '../../utils/cache'; // Create properly typed mock service functions const mockGetTopNews = jest.fn().mockImplementation((params: any) => Promise.resolve({ data: [], meta: { found: 0, returned: 0 } })); const mockGetAllNews = jest.fn().mockImplementation((params: any) => Promise.resolve({ data: [], meta: { found: 0, returned: 0 } })); const mockGetNewsByUuid = jest.fn().mockImplementation((uuid: string) => Promise.resolve({ data: { uuid } })); const mockGetNewsSources = jest.fn().mockImplementation((params: any) => Promise.resolve({ data: [], meta: { found: 0, returned: 0 } })); // Create a mock NewsService class before importing the controller const mockNewsServiceInstance = { getTopNews: mockGetTopNews, getAllNews: mockGetAllNews, getNewsByUuid: mockGetNewsByUuid, getNewsSources: mockGetNewsSources }; // Mock the entire news.service module jest.mock('../../services/news.service', () => { return { // When NewsController creates a new NewsService(), it will get our mockNewsServiceInstance NewsService: jest.fn().mockImplementation(() => mockNewsServiceInstance) }; }); // Now import the controller (after mocks are set up) import { NewsController } from '../../controllers/news.controller'; describe('NewsController', () => { // Test subjects let controller: NewsController; let req: Partial<Request>; let res: Partial<Response>; // Mock data const mockArticles = [ { uuid: 'test-uuid-1', title: 'Test Article 1', description: 'Test description 1', content: 'Test content 1', source: 'Test Source', url: 'https://example.com/article1', image_url: 'https://example.com/image1.jpg', categories: ['general'], published_at: '2025-05-01T12:00:00Z', created_at: '2025-05-01T12:00:00Z', updated_at: '2025-05-01T12:00:00Z' }, { uuid: 'test-uuid-2', title: 'Test Article 2', description: 'Test description 2', content: 'Test content 2', source: 'Test Source', url: 'https://example.com/article2', image_url: 'https://example.com/image2.jpg', categories: ['technology'], published_at: '2025-05-02T12:00:00Z', created_at: '2025-05-02T12:00:00Z', updated_at: '2025-05-02T12:00:00Z' } ]; const mockSources = [ { id: 1, name: 'Test Source 1', url: 'https://testsource1.com' }, { id: 2, name: 'Test Source 2', url: 'https://testsource2.com' } ]; // Setup before each test beforeEach(() => { // Clear mocks jest.clearAllMocks(); mockGetTopNews.mockReset(); mockGetAllNews.mockReset(); mockGetNewsByUuid.mockReset(); mockGetNewsSources.mockReset(); // Clear cache cacheService.clear(); // Create new controller instance controller = new NewsController(); // Create mock request and response objects with proper typing req = { query: {}, params: {} } as Partial<Request>; res = { status: jest.fn().mockReturnThis(), json: jest.fn() } as Partial<Response>; // Cast the mocks to any to avoid TypeScript errors with mock functions (res.status as any).mockReturnThis(); (res.json as any).mockReturnValue(res); }); // Clean up after tests afterEach(() => { jest.clearAllMocks(); }); // Test suite for getTopNews describe('getTopNews', () => { test('should return top news articles with 200 status', async () => { // Arrange mockGetTopNews.mockResolvedValueOnce({ data: mockArticles, meta: { found: mockArticles.length, returned: mockArticles.length } }); // Act await controller.getTopNews(req as Request, res as Response); // Assert expect(mockGetTopNews).toHaveBeenCalled(); expect(res.status).toHaveBeenCalledWith(200); expect(res.json).toHaveBeenCalledWith({ success: true, data: mockArticles, meta: expect.objectContaining({ found: mockArticles.length, returned: mockArticles.length }) }); }); test('should handle query parameters correctly', async () => { // Arrange req.query = { categories: 'technology', language: 'en', limit: '10' }; mockGetTopNews.mockResolvedValueOnce({ data: [mockArticles[1]], meta: { found: 1, returned: 1 } }); // Act await controller.getTopNews(req as Request, res as Response); // Assert expect(mockGetTopNews).toHaveBeenCalledWith(expect.objectContaining({ categories: 'technology', language: 'en', limit: 10 })); expect(res.status).toHaveBeenCalledWith(200); }); test('should handle error from service gracefully', async () => { // Arrange const error = new Error('Service error'); mockGetTopNews.mockRejectedValueOnce(error); // Act await controller.getTopNews(req as Request, res as Response); // Assert expect(res.status).toHaveBeenCalledWith(500); expect(res.json).toHaveBeenCalledWith({ success: false, error: expect.any(String) }); }); }); // Test suite for getAllNews describe('getAllNews', () => { test('should return paginated news with 200 status', async () => { // Arrange req.query = { page: '1', limit: '10' }; mockGetAllNews.mockResolvedValueOnce({ data: mockArticles, meta: { found: 20, returned: mockArticles.length, page: 1, limit: 10 } }); // Act await controller.getAllNews(req as Request, res as Response); // Assert expect(mockGetAllNews).toHaveBeenCalledWith(expect.objectContaining({ page: 1, limit: 10 })); expect(res.status).toHaveBeenCalledWith(200); expect(res.json).toHaveBeenCalledWith({ success: true, data: mockArticles, meta: expect.objectContaining({ page: 1, limit: 10 }) }); }); test('should handle search query parameter', async () => { // Arrange req.query = { search: 'test', search_fields: 'title,description' }; mockGetAllNews.mockResolvedValueOnce({ data: mockArticles, meta: { found: mockArticles.length, returned: mockArticles.length } }); // Act await controller.getAllNews(req as Request, res as Response); // Assert expect(mockGetAllNews).toHaveBeenCalledWith(expect.objectContaining({ search: 'test', search_fields: 'title,description' })); expect(res.status).toHaveBeenCalledWith(200); }); test('should handle date filtering correctly', async () => { // Arrange const testDate = '2025-05-01'; req.query = { published_after: testDate }; mockGetAllNews.mockResolvedValueOnce({ data: [mockArticles[1]], meta: { found: 1, returned: 1 } }); // Act await controller.getAllNews(req as Request, res as Response); // Assert expect(mockGetAllNews).toHaveBeenCalledWith(expect.objectContaining({ published_after: testDate })); expect(res.status).toHaveBeenCalledWith(200); }); test('should handle error from service gracefully', async () => { // Arrange const error = new Error('Service error'); mockGetAllNews.mockRejectedValueOnce(error); // Act await controller.getAllNews(req as Request, res as Response); // Assert expect(res.status).toHaveBeenCalledWith(500); expect(res.json).toHaveBeenCalledWith({ success: false, error: expect.any(String) }); }); }); // Test suite for getNewsByUuid describe('getNewsByUuid', () => { test('should return article when found with 200 status', async () => { // Arrange req.params = { uuid: 'test-uuid-1' }; mockGetNewsByUuid.mockResolvedValueOnce({ data: mockArticles[0] }); // Act await controller.getNewsByUuid(req as Request, res as Response); // Assert expect(mockGetNewsByUuid).toHaveBeenCalledWith('test-uuid-1'); expect(res.status).toHaveBeenCalledWith(200); expect(res.json).toHaveBeenCalledWith({ success: true, data: mockArticles[0] }); }); test('should return 404 when article not found', async () => { // Arrange req.params = { uuid: 'non-existent-uuid' }; const error = new Error('Article not found'); mockGetNewsByUuid.mockRejectedValueOnce(error); // Act await controller.getNewsByUuid(req as Request, res as Response); // Assert expect(mockGetNewsByUuid).toHaveBeenCalledWith('non-existent-uuid'); expect(res.status).toHaveBeenCalledWith(404); expect(res.json).toHaveBeenCalledWith({ success: false, error: expect.any(String) }); }); }); // Test suite for getNewsSources describe('getNewsSources', () => { test('should return sources with 200 status', async () => { // Arrange mockGetNewsSources.mockResolvedValueOnce({ data: mockSources, meta: { found: mockSources.length, returned: mockSources.length } }); // Act await controller.getNewsSources(req as Request, res as Response); // Assert expect(mockGetNewsSources).toHaveBeenCalled(); expect(res.status).toHaveBeenCalledWith(200); expect(res.json).toHaveBeenCalledWith({ success: true, data: mockSources, meta: expect.objectContaining({ found: mockSources.length, returned: mockSources.length }) }); }); test('should handle pagination parameters', async () => { // Arrange req.query = { page: '2', limit: '5' }; mockGetNewsSources.mockResolvedValueOnce({ data: mockSources, meta: { found: 10, returned: 2, page: 2, limit: 5 } }); // Act await controller.getNewsSources(req as Request, res as Response); // Assert expect(mockGetNewsSources).toHaveBeenCalledWith(expect.objectContaining({ page: 2, limit: 5 })); expect(res.status).toHaveBeenCalledWith(200); }); test('should handle error from service gracefully', async () => { // Arrange const error = new Error('Service error'); mockGetNewsSources.mockRejectedValueOnce(error); // Act await controller.getNewsSources(req as Request, res as Response); // Assert expect(res.status).toHaveBeenCalledWith(500); expect(res.json).toHaveBeenCalledWith({ success: false, error: expect.any(String) }); }); }); });

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