biz-a-cli 2.3.70 → 2.3.72

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/app.test.js CHANGED
@@ -1,122 +1,140 @@
1
- import fs from "fs"
2
- import { jest } from '@jest/globals'
3
- import { Duplex } from 'node:stream'
4
- import { finished } from 'node:stream/promises'
5
- import { createDecipheriv } from 'node:crypto'
6
- import * as tar from "tar"
7
- import { env } from "../envs/env.js"
8
- import path from "node:path"
9
-
10
- describe('Biz-A Apps CLI', () => {
11
- let originalOptions, originalCWD, axios, child_process;
12
-
13
- const JEST_MESSAGE_TOTAL = 3;
14
- let returnedCode = 0;
15
- let jestMessage = '';
16
-
17
- const logSpy = jest.spyOn(global.console, 'log').mockImplementation()
18
- const errorSpy = jest.spyOn(global.console, 'error').mockImplementation()
19
-
20
- async function runCommand(...options) {
21
- process.argv[1] = process.cwd() + '\\bin' // mock the key root folder
22
- process.argv = [process.argv[0], process.argv[1], ...options]
23
- await import('../bin/app.js')
24
-
25
- // All below functions does not wait for ES6 import promises
26
- // await jest.runAllTimersAsync()
27
- // jest.runAllTimers()
28
- // jest.runAllTicks()
29
-
30
- await new Promise(process.nextTick) // wait for all import promise (including ES6 import promises) to be resolved
31
- }
32
-
33
- beforeEach(async () => {
34
- originalOptions = process.argv
35
- originalCWD = process.cwd()
36
-
37
- // mock default export first with unstable_mockModule
38
- jest.unstable_mockModule('axios', () => { return { default: jest.fn() } })
39
- axios = (await import('axios')).default
40
-
41
- // then we can mock classes, functions, variables, and objects inside mocked module when doing dynamic import using jest.fn()
42
- axios.get = jest.fn()
43
- axios.post = jest.fn()
44
-
45
- jest.unstable_mockModule('node:child_process', () => ({
46
- spawn: jest.fn().mockReturnValue({
47
- on: jest.fn((event, callback) => {
48
- if (event === "close") callback(returnedCode)
49
- }),
50
- stderr: {
51
- on: jest.fn((event, callback) => {
52
- if (event === "data") callback(jestMessage)
53
- })
54
- }
55
- })
56
- }))
57
- child_process = (await import('node:child_process'));
58
- })
59
-
60
- afterEach(() => {
61
- jest.resetModules()
62
- jest.resetAllMocks()
63
- process.argv = originalOptions
64
- process.chdir(originalCWD)
65
- })
66
-
67
- describe('Add App', () => {
68
- const mockDataFolder = './tests/mockData/'
69
-
70
- function mockValidTemplates(appName, scriptList) {
71
- fs.rmSync(mockDataFolder + '/' + appName, { force: true, recursive: true })
72
- fs.mkdirSync(mockDataFolder + '/' + appName, { recursive: true })
73
- for (const [key, value] of Object.entries(scriptList)) {
74
- fs.writeFileSync(mockDataFolder + '/' + appName + '/' + key, value.act)
75
- }
76
- }
77
-
78
- function mockIssuerKeyResponse() {
79
- axios.get.mockResolvedValue({
80
- data: {
81
- // this data containing 'mockAESKey', which is encrypted using CLI Private key
82
- data: 'eyJpc3N1ZXIiOnsia2V5IjoidEVmZlRwbGpsRW9seVRVcnpEQUs4b05mZEg0T2p2VE9MOHZ4c0p3MkRISC9LY09WV2t0SG5kWmxxUXJURUdoWGtoWUFtQjBobmw1RU5xdFh2TXNjcHFCMnd6eXZJUy9OdUpxeFdBQmE4Y21QbXYyUHBqRFV3eUd5LzJzbFBybG00aGJjbjJWamFPRVJiMHFpclZFNGhZVDluNlFwSnR1ZUp1NXcrVG9JN0V1eC9wdkZHeWVaOU1ham5hSTVtcVliZkNsT0pEZnV3MkpuU1RGNk1kbWg0MURualNuYk5nWkJhSjF2VWw2U1lmVDREeVJoVTkrUER6UTRMZ0xPZXlVYUlTVHQwWnpQWDIyeGI5Nlo3NlE0ZFNGUzhwRzRJRk0vMFpHbnI5eXB0NmNhRFlseW5VUWVFOHVJMklJMnpZcjFNdE12RnkzaDEyUU5Za0tLS3NaaVNRV00vQXRPTm5sa00rcjFKSFpMZW42b3lKYlV1cEgvRmZxQmViQWdRUThLcUVENnN2UlljSXBKOWh3UlJDb2RvNmlHaVVTMUVZZnh2N3pFMDhwL281Q2JmdFRKTXpISjh3T1JZbkdaTDh5cWttT2V0Y3dJL0p6U2dGVDNST2E5V3ZMRkFHWkIrcWdEOTBNeGhwT3kyVzRlbS9lUVBpeHNadDR2UGl2T1l0QVZYZTBLL0lJR0tlZ3VRZ0NzeUZENkRmajQxYkNXbm9oUEgzNWl1Y2h1cGxKVUhsSmszMTRqLytkbTBieEZ6MWFzdDBKanVaRFp3bkVSeVJVbTNFSWZNU2kxVGRRaXRWRjI2bFJ3a1NwK0VSRHh0ZllHWCs3V1U3OUhyUFF3MHBuU3NzU29FbVFzQ2VXU0dFc1AwbEtpTlBjLzBCeDNzbjdNMnJpMzJmZUZLRHM9In0sImFjcXVpcmVyIjp7ImRhdGEiOiJtT1NabzB0N0ZZb2M2a0tuZlNURk04S3VHcUtzLzU2MGtERGdldDMrRW9UTUZZaW5sbThwRFRqc2xVK2VoWnRidk1TejJ1bmNPck5rQ1phQy9LM0JrbmM5T3BZTDZ2SXVGNmY2alZrbjJDS25xbnRnUXBqUkY4T2dYK3NEKzhLbnBMVll2QmhMdkJydjI2RTBHTnk5YTNwbCt2MHNna3EycXJoeUJCeVc3VnpRL3h1Y2FCdDRrUjE0N2FpaTlSaThvUXEzRU1kZ3BPN2crYmxLMFk3eGt5ekQ2YU1JSDFSR1lSMTgwaU94akVqYVpiVE1uQkUwS1RJUnBEMnkwL25MTU5VTHZTUlBraVMyTUh5cmI1QWJCbWNQR1hzVUVvZFc0OHM0QVdmS2hZRldLbzV5anQwZlhCVCtUbEdaWmYrcDJINnRzSVNUYjZSKzlHWFk5c1EzNE5IdmFCRTZtNzlURUVQWTNkeUl3Vm5WQTIzR0FqL211bzI5M2dMTlp0R090ZWcrR1VlWXIzWXViQUZnL2VrN0NyTThGdGRQZC92R3NjdlJ4bkNEYnhmU3U3YXUzUFd1RFB5djk0ZjhXaXVMcWlKUGFQRmFZaUE4SW1lZklnY2ROKzMrTWZDRkdyR1dsWHlZM1pxVVI5NEhteGVjd1Q5Q256N2g2NHc0L3c0b0c5bU9CWmVsRk14dzlxUVlOWTZ5ZzRrakRBa2xUK29GMUszd213VUpPVTlhc3NCVEQ1SXZONlJRSEV0SU02ZmhKY1R0Zm5tK3l0Y201b3hjY3ZrN2tlVDExR2xjVi9Oa3EwNDRKb0NFL0drbzV2TWovQ29ERklJd0VjSENnQ1RvVGE0SForcWUzTWc2Wm1CazhvL2pwV3VFbTF5anJMcUdMSmFOTEJXNjlTUT0iLCJzaWduYXR1cmUiOiJzZDJXYnNDVy9oeXlMMnIzcmVhSFcyZndzVy85THVKNkp6MW0zUUNkWVA1NkZUU1ovSHM4c0Z6V2hmdVhtS1JJdEd5cnA4RGxMNTJkVkw1eTNnYmRIc0VoMDRITDhiWXIwS2lFU2RlbnVPMnoyZkdBWTlsclVjRC92alpZNlAwOHBVQTBEbXIreTk2UjQzdGVhTnZaMkplMklMWW1GZzdNOGF2cDZJZWk5WllzUmxyMFg2TmlzcUlXTWxDeHZOLzdadGNzcWQ3QndtWEhZRmlQWVkxaC9KTUtJdEREZnBneGgzU3RITlZQb01JU09FcTVkbGVRMjJNaTlPL2trZ2pkcmlBRDZsZ1hCZ214Q0FDaE5VQzVEZW5vUVIwQUlTREMvTW9udVA5ZldoMktwMkV2SlRGakR0eUJFdFZZekZZME82NEcxOVNhSFRnRmdxTFd2eXJDL2gzK1BqREIyTTdxOFBXOWVDNjlwM0ZCdUt1R0U3aTkrTmpKeWh4eFJoWWlxbW5MWjFxN3lFUzNudkIrNjJHRDZrUlR6YU5zdjFCUTRFSVpBR1NSaUF4d3o0aWo0ZldtdWJhaEd2WGlseGUzYzE1S09aa0RzcWRpQklIL1RWYzc1TUVBUVYrVVNlbXNEUnF2eUZ3ZFBobW9oM25KeVdmbFdsMU05eVNhdU1MaUFPV2c1bXhhSkdqQ0NtMGV5SHZaWUFFOFNWUjViWFJOZjIrNkpBQUJ2Mk5iSEJIVEJxM3V1cWpEVFZZdFZTRmQ3QmsvUS9NdVRiQnNjYUNqVUtybmtMcUpDTEw4akV0NnJlQnJLdlNPZll1c0txWHBWRmZMV1dHd1ZWSEViTzZVQ0xYTmIzY0c1MXRuNmpOS3NmN29wQXhBTFhUNFRXVjk0RjB6KzRpMjNYOD0ifX0=',
83
- // sign using BizA Server PrivateKey, to be verified by BizA CLI using Server PublicKey
84
- signature: 'Y9Vs9mOV5fqjcX1ZQh8N0vubV6J5+rx1Lvcrb8T9nBONMJIS74lqRdnO4a84rDCXdda0QOJs5750OdE69/ognriSqdvJ5qjb4sjYAuzXmbxGOuU4ptWMAl9CVfi7JdISdXaDMn5o2E9SuuQLwBMUvX6a9p93/L+TK/pSLuv3cwZqaCtiyhsZV1Q1mSz5xpEfGZR//0Vdaj7pZYZ7CDjQAiq6tMc1Mm7azDmxEwDmNqTMvPKJusGTTHvIFH9MbGTsHgq7DzhsGBVW0fyQGx4ycqB5u9D9j9YffjdlFZ3xncxgq/NVHJzHPbosrG2blW3yWr0Sg4P34AAOhNMtAbbrZfa5HYQrGLB5MbOY3s8EiXItufKzz5xTKDVdLER7kWk6th+a7c548TfqWkrkepPEvgBgmGoMw/PvXFsMVMKqRoF31lN7tumKnVyVzz/EwrsVVc7QrsJxfODHgU0bNNxi6gEb+prqKy0ZWmWwtf7coLWq/ybC2yFMrqUjymoJBGvNoLBURihD2m645+qudNlcJlLoyTs8B3KRCwFKtBH2X9eW5ZzDpIQAXDge6Bz/668Xg6DB8TzvpNr0sPOkuXXSKhQnb0SkR7QTHzpFDcg88Ur9Nnnh9aSZZRj1nRo5LawNto+9684SzpIfbjzAnbTHLQ6oY28SIdihKVjc0wj9BZk='
85
- }
86
- })
87
- axios.post.mockResolvedValue({ data: { success: true }, status: 200 })
88
- }
89
-
90
- beforeAll(() => {
91
- fs.rmSync(mockDataFolder, { recursive: true, force: true })
92
- })
93
-
94
- afterAll(() => {
95
- fs.rmSync(mockDataFolder, { recursive: true, force: true })
96
- })
97
-
98
- const stressTestCount = 10;
99
- it.each(Array(stressTestCount).fill().map((v, i) => i + 1))(`shall compress and encrypt app scripts (stress test %p of ${stressTestCount})`, async (testNo) => {
100
- const mockScripts = {
101
- 'a.js': {
102
- act: 'get = function () {return {modelA: {}}}',
103
- exp: '{"modelA":{}}'
104
- },
105
- 'b.js': {
106
- act: 'const get = function () {\nreturn {"modelB": {id: null}}};',
107
- exp: '{"modelB":{"id":null}}'
108
- },
109
- 'c.js': {
110
- act: 'let get = function () {return {modelC: {}, function: {func: () => { \nreturn ["lib"];\n}\n}}};',
111
- exp: '{"modelC":{},"function":{"func":["window.Function","","return[\\\"lib\\\"]"]}}'
112
- },
113
- 'd.js': {
114
- act: 'var get = function () {return {model: {}, tableName: "", fields: [], function: {}}};\nmodule.exports = get;',
115
- exp: '{"model":{},"tableName":"","fields":[],"function":{}}'
116
- },
117
- 'menu.json': {
118
- // act: JSON.stringify([
119
- act: `[
1
+ import fs from "fs";
2
+ import { jest } from "@jest/globals";
3
+ import { Duplex } from "node:stream";
4
+ import { finished } from "node:stream/promises";
5
+ import { createDecipheriv } from "node:crypto";
6
+ import * as tar from "tar";
7
+ import { env } from "../envs/env.js";
8
+ import path from "node:path";
9
+
10
+ describe("Biz-A Apps CLI", () => {
11
+ let originalOptions, originalCWD, axios, child_process;
12
+
13
+ const JEST_MESSAGE_TOTAL = 3;
14
+ let returnedCode = 0;
15
+ let jestMessage = "";
16
+
17
+ const logSpy = jest.spyOn(global.console, "log").mockImplementation();
18
+ const errorSpy = jest.spyOn(global.console, "error").mockImplementation();
19
+
20
+ async function runCommand(...options) {
21
+ process.argv[1] = process.cwd() + "\\bin"; // mock the key root folder
22
+ process.argv = [process.argv[0], process.argv[1], ...options];
23
+ await import("../bin/app.js");
24
+
25
+ // All below functions does not wait for ES6 import promises
26
+ // await jest.runAllTimersAsync()
27
+ // jest.runAllTimers()
28
+ // jest.runAllTicks()
29
+
30
+ await new Promise(process.nextTick); // wait for all import promise (including ES6 import promises) to be resolved
31
+ }
32
+
33
+ beforeEach(async () => {
34
+ originalOptions = process.argv;
35
+ originalCWD = process.cwd();
36
+
37
+ // mock default export first with unstable_mockModule
38
+ jest.unstable_mockModule("axios", () => {
39
+ return { default: jest.fn() };
40
+ });
41
+ axios = (await import("axios")).default;
42
+
43
+ // then we can mock classes, functions, variables, and objects inside mocked module when doing dynamic import using jest.fn()
44
+ axios.get = jest.fn();
45
+ axios.post = jest.fn();
46
+
47
+ jest.unstable_mockModule("node:child_process", () => ({
48
+ spawn: jest.fn().mockReturnValue({
49
+ on: jest.fn((event, callback) => {
50
+ if (event === "close") callback(returnedCode);
51
+ }),
52
+ stderr: {
53
+ on: jest.fn((event, callback) => {
54
+ if (event === "data") callback(jestMessage);
55
+ }),
56
+ },
57
+ }),
58
+ }));
59
+ child_process = await import("node:child_process");
60
+ });
61
+
62
+ afterEach(() => {
63
+ jest.resetModules();
64
+ jest.resetAllMocks();
65
+ process.argv = originalOptions;
66
+ process.chdir(originalCWD);
67
+ });
68
+
69
+ describe("Add App", () => {
70
+ const mockDataFolder = "./tests/mockData/";
71
+
72
+ function mockValidTemplates(appName, scriptList) {
73
+ fs.rmSync(mockDataFolder + "/" + appName, {
74
+ force: true,
75
+ recursive: true,
76
+ });
77
+ fs.mkdirSync(mockDataFolder + "/" + appName, { recursive: true });
78
+ for (const [key, value] of Object.entries(scriptList)) {
79
+ fs.writeFileSync(
80
+ mockDataFolder + "/" + appName + "/" + key,
81
+ value.act,
82
+ );
83
+ }
84
+ }
85
+
86
+ function mockIssuerKeyResponse() {
87
+ axios.get.mockResolvedValue({
88
+ data: {
89
+ // this data containing 'mockAESKey', which is encrypted using CLI Private key
90
+ data: "eyJpc3N1ZXIiOnsia2V5IjoidEVmZlRwbGpsRW9seVRVcnpEQUs4b05mZEg0T2p2VE9MOHZ4c0p3MkRISC9LY09WV2t0SG5kWmxxUXJURUdoWGtoWUFtQjBobmw1RU5xdFh2TXNjcHFCMnd6eXZJUy9OdUpxeFdBQmE4Y21QbXYyUHBqRFV3eUd5LzJzbFBybG00aGJjbjJWamFPRVJiMHFpclZFNGhZVDluNlFwSnR1ZUp1NXcrVG9JN0V1eC9wdkZHeWVaOU1ham5hSTVtcVliZkNsT0pEZnV3MkpuU1RGNk1kbWg0MURualNuYk5nWkJhSjF2VWw2U1lmVDREeVJoVTkrUER6UTRMZ0xPZXlVYUlTVHQwWnpQWDIyeGI5Nlo3NlE0ZFNGUzhwRzRJRk0vMFpHbnI5eXB0NmNhRFlseW5VUWVFOHVJMklJMnpZcjFNdE12RnkzaDEyUU5Za0tLS3NaaVNRV00vQXRPTm5sa00rcjFKSFpMZW42b3lKYlV1cEgvRmZxQmViQWdRUThLcUVENnN2UlljSXBKOWh3UlJDb2RvNmlHaVVTMUVZZnh2N3pFMDhwL281Q2JmdFRKTXpISjh3T1JZbkdaTDh5cWttT2V0Y3dJL0p6U2dGVDNST2E5V3ZMRkFHWkIrcWdEOTBNeGhwT3kyVzRlbS9lUVBpeHNadDR2UGl2T1l0QVZYZTBLL0lJR0tlZ3VRZ0NzeUZENkRmajQxYkNXbm9oUEgzNWl1Y2h1cGxKVUhsSmszMTRqLytkbTBieEZ6MWFzdDBKanVaRFp3bkVSeVJVbTNFSWZNU2kxVGRRaXRWRjI2bFJ3a1NwK0VSRHh0ZllHWCs3V1U3OUhyUFF3MHBuU3NzU29FbVFzQ2VXU0dFc1AwbEtpTlBjLzBCeDNzbjdNMnJpMzJmZUZLRHM9In0sImFjcXVpcmVyIjp7ImRhdGEiOiJtT1NabzB0N0ZZb2M2a0tuZlNURk04S3VHcUtzLzU2MGtERGdldDMrRW9UTUZZaW5sbThwRFRqc2xVK2VoWnRidk1TejJ1bmNPck5rQ1phQy9LM0JrbmM5T3BZTDZ2SXVGNmY2alZrbjJDS25xbnRnUXBqUkY4T2dYK3NEKzhLbnBMVll2QmhMdkJydjI2RTBHTnk5YTNwbCt2MHNna3EycXJoeUJCeVc3VnpRL3h1Y2FCdDRrUjE0N2FpaTlSaThvUXEzRU1kZ3BPN2crYmxLMFk3eGt5ekQ2YU1JSDFSR1lSMTgwaU94akVqYVpiVE1uQkUwS1RJUnBEMnkwL25MTU5VTHZTUlBraVMyTUh5cmI1QWJCbWNQR1hzVUVvZFc0OHM0QVdmS2hZRldLbzV5anQwZlhCVCtUbEdaWmYrcDJINnRzSVNUYjZSKzlHWFk5c1EzNE5IdmFCRTZtNzlURUVQWTNkeUl3Vm5WQTIzR0FqL211bzI5M2dMTlp0R090ZWcrR1VlWXIzWXViQUZnL2VrN0NyTThGdGRQZC92R3NjdlJ4bkNEYnhmU3U3YXUzUFd1RFB5djk0ZjhXaXVMcWlKUGFQRmFZaUE4SW1lZklnY2ROKzMrTWZDRkdyR1dsWHlZM1pxVVI5NEhteGVjd1Q5Q256N2g2NHc0L3c0b0c5bU9CWmVsRk14dzlxUVlOWTZ5ZzRrakRBa2xUK29GMUszd213VUpPVTlhc3NCVEQ1SXZONlJRSEV0SU02ZmhKY1R0Zm5tK3l0Y201b3hjY3ZrN2tlVDExR2xjVi9Oa3EwNDRKb0NFL0drbzV2TWovQ29ERklJd0VjSENnQ1RvVGE0SForcWUzTWc2Wm1CazhvL2pwV3VFbTF5anJMcUdMSmFOTEJXNjlTUT0iLCJzaWduYXR1cmUiOiJzZDJXYnNDVy9oeXlMMnIzcmVhSFcyZndzVy85THVKNkp6MW0zUUNkWVA1NkZUU1ovSHM4c0Z6V2hmdVhtS1JJdEd5cnA4RGxMNTJkVkw1eTNnYmRIc0VoMDRITDhiWXIwS2lFU2RlbnVPMnoyZkdBWTlsclVjRC92alpZNlAwOHBVQTBEbXIreTk2UjQzdGVhTnZaMkplMklMWW1GZzdNOGF2cDZJZWk5WllzUmxyMFg2TmlzcUlXTWxDeHZOLzdadGNzcWQ3QndtWEhZRmlQWVkxaC9KTUtJdEREZnBneGgzU3RITlZQb01JU09FcTVkbGVRMjJNaTlPL2trZ2pkcmlBRDZsZ1hCZ214Q0FDaE5VQzVEZW5vUVIwQUlTREMvTW9udVA5ZldoMktwMkV2SlRGakR0eUJFdFZZekZZME82NEcxOVNhSFRnRmdxTFd2eXJDL2gzK1BqREIyTTdxOFBXOWVDNjlwM0ZCdUt1R0U3aTkrTmpKeWh4eFJoWWlxbW5MWjFxN3lFUzNudkIrNjJHRDZrUlR6YU5zdjFCUTRFSVpBR1NSaUF4d3o0aWo0ZldtdWJhaEd2WGlseGUzYzE1S09aa0RzcWRpQklIL1RWYzc1TUVBUVYrVVNlbXNEUnF2eUZ3ZFBobW9oM25KeVdmbFdsMU05eVNhdU1MaUFPV2c1bXhhSkdqQ0NtMGV5SHZaWUFFOFNWUjViWFJOZjIrNkpBQUJ2Mk5iSEJIVEJxM3V1cWpEVFZZdFZTRmQ3QmsvUS9NdVRiQnNjYUNqVUtybmtMcUpDTEw4akV0NnJlQnJLdlNPZll1c0txWHBWRmZMV1dHd1ZWSEViTzZVQ0xYTmIzY0c1MXRuNmpOS3NmN29wQXhBTFhUNFRXVjk0RjB6KzRpMjNYOD0ifX0=",
91
+ // sign using BizA Server PrivateKey, to be verified by BizA CLI using Server PublicKey
92
+ signature:
93
+ "Y9Vs9mOV5fqjcX1ZQh8N0vubV6J5+rx1Lvcrb8T9nBONMJIS74lqRdnO4a84rDCXdda0QOJs5750OdE69/ognriSqdvJ5qjb4sjYAuzXmbxGOuU4ptWMAl9CVfi7JdISdXaDMn5o2E9SuuQLwBMUvX6a9p93/L+TK/pSLuv3cwZqaCtiyhsZV1Q1mSz5xpEfGZR//0Vdaj7pZYZ7CDjQAiq6tMc1Mm7azDmxEwDmNqTMvPKJusGTTHvIFH9MbGTsHgq7DzhsGBVW0fyQGx4ycqB5u9D9j9YffjdlFZ3xncxgq/NVHJzHPbosrG2blW3yWr0Sg4P34AAOhNMtAbbrZfa5HYQrGLB5MbOY3s8EiXItufKzz5xTKDVdLER7kWk6th+a7c548TfqWkrkepPEvgBgmGoMw/PvXFsMVMKqRoF31lN7tumKnVyVzz/EwrsVVc7QrsJxfODHgU0bNNxi6gEb+prqKy0ZWmWwtf7coLWq/ybC2yFMrqUjymoJBGvNoLBURihD2m645+qudNlcJlLoyTs8B3KRCwFKtBH2X9eW5ZzDpIQAXDge6Bz/668Xg6DB8TzvpNr0sPOkuXXSKhQnb0SkR7QTHzpFDcg88Ur9Nnnh9aSZZRj1nRo5LawNto+9684SzpIfbjzAnbTHLQ6oY28SIdihKVjc0wj9BZk=",
94
+ },
95
+ });
96
+ axios.post.mockResolvedValue({
97
+ data: { success: true },
98
+ status: 200,
99
+ });
100
+ }
101
+
102
+ beforeAll(() => {
103
+ fs.rmSync(mockDataFolder, { recursive: true, force: true });
104
+ });
105
+
106
+ afterAll(() => {
107
+ fs.rmSync(mockDataFolder, { recursive: true, force: true });
108
+ });
109
+
110
+ const stressTestCount = 10;
111
+ it.each(
112
+ Array(stressTestCount)
113
+ .fill()
114
+ .map((v, i) => i + 1),
115
+ )(
116
+ `shall compress and encrypt app scripts (stress test %p of ${stressTestCount})`,
117
+ async (testNo) => {
118
+ const mockScripts = {
119
+ "a.js": {
120
+ act: "get = function () {return {modelA: {}}}",
121
+ exp: '{"modelA":{}}',
122
+ },
123
+ "b.js": {
124
+ act: 'const get = function () {\nreturn {"modelB": {id: null}}};',
125
+ exp: '{"modelB":{"id":null}}',
126
+ },
127
+ "c.js": {
128
+ act: 'let get = function () {return {modelC: {}, function: {func: () => { \nreturn ["lib"];\n}\n}}};',
129
+ exp: '{"modelC":{},"function":{"func":["window.Function","","return[\\\"lib\\\"]"]}}',
130
+ },
131
+ "d.js": {
132
+ act: 'var get = function () {return {model: {}, tableName: "", fields: [], function: {}}};\nmodule.exports = get;',
133
+ exp: '{"model":{},"tableName":"","fields":[],"function":{}}',
134
+ },
135
+ "menu.json": {
136
+ // act: JSON.stringify([
137
+ act: `[
120
138
  {"menuName": "", "caption": "Home", "link": ["./main"],"subMenu": []},
121
139
  {"menuName": "", "caption": "Personalia","link": [], "subMenu": [
122
140
  {"menuName": "","caption": "Data Master","link": [], "subMenu": []},
@@ -165,97 +183,362 @@ describe('Biz-A Apps CLI', () => {
165
183
  }
166
184
  ]}
167
185
  ]`,
168
- // ]),
169
- // not saved as templates file, but as metadata of curret App
170
- exp: "[{\"menuName\":\"\",\"caption\":\"Home\",\"link\":[\"./main\"],\"subMenu\":[]},{\"menuName\":\"\",\"caption\":\"Personalia\",\"link\":[],\"subMenu\":[{\"menuName\":\"\",\"caption\":\"Data Master\",\"link\":[],\"subMenu\":[]},{\"menuName\":\"\",\"caption\":\"Gaji\",\"link\":[],\"subMenu\":[{\"menuName\":\"\",\"caption\":\"Parameter\",\"link\":[\"./form\",\"gajiparam\",null],\"subMenu\":[]},{\"menuName\":\"\",\"caption\":\"Perubahan\",\"link\":[],\"subMenu\":[{\"menuName\":\"\",\"caption\":\"Form Perubahan Gaji\",\"link\":[\"./form\",\"gajiubah\",null],\"subMenu\":[]},{\"menuName\":\"\",\"caption\":\"Daftar Perubahan Gaji\",\"link\":[\"./list\",\"gajiubahdaftar\"],\"subMenu\":[]}]}]}]}]"
171
- }
172
- }
173
-
174
- const appName = 'compressStressTest' + testNo
175
- mockValidTemplates(appName, mockScripts)
176
- mockIssuerKeyResponse()
177
- const mockAESKey = Buffer.from('hAnHadaJXJaq/9fCFMNmjkrB61CBPXJid6vbtXgG8Ug=', 'base64')
178
-
179
- await runCommand('add', '-s', 'https://a.b.c', '-p', '1205', '-i', '2', '-d', mockDataFolder + '/' + appName)
180
-
181
- expect(axios.get.mock.calls).toHaveLength(1)
182
- const getArgs = axios.get.mock.calls[0]
183
- expect(getArgs).toHaveLength(2)
184
- expect(getArgs[0]).toBe(`${env.BIZA_SERVER_LINK}/api/issuerKey`)
185
- expect(getArgs[1]).toHaveProperty('params')
186
- const params = getArgs[1].params
187
- expect(params).toHaveProperty('data')
188
- expect(params.data).toBe('eyJpc3N1ZXIiOiJDTEkiLCJhY3F1aXJlciI6IkNsaWVudCJ9') // BizA CLI Private key
189
- expect(params).toHaveProperty('signature')
190
- expect(params.signature).not.toBeNull() // PSS signature is not deterministic
191
-
192
- expect(axios.post.mock.calls).toHaveLength(1)
193
- const postArgs = axios.post.mock.calls[0]
194
- expect(postArgs).toHaveLength(3)
195
- expect(postArgs[0]).toBe('https://a.b.c:1205/fina/rest/TOrmMethod/%22setApp%22')
196
-
197
- const apiParams = postArgs[1]._parameters
198
- expect(apiParams[0]).toBe(2)
199
- expect(apiParams[1]).toBe(appName.toLowerCase())
200
-
201
- // encrypted template scripts is not deterministic, because IV (initialize vector) using 16 random bytes
202
- const templateBuffer = Buffer.from(apiParams[2], 'base64')
203
- const decryptScripts = {}
204
- const stream = new Duplex()
205
- stream.push(templateBuffer)
206
- stream.push(null)
207
- stream.pipe(
208
- // tar.x -> will extract and write to file
209
- // tar.t -> only extract
210
- tar.t({
211
- strict: true
212
- , sync: true
213
- , onReadEntry: entry => {
214
- const chunks = [];
215
- entry.on('data', chunk => chunks.push(chunk));
216
- entry.on('end', () => {
217
- expect(entry.path.trim().toLowerCase()).not.toBe('menu.json') // shall save "menu.json" as App.metadata
218
- const cipherText = Buffer.from(Buffer.concat(chunks).toString(), 'base64')
219
- const decryptInitializeVector = cipherText.subarray(0, 16)
220
- const decryptData = cipherText.subarray(16)
221
- const decipher = createDecipheriv('aes-256-cbc', mockAESKey, decryptInitializeVector)
222
- const decrypted = Buffer.concat([decipher.update(decryptData), decipher.final()])
223
- decryptScripts[entry.path] = decrypted.toString()
224
- })
225
- }
226
- })
227
- .on('finish', () => stream.end())
228
- )
229
- await finished(stream) // wait for untar process to finish
230
- for (const [fileName, script] of Object.entries(decryptScripts)) {
231
- expect(script).toBe(mockScripts[fileName].exp)
232
- }
233
-
234
- // app.metadata : {
235
- // "acquirer": { // generate by BizA Server
236
- // "data": "", //containing AES Key for template script encryption or deccryption, which is encrypted using BizA Client publicKey
237
- // "signature": "", // sign using BizA CLI Private key, to be verified by BizA Client using CLI Public Key
238
- // },
239
- // "menu" : "" // contain encrypted "menu.json"
240
- // }
241
- expect(apiParams[3]).toHaveProperty('acquirer')
242
- expect(apiParams[3].acquirer).toHaveProperty('data')
243
- expect(apiParams[3].acquirer.data).toBe('eyJkYXRhIjoibU9TWm8wdDdGWW9jNmtLbmZTVEZNOEt1R3FLcy81NjBrRERnZXQzK0VvVE1GWWlubG04cERUanNsVStlaFp0YnZNU3oydW5jT3JOa0NaYUMvSzNCa25jOU9wWUw2dkl1RjZmNmpWa24yQ0tucW50Z1FwalJGOE9nWCtzRCs4S25wTFZZdkJoTHZCcnYyNkUwR055OWEzcGwrdjBzZ2txMnFyaHlCQnlXN1Z6US94dWNhQnQ0a1IxNDdhaWk5Umk4b1FxM0VNZGdwTzdnK2JsSzBZN3hreXpENmFNSUgxUkdZUjE4MGlPeGpFamFaYlRNbkJFMEtUSVJwRDJ5MC9uTE1OVUx2U1JQa2lTMk1IeXJiNUFiQm1jUEdYc1VFb2RXNDhzNEFXZktoWUZXS281eWp0MGZYQlQrVGxHWlpmK3AySDZ0c0lTVGI2Uis5R1hZOXNRMzROSHZhQkU2bTc5VEVFUFkzZHlJd1ZuVkEyM0dBai9tdW8yOTNnTE5adEdPdGVnK0dVZVlyM1l1YkFGZy9lazdDck04RnRkUGQvdkdzY3ZSeG5DRGJ4ZlN1N2F1M1BXdURQeXY5NGY4V2l1THFpSlBhUEZhWWlBOEltZWZJZ2NkTiszK01mQ0ZHckdXbFh5WTNacVVSOTRIbXhlY3dUOUNuejdoNjR3NC93NG9HOW1PQlplbEZNeHc5cVFZTlk2eWc0a2pEQWtsVCtvRjFLM3dtd1VKT1U5YXNzQlRENUl2TjZSUUhFdElNNmZoSmNUdGZubSt5dGNtNW94Y2N2azdrZVQxMUdsY1YvTmtxMDQ0Sm9DRS9Ha281dk1qL0NvREZJSXdFY0hDZ0NUb1RhNEhaK3FlM01nNlptQms4by9qcFd1RW0xeWpyTHFHTEphTkxCVzY5U1E9Iiwic2lnbmF0dXJlIjoic2QyV2JzQ1cvaHl5TDJyM3JlYUhXMmZ3c1cvOUx1SjZKejFtM1FDZFlQNTZGVFNaL0hzOHNGeldoZnVYbUtSSXRHeXJwOERsTDUyZFZMNXkzZ2JkSHNFaDA0SEw4YllyMEtpRVNkZW51TzJ6MmZHQVk5bHJVY0QvdmpaWTZQMDhwVUEwRG1yK3k5NlI0M3RlYU52WjJKZTJJTFltRmc3TThhdnA2SWVpOVpZc1JscjBYNk5pc3FJV01sQ3h2Ti83WnRjc3FkN0J3bVhIWUZpUFlZMWgvSk1LSXRERGZwZ3hoM1N0SE5WUG9NSVNPRXE1ZGxlUTIyTWk5Ty9ra2dqZHJpQUQ2bGdYQmdteENBQ2hOVUM1RGVub1FSMEFJU0RDL01vbnVQOWZXaDJLcDJFdkpURmpEdHlCRXRWWXpGWTBPNjRHMTlTYUhUZ0ZncUxXdnlyQy9oMytQakRCMk03cThQVzllQzY5cDNGQnVLdUdFN2k5K05qSnloeHhSaFlpcW1uTFoxcTd5RVMzbnZCKzYyR0Q2a1JUemFOc3YxQlE0RUlaQUdTUmlBeHd6NGlqNGZXbXViYWhHdlhpbHhlM2MxNUtPWmtEc3FkaUJJSC9UVmM3NU1FQVFWK1VTZW1zRFJxdnlGd2RQaG1vaDNuSnlXZmxXbDFNOXlTYXVNTGlBT1dnNW14YUpHakNDbTBleUh2WllBRThTVlI1YlhSTmYyKzZKQUFCdjJOYkhCSFRCcTN1dXFqRFRWWXRWU0ZkN0JrL1EvTXVUYkJzY2FDalVLcm5rTHFKQ0xMOGpFdDZyZUJyS3ZTT2ZZdXNLcVhwVkZmTFdXR3dWVkhFYk82VUNMWE5iM2NHNTF0bjZqTktzZjdvcEF4QUxYVDRUV1Y5NEYweis0aTIzWDg9In0=')
244
- expect(apiParams[3].acquirer).toHaveProperty('signature')
245
- expect(apiParams[3].acquirer.signature).not.toBeNull() // PSS signature is not deterministic
246
-
247
- expect(apiParams[3]).toHaveProperty('menu') // shall save "menu.json" as App.metadata
248
- expect(apiParams[3].menu).not.toBeNull() // IV (initialize vector) using 16 random bytes
249
-
250
- expect(postArgs[2]).toStrictEqual({ "headers": { "Content-Type": "text/plain" } })
251
-
252
- expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 0][0]).toContain(`Finished packing ${Object.keys(mockScripts).length} files into "./upload/${appName.toLowerCase()}.tgz"`)
253
- expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 1][0]).toContain(`Finished uploading "./upload/${appName.toLocaleLowerCase()}.tgz"`)
254
-
255
- // fs.rmSync(mockDataFolder + '/' + appName, {recursive: true, force: true})
256
- })
257
-
258
- /* it('Invalid script syntax', async ()=>{
186
+ // ]),
187
+ // not saved as templates file, but as metadata of curret App
188
+ exp: '[{"menuName":"","caption":"Home","link":["./main"],"subMenu":[]},{"menuName":"","caption":"Personalia","link":[],"subMenu":[{"menuName":"","caption":"Data Master","link":[],"subMenu":[]},{"menuName":"","caption":"Gaji","link":[],"subMenu":[{"menuName":"","caption":"Parameter","link":["./form","gajiparam",null],"subMenu":[]},{"menuName":"","caption":"Perubahan","link":[],"subMenu":[{"menuName":"","caption":"Form Perubahan Gaji","link":["./form","gajiubah",null],"subMenu":[]},{"menuName":"","caption":"Daftar Perubahan Gaji","link":["./list","gajiubahdaftar"],"subMenu":[]}]}]}]}]',
189
+ },
190
+ };
191
+
192
+ const appName = "compressStressTest" + testNo;
193
+ mockValidTemplates(appName, mockScripts);
194
+ mockIssuerKeyResponse();
195
+ const mockAESKey = Buffer.from(
196
+ "hAnHadaJXJaq/9fCFMNmjkrB61CBPXJid6vbtXgG8Ug=",
197
+ "base64",
198
+ );
199
+
200
+ await runCommand(
201
+ "add",
202
+ "-s",
203
+ "https://a.b.c",
204
+ "-p",
205
+ "1205",
206
+ "-i",
207
+ "2",
208
+ "-d",
209
+ mockDataFolder + "/" + appName,
210
+ );
211
+
212
+ expect(axios.get.mock.calls).toHaveLength(1);
213
+ const getArgs = axios.get.mock.calls[0];
214
+ expect(getArgs).toHaveLength(2);
215
+ expect(getArgs[0]).toBe(
216
+ `${env.BIZA_SERVER_LINK}/api/issuerKey`,
217
+ );
218
+ expect(getArgs[1]).toHaveProperty("params");
219
+ const params = getArgs[1].params;
220
+ expect(params).toHaveProperty("data");
221
+ expect(params.data).toBe(
222
+ "eyJpc3N1ZXIiOiJDTEkiLCJhY3F1aXJlciI6IkNsaWVudCJ9",
223
+ ); // BizA CLI Private key
224
+ expect(params).toHaveProperty("signature");
225
+ expect(params.signature).not.toBeNull(); // PSS signature is not deterministic
226
+
227
+ expect(axios.post.mock.calls).toHaveLength(1);
228
+ const postArgs = axios.post.mock.calls[0];
229
+ expect(postArgs).toHaveLength(3);
230
+ expect(postArgs[0]).toBe(
231
+ "https://a.b.c:1205/fina/rest/TOrmMethod/%22setApp%22",
232
+ );
233
+
234
+ const apiParams = postArgs[1]._parameters;
235
+ expect(apiParams[0]).toBe(2);
236
+ expect(apiParams[1]).toBe(appName.toLowerCase());
237
+
238
+ // encrypted template scripts is not deterministic, because IV (initialize vector) using 16 random bytes
239
+ const templateBuffer = Buffer.from(apiParams[2], "base64");
240
+ const decryptScripts = {};
241
+ const stream = new Duplex();
242
+ stream.push(templateBuffer);
243
+ stream.push(null);
244
+ stream.pipe(
245
+ // tar.x -> will extract and write to file
246
+ // tar.t -> only extract
247
+ tar
248
+ .t({
249
+ strict: true,
250
+ sync: true,
251
+ onReadEntry: (entry) => {
252
+ const chunks = [];
253
+ entry.on("data", (chunk) => chunks.push(chunk));
254
+ entry.on("end", () => {
255
+ expect(
256
+ entry.path.trim().toLowerCase(),
257
+ ).not.toBe("menu.json"); // shall save "menu.json" as App.metadata
258
+ const cipherText = Buffer.from(
259
+ Buffer.concat(chunks).toString(),
260
+ "base64",
261
+ );
262
+ const decryptInitializeVector =
263
+ cipherText.subarray(0, 16);
264
+ const decryptData = cipherText.subarray(16);
265
+ const decipher = createDecipheriv(
266
+ "aes-256-cbc",
267
+ mockAESKey,
268
+ decryptInitializeVector,
269
+ );
270
+ const decrypted = Buffer.concat([
271
+ decipher.update(decryptData),
272
+ decipher.final(),
273
+ ]);
274
+ decryptScripts[entry.path] =
275
+ decrypted.toString();
276
+ });
277
+ },
278
+ })
279
+ .on("finish", () => stream.end()),
280
+ );
281
+ await finished(stream); // wait for untar process to finish
282
+ for (const [fileName, script] of Object.entries(
283
+ decryptScripts,
284
+ )) {
285
+ expect(script).toBe(mockScripts[fileName].exp);
286
+ }
287
+
288
+ // app.metadata : {
289
+ // "acquirer": { // generate by BizA Server
290
+ // "data": "", //containing AES Key for template script encryption or deccryption, which is encrypted using BizA Client publicKey
291
+ // "signature": "", // sign using BizA CLI Private key, to be verified by BizA Client using CLI Public Key
292
+ // },
293
+ // "menu" : "" // contain encrypted "menu.json"
294
+ // }
295
+ expect(apiParams[3]).toHaveProperty("acquirer");
296
+ expect(apiParams[3].acquirer).toHaveProperty("data");
297
+ expect(apiParams[3].acquirer.data).toBe(
298
+ "eyJkYXRhIjoibU9TWm8wdDdGWW9jNmtLbmZTVEZNOEt1R3FLcy81NjBrRERnZXQzK0VvVE1GWWlubG04cERUanNsVStlaFp0YnZNU3oydW5jT3JOa0NaYUMvSzNCa25jOU9wWUw2dkl1RjZmNmpWa24yQ0tucW50Z1FwalJGOE9nWCtzRCs4S25wTFZZdkJoTHZCcnYyNkUwR055OWEzcGwrdjBzZ2txMnFyaHlCQnlXN1Z6US94dWNhQnQ0a1IxNDdhaWk5Umk4b1FxM0VNZGdwTzdnK2JsSzBZN3hreXpENmFNSUgxUkdZUjE4MGlPeGpFamFaYlRNbkJFMEtUSVJwRDJ5MC9uTE1OVUx2U1JQa2lTMk1IeXJiNUFiQm1jUEdYc1VFb2RXNDhzNEFXZktoWUZXS281eWp0MGZYQlQrVGxHWlpmK3AySDZ0c0lTVGI2Uis5R1hZOXNRMzROSHZhQkU2bTc5VEVFUFkzZHlJd1ZuVkEyM0dBai9tdW8yOTNnTE5adEdPdGVnK0dVZVlyM1l1YkFGZy9lazdDck04RnRkUGQvdkdzY3ZSeG5DRGJ4ZlN1N2F1M1BXdURQeXY5NGY4V2l1THFpSlBhUEZhWWlBOEltZWZJZ2NkTiszK01mQ0ZHckdXbFh5WTNacVVSOTRIbXhlY3dUOUNuejdoNjR3NC93NG9HOW1PQlplbEZNeHc5cVFZTlk2eWc0a2pEQWtsVCtvRjFLM3dtd1VKT1U5YXNzQlRENUl2TjZSUUhFdElNNmZoSmNUdGZubSt5dGNtNW94Y2N2azdrZVQxMUdsY1YvTmtxMDQ0Sm9DRS9Ha281dk1qL0NvREZJSXdFY0hDZ0NUb1RhNEhaK3FlM01nNlptQms4by9qcFd1RW0xeWpyTHFHTEphTkxCVzY5U1E9Iiwic2lnbmF0dXJlIjoic2QyV2JzQ1cvaHl5TDJyM3JlYUhXMmZ3c1cvOUx1SjZKejFtM1FDZFlQNTZGVFNaL0hzOHNGeldoZnVYbUtSSXRHeXJwOERsTDUyZFZMNXkzZ2JkSHNFaDA0SEw4YllyMEtpRVNkZW51TzJ6MmZHQVk5bHJVY0QvdmpaWTZQMDhwVUEwRG1yK3k5NlI0M3RlYU52WjJKZTJJTFltRmc3TThhdnA2SWVpOVpZc1JscjBYNk5pc3FJV01sQ3h2Ti83WnRjc3FkN0J3bVhIWUZpUFlZMWgvSk1LSXRERGZwZ3hoM1N0SE5WUG9NSVNPRXE1ZGxlUTIyTWk5Ty9ra2dqZHJpQUQ2bGdYQmdteENBQ2hOVUM1RGVub1FSMEFJU0RDL01vbnVQOWZXaDJLcDJFdkpURmpEdHlCRXRWWXpGWTBPNjRHMTlTYUhUZ0ZncUxXdnlyQy9oMytQakRCMk03cThQVzllQzY5cDNGQnVLdUdFN2k5K05qSnloeHhSaFlpcW1uTFoxcTd5RVMzbnZCKzYyR0Q2a1JUemFOc3YxQlE0RUlaQUdTUmlBeHd6NGlqNGZXbXViYWhHdlhpbHhlM2MxNUtPWmtEc3FkaUJJSC9UVmM3NU1FQVFWK1VTZW1zRFJxdnlGd2RQaG1vaDNuSnlXZmxXbDFNOXlTYXVNTGlBT1dnNW14YUpHakNDbTBleUh2WllBRThTVlI1YlhSTmYyKzZKQUFCdjJOYkhCSFRCcTN1dXFqRFRWWXRWU0ZkN0JrL1EvTXVUYkJzY2FDalVLcm5rTHFKQ0xMOGpFdDZyZUJyS3ZTT2ZZdXNLcVhwVkZmTFdXR3dWVkhFYk82VUNMWE5iM2NHNTF0bjZqTktzZjdvcEF4QUxYVDRUV1Y5NEYweis0aTIzWDg9In0=",
299
+ );
300
+ expect(apiParams[3].acquirer).toHaveProperty("signature");
301
+ expect(apiParams[3].acquirer.signature).not.toBeNull(); // PSS signature is not deterministic
302
+
303
+ expect(apiParams[3]).toHaveProperty("menu"); // shall save "menu.json" as App.metadata
304
+ expect(apiParams[3].menu).not.toBeNull(); // IV (initialize vector) using 16 random bytes
305
+
306
+ expect(postArgs[2]).toStrictEqual({
307
+ headers: { "Content-Type": "text/plain" },
308
+ });
309
+
310
+ expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 0][0]).toContain(
311
+ `Finished packing ${Object.keys(mockScripts).length} files into "./upload/${appName.toLowerCase()}.tgz"`,
312
+ );
313
+ expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 1][0]).toContain(
314
+ `Finished uploading "./upload/${appName.toLocaleLowerCase()}.tgz"`,
315
+ );
316
+
317
+ // fs.rmSync(mockDataFolder + '/' + appName, {recursive: true, force: true})
318
+ },
319
+ );
320
+
321
+ it("shall support addApp body scripts when called from other function", async () => {
322
+ const appName = "bodyScriptSourceApp";
323
+ const workingDir = mockDataFolder + "/" + appName;
324
+ const mockAESKey = Buffer.from(
325
+ "hAnHadaJXJaq/9fCFMNmjkrB61CBPXJid6vbtXgG8Ug=",
326
+ "base64",
327
+ );
328
+ const expectedScript = '{"fromBody":{"id":1}}';
329
+
330
+ mockValidTemplates(appName, {});
331
+ mockIssuerKeyResponse();
332
+ process.env.BIZA_APP_SKIP_PARSE = "1";
333
+
334
+ try {
335
+ process.argv[1] = process.cwd() + "\\bin";
336
+ const { addApp } = await import("../bin/app.js");
337
+
338
+ const result = await addApp({
339
+ workingDir,
340
+ verbose: false,
341
+ server: "https://a.b.c",
342
+ apiPort: 1205,
343
+ dbIndex: 2,
344
+ files: [{ name: "a.js" }],
345
+ body: {
346
+ scripts: {
347
+ "a.js": "get = function () { return {fromBody: {id: 1}} }",
348
+ },
349
+ },
350
+ });
351
+
352
+ expect(result.success).toBe(true);
353
+ expect(axios.post).toHaveBeenCalledTimes(1);
354
+
355
+ const apiParams = axios.post.mock.calls[0][1]._parameters;
356
+ const templateBuffer = Buffer.from(apiParams[2], "base64");
357
+ const decryptScripts = {};
358
+ const stream = new Duplex();
359
+ stream.push(templateBuffer);
360
+ stream.push(null);
361
+ stream.pipe(
362
+ tar
363
+ .t({
364
+ strict: true,
365
+ sync: true,
366
+ onReadEntry: (entry) => {
367
+ const chunks = [];
368
+ entry.on("data", (chunk) => chunks.push(chunk));
369
+ entry.on("end", () => {
370
+ const cipherText = Buffer.from(
371
+ Buffer.concat(chunks).toString(),
372
+ "base64",
373
+ );
374
+ const decryptInitializeVector =
375
+ cipherText.subarray(0, 16);
376
+ const decryptData = cipherText.subarray(16);
377
+ const decipher = createDecipheriv(
378
+ "aes-256-cbc",
379
+ mockAESKey,
380
+ decryptInitializeVector,
381
+ );
382
+ const decrypted = Buffer.concat([
383
+ decipher.update(decryptData),
384
+ decipher.final(),
385
+ ]);
386
+ decryptScripts[entry.path] =
387
+ decrypted.toString();
388
+ });
389
+ },
390
+ })
391
+ .on("finish", () => stream.end()),
392
+ );
393
+ await finished(stream);
394
+
395
+ expect(decryptScripts["a.js"]).toBe(expectedScript);
396
+ } finally {
397
+ delete process.env.BIZA_APP_SKIP_PARSE;
398
+ }
399
+ });
400
+
401
+ it("shall resolve app name from SYS$CONFIG APPLICATION_CONFIG and use it for upload", async () => {
402
+ const appName = "metadataNameSourceApp";
403
+ const workingDir = mockDataFolder + "/" + appName;
404
+
405
+ mockValidTemplates(appName, {});
406
+ mockIssuerKeyResponse();
407
+ axios.request = jest.fn().mockResolvedValue({
408
+ data: {
409
+ data: JSON.stringify([
410
+ {
411
+ "SYS$CONFIG.NAME": "APPLICATION_CONFIG",
412
+ "SYS$CONFIG.DATA": JSON.stringify({
413
+ metadata: {
414
+ name: "BizA Sales App",
415
+ },
416
+ }),
417
+ },
418
+ ]),
419
+ },
420
+ });
421
+ process.env.BIZA_APP_SKIP_PARSE = "1";
422
+
423
+ try {
424
+ process.argv[1] = process.cwd() + "\\bin";
425
+ const { addApp } = await import("../bin/app.js");
426
+
427
+ const result = await addApp({
428
+ workingDir,
429
+ verbose: false,
430
+ server: "https://a.b.c",
431
+ apiPort: 1205,
432
+ dbIndex: 2,
433
+ files: [{ name: "applicationConfig.js" }],
434
+ sub: "metadata-sub",
435
+ body: {
436
+ scripts: {
437
+ "applicationConfig.js":
438
+ 'get = function () { return { metadata: { name: "Name From Body Must Be Ignored" } }; }',
439
+ },
440
+ },
441
+ });
442
+
443
+ expect(result.success).toBe(true);
444
+ expect(axios.post).toHaveBeenCalledTimes(1);
445
+ expect(axios.request).toHaveBeenCalledTimes(1);
446
+
447
+ const apiParams = axios.post.mock.calls[0][1]._parameters;
448
+ expect(apiParams[1]).toBe("bizasalesapp");
449
+ expect(apiParams[3]).toHaveProperty("name", "BizA Sales App");
450
+ } finally {
451
+ delete process.env.BIZA_APP_SKIP_PARSE;
452
+ }
453
+ });
454
+
455
+ it("shall use bundle name for upload when body is null even if application config has metadata name", async () => {
456
+ const appName = "metadataNameSourceApp";
457
+ const workingDir = mockDataFolder + "/" + appName;
458
+
459
+ mockValidTemplates(appName, {
460
+ "applicationConfig.js": {
461
+ act: 'get = function () { return { metadata: { name: "Name From Body Must Be Ignored" } }; }',
462
+ },
463
+ });
464
+ mockIssuerKeyResponse();
465
+ axios.request = jest.fn().mockResolvedValue({
466
+ data: {
467
+ data: JSON.stringify([
468
+ {
469
+ "SYS$CONFIG.NAME": "APPLICATION_CONFIG",
470
+ "SYS$CONFIG.DATA": JSON.stringify({
471
+ metadata: {
472
+ name: "BizA Sales App",
473
+ },
474
+ }),
475
+ },
476
+ ]),
477
+ },
478
+ });
479
+ process.env.BIZA_APP_SKIP_PARSE = "1";
480
+
481
+ try {
482
+ process.argv[1] = process.cwd() + "\\bin";
483
+ const { addApp } = await import("../bin/app.js");
484
+
485
+ const result = await addApp({
486
+ workingDir,
487
+ verbose: false,
488
+ server: "https://a.b.c",
489
+ apiPort: 1205,
490
+ dbIndex: 2,
491
+ files: [{ name: "applicationConfig.js" }],
492
+ sub: "metadata-sub",
493
+ });
494
+
495
+ expect(result.success).toBe(true);
496
+ expect(axios.post).toHaveBeenCalledTimes(1);
497
+ expect(axios.request).toHaveBeenCalledTimes(1);
498
+
499
+ const apiParams = axios.post.mock.calls[0][1]._parameters;
500
+ expect(apiParams[1]).toBe("metadatanamesourceapp");
501
+ } finally {
502
+ delete process.env.BIZA_APP_SKIP_PARSE;
503
+ }
504
+ });
505
+
506
+ it("shall remove hyphens from upload app name derived from bundle folder name", async () => {
507
+ const appName = "biz-a-insight";
508
+ const workingDir = mockDataFolder + "/" + appName;
509
+
510
+ mockValidTemplates(appName, {
511
+ "applicationConfig.js": {
512
+ act: 'get = function () { return { metadata: { name: "Ignored Name" } }; }',
513
+ },
514
+ });
515
+ mockIssuerKeyResponse();
516
+ process.env.BIZA_APP_SKIP_PARSE = "1";
517
+
518
+ try {
519
+ process.argv[1] = process.cwd() + "\\bin";
520
+ const { addApp } = await import("../bin/app.js");
521
+
522
+ const result = await addApp({
523
+ workingDir,
524
+ verbose: false,
525
+ server: "https://a.b.c",
526
+ apiPort: 1205,
527
+ dbIndex: 2,
528
+ files: [{ name: "applicationConfig.js" }],
529
+ sub: "metadata-sub",
530
+ });
531
+
532
+ expect(result.success).toBe(true);
533
+ expect(axios.post).toHaveBeenCalledTimes(1);
534
+
535
+ const apiParams = axios.post.mock.calls[0][1]._parameters;
536
+ expect(apiParams[1]).toBe("bizainsight");
537
+ } finally {
538
+ delete process.env.BIZA_APP_SKIP_PARSE;
539
+ }
540
+ });
541
+ /* it('Invalid script syntax', async ()=>{
259
542
  mockValidTemplates({
260
543
  'a.js' : {act: 'get = function () {return {modelA: {}}'} // missing close closure at the end
261
544
  })
@@ -266,7 +549,7 @@ describe('Biz-A Apps CLI', () => {
266
549
  expect(logSpy.mock.calls[0][0]).toBe('===================\nA.JS\n===================')
267
550
 
268
551
  expect(errorSpy.mock.calls.length).toBe(1)
269
- expect(errorSpy.mock.calls[0][0]).toBe('a.js : SyntaxError: Unexpected token: eof, expected: punc «}»')
552
+ expect(errorSpy.mock.calls[0][0]).toBe('a.js : SyntaxError: Unexpected token: eof, expected: punc «}»')
270
553
  })
271
554
 
272
555
  it('Failed to compile script with node sandbox and ES6 Import', async ()=>{
@@ -304,400 +587,622 @@ describe('Biz-A Apps CLI', () => {
304
587
  expect(errorSpy.mock.calls[0][0]).toBe('a.js : Failed to compile template script.\nPlease make sure the script is correct and not returning empty result') // ES6 import error
305
588
  }) */
306
589
 
307
- const errorHandlingTestCases = [
308
- // [test case name, scripts, expectedFn()]
309
- [
310
- 'Invalid script syntax',
311
- { act: 'get = function () {return {modelA: {}}' }, // missing close closure at the end',
312
- () => {
313
- expect(logSpy.mock.calls.length).toBe(JEST_MESSAGE_TOTAL + 5)
314
- expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 0][0]).toBe('===================\nA.JS\n===================')
315
- expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 1][0]).toBe('Running script with VM') // node sandbox (VN) error as console.log
316
- expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 2][0]).toStrictEqual('a.js:1:38: SyntaxError: Unexpected token: eof, expected: punc «}»')
317
- expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 3][0]).toStrictEqual('Running script with Import function')
318
- expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 4][0]).toStrictEqual('===================\nEOF A.JS\n===================')
319
-
320
- expect(errorSpy.mock.calls.length).toBe(1)
321
- expect(errorSpy.mock.calls[0][0]).toBe('a.js : SyntaxError: Unexpected end of input')
322
- }
323
- ],
324
- [
325
- 'Failed to compile script with node sandbox and ES6 Import',
326
- { act: 'get = function () {return {modelA: {}}}\n modul.expor = get;\n' }, // wrong "module.export"
327
- () => {
328
- expect(logSpy.mock.calls.length).toBe(JEST_MESSAGE_TOTAL + 6)
329
- expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 0][0]).toBe('===================\nA.JS\n===================')
330
- expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 1][0]).toBe('Running script with VM') // node sandbox (VN) error as console.log
331
- expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 2][0]).toBe('Minify : \nget=function(){return{modelA:{}}};modul.expor=get;')
332
- expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 3][0]).toBe("a.js : ReferenceError: modul is not defined")
333
- expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 4][0]).toBe('Running script with Import function')
334
- expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 5][0]).toStrictEqual('===================\nEOF A.JS\n===================')
335
-
336
- expect(errorSpy.mock.calls.length).toBe(1)
337
- expect(errorSpy.mock.calls[0][0]).toStrictEqual('a.js : Error: Invalid data URI')// ES6 import error
338
- }
339
- ],
340
- [
341
- 'Shall not allow empty script',
342
- { act: 'get = function () {}' }, // empty script
343
- () => {
344
- expect(logSpy.mock.calls.length).toBe(JEST_MESSAGE_TOTAL + 5)
345
- expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 0][0]).toBe('===================\nA.JS\n===================')
346
- expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 1][0]).toBe('Running script with VM') // node sandbox (VN) error as console.log
347
- expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 2][0]).toBe('Minify : \nget=function(){};')
348
- expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 3][0]).toBe('Running script with Import function')
349
- expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 4][0]).toStrictEqual('===================\nEOF A.JS\n===================')
350
-
351
- expect(errorSpy.mock.calls.length).toBe(1)
352
- expect(errorSpy.mock.calls[0][0]).toStrictEqual('a.js : Failed to compile template script.\nPlease make sure the script is correct and not returning empty result')
353
- }
354
- ],
355
- ]
356
- it.each(errorHandlingTestCases)('%p', async (name, scripts, expectedFn) => {
357
- const appName = 'errorHandlingTest' + errorHandlingTestCases.findIndex(testCase => testCase[0] === name)
358
- mockValidTemplates(appName, { 'a.js': scripts })
359
- mockIssuerKeyResponse()
360
- await runCommand('add', '-s', 'https://a.b.c', '-p', '1205', '-i', '2', '-d', mockDataFolder + '/' + appName, "-v")
361
- expectedFn()
362
- })
363
-
364
- it('Shall not allow app without script template', async () => {
365
- mockValidTemplates(expect.getState().currentTestName, {})
366
- mockIssuerKeyResponse()
367
- await runCommand('add', '-s', 'https://a.b.c', '-p', '1205', '-i', '2', '-d', mockDataFolder + '/' + expect.getState().currentTestName, "-v")
368
-
369
- expect(logSpy.mock.calls.length).toBe(JEST_MESSAGE_TOTAL + 0)
370
-
371
- expect(errorSpy.mock.calls.length).toBe(1)
372
- expect(errorSpy.mock.calls[0][0]).toBe('Nothing to upload. Please recheck your app folder.')
373
- })
374
-
375
- it('Shall recommend command', async () => {
376
- const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => { })
377
- await runCommand('Add', '-s', 'https://a.b.c', '-p', '1205', '-i', '2', '-d', mockDataFolder + '/NotExistFolder', "-v")
378
- expect(logSpy.mock.calls.length).toBe(0)
379
- expect(exitSpy).toHaveBeenCalledWith(1) // exit code = 1
380
- expect(errorSpy.mock.calls[errorSpy.mock.calls.length - 1][0]).toBe('Did you mean add?')
381
- })
382
-
383
- it('Shall have minimun one command', async () => {
384
- const exitSpy = jest.spyOn(process, 'exit').mockImplementation()
385
- await runCommand('-s', 'https://a.b.c', '-i', 2)
386
- expect(logSpy.mock.calls.length).toBe(0)
387
- expect(exitSpy).toHaveBeenCalledWith(1)
388
- expect(errorSpy.mock.calls[errorSpy.mock.calls.length - 1][0]).toBe('You need at least one command before moving on')
389
- })
390
-
391
- it('Shall use strict command and options', async () => {
392
- const exitSpy = jest.spyOn(process, 'exit').mockImplementation()
393
- await runCommand('Unknown Command', '-s', 'https://a.b.c', '-i', 2)
394
- expect(logSpy.mock.calls.length).toBe(0)
395
- expect(exitSpy).toHaveBeenCalledWith(1)
396
- expect(errorSpy.mock.calls[errorSpy.mock.calls.length - 1][0]).toBe('Unknown argument: Unknown Command')
397
- })
398
-
399
- describe('run Jest', () => { //SCY BZ 4331
400
- const mockScripts = {
401
- 'a.js': {
402
- act: 'get = function () {return {modelA: {}}}',
403
- exp: '{"modelA":{}}'
404
- }
405
- }
406
- const appName = 'compressStressTest1';
407
- const fullTestPath = mockDataFolder + appName;
408
- const absoluteFullTestPath = path.resolve(fullTestPath);
409
-
410
- let jestArgs = ['--no-install', 'jest', '--json'];
411
- let jestArgsWithPassNoTest = jestArgs.slice();
412
- jestArgsWithPassNoTest.push('--passWithNoTests');
413
-
414
- async function runJest() {
415
- mockIssuerKeyResponse();
416
- await runCommand('add', '-s', 'https://a.b.c', '-p', '1205', '-i', '2', '-d', fullTestPath)
417
- }
418
-
419
- function continueExpect(args) {
420
- expect(process.cwd()).toBe(absoluteFullTestPath);
421
- if (process.platform === "win32") {
422
- expect(child_process.spawn).toBeCalledWith("cmd.exe", ["/d", "/s", "/c", "npx", ...args]);
423
- } else {
424
- expect(child_process.spawn).toBeCalledWith("npx", args);
425
- }
426
- expect(logSpy.mock.calls[logSpy.mock.calls.length - JEST_MESSAGE_TOTAL - 2][0]).toBe('====================');
427
- expect(logSpy.mock.calls[logSpy.mock.calls.length - JEST_MESSAGE_TOTAL - 1][0]).toBe(jestMessage);
428
- expect(logSpy.mock.calls[logSpy.mock.calls.length - JEST_MESSAGE_TOTAL][0]).toBe('====================');
429
- expect(errorSpy.mock.calls.length).toBe(0);
430
- }
431
-
432
- function abortExpect(args) {
433
- expect(process.cwd()).toBe(absoluteFullTestPath);
434
- if (process.platform === "win32") {
435
- expect(child_process.spawn).toBeCalledWith("cmd.exe", ["/d", "/s", "/c", "npx", ...args]);
436
- } else {
437
- expect(child_process.spawn).toBeCalledWith("npx", args);
438
- }
439
- expect(logSpy.mock.calls[logSpy.mock.calls.length - JEST_MESSAGE_TOTAL][0]).toBe('====================');
440
- expect(logSpy.mock.calls[logSpy.mock.calls.length - JEST_MESSAGE_TOTAL + 1][0]).toBe(jestMessage);
441
- expect(logSpy.mock.calls[logSpy.mock.calls.length - JEST_MESSAGE_TOTAL + 2][0]).toBe('====================');
442
- expect(errorSpy.mock.calls[errorSpy.mock.calls.length - 1][0]).toBe('Biz-A Add aborted');
443
- }
444
-
445
- it('Shall continue add biz a template, jest found, folder test found, unit test found, unit test passed', async () => {
446
- returnedCode = 0;
447
- jestMessage = 'Passed';
448
-
449
- mockValidTemplates(appName, mockScripts);
450
- fs.mkdirSync(fullTestPath + '/test', { recursive: true })
451
- fs.writeFileSync(fullTestPath + '/test/a.test.js', `it('dummy test', () => {
590
+ const errorHandlingTestCases = [
591
+ // [test case name, scripts, expectedFn()]
592
+ [
593
+ "Invalid script syntax",
594
+ { act: "get = function () {return {modelA: {}}" }, // missing close closure at the end',
595
+ () => {
596
+ expect(logSpy.mock.calls.length).toBe(
597
+ JEST_MESSAGE_TOTAL + 5,
598
+ );
599
+ expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 0][0]).toBe(
600
+ "===================\nA.JS\n===================",
601
+ );
602
+ expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 1][0]).toBe(
603
+ "Running script with VM",
604
+ ); // node sandbox (VN) error as console.log
605
+ const vmSyntaxError =
606
+ logSpy.mock.calls[JEST_MESSAGE_TOTAL + 2][0];
607
+ expect(vmSyntaxError).toContain(
608
+ "a.js:1:38: SyntaxError: Unexpected token: eof, expected: punc",
609
+ );
610
+ expect(vmSyntaxError).toContain("}");
611
+ expect(
612
+ logSpy.mock.calls[JEST_MESSAGE_TOTAL + 3][0],
613
+ ).toStrictEqual("Running script with Import function");
614
+ expect(
615
+ logSpy.mock.calls[JEST_MESSAGE_TOTAL + 4][0],
616
+ ).toStrictEqual(
617
+ "===================\nEOF A.JS\n===================",
618
+ );
619
+
620
+ expect(errorSpy.mock.calls.length).toBe(1);
621
+ expect(errorSpy.mock.calls[0][0]).toBe(
622
+ "a.js : SyntaxError: Unexpected end of input",
623
+ );
624
+ },
625
+ ],
626
+ [
627
+ "Failed to compile script with node sandbox and ES6 Import",
628
+ {
629
+ act: "get = function () {return {modelA: {}}}\n modul.expor = get;\n",
630
+ }, // wrong "module.export"
631
+ () => {
632
+ expect(logSpy.mock.calls.length).toBe(
633
+ JEST_MESSAGE_TOTAL + 6,
634
+ );
635
+ expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 0][0]).toBe(
636
+ "===================\nA.JS\n===================",
637
+ );
638
+ expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 1][0]).toBe(
639
+ "Running script with VM",
640
+ ); // node sandbox (VN) error as console.log
641
+ expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 2][0]).toBe(
642
+ "Minify : \nget=function(){return{modelA:{}}};modul.expor=get;",
643
+ );
644
+ expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 3][0]).toBe(
645
+ "a.js : ReferenceError: modul is not defined",
646
+ );
647
+ expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 4][0]).toBe(
648
+ "Running script with Import function",
649
+ );
650
+ expect(
651
+ logSpy.mock.calls[JEST_MESSAGE_TOTAL + 5][0],
652
+ ).toStrictEqual(
653
+ "===================\nEOF A.JS\n===================",
654
+ );
655
+
656
+ expect(errorSpy.mock.calls.length).toBe(1);
657
+ expect(errorSpy.mock.calls[0][0]).toStrictEqual(
658
+ "a.js : Error: Invalid data URI",
659
+ ); // ES6 import error
660
+ },
661
+ ],
662
+ [
663
+ "Shall not allow empty script",
664
+ { act: "get = function () {}" }, // empty script
665
+ () => {
666
+ expect(logSpy.mock.calls.length).toBe(
667
+ JEST_MESSAGE_TOTAL + 5,
668
+ );
669
+ expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 0][0]).toBe(
670
+ "===================\nA.JS\n===================",
671
+ );
672
+ expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 1][0]).toBe(
673
+ "Running script with VM",
674
+ ); // node sandbox (VN) error as console.log
675
+ expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 2][0]).toBe(
676
+ "Minify : \nget=function(){};",
677
+ );
678
+ expect(logSpy.mock.calls[JEST_MESSAGE_TOTAL + 3][0]).toBe(
679
+ "Running script with Import function",
680
+ );
681
+ expect(
682
+ logSpy.mock.calls[JEST_MESSAGE_TOTAL + 4][0],
683
+ ).toStrictEqual(
684
+ "===================\nEOF A.JS\n===================",
685
+ );
686
+
687
+ expect(errorSpy.mock.calls.length).toBe(1);
688
+ expect(errorSpy.mock.calls[0][0]).toStrictEqual(
689
+ "a.js : Failed to compile template script.\nPlease make sure the script is correct and not returning empty result",
690
+ );
691
+ },
692
+ ],
693
+ ];
694
+ it.each(errorHandlingTestCases)(
695
+ "%p",
696
+ async (name, scripts, expectedFn) => {
697
+ const appName =
698
+ "errorHandlingTest" +
699
+ errorHandlingTestCases.findIndex(
700
+ (testCase) => testCase[0] === name,
701
+ );
702
+ mockValidTemplates(appName, { "a.js": scripts });
703
+ mockIssuerKeyResponse();
704
+ await runCommand(
705
+ "add",
706
+ "-s",
707
+ "https://a.b.c",
708
+ "-p",
709
+ "1205",
710
+ "-i",
711
+ "2",
712
+ "-d",
713
+ mockDataFolder + "/" + appName,
714
+ "-v",
715
+ );
716
+ expectedFn();
717
+ },
718
+ );
719
+
720
+ it("Shall not allow app without script template", async () => {
721
+ mockValidTemplates(expect.getState().currentTestName, {});
722
+ mockIssuerKeyResponse();
723
+ await runCommand(
724
+ "add",
725
+ "-s",
726
+ "https://a.b.c",
727
+ "-p",
728
+ "1205",
729
+ "-i",
730
+ "2",
731
+ "-d",
732
+ mockDataFolder + "/" + expect.getState().currentTestName,
733
+ "-v",
734
+ );
735
+
736
+ expect(logSpy.mock.calls.length).toBe(JEST_MESSAGE_TOTAL + 0);
737
+
738
+ expect(errorSpy.mock.calls.length).toBe(1);
739
+ expect(errorSpy.mock.calls[0][0]).toBe(
740
+ "Nothing to upload. Please recheck your app folder.",
741
+ );
742
+ });
743
+
744
+ it("Shall recommend command", async () => {
745
+ const exitSpy = jest
746
+ .spyOn(process, "exit")
747
+ .mockImplementation(() => {});
748
+ await runCommand(
749
+ "Add",
750
+ "-s",
751
+ "https://a.b.c",
752
+ "-p",
753
+ "1205",
754
+ "-i",
755
+ "2",
756
+ "-d",
757
+ mockDataFolder + "/NotExistFolder",
758
+ "-v",
759
+ );
760
+ expect(logSpy.mock.calls.length).toBe(0);
761
+ expect(exitSpy).toHaveBeenCalledWith(1); // exit code = 1
762
+ expect(errorSpy.mock.calls[errorSpy.mock.calls.length - 1][0]).toBe(
763
+ "Did you mean add?",
764
+ );
765
+ });
766
+
767
+ it("Shall have minimun one command", async () => {
768
+ const exitSpy = jest.spyOn(process, "exit").mockImplementation();
769
+ await runCommand("-s", "https://a.b.c", "-i", 2);
770
+ expect(logSpy.mock.calls.length).toBe(0);
771
+ expect(exitSpy).toHaveBeenCalledWith(1);
772
+ expect(errorSpy.mock.calls[errorSpy.mock.calls.length - 1][0]).toBe(
773
+ "You need at least one command before moving on",
774
+ );
775
+ });
776
+
777
+ it("Shall use strict command and options", async () => {
778
+ const exitSpy = jest.spyOn(process, "exit").mockImplementation();
779
+ await runCommand("Unknown Command", "-s", "https://a.b.c", "-i", 2);
780
+ expect(logSpy.mock.calls.length).toBe(0);
781
+ expect(exitSpy).toHaveBeenCalledWith(1);
782
+ expect(errorSpy.mock.calls[errorSpy.mock.calls.length - 1][0]).toBe(
783
+ "Unknown argument: Unknown Command",
784
+ );
785
+ });
786
+
787
+ describe("run Jest", () => {
788
+ //SCY BZ 4331
789
+ const mockScripts = {
790
+ "a.js": {
791
+ act: "get = function () {return {modelA: {}}}",
792
+ exp: '{"modelA":{}}',
793
+ },
794
+ };
795
+ const appName = "compressStressTest1";
796
+ const fullTestPath = mockDataFolder + appName;
797
+ const absoluteFullTestPath = path.resolve(fullTestPath);
798
+
799
+ let jestArgs = ["--no-install", "jest", "--json"];
800
+ let jestArgsWithPassNoTest = jestArgs.slice();
801
+ jestArgsWithPassNoTest.push("--passWithNoTests");
802
+
803
+ async function runJest() {
804
+ mockIssuerKeyResponse();
805
+ await runCommand(
806
+ "add",
807
+ "-s",
808
+ "https://a.b.c",
809
+ "-p",
810
+ "1205",
811
+ "-i",
812
+ "2",
813
+ "-d",
814
+ fullTestPath,
815
+ );
816
+ }
817
+
818
+ function continueExpect(args) {
819
+ expect(process.cwd()).toBe(absoluteFullTestPath);
820
+ if (process.platform === "win32") {
821
+ expect(child_process.spawn).toBeCalledWith("cmd.exe", [
822
+ "/d",
823
+ "/s",
824
+ "/c",
825
+ "npx",
826
+ ...args,
827
+ ]);
828
+ } else {
829
+ expect(child_process.spawn).toBeCalledWith("npx", args);
830
+ }
831
+ expect(
832
+ logSpy.mock.calls[
833
+ logSpy.mock.calls.length - JEST_MESSAGE_TOTAL - 2
834
+ ][0],
835
+ ).toBe("====================");
836
+ expect(
837
+ logSpy.mock.calls[
838
+ logSpy.mock.calls.length - JEST_MESSAGE_TOTAL - 1
839
+ ][0],
840
+ ).toBe(jestMessage);
841
+ expect(
842
+ logSpy.mock.calls[
843
+ logSpy.mock.calls.length - JEST_MESSAGE_TOTAL
844
+ ][0],
845
+ ).toBe("====================");
846
+ expect(errorSpy.mock.calls.length).toBe(0);
847
+ }
848
+
849
+ function abortExpect(args) {
850
+ expect(process.cwd()).toBe(absoluteFullTestPath);
851
+ if (process.platform === "win32") {
852
+ expect(child_process.spawn).toBeCalledWith("cmd.exe", [
853
+ "/d",
854
+ "/s",
855
+ "/c",
856
+ "npx",
857
+ ...args,
858
+ ]);
859
+ } else {
860
+ expect(child_process.spawn).toBeCalledWith("npx", args);
861
+ }
862
+ expect(
863
+ logSpy.mock.calls[
864
+ logSpy.mock.calls.length - JEST_MESSAGE_TOTAL
865
+ ][0],
866
+ ).toBe("====================");
867
+ expect(
868
+ logSpy.mock.calls[
869
+ logSpy.mock.calls.length - JEST_MESSAGE_TOTAL + 1
870
+ ][0],
871
+ ).toBe(jestMessage);
872
+ expect(
873
+ logSpy.mock.calls[
874
+ logSpy.mock.calls.length - JEST_MESSAGE_TOTAL + 2
875
+ ][0],
876
+ ).toBe("====================");
877
+ expect(
878
+ errorSpy.mock.calls[errorSpy.mock.calls.length - 1][0],
879
+ ).toBe("Biz-A Add aborted");
880
+ }
881
+
882
+ it("Shall continue add biz a template, jest found, folder test found, unit test found, unit test passed", async () => {
883
+ returnedCode = 0;
884
+ jestMessage = "Passed";
885
+
886
+ mockValidTemplates(appName, mockScripts);
887
+ fs.mkdirSync(fullTestPath + "/test", { recursive: true });
888
+ fs.writeFileSync(
889
+ fullTestPath + "/test/a.test.js",
890
+ `it('dummy test', () => {
452
891
  expect(true).toBe(true);
453
- })`)
454
-
455
- await runJest();
456
- continueExpect(jestArgs);
457
- })
458
-
459
- it('Shall abort add biz a template, jest found, folder test found, unit test found, unit test failed', async () => {
460
- returnedCode = 1;
461
- jestMessage = 'FAIL';
462
-
463
- mockValidTemplates(appName, mockScripts);
464
- fs.mkdirSync(fullTestPath + '/test', { recursive: true })
465
- fs.writeFileSync(fullTestPath + '/test/a.test.js', `it('dummy test', () => {
892
+ })`,
893
+ );
894
+
895
+ await runJest();
896
+ continueExpect(jestArgs);
897
+ });
898
+
899
+ it("Shall abort add biz a template, jest found, folder test found, unit test found, unit test failed", async () => {
900
+ returnedCode = 1;
901
+ jestMessage = "FAIL";
902
+
903
+ mockValidTemplates(appName, mockScripts);
904
+ fs.mkdirSync(fullTestPath + "/test", { recursive: true });
905
+ fs.writeFileSync(
906
+ fullTestPath + "/test/a.test.js",
907
+ `it('dummy test', () => {
466
908
  expect(true).toBe(false);
467
- })`)
909
+ })`,
910
+ );
468
911
 
469
- await runJest();
912
+ await runJest();
470
913
 
471
- abortExpect(jestArgs);
472
- })
914
+ abortExpect(jestArgs);
915
+ });
473
916
 
474
- it('Shall continue add biz a template, jest found, folder test found, no unit test', async () => {
475
- returnedCode = 1;
476
- jestMessage = 'No tests found';
917
+ it("Shall continue add biz a template, jest found, folder test found, no unit test", async () => {
918
+ returnedCode = 1;
919
+ jestMessage = "No tests found";
477
920
 
478
- mockValidTemplates(appName, mockScripts);
479
- fs.mkdirSync(fullTestPath + '/test', { recursive: true })
921
+ mockValidTemplates(appName, mockScripts);
922
+ fs.mkdirSync(fullTestPath + "/test", { recursive: true });
480
923
 
481
- await runJest();
924
+ await runJest();
482
925
 
483
- continueExpect(jestArgs);
484
- })
926
+ continueExpect(jestArgs);
927
+ });
485
928
 
486
- it('Shall continue add biz a template, jest found, no folder test', async () => {
487
- returnedCode = 0;
488
- jestMessage = 'No tests found';
929
+ it("Shall continue add biz a template, jest found, no folder test", async () => {
930
+ returnedCode = 0;
931
+ jestMessage = "No tests found";
489
932
 
490
- mockValidTemplates(appName, mockScripts);
491
- await runJest();
933
+ mockValidTemplates(appName, mockScripts);
934
+ await runJest();
492
935
 
493
- continueExpect(jestArgsWithPassNoTest);
494
- })
936
+ continueExpect(jestArgsWithPassNoTest);
937
+ });
495
938
 
496
- it('Shall abort add biz a template, no jest found, folder test found, unit test found, unit test passed', async () => {
497
- returnedCode = 1;
498
- jestMessage = 'missing packages jest';
939
+ it("Shall abort add biz a template, no jest found, folder test found, unit test found, unit test passed", async () => {
940
+ returnedCode = 1;
941
+ jestMessage = "missing packages jest";
499
942
 
500
- mockValidTemplates(appName, mockScripts);
501
- fs.mkdirSync(fullTestPath + '/test', { recursive: true })
502
- fs.writeFileSync(fullTestPath + '/test/a.test.js', `it('dummy test', () => {
943
+ mockValidTemplates(appName, mockScripts);
944
+ fs.mkdirSync(fullTestPath + "/test", { recursive: true });
945
+ fs.writeFileSync(
946
+ fullTestPath + "/test/a.test.js",
947
+ `it('dummy test', () => {
503
948
  expect(true).toBe(true);
504
- })`)
949
+ })`,
950
+ );
505
951
 
506
- await runJest();
952
+ await runJest();
507
953
 
508
- abortExpect(jestArgs);
509
- })
954
+ abortExpect(jestArgs);
955
+ });
510
956
 
511
- it('Shall abort add biz a template, no jest found, folder test found, unit test found, unit test failed', async () => {
512
- returnedCode = 1;
513
- jestMessage = 'missing packages jest';
957
+ it("Shall abort add biz a template, no jest found, folder test found, unit test found, unit test failed", async () => {
958
+ returnedCode = 1;
959
+ jestMessage = "missing packages jest";
514
960
 
515
- mockValidTemplates(appName, mockScripts);
516
- fs.mkdirSync(fullTestPath + '/test', { recursive: true })
517
- fs.writeFileSync(fullTestPath + '/test/a.test.js', `it('dummy test', () => {
961
+ mockValidTemplates(appName, mockScripts);
962
+ fs.mkdirSync(fullTestPath + "/test", { recursive: true });
963
+ fs.writeFileSync(
964
+ fullTestPath + "/test/a.test.js",
965
+ `it('dummy test', () => {
518
966
  expect(true).toBe(false);
519
- })`)
520
-
521
- await runJest();
522
-
523
- abortExpect(jestArgs);
524
- })
525
-
526
- it('Shall abort add biz a template, no jest found, folder test found, no unit test', async () => {
527
- returnedCode = 1;
528
- jestMessage = 'missing packages jest';
529
-
530
- mockValidTemplates(appName, mockScripts);
531
- fs.mkdirSync(fullTestPath + '/test', { recursive: true })
532
-
533
- await runJest();
534
-
535
- abortExpect(jestArgs);
536
- })
537
-
538
- it('Shall continue add biz a template, no jest found, no folder test', async () => {
539
- returnedCode = 1;
540
- jestMessage = 'missing packages jest';
541
-
542
- mockValidTemplates(appName, mockScripts);
543
- await runJest();
544
-
545
- continueExpect(jestArgsWithPassNoTest);
546
- })
547
-
548
- it('Shall continue add biz a template, stdout exists and must be consumed for many unit test outputs', async () => {
549
- returnedCode = 0;
550
- const stdoutChunks = Array(300).fill('Passed');
551
- let stdoutConsumed = false;
552
-
553
- child_process.spawn.mockImplementationOnce(() => ({
554
- on: jest.fn((event, callback) => {
555
- if ((event === 'close') && stdoutConsumed) callback(returnedCode)
556
- }),
557
- stderr: {
558
- on: jest.fn()
559
- },
560
- stdout: {
561
- on: jest.fn((event, callback) => {
562
- if (event === 'data') {
563
- stdoutConsumed = true;
564
- for (const chunk of stdoutChunks) {
565
- callback(chunk)
566
- }
567
- }
568
- })
569
- }
570
- }))
571
-
572
- mockValidTemplates(appName, mockScripts);
573
- fs.mkdirSync(fullTestPath + '/test', { recursive: true })
574
- fs.writeFileSync(fullTestPath + '/test/a.test.js', `it('dummy test', () => {
967
+ })`,
968
+ );
969
+
970
+ await runJest();
971
+
972
+ abortExpect(jestArgs);
973
+ });
974
+
975
+ it("Shall abort add biz a template, no jest found, folder test found, no unit test", async () => {
976
+ returnedCode = 1;
977
+ jestMessage = "missing packages jest";
978
+
979
+ mockValidTemplates(appName, mockScripts);
980
+ fs.mkdirSync(fullTestPath + "/test", { recursive: true });
981
+
982
+ await runJest();
983
+
984
+ abortExpect(jestArgs);
985
+ });
986
+
987
+ it("Shall continue add biz a template, no jest found, no folder test", async () => {
988
+ returnedCode = 1;
989
+ jestMessage = "missing packages jest";
990
+
991
+ mockValidTemplates(appName, mockScripts);
992
+ await runJest();
993
+
994
+ continueExpect(jestArgsWithPassNoTest);
995
+ });
996
+
997
+ it("Shall continue add biz a template, stdout exists and must be consumed for many unit test outputs", async () => {
998
+ returnedCode = 0;
999
+ const stdoutChunks = Array(300).fill("Passed");
1000
+ let stdoutConsumed = false;
1001
+
1002
+ child_process.spawn.mockImplementationOnce(() => ({
1003
+ on: jest.fn((event, callback) => {
1004
+ if (event === "close" && stdoutConsumed)
1005
+ callback(returnedCode);
1006
+ }),
1007
+ stderr: {
1008
+ on: jest.fn(),
1009
+ },
1010
+ stdout: {
1011
+ on: jest.fn((event, callback) => {
1012
+ if (event === "data") {
1013
+ stdoutConsumed = true;
1014
+ for (const chunk of stdoutChunks) {
1015
+ callback(chunk);
1016
+ }
1017
+ }
1018
+ }),
1019
+ },
1020
+ }));
1021
+
1022
+ mockValidTemplates(appName, mockScripts);
1023
+ fs.mkdirSync(fullTestPath + "/test", { recursive: true });
1024
+ fs.writeFileSync(
1025
+ fullTestPath + "/test/a.test.js",
1026
+ `it('dummy test', () => {
575
1027
  expect(true).toBe(true);
576
- })`)
577
-
578
- await runJest();
579
-
580
- expect(stdoutConsumed).toBe(true);
581
- expect(process.cwd()).toBe(absoluteFullTestPath);
582
- if (process.platform === "win32") {
583
- expect(child_process.spawn).toBeCalledWith("cmd.exe", ["/d", "/s", "/c", "npx", ...jestArgs]);
584
- } else {
585
- expect(child_process.spawn).toBeCalledWith("npx", jestArgs);
586
- }
587
- expect(logSpy.mock.calls[logSpy.mock.calls.length - JEST_MESSAGE_TOTAL - 2][0]).toBe('====================');
588
- expect(logSpy.mock.calls[logSpy.mock.calls.length - JEST_MESSAGE_TOTAL - 1][0]).toBe('');
589
- expect(logSpy.mock.calls[logSpy.mock.calls.length - JEST_MESSAGE_TOTAL][0]).toBe('====================');
590
- expect(errorSpy.mock.calls.length).toBe(0);
591
- })
592
- })
593
- })
594
-
595
- describe('Remove App', () => {
596
- it('shall remove specific apps', async () => {
597
- axios.post.mockResolvedValue({ data: { success: true }, status: 200 })
598
-
599
- await runCommand('remove', '-s', 'https://finaapi.imamatek.com', '-p', '1205', '-i', '1', '-n', 'myApp')
600
-
601
- expect(axios.post.mock.calls).toHaveLength(1)
602
- expect(axios.post).toHaveBeenCalledWith(
603
- 'https://finaapi.imamatek.com:1205/fina/rest/TOrmMethod/%22deleteApp%22',
604
- { _parameters: [1, 'myapp'] },
605
- { headers: { 'Content-Type': 'text/plain' } }
606
- )
607
- expect(logSpy).toHaveBeenCalledWith('myapp removed')
608
- })
609
-
610
- it('shall remove multiple apps', async () => {
611
- axios.post.mockResolvedValue({ data: { success: true, _f: 'notExistApp' }, status: 200 })
612
-
613
- await runCommand('remove', '-i', '2', '-s', 'https://www.AA.io', '-p', '2708', '-n', 'firstApp, notExistApp, secondApp')
614
-
615
- expect(axios.post.mock.calls).toHaveLength(1)
616
- expect(axios.post).toHaveBeenCalledWith(
617
- 'https://www.AA.io:2708/fina/rest/TOrmMethod/%22deleteApp%22',
618
- { _parameters: [2, 'firstapp,notexistapp,secondapp'] },
619
- { headers: { 'Content-Type': 'text/plain' } }
620
- )
621
- expect(logSpy).toHaveBeenCalledWith('firstapp removed')
622
- expect(logSpy).toHaveBeenCalledWith('secondapp removed')
623
- expect(logSpy).toHaveBeenCalledWith('notexistapp not found')
624
- })
625
-
626
- it('shall remove all apps', async () => {
627
- axios.post.mockResolvedValue({ data: { success: true }, _f: '', status: 200 })
628
-
629
- await runCommand('remove', '-s', 'https://finaapi.imamatek.com', '-p', '1205', '-i', '2')
630
-
631
- expect(axios.post.mock.calls).toHaveLength(1)
632
- expect(axios.post).toHaveBeenCalledWith(
633
- 'https://finaapi.imamatek.com:1205/fina/rest/TOrmMethod/%22deleteApp%22',
634
- { _parameters: [2, ''] },
635
- { headers: { 'Content-Type': 'text/plain' } }
636
- )
637
- expect(logSpy).toHaveBeenCalledWith('All apps removed')
638
- })
639
- })
640
-
641
- it('Shall have options', async () => {
642
- jest.spyOn(process, 'exit').mockImplementation()
643
- const appModule = await import('../bin/app.js')
644
- expect(appModule.options).toStrictEqual({
645
- "s": {
646
- alias: "server",
647
- describe: `API or Server URL (ex: ${env.BIZA_SERVER_LINK} or http://192.168.1.1 or https://finaapi.imamatek.com)`,
648
- type: "string",
649
- demandOption: true,
650
- default: 'http://localhost'
651
- },
652
- "i": {
653
- alias: "dbIndex",
654
- default: 2,
655
- describe: "database index",
656
- type: "number",
657
- demandOption: false
658
- },
659
- "sub": {
660
- alias: "subdomain",
661
- describe: "Subdomain",
662
- type: "string",
663
- demandOption: false
664
- },
665
- "p": {
666
- alias: "apiPort",
667
- default: 212,
668
- describe: "FINA API Port",
669
- type: "number",
670
- demandOption: false,
671
- }
672
- })
673
- expect(appModule.addCommandOptions).toStrictEqual({
674
- 'd': {
675
- alias: "workingDir",
676
- describe: "Path to templates directory",
677
- type: "string",
678
- demandOption: false,
679
- default: process.cwd(),
680
- },
681
- 'v': {
682
- alias: "verbose",
683
- describe: "Print info to console",
684
- type: "boolean",
685
- demandOption: false,
686
- default: false,
687
- }
688
- })
689
- expect(appModule.removeCommandOptions).toStrictEqual({
690
- 'n': {
691
- alias: "appName",
692
- describe: "Application name",
693
- type: "string",
694
- demandOption: true,
695
- default: ""
696
- }
697
- })
698
- })
699
- })
700
-
701
-
702
-
703
-
1028
+ })`,
1029
+ );
1030
+
1031
+ await runJest();
1032
+
1033
+ expect(stdoutConsumed).toBe(true);
1034
+ expect(process.cwd()).toBe(absoluteFullTestPath);
1035
+ if (process.platform === "win32") {
1036
+ expect(child_process.spawn).toBeCalledWith("cmd.exe", [
1037
+ "/d",
1038
+ "/s",
1039
+ "/c",
1040
+ "npx",
1041
+ ...jestArgs,
1042
+ ]);
1043
+ } else {
1044
+ expect(child_process.spawn).toBeCalledWith("npx", jestArgs);
1045
+ }
1046
+ expect(
1047
+ logSpy.mock.calls[
1048
+ logSpy.mock.calls.length - JEST_MESSAGE_TOTAL - 2
1049
+ ][0],
1050
+ ).toBe("====================");
1051
+ expect(
1052
+ logSpy.mock.calls[
1053
+ logSpy.mock.calls.length - JEST_MESSAGE_TOTAL - 1
1054
+ ][0],
1055
+ ).toBe("");
1056
+ expect(
1057
+ logSpy.mock.calls[
1058
+ logSpy.mock.calls.length - JEST_MESSAGE_TOTAL
1059
+ ][0],
1060
+ ).toBe("====================");
1061
+ expect(errorSpy.mock.calls.length).toBe(0);
1062
+ });
1063
+ });
1064
+ });
1065
+
1066
+ describe("Remove App", () => {
1067
+ it("shall remove specific apps", async () => {
1068
+ axios.post.mockResolvedValue({
1069
+ data: { success: true },
1070
+ status: 200,
1071
+ });
1072
+
1073
+ await runCommand(
1074
+ "remove",
1075
+ "-s",
1076
+ "https://finaapi.imamatek.com",
1077
+ "-p",
1078
+ "1205",
1079
+ "-i",
1080
+ "1",
1081
+ "-n",
1082
+ "myApp",
1083
+ );
1084
+
1085
+ expect(axios.post.mock.calls).toHaveLength(1);
1086
+ expect(axios.post).toHaveBeenCalledWith(
1087
+ "https://finaapi.imamatek.com:1205/fina/rest/TOrmMethod/%22deleteApp%22",
1088
+ { _parameters: [1, "myapp"] },
1089
+ { headers: { "Content-Type": "text/plain" } },
1090
+ );
1091
+ expect(logSpy).toHaveBeenCalledWith("myapp removed");
1092
+ });
1093
+
1094
+ it("shall remove multiple apps", async () => {
1095
+ axios.post.mockResolvedValue({
1096
+ data: { success: true, _f: "notExistApp" },
1097
+ status: 200,
1098
+ });
1099
+
1100
+ await runCommand(
1101
+ "remove",
1102
+ "-i",
1103
+ "2",
1104
+ "-s",
1105
+ "https://www.AA.io",
1106
+ "-p",
1107
+ "2708",
1108
+ "-n",
1109
+ "firstApp, notExistApp, secondApp",
1110
+ );
1111
+
1112
+ expect(axios.post.mock.calls).toHaveLength(1);
1113
+ expect(axios.post).toHaveBeenCalledWith(
1114
+ "https://www.AA.io:2708/fina/rest/TOrmMethod/%22deleteApp%22",
1115
+ { _parameters: [2, "firstapp,notexistapp,secondapp"] },
1116
+ { headers: { "Content-Type": "text/plain" } },
1117
+ );
1118
+ expect(logSpy).toHaveBeenCalledWith("firstapp removed");
1119
+ expect(logSpy).toHaveBeenCalledWith("secondapp removed");
1120
+ expect(logSpy).toHaveBeenCalledWith("notexistapp not found");
1121
+ });
1122
+
1123
+ it("shall remove all apps", async () => {
1124
+ axios.post.mockResolvedValue({
1125
+ data: { success: true },
1126
+ _f: "",
1127
+ status: 200,
1128
+ });
1129
+
1130
+ await runCommand(
1131
+ "remove",
1132
+ "-s",
1133
+ "https://finaapi.imamatek.com",
1134
+ "-p",
1135
+ "1205",
1136
+ "-i",
1137
+ "2",
1138
+ );
1139
+
1140
+ expect(axios.post.mock.calls).toHaveLength(1);
1141
+ expect(axios.post).toHaveBeenCalledWith(
1142
+ "https://finaapi.imamatek.com:1205/fina/rest/TOrmMethod/%22deleteApp%22",
1143
+ { _parameters: [2, ""] },
1144
+ { headers: { "Content-Type": "text/plain" } },
1145
+ );
1146
+ expect(logSpy).toHaveBeenCalledWith("All apps removed");
1147
+ });
1148
+ });
1149
+
1150
+ it("Shall have options", async () => {
1151
+ jest.spyOn(process, "exit").mockImplementation();
1152
+ const appModule = await import("../bin/app.js");
1153
+ expect(appModule.options).toStrictEqual({
1154
+ s: {
1155
+ alias: "server",
1156
+ describe: `API or Server URL (ex: ${env.BIZA_SERVER_LINK} or http://192.168.1.1 or https://finaapi.imamatek.com)`,
1157
+ type: "string",
1158
+ demandOption: true,
1159
+ default: "http://localhost",
1160
+ },
1161
+ i: {
1162
+ alias: "dbIndex",
1163
+ default: 2,
1164
+ describe: "database index",
1165
+ type: "number",
1166
+ demandOption: false,
1167
+ },
1168
+ sub: {
1169
+ alias: "subdomain",
1170
+ describe: "Subdomain",
1171
+ type: "string",
1172
+ demandOption: false,
1173
+ },
1174
+ p: {
1175
+ alias: "apiPort",
1176
+ default: 212,
1177
+ describe: "FINA API Port",
1178
+ type: "number",
1179
+ demandOption: false,
1180
+ },
1181
+ });
1182
+ expect(appModule.addCommandOptions).toStrictEqual({
1183
+ d: {
1184
+ alias: "workingDir",
1185
+ describe: "Path to templates directory",
1186
+ type: "string",
1187
+ demandOption: false,
1188
+ default: process.cwd(),
1189
+ },
1190
+ v: {
1191
+ alias: "verbose",
1192
+ describe: "Print info to console",
1193
+ type: "boolean",
1194
+ demandOption: false,
1195
+ default: false,
1196
+ },
1197
+ });
1198
+ expect(appModule.removeCommandOptions).toStrictEqual({
1199
+ n: {
1200
+ alias: "appName",
1201
+ describe: "Application name",
1202
+ type: "string",
1203
+ demandOption: true,
1204
+ default: "",
1205
+ },
1206
+ });
1207
+ });
1208
+ });