runner-runtime 1.0.13 → 1.0.16

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/events/api.js CHANGED
@@ -10,6 +10,8 @@ const FileType = require("file-type"),
10
10
  Buffer = require("buffer/").Buffer,
11
11
  isImage = require("is-image");
12
12
  const { getAPIFromCollection, smartUrlJoin, replace2RegExp, getParentTargetIDs, base64toCacheFile, getInsideVariables, getCaseInsensitive, camelCaseToSnakeCase } = require('../libs/utils'),
13
+ { generateHarFromRequest } = require('../libs/2har'),
14
+
13
15
  _ = require('lodash');
14
16
  const sdk = require("postman-collection"),
15
17
  Url = sdk.Url,
@@ -2172,7 +2174,15 @@ module.exports = (event, option, callback, eventRuntimeData, eventResultList) =>
2172
2174
  }
2173
2175
  }
2174
2176
 
2175
- _.set(eventRuntimeData, [event?.event_id, "request"], convert2EchoRequest(request, requestJson, postmanJSON, history));
2177
+ const requestOptions = convert2EchoRequest(request, requestJson, postmanJSON, history);
2178
+ _.set(eventRuntimeData, [event?.event_id, "request"], requestOptions);
2179
+
2180
+ if (_.includes(['get_parsed_request'], scene)) {
2181
+ resolve(generateHarFromRequest(request));
2182
+ try {
2183
+ run.abort();
2184
+ } catch (e) { }
2185
+ }
2176
2186
  },
2177
2187
 
2178
2188
  beforeTest(err, cursor, events, item) {
@@ -2228,14 +2238,11 @@ module.exports = (event, option, callback, eventRuntimeData, eventResultList) =>
2228
2238
 
2229
2239
  responseData(cursor, data) {
2230
2240
  callback({
2231
- event: "sse",
2241
+ action: "sse",
2232
2242
  data: {
2233
- action: "sse",
2234
- data: {
2235
- stream: data,
2236
- size: _.size(data),
2237
- time: Date.now()
2238
- },
2243
+ stream: data,
2244
+ size: _.size(data),
2245
+ time: Date.now()
2239
2246
  },
2240
2247
  msg: "success",
2241
2248
  error: null,
@@ -2258,20 +2265,18 @@ module.exports = (event, option, callback, eventRuntimeData, eventResultList) =>
2258
2265
 
2259
2266
  // 写入到自动化测试结果
2260
2267
  if (scene === 'auto_test' && _.includes(['assert', 'assert_visual', 'api', 'sample'], event?.type)) {
2261
- eventResultList.push(_.assign(camelCaseToSnakeCase(_.get(eventRuntimeData, event?.event_id)), _.pick(event, ['type', 'event_id', 'test_id']), {
2262
- iteration_id: aTools.snowflakeId()
2263
- }))
2268
+ eventResultList.push(_.assign(camelCaseToSnakeCase(_.get(eventRuntimeData, event?.event_id)), _.pick(event, ['type', 'event_id', 'test_id', 'iteration_id'])))
2264
2269
  }
2265
- }
2266
2270
 
2267
- resolve(_.assign({
2268
- action: 'httpRequestCompleted',
2269
- data: camelCaseToSnakeCase(_.get(eventRuntimeData, event?.event_id)),
2270
- error: _.get(eventRuntimeData, [event?.event_id, 'error', 'message']) || null,
2271
- event_id: event?.event_id,
2272
- iteration_id: aTools.snowflakeId(),
2273
- type: 'request'
2274
- }, _.pick(requestJson, ['parent_id', 'project_id', 'test_id', 'target_id'])))
2271
+ resolve(_.assign({
2272
+ action: 'request',
2273
+ data: camelCaseToSnakeCase(_.get(eventRuntimeData, event?.event_id)),
2274
+ error: _.get(eventRuntimeData, [event?.event_id, 'error', 'message']) || null,
2275
+ event_id: event?.event_id,
2276
+ iteration_id: _.get(eventRuntimeData, [event?.event_id, 'iteration_id']),
2277
+ type: 'request'
2278
+ }, _.pick(requestJson, ['parent_id', 'project_id', 'test_id', 'target_id'])))
2279
+ }
2275
2280
  }
2276
2281
  })
2277
2282
  }
package/events/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  const atomicSleep = require("atomic-sleep"),
2
2
  jsonpath = require("jsonpath"),
