airdcpp-apisocket 3.0.0-beta.6 → 3.0.0-beta.8

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.
Files changed (34) hide show
  1. package/dist-es/SocketBase.js +8 -9
  2. package/dist-es/SocketBase.js.map +1 -1
  3. package/dist-es/SocketRequestHandler.js +3 -5
  4. package/dist-es/SocketRequestHandler.js.map +1 -1
  5. package/dist-es/tests/mocks/index.d.ts +3 -0
  6. package/dist-es/tests/mocks/index.js +4 -0
  7. package/dist-es/tests/mocks/index.js.map +1 -0
  8. package/dist-es/tests/mocks/mock-data.d.ts +24 -0
  9. package/dist-es/tests/mocks/mock-data.js +25 -0
  10. package/dist-es/tests/mocks/mock-data.js.map +1 -0
  11. package/dist-es/tests/mocks/mock-server.d.ts +39 -0
  12. package/dist-es/tests/{mock-server.js → mocks/mock-server.js} +19 -83
  13. package/dist-es/tests/mocks/mock-server.js.map +1 -0
  14. package/dist-es/tests/mocks/mock-socket.d.ts +23 -0
  15. package/dist-es/tests/mocks/mock-socket.js +31 -0
  16. package/dist-es/tests/mocks/mock-socket.js.map +1 -0
  17. package/dist-es/tests/test-utils.d.ts +7 -0
  18. package/dist-es/tests/test-utils.js +23 -0
  19. package/dist-es/tests/test-utils.js.map +1 -1
  20. package/dist-es/types/public_helpers_internal.d.ts +2 -2
  21. package/package.json +2 -2
  22. package/src/SocketBase.ts +9 -10
  23. package/src/SocketRequestHandler.ts +3 -5
  24. package/src/tests/Socket.test.ts +63 -57
  25. package/src/tests/mocks/index.ts +3 -0
  26. package/src/tests/mocks/mock-data.ts +26 -0
  27. package/src/tests/{mock-server.ts → mocks/mock-server.ts} +35 -121
  28. package/src/tests/mocks/mock-socket.ts +68 -0
  29. package/src/tests/public_helpers.test.ts +4 -2
  30. package/src/tests/test-utils.ts +27 -1
  31. package/src/types/api_internal.ts +0 -1
  32. package/src/types/public_helpers_internal.ts +2 -2
  33. package/dist-es/tests/mock-server.d.ts +0 -96
  34. package/dist-es/tests/mock-server.js.map +0 -1
