runner-runtime 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/libs/utils.js ADDED
@@ -0,0 +1,502 @@
1
+ const atomicSleep = require("atomic-sleep"),
2
+ _ = require('lodash'),
3
+ fs = require("fs"),
4
+ path = require("path"),
5
+ { mockExp } = require('exp-mock'),
6
+ Mock = require('mockjs5-pro'),
7
+ mime = require("mime"),
8
+ os = require("os"),
9
+ Buffer = require("buffer/").Buffer,
10
+ urlJoin = require("@apipost/url-join");
11
+
12
+ // 获取某接口的详细信息
13
+ const getAPIFromCollection = function (collection, target_id) {
14
+ return _.find(collection, _.matchesProperty("target_id", target_id));
15
+ };
16
+
17
+ // 获取某接口的 所有父target
18
+ const getParentTargetIDs = function (collection, target_id, parent_ids) {
19
+ if (_.isArray(collection)) {
20
+
21
+ const item = _.find(collection, _.matchesProperty("target_id", target_id)
22
+ );
23
+
24
+ if (item) {
25
+ if (parent_ids.includes(item.parent_id)) {
26
+ return;
27
+ }
28
+ getParentTargetIDs(collection, item.parent_id, parent_ids);
29
+ }
30
+ }
31
+ };
32
+
33
+ // url 拼接
34
+ const smartUrlJoin = (base, ...paths) => {
35
+ // 检查paths中是否有完整的URL
36
+ for (let path of paths) {
37
+ if (/^[a-zA-Z]+:\/\//.test(path)) {
38
+ return path;
39
+ }
40
+ }
41
+
42
+ // 如果没有完整的URL,则使用url-join正常合并
43
+ return urlJoin(base, ...paths);
44
+ }
45
+
46
+ //replace2RegExp
47
+ const replace2RegExp = (str, replace, value) => {
48
+ try {
49
+ return _.replace(str, new RegExp(String(replace), 'ig'), String(value))
50
+ } catch (e) {
51
+ return _.replace(str, replace, String(value))
52
+ }
53
+ }
54
+
55
+ const base64toCacheFile = (key, value, base64, dirpath) => {
56
+ let src = value;
57
+ try {
58
+ fs.statSync(src);
59
+ } catch (e) {
60
+ if (!_.isEmpty(base64)) {
61
+ const match = String(base64).match(/data:([0-9a-zA-Z\/\-]+);base64,/i);
62
+ const ext = mime.getExtension(match && match[1] ? match[1] : "");
63
+
64
+ if (_.isString(ext)) {
65
+ const tempDirPath = path.resolve(os.tmpdir(), `${dirpath}`);
66
+ try {
67
+ fs.mkdirSync(tempDirPath);
68
+ } catch (e) { }
69
+
70
+ let tempFileName = value;
71
+ if (!_.endsWith(tempFileName, `.${ext}`)) {
72
+ tempFileName = `${key}.${ext}`;
73
+ }
74
+
75
+ const tempFilePath = path.resolve(tempDirPath, tempFileName);
76
+ try {
77
+ fs.writeFileSync(
78
+ tempFilePath,
79
+ Buffer.from(_.replace(base64, /^data:[0-9a-zA-Z\/\-]+;base64,/, ""), "base64")
80
+ );
81
+ src = tempFilePath;
82
+ } catch (e) { }
83
+ }
84
+ }
85
+ }
86
+
87
+ return src;
88
+ }
89
+
90
+ // generateNorthAmericanPhoneNumber
91
+ const generateNorthAmericanPhoneNumber = () => {
92
+ const areaCode = Math.floor(Math.random() * 900) + 100; // 100-999
93
+ const phoneNumber = Math.floor(Math.random() * 10000000) + 1000000; // 1000000-9999999
94
+ const formattedPhoneNumber = `${areaCode}-${phoneNumber.toString().slice(0, 3)}-${phoneNumber.toString().slice(3)}`;
95
+
96
+ return formattedPhoneNumber;
97
+ }
98
+
99
+ // generateChinesePhoneNumber
100
+ const generateChinesePhoneNumber = () => {
101
+ let phoneNumber = '1';
102
+ phoneNumber += _.random(3, 9).toString();
103
+ phoneNumber += _.times(9, () => _.random(0, 9)).join('');
104
+ return phoneNumber;
105
+ }
106
+
107
+ const getInsideVariables = () => {
108
+ const list = {};
109
+
110
+ new Array(
111
+ "natural",
112
+ "integer",
113
+ "float",
114
+ "character",
115
+ "range",
116
+ "date",
117
+ "time",
118
+ "datetime",
119
+ "now",
120
+ "guid",
121
+ "increment",
122
+ "url",
123
+ "protocol",
124
+ "domain",
125
+ "tld",
126
+ "email",
127
+ "ip",
128
+ "region",
129
+ "province",
130
+ "city",
131
+ "county",
132
+ "county",
133
+ "zip",
134
+ "first",
135
+ "last",
136
+ "name",
137
+ "cfirst",
138
+ "clast",
139
+ "cname",
140
+ "color",
141
+ "rgb",
142
+ "rgba",
143
+ "hsl",
144
+ "paragraph",
145
+ "cparagraph",
146
+ "sentence",
147
+ "csentence",
148
+ "word",
149
+ "cword",
150
+ "title",
151
+ "ctitle",
152
+ "username",
153
+ "user_name",
154
+ "nickname",
155
+ "nick_name",
156
+ "avatar",
157
+ "icon",
158
+ "img",
159
+ "photo",
160
+ "pic",
161
+ "description",
162
+ "id",
163
+ "userid",
164
+ "user_id",
165
+ "articleid",
166
+ "article_id"
167
+ ).forEach((func) => {
168
+ list[`$${func}`] = Mock.mock(`@${func}`);
169
+ });
170
+
171
+ if (process && process?.IS_ECHOAPI) {
172
+ //echoapi
173
+ const blackArray = ['region', 'province', 'city', 'county', 'cfirst', 'clast', 'cname', 'cparagraph', 'csentence', 'cword', 'ctitle']
174
+ function createReturnFunction(param) {
175
+ return function () {
176
+ return param;
177
+ };
178
+ }
179
+ blackArray.forEach((func) => {
180
+ list[`$${func}`] = createReturnFunction(`$${func}`);
181
+ });
182
+
183
+ new Array("phone", "mobile", "telephone").forEach((func) => {
184
+ list[`$${func}`] = generateNorthAmericanPhoneNumber();
185
+ });
186
+ } else {
187
+ new Array("phone", "mobile", "telephone").forEach((func) => {
188
+ list[`$${func}`] = generateChinesePhoneNumber();
189
+ });
190
+ }
191
+
192
+ // 兼容 v3
193
+ list.$timestamp = (function () {
194
+ return _.toInteger(Date.now() / 1000);
195
+ })();
196
+
197
+ list.$microTimestamp = (function () {
198
+ return new Date().getTime();
199
+ })();
200
+
201
+ list.$randomInt = (function () {
202
+ return Math.floor(Math.random() * 1000);
203
+ })();
204
+
205
+ list.$randomFloat = (function () {
206
+ return Math.random() * 1000;
207
+ })();
208
+
209
+ return list;
210
+ }
211
+
212
+ const repeatArrayToLength = (arr, targetLength) => {
213
+ if (!_.isArray(arr)) {
214
+ arr = _.values(arr);
215
+ }
216
+
217
+ if (_.isEmpty(arr)) {
218
+ return [];
219
+ }
220
+
221
+ const repeatTimes = _.ceil(targetLength / arr.length);
222
+ const repeatedArray = _.flattenDeep(_.fill(new Array(repeatTimes), arr));
223
+ return _.take(repeatedArray, targetLength);
224
+ }
225
+
226
+ const returnBoolean = (exp, compare, value) => {
227
+ let bool = false;
228
+ if (exp === "") {
229
+ // fix bug
230
+ return compare == "null";
231
+ }
232
+
233
+ switch (compare) {
234
+ case "eq":
235
+ bool = exp == value;
236
+ break;
237
+ case "uneq":
238
+ bool = exp != value;
239
+ break;
240
+ case "gt":
241
+ bool = _.gt(Number(exp), Number(value));
242
+ break;
243
+ case "gte":
244
+ bool = _.gte(Number(exp), Number(value));
245
+ break;
246
+ case "lt":
247
+ bool = _.lt(Number(exp), Number(value));
248
+ break;
249
+ case "lte":
250
+ bool = _.lte(Number(exp), Number(value));
251
+ break;
252
+ case "includes":
253
+ bool = _.includes(exp, value) || _.includes(exp, Number(value));
254
+ break;
255
+ case "unincludes":
256
+ bool = !_.includes(exp, value) && !_.includes(exp, Number(value));
257
+ break;
258
+ case "null":
259
+ bool = _.isEmpty(exp);
260
+ break;
261
+ case "notnull":
262
+ bool = !_.isEmpty(exp);
263
+ break;
264
+ case "exist":
265
+ bool = !_.isUndefined(exp);
266
+ break;
267
+ case "notexist":
268
+ bool = _.isUndefined(exp);
269
+ break;
270
+ case "regularmatch":
271
+ try {
272
+ bool = new RegExp(
273
+ String(value).substring(1, String(value).length - 1)
274
+ ).test(exp);
275
+ } catch (e) { }
276
+ break;
277
+ case "belongscollection":
278
+ bool = _.split(value, ",").indexOf(exp) > -1;
279
+ break;
280
+ case "notbelongscollection":
281
+ bool = _.split(value, ",").indexOf(exp) == -1;
282
+ break;
283
+ }
284
+
285
+ return bool;
286
+ };
287
+
288
+ const getCaseInsensitive = (object, keyToFind) => {
289
+ try {
290
+ // 先将要查找的键转换成小写
291
+ const lowerKey = keyToFind.toLowerCase();
292
+
293
+ if (!_.isObject(object)) {
294
+ return undefined;
295
+ }
296
+
297
+ // 在对象的所有键中查找
298
+ for (const key in object) {
299
+ if (key.toLowerCase() === lowerKey) {
300
+ return object[key];
301
+ }
302
+ }
303
+ } catch (e) { }
304
+
305
+ // 如果没有找到,返回undefined
306
+ return undefined;
307
+ }
308
+
309
+ const variableReplace = (str, variables, option) => {
310
+ const AllVars = getInsideVariables();
311
+ ["variables", "globals", "environment"].forEach((varName) => {
312
+ _.assign(AllVars, _.get(variables, `${varName}`))
313
+ })
314
+ str = _.replace(str, /\{\{([^}]+)\}\}/g, (match, key) => {
315
+ return _.get(AllVars, key.trim(), match);
316
+ });
317
+
318
+ const { lang, custom_functions } = option;
319
+
320
+ try {
321
+ return mockExp(str, AllVars, lang, custom_functions)
322
+ } catch (e) {
323
+ return str;
324
+ }
325
+ }
326
+
327
+ // const createPipeServer = () => {
328
+ // const net = require('net');
329
+ // const fs = require('fs');
330
+ // const path = require('path');
331
+ // const { DatabaseQuery } = require('database-query');
332
+ // const mainProcessSocketPath = path.join(__dirname, 'my_socket.sock');
333
+ // _.set(process, 'env.main_process_socket_path', mainProcessSocketPath);
334
+
335
+ // // 如果套接字文件已存在,删除它
336
+ // if (fs.existsSync(mainProcessSocketPath)) {
337
+ // fs.unlinkSync(mainProcessSocketPath);
338
+ // }
339
+
340
+ // const server = net.createServer((socket) => {
341
+ // socket.on('data', async (stream) => {
342
+ // const { action, data } = JSON.parse(String(Buffer.from(stream)));
343
+ // try {
344
+ // switch (action) {
345
+ // case 'queryDatabase':
346
+ // const { dbconfig, query } = data;
347
+ // const result = await DatabaseQuery(dbconfig, query);
348
+ // socket.write(JSON.stringify(result));
349
+ // break;
350
+ // case 'execute':
351
+ // const { execSync } = require('child_process');
352
+ // const { file, args, option } = data;
353
+ // const ext = file.split('.').pop().toLowerCase();
354
+ // let command;
355
+ // try {
356
+ // switch (ext) {
357
+ // case 'jar':
358
+ // const jarPath = path.resolve(__dirname, `jar-main-1.0-SNAPSHOT.jar`);
359
+ // const { className, method } = option || {};
360
+ // if (_.isObject(option)) {
361
+ // if (_.isString(className) && _.isString(method)) {
362
+ // let para = new Buffer(JSON.stringify({ methodName: method, args: args })).toString("base64");
363
+ // command = `java -jar ${jarPath} ${file} ${className} '${para}'`;
364
+ // }
365
+ // } else {
366
+ // command = `java -jar ${file} ${_.join(_.map(args, JSON.stringify), " ")}`;
367
+ // }
368
+
369
+ // break;
370
+ // case 'php':
371
+ // command = `php -f ${file} ${args.join(' ')}`;
372
+ // break;
373
+ // case 'js':
374
+ // command = `node ${file} ${args.join(' ')}`;
375
+ // break;
376
+ // case 'py':
377
+ // command = `python ${file} ${args.join(' ')}`;
378
+ // break;
379
+ // case 'py3':
380
+ // command = `python3 ${file} ${args.join(' ')}`;
381
+ // break;
382
+ // case 'bsh':
383
+ // command = `bsh ${file} ${args.join(' ')}`;
384
+ // break;
385
+ // case 'go':
386
+ // command = `go run ${file} ${args.join(' ')}`;
387
+ // break;
388
+ // case 'sh':
389
+ // command = `sh ${file} ${args.join(' ')}`;
390
+ // break;
391
+ // case 'rb':
392
+ // case 'ruby':
393
+ // command = `ruby ${file} ${args.join(' ')}`;
394
+ // break;
395
+ // case 'lua':
396
+ // command = `lua ${file} ${args.join(' ')}`;
397
+ // break;
398
+ // case 'rs':
399
+ // command = `rustc ${file} && ./${file.replace(/\.rs$/, '')} ${args.join(' ')}`;
400
+ // break;
401
+ // case 'bat':
402
+ // command = `${file} ${args.join(' ')}`;
403
+ // break;
404
+ // case 'ps1':
405
+ // command = `powershell -File ${file} ${args.join(' ')}`;
406
+ // break;
407
+ // default:
408
+ // throw new Error(`Unsupported file suffixes <${ext}>`);
409
+ // }
410
+
411
+ // const isWindows = process.platform === 'win32';
412
+ // const options = _.assign(isWindows ? { encoding: 'cp936' } : { encoding: 'utf8' }, option);
413
+ // const output = execSync(command, options);
414
+ // socket.write(JSON.stringify({
415
+ // err: 'success',
416
+ // result: String(output)
417
+ // }));
418
+ // } catch (e) {
419
+ // socket.write(JSON.stringify({
420
+ // err: 'error',
421
+ // result: String(e)
422
+ // }));
423
+ // }
424
+ // break;
425
+ // }
426
+
427
+ // } catch (e) {
428
+ // socket.write(JSON.stringify(e));
429
+ // }
430
+
431
+ // });
432
+ // });
433
+
434
+ // server.listen(mainProcessSocketPath, () => {
435
+ // // console.log(`服务器已启动,监听路径 ${mainProcessSocketPath}`);
436
+ // });
437
+ // }
438
+
439
+ const formatAutotestReport = (startTimeAt, eventResultList) => {
440
+ const report = {
441
+ "assert": {
442
+ "total": 0,
443
+ "error": 0
444
+ },
445
+ "http": {
446
+ "total": 0,
447
+ "error": 0
448
+ },
449
+ "start_at": startTimeAt,
450
+ "end_at": Date.now(),
451
+ "total_time": Date.now() - startTimeAt,
452
+ "iteration_count": _.size(eventResultList),
453
+ "total_response_time": null,
454
+ "avg_response_time": null,
455
+ "total_response_size": 0,
456
+ "avg_response_size": 0
457
+ };
458
+
459
+ let totalResponseSize = 0, totalResponseTime = 0
460
+
461
+ _.forEach(eventResultList, (item) => {
462
+ if (item?.type === 'api') {
463
+ report.http.total++;
464
+ totalResponseSize = totalResponseSize + _.toInteger(item?.response?.responseSize);
465
+ totalResponseTime = totalResponseTime + _.toInteger(item?.response?.responseTime);
466
+
467
+ if (!_.isEmpty(item?.status?.assert) && _.isArray(item?.status?.assert)) {
468
+ const assertFailure = _.map(item?.status?.assert, (assertItem) => {
469
+ return assertItem?.passed === false
470
+ })
471
+
472
+ report.assert.total = report.assert.total + _.size(item?.status?.assert);
473
+ report.assert.error = report.assert.error + _.size(assertFailure);
474
+ }
475
+
476
+ if (_.toLower(item?.status?.http) != 'ok') {
477
+ report.http.error++;
478
+ }
479
+ }
480
+ })
481
+
482
+ report.avg_response_time = _.round(totalResponseTime / report.http.total, 2);
483
+ report.avg_response_size = _.round(totalResponseSize / report.http.total, 2);
484
+ report.total_response_time = totalResponseTime;
485
+ report.total_response_size = totalResponseSize;
486
+ return report;
487
+ }
488
+ module.exports = {
489
+ atomicSleep,
490
+ getAPIFromCollection,
491
+ getParentTargetIDs,
492
+ base64toCacheFile,
493
+ replace2RegExp,
494
+ getInsideVariables,
495
+ repeatArrayToLength,
496
+ returnBoolean,
497
+ getCaseInsensitive,
498
+ variableReplace,
499
+ // createPipeServer,
500
+ formatAutotestReport,
501
+ smartUrlJoin
502
+ }
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "runner-runtime",
3
+ "version": "1.0.8",
4
+ "description": "Processing data returned by AI.",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/Apipost-Team/runner-runtime.git"
12
+ },
13
+ "keywords": [
14
+ "auto-json-faker"
15
+ ],
16
+ "author": "Daniel Anderson <support@echoapi.com> (https://github.com/Apipost-Team)",
17
+ "license": "MIT",
18
+ "bugs": {
19
+ "url": "https://github.com/Apipost-Team/runner-runtime/issues"
20
+ },
21
+ "homepage": "https://github.com/Apipost-Team/runner-runtime#readme",
22
+ "dependencies": {
23
+ "@apipost/url-join": "^1.0.1",
24
+ "apipost-tools": "^0.0.38",
25
+ "atomic-sleep": "^1.0.0",
26
+ "content-disposition": "^0.5.4",
27
+ "exp-mock": "^2.0.15",
28
+ "file-type": "^16.5.4",
29
+ "is-image": "^4.0.0",
30
+ "is-svg": "^5.1.0",
31
+ "json-bigint": "^1.0.0",
32
+ "json5": "^2.2.3",
33
+ "jsonpath": "^1.1.1",
34
+ "lodash": "^4.17.21",
35
+ "mime": "^3.0.0",
36
+ "minimatch": "^9.0.4",
37
+ "mockjs5-pro": "^1.0.3",
38
+ "msgpack5": "^6.0.2",
39
+ "net": "^1.0.2",
40
+ "postman-collection": "^5.0.2",
41
+ "postman-runtime-pro": "^7.43.13",
42
+ "strip-json-comments": "^5.0.1",
43
+ "tough": "^0.6.0",
44
+ "tough-cookie": "^5.1.2",
45
+ "uuid": "^9.0.1",
46
+ "validator": "^13.15.0"
47
+ }
48
+ }
package/test.js ADDED
@@ -0,0 +1,124 @@
1
+
2
+ const { run } = require('./index');
3
+
4
+ // 以下为调用示例
5
+ const callback = (res) => {
6
+ console.warn("callback", res)
7
+ }
8
+
9
+
10
+ /// pip start
11
+ (function () {
12
+ const net = require('net');
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+ const mainProcessSocketPath = path.join(__dirname, 'my_socket.sock');
16
+ const { DatabaseQuery } = require('database-query');
17
+ _.set(process, 'env.main_process_socket_path', mainProcessSocketPath);
18
+
19
+ // 如果套接字文件已存在,删除它
20
+ if (fs.existsSync(mainProcessSocketPath)) {
21
+ fs.unlinkSync(mainProcessSocketPath);
22
+ }
23
+
24
+ const server = net.createServer((socket) => {
25
+ socket.on('data', async (stream) => {
26
+ const { action, data } = JSON.parse(String(Buffer.from(stream)));
27
+ try {
28
+ switch (action) {
29
+ case 'queryDatabase':
30
+ const { dbconfig, query } = data;
31
+ const result = await DatabaseQuery(dbconfig, query);
32
+ socket.write(JSON.stringify(result));
33
+ break;
34
+ case 'execute':
35
+ const { execSync } = require('child_process');
36
+ const { file, args, option } = data;
37
+ const ext = file.split('.').pop().toLowerCase();
38
+ let command;
39
+ try {
40
+ switch (ext) {
41
+ case 'jar':
42
+ const jarPath = path.resolve(__dirname, `jar-main-1.0-SNAPSHOT.jar`);
43
+ const { className, method } = option || {};
44
+ if (_.isObject(option)) {
45
+ if (_.isString(className) && _.isString(method)) {
46
+ let para = new Buffer(JSON.stringify({ methodName: method, args: args })).toString("base64");
47
+ command = `java -jar ${jarPath} ${file} ${className} '${para}'`;
48
+ }
49
+ } else {
50
+ command = `java -jar ${file} ${_.join(_.map(args, JSON.stringify), " ")}`;
51
+ }
52
+
53
+ break;
54
+ case 'php':
55
+ command = `php -f ${file} ${args.join(' ')}`;
56
+ break;
57
+ case 'js':
58
+ command = `node ${file} ${args.join(' ')}`;
59
+ break;
60
+ case 'py':
61
+ command = `python ${file} ${args.join(' ')}`;
62
+ break;
63
+ case 'py3':
64
+ command = `python3 ${file} ${args.join(' ')}`;
65
+ break;
66
+ case 'bsh':
67
+ command = `bsh ${file} ${args.join(' ')}`;
68
+ break;
69
+ case 'go':
70
+ command = `go run ${file} ${args.join(' ')}`;
71
+ break;
72
+ case 'sh':
73
+ command = `sh ${file} ${args.join(' ')}`;
74
+ break;
75
+ case 'rb':
76
+ case 'ruby':
77
+ command = `ruby ${file} ${args.join(' ')}`;
78
+ break;
79
+ case 'lua':
80
+ command = `lua ${file} ${args.join(' ')}`;
81
+ break;
82
+ case 'rs':
83
+ command = `rustc ${file} && ./${file.replace(/\.rs$/, '')} ${args.join(' ')}`;
84
+ break;
85
+ case 'bat':
86
+ command = `${file} ${args.join(' ')}`;
87
+ break;
88
+ case 'ps1':
89
+ command = `powershell -File ${file} ${args.join(' ')}`;
90
+ break;
91
+ default:
92
+ throw new Error(`Unsupported file suffixes <${ext}>`);
93
+ }
94
+
95
+ const isWindows = process.platform === 'win32';
96
+ const options = _.assign(isWindows ? { encoding: 'cp936' } : { encoding: 'utf8' }, option);
97
+ const output = execSync(command, options);
98
+ socket.write(JSON.stringify({
99
+ err: 'success',
100
+ result: String(output)
101
+ }));
102
+ } catch (e) {
103
+ socket.write(JSON.stringify({
104
+ err: 'error',
105
+ result: String(e)
106
+ }));
107
+ }
108
+ break;
109
+ }
110
+
111
+ } catch (e) {
112
+ socket.write(JSON.stringify(e));
113
+ }
114
+
115
+ });
116
+ });
117
+
118
+ server.listen(mainProcessSocketPath, () => {
119
+ // console.log(`服务器已启动,监听路径 ${mainProcessSocketPath}`);
120
+ });
121
+ })();
122
+ //pip end
123
+ const { option, test_events } = require('./tmp/request'); // tmp todo...
124
+ run(test_events, option, callback);