load-test-builder

Validate system performance under realistic and stress conditions.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "load-test-builder" with this command: npx skills add patricio0312rev/skills/patricio0312rev-skills-load-test-builder

Load Test Builder

Validate system performance under realistic and stress conditions.

Core Workflow

  • Define scenarios: User journeys and load patterns

  • Set thresholds: Performance requirements

  • Configure load: Ramp-up, peak, duration

  • Run tests: Execute load scenarios

  • Analyze results: Metrics and bottlenecks

  • Integrate CI: Automated performance gates

k6 Load Testing

Installation

macOS

brew install k6

Docker

docker pull grafana/k6

Basic Load Test

// load-tests/basic.js import http from 'k6/http'; import { check, sleep } from 'k6'; import { Rate, Trend } from 'k6/metrics';

// Custom metrics const errorRate = new Rate('errors'); const responseTime = new Trend('response_time');

// Test configuration export const options = { stages: [ { duration: '1m', target: 20 }, // Ramp up to 20 users { duration: '3m', target: 20 }, // Stay at 20 users { duration: '1m', target: 50 }, // Ramp up to 50 users { duration: '3m', target: 50 }, // Stay at 50 users { duration: '1m', target: 0 }, // Ramp down to 0 ], thresholds: { http_req_duration: ['p(95)<500', 'p(99)<1000'], http_req_failed: ['rate<0.01'], errors: ['rate<0.05'], }, };

const BASE_URL = __ENV.BASE_URL || 'http://localhost:3000';

export default function () { // Homepage const homeResponse = http.get(${BASE_URL}/);

check(homeResponse, { 'homepage status is 200': (r) => r.status === 200, 'homepage loads fast': (r) => r.timings.duration < 500, });

responseTime.add(homeResponse.timings.duration); errorRate.add(homeResponse.status !== 200);

sleep(1);

// API request const apiResponse = http.get(${BASE_URL}/api/users);

check(apiResponse, { 'api status is 200': (r) => r.status === 200, 'api returns array': (r) => Array.isArray(JSON.parse(r.body)), });

errorRate.add(apiResponse.status !== 200);

sleep(Math.random() * 3 + 1); // Random think time 1-4 seconds }

User Journey Test

// load-tests/user-journey.js import http from 'k6/http'; import { check, group, sleep } from 'k6'; import { SharedArray } from 'k6/data';

const users = new SharedArray('users', function () { return JSON.parse(open('./data/users.json')); });

export const options = { scenarios: { browse_and_buy: { executor: 'ramping-vus', startVUs: 0, stages: [ { duration: '2m', target: 100 }, { duration: '5m', target: 100 }, { duration: '2m', target: 0 }, ], gracefulRampDown: '30s', }, }, thresholds: { 'group_duration{group:::Login}': ['p(95)<2000'], 'group_duration{group:::Browse Products}': ['p(95)<1000'], 'group_duration{group:::Checkout}': ['p(95)<3000'], http_req_failed: ['rate<0.01'], }, };

const BASE_URL = __ENV.BASE_URL || 'http://localhost:3000';

export default function () { const user = users[Math.floor(Math.random() * users.length)];

group('Login', function () { const loginRes = http.post( ${BASE_URL}/api/auth/login, JSON.stringify({ email: user.email, password: user.password, }), { headers: { 'Content-Type': 'application/json' }, } );

check(loginRes, {
  'login successful': (r) => r.status === 200,
  'has token': (r) => JSON.parse(r.body).token !== undefined,
});

if (loginRes.status !== 200) return;

const token = JSON.parse(loginRes.body).token;

group('Browse Products', function () {
  const productsRes = http.get(`${BASE_URL}/api/products`, {
    headers: { Authorization: `Bearer ${token}` },
  });

  check(productsRes, {
    'products loaded': (r) => r.status === 200,
  });

  sleep(2);

  // View product detail
  const products = JSON.parse(productsRes.body);
  if (products.length > 0) {
    const productId = products[Math.floor(Math.random() * products.length)].id;
    const productRes = http.get(`${BASE_URL}/api/products/${productId}`, {
      headers: { Authorization: `Bearer ${token}` },
    });

    check(productRes, {
      'product detail loaded': (r) => r.status === 200,
    });
  }
});

sleep(1);

group('Checkout', function () {
  // Add to cart
  const cartRes = http.post(
    `${BASE_URL}/api/cart`,
    JSON.stringify({ productId: '1', quantity: 1 }),
    {
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${token}`,
      },
    }
  );

  check(cartRes, {
    'added to cart': (r) => r.status === 200 || r.status === 201,
  });

  // Checkout
  const checkoutRes = http.post(
    `${BASE_URL}/api/checkout`,
    JSON.stringify({ paymentMethod: 'card' }),
    {
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${token}`,
      },
    }
  );

  check(checkoutRes, {
    'checkout successful': (r) => r.status === 200 || r.status === 201,
  });
});

});

sleep(Math.random() * 5 + 2); }

