UNPKG

mozaik

Version:

Mozaik dashboard composition tool

386 lines (284 loc) 14.9 kB
/* global describe it */ import expect from 'expect'; import sinon from 'sinon'; import mockery from 'mockery'; import Promise from 'bluebird'; const sandbox = sinon.sandbox.create(); let mockedMozaik; let Bus, bus; let clock; let apiStub; let thenStub, catchStub; let apiParams; let apiSpy; let clientSpy; describe('Mozaïk | Bus', () => { before(() => { mockery.enable({ warnOnReplace: false, warnOnUnregistered: false }); }); beforeEach(() => { mockery.registerMock('chalk', require('./chalk-mock')); clock = sinon.useFakeTimers(); mockedMozaik = { logger: { info: sinon.spy(), error: sinon.spy() }, config: { apisPollInterval: 15000 } }; Bus = require('../../src/Bus').default; bus = Bus(mockedMozaik); }); afterEach(() => { sandbox.verifyAndRestore(); clock.restore(); mockery.deregisterAll(); }); after(() => { mockery.disable(); }); describe('registerApi()', () => { it('should make the API available', () => { bus.registerApi('test_api', () => { }); expect(bus.listApis()).toEqual(['test_api']); expect(mockedMozaik.logger.info.calledOnce).toEqual(true); expect(mockedMozaik.logger.info.getCall(0).args[0]).toEqual(`registered API 'test_api' (mode: poll)`); }); it('should throw if the API was already registered', () => { bus.registerApi('test_api', () => { }); expect(mockedMozaik.logger.info.calledOnce).toEqual(true); expect(mockedMozaik.logger.info.getCall(0).args[0]).toEqual(`registered API 'test_api' (mode: poll)`); expect(() => { bus.registerApi('test_api', () => { }); }).toThrow(`API 'test_api' already registered`); expect(mockedMozaik.logger.error.calledOnce).toEqual(true); expect(mockedMozaik.logger.error.getCall(0).args[0]).toEqual(`API 'test_api' already registered`); }); it(`should allow to set API mode to 'push'`, () => { bus.registerApi('test_api', () => { }, 'push'); expect(bus.listApis()).toEqual(['test_api']); expect(mockedMozaik.logger.info.calledOnce).toEqual(true); expect(mockedMozaik.logger.info.getCall(0).args[0]).toEqual(`registered API 'test_api' (mode: push)`); }); it('should throw if we pass an invalid API mode', () => { expect(() => { bus.registerApi('test_api', () => { }, 'invalid'); }).toThrow(`API mode 'invalid' is not a valid mode, must be one of 'poll' or 'push'`); expect(mockedMozaik.logger.error.calledOnce).toEqual(true); expect(mockedMozaik.logger.error.getCall(0).args[0]).toEqual(`API mode 'invalid' is not a valid mode, must be one of 'poll' or 'push'`); }); }); describe('addClient()', () => { it('should add a client to the current list', () => { bus.addClient({}, 'test_client'); expect(bus.listClients()['test_client']).toExist(`'test_client' has not been added to the client list`); expect(mockedMozaik.logger.info.calledOnce).toEqual(true); expect(mockedMozaik.logger.info.getCall(0).args[0]).toEqual('Client #test_client connected'); }); it('should throw if a client with the same id already exists', () => { bus.addClient({}, 'test_client'); expect(() => { bus.addClient({}, 'test_client'); }).toThrow(`Client with id 'test_client' already exists`); expect(mockedMozaik.logger.error.calledOnce).toEqual(true); expect(mockedMozaik.logger.error.getCall(0).args[0]).toEqual(`Client with id 'test_client' already exists`); }); }); describe('removeClient()', () => { it('should remove a registered client from the current list', () => { bus.addClient({}, 'test_client'); expect(bus.listClients()['test_client']).toExist(); bus.removeClient('test_client'); expect(bus.listClients()['test_client']).toNotExist(); expect(mockedMozaik.logger.info.calledTwice).toEqual(true, 'logger.info() have not been called twice'); expect(mockedMozaik.logger.info.getCall(0).args[0]).toEqual('Client #test_client connected'); expect(mockedMozaik.logger.info.getCall(1).args[0]).toEqual('Client #test_client disconnected'); }); it('should cleanup subscription and remove timer if no clients left', () => { bus.addClient({ send() {} }, 'test_client'); expect(bus.listClients()['test_client']).toExist(); bus.registerApi('test_api', () => ({ test() { return Promise.resolve(true); }})); expect(bus.listApis()).toEqual(['test_api']); bus.clientSubscription('test_client', { id: 'test_api.test' }); const subscriptions = bus.listSubscriptions(); expect(subscriptions['test_api.test']).toExist(); expect(subscriptions['test_api.test'].timer).toExist(); expect(subscriptions['test_api.test'].clients).toEqual(['test_client']); bus.removeClient('test_client'); expect(subscriptions['test_api.test'].timer).toNotExist(); expect(subscriptions['test_api.test'].clients).toEqual([]); }); }); describe('clientSubscription()', () => { it('should log an error if there is no existing client having given id', () => { apiSpy = { fetch: sinon.spy() }; bus.registerApi('test_api', () => apiSpy); bus.clientSubscription('test_client'); const expectedError = `Unable to find a client with id 'test_client'`; expect(apiSpy.fetch.called).toEqual(false); expect(mockedMozaik.logger.error.calledOnce).toEqual(true); expect(mockedMozaik.logger.error.getCall(0).args[0]).toEqual(expectedError); }); it('should throw and log an error if the request id is invalid', () => { apiSpy = { fetch: sinon.spy() }; bus.registerApi('test_api', () => apiSpy); bus.addClient({}, 'test_client'); const expectedError = `Invalid request id 'test_api', should be something like 'api_id.method'`; expect(() => { bus.clientSubscription('test_client', { id: 'test_api' }); }).toThrow(expectedError); expect(apiSpy.fetch.called).toEqual(false, 'Api method should not be called'); expect(mockedMozaik.logger.error.calledOnce).toEqual(true); expect(mockedMozaik.logger.error.getCall(0).args[0]).toEqual(expectedError); }); it('should throw and log an error if there is no existing api for given request id', () => { apiSpy = { fetch: sinon.spy() }; bus.registerApi('test_api', () => apiSpy); bus.addClient({}, 'test_client'); const expectedError = `Unable to find API matching id 'invalid_api'`; expect(() => { bus.clientSubscription('test_client', { id: 'invalid_api.invalid_method' }); }).toThrow(expectedError); expect(apiSpy.fetch.called).toEqual(false, 'Api method should not be called'); expect(mockedMozaik.logger.error.calledOnce).toEqual(true); expect(mockedMozaik.logger.error.getCall(0).args[0]).toEqual(expectedError); }); it('should throw and log an error if the api method does not exists', () => { apiSpy = { fetch: sinon.spy() }; bus.registerApi('test_api', () => apiSpy); bus.addClient({}, 'test_client'); const expectedError = `Unable to find API method matching 'invalid_method'`; expect(() => { bus.clientSubscription('test_client', { id: 'test_api.invalid_method' }); }).toThrow(expectedError); expect(apiSpy.fetch.called).toEqual(false, 'Api method should not be called'); expect(mockedMozaik.logger.error.calledOnce).toEqual(true); expect(mockedMozaik.logger.error.getCall(0).args[0]).toEqual(expectedError); }); it('should throw and log an error if the api method is not a function', () => { bus.registerApi('test_api', () => ({ method: false })); bus.addClient({}, 'test_client'); const expectedError = `API method 'test_api.method' MUST be a function`; expect(() => { bus.clientSubscription('test_client', { id: 'test_api.method' }); }).toThrow(expectedError); expect(mockedMozaik.logger.error.calledOnce).toEqual(true); expect(mockedMozaik.logger.error.getCall(0).args[0]).toEqual(expectedError); }); it(`should immediately call the api if there's no matching subscription`, () => { const apiData = { test: true }; thenStub = sinon.stub(); catchStub = sinon.stub(); apiStub = sinon.stub().returns({ then: thenStub }); thenStub.yields(apiData).returns({ 'catch': catchStub }); bus.registerApi('test_api', () => ({ fetch: apiStub })); clientSpy = { send: sinon.spy() }; bus.addClient(clientSpy, 'test_client'); bus.clientSubscription('test_client', { id: 'test_api.fetch' }); expect(apiStub.calledOnce).toEqual(true, 'API method should have been called'); expect(clientSpy.send.calledOnce).toEqual(true, 'API data should have been sent to the client'); expect(clientSpy.send.getCall(0).args[0]).toEqual(JSON.stringify({ id: 'test_api.fetch', body: apiData })); }); it(`should create a timer if there's no matching subscription`, () => { const apiData = { test: true }; thenStub = sinon.stub(); catchStub = sinon.stub(); apiStub = sinon.stub().returns({ then: thenStub }); thenStub.yields(apiData).returns({ 'catch': catchStub }); bus.registerApi('test_api', () => ({ fetch: apiStub })); clientSpy = { send: sinon.spy() }; bus.addClient(clientSpy, 'test_client'); bus.clientSubscription('test_client', { id: 'test_api.fetch' }); clock.tick(15000); expect(apiStub.callCount).toEqual(2, 'API method should have been called'); expect(clientSpy.send.callCount).toEqual(2, 'API data should have been sent to the client'); expect(clientSpy.send.alwaysCalledWith(JSON.stringify({ id: 'test_api.fetch', body: apiData }))).toEqual(true); const subscriptions = bus.listSubscriptions(); expect(subscriptions['test_api.fetch']).toExist(); expect(subscriptions['test_api.fetch'].timer).toExist(); }); it(`should create a producer if there's no matching subscription and API mode is 'push'`, () => { apiSpy = sinon.spy(); bus.registerApi('test_api', () => ({ push: apiSpy }), 'push'); clientSpy = { send: sinon.spy() }; bus.addClient(clientSpy, 'test_client'); bus.clientSubscription('test_client', { id: 'test_api.push' }); expect(apiSpy.calledOnce).toEqual(true, 'API method should have been called'); const subscriptions = bus.listSubscriptions(); expect(subscriptions['test_api.push']).toExist(); expect(subscriptions['test_api.push'].timer).toNotExist(); }); it('should not add the same client id twice to the subscription client list', () => { bus.registerApi('test_api', () => ({ push: () => {} }), 'push'); bus.addClient({ send: () => {} }, 'test_client'); bus.clientSubscription('test_client', { id: 'test_api.push' }); const subscriptions = bus.listSubscriptions(); expect(subscriptions['test_api.push']).toExist(); expect(subscriptions['test_api.push'].clients).toEqual(['test_client']); bus.clientSubscription('test_client', { id: 'test_api.push' }); expect(subscriptions['test_api.push'].clients).toEqual(['test_client']); }); }); describe('clientCount()', () => { it('should return the number of connected clients', () => { expect(bus.clientCount()).toEqual(0); bus.addClient({}, 'client_a'); bus.addClient({}, 'client_b'); bus.addClient({}, 'client_c'); expect(bus.clientCount()).toEqual(3); }); }); describe('processApiCall()', () => { let api_params; it('should log api call', () => { apiStub = sinon.stub(); thenStub = sinon.stub(); catchStub = sinon.stub(); thenStub.returns({ 'catch': catchStub }); apiStub.returns({ then: thenStub }); bus.processApiCall('test_api.test_method', apiStub); expect(mockedMozaik.logger.info.calledOnce).toEqual(true); expect(mockedMozaik.logger.info.getCall(0).args[0]).toEqual(`Calling 'test_api.test_method'`); }); it('should call api', () => { apiStub = sinon.stub(); thenStub = sinon.stub(); catchStub = sinon.stub(); thenStub.returns({ 'catch': catchStub }); apiStub.returns({ then: thenStub }); api_params = { api_param: 'api_param' }; bus.processApiCall('test_api.test_method', apiStub, api_params); expect(apiStub.calledOnce).toEqual(true, 'should have called the given api method'); expect(apiStub.getCall(0).args[0]).toEqual(api_params); }); it('should cache result', () => { bus.listSubscriptions()['test_api.test_method'] = { clients: [] }; apiStub = sinon.stub(); thenStub = sinon.stub(); catchStub = sinon.stub(); thenStub.yields('sample_data').returns({ 'catch': catchStub }); apiStub.returns({ then: thenStub }); bus.processApiCall('test_api.test_method', apiStub); expect(bus.listSubscriptions()['test_api.test_method'].cached).toExist; expect(bus.listSubscriptions()['test_api.test_method'].cached).toEqual({ id: 'test_api.test_method', body: 'sample_data' }); }); }); });