@assertrx/dino 1.0.10 → 1.0.12
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.
- package/package.json +1 -1
- package/prompts/generate-k6-test-from-endpoint.md +68 -0
- package/prompts/generate-k6-test-from-openapi.md +85 -0
- package/prompts/setup-k6.md +130 -0
- package/prompts/setup-promptfoo.md +103 -0
- package/scripts/api-server.mjs +28 -0
- package/skills/analyzing-test-results/SKILL.md +150 -0
- package/skills/analyzing-test-results/reference/bottleneck-patterns.md +130 -0
- package/skills/analyzing-test-results/reference/metrics.md +134 -0
- package/skills/analyzing-test-results/reference/visualization.md +152 -0
- package/skills/designing-test-scenarios/SKILL.md +247 -0
- package/skills/designing-test-scenarios/reference/executors.md +215 -0
- package/skills/designing-test-scenarios/reference/test-types.md +215 -0
- package/skills/designing-test-scenarios/reference/thresholds.md +208 -0
- package/skills/generating-api-load-tests/SKILL.md +199 -0
- package/skills/generating-api-load-tests/reference/data-parameterization.md +343 -0
- package/skills/generating-api-load-tests/reference/grpc.md +315 -0
- package/skills/generating-api-load-tests/reference/http.md +380 -0
- package/skills/generating-api-load-tests/reference/websocket.md +369 -0
- package/skills/generating-browser-tests/SKILL.md +269 -0
- package/skills/generating-browser-tests/reference/browser-api.md +251 -0
- package/skills/generating-browser-tests/reference/web-vitals.md +148 -0
- package/skills/generating-tests-from-code/SKILL.md +142 -0
- package/skills/generating-tests-from-code/reference/auth-analysis.md +220 -0
- package/skills/generating-tests-from-code/reference/framework-patterns.md +242 -0
- package/skills/generating-tests-from-openapi/SKILL.md +234 -0
- package/skills/generating-tests-from-openapi/reference/openapi-auth-mapping.md +247 -0
- package/skills/generating-tests-from-openapi/reference/openapi-parsing.md +194 -0
- package/skills/operating-k6-in-ci-cd/SKILL.md +98 -0
- package/skills/operating-k6-in-ci-cd/reference/ci-cd-pipelines.md +265 -0
- package/skills/operating-k6-in-ci-cd/reference/extensions.md +144 -0
- package/skills/operating-k6-in-ci-cd/reference/k6-operator.md +214 -0
- package/skills/promptfoo/SKILL.md +121 -0
- package/skills/promptfoo/reference/assert-types.md +74 -0
- package/skills/promptfoo/reference/providers.md +70 -0
- package/skills/testing-resilience/SKILL.md +230 -0
- package/skills/testing-resilience/reference/fault-injection.md +153 -0
- package/skills/testing-resilience/reference/resilience-patterns.md +500 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: promptfoo
|
|
3
|
+
description: Use when writing, running, or debugging promptfoo AI evaluations — promptfooconfig.yaml structure, providers (OpenAI/Anthropic/HTTP/echo), test cases with vars, assert types (contains, is-json, javascript, similar, llm-rubric), CLI usage (eval, view, JSON output), thresholds, and CI integration. Use when the user asks about LLM evals, prompt evaluation, model output testing, or setting up a promptfoo project.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Promptfoo — LLM / Prompt Evaluations
|
|
7
|
+
|
|
8
|
+
Author, run, and interpret [promptfoo](https://www.promptfoo.dev/) evaluations. Promptfoo evaluates LLM prompts, agents, and model-backed endpoints against test cases with deterministic and model-graded assertions, producing a pass/fail result like a test runner.
|
|
9
|
+
|
|
10
|
+
## When to Use Promptfoo
|
|
11
|
+
|
|
12
|
+
| Goal | Approach |
|
|
13
|
+
|------|----------|
|
|
14
|
+
| Verify prompt/model changes don't regress outputs | Eval suite with deterministic asserts on known inputs |
|
|
15
|
+
| Compare prompts or models side by side | Multiple `prompts` or `providers` in one config — promptfoo renders a matrix |
|
|
16
|
+
| Test subjective quality (tone, faithfulness) | `llm-rubric` assert (author it with the **write-judge-prompt** skill) |
|
|
17
|
+
| Test an HTTP endpoint that wraps a model | `http` provider hitting the real endpoint |
|
|
18
|
+
| Sanity-check the harness itself, no API needed | `echo` provider — instant, offline, zero cost |
|
|
19
|
+
| Find vulnerable/unsafe outputs | `npx promptfoo redteam` (separate command, own config) |
|
|
20
|
+
|
|
21
|
+
## Project Layout
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
promptfoo/
|
|
25
|
+
promptfooconfig.yaml # the eval config: prompts + providers + tests
|
|
26
|
+
prompts/
|
|
27
|
+
system.txt # prompt templates ({{var}} placeholders) — or inline
|
|
28
|
+
tests/ # optional: split test cases into YAML/CSV/JSON files
|
|
29
|
+
README.md # how to run, env vars needed
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
A single `promptfooconfig.yaml` is enough for a skeleton project; split prompts/tests into files only when the config grows.
|
|
33
|
+
|
|
34
|
+
## Config Anatomy (promptfooconfig.yaml)
|
|
35
|
+
|
|
36
|
+
```yaml
|
|
37
|
+
description: "Customer support agent — smoke evals"
|
|
38
|
+
|
|
39
|
+
# What to evaluate. One or more prompt templates; {{vars}} come from tests.
|
|
40
|
+
prompts:
|
|
41
|
+
- file://promptfoo/prompts/system.txt
|
|
42
|
+
# or inline:
|
|
43
|
+
# - "Translate the following to French: {{text}}"
|
|
44
|
+
|
|
45
|
+
# Who answers. Multiple providers = comparison matrix.
|
|
46
|
+
providers:
|
|
47
|
+
- openai:gpt-4o-mini # needs OPENAI_API_KEY
|
|
48
|
+
- anthropic:claude-3-5-haiku-latest # needs ANTHROPIC_API_KEY
|
|
49
|
+
# Local/zero-cost provider for harness checks:
|
|
50
|
+
# - echo
|
|
51
|
+
|
|
52
|
+
# Test cases: inputs + expected behavior.
|
|
53
|
+
tests:
|
|
54
|
+
- description: "Greeting is friendly"
|
|
55
|
+
vars:
|
|
56
|
+
message: "hi there"
|
|
57
|
+
assert:
|
|
58
|
+
- type: contains
|
|
59
|
+
value: "hello"
|
|
60
|
+
- type: llm-rubric
|
|
61
|
+
value: "The reply is polite and offers help"
|
|
62
|
+
|
|
63
|
+
# Assertions applied to every test (defaults; per-test asserts extend these).
|
|
64
|
+
defaultTest:
|
|
65
|
+
assert:
|
|
66
|
+
- type: latency
|
|
67
|
+
threshold: 5000 # ms
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Full assert reference: [reference/assert-types.md](reference/assert-types.md). Provider reference: [reference/providers.md](reference/providers.md).
|
|
71
|
+
|
|
72
|
+
## Running Evals
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Run the default config (promptfooconfig.yaml in cwd)
|
|
76
|
+
npx promptfoo@latest eval
|
|
77
|
+
|
|
78
|
+
# Run a specific config, fail the command on failed asserts (default)
|
|
79
|
+
npx promptfoo@latest eval -c promptfoo/promptfooconfig.yaml
|
|
80
|
+
|
|
81
|
+
# Machine-readable results (for CI or parsing)
|
|
82
|
+
npx promptfoo@latest eval -c promptfoo/promptfooconfig.yaml --output json
|
|
83
|
+
|
|
84
|
+
# Interactive web viewer of the last results
|
|
85
|
+
npx promptfoo@latest view
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
- **Exit code**: `0` when all asserts pass, non-zero when any test fails or the config is invalid — safe for CI.
|
|
89
|
+
- **Cost control**: `--repeat 1` (default), keep test counts small, prefer cheap models while iterating.
|
|
90
|
+
- **Concurrency**: defaults are conservative; raise via `--max-concurrency N` for larger suites.
|
|
91
|
+
|
|
92
|
+
## Test-Design Rules
|
|
93
|
+
|
|
94
|
+
1. **Deterministic first.** Use `contains`, `is-json`, `javascript`, and `similar` before model-graded asserts — they are reproducible, free, and debuggable.
|
|
95
|
+
2. **Rubric only for the subjective.** For `llm-rubric`, write the judge criteria with the **write-judge-prompt** skill, and calibrate it with **validate-evaluator** before trusting results.
|
|
96
|
+
3. **Small, sharp suites.** 5–20 targeted cases beat 200 vague ones. Generate bulk synthetic variants with **generate-synthetic-data** only when you need coverage/robustness checks.
|
|
97
|
+
4. **No secrets in configs.** API keys come from env (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, …) — never commit them.
|
|
98
|
+
5. **Thresholds over vibes.** Set `defaultTest` latency/cost guards so regressions fail loudly even when content asserts pass.
|
|
99
|
+
|
|
100
|
+
## CI Integration (GitHub Actions)
|
|
101
|
+
|
|
102
|
+
```yaml
|
|
103
|
+
- name: Run prompt evals
|
|
104
|
+
env:
|
|
105
|
+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
106
|
+
run: npx promptfoo@latest eval -c promptfoo/promptfooconfig.yaml --output json > results.json
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Interpreting Results
|
|
110
|
+
|
|
111
|
+
- Each row = one test × (prompt × provider). Red cells list the exact assert that failed with actual vs expected — read that first.
|
|
112
|
+
- For `llm-rubric` failures, inspect the judge's reason in the viewer before assuming the model is wrong; a mis-calibrated judge is a common failure mode (see **eval-audit** skill).
|
|
113
|
+
- Flaky content asserts (`contains` on chatty models) → relax to `contains-any`, `similar` with a threshold, or a better-scoped rubric.
|
|
114
|
+
|
|
115
|
+
## Related Skills in This Package
|
|
116
|
+
|
|
117
|
+
- **write-judge-prompt** — author the `llm-rubric` criteria.
|
|
118
|
+
- **validate-evaluator** — calibrate judges against human labels before trusting scores.
|
|
119
|
+
- **eval-audit** — audit an existing eval suite for missing error analysis, vanity metrics, etc.
|
|
120
|
+
- **generate-synthetic-data** — bulk-generate test inputs across behavioral dimensions.
|
|
121
|
+
- **error-analysis** — categorize failures after a run to decide what to fix first.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Promptfoo Assert Types Reference
|
|
2
|
+
|
|
3
|
+
Assertions live under each test's `assert:` list (and `defaultTest.assert:` for suite-wide defaults). Every assert evaluates to pass/fail against the provider output. Prefer deterministic types; use model-graded types only for subjective criteria.
|
|
4
|
+
|
|
5
|
+
## Deterministic (reproducible, free)
|
|
6
|
+
|
|
7
|
+
| Type | What it checks | Key fields |
|
|
8
|
+
|------|----------------|-----------|
|
|
9
|
+
| `contains` | Output includes a substring | `value` |
|
|
10
|
+
| `not-contains` | Output must not include a substring | `value` |
|
|
11
|
+
| `contains-any` | Output includes at least one of several substrings | `value: [..]` |
|
|
12
|
+
| `contains-all` | Output includes all of several substrings | `value: [..]` |
|
|
13
|
+
| `regex` | Output matches a pattern | `value` (regex string) |
|
|
14
|
+
| `equals` | Exact match (trim/normalize first) | `value` |
|
|
15
|
+
| `is-json` | Output parses as JSON; optionally validates a JSON schema | `value` (JSON schema) |
|
|
16
|
+
| `javascript` | Custom predicate over output/context | `value` (JS expression returning boolean/number; `output`, `context` available) |
|
|
17
|
+
| `python` | Same, in Python | `value` (code) |
|
|
18
|
+
| `similar` | Embedding similarity to expected text | `value`, `threshold` (0–1, default 0.75) |
|
|
19
|
+
| `latency` | Response time guard | `threshold` (ms) |
|
|
20
|
+
| `cost` | Per-call cost guard | `threshold` (USD) |
|
|
21
|
+
| `contains-sql` / `is-valid-sql` / `is-valid-function-call` / `is-valid-openai-function-call` / `is-valid-url` / `no-refusal` / `perplexity` / `perplexity-score` | Domain-specific structural checks | varies |
|
|
22
|
+
|
|
23
|
+
## Model-graded (subjective criteria — judge LLM involved)
|
|
24
|
+
|
|
25
|
+
| Type | What it checks | Notes |
|
|
26
|
+
|------|----------------|-------|
|
|
27
|
+
| `llm-rubric` | Output satisfies natural-language criteria | Judge prompt in `value`; author it with the **write-judge-prompt** skill. Optional `rubricPrompt` to override the default judge prompt template |
|
|
28
|
+
| `factuality` | Output is factually consistent with a reference | `value` = ground truth, `subject` = original input |
|
|
29
|
+
| `answer-relevance` | Output addresses the input | no extra fields |
|
|
30
|
+
| `context-recall` / `context-relevance` / `context-faithfulness` | RAG quality: retrieved context vs question/answer | see **evaluate-rag** skill |
|
|
31
|
+
| `classifier` | Label the output with a model-chosen category | `value` = list of allowed labels |
|
|
32
|
+
|
|
33
|
+
## Grading Model
|
|
34
|
+
|
|
35
|
+
Model-graded asserts run on a grader LLM. Configure it suite-wide:
|
|
36
|
+
|
|
37
|
+
```yaml
|
|
38
|
+
# Use a different model for judging than for the system under test
|
|
39
|
+
evaluateOptions:
|
|
40
|
+
# or per-assert via the assert's own provider field
|
|
41
|
+
providers: []
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Simplest per-assert override:
|
|
45
|
+
|
|
46
|
+
```yaml
|
|
47
|
+
- type: llm-rubric
|
|
48
|
+
value: "Answer states the refund window and cites the policy doc."
|
|
49
|
+
# provider: anthropic:claude-3-5-haiku-latest # optional judge override
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Weighted & Threshold Asserts
|
|
53
|
+
|
|
54
|
+
```yaml
|
|
55
|
+
tests:
|
|
56
|
+
- description: "Balanced checks"
|
|
57
|
+
vars: { q: "what is your return policy" }
|
|
58
|
+
assert:
|
|
59
|
+
- type: contains
|
|
60
|
+
value: "30 days"
|
|
61
|
+
weight: 2 # counts double in the score
|
|
62
|
+
- type: llm-rubric
|
|
63
|
+
value: "Polite and on-topic"
|
|
64
|
+
weight: 1
|
|
65
|
+
threshold: 0.8 # test passes only if score >= 0.8
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Suite-wide Pass Threshold
|
|
69
|
+
|
|
70
|
+
```yaml
|
|
71
|
+
# Fail the whole eval if less than 90% of tests pass
|
|
72
|
+
tests:
|
|
73
|
+
- threshold: 0.9
|
|
74
|
+
```
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Promptfoo Providers Reference
|
|
2
|
+
|
|
3
|
+
Providers answer prompts. List one or more under `providers:` — multiple entries render a comparison matrix across all tests.
|
|
4
|
+
|
|
5
|
+
## Model Providers
|
|
6
|
+
|
|
7
|
+
```yaml
|
|
8
|
+
providers:
|
|
9
|
+
- openai:gpt-4o-mini # needs OPENAI_API_KEY
|
|
10
|
+
- anthropic:claude-3-5-haiku-latest # needs ANTHROPIC_API_KEY
|
|
11
|
+
- bedrock:us.anthropic.claude-3-5-sonnet-20241022-v2:0
|
|
12
|
+
- azureopenai:gpt-4o:my-deployment # needs AZURE_OPENAI_API_BASE etc.
|
|
13
|
+
- google:gemini-1.5-flash # needs GOOGLE_API_KEY
|
|
14
|
+
- ollama:chat:llama3.1 # local, no key
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Custom model params (temperature, max tokens) use the long form:
|
|
18
|
+
|
|
19
|
+
```yaml
|
|
20
|
+
providers:
|
|
21
|
+
- id: anthropic:claude-3-5-haiku-latest
|
|
22
|
+
config:
|
|
23
|
+
temperature: 0
|
|
24
|
+
max_tokens: 512
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## HTTP Provider (test any endpoint that wraps an LLM)
|
|
28
|
+
|
|
29
|
+
```yaml
|
|
30
|
+
providers:
|
|
31
|
+
- id: http
|
|
32
|
+
config:
|
|
33
|
+
url: https://api.example.com/v1/answer
|
|
34
|
+
method: POST
|
|
35
|
+
headers:
|
|
36
|
+
Content-Type: application/json
|
|
37
|
+
Authorization: Bearer {{ENV.API_TOKEN}}
|
|
38
|
+
body:
|
|
39
|
+
model: support-agent
|
|
40
|
+
messages: [{ role: user, content: "{{prompt}}" }]
|
|
41
|
+
transformResponse: json.response # JSONPath-ish pick of the text to assert on
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`{{prompt}}` is the rendered prompt template; `{{ENV.NAME}` reads environment variables — no secrets in the config file.
|
|
45
|
+
|
|
46
|
+
## Echo Provider (offline harness checks)
|
|
47
|
+
|
|
48
|
+
Returns the rendered prompt verbatim — no network, no cost. Use it to validate the config, wiring, and deterministic asserts before spending tokens (note: plain `- echo`, not `- echo:text` — the colon shorthand was removed in recent versions):
|
|
49
|
+
|
|
50
|
+
```yaml
|
|
51
|
+
providers:
|
|
52
|
+
- echo
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## File/Script Providers
|
|
56
|
+
|
|
57
|
+
- `file://path/to/provider.py` or `.js` — full control (custom agent, chain, RAG pipeline); export a `CallApi(prompt, context)` function.
|
|
58
|
+
- Python file providers let you wrap internal SDKs not reachable over HTTP.
|
|
59
|
+
|
|
60
|
+
## Environment Variables
|
|
61
|
+
|
|
62
|
+
| Provider | Variable(s) |
|
|
63
|
+
|----------|-------------|
|
|
64
|
+
| OpenAI | `OPENAI_API_KEY` |
|
|
65
|
+
| Anthropic | `ANTHROPIC_API_KEY` |
|
|
66
|
+
| Google | `GOOGLE_API_KEY` / `GEMINI_API_KEY` |
|
|
67
|
+
| Azure OpenAI | `AZURE_OPENAI_API_BASE`, `AZURE_OPENAI_API_KEY`, `AZURE_OPENAI_DEPLOYMENT_NAME` |
|
|
68
|
+
| HTTP | anything referenced via `{{ENV.*}}` |
|
|
69
|
+
|
|
70
|
+
Load from `.env` files or the shell; never commit keys.
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: testing-resilience
|
|
3
|
+
description: Use when creating chaos engineering or resilience tests with k6 and xk6-disruptor. Use when the user wants to inject faults (pod failures, network delays, HTTP errors) into Kubernetes services during load tests, validate circuit breakers, or test system recovery behavior.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Testing Resilience with k6
|
|
7
|
+
|
|
8
|
+
Create chaos and resilience tests using xk6-disruptor to inject faults into Kubernetes services during k6 load tests. Validates system behavior under failure conditions.
|
|
9
|
+
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
xk6-disruptor injects faults into Kubernetes pods and services while k6 generates load, allowing you to verify:
|
|
13
|
+
- System survives pod failures
|
|
14
|
+
- Graceful degradation under network issues
|
|
15
|
+
- Circuit breaker behavior
|
|
16
|
+
- Recovery time after fault removal
|
|
17
|
+
- Auto-scaling response to failures
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```javascript
|
|
22
|
+
import http from 'k6/http';
|
|
23
|
+
import { check, sleep } from 'k6';
|
|
24
|
+
import { ServiceDisruptor } from 'k6/x/disruptor';
|
|
25
|
+
|
|
26
|
+
export const options = {
|
|
27
|
+
scenarios: {
|
|
28
|
+
load: {
|
|
29
|
+
executor: 'constant-arrival-rate',
|
|
30
|
+
rate: 100,
|
|
31
|
+
timeUnit: '1s',
|
|
32
|
+
duration: '2m',
|
|
33
|
+
preAllocatedVUs: 50,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
thresholds: {
|
|
37
|
+
http_req_failed: ['rate<0.3'], // Allow up to 30% errors during chaos
|
|
38
|
+
http_req_duration: ['p(95)<3000'], // Relaxed threshold during chaos
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export function setup() {
|
|
43
|
+
// Inject fault: Add 500ms delay to 50% of requests
|
|
44
|
+
const disruptor = new ServiceDisruptor('my-service', 'default');
|
|
45
|
+
const fault = {
|
|
46
|
+
averageDelay: '500ms',
|
|
47
|
+
errorRate: 0.1, // 10% of requests return errors
|
|
48
|
+
errorCode: 503,
|
|
49
|
+
};
|
|
50
|
+
disruptor.injectHTTPFaults(fault, '60s');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export default function () {
|
|
54
|
+
const res = http.get('http://my-service.default.svc.cluster.local:8080/api');
|
|
55
|
+
check(res, {
|
|
56
|
+
'not server error': (r) => r.status < 500 || r.status === 503,
|
|
57
|
+
});
|
|
58
|
+
sleep(0.1); // Intentional: adds backpressure per VU to avoid overwhelming the disrupted service
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Prerequisites
|
|
63
|
+
|
|
64
|
+
- **Kubernetes cluster** with target services deployed
|
|
65
|
+
- **k6 with xk6-disruptor** extension (or use automatic resolution)
|
|
66
|
+
- **RBAC permissions** for the k6 pod to manage target pods
|
|
67
|
+
|
|
68
|
+
## Disruptor Types
|
|
69
|
+
|
|
70
|
+
### ServiceDisruptor
|
|
71
|
+
|
|
72
|
+
Targets all pods behind a Kubernetes Service:
|
|
73
|
+
|
|
74
|
+
```javascript
|
|
75
|
+
import { ServiceDisruptor } from 'k6/x/disruptor';
|
|
76
|
+
|
|
77
|
+
const disruptor = new ServiceDisruptor('service-name', 'namespace');
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### PodDisruptor
|
|
81
|
+
|
|
82
|
+
Targets specific pods by label selector:
|
|
83
|
+
|
|
84
|
+
```javascript
|
|
85
|
+
import { PodDisruptor } from 'k6/x/disruptor';
|
|
86
|
+
|
|
87
|
+
const disruptor = new PodDisruptor({
|
|
88
|
+
namespace: 'default',
|
|
89
|
+
select: { labels: { app: 'my-app' } },
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Fault Types
|
|
94
|
+
|
|
95
|
+
### HTTP Faults
|
|
96
|
+
|
|
97
|
+
Inject delays and errors into HTTP responses:
|
|
98
|
+
|
|
99
|
+
```javascript
|
|
100
|
+
disruptor.injectHTTPFaults(
|
|
101
|
+
{
|
|
102
|
+
averageDelay: '500ms', // Add delay to responses
|
|
103
|
+
delayVariation: '100ms', // Delay jitter
|
|
104
|
+
errorRate: 0.1, // 10% error rate
|
|
105
|
+
errorCode: 503, // Error HTTP status code
|
|
106
|
+
port: 8080, // Target port
|
|
107
|
+
exclude: '/health', // Exclude health check endpoint
|
|
108
|
+
},
|
|
109
|
+
'60s' // Fault duration
|
|
110
|
+
);
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### gRPC Faults
|
|
114
|
+
|
|
115
|
+
Inject faults into gRPC services:
|
|
116
|
+
|
|
117
|
+
```javascript
|
|
118
|
+
disruptor.injectGrpcFaults(
|
|
119
|
+
{
|
|
120
|
+
averageDelay: '500ms',
|
|
121
|
+
errorRate: 0.2,
|
|
122
|
+
statusCode: 14, // UNAVAILABLE
|
|
123
|
+
statusMessage: 'service overloaded',
|
|
124
|
+
},
|
|
125
|
+
'60s'
|
|
126
|
+
);
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Pod Termination
|
|
130
|
+
|
|
131
|
+
Kill pods to test recovery:
|
|
132
|
+
|
|
133
|
+
```javascript
|
|
134
|
+
disruptor.terminatePods({
|
|
135
|
+
count: 1, // Number of pods to terminate
|
|
136
|
+
interval: '30s', // Wait between terminations
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
See [reference/fault-injection.md](reference/fault-injection.md) for detailed fault configuration.
|
|
141
|
+
|
|
142
|
+
## Resilience Test Patterns
|
|
143
|
+
|
|
144
|
+
### Pattern 1: Graceful Degradation
|
|
145
|
+
|
|
146
|
+
Verify system degrades gracefully under partial failure:
|
|
147
|
+
|
|
148
|
+
```javascript
|
|
149
|
+
export function setup() {
|
|
150
|
+
const disruptor = new ServiceDisruptor('backend-service', 'default');
|
|
151
|
+
disruptor.injectHTTPFaults({
|
|
152
|
+
averageDelay: '1000ms',
|
|
153
|
+
errorRate: 0.2,
|
|
154
|
+
errorCode: 503,
|
|
155
|
+
}, '90s');
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Success criteria:** System returns degraded responses instead of cascading failures.
|
|
160
|
+
|
|
161
|
+
### Pattern 2: Circuit Breaker Validation
|
|
162
|
+
|
|
163
|
+
Verify circuit breaker activates under error conditions:
|
|
164
|
+
|
|
165
|
+
```javascript
|
|
166
|
+
export function setup() {
|
|
167
|
+
const disruptor = new ServiceDisruptor('downstream-service', 'default');
|
|
168
|
+
disruptor.injectHTTPFaults({
|
|
169
|
+
errorRate: 0.8, // 80% errors to trigger circuit breaker
|
|
170
|
+
errorCode: 500,
|
|
171
|
+
}, '60s');
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Success criteria:** Upstream service returns fallback responses quickly (circuit open).
|
|
176
|
+
|
|
177
|
+
### Pattern 3: Recovery Test
|
|
178
|
+
|
|
179
|
+
Verify system recovers after fault is removed:
|
|
180
|
+
|
|
181
|
+
```javascript
|
|
182
|
+
export const options = {
|
|
183
|
+
scenarios: {
|
|
184
|
+
load: {
|
|
185
|
+
executor: 'constant-arrival-rate',
|
|
186
|
+
rate: 50,
|
|
187
|
+
timeUnit: '1s',
|
|
188
|
+
duration: '5m', // Total test: 5 minutes
|
|
189
|
+
preAllocatedVUs: 30,
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
export function setup() {
|
|
195
|
+
const disruptor = new ServiceDisruptor('my-service', 'default');
|
|
196
|
+
// Inject fault for only 2 minutes (test runs 5 minutes)
|
|
197
|
+
disruptor.injectHTTPFaults({
|
|
198
|
+
averageDelay: '2000ms',
|
|
199
|
+
errorRate: 0.5,
|
|
200
|
+
errorCode: 503,
|
|
201
|
+
}, '120s'); // Fault active for 2m, then 3m of recovery
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Success criteria:** Metrics return to baseline within acceptable time after fault removal.
|
|
206
|
+
|
|
207
|
+
See [reference/resilience-patterns.md](reference/resilience-patterns.md) for more patterns.
|
|
208
|
+
|
|
209
|
+
## Thresholds for Resilience Tests
|
|
210
|
+
|
|
211
|
+
Resilience tests need relaxed thresholds compared to normal load tests. Use tag-based thresholds to set different criteria for chaos and recovery phases:
|
|
212
|
+
|
|
213
|
+
```javascript
|
|
214
|
+
export const options = {
|
|
215
|
+
thresholds: {
|
|
216
|
+
// During chaos: accept higher error rates
|
|
217
|
+
http_req_failed: ['rate<0.5'], // Up to 50% errors acceptable
|
|
218
|
+
// Overall response time upper bound (includes chaos phase)
|
|
219
|
+
http_req_duration: ['p(95)<5000'], // 5s max even during chaos
|
|
220
|
+
// Strict threshold for recovery phase only (tagged requests)
|
|
221
|
+
'http_req_duration{phase:recovery}': ['p(95)<500'], // After recovery, back to normal
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Related Skills
|
|
227
|
+
|
|
228
|
+
- For generating load test scripts: `/k6:generating-api-load-tests`
|
|
229
|
+
- For CI/CD and Kubernetes setup: `/k6:operating-k6-in-ci-cd`
|
|
230
|
+
- For scenario design: `/k6:designing-test-scenarios`
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# Fault Injection Reference
|
|
2
|
+
|
|
3
|
+
## ServiceDisruptor
|
|
4
|
+
|
|
5
|
+
Targets all pods behind a Kubernetes Service.
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
import { ServiceDisruptor } from 'k6/x/disruptor';
|
|
9
|
+
|
|
10
|
+
const disruptor = new ServiceDisruptor('service-name', 'namespace');
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### injectHTTPFaults(fault, duration)
|
|
14
|
+
|
|
15
|
+
| Parameter | Type | Description |
|
|
16
|
+
|-----------|------|-------------|
|
|
17
|
+
| `averageDelay` | string | Average delay added to responses (e.g., `'500ms'`) |
|
|
18
|
+
| `delayVariation` | string | Variation around averageDelay (e.g., `'100ms'`) |
|
|
19
|
+
| `errorRate` | float | Fraction of requests returning errors (0.0-1.0) |
|
|
20
|
+
| `errorCode` | integer | HTTP status code for injected errors |
|
|
21
|
+
| `errorBody` | string | Response body for injected errors |
|
|
22
|
+
| `port` | integer | Target port (required if service has multiple ports) |
|
|
23
|
+
| `exclude` | string | URL path prefix to exclude from faults |
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
disruptor.injectHTTPFaults({
|
|
27
|
+
averageDelay: '500ms',
|
|
28
|
+
delayVariation: '100ms',
|
|
29
|
+
errorRate: 0.1,
|
|
30
|
+
errorCode: 503,
|
|
31
|
+
errorBody: '{"error": "service unavailable"}',
|
|
32
|
+
port: 8080,
|
|
33
|
+
exclude: '/health',
|
|
34
|
+
}, '60s');
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### injectGrpcFaults(fault, duration)
|
|
38
|
+
|
|
39
|
+
| Parameter | Type | Description |
|
|
40
|
+
|-----------|------|-------------|
|
|
41
|
+
| `averageDelay` | string | Average delay added to responses |
|
|
42
|
+
| `delayVariation` | string | Variation around averageDelay |
|
|
43
|
+
| `errorRate` | float | Fraction of requests returning errors (0.0-1.0) |
|
|
44
|
+
| `statusCode` | integer | gRPC status code for errors |
|
|
45
|
+
| `statusMessage` | string | gRPC status message for errors |
|
|
46
|
+
| `port` | integer | Target port |
|
|
47
|
+
| `exclude` | string | Service method to exclude |
|
|
48
|
+
|
|
49
|
+
```javascript
|
|
50
|
+
disruptor.injectGrpcFaults({
|
|
51
|
+
averageDelay: '500ms',
|
|
52
|
+
errorRate: 0.2,
|
|
53
|
+
statusCode: 14, // UNAVAILABLE
|
|
54
|
+
statusMessage: 'injected fault',
|
|
55
|
+
}, '60s');
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### terminatePods(options)
|
|
59
|
+
|
|
60
|
+
| Parameter | Type | Description |
|
|
61
|
+
|-----------|------|-------------|
|
|
62
|
+
| `count` | integer | Number of pods to terminate |
|
|
63
|
+
| `interval` | string | Wait time between terminations |
|
|
64
|
+
|
|
65
|
+
```javascript
|
|
66
|
+
disruptor.terminatePods({
|
|
67
|
+
count: 1,
|
|
68
|
+
interval: '30s',
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## PodDisruptor
|
|
73
|
+
|
|
74
|
+
Targets pods by label selector.
|
|
75
|
+
|
|
76
|
+
```javascript
|
|
77
|
+
import { PodDisruptor } from 'k6/x/disruptor';
|
|
78
|
+
|
|
79
|
+
const disruptor = new PodDisruptor({
|
|
80
|
+
namespace: 'default',
|
|
81
|
+
select: {
|
|
82
|
+
labels: { app: 'my-app', version: 'v1' },
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Same methods as ServiceDisruptor: `injectHTTPFaults()`, `injectGrpcFaults()`, `terminatePods()`.
|
|
88
|
+
|
|
89
|
+
Additional method:
|
|
90
|
+
|
|
91
|
+
### targets()
|
|
92
|
+
|
|
93
|
+
Returns list of target pod names:
|
|
94
|
+
|
|
95
|
+
```javascript
|
|
96
|
+
const pods = disruptor.targets();
|
|
97
|
+
console.log(`Targeting ${pods.length} pods:`, pods);
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Kubernetes Requirements
|
|
101
|
+
|
|
102
|
+
### RBAC
|
|
103
|
+
|
|
104
|
+
The k6 pod needs permissions to manage target pods:
|
|
105
|
+
|
|
106
|
+
```yaml
|
|
107
|
+
apiVersion: rbac.authorization.k8s.io/v1
|
|
108
|
+
kind: ClusterRole
|
|
109
|
+
metadata:
|
|
110
|
+
name: k6-disruptor
|
|
111
|
+
rules:
|
|
112
|
+
- apiGroups: [""]
|
|
113
|
+
resources: ["pods", "pods/ephemeralcontainers"]
|
|
114
|
+
verbs: ["get", "list", "patch", "update", "delete"]
|
|
115
|
+
- apiGroups: [""]
|
|
116
|
+
resources: ["services"]
|
|
117
|
+
verbs: ["get", "list"]
|
|
118
|
+
- apiGroups: ["apps"]
|
|
119
|
+
resources: ["deployments"]
|
|
120
|
+
verbs: ["get", "list"]
|
|
121
|
+
---
|
|
122
|
+
apiVersion: rbac.authorization.k8s.io/v1
|
|
123
|
+
kind: ClusterRoleBinding
|
|
124
|
+
metadata:
|
|
125
|
+
name: k6-disruptor-binding
|
|
126
|
+
roleRef:
|
|
127
|
+
apiGroup: rbac.authorization.k8s.io
|
|
128
|
+
kind: ClusterRole
|
|
129
|
+
name: k6-disruptor
|
|
130
|
+
subjects:
|
|
131
|
+
- kind: ServiceAccount
|
|
132
|
+
name: default
|
|
133
|
+
namespace: k6-test
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Running in Cluster
|
|
137
|
+
|
|
138
|
+
For xk6-disruptor to work, k6 must run **inside the Kubernetes cluster** (not locally). Options:
|
|
139
|
+
- k6-operator TestRun
|
|
140
|
+
- Pod with k6 + xk6-disruptor image
|
|
141
|
+
- CI/CD runner with cluster access
|
|
142
|
+
|
|
143
|
+
## Fault Duration vs Test Duration
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
Test Duration: |========================== 5 min ============================|
|
|
147
|
+
Fault Duration: |======== 2 min ========|
|
|
148
|
+
↑ Fault injected ↑ Fault removed ↑ Test ends
|
|
149
|
+
|
|
150
|
+
Phases: |--- Chaos Phase ------|--- Recovery Phase ---|
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Set fault duration shorter than test duration to observe both degradation and recovery.
|