Stress Test

// load-tests/stress.js import http from 'k6/http'; import { check } from 'k6';

export const options = { stages: [ { duration: '2m', target: 100 }, // Normal load { duration: '5m', target: 100 }, { duration: '2m', target: 200 }, // High load { duration: '5m', target: 200 }, { duration: '2m', target: 300 }, // Stress { duration: '5m', target: 300 }, { duration: '2m', target: 400 }, // Breaking point { duration: '5m', target: 400 }, { duration: '10m', target: 0 }, // Recovery ], thresholds: { http_req_duration: ['p(99)<1500'], http_req_failed: ['rate<0.05'], }, };

export default function () { const response = http.get(${__ENV.BASE_URL}/api/health);

check(response, { 'status is 200': (r) => r.status === 200, }); }

Spike Test

// load-tests/spike.js export const options = { stages: [ { duration: '10s', target: 100 }, // Quick ramp { duration: '1m', target: 100 }, // Normal { duration: '10s', target: 1000 }, // Spike! { duration: '3m', target: 1000 }, // Stay at spike { duration: '10s', target: 100 }, // Scale down { duration: '3m', target: 100 }, // Recovery { duration: '10s', target: 0 }, // Ramp down ], };

Soak Test

// load-tests/soak.js export const options = { stages: [ { duration: '5m', target: 100 }, // Ramp up { duration: '8h', target: 100 }, // Sustained load for 8 hours { duration: '5m', target: 0 }, // Ramp down ], thresholds: { http_req_duration: ['p(95)<500'], http_req_failed: ['rate<0.01'], }, };

Artillery

Installation

npm install -D artillery

Configuration

artillery/load-test.yml

config: target: "http://localhost:3000" phases: - duration: 60 arrivalRate: 5 name: "Warm up" - duration: 120 arrivalRate: 10 name: "Normal load" - duration: 60 arrivalRate: 50 name: "Spike" - duration: 60 arrivalRate: 10 name: "Cool down"

defaults: headers: Content-Type: "application/json"

plugins: expect: {}

ensure: p99: 500 maxErrorRate: 1

scenarios:

  • name: "User Journey" flow:
    • get: url: "/" expect: - statusCode: 200 - contentType: text/html

    • think: 2

    • get: url: "/api/products" expect: - statusCode: 200 capture: - json: "$[0].id" as: "productId"

    • think: 1

    • get: url: "/api/products/{{ productId }}" expect: - statusCode: 200

    • post: url: "/api/cart" json: productId: "{{ productId }}" quantity: 1 expect: - statusCode: 201

With Custom Functions

// artillery/processor.js module.exports = { generateUser, logResponse, validateCheckout, };

function generateUser(context, events, done) { context.vars.email = user${Date.now()}@example.com; context.vars.password = 'testpassword123'; return done(); }

function logResponse(requestParams, response, context, events, done) { console.log(Response: ${response.statusCode} - ${response.body}); return done(); }

function validateCheckout(requestParams, response, context, events, done) { const body = JSON.parse(response.body); if (!body.orderId) { return done(new Error('Missing orderId in response')); } context.vars.orderId = body.orderId; return done(); }

artillery/with-processor.yml

config: target: "http://localhost:3000" processor: "./processor.js" phases: - duration: 60 arrivalRate: 10

scenarios:

  • name: "Checkout flow" flow:
    • function: "generateUser"
    • post: url: "/api/auth/register" json: email: "{{ email }}" password: "{{ password }}"
    • post: url: "/api/checkout" afterResponse: "validateCheckout"

Autocannon (Node.js)

// load-tests/autocannon.ts import autocannon from 'autocannon';

async function runLoadTest() { const result = await autocannon({ url: 'http://localhost:3000/api/users', connections: 100, duration: 30, pipelining: 10, headers: { 'Content-Type': 'application/json', }, requests: [ { method: 'GET', path: '/api/users', }, { method: 'POST', path: '/api/users', body: JSON.stringify({ name: 'Test', email: 'test@example.com' }), }, ], });

console.log(autocannon.printResult(result));

// Validate results if (result.latency.p99 > 500) { console.error('P99 latency exceeded 500ms'); process.exit(1); }

if (result.errors > 0) { console.error(Errors detected: ${result.errors}); process.exit(1); } }

runLoadTest();

CI Integration

.github/workflows/load-tests.yml

name: Load Tests

on: schedule: - cron: '0 2 * * *' # Daily at 2 AM workflow_dispatch:

jobs: load-test: runs-on: ubuntu-latest

services:
  app:
    image: myapp:latest
    ports:
      - 3000:3000
    env:
      DATABASE_URL: postgresql://test@localhost/test

steps:
  - uses: actions/checkout@v4

  - name: Install k6
    run: |
      sudo gpg -k
      sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
      echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
      sudo apt-get update
      sudo apt-get install k6

  - name: Wait for app
    run: |
      timeout 60 bash -c 'until curl -s http://localhost:3000/health; do sleep 1; done'

  - name: Run load tests
    run: k6 run --out json=results.json load-tests/basic.js
    env:
      BASE_URL: http://localhost:3000

  - name: Upload results
    uses: actions/upload-artifact@v4
    with:
      name: load-test-results
      path: results.json

  - name: Comment on PR
    if: github.event_name == 'pull_request'
    uses: actions/github-script@v7
    with:
      script: |
        const fs = require('fs');
        const results = JSON.parse(fs.readFileSync('results.json', 'utf8'));
        // Parse and format results for PR comment

Results Analysis

// scripts/analyze-results.js import fs from 'fs';

const results = JSON.parse(fs.readFileSync('results.json', 'utf8'));

const summary = { totalRequests: results.metrics.http_reqs.count, avgDuration: results.metrics.http_req_duration.avg, p95Duration: results.metrics.http_req_duration['p(95)'], p99Duration: results.metrics.http_req_duration['p(99)'], errorRate: results.metrics.http_req_failed.rate, throughput: results.metrics.http_reqs.rate, };

console.table(summary);

// Check thresholds const passed = summary.p95Duration < 500 && summary.p99Duration < 1000 && summary.errorRate < 0.01;

process.exit(passed ? 0 : 1);

Best Practices

  • Start small: Gradually increase load

  • Use realistic data: Production-like scenarios

  • Monitor everything: Backend metrics too

  • Set thresholds: Define pass/fail criteria

  • Test regularly: Catch regressions early

  • Isolate environment: Dedicated test environment

  • Document findings: Track improvements

  • Include think time: Simulate real users

Output Checklist

Every load test setup should include:

  • k6/Artillery configuration

  • Realistic user scenarios

  • Multiple load patterns (normal, stress, spike)

  • Performance thresholds

  • Custom metrics

  • CI integration

  • Results analysis

  • Soak test for memory leaks

  • Documentation

  • Baseline benchmarks

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

framer-motion-animator

No summary provided by upstream source.

Repository SourceNeeds Review
General

eslint-prettier-config

No summary provided by upstream source.

Repository SourceNeeds Review
General

changelog-writer

No summary provided by upstream source.

Repository SourceNeeds Review
General

postman-collection-generator

No summary provided by upstream source.

Repository SourceNeeds Review