import { Document, model, PopulateOptions, Schema, Types, } from 'mongoose'; import { createRequest, createResponse, MockRequest, MockResponse, } from 'node-mocks-http'; import { IApiMeta, } from '../dist/types/IApiMeta'; import BaseController from './base.controller'; import { ApiModel, IApiError, IApiModel, IApiQuery, } from './types'; interface IMockModel extends IApiModel { property?: string; } type MockModelDocument = Document & IApiModel; export type MockModel = ApiModel; const mockFilters = [ 'propertyOne' ]; class MockController extends BaseController { constructor(_model: MockModel) { super(_model); this.filters.push(...mockFilters); } } const mockSchema = new Schema({ property: String, timestamps: { created: { at: { type: Date, default: Date.now, }, by: String, }, updated: { at: { type: Date, default: Date.now, }, by: String, }, }, mark: { deleted: Boolean, }, }); describe('base.controller spec', () => { let mockModel: MockModel; let controller: MockController; const mockNext = jest.fn(); beforeEach(() => { mockModel = model('mockModel', mockSchema); controller = new MockController(mockModel); }); describe('parseSort()', () => { it('should parse a sort string', () => { const parsedSort = controller.parseSort('type'); expect(parsedSort).toEqual({ type: 1 }); }); it('should parse a sort object', () => { const parsedSort = controller.parseSort('{"property": -1}'); expect(parsedSort).toEqual({ property: -1 }); }); it('should parse a sort object with string numbers', () => { const parsedSort = controller.parseSort('{"property": "-1"}'); expect(parsedSort).toEqual({ property: -1 }); }); it('should parse a sort object with invalid numbers', () => { const parsedSort = controller.parseSort('{"property": "sort"}'); expect(parsedSort).toEqual({ property: 1 }); }); it('should parse a sort object with multiple params', () => { const mockSort = { propertyOne: -1, propertyTwo: 1, }; const parsedSort = controller.parseSort(JSON.stringify(mockSort)); expect(parsedSort).toEqual(mockSort); }); it('should not fail on invalid string', () => { const mockSort = '\{"foo: 1'; const parsedSort = controller.parseSort(mockSort); // eslint-disable-next-line @typescript-eslint/naming-convention expect(parsedSort).toEqual({ '1': 1 }); }); }); describe('parseFilter()', () => { it('should return empty object when no filter specified', () => { const mockQuery: IApiQuery = { limit: 0, offset: 0, }; const filter = controller.parseFilter(mockQuery.filter); expect(filter).toEqual({}); }); it('should parse filter for allowed properties', () => { const mockQuery: IApiQuery = { limit: 0, offset: 0, filter: `{"${ mockFilters[0] }": 'foo'}`, }; const filter = controller.parseFilter(mockQuery.filter); const expectedQuery = { ...mockQuery, filter: { [mockFilters[0]]: 'foo', }, }; expect(filter).toEqual(expectedQuery.filter); }); it('should not parse filter for not allowed properties', () => { const mockQuery: IApiQuery = { limit: 0, offset: 0, filter: '{"bar": \'foo\'}', }; const parsedFilter = controller.parseFilter(mockQuery.filter); expect(parsedFilter).toEqual({}); }); it('should not fail for invalid string', () => { const mockQuery: IApiQuery = { limit: 0, offset: 0, filter: '{"bar": \'foo', }; const parsedFilter = controller.parseFilter(mockQuery.filter); expect(parsedFilter).toEqual({}); }); }); describe('"parseDateRange() fills requests dataRange and stats"', () => { it('should be possible to parse date ranges from given year and month param', () => { const year = 2018; const month = 12; const mockRequest: any = createRequest({ params: { year: year.toString(), month: month.toString(), }, }); const mockResponse: any = createResponse(); const from = new Date(year, month - 1, 1, 0, 0, 0); const to = new Date(year, month, 1, 0, 0, 0); const expectedDataRange = { $and: [ { date: { $gte: from, }, }, { date: { $lt: to, }, }, ], }; controller.parseDateRange(mockRequest, mockResponse, mockNext, '', ''); expect(mockRequest.dateRange).toEqual(expectedDataRange); expect(mockRequest.stats.range).toEqual({ from, to }); }); it('should be possible to parse date range from a year param', () => { const year = 2018; const mockRequest: any = createRequest({ params: { year: year.toString(), }, }); const mockResponse: any = createResponse(); const from = new Date(year, 0, 1, 0, 0, 0); const to = new Date(year, 12, 1, 0, 0, 0); const expectedDataRange = { $and: [ { date: { $gte: from, }, }, { date: { $lt: to, }, }, ], }; controller.parseDateRange(mockRequest, mockResponse, mockNext, '', ''); expect(mockRequest.dateRange).toEqual(expectedDataRange); expect(mockRequest.stats.range).toEqual({ from, to }); }); it('should not fail to parse date range from an invalid year param', () => { const year = 'foo'; const mockRequest: any = createRequest({ params: { year: year.toString(), }, }); const mockResponse: any = createResponse(); controller.parseDateRange(mockRequest, mockResponse, mockNext, '', ''); expect(mockRequest.dateRange).toBeFalsy(); expect(mockRequest.stats).toBeFalsy(); }); it( 'should not fail to parse date range from a valid year but invalid month param', () => { const year = 2019; const month = 'foo'; const mockRequest: any = createRequest({ params: { year: year.toString(), month: month, }, stats: {}, }); const mockResponse: any = createResponse(); const from = new Date(year, 0, 1, 0, 0, 0); const to = new Date(year, 12, 1, 0, 0, 0); const expectedDataRange = { $and: [ { date: { $gte: from, }, }, { date: { $lt: to, }, }, ], }; controller.parseDateRange(mockRequest, mockResponse, mockNext, '', ''); expect(mockRequest.dateRange).toEqual(expectedDataRange); expect(mockRequest.stats.range).toEqual({ from, to }); }); }); describe('"statistics()"', () => { let mockRequest: MockRequest; let mockResponse: MockResponse; let error: any; let statsResults: any; let nrOfDocuments: number; beforeEach(() => { mockRequest = createRequest({}); mockResponse = createResponse(); error = null; statsResults = { statsOne: 0, statsTwo: 300, statsThree: -12, }; nrOfDocuments = 111; mockModel.statistics = jest.fn(() => { if (error) { return Promise.reject(error); } else { return Promise.resolve(statsResults); } }); jest.fn((_query, callback) => callback(error, statsResults)); mockModel.countDocuments = jest.fn().mockImplementation(() => { // something is odd here, countDocuments is not a promise if (error) { return Promise.reject(error); } else { return Promise.resolve(nrOfDocuments); } }); }); it('should append statistics to request if model has statistics function', async() => { await controller.statistics(mockRequest, mockResponse, mockNext); expect(mockRequest.stats[mockModel.collection.name]).toEqual(statsResults); }); it('should not recreate stats object if already exists', async () => { mockRequest.stats = { otherStats: 'something', }; await controller.statistics(mockRequest, mockResponse, mockNext); expect(mockRequest.stats[mockModel.collection.name]).toEqual(statsResults); }); it('should respond with server error if error occurs', async() => { error = { id: 'error', message: 'message', }; statsResults = null; await controller.statistics(mockRequest, mockResponse, mockNext); expect(mockResponse.statusCode).toBe(500); expect(JSON.parse(mockResponse._getData())).toEqual({ error }); }); it('should append count of objects to request if model has no statistics function', async() => { delete mockModel.statistics; await controller.statistics(mockRequest, mockResponse, mockNext); expect(mockRequest.stats[mockModel.collection.name]).toEqual(nrOfDocuments); }); it( 'should add minimal stats and not create stats if stats already exists on req', async() => { nrOfDocuments = 1000; statsResults = null; delete mockModel.statistics; mockRequest.stats = { otherStats: 'something', }; await controller.statistics(mockRequest, mockResponse, mockNext); expect(mockRequest.stats[mockModel.collection.name]).toEqual(nrOfDocuments); }); it('should respond with server error if error occurs', async() => { error = { id: 'error', message: 'message', }; nrOfDocuments = null; statsResults = null; delete mockModel.statistics; await controller.statistics(mockRequest, mockResponse, mockNext); expect(mockResponse.statusCode).toBe(500); expect(JSON.parse(mockResponse._getData())).toEqual({ error }); }); afterEach(() => { delete mockModel.statistics; delete mockModel.countDocuments; }); }); describe('"statsResponse()"', () => { let mockRequest: MockRequest; let mockResponse: MockResponse; beforeEach(() => { mockRequest = createRequest({}); mockResponse = createResponse(); }); it('should respond with stats', () => { controller.statsResponse(mockRequest, mockResponse, mockNext); expect(JSON.parse(mockResponse._getData())).toEqual({}); expect(mockResponse.statusCode).toBe(200); }); it('should not recreate stats if exists', () => { const stats = { this: 'that', }; mockRequest.stats = stats; controller.statsResponse(mockRequest, mockResponse, mockNext); expect(JSON.parse(mockResponse._getData())).toEqual(stats); }); }); describe('"findById()"', () => { let mockRequest: MockRequest; let mockResponse: MockResponse; let _model: MockModelDocument; let error: any; let populate: PopulateOptions; beforeEach(() => { mockRequest = createRequest({}); mockResponse = createResponse(); error = null; _model = new mockModel(); mockModel.findById = jest.fn().mockImplementation((_id) => ({ populate: function (foreignProperties: PopulateOptions[]): IMockModel { foreignProperties .forEach((foreignProperty) => { const key = foreignProperty.path as keyof IMockModel; (_model as any)[key] = foreignProperty.select; }); return this; }, exec: jest.fn(() => { if (error) { return Promise.reject(error); } else { return Promise.resolve(_model); } }), })); }); it('should append model to request if it has a valid id', async() => { const id = new Types.ObjectId(); _model = new mockModel({ id: id }); await controller.findById(mockRequest, mockResponse, mockNext, id); expect(mockRequest.model).toEqual(_model); }); it('should respond 500 if error occurs', async() => { const id = new Types.ObjectId(); _model = null; error = { id: 'serverError', message: 'message', }; await controller.findById(mockRequest, mockResponse, mockNext, id); expect(mockResponse.statusCode).toBe(500); expect(JSON.parse(mockResponse._getData())).toEqual({ error }); }); it('should return 404 if model not found', async() => { const id = new Types.ObjectId(); const err: IApiError = { id: 'notFound', message: `${ mockModel.modelName } ${ id } does not exist`, errors: [], fields: {}, }; _model = null; await controller.findById(mockRequest, mockResponse, mockNext, id); expect(mockResponse.statusCode).toBe(404); expect(JSON.parse(mockResponse._getData())).toEqual({ error: err }); }); it('should return 400 if invalid model id', async() => { error = { id: 'invalidId', message: 'Invalid id', errors: [], fields: {}, }; _model = null; await controller.findById(mockRequest, mockResponse, mockNext, 'id'); expect(mockResponse.statusCode).toBe(400); expect(JSON.parse(mockResponse._getData())).toEqual({ error }); }); it('should populate model with requested data', async() => { const id = new Types.ObjectId(); populate = { path: 'foo', select: 'bar baz', }; _model = new mockModel({ id: id }); await controller.findById(mockRequest, mockResponse, mockNext, id, '', [ populate ]); expect(mockRequest.model).toHaveProperty(populate.path, populate.select); }); afterEach(() => { delete mockModel.findById; }); }); describe('"index()"', () => { let mockRequest: MockRequest; let mockResponse: MockResponse; let error: any; let data: MockModelDocument[] = []; let query: IApiQuery; beforeEach(() => { mockResponse = createResponse(); mockResponse.query = {}; // a mock model to test against; error = null; data = [ new mockModel(), new mockModel() ]; query = { q: '', offset: 0, limit: 100, populate: [], select: null, sort: null, }; (mockModel as any).find = jest.fn((q: any): any => { // TODO: fix any mockResponse.query.q = q; const mockFind = { limit: (limit: any): any => { mockResponse.query.limit = limit; return mockFind; }, skip: (skip: any): any => { mockResponse.query.offset = skip; return mockFind; }, sort: (sort: any): any => { mockResponse.query.sort = sort; return mockFind; }, select: (select: any): any => { mockResponse.query.select = select; return mockFind; }, populate: (populate: any): any => { mockResponse.query.populate = populate; return mockFind; }, exec: jest.fn().mockImplementation(() => { if (error) { return Promise.reject(error); } else { return Promise.resolve(data); } }), }; return mockFind; }); }); it('should return a list of models', async () => { mockRequest = createRequest({}); await controller.index(mockRequest, mockResponse, mockNext); expect(mockRequest.data).toEqual(data); expect(mockResponse.query).toEqual({ ...query, q: {}, select: {}, }); }); it('should allow to change query', async() => { query.offset = '10'; query.limit = '10'; mockRequest = createRequest({ query, }); await controller.index(mockRequest, mockResponse, mockNext); expect(mockResponse.query.offset).toEqual(parseInt(query.offset, 10)); expect(mockResponse.query.limit).toEqual(parseInt(query.limit, 10)); }); it('should add sort and filters', async() => { query.sort = 'type'; query.filter = 'type'; mockRequest = createRequest({ query, }); await controller.index(mockRequest, mockResponse, mockNext); expect(mockResponse.query.sort).toEqual({ type: 1 }); /* don't check for filter, they are checked above */ }); it('should call model parseQuery, if exists', async() => { query.sort = 'type'; query.filter = 'type'; mockRequest = createRequest({ query, }); mockModel.parseQuery = jest.fn().mockImplementation((_query: IApiQuery) => _query); await controller.index(mockRequest, mockResponse, mockNext); expect(mockResponse.query.sort).toEqual({ type: 1 }); /* don't check for filter, they are checked above */ }); it('should return error, if error occurs', async() => { query.sort = 'type'; query.filter = 'type'; mockRequest = createRequest({ query, }); error = { id: 'mockError', message: 'mockMessage', }; await controller.index(mockRequest, mockResponse, (err: any) => { expect(err).toEqual(error); }); expect(mockResponse.query.sort).toEqual({ type: 1 }); /* don't check for filter, they are checked above */ }); }); describe('"read()"', () => { let mockRequest: MockRequest; let mockResponse: MockResponse; let _model: MockModelDocument; beforeEach(() => { _model = new mockModel({ property: 'bar' }); mockResponse = createResponse(); }); it('should return model as json', () => { mockRequest = createRequest({ model: _model, }); controller.read(mockRequest, mockResponse, mockNext); const clone = JSON.parse(JSON.stringify(_model.toObject())); // needs parsing of id expect(mockResponse.statusCode).toBe(200); expect(JSON.parse(mockResponse._getData())).toEqual(clone); }); it('should return model not found if no model', () => { const err: IApiError = { id: 'modelMissing', message: 'the model is missing in the request', errors: [], fields: {}, }; _model = null; mockRequest = createRequest({}); controller.read(mockRequest, mockResponse, mockNext); expect(mockResponse.statusCode).toBe(400); expect(JSON.parse(mockResponse._getData())).toEqual({ error: err }); }); }); describe('"create()"', () => { let mockRequest: MockRequest; let mockResponse: MockResponse; let body: any; const user = 'test'; let error: any; let _model: MockModelDocument; beforeEach(() => { error = null; body = { property: 'something', }; mockResponse = createResponse(); mockModel.prototype.save = jest.fn(function () { if (error) { return Promise.reject(error); } else { return Promise.resolve(this); } }); _model = new mockModel(body); mockRequest = createRequest({ user: { username: user, }, body, }); }); it('should create a model', async() => { await controller.create(mockRequest, mockResponse, mockNext); expect(mockResponse.statusCode).toBe(201); const response = JSON.parse(mockResponse._getData()); expect(response).toHaveProperty('property', body.property); expect(response).toHaveProperty('timestamps.created.by', user); }); it('should return validation error if error occurs', async() => { error = { name: 'ValidationError', id: 'validationError', message: 'the model has invalid properties', errors: 'fields', }; await controller.create(mockRequest, mockResponse, mockNext); expect(mockResponse.statusCode).toBe(400); expect(JSON.parse(mockResponse._getData())).toEqual({ error: { id: error.id, message: error.message, fields: error.errors, errors: [], }, }); }); it('should return duplicate error if is duplication', async() => { error = { code: 11000, }; await controller.create(mockRequest, mockResponse, mockNext); expect(mockResponse.statusCode).toBe(400); expect(JSON.parse(mockResponse._getData())).toEqual({ error: { id: 'duplicate', message: `${ mockModel.modelName } already exists`, }, }); }); it('should return next error, if not duplicate nor validation error', async() => { error = { message: 'something', }; await controller.create(mockRequest, mockResponse, (err: any) => { expect(err).toEqual(error); }); }); it('should not allow to set timestamps or id', async() => { body = { property: 'something else', _id: 5, id: 17, timestamps: { crated: { at: new Date(), by: 'someone', }, }, }; _model = new mockModel(body); mockRequest = createRequest({ user: { username: user, }, body, }); await controller.create(mockRequest, mockResponse, mockNext); expect(mockResponse.statusCode).toBe(201); const response = JSON.parse(mockResponse._getData()); expect(response).toHaveProperty('property', body.property); expect(response).toHaveProperty('timestamps.created.by', user); expect(response).not.toHaveProperty('_id', body._id); expect(response).not.toHaveProperty('id', body.id); }); afterEach(() => { delete _model.save; }); }); describe('"update()"', () => { let mockRequest: MockRequest; let mockResponse: MockResponse; let body: any; const user = 'test'; let error: any; let _model: MockModelDocument; beforeEach(() => { error = null; body = { property: 'something', }; mockResponse = createResponse(); mockModel.prototype.save = jest.fn(function () { if (error) { return Promise.reject(error); } else { return Promise.resolve(this); } }); _model = new mockModel({ property: 'initial' }); mockRequest = createRequest({ user: { username: user, }, model: _model, body, }); }); it('should update a model', async () => { await controller.update(mockRequest, mockResponse, mockNext); expect(mockResponse.statusCode).toBe(200); const response = JSON.parse(mockResponse._getData()); expect(response).toHaveProperty('property', body.property); expect(response).toHaveProperty('timestamps.updated.by', user); }); it('should return validation error if error occurs', async() => { error = { name: 'ValidationError', id: 'validationError', message: 'the model has invalid properties', errors: 'fields', }; await controller.update(mockRequest, mockResponse, mockNext); expect(mockResponse.statusCode).toBe(400); expect(JSON.parse(mockResponse._getData())).toEqual({ error: { id: error.id, message: error.message, fields: error.errors, errors: [], }, }); }); it('should return next error, if its not a validation error', async() => { error = { message: 'something', }; await controller.update(mockRequest, mockResponse, (err: any) => { expect(err).toEqual(error); }); }); it('should return model missing, if model is missing', async() => { error = { id: 'modelMissing', message: 'the model is missing in the request', errors: [], fields: {}, }; mockRequest = createRequest({ user: { username: user, }, body, }); await controller.update(mockRequest, mockResponse, mockNext); expect(mockResponse.statusCode).toBe(400); expect(JSON.parse(mockResponse._getData())).toEqual({ error }); }); it('should not allow to set timestamps or id', async() => { body = { property: 'something else', _id: null, id: 17, timestamps: { crated: { at: new Date(), by: 'someone', }, }, }; _model = new mockModel({ property: 'initial' }); mockRequest = createRequest({ user: { username: user, }, model: _model, body, }); await controller.update(mockRequest, mockResponse, mockNext); expect(mockResponse.statusCode).toBe(200); const response = JSON.parse(mockResponse._getData()); expect(response).toHaveProperty('property', body.property); expect(response).toHaveProperty('timestamps.updated.by', user); expect(response).not.toHaveProperty('_id', body._id); expect(response).not.toHaveProperty('id', body.id); }); afterEach(() => { delete _model.save; }); }); describe('"softDelete()"', () => { let mockRequest: MockRequest; let mockResponse: MockResponse; let error: any; let _model: MockModelDocument; const user = 'test'; beforeEach(() => { error = null; mockResponse = createResponse(); mockModel.prototype.save = jest.fn(function () { if (error) { return Promise.reject(error); } else { return Promise.resolve(this); } }); _model = new mockModel({ property: 'something' }); mockRequest = createRequest({ model: _model, user: { username: user, }, }); }); it('should mark a model as deleted', async () => { await controller.softDelete(mockRequest, mockResponse, mockNext); expect(mockResponse.statusCode).toBe(200); const response = JSON.parse(mockResponse._getData()); expect(response).toHaveProperty('mark.deleted', true); }); it('should return deletion error if error occurs', async () => { error = { id: 'delete', message: 'the model has invalid properties', errors: [], fields: {}, }; await controller.softDelete(mockRequest, mockResponse, mockNext); expect(mockResponse.statusCode).toBe(400); expect(JSON.parse(mockResponse._getData())).toEqual({ error }); }); it('should return model missing, if model is missing', async () => { error = { id: 'modelMissing', message: 'the model is missing in the request', errors: [], fields: {}, }; mockRequest = createRequest({}); await controller.softDelete(mockRequest, mockResponse, mockNext); expect(mockResponse.statusCode).toBe(400); expect(JSON.parse(mockResponse._getData())).toEqual({ error }); }); afterEach(() => { delete _model.save; }); }); describe('"delete()"', () => { let mockRequest: MockRequest; let mockResponse: MockResponse; let error: any; let _model: MockModelDocument; beforeEach(() => { error = null; mockResponse = createResponse(); mockModel.deleteOne = jest.fn().mockReturnValue(Promise.resolve(_model)); _model = new mockModel({ property: 'something' }); mockRequest = createRequest({ model: _model, }); }); it('should remove a model', async () => { await controller.delete(mockRequest, mockResponse, mockNext); expect(mockResponse.statusCode).toBe(200); const response = JSON.parse(mockResponse._getData()); expect(response._id.toString()).toEqual(_model._id.toString()); }); it('should return model missing, if model is missing', () => { error = { id: 'modelMissing', message: 'the model is missing in the request', errors: [], fields: {}, }; mockRequest = createRequest({}); controller.delete(mockRequest, mockResponse, mockNext); expect(mockResponse.statusCode).toBe(400); expect(JSON.parse(mockResponse._getData())).toEqual({ error }); }); it('should return error if error occurs', () => { _model = new mockModel({ property: 'something' }); mockRequest = createRequest({ model: _model, }); error = { id: 'mockError', message: 'mock message', }; controller.delete(mockRequest, mockResponse, (err: any) => { expect(err).toEqual(error); }); }); afterEach(() => { delete _model.save; }); }); describe('"apiResponse()"', () => { let mockRequest: MockRequest; let mockResponse: MockResponse; let data: MockModelDocument[] = []; let meta: IApiMeta; beforeEach(() => { mockResponse = createResponse(); meta = { total: 100, count: 10, offset: 0, limit: 100, }; data = [ new mockModel(), new mockModel() ]; }); it('should generate an api response', () => { mockRequest = createRequest({ data: data, meta: meta, }); controller.apiResponse(mockRequest, mockResponse, mockNext); expect(mockResponse.statusCode).toBe(200); const response = JSON.parse(mockResponse._getData()); expect(response.data.length).toEqual(data.length); expect(response.meta).toEqual(meta); }); it('should return a 500 error on meta error', () => { meta.error = { id: 'foo', message: 'bar', errors: [], fields: {}, }; mockRequest = createRequest({ data: data, meta: meta, }); controller.apiResponse(mockRequest, mockResponse, mockNext); expect(mockResponse.statusCode).toBe(500); const response = JSON.parse(mockResponse._getData()); expect(response.data.length).toEqual(data.length); expect(response.meta).toEqual(meta); }); }); describe('"populateMeta()"', () => { let mockRequest: MockRequest; let mockResponse: MockResponse; let data: MockModelDocument[] = []; let error: any; let nrOfDocuments: number; beforeEach(() => { mockResponse = createResponse(); error = null; data = [ new mockModel(), new mockModel() ]; nrOfDocuments = 111; mockModel.countDocuments = jest.fn().mockImplementation((_query) => Promise.resolve(nrOfDocuments)); }); it('should append meta data to request with strings', async() => { const offset = '10'; const limit = '10'; mockRequest = createRequest({ data: data, modelQuery: { total: 'query', offset: offset, limit: limit, }, }); await controller.populateMeta(mockRequest, mockResponse, mockNext); expect(mockRequest.meta).toBeTruthy(); expect(mockRequest.meta.total).toEqual(nrOfDocuments); expect(mockRequest.meta.count).toEqual(data.length); expect(mockRequest.meta.offset).toEqual(parseInt(offset, 10)); expect(mockRequest.meta.limit).toEqual(parseInt(limit, 10)); }); it('should append meta data to request with numbers', async() => { const offset = 10; const limit = 10; mockRequest = createRequest({ data: data, modelQuery: { offset: offset, limit: limit, }, }); await controller.populateMeta(mockRequest, mockResponse, mockNext); expect(mockRequest.meta).toBeTruthy(); expect(mockRequest.meta.total).toEqual(nrOfDocuments); expect(mockRequest.meta.count).toEqual(data.length); expect(mockRequest.meta.offset).toEqual(offset); expect(mockRequest.meta.limit).toEqual(limit); }); it('should append meta data even if no data found', async() => { const offset = 10; const limit = 10; mockRequest = createRequest({ data: null, modelQuery: { offset: offset, limit: limit, }, }); await controller.populateMeta(mockRequest, mockResponse, mockNext); expect(mockRequest.meta).toBeTruthy(); expect(mockRequest.meta.total).toEqual(nrOfDocuments); expect(mockRequest.meta.count).toEqual(0); expect(mockRequest.meta.offset).toEqual(offset); expect(mockRequest.meta.limit).toEqual(limit); }); it('should return next, if error occurs', async () => { mockRequest = createRequest({ modelQuery: {}, }); error = { id: 'mockError', message: 'mockMessage', errors: [], fields: {}, }; mockModel.countDocuments = jest.fn().mockImplementation((_query) => Promise.reject(error)); await controller.populateMeta(mockRequest, mockResponse, mockNext); expect(mockNext).toHaveBeenCalledWith(error); }); }); describe('"parsePagination()"', () => { it('should also parse pagination on a string value', () => { const mockValue = controller.parsePagination('10', '100'); expect(mockValue).toBe(10); }); }); });