src/app/routes/article/getInitialData/index.test.ts
import { Agent } from 'undici';
import * as getOnwardsPageData from '../utils/getOnwardsData';
import * as fetchPageData from '../../utils/fetchPageData';
import nodeLogger from '../../../../testHelpers/loggerMock';
import { BFF_FETCH_ERROR } from '../../../lib/logger.const';
import getInitialData from '.';
import pidginArticleWithLatestMedia from '../../../../../data/pidgin/articles/cw0x29n2pvqo.json';
import { ARTICLE_PAGE } from '../../utils/pageTypes';
process.env.BFF_PATH = 'https://mock-bff-path';
const agent = {
connect: { cert: 'cert', ca: 'ca', key: 'key' },
} as unknown as Agent;
const mockGetAgent = () => Promise.resolve(agent);
jest.mock('../../../../server/utilities/getAgent', () => jest.fn(mockGetAgent));
const bffArticleJson = {
data: {
article: {
content: {},
metadata: {
allowAdvertising: true,
consumableAsSFV: true,
lastPublished: 2041342869000,
},
promo: {},
relatedContent: {},
},
secondaryData: {
topStories: [],
features: [],
mostRead: [],
latestMedia: [],
},
},
};
describe('Articles - BFF Fetching', () => {
afterEach(() => {
jest.clearAllMocks();
});
it('should request local fixture data when the app env is "local"', async () => {
process.env.SIMORGH_APP_ENV = 'local';
const fetchDataSpy = jest.spyOn(fetchPageData, 'default');
fetchDataSpy.mockImplementation(() =>
Promise.resolve({
status: 200,
json: JSON.stringify(bffArticleJson),
}),
);
await getInitialData({
path: '/kyrgyz/articles/c0000000000o',
service: 'kyrgyz',
pageType: ARTICLE_PAGE,
getAgent: mockGetAgent,
});
expect(fetchDataSpy).toHaveBeenCalledWith({
path: 'http://localhost/kyrgyz/articles/c0000000000o',
pageType: ARTICLE_PAGE,
timeout: 60000,
});
});
it('should request BFF data when the app env is "test"', async () => {
process.env.SIMORGH_APP_ENV = 'test';
const fetchDataSpy = jest.spyOn(fetchPageData, 'default');
fetchDataSpy.mockImplementation(() =>
Promise.resolve({
status: 200,
json: JSON.stringify(bffArticleJson),
}),
);
await getInitialData({
path: '/kyrgyz/articles/c0000000000o',
service: 'kyrgyz',
pageType: ARTICLE_PAGE,
getAgent: mockGetAgent,
});
expect(fetchDataSpy).toHaveBeenCalledWith({
path: 'https://mock-bff-path/?id=c0000000000o&service=kyrgyz&pageType=article&serviceEnv=test',
agent,
optHeaders: {
'ctx-service-env': 'test',
},
pageType: ARTICLE_PAGE,
});
});
it('should request BFF data when the app env is "live"', async () => {
process.env.SIMORGH_APP_ENV = 'live';
const fetchDataSpy = jest.spyOn(fetchPageData, 'default');
fetchDataSpy.mockImplementation(() =>
Promise.resolve({
status: 200,
json: JSON.stringify(bffArticleJson),
}),
);
await getInitialData({
path: '/kyrgyz/articles/c0000000000o',
service: 'kyrgyz',
pageType: ARTICLE_PAGE,
getAgent: mockGetAgent,
});
expect(fetchDataSpy).toHaveBeenCalledWith({
path: 'https://mock-bff-path/?id=c0000000000o&service=kyrgyz&pageType=article&serviceEnv=live',
agent,
optHeaders: {
'ctx-service-env': 'live',
},
pageType: ARTICLE_PAGE,
});
});
it('should request WSOJ data.', async () => {
process.env.SIMORGH_APP_ENV = 'live';
const fetchDataSpy = jest.spyOn(fetchPageData, 'default');
const getOnwardsPageDataSpy = jest.spyOn(getOnwardsPageData, 'default');
fetchDataSpy.mockReturnValueOnce(
Promise.resolve({
status: 200,
json: bffArticleJson,
}),
);
await getInitialData({
path: '/kyrgyz/articles/c0000000000o.amp?renderer_env=live',
service: 'kyrgyz',
pageType: ARTICLE_PAGE,
getAgent: mockGetAgent,
});
expect(getOnwardsPageDataSpy).toBeCalledWith({
pathname: '/kyrgyz/articles/c0000000000o.amp?renderer_env=live',
service: 'kyrgyz',
isAdvertising: true,
isArticleSfv: true,
agent,
variant: undefined,
});
});
it('should request BFF data if "renderer_env=test" is supplied in the path, ignoring the app env', async () => {
process.env.SIMORGH_APP_ENV = 'local';
const fetchDataSpy = jest.spyOn(fetchPageData, 'default');
fetchDataSpy.mockImplementation(() =>
Promise.resolve({
status: 200,
json: JSON.stringify(bffArticleJson),
}),
);
await getInitialData({
path: '/kyrgyz/articles/c0000000000o?renderer_env=test',
service: 'kyrgyz',
pageType: ARTICLE_PAGE,
getAgent: mockGetAgent,
});
expect(fetchDataSpy).toHaveBeenCalledWith({
path: 'https://mock-bff-path/?id=c0000000000o&service=kyrgyz&pageType=article&serviceEnv=test',
agent,
optHeaders: {
'ctx-service-env': 'test',
},
pageType: ARTICLE_PAGE,
});
});
it('should request BFF data if "renderer_env=live" is supplied in the path, ignoring the app env', async () => {
process.env.SIMORGH_APP_ENV = 'local';
const fetchDataSpy = jest.spyOn(fetchPageData, 'default');
fetchDataSpy.mockImplementation(() =>
Promise.resolve({
status: 200,
json: JSON.stringify(bffArticleJson),
}),
);
await getInitialData({
path: '/kyrgyz/articles/c0000000000o?renderer_env=live',
service: 'kyrgyz',
pageType: ARTICLE_PAGE,
getAgent: mockGetAgent,
});
expect(fetchDataSpy).toHaveBeenCalledWith({
path: 'https://mock-bff-path/?id=c0000000000o&service=kyrgyz&pageType=article&serviceEnv=live',
agent,
optHeaders: {
'ctx-service-env': 'live',
},
pageType: ARTICLE_PAGE,
});
});
it('should log a 404 to node.logger when the article cannot be found', async () => {
const fetchDataSpy = jest.spyOn(fetchPageData, 'default');
fetchDataSpy.mockRejectedValue({ message: 'Not found', status: 404 });
await getInitialData({
path: '/kyrgyz/articles/c0000000000o',
service: 'kyrgyz',
pageType: ARTICLE_PAGE,
getAgent: mockGetAgent,
});
expect(nodeLogger.error).toHaveBeenCalledWith(BFF_FETCH_ERROR, {
pathname: '/kyrgyz/articles/c0000000000o',
service: 'kyrgyz',
message: 'Not found',
status: 404,
});
});
it('should log a 500 to node.logger when the BFF response fails', async () => {
const fetchDataSpy = jest.spyOn(fetchPageData, 'default');
fetchDataSpy.mockRejectedValue({
message: 'Internal server error',
status: 500,
});
await getInitialData({
path: '/kyrgyz/articles/c0000000000o',
service: 'kyrgyz',
pageType: ARTICLE_PAGE,
getAgent: mockGetAgent,
});
expect(nodeLogger.error).toHaveBeenCalledWith(BFF_FETCH_ERROR, {
pathname: '/kyrgyz/articles/c0000000000o',
service: 'kyrgyz',
message: 'Internal server error',
status: 500,
});
});
it('should throw an error if the article metadata is malformed', async () => {
const malformedBffArticleJson = {
metadata: {},
content: {},
promo: {},
relatedContent: {},
};
const fetchDataSpy = jest.spyOn(fetchPageData, 'default');
fetchDataSpy.mockImplementation(() =>
Promise.resolve({
status: 200,
json: JSON.stringify(malformedBffArticleJson),
}),
);
await getInitialData({
path: '/kyrgyz/articles/c0000000000o',
service: 'kyrgyz',
pageType: ARTICLE_PAGE,
getAgent: mockGetAgent,
});
expect(nodeLogger.error).toHaveBeenCalledWith(BFF_FETCH_ERROR, {
pathname: '/kyrgyz/articles/c0000000000o',
service: 'kyrgyz',
message: 'Article data is malformed',
status: 500,
});
});
it('should transform response as expected', async () => {
const fetchDataSpy = jest.spyOn(fetchPageData, 'default');
fetchDataSpy.mockImplementation(() =>
Promise.resolve({
status: 200,
json: pidginArticleWithLatestMedia,
}),
);
const { pageData } = (await getInitialData({
path: '/kyrgyz/articles/c0000000000o',
service: 'kyrgyz',
pageType: 'article',
getAgent: mockGetAgent,
})) as { pageData: Record<string, unknown> };
expect(pageData).toHaveProperty('content');
expect(pageData).toHaveProperty('metadata');
expect(pageData).toHaveProperty('promo');
expect(pageData).toHaveProperty('secondaryColumn');
expect(pageData.secondaryColumn).toHaveProperty('topStories');
expect(pageData.secondaryColumn).toHaveProperty('features');
expect(pageData.secondaryColumn).toHaveProperty('latestMedia');
expect(pageData).toHaveProperty('mostRead');
});
});