UNPKG

apimocker

Version:

Simple HTTP server that returns mock service API responses to your front end.

450 lines (392 loc) 16 kB
// run "grunt test", or run "mocha" in this test directory to execute. const path = require('path'); // better assertions than node offers. const chai = require('chai'); const sinon = require('sinon'); const untildify = require('untildify'); const apiMocker = require('../lib/apimocker'); const { assert, expect } = chai; describe('unit tests: ', () => { chai.config.includeStack = true; describe('createServer: ', () => { it('sets defaults when no options are passed in', () => { const mocker = apiMocker.createServer(); expect(mocker.options.port).to.equal('8888'); expect(mocker.options.mockDirectory).to.equal('./mocks/'); expect(mocker.options.allowedDomains.length).to.equal(1); expect(mocker.options.allowedDomains[0]).to.equal('*'); expect(mocker.options.allowedHeaders[0]).to.equal('Content-Type'); expect(mocker.options.quiet).to.equal(undefined); expect(mocker.options.logRequestHeaders).to.equal(false); }); it('overrides defaults with command line args', () => { const mocker = apiMocker.createServer({ port: 1234, quiet: true, foo: 'bar' }); expect(mocker.options.port).to.equal(1234); expect(mocker.options.mockDirectory).to.equal('./mocks/'); expect(mocker.options.allowedDomains[0]).to.equal('*'); expect(mocker.options.quiet).to.equal(true); expect(mocker.options.foo).to.equal('bar'); }); }); describe('setConfigFile: ', () => { const mocker = apiMocker.createServer(); beforeEach(() => { delete mocker.configFilePath; }); after(() => { delete mocker.configFilePath; }); it('should set a relative path correctly using node path resolver', () => { assert.equal( path.resolve('../config.json'), mocker.setConfigFile('../config.json').configFilePath ); }); it('should set an absolute path correctly', () => { const absolutePath = path.normalize('/foo/bar/config.json'); expect(mocker.setConfigFile(absolutePath).configFilePath).to.equal(absolutePath); }); it('sets no path, if none is passed in', () => { expect(mocker.setConfigFile().configFilePath).to.equal(undefined); }); }); describe('loadConfigFile: ', () => { const testSimpleConfig = require('./test-simple-config.json'); it('sets options from new format mock config file', () => { const mocker = apiMocker.createServer({ quiet: true }); mocker.setConfigFile('test/test-simple-config.json'); mocker.loadConfigFile(); expect(mocker.options.port).to.equal(testSimpleConfig.port); expect(mocker.options.allowedDomains[0]).to.equal(testSimpleConfig.allowedDomains[0]); expect(mocker.options.allowedHeaders[0]).to.equal('my-custom1'); expect(mocker.options.allowedHeaders[1]).to.equal('my-custom2'); expect(mocker.options.webServices.first).to.eql(mocker.options.webServices['1st']); delete mocker.options.webServices['1st']; expect(mocker.options.webServices).to.deep.equal(testSimpleConfig.webServices); expect(mocker.options.quiet).to.equal(true); expect(mocker.options.latency).to.equal(testSimpleConfig.latency); expect(mocker.options.logRequestHeaders).to.equal(testSimpleConfig.logRequestHeaders); }); it('combines values from defaults, options, and config file', () => { let mocker = apiMocker.createServer({ quiet: true, test: 'fun', port: 2323 }); mocker = mocker.setConfigFile('test/test-partial-config.json'); mocker.loadConfigFile(); // value from config file expect(mocker.options.port).to.equal(8765); expect(mocker.options.latency).to.equal(99); expect(mocker.options.logRequestHeaders).to.equal(false); // value from defaults expect(mocker.options.allowedDomains[0]).to.equal('*'); expect(mocker.options.webServices).to.deep.equal(mocker.defaults.webServices); // value from options passed in to createServer: expect(mocker.options.test).to.equal('fun'); }); it('expands ~ in mockDirectory setting', () => { const mocker = apiMocker.createServer({ quiet: true }); mocker.setConfigFile('test/test-simple-config.json'); mocker.loadConfigFile(); expect(mocker.options.mockDirectory).to.equal(untildify(testSimpleConfig.mockDirectory)); }); it('supports js config files that export a function', () => { const mocker = apiMocker.createServer({ quiet: false }); const port = 1111; mocker.setConfigFile('test/test-function-config.js'); mocker.loadConfigFile(); expect(mocker.options.port).to.equal(port); }); it('should not allow requests that avoid pre flight by default', () => { const mocker = apiMocker.createServer({ quiet: true }); expect(mocker.options.allowAvoidPreFlight).to.equal(false); }); it('should allow requests that avoid pre flight if specified in config', () => { const mocker = apiMocker.createServer({ quiet: true }); mocker.setConfigFile('test/test-partial-config.json'); mocker.loadConfigFile(); expect(mocker.options.allowAvoidPreFlight).to.equal(true); }); }); describe('setSwitchOptions: ', () => { let mocker; let svcOptions; let reqStub; beforeEach(() => { mocker = apiMocker.createServer({ quiet: true }); svcOptions = { switch: 'productId', mockFile: 'base' }; reqStub = { body: {}, params: {}, query: {}, header: () => {} }; }); it('does not set mock file path if switch is not found in request', () => { mocker.setSwitchOptions(svcOptions, reqStub); expect(svcOptions.mockFile).to.equal('base'); }); it('sets correct mock file path if switch is found in query string', () => { reqStub.query = { productId: '123' }; mocker.setSwitchOptions(svcOptions, reqStub); expect(svcOptions.mockFile).to.equal('productId123.base'); }); it('sets correct mock file path if switch is found in json body', () => { reqStub.body.productId = '678'; mocker.setSwitchOptions(svcOptions, reqStub); expect(svcOptions.mockFile).to.equal('productId678.base'); }); it('sets correct mock file path if switch is found in route parameter', () => { reqStub.params = { productId: '123' }; mocker.setSwitchOptions(svcOptions, reqStub); expect(svcOptions.mockFile).to.equal('productId123.base'); }); it('sets correct mock file path if switch is found in request header with matching case', () => { reqStub.header = () => '765'; mocker.setSwitchOptions(svcOptions, reqStub); expect(svcOptions.mockFile).to.equal('productId765.base'); }); it('sets correct mock file path if switch is found in request header with different case', () => { reqStub.header = () => '765'; svcOptions = { switch: 'PRodUCTID', mockFile: 'base' }; mocker.setSwitchOptions(svcOptions, reqStub); expect(svcOptions.mockFile).to.equal('PRodUCTID765.base'); }); it('sets correct mock file path with switch and nested path', () => { reqStub.body.productId = '678'; svcOptions.mockFile = 'path/to/base'; mocker.setSwitchOptions(svcOptions, reqStub); expect(svcOptions.mockFile).to.equal('path/to/productId678.base'); }); it('sets correct mock file path with switch value containing special character', () => { reqStub.body.productId = 'abc/123'; mocker.setSwitchOptions(svcOptions, reqStub); expect(svcOptions.mockFile).to.equal('productIdabc%2F123.base'); }); it('sets correct mock file path with two switch values', () => { svcOptions.switch = ['productId', 'color']; reqStub.body.productId = '345'; reqStub.body.color = 'red'; mocker.setSwitchOptions(svcOptions, reqStub); expect(svcOptions.mockFile).to.equal('productId345colorred.base'); }); it('sets correct http status based on matching switch value', () => { svcOptions.switch = 'password'; svcOptions.switchResponses = { passwordgood: { httpStatus: 200 } }; reqStub.body.password = 'good'; mocker.setSwitchOptions(svcOptions, reqStub); expect(svcOptions.httpStatus).to.equal(200); }); it('sets correct mock file path when switch matches and switchResponse contains a mockFile', () => { reqStub.body.productId = '678'; svcOptions.switchResponses = { productId678: { mockFile: 'specialFileName' } }; mocker.setSwitchOptions(svcOptions, reqStub); expect(svcOptions.mockFile).to.equal('specialFileName'); }); it('sets correct http status when switch value does not match', () => { svcOptions.switch = 'password'; svcOptions.httpStatus = 401; svcOptions.switchResponses = { passwordgood: { httpStatus: 200 } }; reqStub.body.password = 'bad'; mocker.setSwitchOptions(svcOptions, reqStub); expect(svcOptions.httpStatus).to.equal(401); }); it('sets correct http status when two switches match', () => { svcOptions.switch = ['userId', 'password']; svcOptions.httpStatus = 401; svcOptions.switchResponses = { userId1234passwordgood: { httpStatus: 200 } }; reqStub.body.password = 'good'; reqStub.body.userId = '1234'; mocker.setSwitchOptions(svcOptions, reqStub); expect(svcOptions.httpStatus).to.equal(200); }); it('sets correct mock file path when switch uses JsonPath and switch matches', () => { svcOptions.switch = '$.car.engine.part'; svcOptions.switchResponses = { '$.car.engine.partTiming%20Belt': { mockFile: 'product456' } }; reqStub.body = { car: { engine: { part: 'Timing Belt' } } }; mocker.setSwitchOptions(svcOptions, reqStub); expect(svcOptions.mockFile).to.equal('product456'); }); it('sets correct mock file path when switch uses JsonPath and switch value does not match', () => { svcOptions.switch = '$.car.engine.part'; svcOptions.switchResponses = { '$.car.engine.partTiming%20Belt': { mockFile: 'product456' } }; reqStub.body = { car: { wheel: {} } }; mocker.setSwitchOptions(svcOptions, reqStub); expect(svcOptions.mockFile).to.equal('base'); }); it('sets correct mock file path when switch uses JsonPath as a switch object and switch matches', () => { svcOptions.switch = { type: 'jsonpath', switch: '$.car.engine.part' }; svcOptions.switchResponses = { '$.car.engine.partTiming%20Belt': { mockFile: 'product456' } }; reqStub.body = { car: { engine: { part: 'Timing Belt' } } }; mocker.setSwitchOptions(svcOptions, reqStub); expect(svcOptions.mockFile).to.equal('product456'); }); it('sets correct mock file path when switch uses JsonPath and switch value does not match', () => { svcOptions.switch = { type: 'jsonpath', switch: '$.car.engine.part' }; svcOptions.switchResponses = { '$.car.engine.partTiming%20Belt': { mockFile: 'product456' } }; reqStub.body = { car: { wheel: {} } }; mocker.setSwitchOptions(svcOptions, reqStub); expect(svcOptions.mockFile).to.equal('base'); }); it('sets the correct mock file path when switch uses RegExp and switch matches', () => { svcOptions.switch = '/"carEnginePart([^"]*)"/'; svcOptions.switchResponses = { '/"carEnginePart([^"]*)"/Belt': { mockFile: 'product456' } }; reqStub.body = '"carPartWheel": wheel,\n"carEnginePartBelt": belt'; mocker.setSwitchOptions(svcOptions, reqStub); expect(svcOptions.mockFile).to.equal('product456'); }); it('sets the correct mock file path when switch uses RegExp and switch value does not match', () => { svcOptions.switch = '/"carEnginePart([^"]*)"/'; svcOptions.switchResponses = { Belt: { mockFile: 'product456' } }; reqStub.body = '"carPartWheel": wheel'; mocker.setSwitchOptions(svcOptions, reqStub); expect(svcOptions.mockFile).to.equal('base'); }); it('sets the correct mock file path when switch uses RegExp in a switch object and switch matches', () => { svcOptions.switch = { type: 'regexp', switch: '/"carEnginePart([^"]*)"/', key: 'carenginepart' }; svcOptions.switchResponses = { carenginepartBelt: { mockFile: 'product456' } }; reqStub.body = '"carPartWheel": wheel,\n"carEnginePartBelt": belt'; mocker.setSwitchOptions(svcOptions, reqStub); expect(svcOptions.mockFile).to.equal('product456'); }); it('sets the correct mock file path when switch uses RegExp in a switch object and switch does not match', () => { svcOptions.switch = { type: 'regexp', switch: '/"carEnginePart([^"]*)"/', key: 'carenginepart' }; svcOptions.switchResponses = { carenginepartBelt: { mockFile: 'product456' } }; reqStub.body = '"carPartWheel": wheel'; mocker.setSwitchOptions(svcOptions, reqStub); expect(svcOptions.mockFile).to.equal('base'); }); }); describe('setRoute:', () => { const am = apiMocker.createServer(); it('sets no default http status code', () => { const options = { verb: 'get', latency: 0, serviceUrl: 'foo.com', mockFile: 'file.json' }; am.setRoute(options); expect(options.httpStatus).to.equal(undefined); }); }); describe('setRoutes:', () => { const am = apiMocker.createServer(); let setRouteMock; beforeEach(() => { setRouteMock = sinon.mock(am, 'setRoute'); }); afterEach(() => { setRouteMock.restore(); }); it('calls setRoute with a simple service definition', () => { const webServices = { first: { mockFile: 'king.json', latency: 20, verbs: ['get', 'post'] } }; am.options.webServices = webServices; setRouteMock.expects('setRoute').withExactArgs({ latency: 20, mockFile: 'king.json', serviceUrl: 'first', verb: 'get' }); setRouteMock.expects('setRoute').withExactArgs({ latency: 20, mockFile: 'king.json', serviceUrl: 'first', verb: 'post' }); am.setRoutes(webServices); setRouteMock.verify(); }); it('calls setRoute with complex service definition', () => { const webServices = { second: { verbs: ['delete', 'post'], responses: { delete: { httpStatus: 204 }, post: { contentType: 'foobar', mockFile: 'king.json' } } } }; am.options.webServices = webServices; setRouteMock.expects('setRoute').withExactArgs({ httpStatus: 204, latency: 0, serviceUrl: 'second', verb: 'delete' }); setRouteMock.expects('setRoute').withExactArgs({ latency: 0, serviceUrl: 'second', verb: 'post', contentType: 'foobar', mockFile: 'king.json' }); am.setRoutes(webServices); setRouteMock.verify(); }); }); });