@depup/artillery 2.0.30-depup.0

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.
Files changed (90) hide show
  1. package/README.md +63 -0
  2. package/bin/run +29 -0
  3. package/bin/run.cmd +3 -0
  4. package/changes.json +138 -0
  5. package/console-reporter.js +1 -0
  6. package/lib/artillery-global.js +33 -0
  7. package/lib/cli/banner.js +8 -0
  8. package/lib/cli/common-flags.js +80 -0
  9. package/lib/cli/hooks/version.js +20 -0
  10. package/lib/cmds/dino.js +109 -0
  11. package/lib/cmds/quick.js +122 -0
  12. package/lib/cmds/report.js +34 -0
  13. package/lib/cmds/run-aci.js +91 -0
  14. package/lib/cmds/run-fargate.js +192 -0
  15. package/lib/cmds/run-lambda.js +96 -0
  16. package/lib/cmds/run.js +671 -0
  17. package/lib/console-capture.js +92 -0
  18. package/lib/console-reporter.js +438 -0
  19. package/lib/create-bom/built-in-plugins.js +12 -0
  20. package/lib/create-bom/create-bom.js +301 -0
  21. package/lib/dispatcher.js +9 -0
  22. package/lib/dist.js +222 -0
  23. package/lib/index.js +5 -0
  24. package/lib/launch-platform.js +439 -0
  25. package/lib/load-plugins.js +113 -0
  26. package/lib/platform/aws/aws-cloudwatch.js +106 -0
  27. package/lib/platform/aws/aws-create-sqs-queue.js +58 -0
  28. package/lib/platform/aws/aws-ensure-s3-bucket-exists.js +78 -0
  29. package/lib/platform/aws/aws-get-account-id.js +26 -0
  30. package/lib/platform/aws/aws-get-bucket-region.js +18 -0
  31. package/lib/platform/aws/aws-get-credentials.js +28 -0
  32. package/lib/platform/aws/aws-get-default-region.js +26 -0
  33. package/lib/platform/aws/aws-whoami.js +15 -0
  34. package/lib/platform/aws/constants.js +7 -0
  35. package/lib/platform/aws/iam-cf-templates/aws-iam-fargate-cf-template.yml +219 -0
  36. package/lib/platform/aws/iam-cf-templates/aws-iam-lambda-cf-template.yml +125 -0
  37. package/lib/platform/aws/iam-cf-templates/gh-oidc-fargate.yml +241 -0
  38. package/lib/platform/aws/iam-cf-templates/gh-oidc-lambda.yml +153 -0
  39. package/lib/platform/aws-ecs/ecs.js +247 -0
  40. package/lib/platform/aws-ecs/legacy/aws-util.js +134 -0
  41. package/lib/platform/aws-ecs/legacy/bom.js +528 -0
  42. package/lib/platform/aws-ecs/legacy/constants.js +27 -0
  43. package/lib/platform/aws-ecs/legacy/create-s3-client.js +24 -0
  44. package/lib/platform/aws-ecs/legacy/create-test.js +247 -0
  45. package/lib/platform/aws-ecs/legacy/errors.js +34 -0
  46. package/lib/platform/aws-ecs/legacy/find-public-subnets.js +149 -0
  47. package/lib/platform/aws-ecs/legacy/plugins/artillery-plugin-inspect-script/index.js +27 -0
  48. package/lib/platform/aws-ecs/legacy/plugins/artillery-plugin-sqs-reporter/azure-aqs.js +80 -0
  49. package/lib/platform/aws-ecs/legacy/plugins/artillery-plugin-sqs-reporter/index.js +202 -0
  50. package/lib/platform/aws-ecs/legacy/plugins.js +16 -0
  51. package/lib/platform/aws-ecs/legacy/run-cluster.js +1994 -0
  52. package/lib/platform/aws-ecs/legacy/sqs-reporter.js +401 -0
  53. package/lib/platform/aws-ecs/legacy/tags.js +22 -0
  54. package/lib/platform/aws-ecs/legacy/test-run-status.js +9 -0
  55. package/lib/platform/aws-ecs/legacy/time.js +67 -0
  56. package/lib/platform/aws-ecs/legacy/util.js +97 -0
  57. package/lib/platform/aws-ecs/worker/Dockerfile +64 -0
  58. package/lib/platform/aws-ecs/worker/helpers.sh +80 -0
  59. package/lib/platform/aws-ecs/worker/loadgen-worker +656 -0
  60. package/lib/platform/aws-lambda/dependencies.js +130 -0
  61. package/lib/platform/aws-lambda/index.js +734 -0
  62. package/lib/platform/aws-lambda/lambda-handler/a9-handler-dependencies.js +73 -0
  63. package/lib/platform/aws-lambda/lambda-handler/a9-handler-helpers.js +43 -0
  64. package/lib/platform/aws-lambda/lambda-handler/a9-handler-index.js +235 -0
  65. package/lib/platform/aws-lambda/lambda-handler/package.json +15 -0
  66. package/lib/platform/aws-lambda/prices.js +29 -0
  67. package/lib/platform/az/aci.js +694 -0
  68. package/lib/platform/az/aqs-queue-consumer.js +88 -0
  69. package/lib/platform/az/regions.js +52 -0
  70. package/lib/platform/cloud/api.js +72 -0
  71. package/lib/platform/cloud/cloud.js +448 -0
  72. package/lib/platform/cloud/http-client.js +19 -0
  73. package/lib/platform/local/artillery-worker-local.js +154 -0
  74. package/lib/platform/local/index.js +174 -0
  75. package/lib/platform/local/worker.js +261 -0
  76. package/lib/platform/worker-states.js +13 -0
  77. package/lib/queue-consumer/index.js +56 -0
  78. package/lib/stash.js +41 -0
  79. package/lib/telemetry.js +78 -0
  80. package/lib/util/await-on-ee.js +24 -0
  81. package/lib/util/generate-id.js +9 -0
  82. package/lib/util/parse-tag-string.js +21 -0
  83. package/lib/util/prepare-test-execution-plan.js +216 -0
  84. package/lib/util/sleep.js +7 -0
  85. package/lib/util/validate-script.js +132 -0
  86. package/lib/util.js +294 -0
  87. package/lib/utils-config.js +31 -0
  88. package/package.json +323 -0
  89. package/types.d.ts +317 -0
  90. package/util.js +1 -0
