airdcpp-apisocket 3.0.0-beta.5 → 3.0.0-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-es/SocketBase.js +8 -9
- package/dist-es/SocketBase.js.map +1 -1
- package/dist-es/SocketRequestHandler.js +3 -5
- package/dist-es/SocketRequestHandler.js.map +1 -1
- package/dist-es/tests/{helpers.d.ts → mock-server.d.ts} +29 -4
- package/dist-es/tests/mock-server.js +200 -0
- package/dist-es/tests/mock-server.js.map +1 -0
- package/dist-es/tests/test-utils.d.ts +2 -0
- package/dist-es/tests/test-utils.js +10 -0
- package/dist-es/tests/test-utils.js.map +1 -0
- package/dist-es/types/public_helpers_internal.d.ts +2 -2
- package/package.json +1 -1
- package/src/SocketBase.ts +9 -10
- package/src/SocketRequestHandler.ts +3 -5
- package/src/tests/Socket.test.ts +49 -69
- package/src/tests/mock-server.ts +336 -0
- package/src/tests/public_helpers.test.ts +25 -43
- package/src/tests/test-utils.ts +12 -0
- package/src/types/api_internal.ts +0 -1
- package/src/types/public_helpers_internal.ts +2 -2
- package/dist-es/tests/helpers.js +0 -135
- package/dist-es/tests/helpers.js.map +0 -1
- package/src/tests/helpers.ts +0 -211
package/src/tests/Socket.test.ts
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import {
|
2
2
|
DEFAULT_AUTH_RESPONSE, DEFAULT_CONNECT_PARAMS,
|
3
|
-
getConnectedSocket, getMockServer, getSocket
|
4
|
-
} from './
|
3
|
+
getConnectedSocket, getMockServer, getSocket
|
4
|
+
} from './mock-server.js';
|
5
5
|
|
6
6
|
import ApiConstants from '../ApiConstants.js';
|
7
7
|
|
@@ -9,6 +9,7 @@ import { HookCallback, HookSubscriberInfo, SubscriptionRemoveHandler } from '../
|
|
9
9
|
import { IncomingSubscriptionEvent } from '../types/api_internal.js';
|
10
10
|
|
11
11
|
import { jest } from '@jest/globals';
|
12
|
+
import { defusedPromise, waitForExpect } from './test-utils.js';
|
12
13
|
|
13
14
|
let server: ReturnType<typeof getMockServer>;
|
14
15
|
|
@@ -29,7 +30,7 @@ describe('socket', () => {
|
|
29
30
|
|
30
31
|
describe('auth', () => {
|
31
32
|
test('should handle valid credentials', async () => {
|
32
|
-
server.
|
33
|
+
server.addRequestHandler('POST', ApiConstants.LOGIN_URL, DEFAULT_AUTH_RESPONSE);
|
33
34
|
const connectedCallback = jest.fn();
|
34
35
|
|
35
36
|
const { socket, mockConsole } = getSocket();
|
@@ -48,7 +49,7 @@ describe('socket', () => {
|
|
48
49
|
});
|
49
50
|
|
50
51
|
test('should handle valid refresh token', async () => {
|
51
|
-
server.
|
52
|
+
server.addRequestHandler('POST', ApiConstants.LOGIN_URL, DEFAULT_AUTH_RESPONSE);
|
52
53
|
const connectedCallback = jest.fn();
|
53
54
|
|
54
55
|
const { socket, mockConsole } = getSocket();
|
@@ -106,7 +107,7 @@ describe('socket', () => {
|
|
106
107
|
|
107
108
|
// Valid connect attempt
|
108
109
|
server = getMockServer();
|
109
|
-
server.
|
110
|
+
server.addRequestHandler('POST', ApiConstants.LOGIN_URL, DEFAULT_AUTH_RESPONSE);
|
110
111
|
|
111
112
|
await socket.connect(DEFAULT_CONNECT_PARAMS.username, DEFAULT_CONNECT_PARAMS.password, false);
|
112
113
|
|
@@ -125,22 +126,23 @@ describe('socket', () => {
|
|
125
126
|
socket.onDisconnected = disconnectedCallback;
|
126
127
|
|
127
128
|
// Dummy listener
|
128
|
-
server.
|
129
|
+
server.addRequestHandler('POST', 'hubs/listeners/hub_updated', undefined);
|
129
130
|
await socket.addListener('hubs', 'hub_updated', dummyfn);
|
130
131
|
|
131
132
|
// Dummy pending request
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
});
|
133
|
+
server.ignoreMissingHandler('DELETE', 'dummyLogoutDelete');
|
134
|
+
|
135
|
+
const pendingRequestPromise = defusedPromise(socket.delete('dummyLogoutDelete'));
|
136
136
|
|
137
137
|
// Logout
|
138
|
-
server.
|
138
|
+
server.addRequestHandler('DELETE', ApiConstants.LOGOUT_URL);
|
139
139
|
await socket.logout();
|
140
140
|
|
141
141
|
expect(sessionResetCallback.mock.calls.length).toBe(1);
|
142
142
|
await waitForExpect(() => expect(disconnectedCallback.mock.calls.length).toBe(1));
|
143
143
|
|
144
|
+
await expect(pendingRequestPromise).rejects.toMatchInlineSnapshot(`"Socket disconnected"`);
|
145
|
+
|
144
146
|
expect(socket.isActive()).toEqual(false);
|
145
147
|
expect(socket.hasListeners()).toEqual(false);
|
146
148
|
expect(socket.getPendingRequestCount()).toEqual(0);
|
@@ -166,14 +168,14 @@ describe('socket', () => {
|
|
166
168
|
test('should handle wait disconnected timeout', async () => {
|
167
169
|
const { socket, mockConsole } = await getConnectedSocket(server);
|
168
170
|
|
169
|
-
let error;
|
171
|
+
let error: Error | null = null;
|
170
172
|
try {
|
171
173
|
await socket.waitDisconnected(50);
|
172
174
|
} catch (e) {
|
173
175
|
error = e;
|
174
176
|
}
|
175
177
|
|
176
|
-
expect(error).toEqual('Socket disconnect timed out');
|
178
|
+
expect(error?.message).toEqual('Socket disconnect timed out');
|
177
179
|
|
178
180
|
expect(mockConsole.error.mock.calls.length).toBe(1);
|
179
181
|
expect(mockConsole.warn.mock.calls.length).toBe(0);
|
@@ -192,12 +194,7 @@ describe('socket', () => {
|
|
192
194
|
socket.disconnect(true);
|
193
195
|
jest.runOnlyPendingTimers();
|
194
196
|
|
195
|
-
|
196
|
-
/*{
|
197
|
-
const waitForExpectTask = await waitForExpect(() => expect(socket.isActive()).toEqual(false));
|
198
|
-
jest.advanceTimersByTime(1000);
|
199
|
-
await waitForExpectTask;
|
200
|
-
}*/
|
197
|
+
expect(socket.isActive()).toEqual(false);
|
201
198
|
|
202
199
|
// Let it fail once
|
203
200
|
server.stop();
|
@@ -206,7 +203,7 @@ describe('socket', () => {
|
|
206
203
|
expect(mockConsole.error.mock.calls.length).toBe(1);
|
207
204
|
|
208
205
|
server = getMockServer();
|
209
|
-
server.
|
206
|
+
server.addRequestHandler('POST', ApiConstants.CONNECT_URL, undefined);
|
210
207
|
jest.runOnlyPendingTimers();
|
211
208
|
jest.runOnlyPendingTimers();
|
212
209
|
jest.runOnlyPendingTimers();
|
@@ -257,7 +254,7 @@ describe('socket', () => {
|
|
257
254
|
socket.disconnect();
|
258
255
|
await waitForExpect(() => expect(socket.isActive()).toEqual(false));
|
259
256
|
|
260
|
-
server.
|
257
|
+
server.addRequestHandler('POST', ApiConstants.CONNECT_URL, undefined);
|
261
258
|
await socket.reconnect();
|
262
259
|
expect(socket.isConnected()).toEqual(true);
|
263
260
|
|
@@ -268,9 +265,10 @@ describe('socket', () => {
|
|
268
265
|
await waitForExpect(() => expect(socket.isActive()).toEqual(false));
|
269
266
|
});
|
270
267
|
|
271
|
-
|
272
|
-
test.skip('should re-authenticate on lost session', async () => {
|
268
|
+
test('should re-authenticate on lost session', async () => {
|
273
269
|
const ErrorResponse = 'Invalid session token';
|
270
|
+
const authCallback = jest.fn();
|
271
|
+
const connectErrorCallback = jest.fn();
|
274
272
|
|
275
273
|
// Connect and disconnect
|
276
274
|
const { socket, mockConsole } = await getConnectedSocket(server);
|
@@ -282,25 +280,16 @@ describe('socket', () => {
|
|
282
280
|
|
283
281
|
// Fail the initial reconnect attempt with 'Invalid session token'
|
284
282
|
// and connect with credentials afterwards
|
285
|
-
server.addErrorHandler('POST', ApiConstants.CONNECT_URL, ErrorResponse, 400);
|
283
|
+
server.addErrorHandler('POST', ApiConstants.CONNECT_URL, ErrorResponse, 400, connectErrorCallback);
|
286
284
|
|
287
|
-
|
288
|
-
server.addDataHandler('POST', ApiConstants.LOGIN_URL, DEFAULT_AUTH_RESPONSE, authCallback);
|
285
|
+
server.addRequestHandler('POST', ApiConstants.LOGIN_URL, DEFAULT_AUTH_RESPONSE, authCallback);
|
289
286
|
|
290
287
|
jest.runOnlyPendingTimers();
|
291
288
|
socket.reconnect();
|
292
289
|
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
jest.runOnlyPendingTimers();
|
297
|
-
expect(authCallback.mock.calls.length).toBe(1);
|
298
|
-
}
|
299
|
-
);
|
300
|
-
|
301
|
-
jest.advanceTimersByTime(1000);
|
302
|
-
await waitForExpectTask;
|
303
|
-
}
|
290
|
+
await jest.advanceTimersByTimeAsync(1000);
|
291
|
+
expect(authCallback.mock.calls.length).toBe(1);
|
292
|
+
expect(connectErrorCallback.mock.calls.length).toBe(1);
|
304
293
|
|
305
294
|
expect(socket.isConnected()).toEqual(true);
|
306
295
|
expect(mockConsole.error.mock.calls.length).toBe(0);
|
@@ -328,6 +317,9 @@ describe('socket', () => {
|
|
328
317
|
test('should report request timeouts', async () => {
|
329
318
|
const { socket, mockConsole } = await getConnectedSocket(server);
|
330
319
|
|
320
|
+
server.ignoreMissingHandler('POST', 'hubs/listeners/hub_updated');
|
321
|
+
server.ignoreMissingHandler('POST', 'hubs/listeners/hub_added');
|
322
|
+
|
331
323
|
jest.useFakeTimers();
|
332
324
|
socket.addListener('hubs', 'hub_updated', dummyfn)
|
333
325
|
.catch(() => {});
|
@@ -388,8 +380,8 @@ describe('socket', () => {
|
|
388
380
|
|
389
381
|
test('should handle listener messages', async () => {
|
390
382
|
const { socket, mockConsole } = await getConnectedSocket(server);
|
391
|
-
server.
|
392
|
-
server.
|
383
|
+
server.addSubscriptionHandler('hubs', 'hub_updated');
|
384
|
+
server.addSubscriptionHandler('hubs', 'hub_updated', entityId);
|
393
385
|
|
394
386
|
const commonSubscriptionCallback = jest.fn();
|
395
387
|
const entitySubscriptionCallback = jest.fn();
|
@@ -415,8 +407,7 @@ describe('socket', () => {
|
|
415
407
|
test('should handle listener removal', async () => {
|
416
408
|
const { socket, mockConsole } = await getConnectedSocket(server);
|
417
409
|
|
418
|
-
const
|
419
|
-
server.addDataHandler('POST', 'hubs/listeners/hub_updated', undefined, subscribeCallback);
|
410
|
+
const hubUpdatedListener = server.addSubscriptionHandler('hubs', 'hub_updated');
|
420
411
|
|
421
412
|
// Add two simultaneous pending add events
|
422
413
|
const p1 = socket.addListener('hubs', 'hub_updated', dummyfn);
|
@@ -428,17 +419,14 @@ describe('socket', () => {
|
|
428
419
|
const removeListener1 = await p1;
|
429
420
|
const removeListener2 = await p2;
|
430
421
|
|
431
|
-
expect(
|
422
|
+
expect(hubUpdatedListener.subscribeFn.mock.calls.length).toBe(1);
|
432
423
|
expect(socket.getPendingSubscriptionCount()).toBe(0);
|
433
424
|
|
434
|
-
const deleteCallback = jest.fn();
|
435
|
-
server.addDataHandler('DELETE', 'hubs/listeners/hub_updated', undefined, deleteCallback);
|
436
|
-
|
437
425
|
removeListener1();
|
438
|
-
expect(
|
426
|
+
expect(hubUpdatedListener.unsubscribeFn.mock.calls.length).toBe(0); // Shouldn't call API yet, still one left
|
439
427
|
|
440
428
|
removeListener2();
|
441
|
-
await waitForExpect(() => expect(
|
429
|
+
await waitForExpect(() => expect(hubUpdatedListener.unsubscribeFn.mock.calls.length).toBe(1));
|
442
430
|
|
443
431
|
expect(socket.hasListeners()).toBe(false);
|
444
432
|
|
@@ -466,11 +454,9 @@ describe('socket', () => {
|
|
466
454
|
});
|
467
455
|
|
468
456
|
describe('hooks', () => {
|
469
|
-
const
|
470
|
-
|
471
|
-
|
472
|
-
completion_id: 1,
|
473
|
-
};
|
457
|
+
const HOOK_MODULE = 'queue';
|
458
|
+
const HOOK_NAME = 'queue_bundle_finished_hook';
|
459
|
+
const HOOK_COMPLETION_ID = 1;
|
474
460
|
|
475
461
|
const hookSubscriberInfo: HookSubscriberInfo = {
|
476
462
|
id: 'sfv_checker',
|
@@ -487,33 +473,27 @@ describe('socket', () => {
|
|
487
473
|
|
488
474
|
// Add hook
|
489
475
|
{
|
490
|
-
const
|
491
|
-
server.addDataHandler('POST', 'queue/hooks/queue_bundle_finished_hook', undefined, hookAddCallback);
|
476
|
+
const hook = server.addHookHandler(HOOK_MODULE, HOOK_NAME);
|
492
477
|
|
493
478
|
removeListener = await socket.addHook(
|
494
|
-
|
495
|
-
|
479
|
+
HOOK_MODULE,
|
480
|
+
HOOK_NAME,
|
496
481
|
rejectCallback,
|
497
482
|
hookSubscriberInfo
|
498
483
|
);
|
499
484
|
|
500
|
-
expect((
|
501
|
-
expect(
|
502
|
-
}
|
485
|
+
expect((hook.subscribeFn.mock.calls[0][0] as any).data).toEqual(hookSubscriberInfo);
|
486
|
+
expect(hook.subscribeFn.mock.calls.length).toBe(1);
|
503
487
|
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
server.send(hookEventData);
|
509
|
-
await waitForExpect(() => expect(hookEventCallback.mock.calls.length).toBe(1));
|
488
|
+
// Simulate action
|
489
|
+
const hookResolver = hook.addResolver(HOOK_COMPLETION_ID);
|
490
|
+
hookResolver.fire({});
|
491
|
+
await waitForExpect(() => expect(hookResolver.rejectFn.mock.calls.length).toBe(1));
|
510
492
|
}
|
511
493
|
|
512
494
|
// Clean up
|
513
|
-
|
514
|
-
|
515
|
-
expect(socket.hasListeners()).toBe(false);
|
516
|
-
}
|
495
|
+
removeListener();
|
496
|
+
expect(socket.hasListeners()).toBe(false);
|
517
497
|
|
518
498
|
expect(mockConsole.warn.mock.calls.length).toBe(0);
|
519
499
|
|
@@ -0,0 +1,336 @@
|
|
1
|
+
import { Socket } from '../NodeSocket.js';
|
2
|
+
import { Client, Server, WebSocket } from 'mock-socket';
|
3
|
+
import { jest } from '@jest/globals';
|
4
|
+
|
5
|
+
import { OutgoingRequest, RequestSuccessResponse, RequestErrorResponse } from '../types/api_internal.js';
|
6
|
+
import * as Options from '../types/options.js';
|
7
|
+
import ApiConstants from '../ApiConstants.js';
|
8
|
+
import { EventEmitter } from 'events';
|
9
|
+
|
10
|
+
const VERBOSE = false;
|
11
|
+
|
12
|
+
export const getMockConsole = () => ({
|
13
|
+
log: jest.fn((a1: any, a2: any, a3: any, a4: any) => {
|
14
|
+
if (VERBOSE) {
|
15
|
+
console.log(a1, a2, a3, a4);
|
16
|
+
}
|
17
|
+
}),
|
18
|
+
info: jest.fn((a1: any, a2: any, a3: any, a4: any) => {
|
19
|
+
if (VERBOSE) {
|
20
|
+
console.info(a1, a2, a3, a4);
|
21
|
+
}
|
22
|
+
}),
|
23
|
+
warn: jest.fn((a1: any, a2: any, a3: any, a4: any) => {
|
24
|
+
console.warn(a1, a2, a3, a4);
|
25
|
+
}),
|
26
|
+
error: jest.fn((a1: any, a2: any, a3: any, a4: any) => {
|
27
|
+
console.error(a1, a2, a3, a4);
|
28
|
+
}),
|
29
|
+
});
|
30
|
+
|
31
|
+
const DEFAULT_CONNECT_PARAMS = {
|
32
|
+
username: 'test',
|
33
|
+
password: 'test',
|
34
|
+
url: 'ws://localhost:7171/api/v1/',
|
35
|
+
};
|
36
|
+
|
37
|
+
const getDefaultSocketOptions = (mockConsole: Options.LogOutput): Options.APISocketOptions => ({
|
38
|
+
...DEFAULT_CONNECT_PARAMS,
|
39
|
+
logOutput: mockConsole,
|
40
|
+
logLevel: VERBOSE ? 'verbose' : 'warn',
|
41
|
+
});
|
42
|
+
|
43
|
+
const DEFAULT_AUTH_RESPONSE = {
|
44
|
+
auth_token: 'b823187f-4aab-4b71-9764-e63e88401a26',
|
45
|
+
refresh_token: '5124faasf-4aab-4b71-9764-e63e88401a26',
|
46
|
+
user: {
|
47
|
+
permissions: [ 'admin' ],
|
48
|
+
username: 'test',
|
49
|
+
active_sessions: 1,
|
50
|
+
last_login: 0,
|
51
|
+
},
|
52
|
+
system: {
|
53
|
+
cid: 'AHLUODI2YZ2U7FDWMHFNJU65ERGKUN4MH7GW5LY',
|
54
|
+
hostname: 'ubuntu-htpc',
|
55
|
+
network_type: 'private',
|
56
|
+
path_separator: '/',
|
57
|
+
platform: 'other',
|
58
|
+
language: 'fi',
|
59
|
+
},
|
60
|
+
wizard_pending: false,
|
61
|
+
};
|
62
|
+
|
63
|
+
export type MockSocketOptions = Omit<Options.APISocketOptions, 'username' | 'password' | 'url'> & {
|
64
|
+
username?: string;
|
65
|
+
password?: string;
|
66
|
+
url?: string;
|
67
|
+
};
|
68
|
+
|
69
|
+
const getSocket = (socketOptions: MockSocketOptions = {}, mockConsole = getMockConsole()) => {
|
70
|
+
const socket = Socket(
|
71
|
+
{
|
72
|
+
...getDefaultSocketOptions(mockConsole),
|
73
|
+
...socketOptions,
|
74
|
+
},
|
75
|
+
WebSocket as any
|
76
|
+
);
|
77
|
+
|
78
|
+
return { socket, mockConsole };
|
79
|
+
};
|
80
|
+
|
81
|
+
|
82
|
+
type Callback = (requestData: object) => void;
|
83
|
+
|
84
|
+
interface ConnectOptions {
|
85
|
+
socketOptions?: MockSocketOptions;
|
86
|
+
authCallback?: Callback;
|
87
|
+
authResponse?: object;
|
88
|
+
console?: ReturnType<typeof getMockConsole>;
|
89
|
+
}
|
90
|
+
|
91
|
+
const getDefaultConnectOptions = () => ({
|
92
|
+
console: getMockConsole(),
|
93
|
+
authResponse: DEFAULT_AUTH_RESPONSE,
|
94
|
+
});
|
95
|
+
|
96
|
+
const getConnectedSocket = async (
|
97
|
+
server: ReturnType<typeof getMockServer>,
|
98
|
+
userOptions?: ConnectOptions,
|
99
|
+
) => {
|
100
|
+
const options = {
|
101
|
+
...getDefaultConnectOptions(),
|
102
|
+
...userOptions,
|
103
|
+
};
|
104
|
+
|
105
|
+
server.addRequestHandler('POST', ApiConstants.LOGIN_URL, options.authResponse, options.authCallback);
|
106
|
+
|
107
|
+
const { socket, mockConsole } = getSocket(options.socketOptions, options.console);
|
108
|
+
await socket.connect();
|
109
|
+
|
110
|
+
return { socket, mockConsole };
|
111
|
+
};
|
112
|
+
|
113
|
+
const toEmitId = (path: string, method: string) => {
|
114
|
+
return `${path}_${method}`;
|
115
|
+
};
|
116
|
+
|
117
|
+
interface MockServerOptions {
|
118
|
+
url: string;
|
119
|
+
reportMissingListeners?: boolean;
|
120
|
+
}
|
121
|
+
|
122
|
+
const DEFAULT_MOCK_SERVER_OPTIONS: MockServerOptions = {
|
123
|
+
url: DEFAULT_CONNECT_PARAMS.url,
|
124
|
+
reportMissingListeners: true,
|
125
|
+
}
|
126
|
+
|
127
|
+
type MockRequestResponseDataObject<DataT extends object | undefined> = Omit<RequestSuccessResponse<DataT>, 'callback_id'> | Omit<RequestErrorResponse, 'callback_id'>;
|
128
|
+
type MockRequestResponseDataHandler<DataT extends object | undefined> = (request: OutgoingRequest, s: WebSocket) => MockRequestResponseDataObject<DataT>;
|
129
|
+
type MockRequestResponseData<DataT extends object | undefined> = MockRequestResponseDataObject<DataT> | MockRequestResponseDataHandler<DataT>;
|
130
|
+
|
131
|
+
const getMockServer = (initialOptions: Partial<MockServerOptions> = {}) => {
|
132
|
+
const { url, reportMissingListeners }: MockServerOptions = {
|
133
|
+
...DEFAULT_MOCK_SERVER_OPTIONS,
|
134
|
+
...initialOptions,
|
135
|
+
};
|
136
|
+
|
137
|
+
const mockServer = new Server(url);
|
138
|
+
let socket: Client;
|
139
|
+
const emitter = new EventEmitter();
|
140
|
+
|
141
|
+
const send = (data: object) => {
|
142
|
+
socket.send(JSON.stringify(data));
|
143
|
+
};
|
144
|
+
|
145
|
+
const addServerHandler = <DataT extends object | undefined>(
|
146
|
+
method: string,
|
147
|
+
path: string,
|
148
|
+
responseData: MockRequestResponseData<DataT>,
|
149
|
+
subscriptionCallback?: Callback
|
150
|
+
) => {
|
151
|
+
emitter.addListener(
|
152
|
+
toEmitId(path, method),
|
153
|
+
(request: OutgoingRequest, s: WebSocket) => {
|
154
|
+
if (subscriptionCallback) {
|
155
|
+
subscriptionCallback(request);
|
156
|
+
}
|
157
|
+
|
158
|
+
const data = typeof responseData === 'function' ? responseData(request, s) : responseData;
|
159
|
+
const response: RequestSuccessResponse | RequestErrorResponse = {
|
160
|
+
callback_id: request.callback_id,
|
161
|
+
...data,
|
162
|
+
};
|
163
|
+
|
164
|
+
s.send(JSON.stringify(response));
|
165
|
+
}
|
166
|
+
);
|
167
|
+
};
|
168
|
+
|
169
|
+
const addDummyDataHandler = (method: string, path: string) => {
|
170
|
+
emitter.addListener(
|
171
|
+
toEmitId(path, method),
|
172
|
+
(request: OutgoingRequest, s: WebSocket) => {
|
173
|
+
// Do nothing
|
174
|
+
}
|
175
|
+
);
|
176
|
+
}
|
177
|
+
|
178
|
+
const addRequestHandler = <DataT extends object | undefined>(
|
179
|
+
method: string,
|
180
|
+
path: string,
|
181
|
+
data?: DataT | MockRequestResponseDataHandler<DataT>,
|
182
|
+
subscriptionCallback?: Callback
|
183
|
+
) => {
|
184
|
+
const handlerData = typeof data === 'function' ? data : {
|
185
|
+
data,
|
186
|
+
code: data ? 200 : 204,
|
187
|
+
}
|
188
|
+
|
189
|
+
addServerHandler<DataT>(
|
190
|
+
method,
|
191
|
+
path,
|
192
|
+
handlerData,
|
193
|
+
subscriptionCallback
|
194
|
+
);
|
195
|
+
}
|
196
|
+
|
197
|
+
const addErrorHandler = (
|
198
|
+
method: string,
|
199
|
+
path: string,
|
200
|
+
errorStr: string | null,
|
201
|
+
errorCode: number,
|
202
|
+
subscriptionCallback?: Callback
|
203
|
+
) => {
|
204
|
+
addServerHandler(
|
205
|
+
method,
|
206
|
+
path,
|
207
|
+
{
|
208
|
+
error: !errorStr ? null as any : {
|
209
|
+
message: errorStr,
|
210
|
+
},
|
211
|
+
code: errorCode,
|
212
|
+
},
|
213
|
+
subscriptionCallback
|
214
|
+
);
|
215
|
+
}
|
216
|
+
|
217
|
+
const addSubscriptionHandlerImpl = (
|
218
|
+
moduleName: string,
|
219
|
+
type: string,
|
220
|
+
listenerName: string,
|
221
|
+
entityId?: string | number,
|
222
|
+
) => {
|
223
|
+
const subscribeFn = jest.fn();
|
224
|
+
const unsubscribeFn = jest.fn();
|
225
|
+
|
226
|
+
const path = entityId ? `${moduleName}/${entityId}/${type}/${listenerName}` : `${moduleName}/${type}/${listenerName}`;
|
227
|
+
|
228
|
+
addRequestHandler('POST', path, undefined, subscribeFn);
|
229
|
+
addRequestHandler('DELETE', path, undefined, unsubscribeFn);
|
230
|
+
|
231
|
+
const fire = (data: object, entityId?: string | number) => {
|
232
|
+
send({
|
233
|
+
event: listenerName,
|
234
|
+
data,
|
235
|
+
id: entityId,
|
236
|
+
});
|
237
|
+
}
|
238
|
+
|
239
|
+
return {
|
240
|
+
fire,
|
241
|
+
|
242
|
+
subscribeFn,
|
243
|
+
unsubscribeFn,
|
244
|
+
|
245
|
+
path,
|
246
|
+
}
|
247
|
+
}
|
248
|
+
|
249
|
+
|
250
|
+
const addSubscriptionHandler = (
|
251
|
+
moduleName: string,
|
252
|
+
listenerName: string,
|
253
|
+
entityId?: string | number,
|
254
|
+
) => {
|
255
|
+
return addSubscriptionHandlerImpl(moduleName, 'listeners', listenerName, entityId);
|
256
|
+
}
|
257
|
+
|
258
|
+
const addHookHandler = (
|
259
|
+
moduleName: string,
|
260
|
+
listenerName: string,
|
261
|
+
) => {
|
262
|
+
const subscriber = addSubscriptionHandlerImpl(moduleName, 'hooks', listenerName);
|
263
|
+
|
264
|
+
const addResolver = (completionId: number) => {
|
265
|
+
const resolveFn = jest.fn();
|
266
|
+
const rejectFn = jest.fn();
|
267
|
+
|
268
|
+
addRequestHandler(
|
269
|
+
'POST',
|
270
|
+
`${subscriber.path}/${completionId}/resolve`,
|
271
|
+
undefined,
|
272
|
+
resolveFn
|
273
|
+
);
|
274
|
+
|
275
|
+
addRequestHandler(
|
276
|
+
'POST',
|
277
|
+
`${subscriber.path}/${completionId}/reject`,
|
278
|
+
undefined,
|
279
|
+
rejectFn
|
280
|
+
);
|
281
|
+
|
282
|
+
const fire = (data: object) => {
|
283
|
+
send({
|
284
|
+
event: listenerName,
|
285
|
+
data,
|
286
|
+
completion_id: completionId,
|
287
|
+
});
|
288
|
+
}
|
289
|
+
|
290
|
+
return { fire, resolveFn, rejectFn };
|
291
|
+
};
|
292
|
+
|
293
|
+
return {
|
294
|
+
addResolver,
|
295
|
+
|
296
|
+
...subscriber,
|
297
|
+
}
|
298
|
+
}
|
299
|
+
|
300
|
+
|
301
|
+
mockServer.on('connection', s => {
|
302
|
+
socket = s;
|
303
|
+
|
304
|
+
socket.on('message', (messageObj) => {
|
305
|
+
const request: OutgoingRequest = JSON.parse(messageObj as string);
|
306
|
+
const emitId = toEmitId(request.path, request.method);
|
307
|
+
const processed = emitter.emit(emitId, request, s);
|
308
|
+
if (reportMissingListeners && !processed) {
|
309
|
+
console.warn(`Mock server: no listeners for event ${request.method} ${request.path}`);
|
310
|
+
}
|
311
|
+
});
|
312
|
+
});
|
313
|
+
|
314
|
+
mockServer.on('close', () => {
|
315
|
+
emitter.removeAllListeners();
|
316
|
+
});
|
317
|
+
|
318
|
+
return {
|
319
|
+
addRequestHandler,
|
320
|
+
addErrorHandler,
|
321
|
+
|
322
|
+
addSubscriptionHandler,
|
323
|
+
addHookHandler,
|
324
|
+
|
325
|
+
ignoreMissingHandler: addDummyDataHandler,
|
326
|
+
stop: () => {
|
327
|
+
mockServer.stop(() => {
|
328
|
+
// ...
|
329
|
+
});
|
330
|
+
},
|
331
|
+
send,
|
332
|
+
url,
|
333
|
+
};
|
334
|
+
};
|
335
|
+
|
336
|
+
export { getMockServer, getSocket, getConnectedSocket, DEFAULT_CONNECT_PARAMS, DEFAULT_AUTH_RESPONSE };
|