# Ring:load Testing With K6

> Load-testing with k6 for the LerianStudio/k6 Palantir platform: scaffolds product.yaml, smoke/load/stress/soak scenarios, a helper client, builds the webpack bundle, and verifies a local k6 run. Use when new API/gRPC endpoints or throughput-path changes need SLO validation under load, or a Palantir CI load gate is required. Skip when no network-facing endpoints are affected or changes are config-…

- **Type:** Skill
- **Install:** `agentstack add skill-lerianstudio-ring-load-testing-with-k6`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [LerianStudio](https://agentstack.voostack.com/s/lerianstudio)
- **Installs:** 0
- **Category:** [Cloud & Infrastructure](https://agentstack.voostack.com/c/cloud-infrastructure)
- **Latest version:** 0.1.0
- **License:** Apache-2.0
- **Upstream author:** [LerianStudio](https://github.com/LerianStudio)
- **Source:** https://github.com/LerianStudio/ring/tree/main/dev-team/skills/load-testing-with-k6
- **Website:** https://lerian.studio

## Install

```sh
agentstack add skill-lerianstudio-ring-load-testing-with-k6
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# k6 Load Testing (Palantir Platform)

## When to use
- After integration testing passes
- Before production deploy of performance-sensitive changes
- New API endpoints or significant throughput-path changes
- Need to validate SLOs under load (latency, error rate, throughput)
- CI pipeline requires load test gate via Palantir

## Skip when
- Task is documentation-only, configuration-only, or non-code
- No HTTP/gRPC endpoints affected by the change
- Changes limited to static assets, configs, or non-runtime code
- Service has no network-facing interface

## Related
**Complementary:** ring:implementing-tasks, ring:reviewing-code

This skill generates k6 load tests following the Lerian k6 platform conventions.
Tests are structured for execution via Palantir (Self-Service Testing) and are
bundled by webpack into self-contained scripts deployed to EKS via k6-operator.

**Reference repository:** `LerianStudio/k6` — specifically `platform/` directory.

**Block conditions:**
- Test script missing `handleSummary` export = FAIL (Palantir can't collect results)
- `scenario.yaml` param names don't match `__ENV` vars in test.js = FAIL
- Test doesn't read VUS/DURATION from `__ENV` = FAIL
- No `checkResponse()` from shared utils = FAIL
- Missing `product.yaml` = FAIL

## Step 1: Validate Input

Required:
- `product` — product name in lowercase (e.g., `midaz`, `tracer`, `reporter`, `matcher`)
- `endpoints` — list of endpoints to test, each with method, path, and optional payload
- `base_port` — local dev port for the product (e.g., 3000 for midaz, 4020 for tracer)

Optional:
- `scenario_types` — which scenarios to generate (default: `[smoke, load, stress]`)
- `auth_type` — `bearer` (default, uses `shared/auth.js`) | `api-key` | `none`
- `api_key_header` — header name for API key auth (default: `X-API-Key`)
- `custom_thresholds` — override default thresholds
- `existing_product` — if true, extend existing product directory

## Step 2: Understand the Platform Structure

All test code lives under `platform/` in the `LerianStudio/k6` repo:

```
platform/
├── products/{product}/
│   ├── product.yaml              # Product metadata (read by Palantir)
│   ├── helpers/
│   │   └── client.js             # HTTP client for this product's API
│   └── scenarios/
│       └── {scenario}/
│           ├── scenario.yaml     # Catalog metadata (read by Palantir)
│           └── test.js           # k6 test script (webpack entry point)
├── shared/
│   ├── auth.js                   # getAuthHeaders(), authenticate()
│   ├── utils.js                  # checkResponse(), sleepWithJitter(), defaultHandleSummary()
│   └── palantir/                 # SDK for complex scenarios (fixtures, runtime)
│       ├── index.js              # scenario(), fixture(), createTestExports()
│       ├── runtime.js            # Builds k6 exports from config
│       ├── scenario.js           # Declarative scenario config builder
│       └── templates.js          # Built-in test type templates (smoke/quick/full/breakpoint/soak)
├── dist/                         # Webpack output (git-ignored)
├── build.js                      # Bundler entry point
├── webpack.config.js             # Auto-discovers products/*/scenarios/*/test.js
├── config.yaml                   # Platform-level test catalog metadata
└── package.json
```

### Two Patterns for Writing Tests

**Pattern A: Simple client (recommended for most tests)**

Product `helpers/client.js` provides `get()`, `post()`, `patch()`, `del()` scoped to
the product's base URL. Scenarios import the client and `shared/utils.js` directly.

Used by: smoke, load, stress, soak scenarios for midaz, console, pix.

**Pattern B: Palantir SDK (for complex scenarios with fixtures)**

For scenarios that need declarative fixture setup (create rules, limits, etc.),
sanity checks, and built-in metric tracking, use the Palantir SDK:

```javascript
import { scenario, fixture, createTestExports } from '../../../../shared/palantir/index.js';
```

Used by: tracer scenarios (pass-through, denied-by-limit, denied-by-rule, complex-approval).

**Choose Pattern A** unless the product requires setup fixtures (rules, limits, etc.) that
must be created and activated before load can run.

## Step 3: Create Product Files

### 3a. product.yaml

Create `platform/products/{product}/product.yaml`:

```yaml
product: {product}
description: "{Product description} - performance tests"
base_url_env: {PRODUCT}_BASE_URL

defaults:
  thresholds:
    http_req_duration: ["p(95) 0) {
      const id = items.items[0].id;
      const detailRes = get(`/{resource}/${id}`);
      checkResponse(detailRes, 200, 'get {resource}');
    }
  }

  sleep(sleepWithJitter(0.5, 0.3));
}

export { defaultHandleSummary as handleSummary };
```

**Stress test:**

```javascript
import { sleep } from 'k6';
import { get, post } from '../../helpers/client.js';
import { checkResponse, sleepWithJitter, randomString, defaultHandleSummary } from '../../../../shared/utils.js';

const VUS = __ENV.VUS ? parseInt(__ENV.VUS) : 100;
const DURATION = __ENV.DURATION || '5m';
const RAMP_UP = __ENV.RAMP_UP || '1m';

export const options = {
  stages: [
    { duration: RAMP_UP, target: VUS },
    { duration: DURATION, target: VUS },
    { duration: '30s', target: 0 },
  ],
  thresholds: {
    http_req_duration: ['p(95)0.99'],
    'txn_error_rate': ['rate body.decision === 'ALLOW',
    // Product-specific checks
  };
}

const test = createTestExports({ config, client, buildPayload, checks });
export const options = test.options;
export const setup = test.setup;
export default test.default;
export const handleSummary = test.handleSummary;
```

## Step 5: Build and Verify

```bash
cd platform
npm install          # first time only
npm run build        # webpack bundles all scenarios
```

Verify bundle was created:

```bash
ls -la dist/{product}/
# Expected: {scenario}.bundle.js for each scenario
```

Verify bundle runs locally:

```bash
# With product running locally
k6 run dist/{product}/smoke.bundle.js

# Override target URL
k6 run -e TARGET_URL=http://localhost:{port} dist/{product}/smoke.bundle.js
```

## Step 6: Mandatory Checklist

Before marking complete, verify ALL items:

- [ ] `products/{product}/product.yaml` exists with `base_url_env` and default thresholds
- [ ] `products/{product}/helpers/client.js` exists with `TARGET_URL` fallback
- [ ] At least `scenarios/smoke/` exists with both `scenario.yaml` and `test.js`
- [ ] Every `scenario.yaml` param `name` matches a `__ENV.XXX` in `test.js`
- [ ] Every `test.js` exports `handleSummary` (re-export `defaultHandleSummary`)
- [ ] Every `test.js` reads `VUS` and `DURATION` from `__ENV`
- [ ] Every `test.js` defines `thresholds` in `options`
- [ ] Every `test.js` uses `checkResponse()` from `shared/utils.js`
- [ ] `npm run build` succeeds and produces bundles in `dist/{product}/`
- [ ] Bundle runs locally with `k6 run dist/{product}/smoke.bundle.js`

## Environment Variables Reference

### Injected by Palantir SST (available in all tests)

| Variable | Description |
|----------|-------------|
| `TARGET_URL` | Base URL of the product under test |
| `VUS` | Number of virtual users |
| `DURATION` | Test duration (e.g., `30s`, `5m`) |
| `ENVIRONMENT_ID` | SST environment UUID |
| `K6_TESTID` | Test run UUID (for Grafana filtering) |

### Authentication (from shared/auth.js)

| Variable | Description |
|----------|-------------|
| `AUTH_TOKEN` | Bearer token (takes priority) |
| `AUTH_USER` / `AUTH_PASS` | Basic auth credentials |
| `AUTH_URL` | OAuth token endpoint |
| `AUTH_CLIENT_ID` | OAuth client ID |
| `AUTH_CLIENT_SECRET` | OAuth client secret |

### Shared Utilities (from shared/utils.js)

| Function | Description |
|----------|-------------|
| `checkResponse(res, status?, label?)` | Asserts status + duration <5s, tracks `custom_error_rate` and `custom_request_duration` |
| `sleepWithJitter(base?, jitter?)` | Returns `base + random(0, jitter)` — avoids thundering herd |
| `defaultHandleSummary(data)` | Writes summary JSON to `/tmp/summary.json` + stdout markers for SST collection |
| `randomString(length?)` | Random alphanumeric string |

## Output Report

```markdown
## Load Test Summary

| Metric | Value |
|--------|-------|
| Result | PASS |
| Product | {product} |
| Scenarios Created | smoke, load, stress |
| Pattern | A (Simple client) / B (Palantir SDK) |

## Files Created

| File | Purpose |
|------|---------|
| `platform/products/{product}/product.yaml` | Product metadata |
| `platform/products/{product}/helpers/client.js` | HTTP client |
| `platform/products/{product}/scenarios/smoke/scenario.yaml` | Smoke catalog |
| `platform/products/{product}/scenarios/smoke/test.js` | Smoke test |
| `platform/products/{product}/scenarios/load/scenario.yaml` | Load catalog |
| `platform/products/{product}/scenarios/load/test.js` | Load test |

## Palantir Integration

- Bundle path: `dist/{product}/{scenario}.bundle.js`
- Build verified: ✅
- Local run verified: ✅ (smoke @ localhost:{port})

## Next Steps
- Push to `LerianStudio/k6` repository
- Verify in Palantir UI: product appears in catalog with all scenarios
- Run smoke test via SST to validate end-to-end flow
```

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [LerianStudio](https://github.com/LerianStudio)
- **Source:** [LerianStudio/ring](https://github.com/LerianStudio/ring)
- **License:** Apache-2.0
- **Homepage:** https://lerian.studio

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-lerianstudio-ring-load-testing-with-k6
- Seller: https://agentstack.voostack.com/s/lerianstudio
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