@@ -0,0 +1,78 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
+
5
+
6
+
7
+ const { version: artilleryVersion } = require('../package.json');
8
+ const { isCI, name: ciName } = require('ci-info');
9
+ const debug = require('debug')('telemetry');
10
+
11
+ const POSTHOG_TOKEN = '_uzX-_WJoVmE_tsLvu0OFD2tpd0HGz72D5sU1zM2hbs';
12
+
13
+ const notice = () => {
14
+ console.log(
15
+ 'Anonymized telemetry is on. Learn more: https://artillery.io/docs/resources/core/telemetry.html'
16
+ );
17
+ };
18
+
19
+ const isEnabled = () => {
20
+ return typeof process.env.ARTILLERY_DISABLE_TELEMETRY === 'undefined';
21
+ };
22
+
23
+ async function capture(eventName, data) {
24
+ if (!isEnabled()) {
25
+ return;
26
+ }
27
+
28
+ const debugEnabled =
29
+ typeof process.env.ARTILLERY_TELEMETRY_DEBUG !== 'undefined';
30
+
31
+ const url = 'https://us.i.posthog.com/i/v0/e/';
32
+ const headers = {
33
+ 'Content-Type': 'application/json'
34
+ };
35
+
36
+ let telemetryDefaults = {};
37
+ try {
38
+ telemetryDefaults = JSON.parse(process.env.ARTILLERY_TELEMETRY_DEFAULTS);
39
+ } catch (_err) {
40
+ /* empty */
41
+ }
42
+
43
+ const properties = Object.assign(
44
+ {
45
+ ...data,
46
+ $process_person_profile: false,
47
+ version: artilleryVersion,
48
+ os: process.platform,
49
+ isCi: isCI,
50
+ ciName: isCI ? ciName : undefined,
51
+ $ip: 'not-collected'
52
+ },
53
+ telemetryDefaults
54
+ );
55
+
56
+ const payload = {
57
+ api_key: POSTHOG_TOKEN,
58
+ event: eventName,
59
+ distinct_id: data.distinctId || 'artillery-core',
60
+ properties
61
+ };
62
+
63
+ if (debugEnabled) {
64
+ console.log(`Telemetry data: ${JSON.stringify(payload.properties)}`);
65
+ }
66
+
67
+ try {
68
+ await fetch(url, {
69
+ method: 'POST',
70
+ headers: headers,
71
+ body: JSON.stringify(payload)
72
+ });
73
+ } catch (err) {
74
+ debug(err);
75
+ }
76
+ }
77
+
78
+ module.exports = { notice, capture, isEnabled };
@@ -0,0 +1,24 @@
1
+ const sleep = require('./sleep');
2
+
3
+ async function awaitOnEE(ee, message, pollMs = 1000, maxWaitMs = Infinity) {
4
+ let messageFired = false;
5
+ let args = null;
6
+ let waitedMs = 0;
7
+
8
+ ee.once(message, (...eventArgs) => {
9
+ messageFired = true;
10
+ args = eventArgs;
11
+ });
12
+
13
+ while (true && waitedMs < maxWaitMs) {
14
+ if (messageFired) {
15
+ break;
16
+ }
17
+ await sleep(pollMs);
18
+ waitedMs += pollMs;
19
+ }
20
+
21
+ return args;
22
+ }
23
+
24
+ module.exports = awaitOnEE;
@@ -0,0 +1,9 @@
1
+ const { customAlphabet } = require('nanoid');
2
+
3
+ function generateId(prefix = '') {
4
+ const idf = customAlphabet('3456789abcdefghjkmnpqrtwxyz');
5
+ const testRunId = `${prefix}${idf(4)}_${idf(29)}_${idf(4)}`;
6
+ return testRunId;
7
+ }
8
+
9
+ module.exports = generateId;
@@ -0,0 +1,21 @@
1
+ module.exports = function parseTagString(input) {
2
+ const result = {
3
+ tags: [],
4
+ errors: []
5
+ };
6
+
7
+ if (!input) {
8
+ return result;
9
+ }
10
+
11
+ const tagList = input.split(',').map((x) => x.trim());
12
+ for (const t of tagList) {
13
+ const cs = t.split(':');
14
+ if (cs.length !== 2) {
15
+ result.errors.push(t);
16
+ } else {
17
+ result.tags.push({ name: cs[0].trim(), value: cs[1].trim() });
18
+ }
19
+ }
20
+ return result;
21
+ };
@@ -0,0 +1,216 @@
1
+ const csv = require('csv-parse');
2
+ const fs = require('node:fs');
3
+ const path = require('node:path');
4
+ const p = require('node:util').promisify;
5
+ const debug = require('debug')('artillery');
6
+
7
+ const {
8
+ readScript,
9
+ parseScript,
10
+ addOverrides,
11
+ addVariables,
12
+ addDefaultPlugins,
13
+ resolveConfigTemplates,
14
+ checkConfig,
15
+ resolveConfigPath
16
+ } = require('../../util');
17
+
18
+ const validateScript = require('./validate-script');
19
+
20
+ const _ = require('lodash');
21
+
22
+ async function prepareTestExecutionPlan(inputFiles, flags, _args) {
23
+ const scriptPath = inputFiles[0];
24
+ let script1 = {};
25
+
26
+ for (const fn of inputFiles) {
27
+ const fn2 = fn.toLowerCase();
28
+ const absoluteFn = path.resolve(process.cwd(), fn);
29
+ if (
30
+ fn2.endsWith('.yml') ||
31
+ fn2.endsWith('.yaml') ||
32
+ fn2.endsWith('.json')
33
+ ) {
34
+ const data = await readScript(absoluteFn);
35
+ const parsedData = await parseScript(data);
36
+ script1 = _.merge(script1, parsedData);
37
+ } else {
38
+ if (fn2.endsWith('.js')) {
39
+ const parsedData = require(absoluteFn);
40
+ script1 = _.merge(script1, parsedData);
41
+ } else if (fn2.endsWith('.ts')) {
42
+ const outputPath = path.join(
43
+ path.dirname(absoluteFn),
44
+ `dist/${path.basename(fn)}.js`
45
+ );
46
+
47
+ const entryPoint = path.resolve(process.cwd(), fn);
48
+ // TODO: external packages will have to be specified externally to the script
49
+ transpileTypeScript(entryPoint, outputPath, []);
50
+ debug('transpiled TypeScript file into JS. Bundled file:', outputPath);
51
+ const parsedData = require(outputPath);
52
+ script1 = _.merge(script1, parsedData);
53
+ // These magic properties are used by the worker to load the transpiled file
54
+ script1.__transpiledTypeScriptPath = outputPath;
55
+ script1.__originalScriptPath = entryPoint;
56
+ } else {
57
+ console.log('Unknown file type', fn);
58
+ console.log(
59
+ 'Only JSON (.json), YAML (.yml/.yaml) and TypeScript (.ts) files are supported'
60
+ );
61
+ console.log('https://docs.art/e/file-types');
62
+ throw new Error('Unknown file type');
63
+ }
64
+ }
65
+ }
66
+
67
+ // We run the check here because subsequent steps can overwrite the target to undefined in
68
+ // cases where the value of config.target is set to a value from the environment which
69
+ // is not available at this point in time. Example: target is set to an environment variable
70
+ // the value of which is only available at runtime in AWS Fargate
71
+ const hasOriginalTarget =
72
+ typeof script1.config.target !== 'undefined' ||
73
+ typeof script1.config.environments?.[flags.environment]?.target !==
74
+ 'undefined';
75
+
76
+ script1 = await checkConfig(script1, scriptPath, flags);
77
+
78
+ const script2 = await resolveConfigPath(script1, flags, scriptPath);
79
+
80
+ const script3 = await addOverrides(script2, flags);
81
+ const script4 = await addVariables(script3, flags);
82
+ // The resolveConfigTemplates function expects the config and script path to be passed explicitly because it is used in Fargate as well where the two arguments will not be available on the script
83
+ const script5 = await resolveConfigTemplates(
84
+ script4,
85
+ flags,
86
+ script4._configPath,
87
+ script4._scriptPath
88
+ );
89
+
90
+ if (!script5.config.target && !hasOriginalTarget) {
91
+ throw new Error('No target specified and no environment chosen');
92
+ }
93
+
94
+ const validationError = validateScript(script5);
95
+
96
+ if (validationError) {
97
+ console.log(`Scenario validation error: ${validationError}`);
98
+
99
+ process.exit(1);
100
+ }
101
+
102
+ const script6 = await readPayload(script5);
103
+
104
+ if (typeof script6.config.phases === 'undefined' || flags.solo) {
105
+ script6.config.phases = [
106
+ {
107
+ duration: 1,
108
+ arrivalCount: 1
109
+ }
110
+ ];
111
+ }
112
+
113
+ script6.config.statsInterval = script6.config.statsInterval || 30;
114
+
115
+ const script7 = addDefaultPlugins(script5);
116
+ const script8 = replaceProcessorIfTypescript(script7, scriptPath);
117
+
118
+ return script8;
119
+ }
120
+
121
+ async function readPayload(script) {
122
+ if (!script.config.payload) {
123
+ return script;
124
+ }
125
+
126
+ for (const payloadSpec of script.config.payload) {
127
+ const data = fs.readFileSync(payloadSpec.path, 'utf-8');
128
+
129
+ const csvOpts = Object.assign(
130
+ {
131
+ skip_empty_lines:
132
+ typeof payloadSpec.skipEmptyLines === 'undefined'
133
+ ? true
134
+ : payloadSpec.skipEmptyLines,
135
+ cast: typeof payloadSpec.cast === 'undefined' ? true : payloadSpec.cast,
136
+ from_line: payloadSpec.skipHeader === true ? 2 : 1,
137
+ delimiter: payloadSpec.delimiter || ','
138
+ },
139
+ payloadSpec.options
140
+ );
141
+ const parsedData = await p(csv)(data, csvOpts);
142
+ payloadSpec.data = parsedData;
143
+ }
144
+
145
+ return script;
146
+ }
147
+
148
+ function transpileTypeScript(entryPoint, outputPath, userExternalPackages) {
149
+ const esbuild = require('esbuild-wasm');
150
+
151
+ esbuild.buildSync({
152
+ entryPoints: [entryPoint],
153
+ outfile: outputPath,
154
+ bundle: true,
155
+ platform: 'node',
156
+ format: 'cjs',
157
+ sourcemap: 'inline',
158
+ external: ['@playwright/test', ...userExternalPackages]
159
+ });
160
+
161
+ return outputPath;
162
+ }
163
+
164
+ function replaceProcessorIfTypescript(script, scriptPath) {
165
+ const relativeProcessorPath = script.config.processor;
166
+ const userExternalPackages = script.config.bundling?.external || [];
167
+
168
+ if (!relativeProcessorPath) {
169
+ return script;
170
+ }
171
+ const extensionType = path.extname(relativeProcessorPath);
172
+
173
+ if (extensionType !== '.ts') {
174
+ return script;
175
+ }
176
+
177
+ const actualProcessorPath = path.resolve(
178
+ path.dirname(scriptPath),
179
+ relativeProcessorPath
180
+ );
181
+ const processorFileName = path.basename(actualProcessorPath, extensionType);
182
+
183
+ const processorDir = path.dirname(actualProcessorPath);
184
+ const newProcessorPath = path.join(
185
+ processorDir,
186
+ `dist/${processorFileName}.js`
187
+ );
188
+
189
+ //TODO: move require to top of file when Lambda bundle size issue is solved
190
+ //must be conditionally required for now as this package is removed in Lambda for now to avoid bigger package sizes
191
+ const esbuild = require('esbuild-wasm');
192
+
193
+ try {
194
+ esbuild.buildSync({
195
+ entryPoints: [actualProcessorPath],
196
+ outfile: newProcessorPath,
197
+ bundle: true,
198
+ platform: 'node',
199
+ format: 'cjs',
200
+ sourcemap: 'inline',
201
+ external: ['@playwright/test', ...userExternalPackages]
202
+ });
203
+ } catch (error) {
204
+ throw new Error(`Failed to compile Typescript processor\n${error.message}`);
205
+ }
206
+
207
+ global.artillery.hasTypescriptProcessor = newProcessorPath;
208
+ console.log(
209
+ `Bundled Typescript file into JS. New processor path: ${newProcessorPath}`
210
+ );
211
+
212
+ script.config.processor = newProcessorPath;
213
+ return script;
214
+ }
215
+
216
+ module.exports = prepareTestExecutionPlan;
@@ -0,0 +1,7 @@
1
+ async function sleep(ms) {
2
+ return new Promise((resolve) => {
3
+ setTimeout(resolve, ms);
4
+ });
5
+ }
6
+
7
+ module.exports = sleep;
@@ -0,0 +1,132 @@
1
+ const Joi = require('joi').defaults((schema) =>
2
+ schema.options({ allowUnknown: true, abortEarly: true })
3
+ );
4
+
5
+ const config = Joi.object({
6
+ target: Joi.string().when('environments', {
7
+ not: Joi.exist(),
8
+ then: Joi.required()
9
+ }),
10
+ http: Joi.object({
11
+ extendedMetrics: Joi.boolean(),
12
+ maxSockets: Joi.number(),
13
+ timeout: Joi.alternatives(Joi.number(), Joi.string())
14
+ }),
15
+ environments: Joi.object(),
16
+ processor: Joi.string(),
17
+ phases: Joi.array(),
18
+ engines: Joi.object()
19
+ // payload: Joi.alternatives(Joi.object(), Joi.array())
20
+ });
21
+
22
+ const capture = Joi.object({
23
+ as: Joi.string().required()
24
+ });
25
+
26
+ const httpMethodProps = {
27
+ url: Joi.string().required(),
28
+ headers: Joi.object(),
29
+ cookie: Joi.object(),
30
+ followRedirect: Joi.boolean(),
31
+ qs: Joi.object(),
32
+ gzip: Joi.boolean(),
33
+ auth: Joi.object({
34
+ user: Joi.string(),
35
+ pass: Joi.string()
36
+ }),
37
+ beforeRequest: Joi.array().items(Joi.string()).single(),
38
+ afterResponse: Joi.array().items(Joi.string()).single(),
39
+ capture: Joi.array().items(capture).single()
40
+ };
41
+
42
+ const httpItems = {
43
+ get: Joi.object(httpMethodProps),
44
+ post: Joi.object(httpMethodProps),
45
+ put: Joi.object(httpMethodProps),
46
+ patch: Joi.object(httpMethodProps),
47
+ delete: Joi.object(httpMethodProps)
48
+ };
49
+
50
+ const socketioItems = {
51
+ emit: Joi.any().when(Joi.ref('....engine'), {
52
+ is: 'socketio',
53
+ then: Joi.alternatives(
54
+ Joi.object({
55
+ channel: Joi.string(),
56
+ concat: Joi.boolean(),
57
+ data: Joi.any()
58
+ }),
59
+ Joi.array().items(Joi.string())
60
+ ),
61
+ otherwise: Joi.any()
62
+ })
63
+ };
64
+
65
+ const wsItems = {
66
+ connect: Joi.any().when(Joi.ref('....engine'), {
67
+ is: 'ws',
68
+ then: Joi.alternatives(Joi.object(), Joi.string()),
69
+ otherwise: Joi.any()
70
+ }),
71
+ send: Joi.any()
72
+ };
73
+
74
+ const flowItemSchema = Joi.object({
75
+ function: Joi.string(),
76
+ log: Joi.string(),
77
+ think: Joi.alternatives(Joi.number(), Joi.string()),
78
+ loop: Joi.array(),
79
+ ...httpItems,
80
+ ...wsItems,
81
+ ...socketioItems
82
+ }).when('.loop', {
83
+ is: Joi.exist(),
84
+ then: Joi.object({
85
+ count: Joi.alternatives(Joi.number(), Joi.string()),
86
+ over: Joi.alternatives(Joi.array(), Joi.string())
87
+ }),
88
+ otherwise: Joi.when('...engine', {
89
+ is: Joi.exist().valid('socketio'),
90
+ then: Joi.object().max(4),
91
+ otherwise: Joi.object().length(1)
92
+ })
93
+ });
94
+
95
+ const scenarioItem = Joi.object({
96
+ name: Joi.string(),
97
+ engine: Joi.string(),
98
+ beforeScenario: Joi.array().items(Joi.string()).single(),
99
+ afterScenario: Joi.array().items(Joi.string()).single(),
100
+ flow: Joi.any().when('engine', {
101
+ is: Joi.valid('socketio', 'ws', 'http'),
102
+ then: Joi.array().items(flowItemSchema).required(),
103
+ otherwise: Joi.array().items(Joi.any())
104
+ })
105
+ });
106
+
107
+ const beforeAfterSchema = Joi.object({
108
+ flow: Joi.when('engine', {
109
+ is: Joi.exist(),
110
+ then: Joi.when('engine', {
111
+ is: Joi.valid('socketio', 'ws', 'http'),
112
+ then: Joi.array().items(flowItemSchema).required(),
113
+ otherwise: Joi.array().items(Joi.any())
114
+ }),
115
+ otherwise: Joi.array().items(flowItemSchema).required()
116
+ })
117
+ });
118
+
119
+ const schema = Joi.object({
120
+ config: config,
121
+ scenarios: Joi.array().items(scenarioItem).required(),
122
+ before: beforeAfterSchema,
123
+ after: beforeAfterSchema
124
+ });
125
+
126
+ module.exports = (script) => {
127
+ const { error } = schema.validate(script);
128
+
129
+ if (error?.details.length) {
130
+ return error.details[0].message;
131
+ }
132
+ };