Artillery Load Testing-209d67 — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Artillery Load Testing-209d67 (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
This skill makes an AI agent author Artillery 2.x load tests as declarative YAML: realistic traffic phases, multi-step scenarios with captured variables, CSV-driven virtual user data, functional expect checks inside load flows, and ensure thresholds that turn latency regressions into red CI builds. Trigger it when a project contains artillery.yml, artillery in package.json, or when the user asks for load, stress, soak, or spike testing of an HTTP API or website in a Node.js stack.
arrivalRate hides cold-start effects and autoscaling lag. Always define at least warm-up, ramp, and sustain phases.ensure plugin so artillery run exits non-zero when p95/p99 or error rate budgets are blown. A load test that cannot fail is a demo, not a test.statusCode, contentType, and hasProperty inside the flow.payload with hundreds of distinct credentials and SKUs to defeat caches realistically.npm install --save-dev artillery@latest
npx artillery version
# Run a test locally
npx artillery run load/checkout.yml
# Save raw metrics for trend analysis
npx artillery run load/checkout.yml --output artillery-report.json# load/checkout.yml
config:
target: https://staging-api.example.com
http:
timeout: 10
phases:
- duration: 60
arrivalRate: 2
name: warm-up
- duration: 120
arrivalRate: 5
rampTo: 40
name: ramp-to-peak
- duration: 300
arrivalRate: 40
name: sustained-peak
payload:
path: users.csv
fields:
- email
- password
order: random
skipHeader: true
plugins:
expect: {}
ensure: {}
ensure:
thresholds:
- http.response_time.p95: 250
- http.response_time.p99: 500
conditions:
- expression: http.codes.200 > 0
strict: true
maxErrorRate: 1
scenarios:
- name: login-browse-order
flow:
- post:
url: /auth/login
json:
email: '{{ email }}'
password: '{{ password }}'
capture:
- json: $.token
as: authToken
expect:
- statusCode: 200
- contentType: json
- hasProperty: token
- get:
url: /products?category=audio
headers:
Authorization: 'Bearer {{ authToken }}'
capture:
- json: $[0].id
as: productId
expect:
- statusCode: 200
- post:
url: /orders
headers:
Authorization: 'Bearer {{ authToken }}'
json:
productId: '{{ productId }}'
quantity: 1
expect:
- statusCode: 201
- hasProperty: orderId
- think: 2email,password
[email protected],Str0ngPass!001
[email protected],Str0ngPass!002
[email protected],Str0ngPass!003
[email protected],Str0ngPass!004Generate hundreds of rows with a one-liner instead of writing them by hand:
seq -w 1 500 | awk -F, 'BEGIN{print "email,password"} {printf "loadtest-%[email protected],Str0ngPass!%s\n", $1, $1}' > users.csv# In config:
config:
target: https://staging-api.example.com
processor: ./processor.js
scenarios:
- name: create-order-with-dynamic-payload
flow:
- function: generateOrderPayload
- post:
url: /orders
json:
sku: '{{ sku }}'
quantity: '{{ quantity }}'
afterResponse: logSlowResponse// processor.js
module.exports = { generateOrderPayload, logSlowResponse };
function generateOrderPayload(context, events, done) {
// Runs before the request; sets template variables on the VU context
context.vars.sku = `SKU-${1000 + Math.floor(Math.random() * 9000)}`;
context.vars.quantity = 1 + Math.floor(Math.random() * 4);
return done();
}
function logSlowResponse(requestParams, response, context, events, done) {
const tookMs = response.timings ? response.timings.phases.total : 0;
if (tookMs > 1000) {
console.warn(`SLOW ${requestParams.url} -> ${response.statusCode} in ${tookMs}ms`);
events.emit('counter', 'custom.slow_responses', 1);
}
return done();
}The ensure plugin makes Artillery exit with code 1 on any threshold breach, so the job fails without extra scripting.
# .github/workflows/load-test.yml
name: load-test
on:
workflow_dispatch:
schedule:
- cron: '0 2 * * 1'
jobs:
artillery:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- name: Run load test against staging
run: npx artillery run load/checkout.yml --output artillery-report.json
env:
ARTILLERY_DISABLE_TELEMETRY: 'true'
- name: Upload raw metrics
if: always()
uses: actions/upload-artifact@v4
with:
name: artillery-report
path: artillery-report.jsonthink steps (1-3 seconds) between requests so VUs pace like humans instead of a retry storm.weight: to mirror real traffic mix (for example 70 percent browse, 25 percent search, 5 percent checkout).--overrides '{"config":{"phases":[{"duration":10,"arrivalRate":1}]}}') before any big run to catch broken auth and 4xx noise cheaply.--output JSON artifacts from every CI run so you can diff p95 across releases.http.timeout explicitly; the 120-second default hides hangs as slow successes.arrivalRate and no ramp: you are testing the load balancer's SYN queue, not your application.expect or ensure block exists.arrivalRate past what one generator machine can produce: watch for Artillery's own CPU warnings, and split load across workers (or Fargate via artillery run-fargate) instead.artillery.yml, files under load/ or perf/ with Artillery config, or artillery in package.json devDependencies.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.