biz-a-cli 2.3.73 → 2.3.74

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/tests/hub.test.js DELETED
@@ -1,998 +0,0 @@
1
- import { Server as ioServer } from "socket.io";
2
- import { createServer } from "node:http";
3
- import { streamEvent, hubEvent, socketAgent } from "../bin/hubEvent.js";
4
- import { io as ioClient } from "socket.io-client";
5
- import { afterAll, expect, jest } from "@jest/globals";
6
- import axios from "axios";
7
- import net from "node:net";
8
- import tls from "node:tls";
9
- import ss from "socket.io-stream";
10
- import { Writable, pipeline } from "node:stream";
11
- import { text } from "node:stream/consumers";
12
- import { directHubEvent, createSocketServer } from "../bin/directHubEvent.js";
13
- import os from "node:os";
14
- import { cliScriptWorkerPool } from "../worker/cliWorkerPool.js";
15
- import workerpool from "workerpool";
16
- import { isAsyncFunction } from "node:util/types";
17
-
18
- let socketsBySubdomain = {};
19
-
20
- const createSockTunnel2CLI = (socket) => {
21
- return (subdomain, responseCb) => {
22
- const responseCallback = (error) => {
23
- if (responseCb) responseCb(error);
24
- };
25
- let subdomainStr = subdomain.toString();
26
-
27
- socketsBySubdomain[subdomainStr] = socket;
28
- socket.subdomain = subdomainStr;
29
-
30
- responseCallback(null);
31
- };
32
- };
33
-
34
- const deleteSubdomain = (socket) => () => {
35
- if (socket.subdomain) {
36
- delete socketsBySubdomain[socket.subdomain];
37
- console.log(new Date() + ": " + socket.subdomain + " unregistered");
38
- }
39
- };
40
-
41
- const toPromise = (cb) =>
42
- new Promise((resolve, reject) => {
43
- cb(resolve);
44
- setTimeout(() => reject(new Error("timeout")), 1000);
45
- });
46
-
47
- describe("Hub event tests", () => {
48
- let bizAServer_ServerSocket, port;
49
- let cliHttpServer;
50
-
51
- const startSocket = function startSocket(server) {
52
- let io = new ioServer(server);
53
- io.on("connection", (socket) => {
54
- socket.on("createTunnel", createSockTunnel2CLI(socket));
55
- socket.on("disconnect", deleteSubdomain(socket));
56
- });
57
- return io;
58
- };
59
-
60
- function freeSocketClient(sock) {
61
- if (sock) {
62
- if (sock.connected) {
63
- sock.off();
64
- sock.disconnect();
65
- }
66
- sock.destroy();
67
- sock = undefined;
68
- }
69
- }
70
-
71
- beforeAll((done) => {
72
- cliHttpServer = createServer();
73
- bizAServer_ServerSocket = startSocket(cliHttpServer);
74
-
75
- cliHttpServer.listen(async () => {
76
- port = cliHttpServer.address().port;
77
- done();
78
- });
79
- });
80
-
81
- afterAll(async () => {
82
- bizAServer_ServerSocket.close();
83
- await cliHttpServer.close();
84
- });
85
-
86
- test("request to cli", async () => {
87
- const logSpy = jest.spyOn(console, "log").mockImplementation();
88
- let mockedRequest = jest
89
- .spyOn(axios, "request")
90
- .mockReturnValue({ data: "OK" });
91
-
92
- let socket;
93
- try {
94
- socket = ioClient(`http://localhost:${port}`);
95
- await streamEvent(socket, {
96
- server: `http://localhost:${port}`,
97
- subdomain: "scy",
98
- hostname: "localhost",
99
- port: 212,
100
- serverport: 3002,
101
- });
102
-
103
- let result = await toPromise((resolve) =>
104
- socketsBySubdomain["scy"].emit(
105
- "cli-req",
106
- {
107
- method: "POST",
108
- query: { subdomain: "abc" },
109
- body: { data: "xyz" },
110
- },
111
- (cb) => resolve(cb),
112
- ),
113
- );
114
-
115
- expect(result).toStrictEqual("OK");
116
- expect(mockedRequest).toHaveBeenCalledWith({
117
- data: { body: { data: "xyz" }, query: { subdomain: "abc" } },
118
- method: "POST",
119
- url: "http://localhost:3002/cb",
120
- });
121
-
122
- result = await toPromise((resolve) =>
123
- socketsBySubdomain["scy"].emit(
124
- "cli-req",
125
- {
126
- path: "/a/b",
127
- method: "POST",
128
- query: { subdomain: "abc" },
129
- body: { data: "xyz" },
130
- },
131
- (cb) => resolve(cb),
132
- ),
133
- );
134
-
135
- expect(result).toStrictEqual("OK");
136
- expect(mockedRequest).toHaveBeenCalledWith({
137
- data: { body: { data: "xyz" }, query: { subdomain: "abc" } },
138
- method: "POST",
139
- url: "http://localhost:3002/cb/a/b",
140
- });
141
-
142
- socket.disconnect();
143
-
144
- expect(logSpy).toHaveBeenCalledWith("error", "error");
145
-
146
- logSpy.mockRestore();
147
- } catch (error) {
148
- console.log(error);
149
- }
150
- });
151
-
152
- test("should used correct socket agent", () => {
153
- expect(socketAgent(false)).toBe(net);
154
- expect(socketAgent(true)).toBe(tls);
155
- });
156
-
157
- test("should access API with scheme of BizA Client <--https--> BizA Server <--socket stream--> BizA CLI <--socket--> API", async () => {
158
- const uniqueId = "123456"; // this is used for make sure BizA Server will get correct response from BizA CLI
159
- const subdomain = "hub";
160
-
161
- let cliToBizAServerSocket; // mocking of socket client from BizA CLI to BizA Server
162
- let mockAPIServer; // mocking of FINA API Datasnap server. We need to used TCP Server to test data flow using socket, so don't used HTTP Server
163
- let cliToAPISocket; // mocking of socket client from BizA CLI to FINA API Datasnap Server
164
-
165
- try {
166
- cliToBizAServerSocket = ioClient(`http://localhost:${port}`);
167
- await streamEvent(cliToBizAServerSocket, {
168
- server: `http://localhost:${port}`,
169
- subdomain,
170
- hostname: "127.0.0.1",
171
- port: 212,
172
- serverport: 3002,
173
- cliAddress: () => {
174
- return {
175
- ip: "59.60.1.22",
176
- port: "3002",
177
- address: `59.60.1.22:3002`,
178
- publicUrl: "https://some.tunnel.url",
179
- hubUrl: "https://some.hub.url",
180
- };
181
- },
182
- hubServer: "https://some.hub.url",
183
- });
184
-
185
- mockAPIServer = net.createServer((clientSocket) => {
186
- cliToAPISocket = clientSocket;
187
- clientSocket.on("data", (data) => {
188
- if (
189
- data.toString().toUpperCase() ===
190
- "POST /SUCCESS HTTP/1.1"
191
- ) {
192
- clientSocket.write(
193
- "HTTP/1.1 200 Ok\r\nServer: datasnapHTTPService\r\n\r\n",
194
- );
195
- } else if (
196
- data.toString().toUpperCase() === "POST /FAIL HTTP/1.1"
197
- ) {
198
- clientSocket.write(
199
- "HTTP/1.1 403 Forbidden\r\nServer: datasnapHTTPService\r\n\r\n",
200
- );
201
- }
202
- });
203
- });
204
- await toPromise((resolve) => {
205
- mockAPIServer.listen({ port: 212, host: "127.0.0.1" }, () => {
206
- resolve();
207
- });
208
- });
209
-
210
- const getResponse = async (httpRawRequest) => {
211
- let tunnelStream; // mocking of socket stream between BizA CLI and BizA Server, also act as mocking of HTTP Request from BizA Client
212
- let outputStream; // mocking of HTTP Response received by BizA Client
213
- try {
214
- let outputContent = [];
215
- outputStream = new Writable({
216
- write(chunk, encoding, next) {
217
- outputContent.push(chunk);
218
- cliToAPISocket.end(); // Imitate API Server to finish sending response, it will call end "tunnelStream" and lastly it will close "outputStream"
219
- next();
220
- },
221
- });
222
- tunnelStream = await toPromise((resolve) => {
223
- ss(socketsBySubdomain[subdomain]).once(
224
- uniqueId,
225
- (stream) => {
226
- pipeline(stream, outputStream, (err) => {
227
- if (err) console.error(err);
228
- });
229
- resolve(stream);
230
- },
231
- );
232
- socketsBySubdomain[subdomain].emit(
233
- "incomingClient",
234
- uniqueId,
235
- );
236
- });
237
-
238
- tunnelStream.write(httpRawRequest);
239
- await text(tunnelStream); // this will try to read data from "cliToAPISocket" -> "transformStream" -> "tunnelStream" -> "outputStream"
240
- return outputContent.toString();
241
- } finally {
242
- if (tunnelStream) tunnelStream.destroy();
243
- if (outputStream) outputStream.destroy();
244
- freeSocketClient(cliToAPISocket);
245
- }
246
- };
247
-
248
- expect(await getResponse("POST /success HTTP/1.1")).toBe(
249
- "HTTP/1.1 200 Ok\r\n" +
250
- "Server: datasnapHTTPService\r\n" +
251
- "Access-Control-Expose-Headers: biza-cli-address, biza-hub-address\r\n" +
252
- "biza-cli-address: https://some.tunnel.url\r\n" +
253
- "biza-hub-address: https://some.hub.url\r\n\r\n",
254
- );
255
-
256
- expect(await getResponse("POST /fail HTTP/1.1")).toBe(
257
- "HTTP/1.1 403 Forbidden\r\n" +
258
- "Server: datasnapHTTPService\r\n\r\n",
259
- );
260
- } finally {
261
- freeSocketClient(cliToBizAServerSocket);
262
- if (mockAPIServer) mockAPIServer.close();
263
- }
264
- });
265
-
266
- describe("should access API with scheme of BizA Client <--socket tunnel--> BizA CLI <--http--> API", () => {
267
- const apiAddress = "127.0.0.1";
268
- const subdomain = "directHub";
269
- let cliSocketServer; // mocking of CLI socket server
270
- let clientToCliSocket; // mocking of socket client from BizA Client to BizA CLI
271
- let mockedRequest;
272
-
273
- beforeEach(() => {
274
- mockedRequest = jest.spyOn(axios, "request");
275
- mockedRequest.mockClear();
276
- });
277
-
278
- beforeAll(() => {
279
- cliSocketServer = new ioServer(9999); // mocking of CLI socket server
280
- directHubEvent(cliSocketServer, {
281
- subdomain,
282
- hostname: apiAddress,
283
- port,
284
- secure: false,
285
- dbindex: 2,
286
- cliAddress: () => {
287
- return {
288
- ip: "59.60.1.22",
289
- port: "3002",
290
- address: `59.60.1.22:3002`,
291
- publicUrl: "https://some.tunnel.url",
292
- hubUrl: "https://some.hub.url",
293
- };
294
- },
295
- });
296
- clientToCliSocket = ioClient("http://localhost:9999", {
297
- query: { isClient: true },
298
- });
299
- });
300
-
301
- afterAll(async () => {
302
- freeSocketClient(clientToCliSocket);
303
- if (cliSocketServer) {
304
- await cliSocketServer
305
- .of("")
306
- .sockets.forEach((sock) => sock.removeAllListeners());
307
- await cliSocketServer.close();
308
- }
309
- });
310
-
311
- describe("apiRequest listener", () => {
312
- test("should return success response", async () => {
313
- mockedRequest.mockResolvedValueOnce({
314
- status: 200,
315
- statusText: "Success",
316
- headers: { "res-header": "aa" },
317
- data: "body of response",
318
- config: { baseURL: "localhost", url: "/test" },
319
- });
320
-
321
- const [error, response] = await toPromise((resolve) => {
322
- clientToCliSocket.emit(
323
- "apiRequest",
324
- {
325
- subDomain: subdomain,
326
- method: "POST",
327
- path: "/test",
328
- headers: { "req-header": "aa" },
329
- body: "body of request",
330
- responseType: "json",
331
- },
332
- (err, res) => {
333
- resolve([err, res]);
334
- },
335
- );
336
- });
337
-
338
- expect(response).toStrictEqual({
339
- status: 200,
340
- statusText: "Success",
341
- headers: {
342
- "res-header": "aa",
343
- "biza-cli-address": "https://some.tunnel.url",
344
- "biza-hub-address": "https://some.hub.url",
345
- },
346
- body: "body of response",
347
- url: `http://${apiAddress}:${port}/test`,
348
- });
349
-
350
- expect(error).toBe(null);
351
-
352
- expect(mockedRequest).toHaveBeenCalledWith({
353
- timeout: 30 * 1000,
354
- baseURL: `http://127.0.0.1:${port}`,
355
- url: "/test",
356
- method: "POST",
357
- headers: { "req-header": "aa" },
358
- data: "body of request",
359
- // decompress: false,
360
- responseType: "json",
361
- maxContentLength: Infinity,
362
- });
363
- });
364
-
365
- test("should return fail response", async () => {
366
- const mockErrorResponse = {
367
- response: {
368
- status: 400,
369
- statusText: "Failed",
370
- headers: { "res-header": "aa" },
371
- data: "body of response",
372
- config: { baseURL: "localhost", url: "/test" },
373
- },
374
- };
375
- mockedRequest.mockRejectedValueOnce(mockErrorResponse);
376
-
377
- const [error, response] = await toPromise((resolve) => {
378
- clientToCliSocket.emit(
379
- "apiRequest",
380
- {
381
- subDomain: subdomain,
382
- method: "POST",
383
- path: "/test",
384
- headers: { "req-header": "aa" },
385
- body: "body of request",
386
- responseType: "arraybuffer",
387
- },
388
- (err, res) => {
389
- resolve([err, res]);
390
- },
391
- );
392
- });
393
-
394
- expect(response).toBe(null);
395
-
396
- expect(error).toStrictEqual(mockErrorResponse);
397
-
398
- expect(mockedRequest).toHaveBeenCalledWith({
399
- timeout: 30 * 1000,
400
- baseURL: `http://127.0.0.1:${port}`,
401
- url: "/test",
402
- method: "POST",
403
- headers: { "req-header": "aa" },
404
- data: "body of request",
405
- // decompress: false,
406
- responseType: "arraybuffer",
407
- maxContentLength: Infinity,
408
- });
409
- });
410
-
411
- test("should return bad subdomain response", async () => {
412
- const [error, response] = await toPromise((resolve) => {
413
- clientToCliSocket.emit(
414
- "apiRequest",
415
- {
416
- subDomain: "wrong subdomain",
417
- method: "POST",
418
- path: "/test",
419
- headers: { "req-header": "aa" },
420
- body: "body of request",
421
- },
422
- (err, res) => {
423
- resolve([err, res]);
424
- },
425
- );
426
- });
427
-
428
- expect(response).toBe(null);
429
-
430
- expect(error).toStrictEqual({
431
- status: 401,
432
- statusText: "bad subdomain",
433
- url: `http://${apiAddress}:${port}/test`,
434
- });
435
-
436
- expect(mockedRequest).not.toHaveBeenCalledWith();
437
- });
438
- });
439
-
440
- describe("cliCommand listener", () => {
441
- test("status", async () => {
442
- const runCommand = async (command) => {
443
- return await toPromise((resolve) => {
444
- clientToCliSocket.emit(
445
- "cliCommand",
446
- { command },
447
- (err, res) => {
448
- resolve([err, res]);
449
- },
450
- );
451
- });
452
- };
453
-
454
- let [error, response] = await runCommand("StAtus ");
455
- const cpuArchitecture = os.arch();
456
- const osArchitecture = [
457
- "x64",
458
- "arm64",
459
- "ppc64",
460
- "s390x",
461
- ].includes(cpuArchitecture)
462
- ? "64-bit"
463
- : ["ia32", "arm", "mips", "mipsel", "s390"].includes(cpuArchitecture)
464
- ? "32-bit"
465
- : `Unknown CPU architecture: ${cpuArchitecture}`;
466
- expect(response).toMatchObject({
467
- cli: {
468
- hubUrl: "https://some.hub.url",
469
- publicUrl: "https://some.tunnel.url",
470
- address: "59.60.1.22:3002",
471
- version: expect.any(String),
472
- mode: "test",
473
- memoryUsage: expect.any(String),
474
- nodeVersion: process.version,
475
- uptime: expect.objectContaining({
476
- days: expect.any(Number),
477
- hours: expect.any(Number),
478
- minutes: expect.any(Number),
479
- seconds: expect.any(Number),
480
- }),
481
- workers: {
482
- cliScript: cliScriptWorkerPool.stats(),
483
- },
484
- },
485
- api: {
486
- address: `${apiAddress}:${port}`,
487
- dbIndex: 2,
488
- },
489
- os: {
490
- name: os.type(),
491
- architecture: osArchitecture,
492
- platform: os.platform(),
493
- cpuCount: os.cpus().length,
494
- memory: {
495
- total:
496
- (os.totalmem / (1024 * 1024 * 1024)).toFixed(
497
- 2,
498
- ) + " GB",
499
- free: expect.any(String),
500
- used: expect.any(String),
501
- },
502
- },
503
- });
504
- expect(error).toBe(null);
505
-
506
- [error, response] = await runCommand(" invalid command");
507
- expect(response).toBe(null);
508
- expect(error).toBe("Unknown CLI command 'invalid command'");
509
- });
510
-
511
- describe("runCLIScript", () => {
512
- const runCommand = () =>
513
- toPromise((resolve) => {
514
- clientToCliSocket.emit(
515
- "cliCommand",
516
- {
517
- command: "runCliScript",
518
- scriptName: "sumScript",
519
- scriptData: { x: 1, y: 2 },
520
- },
521
- (err, res) => {
522
- resolve([err, res]);
523
- },
524
- );
525
- });
526
-
527
- test("shall used worker pool", async () => {
528
- expect(cliScriptWorkerPool.workerType).toBe("thread"); // used node.js worker thread
529
- expect(cliScriptWorkerPool.workerThreadOpts).toStrictEqual({
530
- env: { NODE_ENV: process.env.NODE_ENV },
531
- });
532
-
533
- const execSpy = jest.spyOn(cliScriptWorkerPool, "exec");
534
-
535
- execSpy.mockResolvedValueOnce("success");
536
- await expect(runCommand()).resolves.toStrictEqual([
537
- null,
538
- "success",
539
- ]);
540
- expect(execSpy.mock.calls.length).toBe(1);
541
- expect(execSpy).toHaveBeenCalledWith("run", [
542
- {
543
- dbindex: 2,
544
- subdomain,
545
- url: `http://${apiAddress}:${port}`,
546
- },
547
- "sumScript",
548
- { x: 1, y: 2 },
549
- ]);
550
- execSpy.mockReset();
551
-
552
- execSpy.mockRejectedValueOnce("fail");
553
- await expect(runCommand()).resolves.toStrictEqual([
554
- "fail",
555
- null,
556
- ]);
557
- expect(execSpy.mock.calls.length).toBe(1);
558
- expect(execSpy).toHaveBeenCalledWith("run", [
559
- {
560
- dbindex: 2,
561
- subdomain,
562
- url: `http://${apiAddress}:${port}`,
563
- },
564
- "sumScript",
565
- { x: 1, y: 2 },
566
- ]);
567
- execSpy.mockReset();
568
-
569
- // process beforeexit can not be reliably triggered or handled as expected within Jest's test environment, especially in worker thread scenario
570
- // so we trigger the function directly
571
- const workerpoolBeforeExit = process
572
- .listeners("beforeexit")
573
- .find(
574
- (listener) =>
575
- listener
576
- .toString()
577
- .toLowerCase()
578
- .indexOf("cliscriptworkerpool") !== -1,
579
- );
580
- expect(isAsyncFunction(workerpoolBeforeExit)).toBeTruthy();
581
- const terminateSpy = jest.spyOn(
582
- cliScriptWorkerPool,
583
- "terminate",
584
- );
585
- terminateSpy.mockReset();
586
- workerpoolBeforeExit();
587
- expect(terminateSpy.mock.calls.length).toBe(1);
588
- });
589
-
590
- test("shall used worker thread", async () => {
591
- let mockAPIServer;
592
- let scriptWorkerPool;
593
- try {
594
- // cannot used jest mocking because worker thread is running in different context.
595
- // We must create http mock server, then direct worker request to the mock server
596
- mockAPIServer = createServer((req, res) => {
597
- res.writeHead(200, {
598
- "Content-Type": "application/json",
599
- });
600
- res.end(
601
- JSON.stringify([
602
- {
603
- "SYS$CLI_SCRIPT.SCRIPT": `get = function (lib) {
604
- return {
605
- functions: {
606
- onInit: function (data) {
607
- return (data.x + data.y);
608
- }
609
- }
610
- }
611
- }`,
612
- },
613
- ]),
614
- );
615
- });
616
- await toPromise((resolve) => {
617
- mockAPIServer.listen(
618
- { host: "127.0.0.1", port: 0 },
619
- () => {
620
- resolve();
621
- },
622
- );
623
- });
624
- scriptWorkerPool = workerpool.pool(
625
- "./worker/cliScriptWorker.js",
626
- {
627
- workerType: "thread",
628
- workerThreadOpts: {
629
- env: { NODE_ENV: "production" },
630
- },
631
- },
632
- ); // set worker NODE_ENV, so logger will not print to console
633
- const sumResult = await scriptWorkerPool
634
- .exec("run", [
635
- {
636
- dbindex: 2,
637
- subdomain,
638
- url: `http://127.0.0.1:${mockAPIServer.address().port}`,
639
- },
640
- "sumScript",
641
- { x: 1, y: 2 },
642
- ])
643
- .then(async (result) => {
644
- await scriptWorkerPool.terminate();
645
- return result;
646
- })
647
- .catch((err) => {
648
- return err.message || err;
649
- });
650
- expect(sumResult).toBe(3);
651
- } finally {
652
- if (mockAPIServer) {
653
- mockAPIServer.close();
654
- }
655
- }
656
- });
657
- });
658
- });
659
- });
660
-
661
- describe("should access API with scheme of BizA Client <--socket--> BizA Hub <--socket--> BizA CLI <--http--> API", () => {
662
- const apiAddress = "127.0.0.1";
663
- const subdomain = "roomA";
664
- let cliToHubSocket;
665
- let hubToCLISocket;
666
- let hubServer;
667
-
668
- beforeAll(async () => {
669
- hubServer = new ioServer(9998);
670
- hubServer.on("connection", (sock) => (hubToCLISocket = sock));
671
- cliToHubSocket = ioClient("http://localhost:9998");
672
- hubEvent(
673
- cliToHubSocket,
674
- {
675
- server: `http://localhost:${port}`,
676
- subdomain,
677
- hostname: apiAddress,
678
- dbindex: 2,
679
- port,
680
- serverport: 3002,
681
- cliAddress: () => {
682
- return {
683
- ip: "59.60.1.22",
684
- port: "3002",
685
- address: `59.60.1.22:3002`,
686
- publicUrl: "https://some.tunnel.url",
687
- hubUrl: "https://some.hub.url",
688
- };
689
- },
690
- hubServer: "https://some.hub.url",
691
- },
692
- (hubUrl) => {
693
- expect(hubUrl).toBe("https://some.hub.url");
694
- },
695
- );
696
- await toPromise((resolve) =>
697
- cliToHubSocket.once("connect", () => resolve()),
698
- );
699
- });
700
-
701
- afterAll(async () => {
702
- freeSocketClient(cliToHubSocket);
703
- if (hubServer) {
704
- hubServer.close();
705
- }
706
- });
707
-
708
- test("apiRequest listener", async () => {
709
- const emitApiRequest = async (reqData) => {
710
- const [error, response] = await toPromise((resolve) => {
711
- hubToCLISocket.emit(
712
- "apiRequest",
713
- {
714
- subDomain: subdomain,
715
- method: "POST",
716
- path: "/test",
717
- headers: { "req-header": "aa" },
718
- body: reqData,
719
- responseType: "json",
720
- },
721
- (err, res) => {
722
- resolve([err, res]);
723
- },
724
- );
725
- });
726
- return { error, response };
727
- };
728
-
729
- mockedRequest = jest.spyOn(axios, "request");
730
- mockedRequest.mockResolvedValue({
731
- status: 200,
732
- statusText: "Success",
733
- headers: { "res-header": "aa" },
734
- data: "body of response",
735
- config: { baseURL: "localhost", url: "/test" },
736
- });
737
-
738
- // without parameterize apiAddress
739
- mockedRequest.mockClear();
740
- let result = await emitApiRequest("body of request");
741
- expect(result.response).toStrictEqual({
742
- status: 200,
743
- statusText: "Success",
744
- headers: {
745
- "res-header": "aa",
746
- "biza-cli-address": "https://some.tunnel.url",
747
- "biza-hub-address": "https://some.hub.url",
748
- },
749
- body: "body of response",
750
- url: `http://127.0.0.1:${port}/test`,
751
- });
752
- expect(result.error).toBe(null);
753
- expect(mockedRequest).toHaveBeenCalledWith({
754
- timeout: 30 * 1000,
755
- baseURL: `http://127.0.0.1:${port}`,
756
- url: "/test",
757
- method: "POST",
758
- headers: { "req-header": "aa" },
759
- data: "body of request",
760
- responseType: "json",
761
- maxContentLength: Infinity,
762
- });
763
-
764
- // parameterize API address as String
765
- mockedRequest.mockClear();
766
- result = await emitApiRequest(
767
- JSON.stringify({
768
- other: "data",
769
- apiAddress: "http://apiHost:apiPort",
770
- }),
771
- );
772
- expect(result.response).toStrictEqual({
773
- status: 200,
774
- statusText: "Success",
775
- headers: {
776
- "res-header": "aa",
777
- "biza-cli-address": "https://some.tunnel.url",
778
- "biza-hub-address": "https://some.hub.url",
779
- },
780
- body: "body of response",
781
- url: "http://apiHost:apiPort/test",
782
- });
783
- expect(result.error).toBe(null);
784
- expect(mockedRequest).toHaveBeenCalledWith({
785
- timeout: 30 * 1000,
786
- baseURL: "http://apiHost:apiPort",
787
- url: "/test",
788
- method: "POST",
789
- headers: { "req-header": "aa" },
790
- data: JSON.stringify({ other: "data" }),
791
- responseType: "json",
792
- maxContentLength: Infinity,
793
- });
794
-
795
- // parameterize API address as Object
796
- mockedRequest.mockClear();
797
- result = await emitApiRequest({
798
- other: "data",
799
- apiAddress: "http://apiHost:apiPort",
800
- });
801
- expect(result.response).toStrictEqual({
802
- status: 200,
803
- statusText: "Success",
804
- headers: {
805
- "res-header": "aa",
806
- "biza-cli-address": "https://some.tunnel.url",
807
- "biza-hub-address": "https://some.hub.url",
808
- },
809
- body: "body of response",
810
- url: "http://apiHost:apiPort/test",
811
- });
812
- expect(result.error).toBe(null);
813
- expect(mockedRequest).toHaveBeenCalledWith({
814
- timeout: 30 * 1000,
815
- baseURL: "http://apiHost:apiPort",
816
- url: "/test",
817
- method: "POST",
818
- headers: { "req-header": "aa" },
819
- data: { other: "data" },
820
- responseType: "json",
821
- maxContentLength: Infinity,
822
- });
823
- });
824
-
825
- describe("cliCommand listener", () => {
826
- test("status", async () => {
827
- const runCommand = async (command) => {
828
- return await toPromise((resolve) => {
829
- hubToCLISocket.emit(
830
- "cliCommand",
831
- { command },
832
- (err, res) => {
833
- resolve([err, res]);
834
- },
835
- );
836
- });
837
- };
838
-
839
- let [error, response] = await runCommand("StAtus ");
840
- const cpuArchitecture = os.arch();
841
- const osArchitecture = [
842
- "x64",
843
- "arm64",
844
- "ppc64",
845
- "s390x",
846
- ].includes(cpuArchitecture)
847
- ? "64-bit"
848
- : ["ia32", "arm", "mips", "mipsel", "s390"].includes(
849
- cpuArchitecture,
850
- )
851
- ? "32-bit"
852
- : `Unknown CPU architecture: ${cpuArchitecture}`;
853
- expect(response).toMatchObject({
854
- cli: {
855
- hubUrl: "https://some.hub.url",
856
- publicUrl: "https://some.tunnel.url",
857
- address: "59.60.1.22:3002",
858
- version: expect.any(String),
859
- mode: "test",
860
- memoryUsage: expect.any(String),
861
- nodeVersion: process.version,
862
- uptime: expect.objectContaining({
863
- days: expect.any(Number),
864
- hours: expect.any(Number),
865
- minutes: expect.any(Number),
866
- seconds: expect.any(Number),
867
- }),
868
- workers: {
869
- cliScript: cliScriptWorkerPool.stats(),
870
- },
871
- },
872
- api: {
873
- address: `${apiAddress}:${port}`,
874
- dbIndex: 2,
875
- },
876
- os: {
877
- name: os.type(),
878
- architecture: osArchitecture,
879
- platform: os.platform(),
880
- cpuCount: os.cpus().length,
881
- memory: {
882
- total:
883
- (os.totalmem / (1024 * 1024 * 1024)).toFixed(
884
- 2,
885
- ) + " GB",
886
- free: expect.any(String),
887
- used: expect.any(String),
888
- },
889
- },
890
- });
891
- expect(error).toBe(null);
892
-
893
- [error, response] = await runCommand(" invalid command");
894
- expect(response).toBe(null);
895
- expect(error).toBe("Unknown CLI command 'invalid command'");
896
- });
897
-
898
- describe("runCLIScript", () => {
899
- test("shall used worker pool", async () => {
900
- expect(cliScriptWorkerPool.workerType).toBe("thread"); // used node.js worker thread
901
-
902
- const execSpy = jest.spyOn(cliScriptWorkerPool, "exec");
903
- const runCommand = () =>
904
- toPromise((resolve) => {
905
- hubToCLISocket.emit(
906
- "cliCommand",
907
- {
908
- command: "runCliScript",
909
- scriptName: "sumScript",
910
- scriptData: { x: 1, y: 2 },
911
- },
912
- (err, res) => {
913
- resolve([err, res]);
914
- },
915
- );
916
- });
917
-
918
- execSpy.mockResolvedValueOnce("success");
919
- await expect(runCommand()).resolves.toStrictEqual([
920
- null,
921
- "success",
922
- ]);
923
- expect(execSpy.mock.calls.length).toBe(1);
924
- expect(execSpy).toHaveBeenCalledWith("run", [
925
- {
926
- dbindex: 2,
927
- subdomain,
928
- url: `http://${apiAddress}:${port}`,
929
- },
930
- "sumScript",
931
- { x: 1, y: 2 },
932
- ]);
933
- execSpy.mockReset();
934
-
935
- execSpy.mockRejectedValueOnce("fail");
936
- await expect(runCommand()).resolves.toStrictEqual([
937
- "fail",
938
- null,
939
- ]);
940
- expect(execSpy.mock.calls.length).toBe(1);
941
- expect(execSpy).toHaveBeenCalledWith("run", [
942
- {
943
- dbindex: 2,
944
- subdomain,
945
- url: `http://${apiAddress}:${port}`,
946
- },
947
- "sumScript",
948
- { x: 1, y: 2 },
949
- ]);
950
- execSpy.mockReset();
951
-
952
- // process beforeexit can not be reliably triggered or handled as expected within Jest's test environment, especially in worker thread scenario
953
- // so we trigger the function directly
954
- const terminateSpy = jest.spyOn(
955
- cliScriptWorkerPool,
956
- "terminate",
957
- );
958
- const workerpoolBeforeExit = process
959
- .listeners("beforeexit")
960
- .find(
961
- (listener) =>
962
- listener
963
- .toString()
964
- .toLowerCase()
965
- .indexOf("cliscriptworkerpool") !== -1,
966
- );
967
- expect(isAsyncFunction(workerpoolBeforeExit)).toBeTruthy();
968
- terminateSpy.mockReset();
969
- workerpoolBeforeExit();
970
- expect(terminateSpy.mock.calls.length).toBe(1);
971
- });
972
- });
973
- });
974
- });
975
-
976
- test("shall create socket server with correct options", () => {
977
- const sockHttpServer = createServer();
978
- const sockServer = createSocketServer(sockHttpServer, "1.2.3.4");
979
- try {
980
- const opts = sockServer.opts;
981
- expect(opts.cors.origin).toStrictEqual([
982
- "https://biz-a.id",
983
- "https://test.biz-a.id",
984
- /\.biz-a\.id$/,
985
- "vscode-file://vscode-app",
986
- /\.vscode-cdn\.net$/,
987
- "http://1.2.3.4:4200",
988
- "http://localhost:4200",
989
- ]);
990
- expect(opts.maxHttpBufferSize).toBe(1e8);
991
- expect(opts.pingInterval).toBe(35000);
992
- expect(opts.pingTimeout).toBe(30000);
993
- } finally {
994
- sockServer.close();
995
- sockHttpServer.close();
996
- }
997
- });
998
- });