blob: 5c8d6b73a3849c142be4e0e28c6c3f6522a59e6c [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 Condon49e15be2018-05-16 16:58:29 +010036class MockWSock {}
Sean Condon83fc39f2018-04-19 18:56:13 +010037
38/**
39 * ONOS GUI -- Remote -- Web Socket Service - Unit Tests
40 */
41describe('WebSocketService', () => {
Sean Condonfd6d11b2018-06-02 20:29:49 +010042 let wss: WebSocketService;
43 let fs: FnService;
44 let ar: MockActivatedRoute;
45 let windowMock: Window;
46 let logServiceSpy: jasmine.SpyObj<LogService>;
47
48 const noop = () => ({});
49 const send = jasmine.createSpy('send')
50 .and.callFake((ev) => ev);
51 const mockWebSocket = {
52 send: send,
53 onmessage: (msgEvent) => ({}),
54 onopen: () => ({}),
55 onclose: () => ({}),
56 };
Sean Condon83fc39f2018-04-19 18:56:13 +010057
Sean Condon49e15be2018-05-16 16:58:29 +010058 beforeEach(() => {
Sean Condonfd6d11b2018-06-02 20:29:49 +010059 const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
60 ar = new MockActivatedRoute({'debug': 'txrx'});
61
62 windowMock = <any>{
63 location: <any> {
64 hostname: 'foo',
65 host: 'foo',
66 port: '80',
67 protocol: 'http',
68 search: { debug: 'true'},
69 href: 'ws://foo:123/onos/ui/websock/path',
70 absUrl: 'ws://foo:123/onos/ui/websock/path'
71 }
72 };
73 fs = new FnService(ar, logSpy, windowMock);
Sean Condon49e15be2018-05-16 16:58:29 +010074
75 TestBed.configureTestingModule({
76 providers: [WebSocketService,
Sean Condonfd6d11b2018-06-02 20:29:49 +010077 { provide: FnService, useValue: fs },
78 { provide: LogService, useValue: logSpy },
Sean Condon49e15be2018-05-16 16:58:29 +010079 { provide: GlyphService, useClass: MockGlyphService },
Sean Condonfd6d11b2018-06-02 20:29:49 +010080 { provide: UrlFnService, useValue: new UrlFnService(logSpy, windowMock) },
81 { provide: Window, useFactory: (() => windowMock ) },
82 { provide: WSock, useFactory: (() => {
83 return {
84 newWebSocket: (() => mockWebSocket)
85 };
86 })
87 }
Sean Condon49e15be2018-05-16 16:58:29 +010088 ]
89 });
Sean Condonfd6d11b2018-06-02 20:29:49 +010090
91 wss = TestBed.get(WebSocketService);
92 logServiceSpy = TestBed.get(LogService);
Sean Condon49e15be2018-05-16 16:58:29 +010093 });
94
Sean Condonfd6d11b2018-06-02 20:29:49 +010095 it('should define WebSocketService', () => {
96 expect(wss).toBeDefined();
97 });
98
99 it('should define api functions', () => {
100 expect(fs.areFunctions(wss, ['bootstrap', 'error',
101 'handleOpen', 'handleMessage', 'handleClose',
102 'findGuiSuccessor', 'informListeners', 'send',
103 'noHandlersWarn', 'resetState',
104 'createWebSocket', 'bindHandlers', 'unbindHandlers',
105 'addOpenListener', 'removeOpenListener', 'sendEvent',
106 'setVeilDelegate', 'setLoadingDelegate'
107 ])).toBeTruthy();
108 });
109
110 it('should use the appropriate URL, createWebsocket', () => {
111 const url = wss.createWebSocket();
112 expect(url).toEqual('ws://foo:80/onos/ui/websock/core');
113 });
114
115 it('should use the appropriate URL with modified port, createWebsocket',
116 () => {
117 const url = wss.createWebSocket(<WsOptions>{ wsport: 1243 });
118 expect(url).toEqual('ws://foo:1243/onos/ui/websock/core');
119 });
120
121 it('should verify websocket event handlers, createWebsocket', () => {
122 wss.createWebSocket({ wsport: 1234 });
123 expect(fs.isF(mockWebSocket.onopen)).toBeTruthy();
124 expect(fs.isF(mockWebSocket.onmessage)).toBeTruthy();
125 expect(fs.isF(mockWebSocket.onclose)).toBeTruthy();
126 });
127
128 it('should invoke listener callbacks when websocket is up, handleOpen',
129 () => {
130 let num = 0;
131 function incrementNum(host: string, url: string) {
132 expect(host).toEqual('foo');
133 num++;
134 }
135 wss.addOpenListener(incrementNum);
136 wss.createWebSocket({ wsport: 1234 });
137
138 mockWebSocket.onopen();
139 expect(num).toBe(1);
140 });
141
142 it('should send pending events, handleOpen', () => {
143 const fakeEvent = {
144 event: 'mockEv',
145 payload: { mock: 'thing' }
146 };
147 wss.sendEvent(fakeEvent.event, fakeEvent.payload);
148 // on opening the socket, a single authentication event should have
149 // been sent already...
150 expect(mockWebSocket.send.calls.count()).toEqual(1);
151
152 wss.createWebSocket({ wsport: 1234 });
153 mockWebSocket.onopen();
154 expect(mockWebSocket.send).toHaveBeenCalledWith(JSON.stringify(fakeEvent));
155 });
156
157 it('should handle an incoming bad JSON message, handleMessage', () => {
158 const badMsg = {
159 data: 'bad message'
160 };
161 wss.createWebSocket({ wsport: 1234 });
162 expect(mockWebSocket.onmessage(badMsg)).toBeNull();
163 expect(logServiceSpy.error).toHaveBeenCalled();
164 });
165
166 it('should verify message was handled, handleMessage', () => {
167 let num = 0;
168 function fakeHandler(data1: Object) { num++; }
169 const data = JSON.stringify(<EventType>{
170 event: 'mockEvResp',
171 payload: {}
172 });
173 const event = {
174 data: data
175 };
176
177 wss.createWebSocket({ wsport: 1234 });
178 wss.bindHandlers(new Map<string, (data) => void>([
179 ['mockEvResp', (data2) => fakeHandler(data2)]
180 ]));
181 expect(mockWebSocket.onmessage(event)).toBe(undefined);
182 expect(num).toBe(1);
183 });
184
185 it('should warn if there is an unhandled event, handleMessage', () => {
186 const data = { foo: 'bar', bar: 'baz'};
187 const dataString = JSON.stringify(data);
188 const badEv = {
189 data: dataString
190 };
191 wss.createWebSocket({ wsport: 1234 });
192 mockWebSocket.onmessage(badEv);
193 expect(logServiceSpy.warn).toHaveBeenCalledWith('Unhandled event:', data);
194 });
195
196 it('should not warn if valid input, bindHandlers', () => {
197 expect(wss.bindHandlers(new Map<string, (data) => void>([
198 ['test', noop ],
199 ['bar', noop ]
200 ]))).toBe(undefined);
201
202 expect(logServiceSpy.warn).not.toHaveBeenCalled();
203 });
204
205 it('should warn if no arguments, bindHandlers', () => {
206 expect(wss.bindHandlers(
207 new Map<string, (data) => void>([])
208 )).toBeNull();
209 expect(logServiceSpy.warn).toHaveBeenCalledWith(
210 'WSS.bindHandlers(): no event handlers'
211 );
212 });
213
214 it('should warn if duplicate handlers were given, bindHandlers',
215 () => {
216 wss.bindHandlers(
217 new Map<string, (data) => void>([
218 ['noop', noop ]
219 ])
220 );
221 expect(wss.bindHandlers(
222 new Map<string, (data) => void>([
223 ['noop', noop ]
224 ])
225 )).toBe(undefined);
226 expect(logServiceSpy.warn).toHaveBeenCalledWith('duplicate bindings ignored:',
227 ['noop']);
228 });
229
230 it('should warn if no arguments, unbindHandlers', () => {
231 expect(wss.unbindHandlers(
232 new Map<string, (data) => void>([])
233 )).toBeNull();
234 expect(logServiceSpy.warn).toHaveBeenCalledWith(
235 'WSS.unbindHandlers(): no event handlers'
236 );
237 });
238 // Note: cannot test unbindHandlers' forEach due to it using closure variable
239
240 it('should not warn if valid argument, addOpenListener', () => {
241 let o = wss.addOpenListener(noop);
242 expect(o.id === 1);
243 expect(o.cb === noop);
244 expect(logServiceSpy.warn).not.toHaveBeenCalled();
245 o = wss.addOpenListener(noop);
246 expect(o.id === 2);
247 expect(o.cb === noop);
248 expect(logServiceSpy.warn).not.toHaveBeenCalled();
249 });
250
251 it('should log error if callback not a function, addOpenListener',
252 () => {
253 const o = wss.addOpenListener(null);
254 expect(o.id === 1);
255 expect(o.cb === null);
256 expect(o.error === 'No callback defined');
257 expect(logServiceSpy.error).toHaveBeenCalledWith(
258 'WSS.addOpenListener(): callback not a function'
259 );
260 });
261
262 it('should not warn if valid listener object, removeOpenListener', () => {
263 expect(wss.removeOpenListener(<Callback>{
264 id: 1,
265 error: 'error',
266 cb: noop
267 })).toBe(undefined);
268 expect(logServiceSpy.warn).not.toHaveBeenCalled();
269 });
270
271 it('should warn if listener is invalid, removeOpenListener', () => {
272 expect(wss.removeOpenListener(<Callback>{})).toBeNull();
273 expect(logServiceSpy.warn).toHaveBeenCalledWith(
274 'WSS.removeOpenListener(): invalid listener', {}
275 );
276 });
277
278 // Note: handleClose is not currently tested due to all work it does relies
279 // on closure variables that cannot be mocked
280
Sean Condon83fc39f2018-04-19 18:56:13 +0100281});