| /* |
| * Copyright 2018-present Open Networking Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| import { TestBed, inject } from '@angular/core/testing'; |
| |
| import { LogService } from '../log.service'; |
| import { WebSocketService, WsOptions, Callback, EventType } from './websocket.service'; |
| import { FnService } from '../util/fn.service'; |
| import { GlyphService } from '../svg/glyph.service'; |
| import { ActivatedRoute, Params } from '@angular/router'; |
| import { UrlFnService } from './urlfn.service'; |
| import { WSock } from './wsock.service'; |
| import { of } from 'rxjs'; |
| |
| class MockActivatedRoute extends ActivatedRoute { |
| constructor(params: Params) { |
| super(); |
| this.queryParams = of(params); |
| } |
| } |
| |
| class MockGlyphService {} |
| |
| /** |
| * ONOS GUI -- Remote -- Web Socket Service - Unit Tests |
| */ |
| describe('WebSocketService', () => { |
| let wss: WebSocketService; |
| let fs: FnService; |
| let ar: MockActivatedRoute; |
| let windowMock: Window; |
| let logServiceSpy: jasmine.SpyObj<LogService>; |
| |
| const noop = () => ({}); |
| const send = jasmine.createSpy('send') |
| .and.callFake((ev) => ev); |
| const mockWebSocket = { |
| send: send, |
| onmessage: (msgEvent) => ({}), |
| onopen: () => ({}), |
| onclose: () => ({}), |
| }; |
| |
| beforeEach(() => { |
| const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']); |
| ar = new MockActivatedRoute({'debug': 'txrx'}); |
| |
| windowMock = <any>{ |
| location: <any> { |
| hostname: 'foo', |
| host: 'foo', |
| port: '80', |
| protocol: 'http', |
| search: { debug: 'true'}, |
| href: 'ws://foo:123/onos/ui/websock/path', |
| absUrl: 'ws://foo:123/onos/ui/websock/path' |
| } |
| }; |
| fs = new FnService(ar, logSpy, windowMock); |
| |
| TestBed.configureTestingModule({ |
| providers: [WebSocketService, |
| { provide: FnService, useValue: fs }, |
| { provide: LogService, useValue: logSpy }, |
| { provide: GlyphService, useClass: MockGlyphService }, |
| { provide: UrlFnService, useValue: new UrlFnService(logSpy, windowMock) }, |
| { provide: 'Window', useFactory: (() => windowMock ) }, |
| { provide: WSock, useFactory: (() => { |
| return { |
| newWebSocket: (() => mockWebSocket) |
| }; |
| }) |
| } |
| ] |
| }); |
| |
| wss = TestBed.get(WebSocketService); |
| logServiceSpy = TestBed.get(LogService); |
| }); |
| |
| it('should define WebSocketService', () => { |
| expect(wss).toBeDefined(); |
| }); |
| |
| it('should define api functions', () => { |
| expect(fs.areFunctions(wss, ['bootstrap', 'error', |
| 'handleOpen', 'handleMessage', 'handleClose', |
| 'findGuiSuccessor', 'informListeners', 'send', |
| 'noHandlersWarn', 'resetState', |
| 'createWebSocket', 'bindHandlers', 'unbindHandlers', |
| 'addOpenListener', 'removeOpenListener', 'sendEvent', |
| 'setVeilDelegate', 'setLoadingDelegate', 'isConnected', |
| 'closeWebSocket', 'isHandling' |
| ])).toBeTruthy(); |
| }); |
| |
| it('should use the appropriate URL, createWebsocket', () => { |
| const url = wss.createWebSocket(); |
| expect(url).toEqual('ws://foo:80/onos/ui/websock/core'); |
| }); |
| |
| it('should use the appropriate URL with modified port, createWebsocket', |
| () => { |
| const url = wss.createWebSocket(<WsOptions>{ wsport: 1243 }); |
| expect(url).toEqual('ws://foo:1243/onos/ui/websock/core'); |
| }); |
| |
| it('should verify websocket event handlers, createWebsocket', () => { |
| wss.createWebSocket({ wsport: 1234 }); |
| expect(fs.isF(mockWebSocket.onopen)).toBeTruthy(); |
| expect(fs.isF(mockWebSocket.onmessage)).toBeTruthy(); |
| expect(fs.isF(mockWebSocket.onclose)).toBeTruthy(); |
| }); |
| |
| it('should invoke listener callbacks when websocket is up, handleOpen', |
| () => { |
| let num = 0; |
| function incrementNum(host: string, url: string) { |
| expect(host).toEqual('foo'); |
| num++; |
| } |
| wss.addOpenListener(incrementNum); |
| wss.createWebSocket({ wsport: 1234 }); |
| |
| mockWebSocket.onopen(); |
| expect(num).toBe(1); |
| }); |
| |
| it('should send pending events, handleOpen', () => { |
| const fakeEvent = { |
| event: 'mockEv', |
| payload: { mock: 'thing' } |
| }; |
| wss.sendEvent(fakeEvent.event, fakeEvent.payload); |
| // on opening the socket, a single authentication event should have |
| // been sent already... |
| expect(mockWebSocket.send.calls.count()).toEqual(1); |
| |
| wss.createWebSocket({ wsport: 1234 }); |
| mockWebSocket.onopen(); |
| expect(mockWebSocket.send).toHaveBeenCalledWith(JSON.stringify(fakeEvent)); |
| }); |
| |
| it('should handle an incoming bad JSON message, handleMessage', () => { |
| const badMsg = { |
| data: 'bad message' |
| }; |
| wss.createWebSocket({ wsport: 1234 }); |
| expect(mockWebSocket.onmessage(badMsg)).toBeNull(); |
| expect(logServiceSpy.error).toHaveBeenCalled(); |
| }); |
| |
| it('should verify message was handled, handleMessage', () => { |
| let num = 0; |
| function fakeHandler(data1: Object) { num++; } |
| const data = JSON.stringify(<EventType>{ |
| event: 'mockEvResp', |
| payload: {} |
| }); |
| const event = { |
| data: data |
| }; |
| |
| wss.createWebSocket({ wsport: 1234 }); |
| wss.bindHandlers(new Map<string, (data) => void>([ |
| ['mockEvResp', (data2) => fakeHandler(data2)] |
| ])); |
| expect(mockWebSocket.onmessage(event)).toBe(undefined); |
| expect(num).toBe(1); |
| }); |
| |
| it('should warn if there is an unhandled event, handleMessage', () => { |
| const data = { foo: 'bar', bar: 'baz'}; |
| const dataString = JSON.stringify(data); |
| const badEv = { |
| data: dataString |
| }; |
| wss.createWebSocket({ wsport: 1234 }); |
| mockWebSocket.onmessage(badEv); |
| expect(logServiceSpy.warn).toHaveBeenCalledWith('Unhandled event:', data); |
| }); |
| |
| it('should not warn if valid input, bindHandlers', () => { |
| expect(wss.bindHandlers(new Map<string, (data) => void>([ |
| ['test', noop ], |
| ['bar', noop ] |
| ]))).toBe(undefined); |
| |
| expect(logServiceSpy.warn).not.toHaveBeenCalled(); |
| }); |
| |
| it('should warn if no arguments, bindHandlers', () => { |
| expect(wss.bindHandlers( |
| new Map<string, (data) => void>([]) |
| )).toBeNull(); |
| expect(logServiceSpy.warn).toHaveBeenCalledWith( |
| 'WSS.bindHandlers(): no event handlers' |
| ); |
| }); |
| |
| it('should warn if duplicate handlers were given, bindHandlers', |
| () => { |
| wss.bindHandlers( |
| new Map<string, (data) => void>([ |
| ['noop', noop ] |
| ]) |
| ); |
| expect(wss.bindHandlers( |
| new Map<string, (data) => void>([ |
| ['noop', noop ] |
| ]) |
| )).toBe(undefined); |
| expect(logServiceSpy.warn).toHaveBeenCalledWith('duplicate bindings ignored:', |
| ['noop']); |
| }); |
| |
| it('should warn if no arguments, unbindHandlers', () => { |
| expect(wss.unbindHandlers([])).toBeNull(); |
| expect(logServiceSpy.warn).toHaveBeenCalledWith( |
| 'WSS.unbindHandlers(): no event handlers' |
| ); |
| }); |
| // Note: cannot test unbindHandlers' forEach due to it using closure variable |
| |
| it('should not warn if valid argument, addOpenListener', () => { |
| let o = wss.addOpenListener(noop); |
| expect(o.id === 1); |
| expect(o.cb === noop); |
| expect(logServiceSpy.warn).not.toHaveBeenCalled(); |
| o = wss.addOpenListener(noop); |
| expect(o.id === 2); |
| expect(o.cb === noop); |
| expect(logServiceSpy.warn).not.toHaveBeenCalled(); |
| }); |
| |
| it('should log error if callback not a function, addOpenListener', |
| () => { |
| const o = wss.addOpenListener(null); |
| expect(o.id === 1); |
| expect(o.cb === null); |
| expect(o.error === 'No callback defined'); |
| expect(logServiceSpy.error).toHaveBeenCalledWith( |
| 'WSS.addOpenListener(): callback not a function' |
| ); |
| }); |
| |
| it('should not warn if valid listener object, removeOpenListener', () => { |
| expect(wss.removeOpenListener(<Callback>{ |
| id: 1, |
| error: 'error', |
| cb: noop |
| })).toBe(undefined); |
| expect(logServiceSpy.warn).not.toHaveBeenCalled(); |
| }); |
| |
| it('should warn if listener is invalid, removeOpenListener', () => { |
| expect(wss.removeOpenListener(<Callback>{})).toBeNull(); |
| expect(logServiceSpy.warn).toHaveBeenCalledWith( |
| 'WSS.removeOpenListener(): invalid listener', {} |
| ); |
| }); |
| |
| // Note: handleClose is not currently tested due to all work it does relies |
| // on closure variables that cannot be mocked |
| |
| }); |