package/src/SocketBase.ts CHANGED
@@ -125,11 +125,11 @@ const ApiSocket = (userOptions: Options.APISocketOptions, WebSocketImpl: WebSock
125
125
  // Connect handler for creation of new session
126
126
  const handlePasswordLogin = (username = options.username, password = options.password) => {
127
127
  if (!username) {
128
- throw '"username" option was not supplied for authentication';
128
+ throw new Error('"username" option was not supplied for authentication');
129
129
  }
130
130
 
131
131
  if (!password) {
132
- throw '"password" option was not supplied for authentication';
132
+ throw new Error('"password" option was not supplied for authentication');
133
133
  }
134
134
 
135
135
  const data: API.CredentialsAuthenticationData = {
@@ -146,7 +146,7 @@ const ApiSocket = (userOptions: Options.APISocketOptions, WebSocketImpl: WebSock
146
146
 
147
147
  const handleRefreshTokenLogin = (refreshToken: string) => {
148
148
  if (!refreshToken) {
149
- throw '"refreshToken" option was not supplied for authentication';
149
+ throw new Error('"refreshToken" option was not supplied for authentication');
150
150
  }
151
151
 
152
152
  const data: API.RefreshTokenAuthenticationData = {
@@ -317,7 +317,7 @@ const ApiSocket = (userOptions: Options.APISocketOptions, WebSocketImpl: WebSock
317
317
  if (isActive()) {
318
318
  if (attempts >= maxAttempts) {
319
319
  logger.error(`Socket disconnect timed out after ${timeoutMs} ms`);
320
- reject('Socket disconnect timed out');
320
+ reject(new Error('Socket disconnect timed out'));
321
321
  } else {
322
322
  setTimeout(wait, checkInterval);
323
323
  attempts++;
@@ -343,7 +343,6 @@ const ApiSocket = (userOptions: Options.APISocketOptions, WebSocketImpl: WebSock
343
343
  }
344
344
  } else {
345
345
  logger.warn('Attempting to disconnect a closed socket (ignore)');
346
- //throw 'Attempting to disconnect a closed socket';
347
346
  }
348
347
 
349
348
  return;
@@ -363,7 +362,7 @@ const ApiSocket = (userOptions: Options.APISocketOptions, WebSocketImpl: WebSock
363
362
  // Username and password are not required if those are available in socket options
364
363
  connect: (username?: string, password?: string, reconnectOnFailure = true) => {
365
364
  if (isActive()) {
366
- throw 'Connect may only be used for a closed socket';
365
+ throw new Error('Connect may only be used for a closed socket');
367
366
  }
368
367
 
369
368
  resetSession();
@@ -373,7 +372,7 @@ const ApiSocket = (userOptions: Options.APISocketOptions, WebSocketImpl: WebSock
373
372
 
374
373
  connectRefreshToken: (refreshToken: string, reconnectOnFailure = true) => {
375
374
  if (isActive()) {
376
- throw 'Connect may only be used for a closed socket';
375
+ throw new Error('Connect may only be used for a closed socket');
377
376
  }
378
377
 
379
378
  resetSession();
@@ -384,7 +383,7 @@ const ApiSocket = (userOptions: Options.APISocketOptions, WebSocketImpl: WebSock
384
383
  // Connect and attempt to associate the socket with an existing session
385
384
  reconnect: (token: API.AuthTokenType | undefined = undefined, reconnectOnFailure = true) => {
386
385
  if (isActive()) {
387
- throw 'Reconnect may only be used for a closed socket';
386
+ throw new Error('Reconnect may only be used for a closed socket');
388
387
  }
389
388
 
390
389
  if (token) {
@@ -392,7 +391,7 @@ const ApiSocket = (userOptions: Options.APISocketOptions, WebSocketImpl: WebSock
392
391
  }
393
392
 
394
393
  if (!authToken) {
395
- throw 'No session token available for reconnecting';
394
+ throw new Error('No session token available for reconnecting');
396
395
  }
397
396
 
398
397
  logger.info('Reconnecting socket');
@@ -463,7 +462,7 @@ const ApiSocket = (userOptions: Options.APISocketOptions, WebSocketImpl: WebSock
463
462
  ...requests.socket,
464
463
  };
465
464
 
466
- return socket!;
465
+ return socket;
467
466
  };
468
467
 
469
468
  export default ApiSocket;
@@ -59,12 +59,12 @@ const SocketRequestHandler = (
59
59
  // Pre-checks
60
60
  if (!authenticating && !socket().isConnected()) {
61
61
  logger.warn(`Attempting to send request on a non-authenticated socket: ${path}`);
62
- return Promise.reject('Not authorized');
62
+ return Promise.reject(new Error('Not authorized'));
63
63
  }
64
64
 
65
65
  if (!socket().nativeSocket) {
66
66
  logger.warn(`Attempting to send request without a socket: ${path}`);
67
- return Promise.reject('No socket');
67
+ return Promise.reject(new Error('No socket'));
68
68
  }
69
69
 
70
70
  const callbackId = getCallbackId();
@@ -143,12 +143,10 @@ const SocketRequestHandler = (
143
143
  },
144
144
 
145
145
  delete: (path) => {
146
- //invariant(!data, 'No data is allowed for delete command');
147
146
  return sendRequest('DELETE', path);
148
147
  },
149
148
 
150
149
  get: (path) => {
151
- //invariant(!data, 'No data is allowed for get command');
152
150
  return sendRequest('GET', path);
153
151
  },
154
152
 
@@ -188,7 +186,7 @@ const SocketRequestHandler = (
188
186
  if (messageObj.code >= 200 && messageObj.code <= 204) {
189
187
  const { data } = messageObj as APIInternal.RequestSuccessResponse;
190
188
  if (!callbacks[id].ignored) {
191
- logger.verbose(chalk.green(id.toString()), 'SUCCEEDED', data ? data : '(no data)');
189
+ logger.verbose(chalk.green(id.toString()), 'SUCCEEDED', data ?? '(no data)');
192
190
  }
193
191
 
194
192
  callbacks[id].resolver.resolve(data);
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  DEFAULT_AUTH_RESPONSE, DEFAULT_CONNECT_PARAMS,
3
- getConnectedSocket, getMockServer, getSocket
4
- } from './mock-server.js';
3
+ getConnectedSocket as getOriginalConnectedSocket, getMockServer as getOriginalMockServer, getSocket as getOriginalSocket
4
+ } from './mocks';
5
5
 
6
6
  import ApiConstants from '../ApiConstants.js';
7
7
 
@@ -9,18 +9,23 @@ 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 { waitForExpect } from './test-utils.js';
13
-
14
- let server: ReturnType<typeof getMockServer>;
12
+ import { defusedPromise, getMockConsole, waitForExpect } from './test-utils.js';
15
13
 
16
14
  const dummyfn = () => {
17
15
  // ..
18
16
  };
19
17
 
18
+
20
19
  // tslint:disable:no-empty
21
20
  describe('socket', () => {
21
+ let server: ReturnType<typeof getOriginalMockServer>;
22
+ let mockConsole: ReturnType<typeof getMockConsole>;
23
+
22
24
  beforeEach(() => {
23
- server = getMockServer();
25
+ mockConsole = getMockConsole();
26
+ server = getOriginalMockServer({
27
+ mockF: jest,
28
+ });
24
29
  });
25
30
 
26
31
  afterEach(() => {
@@ -28,12 +33,27 @@ describe('socket', () => {
28
33
  jest.useRealTimers();
29
34
  });
30
35
 
36
+ const getDefaultSocketOptions = () => ({
37
+ logOutput: mockConsole,
38
+ });
39
+
40
+ const getMockSocket = () => {
41
+ return getOriginalSocket(getDefaultSocketOptions());
42
+ }
43
+
44
+
45
+ const getConnectedMockSocket = () => {
46
+ return getOriginalConnectedSocket(server, {
47
+ socketOptions: getDefaultSocketOptions(),
48
+ });
49
+ }
50
+
31
51
  describe('auth', () => {
32
52
  test('should handle valid credentials', async () => {
33
53
  server.addRequestHandler('POST', ApiConstants.LOGIN_URL, DEFAULT_AUTH_RESPONSE);
34
54
  const connectedCallback = jest.fn();
35
55
 
36
- const { socket, mockConsole } = getSocket();
56
+ const { socket } = getMockSocket();
37
57
  socket.onConnected = connectedCallback;
38
58
  const response = await socket.connect();
39
59
 
@@ -52,7 +72,7 @@ describe('socket', () => {
52
72
  server.addRequestHandler('POST', ApiConstants.LOGIN_URL, DEFAULT_AUTH_RESPONSE);
53
73
  const connectedCallback = jest.fn();
54
74
 
55
- const { socket, mockConsole } = getSocket();
75
+ const { socket } = getMockSocket();
56
76
  socket.onConnected = connectedCallback;
57
77
  const response = await socket.connectRefreshToken('refresh token');
58
78
 
@@ -70,7 +90,7 @@ describe('socket', () => {
70
90
  test('should handle invalid credentials', async () => {
71
91
  server.addErrorHandler('POST', ApiConstants.LOGIN_URL, 'Invalid username or password', 401);
72
92
 
73
- const { socket, mockConsole } = getSocket();
93
+ const { socket } = getMockSocket();
74
94
  let error;
75
95
 
76
96
  try {
@@ -89,9 +109,10 @@ describe('socket', () => {
89
109
 
90
110
  test('should handle connect with custom credentials', async () => {
91
111
  server.stop();
92
- const { socket } = getSocket({
112
+ const { socket } = getOriginalSocket({
93
113
  username: 'dummy',
94
114
  password: 'dummy',
115
+ ...getDefaultSocketOptions(),
95
116
  });
96
117
 
97
118
  // Fail without a server handler with auto reconnect disabled
@@ -106,7 +127,7 @@ describe('socket', () => {
106
127
  await waitForExpect(() => expect(socket.isActive()).toEqual(false));
107
128
 
108
129
  // Valid connect attempt
109
- server = getMockServer();
130
+ server = getOriginalMockServer();
110
131
  server.addRequestHandler('POST', ApiConstants.LOGIN_URL, DEFAULT_AUTH_RESPONSE);
111
132
 
112
133
  await socket.connect(DEFAULT_CONNECT_PARAMS.username, DEFAULT_CONNECT_PARAMS.password, false);
@@ -121,7 +142,7 @@ describe('socket', () => {
121
142
  const sessionResetCallback = jest.fn();
122
143
  const disconnectedCallback = jest.fn();
123
144
 
124
- const { socket, mockConsole } = await getConnectedSocket(server);
145
+ const { socket } = await getConnectedMockSocket();
125
146
  socket.onSessionReset = sessionResetCallback;
126
147
  socket.onDisconnected = disconnectedCallback;
127
148
 
@@ -132,10 +153,7 @@ describe('socket', () => {
132
153
  // Dummy pending request
133
154
  server.ignoreMissingHandler('DELETE', 'dummyLogoutDelete');
134
155
 
135
- socket.delete('dummyLogoutDelete').catch((error: Error) => {
136
- // TODO: fix, too unreliable at the moment (depends on the timings)
137
- //expect(error.message).toEqual('Socket disconnected');
138
- });
156
+ const pendingRequestPromise = defusedPromise(socket.delete('dummyLogoutDelete'));
139
157
 
140
158
  // Logout
141
159
  server.addRequestHandler('DELETE', ApiConstants.LOGOUT_URL);
@@ -144,6 +162,8 @@ describe('socket', () => {
144
162
  expect(sessionResetCallback.mock.calls.length).toBe(1);
145
163
  await waitForExpect(() => expect(disconnectedCallback.mock.calls.length).toBe(1));
146
164
 
165
+ await expect(pendingRequestPromise).rejects.toMatchInlineSnapshot(`"Socket disconnected"`);
166
+
147
167
  expect(socket.isActive()).toEqual(false);
148
168
  expect(socket.hasListeners()).toEqual(false);
149
169
  expect(socket.getPendingRequestCount()).toEqual(0);
@@ -155,7 +175,7 @@ describe('socket', () => {
155
175
 
156
176
  describe('disconnect', () => {
157
177
  test('should handle disconnect', async () => {
158
- const { socket, mockConsole } = await getConnectedSocket(server);
178
+ const { socket } = await getConnectedMockSocket();
159
179
 
160
180
  socket.disconnect();
161
181
 
@@ -167,16 +187,16 @@ describe('socket', () => {
167
187
  });
168
188
 
169
189
  test('should handle wait disconnected timeout', async () => {
170
- const { socket, mockConsole } = await getConnectedSocket(server);
190
+ const { socket } = await getConnectedMockSocket();
171
191
 
172
- let error;
192
+ let error: Error | null = null;
173
193
  try {
174
194
  await socket.waitDisconnected(50);
175
195
  } catch (e) {
176
196
  error = e;
177
197
  }
178
198
 
179
- expect(error).toEqual('Socket disconnect timed out');
199
+ expect(error?.message).toEqual('Socket disconnect timed out');
180
200
 
181
201
  expect(mockConsole.error.mock.calls.length).toBe(1);
182
202
  expect(mockConsole.warn.mock.calls.length).toBe(0);
@@ -188,19 +208,14 @@ describe('socket', () => {
188
208
 
189
209
  describe('reconnect', () => {
190
210
  test('should handle auto reconnect', async () => {
191
- const { socket, mockConsole } = await getConnectedSocket(server);
211
+ const { socket } = await getConnectedMockSocket();
192
212
 
193
213
  jest.useFakeTimers();
194
214
 
195
215
  socket.disconnect(true);
196
216
  jest.runOnlyPendingTimers();
197
217
 
198
- // TODO: fix
199
- /*{
200
- const waitForExpectTask = await waitForExpect(() => expect(socket.isActive()).toEqual(false));
201
- jest.advanceTimersByTime(1000);
202
- await waitForExpectTask;
203
- }*/
218
+ expect(socket.isActive()).toEqual(false);
204
219
 
205
220
  // Let it fail once
206
221
  server.stop();
@@ -208,7 +223,7 @@ describe('socket', () => {
208
223
  jest.runOnlyPendingTimers();
209
224
  expect(mockConsole.error.mock.calls.length).toBe(1);
210
225
 
211
- server = getMockServer();
226
+ server = getOriginalMockServer();
212
227
  server.addRequestHandler('POST', ApiConstants.CONNECT_URL, undefined);
213
228
  jest.runOnlyPendingTimers();
214
229
  jest.runOnlyPendingTimers();
@@ -229,7 +244,7 @@ describe('socket', () => {
229
244
  });
230
245
 
231
246
  test('should cancel auto reconnect', async () => {
232
- const { socket, mockConsole } = await getConnectedSocket(server);
247
+ const { socket } = await getConnectedMockSocket();
233
248
 
234
249
  jest.useFakeTimers();
235
250
 
@@ -255,7 +270,7 @@ describe('socket', () => {
255
270
  });
256
271
 
257
272
  test('should handle manual reconnect', async () => {
258
- const { socket, mockConsole } = await getConnectedSocket(server);
273
+ const { socket } = await getConnectedMockSocket();
259
274
 
260
275
  socket.disconnect();
261
276
  await waitForExpect(() => expect(socket.isActive()).toEqual(false));
@@ -271,12 +286,13 @@ describe('socket', () => {
271
286
  await waitForExpect(() => expect(socket.isActive()).toEqual(false));
272
287
  });
273
288
 
274
- // TODO: fix
275
- test.skip('should re-authenticate on lost session', async () => {
289
+ test('should re-authenticate on lost session', async () => {
276
290
  const ErrorResponse = 'Invalid session token';
291
+ const authCallback = jest.fn();
292
+ const connectErrorCallback = jest.fn();
277
293
 
278
294
  // Connect and disconnect
279
- const { socket, mockConsole } = await getConnectedSocket(server);
295
+ const { socket } = await getConnectedMockSocket();
280
296
 
281
297
  jest.useFakeTimers();
282
298
  socket.disconnect();
@@ -285,25 +301,16 @@ describe('socket', () => {
285
301
 
286
302
  // Fail the initial reconnect attempt with 'Invalid session token'
287
303
  // and connect with credentials afterwards
288
- server.addErrorHandler('POST', ApiConstants.CONNECT_URL, ErrorResponse, 400);
304
+ server.addErrorHandler('POST', ApiConstants.CONNECT_URL, ErrorResponse, 400, connectErrorCallback);
289
305
 
290
- const authCallback = jest.fn();
291
306
  server.addRequestHandler('POST', ApiConstants.LOGIN_URL, DEFAULT_AUTH_RESPONSE, authCallback);
292
307
 
293
308
  jest.runOnlyPendingTimers();
294
309
  socket.reconnect();
295
310
 
296
- {
297
- const waitForExpectTask = waitForExpect(
298
- () => {
299
- jest.runOnlyPendingTimers();
300
- expect(authCallback.mock.calls.length).toBe(1);
301
- }
302
- );
303
-
304
- jest.advanceTimersByTime(1000);
305
- await waitForExpectTask;
306
- }
311
+ await jest.advanceTimersByTimeAsync(1000);
312
+ expect(authCallback.mock.calls.length).toBe(1);
313
+ expect(connectErrorCallback.mock.calls.length).toBe(1);
307
314
 
308
315
  expect(socket.isConnected()).toEqual(true);
309
316
  expect(mockConsole.error.mock.calls.length).toBe(0);
@@ -329,7 +336,7 @@ describe('socket', () => {
329
336
 
330
337
  describe('requests', () => {
331
338
  test('should report request timeouts', async () => {
332
- const { socket, mockConsole } = await getConnectedSocket(server);
339
+ const { socket } = await getConnectedMockSocket();
333
340
 
334
341
  server.ignoreMissingHandler('POST', 'hubs/listeners/hub_updated');
335
342
  server.ignoreMissingHandler('POST', 'hubs/listeners/hub_added');
@@ -359,7 +366,7 @@ describe('socket', () => {
359
366
  });
360
367
 
361
368
  test('should handle missing error messages', async () => {
362
- const { socket } = await getConnectedSocket(server);
369
+ const { socket } = await getConnectedMockSocket();
363
370
 
364
371
  server.addErrorHandler('POST', 'test/test', null, 401);
365
372
 
@@ -393,7 +400,7 @@ describe('socket', () => {
393
400
  };
394
401
 
395
402
  test('should handle listener messages', async () => {
396
- const { socket, mockConsole } = await getConnectedSocket(server);
403
+ const { socket } = await getConnectedMockSocket();
397
404
  server.addSubscriptionHandler('hubs', 'hub_updated');
398
405
  server.addSubscriptionHandler('hubs', 'hub_updated', entityId);
399
406
 
@@ -419,7 +426,7 @@ describe('socket', () => {
419
426
  });
420
427
 
421
428
  test('should handle listener removal', async () => {
422
- const { socket, mockConsole } = await getConnectedSocket(server);
429
+ const { socket } = await getConnectedMockSocket();
423
430
 
424
431
  const hubUpdatedListener = server.addSubscriptionHandler('hubs', 'hub_updated');
425
432
 
@@ -451,7 +458,7 @@ describe('socket', () => {
451
458
  });
452
459
 
453
460
  test('should handle view updates', async () => {
454
- const { socket, mockConsole } = await getConnectedSocket(server);
461
+ const { socket } = await getConnectedMockSocket();
455
462
  const viewUpdateCallback = jest.fn();
456
463
 
457
464
  const removeListener = socket.addViewUpdateListener('hub_user_view', viewUpdateCallback, entityId);
@@ -482,7 +489,7 @@ describe('socket', () => {
482
489
  };
483
490
 
484
491
  test('should handle hook actions', async () => {
485
- const { socket, mockConsole } = await getConnectedSocket(server);
492
+ const { socket } = await getConnectedMockSocket();
486
493
  let removeListener: SubscriptionRemoveHandler | null = null;
487
494
 
488
495
  // Add hook
@@ -506,10 +513,8 @@ describe('socket', () => {
506
513
  }
507
514
 
508
515
  // Clean up
509
- {
510
- removeListener();
511
- expect(socket.hasListeners()).toBe(false);
512
- }
516
+ removeListener();
517
+ expect(socket.hasListeners()).toBe(false);
513
518
 
514
519
  expect(mockConsole.warn.mock.calls.length).toBe(0);
515
520
 
@@ -520,9 +525,10 @@ describe('socket', () => {
520
525
 
521
526
  describe('logging', () => {
522
527
  const connect = async (logLevel: string) => {
523
- const { socket, mockConsole } = await getConnectedSocket(server, {
528
+ const { socket } = await getOriginalConnectedSocket(server, {
524
529
  socketOptions: {
525
530
  logLevel,
531
+ ...getDefaultSocketOptions(),
526
532
  },
527
533
  });
528
534
 
@@ -0,0 +1,3 @@
1
+ export * from './mock-data.js';
2
+ export * from './mock-server.js';
3
+ export * from './mock-socket.js';
@@ -0,0 +1,26 @@
1
+
2
+ export const DEFAULT_CONNECT_PARAMS = {
3
+ username: 'test',
4
+ password: 'test',
5
+ url: 'ws://localhost:7171/api/v1/',
6
+ };
7
+
8
+ export const DEFAULT_AUTH_RESPONSE = {
9
+ auth_token: 'b823187f-4aab-4b71-9764-e63e88401a26',
10
+ refresh_token: '5124faasf-4aab-4b71-9764-e63e88401a26',
11
+ user: {
12
+ permissions: [ 'admin' ],
13
+ username: 'test',
14
+ active_sessions: 1,
15
+ last_login: 0,
16
+ },
17
+ system: {
18
+ cid: 'AHLUODI2YZ2U7FDWMHFNJU65ERGKUN4MH7GW5LY',
19
+ hostname: 'ubuntu-htpc',
20
+ network_type: 'private',
21
+ path_separator: '/',
22
+ platform: 'other',
23
+ language: 'fi',
24
+ },
25
+ wizard_pending: false,
26
+ };