@skyramp/skyramp 1.0.0-sha.b2dfe11 → 1.2.2
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/LICENSE +21 -0
- package/README.md +10 -17
- package/package.json +14 -5
- package/scripts/download-binary.js +189 -0
- package/src/classes/Asserts.d.ts +16 -0
- package/src/classes/Asserts.js +41 -0
- package/src/classes/AsyncScenario.d.ts +133 -0
- package/src/classes/AsyncScenario.js +324 -0
- package/src/classes/AsyncTestStatus.d.ts +172 -0
- package/src/classes/AsyncTestStatus.js +488 -0
- package/src/classes/DelayConfig.d.ts +4 -0
- package/src/classes/DelayConfig.js +25 -0
- package/src/classes/Endpoint.d.ts +2 -2
- package/src/classes/Endpoint.js +81 -43
- package/src/classes/GrpcEndpoint.d.ts +1 -1
- package/src/classes/GrpcEndpoint.js +24 -3
- package/src/classes/LoadTestConfig.d.ts +131 -0
- package/src/classes/LoadTestConfig.js +186 -0
- package/src/classes/Protocol.d.ts +5 -0
- package/src/classes/Protocol.js +8 -0
- package/src/classes/RequestV2.d.ts +30 -0
- package/src/classes/RequestV2.js +181 -0
- package/src/classes/RequestValue.d.ts +24 -0
- package/src/classes/RequestValue.js +113 -0
- package/src/classes/ResponseV2.d.ts +24 -0
- package/src/classes/ResponseV2.js +96 -0
- package/src/classes/ResponseValue.d.ts +21 -0
- package/src/classes/ResponseValue.js +93 -0
- package/src/classes/RestEndpoint.d.ts +11 -2
- package/src/classes/RestEndpoint.js +90 -5
- package/src/classes/RestParam.d.ts +4 -0
- package/src/classes/RestParam.js +32 -0
- package/src/classes/Scenario.d.ts +48 -0
- package/src/classes/Scenario.js +208 -0
- package/src/classes/SkyrampClient.d.ts +184 -4
- package/src/classes/SkyrampClient.js +774 -40
- package/src/classes/Step.d.ts +28 -0
- package/src/classes/Step.js +113 -0
- package/src/classes/TrafficConfig.d.ts +6 -0
- package/src/classes/TrafficConfig.js +28 -0
- package/src/function.js +46 -0
- package/src/index.d.ts +14 -1
- package/src/index.js +36 -3
- package/src/lib.js +6 -6
- package/src/utils.js +180 -20
- package/src/utils.d.ts +0 -5
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
const lib = require('../lib');
|
|
2
|
+
const getOverallStatusWrapper = lib.func('getOverallStatusWrapper', 'string', ['string']);
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Unified AsyncTestStatus class for handling all test status operations
|
|
6
|
+
*/
|
|
7
|
+
class AsyncTestStatus {
|
|
8
|
+
/**
|
|
9
|
+
* Creates an AsyncTestStatus instance
|
|
10
|
+
* @param {Object} options - The test status options
|
|
11
|
+
*/
|
|
12
|
+
constructor(options) {
|
|
13
|
+
// Store the original JSON response for getOverallStatusWrapper
|
|
14
|
+
this.jsonResponse = options || {};
|
|
15
|
+
this.testStatus = options || {};
|
|
16
|
+
this.testId = options.id || "";
|
|
17
|
+
this.testType = "";
|
|
18
|
+
this.scenarioDict = {};
|
|
19
|
+
|
|
20
|
+
// Initialize based on test type
|
|
21
|
+
if (options && options.results && options.results.length > 1) {
|
|
22
|
+
if (options.results[1].type === "load") {
|
|
23
|
+
this._initializeLoadTest(options);
|
|
24
|
+
} else {
|
|
25
|
+
this._initializeIntegrationTest(options);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Initialize load test specific data
|
|
32
|
+
* @private
|
|
33
|
+
*/
|
|
34
|
+
_initializeLoadTest(options) {
|
|
35
|
+
this.testType = "load";
|
|
36
|
+
const results = options.results;
|
|
37
|
+
|
|
38
|
+
for (let i = 1; i < results.length; i++) {
|
|
39
|
+
const result = results[i];
|
|
40
|
+
const rawResult = result;
|
|
41
|
+
const stats = rawResult.stat || {};
|
|
42
|
+
|
|
43
|
+
for (const [key, resultData] of Object.entries(stats)) {
|
|
44
|
+
const resultStat = resultData;
|
|
45
|
+
resultData.id = key;
|
|
46
|
+
const resultId = resultStat.description;
|
|
47
|
+
|
|
48
|
+
if (resultStat.type === "scenario") {
|
|
49
|
+
const newScenario = new AsyncTestStatus.AsyncScenarioStatus(resultData, "load");
|
|
50
|
+
this.scenarioDict[resultId] = newScenario;
|
|
51
|
+
} else if (resultStat.type === "request") {
|
|
52
|
+
const itemList = resultId.split('.');
|
|
53
|
+
for (let j = itemList.length - 1; j >= 0; j--) {
|
|
54
|
+
itemList.pop();
|
|
55
|
+
const parentScenario = itemList.slice(0, -1).join('.');
|
|
56
|
+
if (this.scenarioDict[parentScenario]) {
|
|
57
|
+
const request = new AsyncTestStatus.AsyncRequestStatus(resultData, "load");
|
|
58
|
+
this.scenarioDict[parentScenario].requests.push(request);
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
} else if (resultStat.assertStatement) {
|
|
63
|
+
const itemList = resultStat.description.split('.');
|
|
64
|
+
let scenarioId = "";
|
|
65
|
+
let requestId = "";
|
|
66
|
+
|
|
67
|
+
if (itemList.length > 4) {
|
|
68
|
+
scenarioId = itemList.slice(0, -4).join('.');
|
|
69
|
+
requestId = itemList.slice(0, -2).join('.');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (this.scenarioDict[scenarioId]) {
|
|
73
|
+
const scenario = this.scenarioDict[scenarioId];
|
|
74
|
+
for (const request of scenario.getRequests()) {
|
|
75
|
+
if (request.resultObject.description === requestId) {
|
|
76
|
+
request.asserts.push(resultStat);
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (rawResult.type === "load") {
|
|
85
|
+
const timeseriesList = rawResult.timeseries || [];
|
|
86
|
+
if (this.scenarioDict[rawResult.description]) {
|
|
87
|
+
this.scenarioDict[rawResult.description].timeseries = timeseriesList;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Initialize integration test specific data
|
|
95
|
+
* @private
|
|
96
|
+
*/
|
|
97
|
+
_initializeIntegrationTest(options) {
|
|
98
|
+
this.testType = "integration";
|
|
99
|
+
|
|
100
|
+
for (let i = 1; i < options.results.length; i++) {
|
|
101
|
+
const result = options.results[i];
|
|
102
|
+
const resultObject = result;
|
|
103
|
+
|
|
104
|
+
if (resultObject.type === "scenario") {
|
|
105
|
+
this.scenarioDict[resultObject.nestedInfo] = new AsyncTestStatus.AsyncScenarioStatus(result, "integration");
|
|
106
|
+
if (resultObject.parent && resultObject.parent !== "") {
|
|
107
|
+
if (this.scenarioDict[resultObject.parent]) {
|
|
108
|
+
this.scenarioDict[resultObject.parent].subScenarios.push(
|
|
109
|
+
this.scenarioDict[resultObject.nestedInfo]
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
} else if (resultObject.type === "request") {
|
|
114
|
+
if (!this.scenarioDict[resultObject.parent]) {
|
|
115
|
+
this.scenarioDict[resultObject.parent] = new AsyncTestStatus.AsyncScenarioStatus(result, "integration");
|
|
116
|
+
}
|
|
117
|
+
this.scenarioDict[resultObject.parent].requests.push(
|
|
118
|
+
new AsyncTestStatus.AsyncRequestStatus(result, "integration")
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Returns the test id
|
|
126
|
+
* @returns {string} The test id
|
|
127
|
+
*/
|
|
128
|
+
getTestId() {
|
|
129
|
+
return this.testId;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Returns the test type
|
|
134
|
+
* @returns {string} The test type
|
|
135
|
+
*/
|
|
136
|
+
getTestType() {
|
|
137
|
+
return this.testType;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Get the scenario by name
|
|
142
|
+
* @param {string} scenarioName - The scenario name
|
|
143
|
+
* @returns {AsyncTestStatus.AsyncScenarioStatus} The scenario status object
|
|
144
|
+
*/
|
|
145
|
+
getScenario(scenarioName) {
|
|
146
|
+
const scenarioList = this.getScenarios(scenarioName);
|
|
147
|
+
if (scenarioList.length === 1) {
|
|
148
|
+
return scenarioList[0];
|
|
149
|
+
}
|
|
150
|
+
if (scenarioList.length === 0) {
|
|
151
|
+
throw new Error(`Scenario ${scenarioName} not found in the test`);
|
|
152
|
+
}
|
|
153
|
+
if (scenarioList.length > 1) {
|
|
154
|
+
throw new Error(`Multiple scenarios found with the name ${scenarioName}, please iterate over the scenarios`);
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Get scenarios, optionally filtered by name
|
|
161
|
+
* @param {string} [scenarioName=""] - Optional scenario name filter
|
|
162
|
+
* @returns {AsyncTestStatus.AsyncScenarioStatus[]} Array of scenario status objects
|
|
163
|
+
*/
|
|
164
|
+
getScenarios(scenarioName = "") {
|
|
165
|
+
const scenarioList = Object.values(this.scenarioDict).sort(AsyncTestStatus.sortKeys);
|
|
166
|
+
if (scenarioName === "") {
|
|
167
|
+
return scenarioList;
|
|
168
|
+
}
|
|
169
|
+
const scenarios = [];
|
|
170
|
+
for (const scenario of scenarioList) {
|
|
171
|
+
if (scenario.name === scenarioName) {
|
|
172
|
+
scenarios.push(scenario);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return scenarios;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Get request by scenario and request name
|
|
180
|
+
* @param {string} scenarioName - The scenario name
|
|
181
|
+
* @param {string} requestName - The request name
|
|
182
|
+
* @returns {AsyncTestStatus.AsyncRequestStatus} The request status object
|
|
183
|
+
*/
|
|
184
|
+
getRequest(scenarioName, requestName) {
|
|
185
|
+
const scenario = this.getScenario(scenarioName);
|
|
186
|
+
return scenario.getRequest(requestName);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Get the overall status
|
|
191
|
+
* @returns {Promise<Object>} The overall status as parsed JSON object
|
|
192
|
+
*/
|
|
193
|
+
async getOverallStatus() {
|
|
194
|
+
return new Promise((resolve, reject) => {
|
|
195
|
+
getOverallStatusWrapper.async(
|
|
196
|
+
JSON.stringify(this.jsonResponse),
|
|
197
|
+
(err, res) => {
|
|
198
|
+
if (err) {
|
|
199
|
+
reject(err);
|
|
200
|
+
} else if (res.error) {
|
|
201
|
+
reject(new Error(res));
|
|
202
|
+
} else {
|
|
203
|
+
resolve(JSON.parse(res));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
);
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Create the appropriate TestStatus object based on the options
|
|
212
|
+
* @param {Object} options - The test status options
|
|
213
|
+
* @returns {AsyncTestStatus} The appropriate test status instance
|
|
214
|
+
*/
|
|
215
|
+
static create(options) {
|
|
216
|
+
return new AsyncTestStatus(options);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Sorts entries based on their ID structure
|
|
221
|
+
* @param {Object} entry1 - First entry to compare
|
|
222
|
+
* @param {Object} entry2 - Second entry to compare
|
|
223
|
+
* @returns {number} Sort comparison result
|
|
224
|
+
*/
|
|
225
|
+
static sortKeys(entry1, entry2) {
|
|
226
|
+
const entry1List = entry1.id.split('.');
|
|
227
|
+
const entry2List = entry2.id.split('.');
|
|
228
|
+
|
|
229
|
+
for (let i = 0; i < Math.min(entry1List.length, entry2List.length); i++) {
|
|
230
|
+
const part1 = entry1List[i];
|
|
231
|
+
const part2 = entry2List[i];
|
|
232
|
+
|
|
233
|
+
let part1Num, part2Num;
|
|
234
|
+
try {
|
|
235
|
+
part1Num = part1.substring(1).match(/^\d+$/) ? parseInt(part1.substring(1)) : part1;
|
|
236
|
+
part2Num = part2.substring(1).match(/^\d+$/) ? parseInt(part2.substring(1)) : part2;
|
|
237
|
+
} catch (e) {
|
|
238
|
+
part1Num = part1;
|
|
239
|
+
part2Num = part2;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (part1Num !== part2Num) {
|
|
243
|
+
return part1Num < part2Num ? -1 : 1;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return entry1List.length - entry2List.length;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* AsyncScenarioStatus inner class
|
|
253
|
+
*/
|
|
254
|
+
AsyncTestStatus.AsyncScenarioStatus = class {
|
|
255
|
+
/**
|
|
256
|
+
* Creates an AsyncScenarioStatus instance
|
|
257
|
+
* @param {Object} dataObject - The data object containing scenario information
|
|
258
|
+
* @param {string} testType - The type of test (load or integration)
|
|
259
|
+
*/
|
|
260
|
+
constructor(dataObject, testType) {
|
|
261
|
+
this.testType = testType;
|
|
262
|
+
this.result = dataObject;
|
|
263
|
+
this.name = "";
|
|
264
|
+
this.overallStatus = {};
|
|
265
|
+
this.timeseries = [];
|
|
266
|
+
this.subScenarios = [];
|
|
267
|
+
this.requests = [];
|
|
268
|
+
|
|
269
|
+
if (testType === "load") {
|
|
270
|
+
const testStat = dataObject;
|
|
271
|
+
this.result = testStat;
|
|
272
|
+
this.name = testStat.description ? testStat.description.split('.').pop() : '';
|
|
273
|
+
this.id = testStat.id;
|
|
274
|
+
this.stats = testStat;
|
|
275
|
+
this.timeseries = null;
|
|
276
|
+
} else {
|
|
277
|
+
const rawResult = dataObject;
|
|
278
|
+
const descList = rawResult.description ? rawResult.description.split('.') : [''];
|
|
279
|
+
this.name = descList[descList.length - 1];
|
|
280
|
+
this.id = rawResult.id;
|
|
281
|
+
this.status = rawResult.status;
|
|
282
|
+
this.error = rawResult.error;
|
|
283
|
+
this.stepDescription = rawResult.stepDescription;
|
|
284
|
+
this.stepName = rawResult.stepName;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Returns string representation of the scenario status
|
|
290
|
+
* @returns {string} String representation
|
|
291
|
+
*/
|
|
292
|
+
toString() {
|
|
293
|
+
if (this.testType === "load") {
|
|
294
|
+
return `AsyncScenarioStatus(name=${this.name}, status=${JSON.stringify(this.stats)}, sub_scenarios=${JSON.stringify(this.subScenarios)}, requests=${JSON.stringify(this.requests)})`;
|
|
295
|
+
}
|
|
296
|
+
return `AsyncScenarioStatus(name=${this.name}, status=${JSON.stringify(this.status)}, error=${this.error})`;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Get sub scenarios
|
|
301
|
+
* @returns {AsyncTestStatus.AsyncScenarioStatus[]} Array of sub scenarios
|
|
302
|
+
*/
|
|
303
|
+
getSubScenarios() {
|
|
304
|
+
if (this.testType === "load") {
|
|
305
|
+
this.subScenarios.sort(AsyncTestStatus.sortKeys);
|
|
306
|
+
}
|
|
307
|
+
return this.subScenarios;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Get request by name from this scenario
|
|
312
|
+
* @param {string} requestName - The request name to find
|
|
313
|
+
* @returns {AsyncTestStatus.AsyncRequestStatus} The request status object
|
|
314
|
+
*/
|
|
315
|
+
getRequest(requestName) {
|
|
316
|
+
const requestList = [];
|
|
317
|
+
for (const request of this.requests) {
|
|
318
|
+
if (request.resultObject.description && request.resultObject.description.endsWith('.' + requestName)) {
|
|
319
|
+
requestList.push(request);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (requestList.length === 1) {
|
|
323
|
+
return requestList[0];
|
|
324
|
+
}
|
|
325
|
+
if (requestList.length === 0) {
|
|
326
|
+
throw new Error(`Request ${requestName} not found in the scenario`);
|
|
327
|
+
}
|
|
328
|
+
if (requestList.length > 1) {
|
|
329
|
+
throw new Error(`Multiple requests found with the name ${requestName}, please iterate over the requests`);
|
|
330
|
+
}
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Get all requests
|
|
336
|
+
* @returns {AsyncTestStatus.AsyncRequestStatus[]} Array of request status objects
|
|
337
|
+
*/
|
|
338
|
+
getRequests() {
|
|
339
|
+
this.requests.sort(AsyncTestStatus.sortKeys);
|
|
340
|
+
return this.requests;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Get overall status of the scenario
|
|
345
|
+
* @returns {Promise<string>} The overall status as JSON string
|
|
346
|
+
*/
|
|
347
|
+
async getOverallStatus() {
|
|
348
|
+
// This method should not be used on scenario level, it should be called on the main AsyncTestStatus
|
|
349
|
+
throw new Error('getOverallStatus should be called on the main AsyncTestStatus object, not on individual scenarios');
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Assert status (for integration tests)
|
|
354
|
+
* @returns {*} The status
|
|
355
|
+
*/
|
|
356
|
+
assertStatus() {
|
|
357
|
+
return this.status;
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* AsyncRequestStatus inner class
|
|
363
|
+
*/
|
|
364
|
+
AsyncTestStatus.AsyncRequestStatus = class {
|
|
365
|
+
/**
|
|
366
|
+
* Creates an AsyncRequestStatus instance
|
|
367
|
+
* @param {Object} resultObject - The result object containing request data
|
|
368
|
+
* @param {string} testType - The type of test (load or integration)
|
|
369
|
+
*/
|
|
370
|
+
constructor(resultObject, testType) {
|
|
371
|
+
this.testType = testType;
|
|
372
|
+
this.name = "";
|
|
373
|
+
this.id = "";
|
|
374
|
+
this.resultObject = resultObject;
|
|
375
|
+
this.jsonObject = resultObject;
|
|
376
|
+
this.asserts = [];
|
|
377
|
+
|
|
378
|
+
if (testType === "load") {
|
|
379
|
+
const testStat = resultObject;
|
|
380
|
+
this.testStatus = testStat;
|
|
381
|
+
this.name = testStat.description ? testStat.description.split('.').pop() : '';
|
|
382
|
+
this.id = testStat.id;
|
|
383
|
+
} else {
|
|
384
|
+
const rawResult = resultObject;
|
|
385
|
+
this.id = rawResult.id;
|
|
386
|
+
this.name = rawResult.description ? rawResult.description.split('.').pop() : '';
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Returns string representation of the request status
|
|
392
|
+
* @returns {string} String representation
|
|
393
|
+
*/
|
|
394
|
+
toString() {
|
|
395
|
+
if (this.testType === "load") {
|
|
396
|
+
return `AsyncRequestStatus(name=${this.name}, status=${JSON.stringify(this.resultObject)}, asserts=${JSON.stringify(this.asserts)})`;
|
|
397
|
+
}
|
|
398
|
+
return `AsyncRequestStatus(name=${this.resultObject.name}, status=${this.resultObject.status})`;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Converts the object to JSON
|
|
403
|
+
* @returns {Object} JSON representation of the request status
|
|
404
|
+
*/
|
|
405
|
+
toJson() {
|
|
406
|
+
return {
|
|
407
|
+
name: this.name,
|
|
408
|
+
status: this.resultObject,
|
|
409
|
+
asserts: this.asserts.map(assert => assert.toJson ? assert.toJson() : assert)
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Get the response of the load test for a specific status code
|
|
415
|
+
* @param {string} statusCode - The HTTP status code
|
|
416
|
+
* @param {string|null} [jsonPath=null] - Optional JSON path to extract specific data
|
|
417
|
+
* @returns {string|null} The response data or null if not found
|
|
418
|
+
*/
|
|
419
|
+
getLoadTestResponse(statusCode, jsonPath = null) {
|
|
420
|
+
if (this.testType !== "load") {
|
|
421
|
+
throw new Error('getLoadTestResponse not implemented for integration test request status');
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const jsonData = this.resultObject.logTable ? this.resultObject.logTable[statusCode] : null;
|
|
425
|
+
if (!jsonData) {
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
428
|
+
const jsonResponse = jsonData.Response;
|
|
429
|
+
if (jsonResponse) {
|
|
430
|
+
const payload = jsonResponse.payload;
|
|
431
|
+
if (!payload) {
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
if (jsonPath === null) {
|
|
435
|
+
return JSON.stringify(payload, null, 2);
|
|
436
|
+
}
|
|
437
|
+
return payload[jsonPath];
|
|
438
|
+
}
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Get the response of the request
|
|
444
|
+
* @param {string|null} [jsonPath=null] - Optional JSON path
|
|
445
|
+
* @returns {string|null} The response data
|
|
446
|
+
*/
|
|
447
|
+
getResponse(jsonPath = null) {
|
|
448
|
+
if (this.testType === "load") {
|
|
449
|
+
throw new Error('getResponse not implemented for load test request status');
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const output = this.resultObject.output;
|
|
453
|
+
if (!output) {
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
if (jsonPath === null) {
|
|
457
|
+
return JSON.stringify(output.payload, null, 2);
|
|
458
|
+
}
|
|
459
|
+
return output.payload[jsonPath];
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Get the value of the variable
|
|
464
|
+
* @param {string} key - The variable key
|
|
465
|
+
* @returns {string} The variable value
|
|
466
|
+
*/
|
|
467
|
+
getVarValue(key) {
|
|
468
|
+
if (this.testType === "load") {
|
|
469
|
+
throw new Error('getVarValue not implemented for load test request status');
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
const stateObject = this.resultObject.state;
|
|
473
|
+
if (stateObject) {
|
|
474
|
+
if (stateObject.exports && key in stateObject.exports && stateObject.exports[key] !== null) {
|
|
475
|
+
return stateObject.exports[key];
|
|
476
|
+
}
|
|
477
|
+
if (stateObject.vars && key in stateObject.vars && stateObject.vars[key] !== null) {
|
|
478
|
+
return stateObject.vars[key];
|
|
479
|
+
}
|
|
480
|
+
if (stateObject.scenarioVars && key in stateObject.scenarioVars && stateObject.scenarioVars[key] !== null) {
|
|
481
|
+
return stateObject.scenarioVars[key];
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
throw new Error(`Key ${key} not found in the async data`);
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
module.exports = AsyncTestStatus;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `DelayConfig` class represents a configuration for delay values.
|
|
3
|
+
* @class
|
|
4
|
+
*/
|
|
5
|
+
class DelayConfig {
|
|
6
|
+
/**
|
|
7
|
+
* Initializes a new instance of the DelayConfig class with the provided minimum and maximum delay values.
|
|
8
|
+
* @constructor
|
|
9
|
+
* @param {number} minDelay - The minimum delay value.
|
|
10
|
+
* @param {number} maxDelay - The maximum delay value.
|
|
11
|
+
*/
|
|
12
|
+
constructor(minDelay, maxDelay) {
|
|
13
|
+
this.minDelay = minDelay;
|
|
14
|
+
this.maxDelay = maxDelay;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
toJson() {
|
|
18
|
+
return {
|
|
19
|
+
minDelay: this.minDelay,
|
|
20
|
+
maxDelay: this.maxDelay,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = DelayConfig;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export declare class Endpoint {
|
|
2
|
-
constructor(endpointData: string);
|
|
3
|
-
mockMethod(methodName: string,
|
|
2
|
+
constructor(endpointData: string, endpointAddress: string);
|
|
3
|
+
mockMethod(methodName: string, mockObject: string, dynamic?: boolean): void;
|
|
4
4
|
mockMethodFromFile(methodName: string, fileName: string): void;
|
|
5
5
|
writeMockConfigurationToFile(kubernetesService: string): Promise<void>;
|
|
6
6
|
}
|
package/src/classes/Endpoint.js
CHANGED
|
@@ -1,73 +1,111 @@
|
|
|
1
1
|
const lib = require('../lib');
|
|
2
|
-
const
|
|
2
|
+
const { getYamlBytes, readDataFromFile, SKYRAMP_YAML_VERSION } = require('../utils');
|
|
3
3
|
const writeMockDescriptionWrapper = lib.func('writeMockDescriptionWrapper', 'string', ['string', 'string']);
|
|
4
|
-
const { readDataFromFile } = require('../utils');
|
|
5
4
|
|
|
5
|
+
/**
|
|
6
|
+
* The `Endpoint` class represents an endpoint object to manage endpoint data and generate mock configurations.
|
|
7
|
+
* @class
|
|
8
|
+
*/
|
|
6
9
|
class Endpoint {
|
|
7
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Intialize a new instance of an endpoint.
|
|
12
|
+
* @private
|
|
13
|
+
* @constructor
|
|
14
|
+
* @param {string} endpointData - The endpoint data in JSON format.
|
|
15
|
+
* @param {string} endpointAddress - The address of the endpoint.
|
|
16
|
+
* @throws {Error} If the endpoint data is invalid JSON.
|
|
17
|
+
*/
|
|
18
|
+
constructor(endpointData, endpointAddress) {
|
|
8
19
|
try {
|
|
9
20
|
const endpoint = JSON.parse(endpointData);
|
|
10
21
|
this.services = endpoint.services;
|
|
11
22
|
this.endpoint = endpoint.endpoints[0];
|
|
12
|
-
|
|
13
|
-
|
|
23
|
+
|
|
24
|
+
if (endpointAddress && this.services.length > 0) {
|
|
25
|
+
for (const service of this.services) {
|
|
26
|
+
service.addr = endpointAddress;
|
|
27
|
+
delete service.alias;
|
|
28
|
+
}
|
|
14
29
|
}
|
|
15
|
-
this.
|
|
30
|
+
this.responses = endpoint.responses;
|
|
31
|
+
|
|
16
32
|
this.mockDescription = {
|
|
33
|
+
version: SKYRAMP_YAML_VERSION,
|
|
17
34
|
services: this.services,
|
|
18
35
|
endpoints: [this.endpoint],
|
|
19
|
-
|
|
36
|
+
responses: this.responses
|
|
20
37
|
};
|
|
21
|
-
} catch(error) {
|
|
38
|
+
} catch (error) {
|
|
22
39
|
throw new Error(endpointData);
|
|
23
40
|
}
|
|
24
41
|
}
|
|
25
42
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
getMethodNameForMethodType(method_type) {
|
|
44
|
+
const matchingMethod = this.endpoint.methods.find(method => method.type === method_type);
|
|
45
|
+
if (!matchingMethod) {
|
|
46
|
+
throw new Error(`Method ${method_type} not found`);
|
|
47
|
+
}
|
|
48
|
+
return matchingMethod.name;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
mockMethod(methodName, mockObject, dynamic = false) {
|
|
52
|
+
if (!this.endpoint.methods.some(method => method.name === methodName)) {
|
|
53
|
+
throw new Error(`Method ${methodName} not found`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
for (const response of this.responses) {
|
|
57
|
+
if (response.methodName === methodName && response.endpointName === this.endpoint.name) {
|
|
58
|
+
if (dynamic) {
|
|
59
|
+
response.javascriptPath = mockObject;
|
|
60
|
+
delete response.blob;
|
|
61
|
+
} else {
|
|
62
|
+
response.blob = mockObject.responseValue.blob;
|
|
63
|
+
delete response.javascriptPath;
|
|
42
64
|
}
|
|
65
|
+
|
|
66
|
+
return;
|
|
43
67
|
}
|
|
44
68
|
}
|
|
45
|
-
|
|
69
|
+
|
|
70
|
+
let newResponse = {
|
|
71
|
+
methodName: methodName,
|
|
72
|
+
endpointName: this.endpoint.name
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
if (dynamic) {
|
|
76
|
+
if (mockObject.responseValue.javascriptPath !== undefined
|
|
77
|
+
&& mockObject.responseValue.javascriptPath !== null) {
|
|
78
|
+
newResponse.javascriptPath = mockObject.responseValue.javascriptPath
|
|
79
|
+
} else if (mockObject.responseValue.javascript !== undefined
|
|
80
|
+
&& mockObject.responseValue.javascript !== null) {
|
|
81
|
+
newResponse.javascript = mockObject.responseValue.javascript;
|
|
82
|
+
}
|
|
83
|
+
} else {
|
|
84
|
+
newResponse.blob = mockObject.responseValue.blob;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
this.responses.append(newResponse);
|
|
46
88
|
}
|
|
47
89
|
|
|
48
90
|
mockMethodFromFile(methodName, fileName) {
|
|
49
|
-
const [
|
|
50
|
-
return this.mockMethod(methodName,
|
|
91
|
+
const [jsonData, dynamic] = readDataFromFile(fileName);
|
|
92
|
+
return this.mockMethod(methodName, jsonData, dynamic);
|
|
51
93
|
}
|
|
52
94
|
|
|
53
95
|
writeMockConfigurationToFile(kubernetesService) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
});
|
|
96
|
+
const yamlContent = getYamlBytes(this.mockDescription);
|
|
97
|
+
return new Promise((resolve, reject) => {
|
|
98
|
+
writeMockDescriptionWrapper.async(yamlContent, kubernetesService, (err, res) => {
|
|
99
|
+
if (err) {
|
|
100
|
+
reject(err);
|
|
101
|
+
} else if (res) {
|
|
102
|
+
reject(new Error(res));
|
|
103
|
+
} else {
|
|
104
|
+
resolve();
|
|
105
|
+
}
|
|
66
106
|
});
|
|
67
|
-
}
|
|
68
|
-
throw new Error('Error converting to YAML bytes:', error);
|
|
69
|
-
}
|
|
107
|
+
});
|
|
70
108
|
}
|
|
71
109
|
}
|
|
72
110
|
|
|
73
|
-
module.exports = Endpoint;
|
|
111
|
+
module.exports = Endpoint;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Endpoint } from './Endpoint';
|
|
2
2
|
export declare class GrpcEndpoint extends Endpoint {
|
|
3
|
-
constructor(name: string, service: string, port: number, pbFile: string);
|
|
3
|
+
constructor(name: string, service: string, port: number, pbFile: string, endpointAddress: string);
|
|
4
4
|
}
|