biz-a-cli 2.3.63 → 2.3.65
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/bin/app.js +145 -131
- package/bin/log/debug.log +5 -0
- package/bin/log/error.log +5 -0
- package/bin/log/exception.log +0 -0
- package/bin/log/info.log +5 -0
- package/callbackController.js +4 -2
- package/log/debug.log +180 -0
- package/log/error.log +5 -0
- package/log/exception.log +0 -0
- package/log/info.log +5 -0
- package/package.json +14 -4
- package/scheduler/datalib.js +9 -4
- package/tests/app.test.js +141 -113
- package/tests/data.test.js +139 -84
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "biz-a-cli",
|
|
3
3
|
"nameDev": "biz-a-cli-dev",
|
|
4
|
-
"version": "2.3.
|
|
4
|
+
"version": "2.3.65",
|
|
5
5
|
"versionDev": "0.0.34",
|
|
6
6
|
"description": "",
|
|
7
7
|
"main": "bin/index.js",
|
|
@@ -56,8 +56,18 @@
|
|
|
56
56
|
"!<rootDir>/tests/mockData",
|
|
57
57
|
"!<rootDir>/tests/mockData/**"
|
|
58
58
|
],
|
|
59
|
-
"testPathIgnorePatterns": [
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
"testPathIgnorePatterns": [
|
|
60
|
+
"<rootDir>/node_modules",
|
|
61
|
+
"<rootDir>/tests/mockData"
|
|
62
|
+
],
|
|
63
|
+
"watchPathIgnorePatterns": [
|
|
64
|
+
"<rootDir>/node_modules",
|
|
65
|
+
"<rootDir>/tests/mockData"
|
|
66
|
+
],
|
|
67
|
+
"coveragePathIgnorePatterns": [
|
|
68
|
+
"<rootDir>/node_modules",
|
|
69
|
+
"<rootDir>/tests/mockData",
|
|
70
|
+
"<rootDir>/tests/mockData/**"
|
|
71
|
+
]
|
|
62
72
|
}
|
|
63
73
|
}
|
package/scheduler/datalib.js
CHANGED
|
@@ -12,6 +12,7 @@ dayjs.default.extend(customParseFormat)
|
|
|
12
12
|
dayjs.default.extend(timezone)
|
|
13
13
|
dayjs.default.extend(utc)
|
|
14
14
|
|
|
15
|
+
export const AXIOS_TIMEOUT = 30 * 1000;
|
|
15
16
|
const logger = createLogger({
|
|
16
17
|
level: 'info',
|
|
17
18
|
transports: [
|
|
@@ -66,7 +67,8 @@ export function getTableObj(model, tableName) {
|
|
|
66
67
|
export function options(apiConfig) {
|
|
67
68
|
return {
|
|
68
69
|
headers: { 'content-type': 'text/plain' },
|
|
69
|
-
params: { subdomain: apiConfig.subdomain }
|
|
70
|
+
params: { subdomain: apiConfig.subdomain },
|
|
71
|
+
timeout: apiConfig.timeout ?? AXIOS_TIMEOUT //SCY BZ 4316
|
|
70
72
|
};
|
|
71
73
|
}
|
|
72
74
|
|
|
@@ -163,17 +165,21 @@ export async function extractFunctionScript(selectedConfig, data) {
|
|
|
163
165
|
const scriptFn = runScriptInThisContext(data[0].script);
|
|
164
166
|
if (!scriptFn().functions) return
|
|
165
167
|
|
|
168
|
+
const axiosInstance = axios.create({ timeout: AXIOS_TIMEOUT });
|
|
169
|
+
|
|
166
170
|
let lib = {
|
|
167
|
-
axios: axios.default,
|
|
171
|
+
// axios: axios.default,
|
|
172
|
+
axios: axiosInstance, //SCY BZ 4316
|
|
168
173
|
dayjs: dayjs.default,
|
|
169
174
|
crypto: crypto,
|
|
170
175
|
log: logger,
|
|
171
|
-
data: {sendModel, queryData, crudData, genId, getUrlAndParam}
|
|
176
|
+
data: { sendModel, queryData, crudData, genId, getUrlAndParam }
|
|
172
177
|
}
|
|
173
178
|
|
|
174
179
|
if (scriptFn().functions.useLibrary) {
|
|
175
180
|
Object.assign(lib, await setLibrary(config, scriptFn().functions.useLibrary(), lib));
|
|
176
181
|
}
|
|
182
|
+
|
|
177
183
|
return scriptFn(lib).functions;
|
|
178
184
|
}
|
|
179
185
|
|
|
@@ -187,7 +193,6 @@ export function getQueryDataPrmsParameters(column, value) {
|
|
|
187
193
|
|
|
188
194
|
export async function queryDataPromise(config, column, value) {
|
|
189
195
|
let param = getQueryDataPrmsParameters(column, value);
|
|
190
|
-
// let param = getQueryDataPrmsParameters(d);
|
|
191
196
|
|
|
192
197
|
return await queryData(param, config, true);
|
|
193
198
|
}
|
package/tests/app.test.js
CHANGED
|
@@ -6,14 +6,15 @@ import { createDecipheriv } from 'node:crypto'
|
|
|
6
6
|
import * as tar from "tar"
|
|
7
7
|
import { env } from "../envs/env.js"
|
|
8
8
|
|
|
9
|
-
describe('Biz-A Apps CLI', ()=>{
|
|
9
|
+
describe('Biz-A Apps CLI', () => {
|
|
10
|
+
let originalOptions, originalCWD, axios, child_process;
|
|
11
|
+
let returnedCode = 0;
|
|
10
12
|
|
|
11
|
-
let originalOptions, originalCWD, axios
|
|
12
13
|
const logSpy = jest.spyOn(global.console, 'log').mockImplementation()
|
|
13
14
|
const errorSpy = jest.spyOn(global.console, 'error').mockImplementation()
|
|
14
15
|
|
|
15
|
-
async function runCommand(...options){
|
|
16
|
-
process.argv[1] = process.cwd()+'\\bin' // mock the key root folder
|
|
16
|
+
async function runCommand(...options) {
|
|
17
|
+
process.argv[1] = process.cwd() + '\\bin' // mock the key root folder
|
|
17
18
|
process.argv = [process.argv[0], process.argv[1], ...options]
|
|
18
19
|
await import('../bin/app.js')
|
|
19
20
|
|
|
@@ -25,79 +26,90 @@ describe('Biz-A Apps CLI', ()=>{
|
|
|
25
26
|
await new Promise(process.nextTick) // wait for all import promise (including ES6 import promises) to be resolved
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
beforeEach(async ()=>{
|
|
29
|
+
beforeEach(async () => {
|
|
29
30
|
originalOptions = process.argv
|
|
30
31
|
originalCWD = process.cwd()
|
|
31
32
|
|
|
32
33
|
// mock default export first with unstable_mockModule
|
|
33
|
-
jest.unstable_mockModule('axios', ()=>{return {default: jest.fn()}})
|
|
34
|
+
jest.unstable_mockModule('axios', () => { return { default: jest.fn() } })
|
|
34
35
|
axios = (await import('axios')).default
|
|
35
36
|
|
|
36
37
|
// then we can mock classes, functions, variables, and objects inside mocked module when doing dynamic import using jest.fn()
|
|
37
38
|
axios.get = jest.fn()
|
|
38
39
|
axios.post = jest.fn()
|
|
40
|
+
|
|
41
|
+
jest.unstable_mockModule('node:child_process', () => ({
|
|
42
|
+
spawn: jest.fn().mockReturnValue({
|
|
43
|
+
on: jest.fn((event, callback) => {
|
|
44
|
+
if (event === "close") {
|
|
45
|
+
callback(returnedCode);
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
}));
|
|
50
|
+
child_process = (await import('node:child_process'));
|
|
39
51
|
})
|
|
40
52
|
|
|
41
|
-
afterEach(()=>{
|
|
53
|
+
afterEach(() => {
|
|
42
54
|
jest.resetModules()
|
|
43
55
|
jest.resetAllMocks()
|
|
44
56
|
process.argv = originalOptions
|
|
45
57
|
process.chdir(originalCWD)
|
|
46
58
|
})
|
|
47
59
|
|
|
48
|
-
describe('Add App', ()=>{
|
|
60
|
+
describe('Add App', () => {
|
|
49
61
|
const mockDataFolder = './tests/mockData/'
|
|
50
62
|
|
|
51
|
-
function mockValidTemplates(appName, scriptList){
|
|
52
|
-
fs.rmSync(mockDataFolder+'/'+appName, {force: true, recursive: true})
|
|
53
|
-
fs.mkdirSync(mockDataFolder+'/'+appName, {recursive: true})
|
|
54
|
-
for (const [key, value] of Object.entries(scriptList)){
|
|
55
|
-
fs.writeFileSync(mockDataFolder+'/'+appName + '/' + key, value.act)
|
|
63
|
+
function mockValidTemplates(appName, scriptList) {
|
|
64
|
+
fs.rmSync(mockDataFolder + '/' + appName, { force: true, recursive: true })
|
|
65
|
+
fs.mkdirSync(mockDataFolder + '/' + appName, { recursive: true })
|
|
66
|
+
for (const [key, value] of Object.entries(scriptList)) {
|
|
67
|
+
fs.writeFileSync(mockDataFolder + '/' + appName + '/' + key, value.act)
|
|
56
68
|
}
|
|
57
69
|
}
|
|
58
70
|
|
|
59
|
-
function mockIssuerKeyResponse(){
|
|
71
|
+
function mockIssuerKeyResponse() {
|
|
60
72
|
axios.get.mockResolvedValue({
|
|
61
|
-
data:{
|
|
73
|
+
data: {
|
|
62
74
|
// this data containing 'mockAESKey', which is encrypted using CLI Private key
|
|
63
75
|
data: 'eyJpc3N1ZXIiOnsia2V5IjoidEVmZlRwbGpsRW9seVRVcnpEQUs4b05mZEg0T2p2VE9MOHZ4c0p3MkRISC9LY09WV2t0SG5kWmxxUXJURUdoWGtoWUFtQjBobmw1RU5xdFh2TXNjcHFCMnd6eXZJUy9OdUpxeFdBQmE4Y21QbXYyUHBqRFV3eUd5LzJzbFBybG00aGJjbjJWamFPRVJiMHFpclZFNGhZVDluNlFwSnR1ZUp1NXcrVG9JN0V1eC9wdkZHeWVaOU1ham5hSTVtcVliZkNsT0pEZnV3MkpuU1RGNk1kbWg0MURualNuYk5nWkJhSjF2VWw2U1lmVDREeVJoVTkrUER6UTRMZ0xPZXlVYUlTVHQwWnpQWDIyeGI5Nlo3NlE0ZFNGUzhwRzRJRk0vMFpHbnI5eXB0NmNhRFlseW5VUWVFOHVJMklJMnpZcjFNdE12RnkzaDEyUU5Za0tLS3NaaVNRV00vQXRPTm5sa00rcjFKSFpMZW42b3lKYlV1cEgvRmZxQmViQWdRUThLcUVENnN2UlljSXBKOWh3UlJDb2RvNmlHaVVTMUVZZnh2N3pFMDhwL281Q2JmdFRKTXpISjh3T1JZbkdaTDh5cWttT2V0Y3dJL0p6U2dGVDNST2E5V3ZMRkFHWkIrcWdEOTBNeGhwT3kyVzRlbS9lUVBpeHNadDR2UGl2T1l0QVZYZTBLL0lJR0tlZ3VRZ0NzeUZENkRmajQxYkNXbm9oUEgzNWl1Y2h1cGxKVUhsSmszMTRqLytkbTBieEZ6MWFzdDBKanVaRFp3bkVSeVJVbTNFSWZNU2kxVGRRaXRWRjI2bFJ3a1NwK0VSRHh0ZllHWCs3V1U3OUhyUFF3MHBuU3NzU29FbVFzQ2VXU0dFc1AwbEtpTlBjLzBCeDNzbjdNMnJpMzJmZUZLRHM9In0sImFjcXVpcmVyIjp7ImRhdGEiOiJtT1NabzB0N0ZZb2M2a0tuZlNURk04S3VHcUtzLzU2MGtERGdldDMrRW9UTUZZaW5sbThwRFRqc2xVK2VoWnRidk1TejJ1bmNPck5rQ1phQy9LM0JrbmM5T3BZTDZ2SXVGNmY2alZrbjJDS25xbnRnUXBqUkY4T2dYK3NEKzhLbnBMVll2QmhMdkJydjI2RTBHTnk5YTNwbCt2MHNna3EycXJoeUJCeVc3VnpRL3h1Y2FCdDRrUjE0N2FpaTlSaThvUXEzRU1kZ3BPN2crYmxLMFk3eGt5ekQ2YU1JSDFSR1lSMTgwaU94akVqYVpiVE1uQkUwS1RJUnBEMnkwL25MTU5VTHZTUlBraVMyTUh5cmI1QWJCbWNQR1hzVUVvZFc0OHM0QVdmS2hZRldLbzV5anQwZlhCVCtUbEdaWmYrcDJINnRzSVNUYjZSKzlHWFk5c1EzNE5IdmFCRTZtNzlURUVQWTNkeUl3Vm5WQTIzR0FqL211bzI5M2dMTlp0R090ZWcrR1VlWXIzWXViQUZnL2VrN0NyTThGdGRQZC92R3NjdlJ4bkNEYnhmU3U3YXUzUFd1RFB5djk0ZjhXaXVMcWlKUGFQRmFZaUE4SW1lZklnY2ROKzMrTWZDRkdyR1dsWHlZM1pxVVI5NEhteGVjd1Q5Q256N2g2NHc0L3c0b0c5bU9CWmVsRk14dzlxUVlOWTZ5ZzRrakRBa2xUK29GMUszd213VUpPVTlhc3NCVEQ1SXZONlJRSEV0SU02ZmhKY1R0Zm5tK3l0Y201b3hjY3ZrN2tlVDExR2xjVi9Oa3EwNDRKb0NFL0drbzV2TWovQ29ERklJd0VjSENnQ1RvVGE0SForcWUzTWc2Wm1CazhvL2pwV3VFbTF5anJMcUdMSmFOTEJXNjlTUT0iLCJzaWduYXR1cmUiOiJzZDJXYnNDVy9oeXlMMnIzcmVhSFcyZndzVy85THVKNkp6MW0zUUNkWVA1NkZUU1ovSHM4c0Z6V2hmdVhtS1JJdEd5cnA4RGxMNTJkVkw1eTNnYmRIc0VoMDRITDhiWXIwS2lFU2RlbnVPMnoyZkdBWTlsclVjRC92alpZNlAwOHBVQTBEbXIreTk2UjQzdGVhTnZaMkplMklMWW1GZzdNOGF2cDZJZWk5WllzUmxyMFg2TmlzcUlXTWxDeHZOLzdadGNzcWQ3QndtWEhZRmlQWVkxaC9KTUtJdEREZnBneGgzU3RITlZQb01JU09FcTVkbGVRMjJNaTlPL2trZ2pkcmlBRDZsZ1hCZ214Q0FDaE5VQzVEZW5vUVIwQUlTREMvTW9udVA5ZldoMktwMkV2SlRGakR0eUJFdFZZekZZME82NEcxOVNhSFRnRmdxTFd2eXJDL2gzK1BqREIyTTdxOFBXOWVDNjlwM0ZCdUt1R0U3aTkrTmpKeWh4eFJoWWlxbW5MWjFxN3lFUzNudkIrNjJHRDZrUlR6YU5zdjFCUTRFSVpBR1NSaUF4d3o0aWo0ZldtdWJhaEd2WGlseGUzYzE1S09aa0RzcWRpQklIL1RWYzc1TUVBUVYrVVNlbXNEUnF2eUZ3ZFBobW9oM25KeVdmbFdsMU05eVNhdU1MaUFPV2c1bXhhSkdqQ0NtMGV5SHZaWUFFOFNWUjViWFJOZjIrNkpBQUJ2Mk5iSEJIVEJxM3V1cWpEVFZZdFZTRmQ3QmsvUS9NdVRiQnNjYUNqVUtybmtMcUpDTEw4akV0NnJlQnJLdlNPZll1c0txWHBWRmZMV1dHd1ZWSEViTzZVQ0xYTmIzY0c1MXRuNmpOS3NmN29wQXhBTFhUNFRXVjk0RjB6KzRpMjNYOD0ifX0=',
|
|
64
76
|
// sign using BizA Server PrivateKey, to be verified by BizA CLI using Server PublicKey
|
|
65
77
|
signature: 'Y9Vs9mOV5fqjcX1ZQh8N0vubV6J5+rx1Lvcrb8T9nBONMJIS74lqRdnO4a84rDCXdda0QOJs5750OdE69/ognriSqdvJ5qjb4sjYAuzXmbxGOuU4ptWMAl9CVfi7JdISdXaDMn5o2E9SuuQLwBMUvX6a9p93/L+TK/pSLuv3cwZqaCtiyhsZV1Q1mSz5xpEfGZR//0Vdaj7pZYZ7CDjQAiq6tMc1Mm7azDmxEwDmNqTMvPKJusGTTHvIFH9MbGTsHgq7DzhsGBVW0fyQGx4ycqB5u9D9j9YffjdlFZ3xncxgq/NVHJzHPbosrG2blW3yWr0Sg4P34AAOhNMtAbbrZfa5HYQrGLB5MbOY3s8EiXItufKzz5xTKDVdLER7kWk6th+a7c548TfqWkrkepPEvgBgmGoMw/PvXFsMVMKqRoF31lN7tumKnVyVzz/EwrsVVc7QrsJxfODHgU0bNNxi6gEb+prqKy0ZWmWwtf7coLWq/ybC2yFMrqUjymoJBGvNoLBURihD2m645+qudNlcJlLoyTs8B3KRCwFKtBH2X9eW5ZzDpIQAXDge6Bz/668Xg6DB8TzvpNr0sPOkuXXSKhQnb0SkR7QTHzpFDcg88Ur9Nnnh9aSZZRj1nRo5LawNto+9684SzpIfbjzAnbTHLQ6oY28SIdihKVjc0wj9BZk='
|
|
66
78
|
}
|
|
67
79
|
})
|
|
68
|
-
axios.post.mockResolvedValue({data: {success: true}, status: 200})
|
|
80
|
+
axios.post.mockResolvedValue({ data: { success: true }, status: 200 })
|
|
69
81
|
}
|
|
70
82
|
|
|
71
|
-
beforeAll(()=>{
|
|
72
|
-
fs.rmSync(mockDataFolder, {recursive: true, force: true})
|
|
83
|
+
beforeAll(() => {
|
|
84
|
+
fs.rmSync(mockDataFolder, { recursive: true, force: true })
|
|
73
85
|
})
|
|
74
86
|
|
|
75
|
-
afterAll(()=>{
|
|
76
|
-
fs.rmSync(mockDataFolder, {recursive: true, force: true})
|
|
87
|
+
afterAll(() => {
|
|
88
|
+
fs.rmSync(mockDataFolder, { recursive: true, force: true })
|
|
77
89
|
})
|
|
78
90
|
|
|
79
91
|
const stressTestCount = 10;
|
|
80
|
-
it.each(Array(stressTestCount).fill().map((v,i)=>i+1))(`shall compress and encrypt app scripts (stress test %p of ${stressTestCount})`, async (testNo)=>{
|
|
92
|
+
it.each(Array(stressTestCount).fill().map((v, i) => i + 1))(`shall compress and encrypt app scripts (stress test %p of ${stressTestCount})`, async (testNo) => {
|
|
81
93
|
const mockScripts = {
|
|
82
|
-
'a.js'
|
|
94
|
+
'a.js': {
|
|
83
95
|
act: 'get = function () {return {modelA: {}}}',
|
|
84
96
|
exp: '{"modelA":{}}'
|
|
85
97
|
},
|
|
86
|
-
'b.js'
|
|
98
|
+
'b.js': {
|
|
87
99
|
act: 'const get = function () {\nreturn {"modelB": {id: null}}};',
|
|
88
100
|
exp: '{"modelB":{"id":null}}'
|
|
89
101
|
},
|
|
90
|
-
'c.js'
|
|
102
|
+
'c.js': {
|
|
91
103
|
act: 'let get = function () {return {modelC: {}, function: {func: () => { \nreturn ["lib"];\n}\n}}};',
|
|
92
104
|
exp: '{"modelC":{},"function":{"func":["window.Function","","return[\\\"lib\\\"]"]}}'
|
|
93
105
|
},
|
|
94
|
-
'd.js'
|
|
106
|
+
'd.js': {
|
|
95
107
|
act: 'var get = function () {return {model: {}, tableName: "", fields: [], function: {}}};\nmodule.exports = get;',
|
|
96
108
|
exp: '{"model":{},"tableName":"","fields":[],"function":{}}'
|
|
97
109
|
},
|
|
98
|
-
'menu.json'
|
|
110
|
+
'menu.json': {
|
|
99
111
|
// act: JSON.stringify([
|
|
100
|
-
|
|
112
|
+
act: `[
|
|
101
113
|
{"menuName": "", "caption": "Home", "link": ["./main"],"subMenu": []},
|
|
102
114
|
{"menuName": "", "caption": "Personalia","link": [], "subMenu": [
|
|
103
115
|
{"menuName": "","caption": "Data Master","link": [], "subMenu": []},
|
|
@@ -191,7 +203,7 @@ describe('Biz-A Apps CLI', ()=>{
|
|
|
191
203
|
tar.t({
|
|
192
204
|
strict: true
|
|
193
205
|
, sync: true
|
|
194
|
-
, onReadEntry: entry=>{
|
|
206
|
+
, onReadEntry: entry => {
|
|
195
207
|
const chunks = [];
|
|
196
208
|
entry.on('data', chunk => chunks.push(chunk));
|
|
197
209
|
entry.on('end', () => {
|
|
@@ -205,10 +217,10 @@ describe('Biz-A Apps CLI', ()=>{
|
|
|
205
217
|
})
|
|
206
218
|
}
|
|
207
219
|
})
|
|
208
|
-
|
|
220
|
+
.on('finish', () => stream.end())
|
|
209
221
|
)
|
|
210
222
|
await finished(stream) // wait for untar process to finish
|
|
211
|
-
for (const [fileName, script] of Object.entries(decryptScripts)){
|
|
223
|
+
for (const [fileName, script] of Object.entries(decryptScripts)) {
|
|
212
224
|
expect(script).toBe(mockScripts[fileName].exp)
|
|
213
225
|
}
|
|
214
226
|
|
|
@@ -228,7 +240,7 @@ describe('Biz-A Apps CLI', ()=>{
|
|
|
228
240
|
expect(apiParams[3]).toHaveProperty('menu') // shall save "menu.json" as App.metadata
|
|
229
241
|
expect(apiParams[3].menu).not.toBeNull() // IV (initialize vector) using 16 random bytes
|
|
230
242
|
|
|
231
|
-
expect(postArgs[2]).toStrictEqual({"headers": {"Content-Type": "text/plain"}})
|
|
243
|
+
expect(postArgs[2]).toStrictEqual({ "headers": { "Content-Type": "text/plain" } })
|
|
232
244
|
|
|
233
245
|
expect(logSpy.mock.calls[0][0]).toContain(`Finished packing ${Object.keys(mockScripts).length} files into "./upload/${appName.toLowerCase()}.tgz"`)
|
|
234
246
|
expect(logSpy.mock.calls[1][0]).toContain(`Finished uploading "./upload/${appName.toLocaleLowerCase()}.tgz"`)
|
|
@@ -236,61 +248,61 @@ describe('Biz-A Apps CLI', ()=>{
|
|
|
236
248
|
// fs.rmSync(mockDataFolder + '/' + appName, {recursive: true, force: true})
|
|
237
249
|
})
|
|
238
250
|
|
|
239
|
-
/* it('Invalid script syntax', async ()=>{
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
251
|
+
/* it('Invalid script syntax', async ()=>{
|
|
252
|
+
mockValidTemplates({
|
|
253
|
+
'a.js' : {act: 'get = function () {return {modelA: {}}'} // missing close closure at the end
|
|
254
|
+
})
|
|
255
|
+
mockIssuerKeyResponse()
|
|
256
|
+
await runCommand('add', '-s', 'https://a.b.c', '-p', '1205', '-i', '2', '-d', appFolderName, "-v")
|
|
257
|
+
|
|
258
|
+
expect(logSpy.mock.calls.length).toBe(1)
|
|
259
|
+
expect(logSpy.mock.calls[0][0]).toBe('===================\nA.JS\n===================')
|
|
260
|
+
|
|
261
|
+
expect(errorSpy.mock.calls.length).toBe(1)
|
|
262
|
+
expect(errorSpy.mock.calls[0][0]).toBe('a.js : SyntaxError: Unexpected token: eof, expected: punc «}»')
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
it('Failed to compile script with node sandbox and ES6 Import', async ()=>{
|
|
266
|
+
mockValidTemplates({
|
|
267
|
+
'a.js' : {act: 'get = function () {return {modelA: {}}}\n modul.expor = get;\n'} // wrong "module.export"
|
|
268
|
+
})
|
|
269
|
+
mockIssuerKeyResponse()
|
|
270
|
+
await runCommand('add', '-s', 'https://a.b.c', '-p', '1205', '-i', '2', '-d', appFolderName, "-v")
|
|
271
|
+
|
|
272
|
+
expect(logSpy.mock.calls.length).toBe(5)
|
|
273
|
+
expect(logSpy.mock.calls[0][0]).toBe('===================\nA.JS\n===================')
|
|
274
|
+
expect(logSpy.mock.calls[1][0]).toBe('Minify : \nget=function(){return{modelA:{}}};modul.expor=get;')
|
|
275
|
+
expect(logSpy.mock.calls[2][0]).toBe('Running script with VM') // node sandbox (VN) error as console.log
|
|
276
|
+
expect(logSpy.mock.calls[3][0]).toBe('a.js : ReferenceError: modul is not defined')
|
|
277
|
+
expect(logSpy.mock.calls[4][0]).toBe('Running script with Import function')
|
|
278
|
+
|
|
279
|
+
expect(errorSpy.mock.calls.length).toBe(1)
|
|
280
|
+
expect(errorSpy.mock.calls[0][0]).toBe("a.js : ReferenceError: modul is not defined") // ES6 import error
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
it('Shall not allow empty script', async ()=>{
|
|
284
|
+
mockValidTemplates({
|
|
285
|
+
'a.js' : {act: 'get = function () {}'} // empty script
|
|
286
|
+
})
|
|
287
|
+
mockIssuerKeyResponse()
|
|
288
|
+
await runCommand('add', '-s', 'https://a.b.c', '-p', '1205', '-i', '2', '-d', appFolderName, "-v")
|
|
289
|
+
|
|
290
|
+
expect(logSpy.mock.calls.length).toBe(4)
|
|
291
|
+
expect(logSpy.mock.calls[0][0]).toBe('===================\nA.JS\n===================')
|
|
292
|
+
expect(logSpy.mock.calls[1][0]).toBe('Minify : \nget=function(){};')
|
|
293
|
+
expect(logSpy.mock.calls[2][0]).toBe('Running script with VM') // node sandbox (VN) error as console.log
|
|
294
|
+
expect(logSpy.mock.calls[3][0]).toBe('Running script with Import function')
|
|
295
|
+
|
|
296
|
+
expect(errorSpy.mock.calls.length).toBe(1)
|
|
297
|
+
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
|
|
298
|
+
}) */
|
|
287
299
|
|
|
288
300
|
const errorHandlingTestCases = [
|
|
289
301
|
// [test case name, scripts, expectedFn()]
|
|
290
302
|
[
|
|
291
303
|
'Invalid script syntax',
|
|
292
|
-
{act: 'get = function () {return {modelA: {}}'}, // missing close closure at the end',
|
|
293
|
-
()=>{
|
|
304
|
+
{ act: 'get = function () {return {modelA: {}}' }, // missing close closure at the end',
|
|
305
|
+
() => {
|
|
294
306
|
expect(logSpy.mock.calls.length).toBe(5)
|
|
295
307
|
expect(logSpy.mock.calls[0][0]).toBe('===================\nA.JS\n===================')
|
|
296
308
|
expect(logSpy.mock.calls[1][0]).toBe('Running script with VM') // node sandbox (VN) error as console.log
|
|
@@ -304,8 +316,8 @@ describe('Biz-A Apps CLI', ()=>{
|
|
|
304
316
|
],
|
|
305
317
|
[
|
|
306
318
|
'Failed to compile script with node sandbox and ES6 Import',
|
|
307
|
-
{act: 'get = function () {return {modelA: {}}}\n modul.expor = get;\n'}, // wrong "module.export"
|
|
308
|
-
()=>{
|
|
319
|
+
{ act: 'get = function () {return {modelA: {}}}\n modul.expor = get;\n' }, // wrong "module.export"
|
|
320
|
+
() => {
|
|
309
321
|
expect(logSpy.mock.calls.length).toBe(6)
|
|
310
322
|
expect(logSpy.mock.calls[0][0]).toBe('===================\nA.JS\n===================')
|
|
311
323
|
expect(logSpy.mock.calls[1][0]).toBe('Running script with VM') // node sandbox (VN) error as console.log
|
|
@@ -320,8 +332,8 @@ describe('Biz-A Apps CLI', ()=>{
|
|
|
320
332
|
],
|
|
321
333
|
[
|
|
322
334
|
'Shall not allow empty script',
|
|
323
|
-
{act: 'get = function () {}'}, // empty script
|
|
324
|
-
()=>{
|
|
335
|
+
{ act: 'get = function () {}' }, // empty script
|
|
336
|
+
() => {
|
|
325
337
|
expect(logSpy.mock.calls.length).toBe(5)
|
|
326
338
|
expect(logSpy.mock.calls[0][0]).toBe('===================\nA.JS\n===================')
|
|
327
339
|
expect(logSpy.mock.calls[1][0]).toBe('Running script with VM') // node sandbox (VN) error as console.log
|
|
@@ -334,15 +346,15 @@ describe('Biz-A Apps CLI', ()=>{
|
|
|
334
346
|
}
|
|
335
347
|
],
|
|
336
348
|
]
|
|
337
|
-
it.each(errorHandlingTestCases)('%p', async (name, scripts, expectedFn)=>{
|
|
349
|
+
it.each(errorHandlingTestCases)('%p', async (name, scripts, expectedFn) => {
|
|
338
350
|
const appName = 'errorHandlingTest' + errorHandlingTestCases.findIndex(testCase => testCase[0] === name)
|
|
339
|
-
mockValidTemplates(appName, {'a.js'
|
|
351
|
+
mockValidTemplates(appName, { 'a.js': scripts })
|
|
340
352
|
mockIssuerKeyResponse()
|
|
341
353
|
await runCommand('add', '-s', 'https://a.b.c', '-p', '1205', '-i', '2', '-d', mockDataFolder + '/' + appName, "-v")
|
|
342
354
|
expectedFn()
|
|
343
355
|
})
|
|
344
356
|
|
|
345
|
-
it('Shall not allow app without script template', async ()=>{
|
|
357
|
+
it('Shall not allow app without script template', async () => {
|
|
346
358
|
mockValidTemplates(expect.getState().currentTestName, {})
|
|
347
359
|
mockIssuerKeyResponse()
|
|
348
360
|
await runCommand('add', '-s', 'https://a.b.c', '-p', '1205', '-i', '2', '-d', mockDataFolder + '/' + expect.getState().currentTestName, "-v")
|
|
@@ -353,89 +365,106 @@ describe('Biz-A Apps CLI', ()=>{
|
|
|
353
365
|
expect(errorSpy.mock.calls[0][0]).toBe('Nothing to upload. Please recheck your app folder.')
|
|
354
366
|
})
|
|
355
367
|
|
|
356
|
-
it('Shall recommend command', async ()=>{
|
|
357
|
-
const exitSpy = jest.spyOn(process, 'exit').mockImplementation(()=>{})
|
|
368
|
+
it('Shall recommend command', async () => {
|
|
369
|
+
const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => { })
|
|
358
370
|
await runCommand('Add', '-s', 'https://a.b.c', '-p', '1205', '-i', '2', '-d', mockDataFolder + '/NotExistFolder', "-v")
|
|
359
371
|
expect(logSpy.mock.calls.length).toBe(0)
|
|
360
372
|
expect(exitSpy).toHaveBeenCalledWith(1) // exit code = 1
|
|
361
|
-
expect(errorSpy.mock.calls[errorSpy.mock.calls.length-1][0]).toBe('Did you mean add?')
|
|
373
|
+
expect(errorSpy.mock.calls[errorSpy.mock.calls.length - 1][0]).toBe('Did you mean add?')
|
|
362
374
|
})
|
|
363
375
|
|
|
364
|
-
it('Shall have minimun one command', async ()=>{
|
|
376
|
+
it('Shall have minimun one command', async () => {
|
|
365
377
|
const exitSpy = jest.spyOn(process, 'exit').mockImplementation()
|
|
366
378
|
await runCommand('-s', 'https://a.b.c', '-i', 2)
|
|
367
379
|
expect(logSpy.mock.calls.length).toBe(0)
|
|
368
380
|
expect(exitSpy).toHaveBeenCalledWith(1)
|
|
369
|
-
expect(errorSpy.mock.calls[errorSpy.mock.calls.length-1][0]).toBe('You need at least one command before moving on')
|
|
381
|
+
expect(errorSpy.mock.calls[errorSpy.mock.calls.length - 1][0]).toBe('You need at least one command before moving on')
|
|
370
382
|
})
|
|
371
383
|
|
|
372
|
-
it('Shall use strict command and options', async ()=>{
|
|
384
|
+
it('Shall use strict command and options', async () => {
|
|
373
385
|
const exitSpy = jest.spyOn(process, 'exit').mockImplementation()
|
|
374
386
|
await runCommand('Unknown Command', '-s', 'https://a.b.c', '-i', 2)
|
|
375
387
|
expect(logSpy.mock.calls.length).toBe(0)
|
|
376
388
|
expect(exitSpy).toHaveBeenCalledWith(1)
|
|
377
|
-
expect(errorSpy.mock.calls[errorSpy.mock.calls.length-1][0]).toBe('Unknown argument: Unknown Command')
|
|
389
|
+
expect(errorSpy.mock.calls[errorSpy.mock.calls.length - 1][0]).toBe('Unknown argument: Unknown Command')
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
it('Shall not continue add biz a template if code is not zero', async () => { //SCY BZ 4331
|
|
393
|
+
returnedCode = 1;
|
|
394
|
+
|
|
395
|
+
const mockScripts = {
|
|
396
|
+
'a.js': {
|
|
397
|
+
act: 'get = function () {return {modelA: {}}}',
|
|
398
|
+
exp: '{"modelA":{}}'
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
const appName = 'compressStressTest1';
|
|
402
|
+
|
|
403
|
+
mockValidTemplates(appName, mockScripts);
|
|
404
|
+
await runCommand('add', '-s', 'https://a.b.c', '-p', '1205', '-i', '2', '-d', mockDataFolder + '/' + appName)
|
|
405
|
+
|
|
406
|
+
expect(errorSpy.mock.calls[errorSpy.mock.calls.length - 1][0]).toBe('Biz-A Add aborted')
|
|
378
407
|
})
|
|
379
408
|
})
|
|
380
409
|
|
|
381
|
-
describe('Remove App', ()=>{
|
|
382
|
-
it('shall remove specific apps', async ()=>{
|
|
383
|
-
axios.post.mockResolvedValue({data: {success: true}, status: 200})
|
|
410
|
+
describe('Remove App', () => {
|
|
411
|
+
it('shall remove specific apps', async () => {
|
|
412
|
+
axios.post.mockResolvedValue({ data: { success: true }, status: 200 })
|
|
384
413
|
|
|
385
414
|
await runCommand('remove', '-s', 'https://finaapi.imamatek.com', '-p', '1205', '-i', '1', '-n', 'myApp')
|
|
386
415
|
|
|
387
416
|
expect(axios.post.mock.calls).toHaveLength(1)
|
|
388
417
|
expect(axios.post).toHaveBeenCalledWith(
|
|
389
418
|
'https://finaapi.imamatek.com:1205/fina/rest/TOrmMethod/%22deleteApp%22',
|
|
390
|
-
{_parameters: [1, 'myapp']},
|
|
391
|
-
{headers: {'Content-Type': 'text/plain'}}
|
|
419
|
+
{ _parameters: [1, 'myapp'] },
|
|
420
|
+
{ headers: { 'Content-Type': 'text/plain' } }
|
|
392
421
|
)
|
|
393
422
|
expect(logSpy).toHaveBeenCalledWith('myapp removed')
|
|
394
423
|
})
|
|
395
424
|
|
|
396
|
-
it('shall remove multiple apps', async ()=>{
|
|
397
|
-
axios.post.mockResolvedValue({data: {success: true, _f: 'notExistApp'}, status: 200})
|
|
425
|
+
it('shall remove multiple apps', async () => {
|
|
426
|
+
axios.post.mockResolvedValue({ data: { success: true, _f: 'notExistApp' }, status: 200 })
|
|
398
427
|
|
|
399
428
|
await runCommand('remove', '-i', '2', '-s', 'https://www.AA.io', '-p', '2708', '-n', 'firstApp, notExistApp, secondApp')
|
|
400
429
|
|
|
401
430
|
expect(axios.post.mock.calls).toHaveLength(1)
|
|
402
431
|
expect(axios.post).toHaveBeenCalledWith(
|
|
403
432
|
'https://www.AA.io:2708/fina/rest/TOrmMethod/%22deleteApp%22',
|
|
404
|
-
{_parameters: [2, 'firstapp,notexistapp,secondapp']},
|
|
405
|
-
{headers: {'Content-Type': 'text/plain'}}
|
|
433
|
+
{ _parameters: [2, 'firstapp,notexistapp,secondapp'] },
|
|
434
|
+
{ headers: { 'Content-Type': 'text/plain' } }
|
|
406
435
|
)
|
|
407
436
|
expect(logSpy).toHaveBeenCalledWith('firstapp removed')
|
|
408
437
|
expect(logSpy).toHaveBeenCalledWith('secondapp removed')
|
|
409
438
|
expect(logSpy).toHaveBeenCalledWith('notexistapp not found')
|
|
410
439
|
})
|
|
411
440
|
|
|
412
|
-
it('shall remove all apps', async ()=>{
|
|
413
|
-
axios.post.mockResolvedValue({data: {success: true}, _f: '', status: 200})
|
|
441
|
+
it('shall remove all apps', async () => {
|
|
442
|
+
axios.post.mockResolvedValue({ data: { success: true }, _f: '', status: 200 })
|
|
414
443
|
|
|
415
444
|
await runCommand('remove', '-s', 'https://finaapi.imamatek.com', '-p', '1205', '-i', '2')
|
|
416
445
|
|
|
417
446
|
expect(axios.post.mock.calls).toHaveLength(1)
|
|
418
447
|
expect(axios.post).toHaveBeenCalledWith(
|
|
419
448
|
'https://finaapi.imamatek.com:1205/fina/rest/TOrmMethod/%22deleteApp%22',
|
|
420
|
-
{_parameters: [2, '']},
|
|
421
|
-
{headers: {'Content-Type': 'text/plain'}}
|
|
449
|
+
{ _parameters: [2, ''] },
|
|
450
|
+
{ headers: { 'Content-Type': 'text/plain' } }
|
|
422
451
|
)
|
|
423
452
|
expect(logSpy).toHaveBeenCalledWith('All apps removed')
|
|
424
453
|
})
|
|
425
454
|
})
|
|
426
455
|
|
|
427
|
-
it('Shall have options', async ()=>{
|
|
456
|
+
it('Shall have options', async () => {
|
|
428
457
|
jest.spyOn(process, 'exit').mockImplementation()
|
|
429
458
|
const appModule = await import('../bin/app.js')
|
|
430
459
|
expect(appModule.options).toStrictEqual({
|
|
431
|
-
"s"
|
|
460
|
+
"s": {
|
|
432
461
|
alias: "server",
|
|
433
462
|
describe: `API or Server URL (ex: ${env.BIZA_SERVER_LINK} or http://192.168.1.1 or https://finaapi.imamatek.com)`,
|
|
434
463
|
type: "string",
|
|
435
464
|
demandOption: true,
|
|
436
465
|
default: 'http://localhost'
|
|
437
466
|
},
|
|
438
|
-
"i"
|
|
467
|
+
"i": {
|
|
439
468
|
alias: "dbIndex",
|
|
440
469
|
default: 2,
|
|
441
470
|
describe: "database index",
|
|
@@ -450,7 +479,7 @@ describe('Biz-A Apps CLI', ()=>{
|
|
|
450
479
|
},
|
|
451
480
|
"p": {
|
|
452
481
|
alias: "apiPort",
|
|
453
|
-
default
|
|
482
|
+
default: 212,
|
|
454
483
|
describe: "FINA API Port",
|
|
455
484
|
type: "number",
|
|
456
485
|
demandOption: false,
|
|
@@ -482,5 +511,4 @@ describe('Biz-A Apps CLI', ()=>{
|
|
|
482
511
|
}
|
|
483
512
|
})
|
|
484
513
|
})
|
|
485
|
-
|
|
486
514
|
})
|