import { HttpService } from "@nestjs/axios"; import { ConfigModule } from "@nestjs/config"; import { Test } from "@nestjs/testing"; import { Observable, of } from "rxjs"; import { EsTime, Order } from "src/core/domain"; import { PageDto } from "src/core/domain/dtos"; import { HttpResponseException } from "src/core/exceptions"; import { PageInterceptor } from "src/core/interceptors/page.interceptor"; const execCtxMock = { switchToHttp: jest.fn().mockReturnThis(), getRequest: jest.fn().mockReturnThis(), getHandler: jest.fn().mockReturnThis(), getArgs: jest.fn().mockReturnThis(), getArgByIndex: jest.fn().mockReturnThis(), switchToRpc: jest.fn().mockReturnThis(), switchToWs: jest.fn().mockReturnThis(), getType: jest.fn().mockReturnThis(), getClass: jest.fn().mockReturnThis(), }; const callHandlerMock = { handle: jest.fn(), }; describe('Unit tests for PageInterceptor', () => { let pageInter: PageInterceptor; let httpService: HttpService; beforeAll(async () => { const moduleRef = await Test.createTestingModule({ providers: [ { provide: HttpService, useValue: { post: jest.fn(), delete: jest.fn() }, }, PageInterceptor, ], imports: [ ConfigModule.forRoot({ isGlobal: true, cache: true, expandVariables: true, }) ], }).compile(); pageInter = moduleRef.get(PageInterceptor); httpService = moduleRef.get(HttpService); execCtxMock.getRequest.mockReturnValue({ query: { query: 'thisIsMyQuery', page: 1, limit: 5, order: Order.DESC } }); callHandlerMock.handle.mockReturnValue( of({ hits: { total: { value: 1 }, hits: [{}] } }) ); }); it('Should be defined', () => { expect(pageInter).toBeDefined(); expect(httpService).toBeDefined(); }); describe('intercept()', () => { let tmp; beforeAll(() => { tmp = pageInter.getPIT; pageInter.getPIT = jest.fn().mockReturnValue({}); }); afterAll(() => { pageInter.getPIT = tmp; }); it('Should return a Promise', () => { expect(pageInter.intercept(execCtxMock, callHandlerMock)).toBeInstanceOf(Promise); }); it('Should return a Promise with Observable and PageDto inside', () => { pageInter.intercept(execCtxMock, callHandlerMock).then((res) => { expect(res).toBeInstanceOf(Observable); res.subscribe((data) => { expect(data).toBeInstanceOf(PageDto); }); }); }); it('Should touch CallHandler.handle() method', () => { let chHandleSpy = jest.spyOn(callHandlerMock, 'handle'); pageInter.intercept(execCtxMock, callHandlerMock); expect(chHandleSpy).toBeCalled(); }); it('Should construct a page with proper data on it', () => { callHandlerMock.handle.mockReturnValueOnce( of({ hits: { total: { value: 1 }, hits: [{ _source: { dummy: 'dum' } }] } }) ); pageInter.intercept(execCtxMock, callHandlerMock).then((res) => { res.subscribe((page) => { expect(page.data.length).toBe(1); expect(page.data[0]).toEqual({ dummy: 'dum' }); }); }); }); it('Should construct correct meta-data of the page', () => { execCtxMock.getRequest.mockReturnValueOnce({ query: { page: 5, order: 'desc', limit: 100, } }); callHandlerMock.handle.mockReturnValueOnce( of({ hits: { total: { value: 921 }, hits: [] } }) ); pageInter.intercept(execCtxMock, callHandlerMock).then((res) => { res.subscribe((page) => { expect(page.meta).toEqual({ total: 921, pagenum: 5, order: 'desc', hasNext: true, hasPrev: true, pagesize: 100 }); }); }); }); it('Should reverse the search results', () => { execCtxMock.getRequest.mockReturnValueOnce({ query: { page: 1, order: 'desc', limit: 3 } }); pageInter['prevSearch']._prevPage = 3; pageInter['prevSearch'].isSet = jest.fn().mockImplementationOnce(() => { return true; }) callHandlerMock.handle.mockReturnValueOnce( of({ hits: { total: { value: 1 }, hits: [ { sort: ['1', 'less relevant'], _source: '1' }, { sort: ['2', 'average'], _source: '2' }, { sort: ['3', 'most relevant'], _source: '3' } ] } }) ); pageInter.intercept(execCtxMock, callHandlerMock).then((res) => { res.subscribe((page) => { expect(pageInter['prevSearch']._tiebreaker).toEqual(['1', 'less relevant']); expect(page.data).toEqual(['3', '2', '1']); }); }); }); }); describe('getPIT()', () => { it('Should touch HttpService.post() method', () => { let httpPostMock = jest.spyOn(httpService, 'post').mockReturnValueOnce(of({ data: {id: '2567'}, status: 0, statusText: '', headers: {}, config: {}, })); pageInter.getPIT(1); expect(httpPostMock).toHaveBeenCalled(); }); it('Should contain correct port in the URI from .env', () => { let httpPostMock = jest.spyOn(httpService, 'post').mockReturnValueOnce(of({ data: {id: '2567'}, status: 0, statusText: '', headers: {}, config: {}, })); pageInter.getPIT(1); expect(httpPostMock).toHaveBeenCalledWith(`http://${process.env.ES_CONTAINER_NAME}:${process.env.ES_PORT}/papers/_pit?keep_alive=1m`); }); it('Should touch HttpService with correct URI when time alive and time-unit are set', () => { let httpPostMock = jest.spyOn(httpService, 'post').mockReturnValueOnce(of({ data: {id: '2567'}, status: 0, statusText: '', headers: {}, config: {}, })); let time = 2; let unit = EsTime.sec; pageInter.getPIT(time, unit); expect(httpPostMock).toHaveBeenCalledWith(`http://${process.env.ES_CONTAINER_NAME}:${process.env.ES_PORT}/papers/_pit?keep_alive=${time+unit}`); }); it('Should return error exeception when HttpService fails', () => { jest.spyOn(httpService, 'post').mockImplementationOnce(() => { throw HttpResponseException; }); expect(pageInter.getPIT(1)).rejects.toEqual(HttpResponseException); }); it('Should return a non-empty string when HttpService request succeedes', () => { jest.spyOn(httpService, 'post').mockReturnValueOnce(of({ data: {id: '2567', keep_alive: '1m'}, status: 0, statusText: '', headers: {}, config: {}, })); expect(pageInter.getPIT(1)).resolves.toEqual({ id: '2567', keep_alive: '1m', }); }); }); describe('deletePIT()', () => { it('Should touch HttpService.delete() method', () => { let httpDeleteMock = jest.spyOn(httpService, 'delete').mockReturnValueOnce( of({ data: {succeeded: true}, status: 0, statusText: '', headers: {}, config: {}, })); pageInter.deletePIT(''); expect(httpDeleteMock).toHaveBeenCalled(); }); it('Should contain correct port in the URI from .env and passed PIT ID in the request body', () => { let httpDeleteMock = jest.spyOn(httpService, 'delete').mockReturnValueOnce( of({ data: { succeeded: true }, status: 0, statusText: '', headers: {}, config: {}, })); pageInter.deletePIT('thisIsIDSpecified'); expect(httpDeleteMock).toHaveBeenCalledWith(`http://${process.env.ES_CONTAINER_NAME}:${process.env.ES_PORT}/_pit`, { data: { id: 'thisIsIDSpecified' }, headers: { 'Content-Type': 'application/json' } }); }); it('Should return error exeception when HttpService fails', () => { jest.spyOn(httpService, 'delete').mockImplementationOnce(() => { throw HttpResponseException; }); expect(pageInter.deletePIT('')).rejects.toEqual(HttpResponseException); }); it('Should return true when Elasticsearch successfully removed PIT', () => { jest.spyOn(httpService, 'delete').mockReturnValueOnce( of({ data: { succeeded: true }, status: 0, statusText: '', headers: {}, config: {}, })); expect(pageInter.deletePIT('')).resolves.toBe(true); }); }); });