blob: e0c7675fa7d380dc500ffb02b2f3580cc4fecdf2 [file] [log] [blame]
Sean Condon83fc39f2018-04-19 18:56:13 +01001/*
2 * Copyright 2015-present Open Networking Foundation
3 *
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 Condon49e15be2018-05-16 16:58:29 +010018import { LogService } from '../../../../app/log.service';
Sean Condonfd6d11b2018-06-02 20:29:49 +010019import { WebSocketService, WsOptions, Callback, EventType } from '../../../../app/fw/remote/websocket.service';
Sean Condon49e15be2018-05-16 16:58:29 +010020import { FnService } from '../../../../app/fw/util/fn.service';
21import { GlyphService } from '../../../../app/fw/svg/glyph.service';
Sean Condonfd6d11b2018-06-02 20:29:49 +010022import { ActivatedRoute, Params } from '@angular/router';
Sean Condon49e15be2018-05-16 16:58:29 +010023import { UrlFnService } from '../../../../app/fw/remote/urlfn.service';
24import { WSock } from '../../../../app/fw/remote/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'},
67 href: 'ws://foo:123/onos/ui/websock/path',
68 absUrl: 'ws://foo:123/onos/ui/websock/path'
69 }
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 Condon2bd11b72018-06-15 08:00:48 +0100104 'setVeilDelegate', 'setLoadingDelegate', 'isConnected', 'closeWebSocket'
Sean Condonfd6d11b2018-06-02 20:29:49 +0100105 ])).toBeTruthy();
106 });
107
108 it('should use the appropriate URL, createWebsocket', () => {
109 const url = wss.createWebSocket();
110 expect(url).toEqual('ws://foo:80/onos/ui/websock/core');
111 });
112
113 it('should use the appropriate URL with modified port, createWebsocket',
114 () => {
115 const url = wss.createWebSocket(<WsOptions>{ wsport: 1243 });
116 expect(url).toEqual('ws://foo:1243/onos/ui/websock/core');
117 });
118
119 it('should verify websocket event handlers, createWebsocket', () => {
120 wss.createWebSocket({ wsport: 1234 });
121 expect(fs.isF(mockWebSocket.onopen)).toBeTruthy();
122 expect(fs.isF(mockWebSocket.onmessage)).toBeTruthy();
123 expect(fs.isF(mockWebSocket.onclose)).toBeTruthy();
124 });
125
126 it('should invoke listener callbacks when websocket is up, handleOpen',
127 () => {
128 let num = 0;
129 function incrementNum(host: string, url: string) {
130 expect(host).toEqual('foo');
131 num++;
132 }
133 wss.addOpenListener(incrementNum);
134 wss.createWebSocket({ wsport: 1234 });
135
136 mockWebSocket.onopen();
137 expect(num).toBe(1);
138 });
139
140 it('should send pending events, handleOpen', () => {
141 const fakeEvent = {
142 event: 'mockEv',
143 payload: { mock: 'thing' }
144 };
145 wss.sendEvent(fakeEvent.event, fakeEvent.payload);
146 // on opening the socket, a single authentication event should have
147 // been sent already...
148 expect(mockWebSocket.send.calls.count()).toEqual(1);
149
150 wss.createWebSocket({ wsport: 1234 });
151 mockWebSocket.onopen();
152 expect(mockWebSocket.send).toHaveBeenCalledWith(JSON.stringify(fakeEvent));
153 });
154
155 it('should handle an incoming bad JSON message, handleMessage', () => {
156 const badMsg = {
157 data: 'bad message'
158 };
159 wss.createWebSocket({ wsport: 1234 });
160 expect(mockWebSocket.onmessage(badMsg)).toBeNull();
161 expect(logServiceSpy.error).toHaveBeenCalled();
162 });
163
164 it('should verify message was handled, handleMessage', () => {
165 let num = 0;
166 function fakeHandler(data1: Object) { num++; }
167 const data = JSON.stringify(<EventType>{
168 event: 'mockEvResp',
169 payload: {}
170 });
171 const event = {
172 data: data
173 };
174
175 wss.createWebSocket({ wsport: 1234 });
176 wss.bindHandlers(new Map<string, (data) => void>([
177 ['mockEvResp', (data2) => fakeHandler(data2)]
178 ]));
179 expect(mockWebSocket.onmessage(event)).toBe(undefined);
180 expect(num).toBe(1);
181 });
182
183 it('should warn if there is an unhandled event, handleMessage', () => {
184 const data = { foo: 'bar', bar: 'baz'};
185 const dataString = JSON.stringify(data);
186 const badEv = {
187 data: dataString
188 };
189 wss.createWebSocket({ wsport: 1234 });
190 mockWebSocket.onmessage(badEv);
191 expect(logServiceSpy.warn).toHaveBeenCalledWith('Unhandled event:', data);
192 });
193
194 it('should not warn if valid input, bindHandlers', () => {
195 expect(wss.bindHandlers(new Map<string, (data) => void>([
196 ['test', noop ],
197 ['bar', noop ]
198 ]))).toBe(undefined);
199
200 expect(logServiceSpy.warn).not.toHaveBeenCalled();
201 });
202
203 it('should warn if no arguments, bindHandlers', () => {
204 expect(wss.bindHandlers(
205 new Map<string, (data) => void>([])
206 )).toBeNull();
207 expect(logServiceSpy.warn).toHaveBeenCalledWith(
208 'WSS.bindHandlers(): no event handlers'
209 );
210 });
211
212 it('should warn if duplicate handlers were given, bindHandlers',
213 () => {
214 wss.bindHandlers(
215 new Map<string, (data) => void>([
216 ['noop', noop ]
217 ])
218 );
219 expect(wss.bindHandlers(
220 new Map<string, (data) => void>([
221 ['noop', noop ]
222 ])
223 )).toBe(undefined);
224 expect(logServiceSpy.warn).toHaveBeenCalledWith('duplicate bindings ignored:',
225 ['noop']);
226 });
227
228 it('should warn if no arguments, unbindHandlers', () => {
Sean Condon2bd11b72018-06-15 08:00:48 +0100229 expect(wss.unbindHandlers([])).toBeNull();
Sean Condonfd6d11b2018-06-02 20:29:49 +0100230 expect(logServiceSpy.warn).toHaveBeenCalledWith(
231 'WSS.unbindHandlers(): no event handlers'
232 );
233 });
234 // Note: cannot test unbindHandlers' forEach due to it using closure variable
235
236 it('should not warn if valid argument, addOpenListener', () => {
237 let o = wss.addOpenListener(noop);
238 expect(o.id === 1);
239 expect(o.cb === noop);
240 expect(logServiceSpy.warn).not.toHaveBeenCalled();
241 o = wss.addOpenListener(noop);
242 expect(o.id === 2);
243 expect(o.cb === noop);
244 expect(logServiceSpy.warn).not.toHaveBeenCalled();
245 });
246
247 it('should log error if callback not a function, addOpenListener',
248 () => {
249 const o = wss.addOpenListener(null);
250 expect(o.id === 1);
251 expect(o.cb === null);
252 expect(o.error === 'No callback defined');
253 expect(logServiceSpy.error).toHaveBeenCalledWith(
254 'WSS.addOpenListener(): callback not a function'
255 );
256 });
257
258 it('should not warn if valid listener object, removeOpenListener', () => {
259 expect(wss.removeOpenListener(<Callback>{
260 id: 1,
261 error: 'error',
262 cb: noop
263 })).toBe(undefined);
264 expect(logServiceSpy.warn).not.toHaveBeenCalled();
265 });
266
267 it('should warn if listener is invalid, removeOpenListener', () => {
268 expect(wss.removeOpenListener(<Callback>{})).toBeNull();
269 expect(logServiceSpy.warn).toHaveBeenCalledWith(
270 'WSS.removeOpenListener(): invalid listener', {}
271 );
272 });
273
274 // Note: handleClose is not currently tested due to all work it does relies
275 // on closure variables that cannot be mocked
276
Sean Condon83fc39f2018-04-19 18:56:13 +0100277});