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/.editorconfig +16 -0
- package/bin/app.js +659 -256
- package/bin/hub.js +185 -134
- package/bin/hubEvent.js +412 -284
- package/bin/log/debug.log +12 -0
- package/bin/log/error.log +12 -0
- package/bin/log/exception.log +6 -0
- package/bin/log/info.log +12 -0
- package/log/debug.log +181 -0
- package/log/error.log +7 -0
- package/log/exception.log +3 -0
- package/log/info.log +6 -0
- package/package.json +70 -71
- package/tests/app.test.js +1093 -588
- package/tests/hubPublish.test.js +231 -0
package/tests/app.test.js
CHANGED
|
@@ -1,122 +1,140 @@
|
|
|
1
|
-
import fs from "fs"
|
|
2
|
-
import { jest } from
|
|
3
|
-
import { Duplex } from
|
|
4
|
-
import { finished } from
|
|
5
|
-
import { createDecipheriv } from
|
|
6
|
-
import * as tar from "tar"
|
|
7
|
-
import { env } from "../envs/env.js"
|
|
8
|
-
import path from "node:path"
|
|
9
|
-
|
|
10
|
-
describe(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
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
|
-
|
|
912
|
+
await runJest();
|
|
470
913
|
|
|
471
|
-
|
|
472
|
-
|
|
914
|
+
abortExpect(jestArgs);
|
|
915
|
+
});
|
|
473
916
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
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
|
-
|
|
479
|
-
|
|
921
|
+
mockValidTemplates(appName, mockScripts);
|
|
922
|
+
fs.mkdirSync(fullTestPath + "/test", { recursive: true });
|
|
480
923
|
|
|
481
|
-
|
|
924
|
+
await runJest();
|
|
482
925
|
|
|
483
|
-
|
|
484
|
-
|
|
926
|
+
continueExpect(jestArgs);
|
|
927
|
+
});
|
|
485
928
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
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
|
-
|
|
491
|
-
|
|
933
|
+
mockValidTemplates(appName, mockScripts);
|
|
934
|
+
await runJest();
|
|
492
935
|
|
|
493
|
-
|
|
494
|
-
|
|
936
|
+
continueExpect(jestArgsWithPassNoTest);
|
|
937
|
+
});
|
|
495
938
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
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
|
-
|
|
501
|
-
|
|
502
|
-
|
|
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
|
-
|
|
952
|
+
await runJest();
|
|
507
953
|
|
|
508
|
-
|
|
509
|
-
|
|
954
|
+
abortExpect(jestArgs);
|
|
955
|
+
});
|
|
510
956
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
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
|
-
|
|
516
|
-
|
|
517
|
-
|
|
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
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
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
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
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
|
+
});
|