@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
|
@@ -1,24 +1,155 @@
|
|
|
1
1
|
const lib = require('../lib');
|
|
2
|
-
const
|
|
2
|
+
const koffi = require('koffi');
|
|
3
|
+
const TrafficConfig = require('./TrafficConfig');
|
|
4
|
+
const RequestV2 = require('./RequestV2');
|
|
5
|
+
const ResponseV2 = require('./ResponseV2');
|
|
3
6
|
|
|
7
|
+
const workerInfoType = koffi.struct({
|
|
8
|
+
container_name: 'char*',
|
|
9
|
+
error: 'char*',
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const testerInfoType = koffi.struct({
|
|
13
|
+
tester_id: 'char*',
|
|
14
|
+
error: 'char*',
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const testerGenerateType = koffi.struct({
|
|
18
|
+
generated_files: 'char*',
|
|
19
|
+
error: 'char*',
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const contractResponseType = koffi.struct({
|
|
23
|
+
response: 'char*',
|
|
24
|
+
error: 'char*',
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const WORKER_URL = "public.ecr.aws/j1n2c2p2/rampup/worker";
|
|
28
|
+
const WORKER_TAG = "latest";
|
|
29
|
+
const CONTAINER_PORT = 35142;
|
|
30
|
+
|
|
31
|
+
// k8s related
|
|
32
|
+
const { createTestDescriptionFromScenario, getYamlBytes, readDataFromFile, SKYRAMP_YAML_VERSION } = require('../utils');
|
|
4
33
|
const applyLocalWrapper = lib.func('applyLocalWrapper', 'string', []);
|
|
5
|
-
const addKubeconfigWrapper = lib.func('addKubeconfigWrapper', 'string', ['string', '
|
|
6
|
-
const deleteSkyrampWorkerWrapper = lib.func('deleteSkyrampWorkerWrapper', 'string', ['string'])
|
|
7
|
-
const deploySkyrampWorkerWrapper = lib.func('deploySkyrampWorkerWrapper', 'string', ['string', 'string', 'bool']);
|
|
34
|
+
const addKubeconfigWrapper = lib.func('addKubeconfigWrapper', 'string', ['string', 'string', 'string']);
|
|
35
|
+
const deleteSkyrampWorkerWrapper = lib.func('deleteSkyrampWorkerWrapper', 'string', ['string', 'string', 'string', 'string'])
|
|
36
|
+
const deploySkyrampWorkerWrapper = lib.func('deploySkyrampWorkerWrapper', 'string', ['string', 'string', 'string', 'string', 'string', 'bool']);
|
|
8
37
|
const getKubeConfigPath = lib.func('getKubeConfigPath', 'string', []);
|
|
9
38
|
const removeLocalWrapper = lib.func('removeLocalWrapper', 'string', []);
|
|
10
39
|
const removeClusterFromConfigWrapper = lib.func('removeClusterFromConfigWrapper', 'string', ['string']);
|
|
11
|
-
|
|
40
|
+
// docker related
|
|
41
|
+
const newStartDockerSkyrampWorkerWrapper = lib.func('newStartDockerSkyrampWorkerWrapper', workerInfoType, ['string', 'string', 'int', 'string', 'string']);
|
|
42
|
+
const newDeleteDockerSkyrampWorkerWrapper = lib.func('newDeleteDockerSkyrampWorkerWrapper', 'string', ['string']);
|
|
43
|
+
// tester related
|
|
44
|
+
const runTesterStartWrapper = lib.func('runTesterStartWrapper', testerInfoType, ['string', 'string', 'string', 'string', 'string', 'string', 'bool']);
|
|
45
|
+
// mocker related
|
|
46
|
+
const runTesterStartWrapperv1 = lib.func('runTesterStartWrapperWithGlobalHeaders', testerInfoType, ['string', 'string', 'string', 'string', 'string', 'string', 'string', 'string', 'string', 'bool', 'bool', 'string', 'string', 'string']);
|
|
47
|
+
const applyMockDescriptionWrapper = lib.func('applyMockDescriptionWrapper', 'string', ['string', 'string', 'string', 'string', 'string', 'string', 'string', 'string']);
|
|
48
|
+
// NPM only: for VS code extension use
|
|
49
|
+
const initTargetWrapper = lib.func('initTargetWrapper', 'string', ['string']);
|
|
50
|
+
const deployTargetWrapper = lib.func('deployTargetWrapper', 'string', ['string', 'string', 'string', 'string', 'string', 'string', 'bool']);
|
|
51
|
+
const deleteTargetWrapper = lib.func('deleteTargetWrapper', 'string', ['string', 'string', 'string', 'string', 'string']);
|
|
52
|
+
const runTesterGenerateRestWrapper = lib.func('runTesterGenerateRestWrapper', testerGenerateType, ['string', 'string', 'string', 'string', 'string', 'string', 'string', 'string', 'int', 'bool', 'bool', 'bool']);
|
|
53
|
+
|
|
54
|
+
const generateRestTestWrapper = lib.func('generateRestTestWrapper', 'string', ['string', 'string', 'string', 'string', 'string', 'string', 'string', 'string', 'string', 'string', 'string', 'string', 'string', 'string', 'string', 'string', 'string', 'string', 'bool', 'bool', 'string', 'string', 'string', 'string', 'string', 'string', 'string', 'string', 'bool', 'string', 'bool', 'string', 'string', 'string', 'string', 'string', 'string', 'string', 'string', 'bool', 'string', 'string']);
|
|
55
|
+
const traceCollectWrapper = lib.func('traceCollectWrapper', 'string', ['string', 'string', 'bool', 'string']);
|
|
56
|
+
const analyzeOpenapiWrapper = lib.func('analyzeOpenapiWrapper', 'string', ['string', 'string']);
|
|
57
|
+
|
|
58
|
+
// Load test scenario support
|
|
59
|
+
const sendScenarioWrapper = lib.func('sendScenarioWrapper', contractResponseType, ['string', 'string', 'string', 'string', 'string', 'string', 'string', 'string', 'string', 'string', 'string', 'bool', 'bool', 'bool']);
|
|
60
|
+
|
|
61
|
+
// contract test
|
|
62
|
+
// func sendRequestWrapper(address, namespace, kubePath, kubeContext, clusterName, request, dockerNetwork, workerImage *C.char, localImage bool) *C.char {
|
|
63
|
+
const sendRequestWrapper = lib.func('sendRequestWrapper', contractResponseType, ['string', 'string', 'string', 'string', 'string', 'string', 'string', 'string', 'string', 'bool']);
|
|
12
64
|
|
|
65
|
+
// dashboard
|
|
66
|
+
const deployDockerDashboardWrapper = lib.func('deployDockerDashboardWrapper', 'string', ['string']);
|
|
67
|
+
const deployK8sDashboardWrapper = lib.func('deployK8sDashboardWrapper', 'string', ['string']);
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The `SkyrampClient` class is the main client that provides methods for interacting with the Skyramp service.
|
|
71
|
+
* It allows users to manage Kubernetes clusters, deploy and delete workers and targets, apply mock descriptions, and start tests.
|
|
72
|
+
*
|
|
73
|
+
* @class
|
|
74
|
+
* @name SkyrampClient
|
|
75
|
+
*/
|
|
13
76
|
class SkyrampClient {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Initializes a new instance of the SkyrampClient class.
|
|
79
|
+
* @constructor
|
|
80
|
+
* @param {string|Object} kubeconfigPathOrOptions - Either the path to kubeconfig file or an options object
|
|
81
|
+
* @param {string} [kubeconfigPathOrOptions.kubeconfigPath] - The path to the kubeconfig file
|
|
82
|
+
* @param {string} [kubeconfigPathOrOptions.clusterName] - The name of the cluster
|
|
83
|
+
* @param {string} [kubeconfigPathOrOptions.context] - The context of the cluster
|
|
84
|
+
* @param {string} [kubeconfigPathOrOptions.userToken] - The user token for authentication
|
|
85
|
+
* @param {string} [kubeconfigPathOrOptions.address] - The address of the service
|
|
86
|
+
* @param {string} [kubeconfigPathOrOptions.dockerSkyrampPort] - The docker port for skyramp worker
|
|
87
|
+
* @param {string} [kubeconfigPathOrOptions.dockerNetwork] - The docker network for skyramp worker
|
|
88
|
+
* @param {string} [kubeconfigPathOrOptions.workerImage=""] - The worker image to use
|
|
89
|
+
* @param {string} [kubeconfigPathOrOptions.local_image=false] - The flag to indicate if the worker image is local
|
|
90
|
+
* @param {string} [clusterName] - The name of the cluster (when using individual parameters)
|
|
91
|
+
* @param {string} [context] - The context of the cluster (when using individual parameters)
|
|
92
|
+
* @param {string} [userToken] - The user token for authentication (when using individual parameters)
|
|
93
|
+
* @param {string} [directory=process.cwd()] - The working directory of the client.
|
|
94
|
+
*/
|
|
95
|
+
constructor(kubeconfigPathOrOptions, clusterName, context, userToken, directory = process.cwd()) {
|
|
96
|
+
this.local_image = false;
|
|
97
|
+
if (typeof kubeconfigPathOrOptions === 'object') {
|
|
98
|
+
const options = kubeconfigPathOrOptions;
|
|
99
|
+
this.workerNamespaces = [];
|
|
100
|
+
this.userToken = options.userToken;
|
|
101
|
+
this.address = options.address;
|
|
102
|
+
// set docker params
|
|
103
|
+
if (options.runtime == "docker") {
|
|
104
|
+
if (options.dockerSkyrampPort) {
|
|
105
|
+
this.address = `localhost:${options.dockerSkyrampPort}`;
|
|
106
|
+
}
|
|
107
|
+
if (options.dockerNetwork) {
|
|
108
|
+
this.dockerNetwork = options.dockerNetwork;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// set k8s params
|
|
112
|
+
if (options.runtime == "k8s") {
|
|
113
|
+
if (options.k8SNamespace) {
|
|
114
|
+
this.namespace = options.k8SNamespace;
|
|
115
|
+
}
|
|
116
|
+
if (options.clusterName) {
|
|
117
|
+
this.clusterName = options.clusterName;
|
|
118
|
+
}
|
|
119
|
+
if (options.k8SContext) {
|
|
120
|
+
this.context = options.k8SContext;
|
|
121
|
+
}
|
|
122
|
+
if (options.k8SConfigPath) {
|
|
123
|
+
this.kubeconfigPath = options.k8SConfigPath;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (this.kubeconfigPath || this.clusterName || this.context) {
|
|
128
|
+
this.addKubeConfig(this.context, this.clusterName, this.kubeconfigPath);
|
|
129
|
+
}
|
|
130
|
+
// set default worker image
|
|
131
|
+
if (options.workerImage) {
|
|
132
|
+
this.workerImage = options.workerImage;
|
|
133
|
+
}
|
|
134
|
+
if (options.local_image) {
|
|
135
|
+
this.local_image = options.local_image;
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
if (kubeconfigPathOrOptions || clusterName || context) {
|
|
139
|
+
this.addKubeConfig(context, clusterName, kubeconfigPathOrOptions);
|
|
140
|
+
}
|
|
141
|
+
this.workerNamespaces = [];
|
|
142
|
+
this.userToken = userToken;
|
|
143
|
+
this.projectPath = directory;
|
|
144
|
+
this.failedResponses = [];
|
|
19
145
|
}
|
|
20
146
|
}
|
|
21
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Applies local changes to the Kubernetes cluster configuration.
|
|
150
|
+
*
|
|
151
|
+
* @returns {Promise} A promise that resolves when the local changes are successfully applied, or rejects with an error if any error occurs.
|
|
152
|
+
*/
|
|
22
153
|
async applyLocal() {
|
|
23
154
|
return new Promise((resolve, reject) => {
|
|
24
155
|
applyLocalWrapper.async((err, res) => {
|
|
@@ -29,15 +160,23 @@ class SkyrampClient {
|
|
|
29
160
|
} else {
|
|
30
161
|
this.kubeConfigPath = getKubeConfigPath();
|
|
31
162
|
if (this.kubeConfigPath == "") {
|
|
32
|
-
|
|
163
|
+
reject(new Error("no kubeconfig found"));
|
|
33
164
|
} else {
|
|
34
|
-
|
|
165
|
+
resolve();
|
|
35
166
|
}
|
|
36
167
|
}
|
|
37
168
|
});
|
|
38
169
|
});
|
|
39
170
|
}
|
|
40
171
|
|
|
172
|
+
/**
|
|
173
|
+
* Adds a Kubernetes configuration to the SkyrampClient object.
|
|
174
|
+
*
|
|
175
|
+
* @param {string} context - The context of the Kubernetes configuration.
|
|
176
|
+
* @param {string} clusterName - The name of the Kubernetes cluster.
|
|
177
|
+
* @param {string} kubeConfigPath - The path to the kubeconfig file.
|
|
178
|
+
* @returns {Promise} - A Promise that resolves if the operation is successful or rejects with an error if an error occurs.
|
|
179
|
+
*/
|
|
41
180
|
async addKubeConfig(context, clusterName, kubeConfigPath) {
|
|
42
181
|
return new Promise((resolve, reject) => {
|
|
43
182
|
addKubeconfigWrapper.async(context, clusterName, kubeConfigPath, (err, res) => {
|
|
@@ -53,14 +192,11 @@ class SkyrampClient {
|
|
|
53
192
|
});
|
|
54
193
|
}
|
|
55
194
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
195
|
+
/**
|
|
196
|
+
* Removes the local configuration of the SkyrampClient.
|
|
197
|
+
*
|
|
198
|
+
* @returns {Promise<void>} A promise that resolves when the removal is successful, or rejects with an error if an error occurs during the removal process.
|
|
199
|
+
*/
|
|
64
200
|
async removeLocal() {
|
|
65
201
|
return new Promise((resolve, reject) => {
|
|
66
202
|
removeLocalWrapper.async((err, res) => {
|
|
@@ -75,7 +211,13 @@ class SkyrampClient {
|
|
|
75
211
|
});
|
|
76
212
|
}
|
|
77
213
|
|
|
78
|
-
|
|
214
|
+
/**
|
|
215
|
+
* Asynchronously removes a cluster from the configuration.
|
|
216
|
+
*
|
|
217
|
+
* @param {string} clusterName - The name of the cluster to be removed from the configuration.
|
|
218
|
+
* @returns {Promise} A Promise that resolves if the cluster is successfully removed, or rejects with an error if the removal fails.
|
|
219
|
+
*/
|
|
220
|
+
async removeCluster(clusterName) {
|
|
79
221
|
return new Promise((resolve, reject) => {
|
|
80
222
|
removeClusterFromConfigWrapper.async(clusterName, (err, res) => {
|
|
81
223
|
if (err) {
|
|
@@ -89,12 +231,18 @@ class SkyrampClient {
|
|
|
89
231
|
});
|
|
90
232
|
}
|
|
91
233
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
234
|
+
/**
|
|
235
|
+
* Deploys a worker to a Kubernetes cluster.
|
|
236
|
+
*
|
|
237
|
+
* @param {string} namespace - The namespace where the worker will be deployed.
|
|
238
|
+
* @param {string} [workerImage=''] - The image of the worker to be deployed.
|
|
239
|
+
* @param {boolean} [localImage=false] - Indicates whether the worker image is local or not.
|
|
240
|
+
* @returns {Promise} - A Promise that resolves if deployment is successful, and rejects with an error if any error occurs during deployment.
|
|
241
|
+
* @throws {Error} - If there is no cluster to deploy the worker to.
|
|
242
|
+
*/
|
|
243
|
+
async deploySkyrampWorker(namespace, workerImage = '', localImage = false, kubePath = '', kubeContext = '', clusterName = '') {
|
|
96
244
|
return new Promise((resolve, reject) => {
|
|
97
|
-
deploySkyrampWorkerWrapper.async(namespace, workerImage, localImage, (err, res) => {
|
|
245
|
+
deploySkyrampWorkerWrapper.async(namespace, kubePath, kubeContext, clusterName, workerImage, localImage, (err, res) => {
|
|
98
246
|
if (err) {
|
|
99
247
|
reject(err);
|
|
100
248
|
} else if (res) {
|
|
@@ -107,7 +255,14 @@ class SkyrampClient {
|
|
|
107
255
|
});
|
|
108
256
|
}
|
|
109
257
|
|
|
110
|
-
|
|
258
|
+
/**
|
|
259
|
+
* Deletes a worker from a specified namespace in the SkyrampClient class.
|
|
260
|
+
*
|
|
261
|
+
* @param {string} namespace - The namespace from which the worker should be deleted.
|
|
262
|
+
* @returns {Promise} - A promise that resolves with no value upon successful deletion.
|
|
263
|
+
* @throws {Error} - If there is no cluster to delete the worker from or if there is no worker to delete from the specified namespace.
|
|
264
|
+
*/
|
|
265
|
+
async deleteSkyrampWorker(namespace, kubePath = '', kubeContext = '', clusterName = '') {
|
|
111
266
|
if (this.kubeConfigPath === null) {
|
|
112
267
|
throw new Error('No cluster to delete worker from.');
|
|
113
268
|
}
|
|
@@ -116,7 +271,7 @@ class SkyrampClient {
|
|
|
116
271
|
throw new Error(`No worker to delete from ${namespace} namespace.`);
|
|
117
272
|
}
|
|
118
273
|
return new Promise((resolve, reject) => {
|
|
119
|
-
deleteSkyrampWorkerWrapper.async(namespace, (err, res) => {
|
|
274
|
+
deleteSkyrampWorkerWrapper.async(namespace, kubePath, kubeContext, clusterName, (err, res) => {
|
|
120
275
|
if (err) {
|
|
121
276
|
reject(err);
|
|
122
277
|
} else if (res) {
|
|
@@ -129,24 +284,603 @@ class SkyrampClient {
|
|
|
129
284
|
});
|
|
130
285
|
}
|
|
131
286
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
287
|
+
/**
|
|
288
|
+
* Asynchronous method that runs a Docker worker using the provided image, tag, port, and network name.
|
|
289
|
+
*
|
|
290
|
+
* @param {string} [workerImage=WORKER_URL] - The URL of the Docker worker image.
|
|
291
|
+
* @param {string} [workerTag=WORKER_TAG] - The tag of the Docker worker image.
|
|
292
|
+
* @param {number} [hostPort=CONTAINER_PORT] - The port on the host machine to expose for the Docker worker.
|
|
293
|
+
* @param {string} targetNetworkName - The name of the network to connect the Docker worker to.
|
|
294
|
+
* @returns {Promise} - A Promise that resolves if the Docker worker starts successfully and rejects if there is an error starting the worker.
|
|
295
|
+
*/
|
|
296
|
+
async runDockerSkyrampWorker(workerImage = WORKER_URL, workerTag = WORKER_TAG, hostPost = CONTAINER_PORT, targetNetworkName = "", testServiceAlias = "") {
|
|
297
|
+
return new Promise((resolve, reject) => {
|
|
298
|
+
newStartDockerSkyrampWorkerWrapper.async(workerImage, workerTag, hostPost, targetNetworkName, testServiceAlias, (err, res) => {
|
|
299
|
+
if (err) {
|
|
300
|
+
reject(err);
|
|
301
|
+
} else if (res.error) {
|
|
302
|
+
reject(new Error(res.error));
|
|
303
|
+
} else {
|
|
304
|
+
resolve(res.container_name);
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Asynchronous method that removes a Docker container running the Skyramp worker.
|
|
312
|
+
*
|
|
313
|
+
* @param {string} containerName - The name of the Docker container to be removed.
|
|
314
|
+
* @returns {Promise} A promise that resolves when the container is successfully removed.
|
|
315
|
+
* @throws {Error} If an error occurs during the removal process.
|
|
316
|
+
* @throws {Error} If a non-empty response is received.
|
|
317
|
+
*/
|
|
318
|
+
async removeDockerSkyrampWorker(containerName) {
|
|
319
|
+
return new Promise((resolve, reject) => {
|
|
320
|
+
newDeleteDockerSkyrampWorkerWrapper.async(containerName, (err, res) => {
|
|
321
|
+
if (err) {
|
|
322
|
+
reject(err);
|
|
323
|
+
} else if (res) {
|
|
324
|
+
reject(new Error(res));
|
|
325
|
+
} else {
|
|
326
|
+
resolve();
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
async mockerApply(namespace, kubeConfig, kubeContext, clusterName, address, endpoint) {
|
|
333
|
+
const yamlContent = getYamlBytes(endpoint.mockDescription);
|
|
334
|
+
return this.applyMockDescription(namespace, kubeConfig, kubeContext, clusterName, address, yamlContent);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Applies a mock description to a specified namespace and address.
|
|
339
|
+
*
|
|
340
|
+
* @param {string} namespace - The namespace where the mock description will be applied.
|
|
341
|
+
* @param {string} address - The address to which the mock description will be applied.
|
|
342
|
+
* @param {object} response - The details of the mock response.
|
|
343
|
+
* @param {object} trafficConfig - The traffic configuration parameters.
|
|
344
|
+
* @returns {Promise} A Promise that resolves when the mock description is successfully applied.
|
|
345
|
+
*
|
|
346
|
+
*/
|
|
347
|
+
async mockerApplyV1(...args) {
|
|
348
|
+
let namespace, kubeConfig, kubeContext, clusterName, address, response, trafficConfig;
|
|
349
|
+
|
|
350
|
+
if (args.length === 1 && typeof args[0] === 'object') {
|
|
351
|
+
const options = args[0];
|
|
352
|
+
namespace = options.namespace;
|
|
353
|
+
kubeConfig = options.kubeConfig;
|
|
354
|
+
kubeContext = options.kubeContext;
|
|
355
|
+
clusterName = options.clusterName;
|
|
356
|
+
address = options.address;
|
|
357
|
+
response = options.response;
|
|
358
|
+
trafficConfig = options.trafficConfig;
|
|
359
|
+
} else {
|
|
360
|
+
[namespace, kubeConfig, kubeContext, clusterName, address, response, trafficConfig] = args;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const mockDescription = this.getMockDescriptionv1(response);
|
|
364
|
+
if (trafficConfig instanceof TrafficConfig) {
|
|
365
|
+
mockDescription.mock = { ...mockDescription.mock, ...trafficConfig.toJson() }
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const yamlContent = getYamlBytes(mockDescription);
|
|
369
|
+
return this.applyMockDescription(namespace, kubeConfig, kubeContext, clusterName, address, yamlContent);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// NPM only: for VS code extension use
|
|
373
|
+
async mockerApplyFromFile(namespace, kubeConfig, kubeContext, clusterName, address, filepath) {
|
|
374
|
+
const yamlContent = getYamlBytes(readDataFromFile(filepath)[0]);
|
|
375
|
+
return this.applyMockDescription(namespace, kubeConfig, kubeContext, clusterName, address, yamlContent);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
async applyMockDescription(namespace, kubeConfig, kubeContext, clusterName, address, yamlContent) {
|
|
379
|
+
return new Promise((resolve, reject) => {
|
|
380
|
+
applyMockDescriptionWrapper.async(namespace, kubeConfig, kubeContext, clusterName, address, yamlContent, '', '', (err, res) => {
|
|
381
|
+
if (err) {
|
|
382
|
+
reject(err);
|
|
383
|
+
} else if (res) {
|
|
384
|
+
reject(new Error(res));
|
|
385
|
+
} else {
|
|
386
|
+
resolve();
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
async testerStart(namespace, kubePath, kubeContext, clusterName, address, scenario) {
|
|
393
|
+
const preparedScenario = scenario.prepareTestDescription();
|
|
394
|
+
const testDescription = createTestDescriptionFromScenario({ scenario: preparedScenario });
|
|
395
|
+
const testYamlContent = getYamlBytes(testDescription);
|
|
396
|
+
return this.runTesterStart(namespace, kubePath, kubeContext, clusterName, address, testYamlContent, false);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Starts a test scenario in a specified namespace.
|
|
401
|
+
*
|
|
402
|
+
* @param {string|object} namespaceOrOptions - The namespace in which to start the test scenario or an options object.
|
|
403
|
+
* @param {string} [address] - The address of the target service to test.
|
|
404
|
+
* @param {object} [scenario] - The scenario object that defines the test steps and assertions.
|
|
405
|
+
* @param {string} [testName] - The name of the test.
|
|
406
|
+
* @param {object} [globalHeaders] - Optional global headers to be included in the test requests.
|
|
407
|
+
* @param {boolean} [generateTestReport] - Optional flag to generate a test report. Default is false.
|
|
408
|
+
* @param {boolean} [isDockerenv] - Optional flag to indicate if the test is running in a Docker environment. Default is false.
|
|
409
|
+
*
|
|
410
|
+
* @returns {Promise} - A promise that resolves when the test scenario is started.
|
|
411
|
+
*/
|
|
412
|
+
async testerStartV1(options = {}) {
|
|
413
|
+
const {
|
|
414
|
+
namespace = "",
|
|
415
|
+
address = "",
|
|
416
|
+
scenario,
|
|
417
|
+
testName,
|
|
418
|
+
globalHeaders = null,
|
|
419
|
+
globalVars = null, // eslint-disable-line no-unused-vars
|
|
420
|
+
generateTestReport = false,
|
|
421
|
+
isDockerenv = false,
|
|
422
|
+
kubePath = "",
|
|
423
|
+
kubeContext = "",
|
|
424
|
+
clusterName = ""
|
|
425
|
+
} = typeof options === 'object' ? options : {
|
|
426
|
+
namespace: options,
|
|
427
|
+
address: arguments[1],
|
|
428
|
+
scenario: arguments[2],
|
|
429
|
+
testName: arguments[3],
|
|
430
|
+
globalHeaders: arguments[4] || null,
|
|
431
|
+
globalVars: arguments[5] || null,
|
|
432
|
+
generateTestReport: arguments[6] || false,
|
|
433
|
+
isDockerenv: arguments[7] || false
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
const scenarioToUse = scenario || this.scenario;
|
|
437
|
+
const preparedScenario = scenarioToUse ? scenarioToUse.prepareTestDescription() : null;
|
|
438
|
+
|
|
439
|
+
const testDescription = createTestDescriptionFromScenario({ ...options, scenario: preparedScenario });
|
|
440
|
+
const testYamlContent = getYamlBytes(testDescription);
|
|
441
|
+
const stringifiedHeaders = JSON.stringify(globalHeaders);
|
|
442
|
+
|
|
443
|
+
return this.runTesterStartv1(namespace,
|
|
444
|
+
kubePath,
|
|
445
|
+
kubeContext,
|
|
446
|
+
clusterName,
|
|
447
|
+
address,
|
|
448
|
+
testYamlContent,
|
|
449
|
+
testName,
|
|
450
|
+
stringifiedHeaders,
|
|
451
|
+
generateTestReport,
|
|
452
|
+
isDockerenv
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// NPM only: for VS code extension use
|
|
457
|
+
async testerStartFromFile(namespace, kubePath, kubeContext, clusterName, address, filepath) {
|
|
458
|
+
const testDescription = readDataFromFile(filepath)[0]
|
|
459
|
+
const testYamlContent = getYamlBytes(testDescription);
|
|
460
|
+
return this.runTesterStart(namespace, kubePath, kubeContext, clusterName, address, testYamlContent, true);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
async runTesterStart(namespace, kubePath, kubeContext, clusterName, address, testYamlContent, generateReport) {
|
|
464
|
+
return new Promise((resolve, reject) => {
|
|
465
|
+
runTesterStartWrapper.async(namespace, kubePath, kubeContext, clusterName, address, testYamlContent, generateReport, (err, res) => {
|
|
466
|
+
if (err) {
|
|
467
|
+
reject(err);
|
|
468
|
+
} else if (res.error) {
|
|
469
|
+
reject(new Error(res.error));
|
|
470
|
+
} else {
|
|
471
|
+
resolve(res.tester_id);
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
async runTesterStartv1(namespace, kubePath, kubeContext, clusterName, address, testYamlContent, testName, globalHeaders, generateReport = true) {
|
|
478
|
+
return new Promise((resolve, reject) => {
|
|
479
|
+
runTesterStartWrapperv1.async(namespace, kubePath, kubeContext, clusterName, address, testYamlContent, "", testName, globalHeaders, generateReport, true, "", "", "", (err, res) => {
|
|
480
|
+
if (err) {
|
|
481
|
+
reject(err);
|
|
482
|
+
} else if (res.error) {
|
|
483
|
+
reject(new Error(res.error));
|
|
484
|
+
} else {
|
|
485
|
+
resolve(res.tester_id);
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// NPM only: for VS code extension use
|
|
492
|
+
async initTarget(taretName) {
|
|
493
|
+
return new Promise((resolve, reject) => {
|
|
494
|
+
initTargetWrapper.async(taretName, (err, res) => {
|
|
495
|
+
if (err) {
|
|
496
|
+
reject(err);
|
|
497
|
+
} else if (res) {
|
|
498
|
+
reject(new Error(res));
|
|
499
|
+
} else {
|
|
500
|
+
resolve();
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// NPM only: for VS code extension use
|
|
507
|
+
async deployTarget(targetDescription, namespace, workerImage = '', localImage = false) {
|
|
508
|
+
if (this.kubeConfigPath === null) {
|
|
509
|
+
throw new Error('No cluster to deploy target to.');
|
|
510
|
+
}
|
|
511
|
+
return new Promise((resolve, reject) => {
|
|
512
|
+
deployTargetWrapper.async(targetDescription, namespace, '', '', '', workerImage, localImage, (err, res) => {
|
|
513
|
+
if (err) {
|
|
514
|
+
reject(err);
|
|
515
|
+
} else if (res) {
|
|
516
|
+
reject(new Error(res));
|
|
517
|
+
} else {
|
|
518
|
+
resolve();
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// NPM only: for VS code extension use
|
|
525
|
+
async deleteTarget(targetDescription, namespace) {
|
|
526
|
+
if (this.kubeConfigPath === null) {
|
|
527
|
+
throw new Error('No cluster to delete target from.');
|
|
528
|
+
}
|
|
529
|
+
return new Promise((resolve, reject) => {
|
|
530
|
+
deleteTargetWrapper.async(targetDescription, namespace, '', '', '', (err, res) => {
|
|
531
|
+
if (err) {
|
|
532
|
+
reject(err);
|
|
533
|
+
} else if (res) {
|
|
534
|
+
reject(new Error(res));
|
|
535
|
+
} else {
|
|
536
|
+
resolve();
|
|
537
|
+
}
|
|
538
|
+
});
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
getMockDescriptionv1(responseOrResponses) {
|
|
543
|
+
const mockDescription = {
|
|
544
|
+
version: SKYRAMP_YAML_VERSION,
|
|
545
|
+
services: [],
|
|
546
|
+
endpoints: [],
|
|
547
|
+
responses: [],
|
|
548
|
+
mock: { responses: [] }
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
const processResponse = (response) => {
|
|
552
|
+
const endpoint = response?.endpointDescriptor?.endpoint;
|
|
553
|
+
const service = response?.endpointDescriptor?.services[0];
|
|
554
|
+
|
|
555
|
+
if (!mockDescription?.endpoints.some(ep => ep.name === endpoint.name)) {
|
|
556
|
+
mockDescription?.endpoints.push(endpoint);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
if (!mockDescription?.services?.some(s => s?.name === service?.name)) {
|
|
560
|
+
mockDescription?.services?.push(service);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
mockDescription?.responses?.push(response?.toJson());
|
|
564
|
+
let mockRes = { "responseName": response?.name }
|
|
565
|
+
if (response?.trafficConfig && response?.trafficConfig instanceof TrafficConfig) {
|
|
566
|
+
mockRes = { ...mockRes, ...response?.trafficConfig?.toJson() }
|
|
567
|
+
}
|
|
568
|
+
mockDescription?.mock?.responses?.push(mockRes);
|
|
569
|
+
};
|
|
570
|
+
if (Array.isArray(responseOrResponses)) {
|
|
571
|
+
responseOrResponses.forEach(processResponse);
|
|
572
|
+
} else {
|
|
573
|
+
processResponse(responseOrResponses);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
return mockDescription;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Generates test scenarios based on the provided parameters.
|
|
581
|
+
*
|
|
582
|
+
* @param {Protocol} protocol - The protocol to be used.
|
|
583
|
+
* @param {string} apiSchemaPath - The path to the API schema.
|
|
584
|
+
* @param {string} alias - The alias for the generated tests.
|
|
585
|
+
* @param {string} endpointPath - Endpoint REST path to filter for.
|
|
586
|
+
* @param {Language} language - The programming language for the generated tests.
|
|
587
|
+
* @param {string} tag - Tag to filter OpenAPI endpoint paths for.
|
|
588
|
+
* @param {string} sampleRequestPath - The path to the sample request.
|
|
589
|
+
* @param {int} port - The port number.
|
|
590
|
+
* @param {boolean} generateRobot - Whether to generate robot tests.
|
|
591
|
+
* @param {boolean} functionalScenario - Whether to generate functional scenarios.
|
|
592
|
+
* @param {boolean} negativeScenario - Whether to generate negative scenarios.
|
|
593
|
+
* @returns {Promise<string[]>} A promise that resolves with a list of generated test file paths.
|
|
594
|
+
*/
|
|
595
|
+
async testerGenerate(protocol, apiSchemaPath, alias, endpointPath, language, tag, sampleRequestPath, port, generateRobot, functionalScenario, negativeScenario) {
|
|
596
|
+
return new Promise((resolve, reject) => {
|
|
597
|
+
runTesterGenerateRestWrapper.async(protocol, apiSchemaPath, alias, endpointPath, language, tag, sampleRequestPath, this.projectPath, port, generateRobot, functionalScenario, negativeScenario, (err, res) => {
|
|
598
|
+
if (err) {
|
|
599
|
+
console.error(`Error generating tests: ${err.name} - ${err.message}`);
|
|
600
|
+
reject(err);
|
|
601
|
+
} else if (res.error) {
|
|
602
|
+
console.error(`Error generating tests: ${res.error}`);
|
|
603
|
+
reject(new Error(res.error));
|
|
604
|
+
} else {
|
|
605
|
+
console.log(`Test generation completed successfully. Generated files: ${res.generated_files}`);
|
|
606
|
+
|
|
607
|
+
const generatedTestNames = res.generated_files.split(',');
|
|
608
|
+
resolve(generatedTestNames);
|
|
609
|
+
}
|
|
610
|
+
});
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Sends a request to a Skyramp worker using the V2 API
|
|
616
|
+
* @param {Object} options - The options for sending the request
|
|
617
|
+
* @param {string} [options.address=""] - The address of the worker to send the request to
|
|
618
|
+
* @param {string} [options.namespace=""] - The namespace where the worker is deployed
|
|
619
|
+
* @param {string} [options.kubePath=""] - The path to the kubeconfig file
|
|
620
|
+
* @param {string} [options.kubeContext=""] - The kubernetes context to use
|
|
621
|
+
* @param {string} [options.clusterName=""] - The name of the kubernetes cluster
|
|
622
|
+
* @param {string} [options.dockerNetwork=""] - The docker network where the worker is deployed
|
|
623
|
+
* @param {string} [options.workerImage=""] - The worker image to use
|
|
624
|
+
* @param {string} [options.local_image=""] - The flag to indicate if the worker image is local
|
|
625
|
+
* @returns {Promise<string>} A promise that resolves with the response from the worker
|
|
626
|
+
*/
|
|
627
|
+
async sendRequest(options) {
|
|
628
|
+
const req = new RequestV2(options);
|
|
629
|
+
return new Promise((resolve, reject) => {
|
|
630
|
+
const jsonRequest = req.toJson();
|
|
631
|
+
sendRequestWrapper.async(this.address, this.namespace, this.kubeconfigPath, this.context, this.clusterName, jsonRequest, this.dockerNetwork, this.workerImage, this.clientID, this.local_image, (err, res) => {
|
|
632
|
+
if (err) {
|
|
633
|
+
reject(err);
|
|
634
|
+
} else if (res) {
|
|
635
|
+
if (res.error != null) {
|
|
636
|
+
if (res.response != null) {
|
|
637
|
+
const jsonResponse = JSON.parse(res.response);
|
|
638
|
+
const response = new ResponseV2(jsonResponse);
|
|
639
|
+
this.failedResponses.push(response)
|
|
640
|
+
}
|
|
641
|
+
reject(res.error);
|
|
642
|
+
} else if (res.response != null) {
|
|
643
|
+
const jsonResponse = JSON.parse(res.response);
|
|
644
|
+
const response = new ResponseV2(jsonResponse);
|
|
645
|
+
if (response.error != "") {
|
|
646
|
+
this.failedResponses.push(response)
|
|
647
|
+
}
|
|
648
|
+
resolve(response);
|
|
649
|
+
} else {
|
|
650
|
+
reject(new Error('failed to send request'));
|
|
651
|
+
}
|
|
652
|
+
} else {
|
|
653
|
+
reject(new Error('failed to send request'));
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
isSuccess() {
|
|
660
|
+
return this.failedResponses.length == 0
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
getFailedResponses() {
|
|
664
|
+
return this.failedResponses
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
async deployDashboard(network) {
|
|
668
|
+
if (network === undefined || network === null || network === "") {
|
|
669
|
+
network = "skyramp-dashboard"
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
if (this.kubeconfigPath !== null || this.kubeconfigPath !== undefined) {
|
|
673
|
+
network = "skyramp"
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
return new Promise((resolve, reject) => {
|
|
677
|
+
if (this.kubeconfigPath === null || this.kubeconfigPath === undefined) {
|
|
678
|
+
deployDockerDashboardWrapper.async(network, (err, res) => {
|
|
137
679
|
if (err) {
|
|
138
680
|
reject(err);
|
|
139
|
-
} else if (res) {
|
|
140
|
-
reject(new Error(res));
|
|
141
681
|
} else {
|
|
142
|
-
resolve();
|
|
143
|
-
this.mockedEndpoints.push(endpoint);
|
|
682
|
+
resolve(res);
|
|
144
683
|
}
|
|
145
684
|
});
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
|
|
685
|
+
} else {
|
|
686
|
+
deployK8sDashboardWrapper.async(network, (err, res) => {
|
|
687
|
+
if (err) {
|
|
688
|
+
reject(err);
|
|
689
|
+
} else {
|
|
690
|
+
resolve(res);
|
|
691
|
+
}
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Analyzes an OpenAPI schema and URI.
|
|
699
|
+
*
|
|
700
|
+
* @param {Object} options - The options for analysis.
|
|
701
|
+
* @param {string} options.apiSchema - The OpenAPI schema to analyze.
|
|
702
|
+
* @param {string} options.uri - The URI to analyze.
|
|
703
|
+
* @returns {Promise<string>} A promise that resolves with the analysis result.
|
|
704
|
+
*/
|
|
705
|
+
async analyzeOpenapi(options) {
|
|
706
|
+
return new Promise((resolve, reject) => {
|
|
707
|
+
analyzeOpenapiWrapper.async(
|
|
708
|
+
options.apiSchema || "",
|
|
709
|
+
options.uri || "",
|
|
710
|
+
(err, res) => {
|
|
711
|
+
if (err) {
|
|
712
|
+
reject(err);
|
|
713
|
+
} else if (res.error) {
|
|
714
|
+
reject(new Error(res));
|
|
715
|
+
} else {
|
|
716
|
+
resolve(res);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
);
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
async traceCollect(options) {
|
|
724
|
+
return new Promise((resolve, reject) => {
|
|
725
|
+
traceCollectWrapper.async(
|
|
726
|
+
options.output || "",
|
|
727
|
+
options.workerContainerName || "",
|
|
728
|
+
options.playwright || false,
|
|
729
|
+
options.playwrightOutput || "",
|
|
730
|
+
(err, res) => {
|
|
731
|
+
if (err) {
|
|
732
|
+
reject(err);
|
|
733
|
+
} else {
|
|
734
|
+
resolve(res);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
);
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
async generateRestTest(options) {
|
|
742
|
+
return new Promise((resolve, reject) => {
|
|
743
|
+
generateRestTestWrapper.async(
|
|
744
|
+
options.testType || "",
|
|
745
|
+
options.uri || "",
|
|
746
|
+
options.method || "",
|
|
747
|
+
options.language || "",
|
|
748
|
+
options.framework || "",
|
|
749
|
+
options.output || "",
|
|
750
|
+
options.outputDir || "",
|
|
751
|
+
options.runtime || "",
|
|
752
|
+
options.dockerNetwork || "",
|
|
753
|
+
options.dockerWorkerPort || 0,
|
|
754
|
+
options.k8sNamespace || "",
|
|
755
|
+
options.k8sConfig || "",
|
|
756
|
+
options.k8sContext || "",
|
|
757
|
+
options.authHeader || "",
|
|
758
|
+
options.authType || "",
|
|
759
|
+
options.requestData || "",
|
|
760
|
+
options.responseData || "",
|
|
761
|
+
options.responseStatusCode || "",
|
|
762
|
+
options.force || false,
|
|
763
|
+
options.deployDashboard || false,
|
|
764
|
+
options.formParams || "",
|
|
765
|
+
options.pathParams || "",
|
|
766
|
+
options.queryParams || "",
|
|
767
|
+
JSON.stringify(options.apiSchema || []),
|
|
768
|
+
options.traceFilePath || "",
|
|
769
|
+
JSON.stringify(options.generateInclude || []),
|
|
770
|
+
JSON.stringify(options.generateExclude || []),
|
|
771
|
+
JSON.stringify(options.generateNoProxy || []),
|
|
772
|
+
options.generateInsecure || false,
|
|
773
|
+
options.assertOption || "",
|
|
774
|
+
options.playwright || false,
|
|
775
|
+
options.playwrightOutput || "",
|
|
776
|
+
options.playwrightInput || "",
|
|
777
|
+
options.loadCount || "0",
|
|
778
|
+
options.loadDuration || "0",
|
|
779
|
+
options.loadNumThreads || "0",
|
|
780
|
+
options.loadRampupDuration || "0",
|
|
781
|
+
options.loadRampupInterval || "0",
|
|
782
|
+
options.loadTargetRPS || "0",
|
|
783
|
+
options.unblock || false,
|
|
784
|
+
options.entrypoint || "",
|
|
785
|
+
options.accessToken || "",
|
|
786
|
+
(err, res) => {
|
|
787
|
+
if (err) {
|
|
788
|
+
reject(err);
|
|
789
|
+
} else {
|
|
790
|
+
resolve(res);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
);
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/**
|
|
798
|
+
* Sends a scenario for load testing using the V2 API
|
|
799
|
+
* @param {Object|Array} scenario - The scenario object or array of scenarios to run
|
|
800
|
+
* @param {Object} [options={}] - Additional options for the load test
|
|
801
|
+
* @param {string} [options.until] - The condition to terminate the test
|
|
802
|
+
* @param {LoadTestConfig} [options.loadTestConfig] - The load test configuration
|
|
803
|
+
* @param {string} [options.dependenciesFilepath=""] - The path to the dependencies file
|
|
804
|
+
* @param {boolean} [options.blocked=true] - Whether to block until test completion
|
|
805
|
+
* @param {boolean} [options.debug=true] - Whether to print debug information
|
|
806
|
+
* @param {boolean} [options.skipCertVerification=false] - Whether to skip SSL verification
|
|
807
|
+
* @returns {Promise<AsyncTestStatus>} A promise that resolves with the async test status
|
|
808
|
+
*/
|
|
809
|
+
async sendScenario(scenario, options = {}) {
|
|
810
|
+
const {
|
|
811
|
+
until,
|
|
812
|
+
loadTestConfig,
|
|
813
|
+
dependenciesFilepath = "",
|
|
814
|
+
blocked = true,
|
|
815
|
+
debug = true,
|
|
816
|
+
skipCertVerification = false
|
|
817
|
+
} = options;
|
|
818
|
+
|
|
819
|
+
if (debug) { /* add to api for future use */}
|
|
820
|
+
let configJson = "";
|
|
821
|
+
if (loadTestConfig) {
|
|
822
|
+
configJson = loadTestConfig.toJson();
|
|
149
823
|
}
|
|
824
|
+
|
|
825
|
+
// Ensure scenario is an array
|
|
826
|
+
const scenarioList = Array.isArray(scenario) ? scenario : [scenario];
|
|
827
|
+
|
|
828
|
+
// Process scenarios to match expected AsyncScenario format
|
|
829
|
+
const processedScenarios = scenarioList.map(_scenario => {
|
|
830
|
+
// If scenario has toJson method, use it for proper formatting
|
|
831
|
+
if (typeof _scenario.toJson === 'function') {
|
|
832
|
+
const scenarioJson = _scenario.toJson();
|
|
833
|
+
|
|
834
|
+
return {
|
|
835
|
+
...scenarioJson,
|
|
836
|
+
until: until || scenarioJson.until
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// Fallback for scenarios without toJson method
|
|
841
|
+
return {
|
|
842
|
+
name: _scenario.name,
|
|
843
|
+
steps: _scenario.steps || [],
|
|
844
|
+
vars: _scenario.vars || {},
|
|
845
|
+
until: until || _scenario.until || "",
|
|
846
|
+
maxRetries: _scenario.maxRetries || 5,
|
|
847
|
+
interval: _scenario.retryInterval || 1,
|
|
848
|
+
ignoreFailure: _scenario.ignoreFailure || false
|
|
849
|
+
};
|
|
850
|
+
});
|
|
851
|
+
|
|
852
|
+
return new Promise((resolve, reject) => {
|
|
853
|
+
sendScenarioWrapper.async(
|
|
854
|
+
this.address || "",
|
|
855
|
+
this.dockerNetwork || "",
|
|
856
|
+
this.namespace || "",
|
|
857
|
+
this.kubeconfigPath || "",
|
|
858
|
+
this.context || "",
|
|
859
|
+
this.clusterName || "",
|
|
860
|
+
JSON.stringify(processedScenarios),
|
|
861
|
+
"",
|
|
862
|
+
configJson,
|
|
863
|
+
dependenciesFilepath,
|
|
864
|
+
this.workerImage || "",
|
|
865
|
+
blocked,
|
|
866
|
+
this.local_image || false,
|
|
867
|
+
skipCertVerification,
|
|
868
|
+
(err, res) => {
|
|
869
|
+
if (err) {
|
|
870
|
+
reject(err);
|
|
871
|
+
} else if (res.error) {
|
|
872
|
+
reject(new Error(res.error));
|
|
873
|
+
} else if (res.response) {
|
|
874
|
+
const AsyncTestStatus = require('./AsyncTestStatus');
|
|
875
|
+
const jsonResponse = JSON.parse(res.response);
|
|
876
|
+
const testStatus = AsyncTestStatus.create(jsonResponse);
|
|
877
|
+
resolve(testStatus);
|
|
878
|
+
} else {
|
|
879
|
+
reject(new Error('Failed to send scenario'));
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
);
|
|
883
|
+
});
|
|
150
884
|
}
|
|
151
885
|
}
|
|
152
886
|
|