Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(artillery): add ability to specify scenario to run by name #2184

3 changes: 3 additions & 0 deletions packages/artillery/lib/cmds/run-lambda.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ RunLambdaCommand.flags = {
key: Flags.string({
description: 'API key for Artillery Cloud'
}),
'scenario-name': Flags.string({
description: 'Name of the specific scenario to run'
}),
architecture: Flags.string({
description: 'Architecture of the Lambda function',
default: 'arm64'
Expand Down
6 changes: 5 additions & 1 deletion packages/artillery/lib/cmds/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@ RunCommand.flags = {
}),
key: Flags.string({
description: 'API key for Artillery Cloud'
}),
'scenario-name': Flags.string({
description: 'Name of the specific scenario to run'
})
};

Expand Down Expand Up @@ -207,7 +210,8 @@ RunCommand.runCommandImplementation = async function (flags, argv, args) {
scriptPath: args.script,
// TODO: This should be an array of files, like inputFiles above
absoluteScriptPath: path.resolve(process.cwd(), args.script),
plugins: []
plugins: [],
scenarioName: flags['scenario-name']
};

// Set "name" tag if not set explicitly
Expand Down
43 changes: 43 additions & 0 deletions packages/artillery/test/cli/command-run.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,49 @@ tap.test('Environment specified with -e should be used', async (t) => {
t.ok(exitCode === 0 && !output.stdout.includes('ECONNREFUSED'));
});

tap.test('Can specify scenario to run by name', async (t) => {
const reportFile = 'report-with-scenario-by-name.json';
const reportFilePath = await getRootPath(reportFile);

const [exitCode, output] = await execute([
'run',
'--scenario-name',
'Test Scenario 2',
'-o',
`${reportFilePath}`,
'test/scripts/scenario-named/scenario.yml'
]);

t.ok(
exitCode === 0 && output.stdout.includes('Successfully running scenario 2')
);
const json = JSON.parse(fs.readFileSync(reportFilePath, 'utf8'));

t.ok(
deleteFile(reportFilePath) &&
bernardobridge marked this conversation as resolved.
Show resolved Hide resolved
json.aggregate.counters['vusers.created_by_name.Test Scenario 2'] === 6
bernardobridge marked this conversation as resolved.
Show resolved Hide resolved
);
});

tap.test(
'Errors correctly when specifying a non-existing scenario by name',
async (t) => {
const [exitCode, output] = await execute([
'run',
'--scenario-name',
'Test Scenario 3',
'test/scripts/scenario-named/scenario.yml'
]);

t.equal(exitCode, 11);
t.ok(
output.stdout.includes(
'Error: Scenario Test Scenario 3 not found in script. Make sure your chosen scenario matches the one in your script exactly.'
)
);
}
);

tap.test('Run a script with one payload command line', async (t) => {
const [, output] = await execute([
'run',
Expand Down
13 changes: 13 additions & 0 deletions packages/artillery/test/scripts/scenario-named/scenario.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
config:
target: http://asciiart.artillery.io:8080
phases:
- duration: 3
arrivalRate: 2

scenarios:
- name: Test Scenario 1
flow:
- log: "Successfully running scenario 1"
- name: Test Scenario 2
flow:
- log: "Successfully running scenario 2"
33 changes: 30 additions & 3 deletions packages/core/lib/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,13 @@ function run(script, ee, options, runState, contextVars) {
if (runState.pendingScenarios >= spec.maxVusers) {
metrics.counter('vusers.skipped', 1);
} else {
scenarioContext = runScenario(script, metrics, runState, contextVars);
scenarioContext = runScenario(
script,
metrics,
runState,
contextVars,
options
);
}
});
phaser.on('phaseStarted', function (spec) {
Expand Down Expand Up @@ -242,7 +248,7 @@ function run(script, ee, options, runState, contextVars) {
phaser.run();
}

function runScenario(script, metrics, runState, contextVars) {
function runScenario(script, metrics, runState, contextVars, options) {
const start = process.hrtime();

//
Expand All @@ -263,7 +269,7 @@ function runScenario(script, metrics, runState, contextVars) {
const w = engineUtil.template(scenario.weight, {
vars: variableValues
});
scenario.weight = isNaN(parseInt(w)) ? 0 : parseInt(w);
scenario.weight = isNaN(parseInt(w)) ? 0 : parseInt(w); //eslint-disable-line radix
debug(
`scenario ${scenario.name} weight has been set to ${scenario.weight}`
);
Expand Down Expand Up @@ -315,8 +321,29 @@ function runScenario(script, metrics, runState, contextVars) {
);
}

//default to weighted picked scenario
let i = runState.picker()[0];

if (options.scenarioName) {
let foundIndex;
const foundScenario = script.scenarios.filter((scenario, index) => {
foundIndex = index;
return new RegExp(options.scenarioName).test(scenario.name);
});

if (foundScenario?.length === 0) {
throw new Error(
`Scenario ${options.scenarioName} not found in script. Make sure your chosen scenario matches the one in your script exactly.`
);
} else if (foundScenario.length > 1) {
throw new Error(
`Multiple scenarios for ${options.scenarioName} found in script. Make sure you give unique names to your scenarios in your script.`
);
} else {
debug(`Scenario ${options.scenarioName} found in script. running it!`);
i = foundIndex;
}
}
debug(
'picking scenario %s (%s) weight = %s',
i,
Expand Down