3
+ aTools = require("apipost-tools"),
3
4
  JSON5 = require('json5'),
4
5
  _ = require('lodash'),
5
6
  { returnBoolean, variableReplace } = require('../libs/utils');
@@ -196,7 +197,9 @@ const events = {
196
197
  const executeEvent = (event, option, callback, eventRuntimeData, eventResultList) => {
197
198
  return new Promise((resolve, reject) => {
198
199
  if (_.isUndefined(_.get(eventRuntimeData, event.event_id))) {
199
- _.set(eventRuntimeData, event.event_id, {});
200
+ _.set(eventRuntimeData, event.event_id, {
201
+ iteration_id: aTools.snowflakeId()
202
+ });
200
203
  }
201
204
  const eventCall = _.get(events, `${event?.type}`);
202
205
 
@@ -208,10 +211,13 @@ const executeEvent = (event, option, callback, eventRuntimeData, eventResultList
208
211
  const { total, scene } = option;
209
212
  if (scene == 'auto_test' && _.isInteger(event?.progress)) {
210
213
  callback({
211
- action: 'inProcess',
212
- progress: event?.progress + 1,
213
- total,
214
- current_event_id: event?.event_id
214
+ action: 'process',
215
+ data: {
216
+ processCurrentCount: event?.progress + 1,
217
+ processTotalCount: total,
218
+ event_id: event?.event_id,
219
+ testing_id: event?.test_id,
220
+ }
215
221
  })
216
222
  }
217
223
  })
package/index.js CHANGED
@@ -19,21 +19,6 @@ const run = async (events, option, callback) => {
19
19
  if (scene == 'http_request') {
20
20
  const event = _.first(tempEvents);
21
21
 
22
- if (_.isObject(event) && _.has(event, 'type')) {
23
- try {
24
- const data = await executeEvent(event, _.assign(eventOptions, { iterationData: {} }), callback, eventRuntimeData, eventResultList);
25
- callback(data)
26
- } catch (e) {
27
- callback(e);
28
-
29
- if (_.includes(ABORT_RECURSION_ERROR, e?.action)) {
30
- throw new Error(String(e?.message));
31
- }
32
- }
33
- }
34
- } else if (scene == 'get_parsed_request') {
35
- const event = _.first(tempEvents);
36
-
37
22
  if (_.isObject(event) && _.has(event, 'type')) {
38
23
  try {
39
24
  const data = await executeEvent(event, _.assign(eventOptions, { iterationData: {} }), callback, eventRuntimeData, eventResultList);
@@ -72,4 +57,23 @@ const run = async (events, option, callback) => {
72
57
  })
73
58
  }
74
59
  }
75
- module.exports = { run }
60
+
61
+ const getHar = async (events, option) => {
62
+ const eventOptions = _.pick(option, ["scene", "lang", "project", "env", "globals", "cookies", "system_configs", "custom_functions", "collection", "database_configs", "enable_sandbox", "sleep", "name", "testing_id"]);
63
+ const eventRuntimeData = {
64
+ variables: {}
65
+ };
66
+ const eventResultList = [];
67
+ const event = _.first(events);
68
+
69
+ if (_.isObject(event) && _.has(event, 'type')) {
70
+ try {
71
+ return await executeEvent(event, _.assign(eventOptions, { scene: 'get_parsed_request' }), () => { }, eventRuntimeData, eventResultList);
72
+ } catch (e) {
73
+ if (_.includes(ABORT_RECURSION_ERROR, e?.action)) {
74
+ throw new Error(String(e?.message));
75
+ }
76
+ }
77
+ }
78
+ }
79
+ module.exports = { run, getHar }
package/libs/2har.js ADDED
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Converts a Postman-style request configuration into HAR format JSON.
3
+ *
4
+ * @param {Object} options - Request options based on Postman format.
5
+ * {
6
+ * url: string,
7
+ * method: string,
8
+ * headers: array,
9
+ * queryParams: array,
10
+ * body: {
11
+ * mode: string, // none | urlencoded | formdata | raw | json
12
+ * [urlencoded]: array, // For urlencoded form data
13
+ * [formdata]: array, // For multipart/form-data
14
+ * [raw]: string, // For raw (text or binary)
15
+ * [json]: object // For JSON body
16
+ * }
17
+ * }
18
+ * @returns {Object} HAR format JSON object.
19
+ */
20
+ function generateHarFromRequest(options) {
21
+ const {
22
+ url,
23
+ method = 'GET',
24
+ headers = [], // Array of { key, value }
25
+ queryParams = [], // Array of { key, value }
26
+ body = { mode: 'none' }, // Body in Postman format
27
+ } = options;
28
+
29
+ const har = {
30
+ log: {
31
+ version: '1.2',
32
+ creator: {
33
+ name: 'Custom Node Request to HAR Converter',
34
+ version: '1.0',
35
+ },
36
+ entries: [
37
+ {
38
+ request: {
39
+ method: method.toUpperCase(),
40
+ url: appendQueryParams(url, queryParams),
41
+ headers: processHeaders(headers),
42
+ queryString: processQueryString(queryParams),
43
+ postData: processBody(body),
44
+ headersSize: -1, // Optional, usually auto-calculated
45
+ bodySize: -1, // Optional, usually auto-calculated
46
+ },
47
+ startedDateTime: new Date().toISOString(),
48
+ time: 0, // Mock timing data (replace with actual timing if needed)
49
+ },
50
+ ],
51
+ },
52
+ };
53
+
54
+ return har;
55
+ }
56
+
57
+ /**
58
+ * Appends query parameters to a URL.
59
+ * @param {string} url - The base URL.
60
+ * @param {Array} queryParams - Array of { key: string, value: string }.
61
+ * @returns {string} URL with query parameters appended.
62
+ */
63
+ function appendQueryParams(url, queryParams) {
64
+ if (!queryParams || queryParams.length === 0) return url;
65
+
66
+ const searchParams = new URLSearchParams();
67
+ queryParams.forEach(({ key, value }) => {
68
+ searchParams.append(key, value);
69
+ });
70
+
71
+ return `${url}?${searchParams.toString()}`;
72
+ }
73
+
74
+ /**
75
+ * Converts Postman-style headers into HAR format.
76
+ * @param {Array} headers - Array of { key: string, value: string }.
77
+ * @returns {Array} Array of HAR headers.
78
+ */
79
+ function processHeaders(headers) {
80
+ return (headers || []).map(({ key, value }) => ({
81
+ name: key,
82
+ value: value,
83
+ }));
84
+ }
85
+
86
+ /**
87
+ * Converts Postman-style query parameters into HAR format.
88
+ * @param {Array} queryParams - Array of { key: string, value: string }.
89
+ * @returns {Array} Array of HAR query parameters.
90
+ */
91
+ function processQueryString(queryParams) {
92
+ return (queryParams || []).map(({ key, value }) => ({
93
+ name: key,
94
+ value: String(value),
95
+ }));
96
+ }
97
+
98
+ /**
99
+ * Processes the body into HAR format based on its mode.
100
+ * @param {Object} body - Postman-style body object with a defined mode.
101
+ * @returns {Object|undefined} HAR postData object.
102
+ */
103
+ function processBody(body) {
104
+ const { mode } = body;
105
+
106
+ if (mode === 'none') {
107
+ return undefined; // No body
108
+ }
109
+
110
+ if (mode === 'urlencoded') {
111
+ return {
112
+ mimeType: 'application/x-www-form-urlencoded',
113
+ params: (body.urlencoded || []).map(({ key, value }) => ({
114
+ name: key,
115
+ value: String(value),
116
+ })),
117
+ };
118
+ }
119
+
120
+ if (mode === 'formdata') {
121
+ return {
122
+ mimeType: 'multipart/form-data',
123
+ params: (body.formdata || []).map(({ key, value, type = 'text' }) => {
124
+ const param = { name: key, value: String(value) };
125
+ if (type === 'file') param.fileName = value; // File handling
126
+ return param;
127
+ }),
128
+ };
129
+ }
130
+
131
+ if (mode === 'raw') {
132
+ return {
133
+ mimeType: 'text/plain',
134
+ text: String(body.raw),
135
+ };
136
+ }
137
+
138
+ if (mode === 'json') {
139
+ return {
140
+ mimeType: 'application/json',
141
+ text: JSON.stringify(body.json || {}),
142
+ };
143
+ }
144
+
145
+ return undefined; // No body
146
+ }
147
+
148
+ module.exports = { generateHarFromRequest };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "runner-runtime",
3
- "version": "1.0.13",
3
+ "version": "1.0.16",
4
4
  "description": "Processing data returned by AI.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -24,6 +24,7 @@
24
24
  "apipost-tools": "^0.0.38",
25
25
  "atomic-sleep": "^1.0.0",
26
26
  "content-disposition": "^0.5.4",
27
+ "database-query": "^1.1.12",
27
28
  "exp-mock": "^2.0.15",
28
29
  "file-type": "^16.5.4",
29
30
  "is-image": "^3.0.0",
@@ -39,6 +40,7 @@
39
40
  "net": "^1.0.2",
40
41
  "postman-collection": "^5.0.2",
41
42
  "postman-runtime-pro": "^7.43.13",
43
+ "request-har": "^1.0.0",
42
44
  "strip-json-comments": "^5.0.1",
43
45
  "tough": "^0.6.0",
44
46
  "tough-cookie": "^5.1.2",
package/test.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const _ =require('lodash');
2
- const { run } = require('./index');
2
+ const { getHar } = require('./index');
3
3
 
4
4
  // 以下为调用示例
5
5
  const callback = (res) => {
@@ -121,4 +121,4 @@ const callback = (res) => {
121
121
  })();
122
122
  //pip end
123
123
  const { option, test_events } = require('./tmp/request'); // tmp todo...
124
- run(test_events, option, callback);
124
+ getHar(test_events, option, callback);
package/tmp/request.js CHANGED
@@ -118,7 +118,7 @@ module.exports = {
118
118
  },
119
119
  "collection": [
120
120
  {
121
- "target_id": "3dc7c7a076d024",
121
+ "target_id": "3d53610d7d6015",
122
122
  "target_type": "api",
123
123
  "parent_id": "0",
124
124
  "name": "User login",
@@ -265,104 +265,104 @@ module.exports = {
265
265
  // "event_id": "178664d77c014",
266
266
  // "sort": 4
267
267
  // },
268
- {
269
- "type": "api",
270
- "auto_sync": false,
271
- "test_id": "270a34e2b9f019",
272
- "event_id": "3a76fca3f9f002",
273
- "enabled": 1,
274
- "data": {
275
- "target_id": "3dc7c7a076d024",
276
- "project_id": "2cf6deb75864000",
277
- "parent_id": "0",
278
- "target_type": "api",
279
- "apiData": {
280
- "name": "User login",
281
- "method": "POST",
282
- "protocol": "http/1.1",
283
- "url": "https://echo.apipost.cn/userlogin.php",
284
- "request": {
285
- "query": {
286
- "parameter": [],
287
- "query_add_equal": 1
288
- },
289
- "cookie": {
290
- "parameter": []
291
- },
292
- "header": {
293
- "parameter": []
294
- },
295
- "restful": {
296
- "parameter": []
297
- },
298
- "pre_tasks": [],
299
- "post_tasks": [],
300
- "auth": {
301
- "type": "inherit"
302
- },
303
- "body": {
304
- "mode": "form-data",
305
- "binary": null,
306
- "parameter": [
307
- {
308
- "key": "username",
309
- "not_null": -1,
310
- "param_id": "17e6c2b93e4002",
311
- "is_checked": 1,
312
- "content_type": "",
313
- "value": "admin",
314
- "file_name": "",
315
- "field_type": "String",
316
- "description": "用户名",
317
- "file_base64": ""
318
- },
319
- {
320
- "key": "password",
321
- "is_checked": 1,
322
- "content_type": "",
323
- "description": "密码",
324
- "file_base64": "",
325
- "value": "123456",
326
- "not_null": -1,
327
- "param_id": "17e6c2b93e4003",
328
- "file_name": "",
329
- "field_type": "String"
330
- },
331
- {
332
- "key": "",
333
- "value": "",
334
- "param_id": "1b33200fbe4000",
335
- "file_name": "",
336
- "is_checked": 1,
337
- "description": "",
338
- "file_base64": "",
339
- "not_null": 1,
340
- "field_type": "String",
341
- "content_type": ""
342
- }
343
- ],
344
- "raw_schema": {
345
- "type": "object"
346
- },
347
- "raw_parameter": [],
348
- "raw": ""
349
- }
350
- }
351
- }
352
- }
353
- },
354
- {
355
- "parent_event_id": "0",
356
- "enabled": 1,
357
- "type": "wait",
358
- "data": {
359
- "sleep": "1000"
360
- },
361
- "project_id": "2cf6deb75864000",
362
- "test_id": "270a34e2b9f019",
363
- "event_id": "167019c37c00c",
364
- "sort": 2
365
- },
268
+ // {
269
+ // "type": "api",
270
+ // "auto_sync": false,
271
+ // "test_id": "270a34e2b9f019",
272
+ // "event_id": "3a76fca3f9f002",
273
+ // "enabled": 1,
274
+ // "data": {
275
+ // "target_id": "3dc7c7a076d024",
276
+ // "project_id": "2cf6deb75864000",
277
+ // "parent_id": "0",
278
+ // "target_type": "api",
279
+ // "apiData": {
280
+ // "name": "User login",
281
+ // "method": "POST",
282
+ // "protocol": "http/1.1",
283
+ // "url": "https://echo.apipost.cn/userlogin.php",
284
+ // "request": {
285
+ // "query": {
286
+ // "parameter": [],
287
+ // "query_add_equal": 1
288
+ // },
289
+ // "cookie": {
290
+ // "parameter": []
291
+ // },
292
+ // "header": {
293
+ // "parameter": []
294
+ // },
295
+ // "restful": {
296
+ // "parameter": []
297
+ // },
298
+ // "pre_tasks": [],
299
+ // "post_tasks": [],
300
+ // "auth": {
301
+ // "type": "inherit"
302
+ // },
303
+ // "body": {
304
+ // "mode": "form-data",
305
+ // "binary": null,
306
+ // "parameter": [
307
+ // {
308
+ // "key": "username",
309
+ // "not_null": -1,
310
+ // "param_id": "17e6c2b93e4002",
311
+ // "is_checked": 1,
312
+ // "content_type": "",
313
+ // "value": "admin",
314
+ // "file_name": "",
315
+ // "field_type": "String",
316
+ // "description": "用户名",
317
+ // "file_base64": ""
318
+ // },
319
+ // {
320
+ // "key": "password",
321
+ // "is_checked": 1,
322
+ // "content_type": "",
323
+ // "description": "密码",
324
+ // "file_base64": "",
325
+ // "value": "123456",
326
+ // "not_null": -1,
327
+ // "param_id": "17e6c2b93e4003",
328
+ // "file_name": "",
329
+ // "field_type": "String"
330
+ // },
331
+ // {
332
+ // "key": "",
333
+ // "value": "",
334
+ // "param_id": "1b33200fbe4000",
335
+ // "file_name": "",
336
+ // "is_checked": 1,
337
+ // "description": "",
338
+ // "file_base64": "",
339
+ // "not_null": 1,
340
+ // "field_type": "String",
341
+ // "content_type": ""
342
+ // }
343
+ // ],
344
+ // "raw_schema": {
345
+ // "type": "object"
346
+ // },
347
+ // "raw_parameter": [],
348
+ // "raw": ""
349
+ // }
350
+ // }
351
+ // }
352
+ // }
353
+ // },
354
+ // {
355
+ // "parent_event_id": "0",
356
+ // "enabled": 1,
357
+ // "type": "wait",
358
+ // "data": {
359
+ // "sleep": "1000"
360
+ // },
361
+ // "project_id": "2cf6deb75864000",
362
+ // "test_id": "270a34e2b9f019",
363
+ // "event_id": "167019c37c00c",
364
+ // "sort": 2
365
+ // },
366
366
  // {
367
367
  // "parent_event_id": "0",
368
368
  // "enabled": 1,
@@ -406,19 +406,19 @@ module.exports = {
406
406
  // "sort": 3
407
407
  // },
408
408
 
409
- // {
410
- // "type": "api",
411
- // "auto_sync": true,
412
- // "test_id": "3d53fabfbd602c",
413
- // "event_id": "3d544fb0bd602e",
414
- // "enabled": 1,
415
- // "data": {
416
- // "target_id": "3d53610d7d6015",
417
- // "project_id": "2adc267c9064000",
418
- // "parent_id": "0",
419
- // "target_type": "api"
420
- // }
421
- // },
409
+ {
410
+ "type": "api",
411
+ "auto_sync": true,
412
+ "test_id": "3d53fabfbd602c",
413
+ "event_id": "3d544fb0bd602e",
414
+ "enabled": 1,
415
+ "data": {
416
+ "target_id": "3d53610d7d6015",
417
+ "project_id": "2adc267c9064000",
418
+ "parent_id": "0",
419
+ "target_type": "api"
420
+ }
421
+ },
422
422
  // {
423
423
  // "parent_event_id": "0",
424
424
  // "enabled": 1,