final-controller.test.ts•6.4 kB
/**
* Final, reliable tests for the News Controller
*
* This version fixes all the structure mismatches and properly tests the controller behavior.
*/
import { describe, test, expect, beforeEach, afterEach, jest } from '@jest/globals';
import { Request, Response } from 'express';
// Mock the services before importing the controller
const mockGetTopNews = jest.fn();
const mockGetAllNews = jest.fn();
const mockGetNewsByUuid = jest.fn();
const mockGetNewsSources = jest.fn();
// Mock the NewsService before importing the controller
jest.mock('../services/news.service', () => {
return {
NewsService: jest.fn().mockImplementation(() => ({
getTopNews: mockGetTopNews,
getAllNews: mockGetAllNews,
getNewsByUuid: mockGetNewsByUuid,
getNewsSources: mockGetNewsSources
}))
};
});
// Mock the cache service to prevent actual cache operations
jest.mock('../utils/cache', () => ({
cacheService: {
get: jest.fn(),
set: jest.fn(),
has: jest.fn(),
clear: jest.fn(),
getStats: jest.fn().mockReturnValue({ hits: 0, misses: 0, keys: 0 })
}
}));
// Import the controller after setting up mocks
import { NewsController } from '../controllers/news.controller';
describe('NewsController Tests', () => {
let controller: NewsController;
let req: Partial<Request>;
let res: Partial<Response>;
beforeEach(() => {
// Create a fresh controller
controller = new NewsController();
// Reset mock functions
jest.clearAllMocks();
// Set up request with all necessary properties
req = {
query: {
search: undefined,
search_fields: undefined,
locale: undefined,
categories: undefined,
exclude_categories: undefined,
domains: undefined,
exclude_domains: undefined,
source_ids: undefined,
exclude_source_ids: undefined,
language: undefined,
published_before: undefined,
published_after: undefined,
limit: undefined,
page: undefined,
sort: undefined
},
params: {}
};
// Set up response
res = {
status: jest.fn().mockReturnThis(),
json: jest.fn()
};
});
afterEach(() => {
jest.clearAllMocks();
});
describe('getTopNews', () => {
test('returns 200 and wrapped data when service succeeds', async () => {
// Arrange - mock the service response
const mockArticles = [{
uuid: 'test-123',
title: 'Test Article'
}];
mockGetTopNews.mockResolvedValueOnce({
data: mockArticles,
meta: { found: 1, returned: 1 }
});
// Act - call the controller method
await controller.getTopNews(req as Request, res as Response);
// Assert - verify proper response format
expect(res.status).toHaveBeenCalledWith(200);
expect(res.json).toHaveBeenCalledWith({
success: true,
data: {
data: expect.arrayContaining([
expect.objectContaining({ uuid: 'test-123' })
]),
meta: expect.objectContaining({ found: 1, returned: 1 })
}
});
});
test('returns 500 when service fails', async () => {
// Arrange - mock a service error
mockGetTopNews.mockRejectedValueOnce(new Error('API 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.stringContaining('API error')
});
});
test('passes query parameters correctly', async () => {
// Arrange - set up request params
req.query = {
...req.query,
categories: 'technology',
limit: '10'
};
mockGetTopNews.mockResolvedValueOnce({
data: [],
meta: { found: 0, returned: 0 }
});
// Act
await controller.getTopNews(req as Request, res as Response);
// Assert - verify params passed correctly
expect(mockGetTopNews).toHaveBeenCalledWith(
expect.objectContaining({
categories: 'technology',
limit: 10 // Number conversion by controller
})
);
});
});
describe('getNewsByUuid', () => {
test('returns 200 and article when found', async () => {
// Arrange
const uuid = 'test-uuid-123';
req.params = { uuid };
mockGetNewsByUuid.mockResolvedValueOnce({
data: { uuid, title: 'Test Article' }
});
// Act
await controller.getNewsByUuid(req as Request, res as Response);
// Assert
expect(mockGetNewsByUuid).toHaveBeenCalledWith(uuid);
expect(res.status).toHaveBeenCalledWith(200);
expect(res.json).toHaveBeenCalledWith({
success: true,
data: {
data: expect.objectContaining({ uuid })
}
});
});
test('returns 500 for error, including not found', async () => {
// Arrange
const uuid = 'non-existent';
req.params = { uuid };
mockGetNewsByUuid.mockRejectedValueOnce(new Error('Article not found'));
// Act
await controller.getNewsByUuid(req as Request, res as Response);
// Assert - Note: controller returns 500 for all errors
expect(mockGetNewsByUuid).toHaveBeenCalledWith(uuid);
expect(res.status).toHaveBeenCalledWith(500);
expect(res.json).toHaveBeenCalledWith({
success: false,
error: expect.stringContaining('not found')
});
});
});
describe('getAllNews', () => {
test('returns paginated results with proper structure', async () => {
// Arrange
req.query = {
...req.query,
page: '2',
limit: '10'
};
const mockResults = {
data: [{ uuid: 'page2-1', title: 'Page 2 Article 1' }],
meta: { found: 25, returned: 1, page: 2, limit: 10 }
};
mockGetAllNews.mockResolvedValueOnce(mockResults);
// Act
await controller.getAllNews(req as Request, res as Response);
// Assert
expect(res.status).toHaveBeenCalledWith(200);
expect(res.json).toHaveBeenCalledWith({
success: true,
data: mockResults
});
});
});
});