blob: d1bd5d843f93cd9a343aec45d9cde3d3d2f381a4 [file] [log] [blame]
Sean Condon83fc39f2018-04-19 18:56:13 +01001/*
Sean Condon5ca00262018-09-06 17:55:25 +01002 * Copyright 2018-present Open Networking Foundation
Sean Condon83fc39f2018-04-19 18:56:13 +01003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16import { TestBed, inject } from '@angular/core/testing';
17
Sean Condon5ca00262018-09-06 17:55:25 +010018import { LogService } from '../log.service';
Sean Condon28ecc5f2018-06-25 12:50:16 +010019import { WebSocketService, WsOptions, Callback, EventType } from './websocket.service';
20import { FnService } from '../util/fn.service';
21import { GlyphService } from '../svg/glyph.service';
Sean Condonfd6d11b2018-06-02 20:29:49 +010022import { ActivatedRoute, Params } from '@angular/router';
Sean Condon28ecc5f2018-06-25 12:50:16 +010023import { UrlFnService } from './urlfn.service';
24import { WSock } from './wsock.service';
Sean Condonfd6d11b2018-06-02 20:29:49 +010025import { of } from 'rxjs';
Sean Condon49e15be2018-05-16 16:58:29 +010026
Sean Condonfd6d11b2018-06-02 20:29:49 +010027class MockActivatedRoute extends ActivatedRoute {
28 constructor(params: Params) {
29 super();
30 this.queryParams = of(params);
31 }
32}
Sean Condon49e15be2018-05-16 16:58:29 +010033
34class MockGlyphService {}
35
Sean Condon83fc39f2018-04-19 18:56:13 +010036/**
37 * ONOS GUI -- Remote -- Web Socket Service - Unit Tests
38 */
39describe('WebSocketService', () => {
Sean Condonfd6d11b2018-06-02 20:29:49 +010040 let wss: WebSocketService;
41 let fs: FnService;
42 let ar: MockActivatedRoute;
43 let windowMock: Window;
44 let logServiceSpy: jasmine.SpyObj<LogService>;
45
46 const noop = () => ({});
47 const send = jasmine.createSpy('send')
48 .and.callFake((ev) => ev);
49 const mockWebSocket = {
50 send: send,
51 onmessage: (msgEvent) => ({}),
52 onopen: () => ({}),
53 onclose: () => ({}),
54 };
Sean Condon83fc39f2018-04-19 18:56:13 +010055
Sean Condon49e15be2018-05-16 16:58:29 +010056 beforeEach(() => {
Sean Condonfd6d11b2018-06-02 20:29:49 +010057 const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
58 ar = new MockActivatedRoute({'debug': 'txrx'});
59
60 windowMock = <any>{
61 location: <any> {
62 hostname: 'foo',
63 host: 'foo',
64 port: '80',
65 protocol: 'http',
66 search: { debug: 'true'},
Sean Condonbf7ff4f2019-03-17 16:18:42 +000067 href: 'ws://foo:123/onos/ui/websock/path',
68 absUrl: 'ws://foo:123/onos/ui/websock/path'
Sean Condonfd6d11b2018-06-02 20:29:49 +010069 }
70 };
71 fs = new FnService(ar, logSpy, windowMock);
Sean Condon49e15be2018-05-16 16:58:29 +010072
73 TestBed.configureTestingModule({
74 providers: [WebSocketService,
Sean Condonfd6d11b2018-06-02 20:29:49 +010075 { provide: FnService, useValue: fs },
76 { provide: LogService, useValue: logSpy },
Sean Condon49e15be2018-05-16 16:58:29 +010077 { provide: GlyphService, useClass: MockGlyphService },
Sean Condonfd6d11b2018-06-02 20:29:49 +010078 { provide: UrlFnService, useValue: new UrlFnService(logSpy, windowMock) },
Sean Condona00bf382018-06-23 07:54:01 +010079 { provide: 'Window', useFactory: (() => windowMock ) },
Sean Condonfd6d11b2018-06-02 20:29:49 +010080 { provide: WSock, useFactory: (() => {
81 return {
82 newWebSocket: (() => mockWebSocket)
83 };
84 })
85 }
Sean Condon49e15be2018-05-16 16:58:29 +010086 ]
87 });
Sean Condonfd6d11b2018-06-02 20:29:49 +010088
89 wss = TestBed.get(WebSocketService);
90 logServiceSpy = TestBed.get(LogService);
Sean Condon49e15be2018-05-16 16:58:29 +010091 });
92
Sean Condonfd6d11b2018-06-02 20:29:49 +010093 it('should define WebSocketService', () => {
94 expect(wss).toBeDefined();
95 });
96
97 it('should define api functions', () => {
98 expect(fs.areFunctions(wss, ['bootstrap', 'error',
99 'handleOpen', 'handleMessage', 'handleClose',
100 'findGuiSuccessor', 'informListeners', 'send',
101 'noHandlersWarn', 'resetState',
102 'createWebSocket', 'bindHandlers', 'unbindHandlers',
103 'addOpenListener', 'removeOpenListener', 'sendEvent',
Sean Condon28ecc5f2018-06-25 12:50:16 +0100104 'setVeilDelegate', 'setLoadingDelegate', 'isConnected',
105 'closeWebSocket', 'isHandling'
Sean Condonfd6d11b2018-06-02 20:29:49 +0100106 ])).toBeTruthy();
107 });
108
109 it('should use the appropriate URL, createWebsocket', () => {
110 const url = wss.createWebSocket();
Sean Condonbf7ff4f2019-03-17 16:18:42 +0000111 expect(url).toEqual('ws://foo:80/onos/ui/websock/core');
Sean Condonfd6d11b2018-06-02 20:29:49 +0100112 });
113
114 it('should use the appropriate URL with modified port, createWebsocket',
115 () => {
116 const url = wss.createWebSocket(<WsOptions>{ wsport: 1243 });
Sean Condonbf7ff4f2019-03-17 16:18:42 +0000117 expect(url).toEqual('ws://foo:1243/onos/ui/websock/core');
Sean Condonfd6d11b2018-06-02 20:29:49 +0100118 });
119
120 it('should verify websocket event handlers, createWebsocket', () => {
121 wss.createWebSocket({ wsport: 1234 });
122 expect(fs.isF(mockWebSocket.onopen)).toBeTruthy();
123 expect(fs.isF(mockWebSocket.onmessage)).toBeTruthy();
124 expect(fs.isF(mockWebSocket.onclose)).toBeTruthy();
125 });
126
127 it('should invoke listener callbacks when websocket is up, handleOpen',
128 () => {
129 let num = 0;
130 function incrementNum(host: string, url: string) {
131 expect(host).toEqual('foo');
132 num++;
133 }
134 wss.addOpenListener(incrementNum);
135 wss.createWebSocket({ wsport: 1234 });
136
137 mockWebSocket.onopen();
138 expect(num).toBe(1);
139 });
140
Sean Condondfc6dba2019-11-09 11:50:23 +0000141 xit('should send pending events, handleOpen', () => {
Sean Condonfd6d11b2018-06-02 20:29:49 +0100142 const fakeEvent = {
143 event: 'mockEv',
144 payload: { mock: 'thing' }
145 };
146 wss.sendEvent(fakeEvent.event, fakeEvent.payload);
147 // on opening the socket, a single authentication event should have
148 // been sent already...
149 expect(mockWebSocket.send.calls.count()).toEqual(1);
150
151 wss.createWebSocket({ wsport: 1234 });
152 mockWebSocket.onopen();
153 expect(mockWebSocket.send).toHaveBeenCalledWith(JSON.stringify(fakeEvent));
154 });
155
156 it('should handle an incoming bad JSON message, handleMessage', () => {
157 const badMsg = {
158 data: 'bad message'
159 };
160 wss.createWebSocket({ wsport: 1234 });
161 expect(mockWebSocket.onmessage(badMsg)).toBeNull();
162 expect(logServiceSpy.error).toHaveBeenCalled();
163 });
164
165 it('should verify message was handled, handleMessage', () => {
166 let num = 0;
167 function fakeHandler(data1: Object) { num++; }
168 const data = JSON.stringify(<EventType>{
169 event: 'mockEvResp',
170 payload: {}
171 });
172 const event = {
173 data: data
174 };
175
176 wss.createWebSocket({ wsport: 1234 });
177 wss.bindHandlers(new Map<string, (data) => void>([
178 ['mockEvResp', (data2) => fakeHandler(data2)]
179 ]));
180 expect(mockWebSocket.onmessage(event)).toBe(undefined);
181 expect(num).toBe(1);
182 });
183
184 it('should warn if there is an unhandled event, handleMessage', () => {
185 const data = { foo: 'bar', bar: 'baz'};
186 const dataString = JSON.stringify(data);
187 const badEv = {
188 data: dataString
189 };
190 wss.createWebSocket({ wsport: 1234 });
191 mockWebSocket.onmessage(badEv);
192 expect(logServiceSpy.warn).toHaveBeenCalledWith('Unhandled event:', data);
193 });
194
195 it('should not warn if valid input, bindHandlers', () => {
196 expect(wss.bindHandlers(new Map<string, (data) => void>([
197 ['test', noop ],
198 ['bar', noop ]
199 ]))).toBe(undefined);
200
201 expect(logServiceSpy.warn).not.toHaveBeenCalled();
202 });
203
204 it('should warn if no arguments, bindHandlers', () => {
205 expect(wss.bindHandlers(
206 new Map<string, (data) => void>([])
207 )).toBeNull();
208 expect(logServiceSpy.warn).toHaveBeenCalledWith(
209 'WSS.bindHandlers(): no event handlers'
210 );
211 });
212
213 it('should warn if duplicate handlers were given, bindHandlers',
214 () => {
215 wss.bindHandlers(
216 new Map<string, (data) => void>([
217 ['noop', noop ]
218 ])
219 );
220 expect(wss.bindHandlers(
221 new Map<string, (data) => void>([
222 ['noop', noop ]
223 ])
224 )).toBe(undefined);
225 expect(logServiceSpy.warn).toHaveBeenCalledWith('duplicate bindings ignored:',
226 ['noop']);
227 });
228
229 it('should warn if no arguments, unbindHandlers', () => {
Sean Condon2bd11b72018-06-15 08:00:48 +0100230 expect(wss.unbindHandlers([])).toBeNull();
Sean Condonfd6d11b2018-06-02 20:29:49 +0100231 expect(logServiceSpy.warn).toHaveBeenCalledWith(
232 'WSS.unbindHandlers(): no event handlers'
233 );
234 });
235 // Note: cannot test unbindHandlers' forEach due to it using closure variable
236
237 it('should not warn if valid argument, addOpenListener', () => {
238 let o = wss.addOpenListener(noop);
Sean Condondfc6dba2019-11-09 11:50:23 +0000239 expect(o.id).toEqual(1);
240 expect(o.cb).toEqual(noop);
Sean Condonfd6d11b2018-06-02 20:29:49 +0100241 expect(logServiceSpy.warn).not.toHaveBeenCalled();
242 o = wss.addOpenListener(noop);
Sean Condondfc6dba2019-11-09 11:50:23 +0000243 expect(o.id).toEqual(2);
244 expect(o.cb).toEqual(noop);
Sean Condonfd6d11b2018-06-02 20:29:49 +0100245 expect(logServiceSpy.warn).not.toHaveBeenCalled();
246 });
247
248 it('should log error if callback not a function, addOpenListener',
249 () => {
250 const o = wss.addOpenListener(null);
Sean Condondfc6dba2019-11-09 11:50:23 +0000251 expect(o.id).toEqual(1);
252 expect(o.cb).toEqual(null);
253 expect(o.error).toEqual('No callback defined');
Sean Condonfd6d11b2018-06-02 20:29:49 +0100254 expect(logServiceSpy.error).toHaveBeenCalledWith(
255 'WSS.addOpenListener(): callback not a function'
256 );
257 });
258
259 it('should not warn if valid listener object, removeOpenListener', () => {
260 expect(wss.removeOpenListener(<Callback>{
261 id: 1,
262 error: 'error',
263 cb: noop
264 })).toBe(undefined);
265 expect(logServiceSpy.warn).not.toHaveBeenCalled();
266 });
267
268 it('should warn if listener is invalid, removeOpenListener', () => {
269 expect(wss.removeOpenListener(<Callback>{})).toBeNull();
270 expect(logServiceSpy.warn).toHaveBeenCalledWith(
271 'WSS.removeOpenListener(): invalid listener', {}
272 );
273 });
274
275 // Note: handleClose is not currently tested due to all work it does relies
276 // on closure variables that cannot be mocked
277
Sean Condon83fc39f2018-04-19 18:56:13 +0100278});