Mercurial > hg > thermostat-ng > web-client
view src/app/shared/services/command-channel.service.spec.js @ 245:2eb2ae0f3b3f
Transition project to Angular 4 Hybrid
Reviewed-by: jkang
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-October/025266.html
author | Andrew Azores <aazores@redhat.com> |
---|---|
date | Fri, 13 Oct 2017 14:05:09 -0400 |
parents | becd239128c2 |
children |
line wrap: on
line source
/** * Copyright 2012-2017 Red Hat, Inc. * * Thermostat is distributed under the GNU General Public License, * version 2 or any later version (with a special exception described * below, commonly known as the "Classpath Exception"). * * A copy of GNU General Public License (GPL) is included in this * distribution, in the file COPYING. * * Linking Thermostat code with other modules is making a combined work * based on Thermostat. Thus, the terms and conditions of the GPL * cover the whole combination. * * As a special exception, the copyright holders of Thermostat give you * permission to link this code with independent modules to produce an * executable, regardless of the license terms of these independent * modules, and to copy and distribute the resulting executable under * terms of your choice, provided that you also meet, for each linked * independent module, the terms and conditions of the license of that * module. An independent module is a module which is not derived from * or based on Thermostat code. If you modify Thermostat, you may * extend this exception to your version of the software, but you are * not obligated to do so. If you do not wish to do so, delete this * exception statement from your version. */ import { default as servicesModule, init as initServices } from 'shared/services/services.module.js'; import configModule from 'shared/config/config.module.js'; describe('CommandChannelService', () => { let svc, scope, authService, webSocketFactory, translate; beforeEach(() => { let addEventListener = sinon.spy(); let removeEventListener = sinon.spy(); let send = sinon.spy(); let close = sinon.spy(); authService = { getCommandChannelUrl: sinon.stub().returns('http://foo:bar@example.com:1234/?token=fakeToken') }; webSocketFactory = { addEventListener: addEventListener, removeEventListener: removeEventListener, send: send, close: close, createSocket: sinon.stub().returns({ addEventListener: addEventListener, removeEventListener: removeEventListener, send: send, close: close }) }; let translateThen = sinon.stub().yields({ 'services.commandChannel.responseCodes.OK': 'Request succeeded', 'services.commandChannel.responseCodes.ERROR': 'Request failed for unknown reason', 'services.commandChannel.responseCodes.AUTH_FAIL': 'Request failed for authentication or authorization reasons', 'services.commandChannel.responseCodes.UNKNOWN': 'Request failed with unknown response type' }); translate = sinon.stub().returns({ then: translateThen }); translate.then = translateThen; angular.mock.module(configModule, $provide => { 'ngInject'; $provide.constant('commandChannelUrl', 'ws://foo-host:1234'); }); angular.mock.module(servicesModule); initServices(); angular.mock.module($provide => { 'ngInject'; $provide.value('authService', authService); $provide.value('webSocketFactory', webSocketFactory); $provide.value('$translate', translate); }); angular.mock.inject(($rootScope, commandChannelService) => { 'ngInject'; scope = $rootScope; svc = commandChannelService; }); }); it('should exist', () => { should.exist(svc); }); it('should increment sequence number on access', () => { let seqA = svc.sequence; let seqB = svc.sequence; seqA.should.be.a.Number(); seqB.should.be.a.Number(); seqB.should.equal(seqA + 1); }); it('should wrap sequence numbers back to 1', () => { svc._sequence = Number.MAX_SAFE_INTEGER - 1; svc.sequence.should.equal(Number.MAX_SAFE_INTEGER - 1); svc.sequence.should.equal(Number.MAX_SAFE_INTEGER); svc.sequence.should.equal(1); }); describe('sendMessage', () => { it('should connect to correct command channel URL', () => { svc.sendMessage('foo', 'bar'); webSocketFactory.createSocket.should.be.calledWith('http://foo:bar@example.com:1234/foo?token=fakeToken'); scope.$apply(); }); it('$translate, should reject if browser does not support WebSockets', done => { webSocketFactory.createSocket.returns(null); svc.sendMessage('foo', 'bar').catch(() => done()); scope.$apply(); }); it('should send message when socket is ready', () => { svc.sendMessage('foo'); webSocketFactory.addEventListener.should.be.calledWith('open', sinon.match.func); webSocketFactory.addEventListener.withArgs('open').args[0][1](); webSocketFactory.send.should.be.calledOnce(); webSocketFactory.send.should.be.calledWith(JSON.stringify({ type: 2, payload: {} })); }); it('should resolve with socket response respType replaced', done => { svc.sendMessage('foo', 'bar').then(v => { v.should.deepEqual({ payload: { respType: { value: 'OK', message: 'Request succeeded' } } }); done(); }); webSocketFactory.addEventListener.should.be.calledWith('message', sinon.match.func); webSocketFactory.removeEventListener.should.not.be.called(); let onmessage = webSocketFactory.addEventListener.withArgs('message').args[0][1]; onmessage({ data: JSON.stringify({ payload: { respType: 'OK' }}) }); webSocketFactory.close.should.be.calledOnce(); webSocketFactory.removeEventListener.should.be.calledWith('close'); scope.$apply(); }); it('should resolve with socket response respType replaced when respType is not recognized', done => { svc.sendMessage('foo', 'bar').then(v => { v.should.deepEqual({ payload: { respType: { value: 'UNKNOWN', message: 'Request failed with unknown response type' } } }); done(); }); webSocketFactory.addEventListener.should.be.calledWith('message', sinon.match.func); webSocketFactory.removeEventListener.should.not.be.called(); let onmessage = webSocketFactory.addEventListener.withArgs('message').args[0][1]; onmessage({ data: JSON.stringify({ payload: { respType: 'FOO_RESPONSE' }}) }); webSocketFactory.close.should.be.calledOnce(); webSocketFactory.removeEventListener.should.be.calledWith('close'); scope.$apply(); }); it('should reject on error', done => { svc.sendMessage('foo', 'bar').catch(v => { v.should.containEql('fooError'); done(); }); webSocketFactory.addEventListener.should.be.calledWith('error', sinon.match.func); webSocketFactory.addEventListener.withArgs('error').args[0][1]('fooError'); webSocketFactory.close.should.be.calledOnce(); scope.$apply(); }); it('should reject if socket closes before response message received', done => { svc.sendMessage('foo', 'bar').catch(v => { v.should.equal('fakeReason'); done(); }); webSocketFactory.addEventListener.should.be.calledWith('close', sinon.match.func); webSocketFactory.addEventListener.withArgs('close').args[0][1]({ reason: 'fakeReason' }); scope.$apply(); }); it('should reject with default message if socket closes before response message received', done => { svc.sendMessage('foo', 'bar').catch(v => { v.should.equal('No response received'); done(); }); translate.then.yields('No response received'); webSocketFactory.addEventListener.should.be.calledWith('close', sinon.match.func); webSocketFactory.addEventListener.withArgs('close').args[0][1]({}); scope.$apply(); }); }); it('should connect to websocket with credentials from authService', () => { svc.sendMessage('foo', 'bar'); webSocketFactory.createSocket.should.be.calledWith('http://foo:bar@example.com:1234/foo?token=fakeToken'); }); describe('responseCodes', () => { it('should enumerate the expected response types', () => { svc.responseCodes.should.have.size(4); svc.responseCodes.should.have.ownProperty('OK'); svc.responseCodes.OK.value.should.equal('OK'); svc.responseCodes.OK.message.should.not.equal(''); svc.responseCodes.should.have.ownProperty('ERROR'); svc.responseCodes.ERROR.value.should.equal('ERROR'); svc.responseCodes.ERROR.message.should.not.equal(''); svc.responseCodes.should.have.ownProperty('AUTH_FAIL'); svc.responseCodes.AUTH_FAIL.value.should.equal('AUTH_FAIL'); svc.responseCodes.AUTH_FAIL.message.should.not.equal(''); svc.responseCodes.should.have.ownProperty('UNKNOWN'); svc.responseCodes.UNKNOWN.value.should.equal('UNKNOWN'); svc.responseCodes.UNKNOWN.message.should.not.equal(''); }); it('should be read-only', () => { should.throws(() => { svc.responseCodes = []; }, 'property should not be assignable'); should.throws(() => { svc.responseCodes.ADDED = { value: 'ADDED', message: 'Added a new response code' }; }, 'property should be immutable'); should.throws(() => { svc.responseCodes.OK.value = 'NOK'; }, 'values should be immutable'); }); }); });