@postman/postman-plugin 0.3.0 → 0.4.0
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/README.md +32 -8
- package/dist/hosts/antigravity.js +47 -0
- package/dist/hosts/copilot.js +60 -0
- package/dist/hosts/index.js +3 -1
- package/mcp.pi.json +2 -2
- package/package.json +1 -1
- package/skills/ai-readiness/SKILL.md +40 -44
- package/skills/api-discovery/SKILL.md +64 -145
- package/skills/api-discovery/reference/orbit.md +17 -5
- package/skills/api-documentation/SKILL.md +41 -46
- package/skills/api-engineer/SKILL.md +78 -23
- package/skills/api-mocking/SKILL.md +117 -163
- package/skills/api-monitoring/SKILL.md +52 -129
- package/skills/api-simulation/SKILL.md +130 -178
- package/skills/api-simulation/reference/sim_yaml_schema.md +41 -103
- package/skills/api-testing/SKILL.md +48 -95
- package/skills/bootstrap/SKILL.md +123 -224
- package/skills/ci-integration/SKILL.md +53 -113
- package/skills/collection-schema-v3/SKILL.md +43 -204
- package/skills/collection-schema-v3/reference/http.md +150 -0
- package/skills/datasets/SKILL.md +89 -315
- package/skills/datasets/reference/spreadsheets.md +25 -0
- package/skills/flows/SKILL.md +57 -206
- package/skills/flows/reference/flow_cli_flags.md +3 -2
- package/skills/performance-testing/SKILL.md +46 -62
- package/skills/performance-testing/references/jmeter-import/README.md +113 -0
- package/skills/performance-testing/references/jmeter-import/import-report.md +52 -0
- package/skills/performance-testing/references/jmeter-import/mapping-table.md +97 -0
- package/skills/performance-testing/references/jmeter-import/postman-perf-capabilities.md +56 -0
- package/skills/performance-testing/references/jmeter-import/script-translation.md +98 -0
- package/skills/performance-testing/references/jmeter-import/scripts/groovy_to_js.py +83 -0
- package/skills/performance-testing/references/jmeter-import/scripts/jmx_to_postman.py +1433 -0
- package/skills/postman-mcp-server/SKILL.md +53 -59
- package/skills/postman-mcp-server/references/docs.md +0 -1
- package/skills/postman-mcp-server/references/learn.md +10 -9
- package/skills/postman-mcp-server/references/mock.md +0 -1
- package/skills/postman-mcp-server/references/search.md +0 -1
- package/skills/postman-mcp-server/references/security.md +0 -1
- package/skills/postman-mcp-server/references/setup.md +0 -1
- package/skills/postman-mcp-server/references/sync.md +0 -1
- package/skills/postman-mcp-server/references/test.md +0 -1
- package/skills/variables/SKILL.md +44 -87
- package/skills/webhook-testing/SKILL.md +103 -0
- package/skills/webhook-testing/reference/forwarding.md +10 -0
- package/skills/webhook-testing/reference/outbound_webhooks.md +25 -0
- package/skills/webhook-testing/reference/webhook_scripts.md +163 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# JMeter → Postman mapping table
|
|
2
|
+
|
|
3
|
+
Fidelity: **exact** (behaviour preserved) · **approx** (closest supported equivalent, reported) · **manual** (needs the model/user) · **unsupported** (dropped, reported).
|
|
4
|
+
Owner: **script** (deterministic Python) · **model** (judgment call).
|
|
5
|
+
|
|
6
|
+
## Structure
|
|
7
|
+
|
|
8
|
+
| JMeter element | XML tag | Postman | Fidelity | Owner |
|
|
9
|
+
|---|---|---|---|---|
|
|
10
|
+
| Test Plan | `TestPlan` | Collection (`info` + `item`) | exact | script |
|
|
11
|
+
| Thread Group | `ThreadGroup` | load config (VUs/duration/profile) + folder when >1 | approx | script |
|
|
12
|
+
| setUp/tearDown Thread Group | `SetupThreadGroup`/`PostThreadGroup` | folder; suggest `--setup-collection`/`--teardown-collection` | approx | script+model |
|
|
13
|
+
| Concurrency Thread Group | `...ConcurrencyThreadGroup` | VUs=`TargetLevel`, duration=`RampUp`+`Hold`, `ramp-up` | approx | script |
|
|
14
|
+
| Stepping Thread Group | `kg.apc...SteppingThreadGroup` | VUs=`num_threads`, duration=`flighttime`, `ramp-up` | approx | script |
|
|
15
|
+
| Ultimate Thread Group | `kg.apc...UltimateThreadGroup` | VUs=max row concurrency, duration=sum of row times | approx | script |
|
|
16
|
+
| Arrivals Thread Group | `...ArrivalsThreadGroup` | treated like concurrency | approx | script |
|
|
17
|
+
| Transaction Controller | `TransactionController` | folder | exact | script |
|
|
18
|
+
| Simple/Generic Controller | `GenericController`/`SimpleController` | folder | exact | script |
|
|
19
|
+
| Loop Controller | `LoopController` | flattened; loop count → duration | approx | script |
|
|
20
|
+
| If Controller | `IfController` | each enclosed request gets a pre-request skip guard (`pm.execution.skipRequest`); `${var}` → `pm.variables.get(...)` | approx | script |
|
|
21
|
+
| While Controller | `WhileController` | flattened, requests run unconditionally; restore looping with `pm.execution.setNextRequest` | approx | model |
|
|
22
|
+
| Recording Controller | `RecordingController` | transparent (children recursed) | exact | script |
|
|
23
|
+
| Module / Include Controller | `ModuleController`/`IncludeController` | flattened children if inline; external refs reported | manual | model |
|
|
24
|
+
|
|
25
|
+
## Requests
|
|
26
|
+
|
|
27
|
+
| JMeter | XML | Postman | Fidelity | Owner |
|
|
28
|
+
|---|---|---|---|---|
|
|
29
|
+
| HTTP Request | `HTTPSamplerProxy` | request item | exact | script |
|
|
30
|
+
| method / path / domain / port / protocol | `HTTPSampler.*` | `request.method` + `url` (blank protocol → `http`) | exact | script |
|
|
31
|
+
| Query/body params | `Arguments`/`HTTPArgument` | query (GET) / urlencoded (POST/PUT/PATCH) / formdata (multipart) | exact | script |
|
|
32
|
+
| Raw body | `postBodyRaw=true` | `body.mode=raw` | exact | script |
|
|
33
|
+
| Header Manager | `HeaderManager` | `request.header` (merged by scope, later wins) | exact | script |
|
|
34
|
+
| HTTP Request Defaults | `ConfigTestElement`/`HttpDefaultsGui` | fallback for blank domain/port/protocol/path | exact | script |
|
|
35
|
+
| Auth Manager Basic/Digest | `AuthManager` | `request.auth` basic/digest (first entry; multiples reported) | exact | script |
|
|
36
|
+
| Auth Manager Kerberos/NTLM | `AuthManager` mechanism | — | unsupported | script |
|
|
37
|
+
| Cookie / Cache / DNS Manager | `CookieManager`/... | auto per-VU cookies; presets not transferred | unsupported | script |
|
|
38
|
+
| Non-HTTP samplers | `JDBC/FTP/LDAP/Java/TCP/Debug/Dummy...Sampler` | — | unsupported | script (reported) |
|
|
39
|
+
|
|
40
|
+
## Extractors (post-response → test script `pm.collectionVariables.set`)
|
|
41
|
+
|
|
42
|
+
| JMeter | XML | Fidelity | Owner |
|
|
43
|
+
|---|---|---|---|
|
|
44
|
+
| Regular Expression Extractor | `RegexExtractor` | exact (via `new RegExp`, group from `$n$` template) | script |
|
|
45
|
+
| Boundary Extractor | `BoundaryExtractor` | exact (left/right → non-greedy regex) | script |
|
|
46
|
+
| JSON Extractor (native) | `JSONPostProcessor` | exact for dot/index paths; complex → manual | script→model |
|
|
47
|
+
| JSON Path Extractor (plugin) | `...JSONPathExtractor` | exact for simple paths | script→model |
|
|
48
|
+
| XPath / XPath2 Extractor | `XPathExtractor`/`XPath2Extractor` | manual (no sandbox XPath) | model |
|
|
49
|
+
| CSS/HTML Extractor | `HtmlExtractor` | manual (no sandbox CSS) | model |
|
|
50
|
+
|
|
51
|
+
## Assertions (→ `pm.test`)
|
|
52
|
+
|
|
53
|
+
| JMeter | XML | Maps to | Fidelity | Owner |
|
|
54
|
+
|---|---|---|---|---|
|
|
55
|
+
| Response Assertion | `ResponseAssertion` | status / contains / equals / matches, honouring `test_type` bitmask + NOT | exact | script |
|
|
56
|
+
| — test_field response_code | | `pm.response.code` | exact | script |
|
|
57
|
+
| — test_field response_data | | `pm.response.text()` | exact | script |
|
|
58
|
+
| — test_field headers/message | | `pm.response.headers`/`.status` | exact | script |
|
|
59
|
+
| Duration Assertion | `DurationAssertion` | `pm.test` responseTime + `--pass-if less_than(p95, ms)` hint | exact | script |
|
|
60
|
+
| Size Assertion | `SizeAssertion` | `pm.test` on `responseSize` | approx | script |
|
|
61
|
+
| JSON Assertion | `JSONPathAssertion` | `pm.test` on simple path | approx | script→model |
|
|
62
|
+
|
|
63
|
+
`ResponseAssertion.test_type` bits: `MATCH=1, CONTAINS=2, NOT=4, EQUALS=8, SUBSTRING=16, OR=32`.
|
|
64
|
+
|
|
65
|
+
## Variables & data
|
|
66
|
+
|
|
67
|
+
| JMeter | XML | Postman | Fidelity | Owner |
|
|
68
|
+
|---|---|---|---|---|
|
|
69
|
+
| User Defined Variables | `Arguments` under TestPlan/scope | collection `variable[]` | exact | script |
|
|
70
|
+
| `${var}` references | inline | `{{var}}` | exact | script |
|
|
71
|
+
| `${__UUID}` / `${__time}` | function | `{{$guid}}` / `{{$timestamp}}` | exact | script |
|
|
72
|
+
| `${__Random(...)}` and other `__fn` | function | `{{$randomInt}}` or flagged reference | approx/manual | script→model |
|
|
73
|
+
| CSV Data Set Config | `CSVDataSet` | `--data-file` + column list | approx (one file/run; naive CSV) | script |
|
|
74
|
+
| Counter / Random Variable | `CounterConfig`/`RandomVariableConfig` | pre-request script or `{{$randomInt}}` | manual | model |
|
|
75
|
+
|
|
76
|
+
## Scripting
|
|
77
|
+
|
|
78
|
+
| JMeter | XML | Postman | Fidelity | Owner |
|
|
79
|
+
|---|---|---|---|---|
|
|
80
|
+
| JSR223 PreProcessor | `JSR223PreProcessor` | prerequest script (JS, IIFE-wrapped) | model-assisted | script+model |
|
|
81
|
+
| JSR223 PostProcessor | `JSR223PostProcessor` | test script | model-assisted | script+model |
|
|
82
|
+
| BeanShell Pre/PostProcessor | `BeanShell*Processor` | pre/test script | model-assisted | script+model |
|
|
83
|
+
| JSR223 / BeanShell Sampler | `JSR223Sampler`/`BeanShellSampler` | script-only stub (no HTTP) | manual | script+model |
|
|
84
|
+
|
|
85
|
+
## Timing / pacing — approximated, always reported
|
|
86
|
+
|
|
87
|
+
Postman has no native think time. A fixed delay is recovered where one exists;
|
|
88
|
+
random/throughput/sync shaping is not reproduced.
|
|
89
|
+
|
|
90
|
+
| JMeter | XML | Mapping |
|
|
91
|
+
|---|---|---|
|
|
92
|
+
| Constant Timer (per-request) | `ConstantTimer` under a sampler | pre-request pause of its delay |
|
|
93
|
+
| Constant / Uniform / Gaussian / Poisson Timer (group scope) | `ConstantTimer`/`UniformRandomTimer`/... | `--delay-request <ms>` (fixed part only) |
|
|
94
|
+
| Constant Throughput Timer | `ConstantThroughputTimer` | not reproduced; reported |
|
|
95
|
+
| Throughput Shaping Timer | plugin | not reproduced; reported |
|
|
96
|
+
| Synchronizing Timer | `SyncTimer` | not reproduced; reported |
|
|
97
|
+
| Test Action (pause/stop) | `TestAction` | dropped; reported |
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# What Postman performance testing supports (the import target)
|
|
2
|
+
|
|
3
|
+
Grounded in the `postman-cli`, `performance-test-api`, and `postman-performance-test` source — not the marketing docs. This is the ceiling the importer maps onto. When JMeter asks for something not here, it is reported, never faked.
|
|
4
|
+
|
|
5
|
+
## `postman performance run` CLI flags that matter for import
|
|
6
|
+
|
|
7
|
+
| Concern | Flag | Notes |
|
|
8
|
+
|---------|------|-------|
|
|
9
|
+
| Virtual users | `--vu-count <n>` | default 20; cloud runs enforce a **minimum of 10** |
|
|
10
|
+
| Duration | `-d, --duration <minutes>` | **minutes**, not seconds; default 1; cloud ceiling ~120 min (plan-gated) |
|
|
11
|
+
| Load profile | `-p, --load-profile <p>` | one of `fixed`, `ramp-up`, `spike`, `peak`; default `ramp-up` |
|
|
12
|
+
| Data file | `--data-file <path>` | JSON array or CSV; **local runs only**; distribution is forced to `random` |
|
|
13
|
+
| Dataset | `--dataset-id` + `--dataset-view-id` + `--dataset-distribution` | cloud path; distribution `round-robin`/`fixed`/`random` |
|
|
14
|
+
| Pass/fail | `--pass-if "<fn>(<metric>, <value>)"` | **single, not repeatable** |
|
|
15
|
+
| Environment | `-e, --environment <id>` | |
|
|
16
|
+
| Setup/teardown | `--setup-collection` / `--teardown-collection` | run **once** before/after — not concurrent load |
|
|
17
|
+
|
|
18
|
+
There is **no** `--load-segments`, no `--stages`, no `--iterations`, no think-time flag. A latent YAML "stages" schema exists in the CLI repo but is not wired to the command — do not target it.
|
|
19
|
+
|
|
20
|
+
## Load profiles
|
|
21
|
+
|
|
22
|
+
Four named shapes only, each derived from `(vu-count, duration)`:
|
|
23
|
+
|
|
24
|
+
- `fixed` — constant VUs.
|
|
25
|
+
- `ramp-up` — ramps from 25% to full over the first quarter.
|
|
26
|
+
- `spike` — low baseline, one spike to full mid-run, back down.
|
|
27
|
+
- `peak` — baseline, ramp up, hold, ramp down.
|
|
28
|
+
|
|
29
|
+
JMeter custom ramp curves, stepping schedules, and arrival-rate shapes **cannot be reproduced**. Pick the closest of the four and report the approximation. Rule used by the importer: any ramp/stepping/concurrency/ultimate/arrivals group → `ramp-up`; a flat group with zero ramp → `fixed`.
|
|
30
|
+
|
|
31
|
+
## `--pass-if` metrics and operators
|
|
32
|
+
|
|
33
|
+
- Functions (operators): `less_than`, `greater_than`, `less_than_eq`, `greater_than_eq`.
|
|
34
|
+
- Metrics (CLI): `avg`, `p90`, `p95`, `p99` (ms); `error_rate`, `failure_rate` (%); `rps`.
|
|
35
|
+
- **Server-side (cloud) accepts a narrower set**: `responseTime.average/p90/p95/p99`, `throughput`, `errorRate`. `failure_rate` is local-only — if you emit it, note it won't gate a cloud run.
|
|
36
|
+
- Only one condition per run. A JMeter Duration Assertion is the only clean source (→ `less_than(p95, <ms>)`); otherwise suggest `less_than(error_rate, 5)` and tell the user it's a default.
|
|
37
|
+
|
|
38
|
+
## Scripts under load
|
|
39
|
+
|
|
40
|
+
- Pre-request / test scripts run per VU per iteration via `postman-runtime`.
|
|
41
|
+
- `postman.setNextRequest(...)` **is supported** — flow control within one VU's iteration works.
|
|
42
|
+
- Vault access **in scripts** is disabled; per-request timeouts are forced to infinite; each VU gets its own cookie jar.
|
|
43
|
+
- Known engine caveat: mutating `pm.request` inside a script can bleed across VUs. `pm.iterationData` / `pm.variables` are correctly isolated. Avoid importing JMeter logic into `pm.request` mutation.
|
|
44
|
+
|
|
45
|
+
## Data
|
|
46
|
+
|
|
47
|
+
- One `--data-file` (JSON array of objects, or CSV) per run. Multiple JMeter CSV Data Sets must be merged or a primary chosen.
|
|
48
|
+
- Rows map to VUs/iterations; rows < VUs repeat. The CLI CSV parser is a naive comma split — **no quoted-field/escape support**, so JMeter CSVs with embedded commas/quotes need cleaning.
|
|
49
|
+
|
|
50
|
+
## Hard limits to encode in every import
|
|
51
|
+
|
|
52
|
+
1. One collection, repeated across VUs. **No concurrent thread groups.**
|
|
53
|
+
2. Time-based, not iteration-based. **Loop counts → duration.**
|
|
54
|
+
3. One `--pass-if`.
|
|
55
|
+
4. **No native think time** — timers are approximated (`--delay-request` for group scope, a pre-request pause for per-request), not reproduced exactly.
|
|
56
|
+
5. **HTTP(S) only.**
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Translating JSR223 / BeanShell scripts to Postman JavaScript
|
|
2
|
+
|
|
3
|
+
`scripts/groovy_to_js.py` does a best-effort mechanical pass and the importer wraps every result in an IIFE so it is always syntactically valid. It is **never trusted** — each imported script is flagged as a manual task. This file is how you (the model) finish the job.
|
|
4
|
+
|
|
5
|
+
## Execution-context map
|
|
6
|
+
|
|
7
|
+
JMeter runs these inside a sampler's processor. Postman runs them as a request's `prerequest` or `test` event. Equivalents:
|
|
8
|
+
|
|
9
|
+
| JMeter (Groovy/BeanShell) | Postman JS | Context |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| `vars.get("x")` / `vars["x"]` | `pm.variables.get("x")` | both |
|
|
12
|
+
| `vars.put("x", v)` | `pm.variables.set("x", v)` | both |
|
|
13
|
+
| `props.get/put(...)` | `pm.variables.get/set(...)` (approx) | both |
|
|
14
|
+
| `log.info/warn/error(...)` | `console.log/warn/error(...)` | both |
|
|
15
|
+
| `prev.getResponseDataAsString()` | `pm.response.text()` | post only |
|
|
16
|
+
| `prev.getResponseCode()` | `String(pm.response.code)` | post only |
|
|
17
|
+
| `prev.getResponseMessage()` | `pm.response.status` | post only |
|
|
18
|
+
| `prev.getTime()` | `pm.response.responseTime` | post only |
|
|
19
|
+
| `ctx.getPreviousResult()` | `pm.response` | post only |
|
|
20
|
+
| `sampler.getUrl()` | `pm.request.url.toString()` | pre only |
|
|
21
|
+
| `sampler.addArgument(k,v)` | `pm.request.url.query.add({key:k,value:v})` or body edit | pre only |
|
|
22
|
+
| `System.currentTimeMillis()` | `Date.now()` | both |
|
|
23
|
+
| `UUID.randomUUID().toString()` | `require('uuid').v4()` | both |
|
|
24
|
+
| `Integer.parseInt(x)` | `parseInt(x)` | both |
|
|
25
|
+
|
|
26
|
+
## What the mechanical pass cannot do — fix these by hand
|
|
27
|
+
|
|
28
|
+
- **BeanShell implicit result vars** (`ResponseCode`, `ResponseMessage`, `IsSuccess`, `bsh.args`, `Parameters`). No pm.* equivalent in a processor. For a sampler that sets `IsSuccess=false`, express it as a `pm.test(...)` that fails. For `return X`, delete the return (the IIFE makes it legal, but it's a no-op).
|
|
29
|
+
- **Groovy closures / collection methods** (`.each {}`, `.collect {}`, `->`). Rewrite as `for...of` / `.map()` / arrow functions.
|
|
30
|
+
- **`import` / `new ClassName()` / Java types** (`StringBuilder`, `SimpleDateFormat`, `JsonSlurper`). Replace with JS: string concat, `Date`, `JSON.parse`. `JsonSlurper().parseText(prev...)` → `pm.response.json()`.
|
|
31
|
+
- **`try/catch`, multi-statement logic** — valid JS already; just verify semantics.
|
|
32
|
+
- **GString interpolation** `"${x} and ${y}"` → template literal `` `${x} and ${y}` `` **only if** `x`/`y` are JS locals; if they are JMeter vars use `pm.variables.get`.
|
|
33
|
+
|
|
34
|
+
## Procedure
|
|
35
|
+
|
|
36
|
+
1. Read the `// --- imported ... (REVIEW) ---` block in the request's event script.
|
|
37
|
+
2. Compare against the original `<script>` text in the `.jmx` (the IR keeps the element's name).
|
|
38
|
+
3. Rewrite line by line using the table. Preserve variable names so `{{var}}` references elsewhere still resolve.
|
|
39
|
+
4. Remove the `// REVIEW` marker once correct.
|
|
40
|
+
5. Re-run the node syntax gate from `SKILL.md` step 3.
|
|
41
|
+
|
|
42
|
+
## Example
|
|
43
|
+
|
|
44
|
+
JMeter (Groovy post-processor):
|
|
45
|
+
```groovy
|
|
46
|
+
import groovy.json.JsonSlurper
|
|
47
|
+
def json = new JsonSlurper().parseText(prev.getResponseDataAsString())
|
|
48
|
+
def token = json.data.token
|
|
49
|
+
vars.put("authToken", token)
|
|
50
|
+
log.info("got " + token)
|
|
51
|
+
```
|
|
52
|
+
Postman (test script):
|
|
53
|
+
```javascript
|
|
54
|
+
const json = pm.response.json();
|
|
55
|
+
const token = json.data.token;
|
|
56
|
+
pm.collectionVariables.set("authToken", token);
|
|
57
|
+
console.log("got " + token);
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Canonical JS templates (match the script byte-for-byte in fallback mode)
|
|
61
|
+
|
|
62
|
+
When importing by hand, emit exactly these so hand output equals the script's. `<default>` empty-string if JMeter has none. Source is `pm.response.text()` normally, or `pm.response.headers.toString()` when the extractor's `useHeaders` is true.
|
|
63
|
+
|
|
64
|
+
**Regex extractor** (`$n$` template → group `n`):
|
|
65
|
+
```javascript
|
|
66
|
+
try { var _m = String(pm.response.text()).match(new RegExp("<REGEX>")); pm.collectionVariables.set("<ref>", _m ? _m[<n>] : "<default>"); } catch (e) { pm.collectionVariables.set("<ref>", "<default>"); }
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Boundary extractor** (left/right → non-greedy capture):
|
|
70
|
+
```javascript
|
|
71
|
+
try { var _b = String(pm.response.text()).match(new RegExp("<lboundary>([\\s\\S]*?)<rboundary>")); pm.collectionVariables.set("<ref>", _b ? _b[1] : "<default>"); } catch (e) { pm.collectionVariables.set("<ref>", "<default>"); }
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**JSON extractor** (`JSONPostProcessor` / plugin). Only **simple** paths (`$.a.b`, `$.a[0].c`) import; `$`/`$.` → `pm.response.json()`:
|
|
75
|
+
```javascript
|
|
76
|
+
try { pm.collectionVariables.set("<ref>", pm.response.json().a.b[0]); } catch (e) { pm.collectionVariables.set("<ref>", "<default>"); }
|
|
77
|
+
```
|
|
78
|
+
Wildcards / filters / recursive descent (`*`, `..`, `?`, `@`) → do NOT import; emit `// [manual] JSONPath "<expr>" too complex` and add a manual task.
|
|
79
|
+
|
|
80
|
+
**Implicit HTTP success** (first test on every request unless a `response_code`/`response_message` assertion or `assume_success` exists):
|
|
81
|
+
```javascript
|
|
82
|
+
pm.test('JMeter default success (HTTP 200-399)', function () {
|
|
83
|
+
pm.expect(pm.response.code).to.be.within(200, 399);
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Response Assertion** — decode `test_type` bits (`MATCH=1, CONTAINS=2, NOT=4, EQUALS=8, SUBSTRING=16, OR=32`). Field getter: `response_code`→`pm.response.code`, `response_message`→`pm.response.status`, `response_headers`→`pm.response.headers.toString()`, else `pm.response.text()`. For each test string `s`:
|
|
88
|
+
- **EQUALS (8)** on `response_code`: `pm.expect(pm.response.code).to.eql(<s>)` (number). Other fields: `.to.eql("<s>")`.
|
|
89
|
+
- **SUBSTRING (16)** → literal: `pm.expect(<getter>).to.include("<s>")`.
|
|
90
|
+
- **CONTAINS (2) or MATCH (1)** → regex: `pm.expect(String(<getter>)).to.match(new RegExp("<s>"))`.
|
|
91
|
+
- **NOT (4)** → insert `.not` before `.to`.
|
|
92
|
+
Wrap each in `pm.test("<name>", function () { <expr>; });`.
|
|
93
|
+
|
|
94
|
+
**Duration Assertion** → a test plus a perf `--pass-if` hint:
|
|
95
|
+
```javascript
|
|
96
|
+
pm.test("Response time < <ms>ms", function () { pm.expect(pm.response.responseTime).to.be.below(<ms>); });
|
|
97
|
+
```
|
|
98
|
+
and set the run's `--pass-if "less_than(p95, <ms>)"`.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""Best-effort, deterministic Groovy / BeanShell -> JavaScript translation.
|
|
2
|
+
|
|
3
|
+
This handles the common mechanical rewrites so the model only has to review and
|
|
4
|
+
fix the genuinely ambiguous parts. Every script that passes through here is ALSO
|
|
5
|
+
flagged as a manual task, because no regex pass can guarantee semantic fidelity
|
|
6
|
+
for arbitrary JSR223/BeanShell code.
|
|
7
|
+
|
|
8
|
+
Keep the rules ordered from most specific to least specific. The model may extend
|
|
9
|
+
this table; see references/script-translation.md for the patterns and rationale.
|
|
10
|
+
"""
|
|
11
|
+
import re
|
|
12
|
+
|
|
13
|
+
# (pattern, replacement) applied in order. Patterns are plain regex on source text.
|
|
14
|
+
RULES = [
|
|
15
|
+
# --- JMeter bound variables -> pm equivalents ---
|
|
16
|
+
# vars["x"] / vars['x'] -> pm.variables.get("x") (bracket form, must precede .get/.put rules)
|
|
17
|
+
(r'\bvars\[\s*([\'"][^\'"]*[\'"])\s*\]', r'pm.variables.get(\1)'),
|
|
18
|
+
(r'\bprops\[\s*([\'"][^\'"]*[\'"])\s*\]', r'pm.variables.get(\1)'),
|
|
19
|
+
(r'\bvars\.get\(', 'pm.variables.get('),
|
|
20
|
+
(r'\bvars\.put\(', 'pm.variables.set('),
|
|
21
|
+
(r'\bvars\.getObject\(', 'pm.variables.get('),
|
|
22
|
+
(r'\bvars\.putObject\(', 'pm.variables.set('),
|
|
23
|
+
(r'\bprops\.get\(', 'pm.variables.get('),
|
|
24
|
+
(r'\bprops\.put\(', 'pm.variables.set('),
|
|
25
|
+
|
|
26
|
+
# --- logging ---
|
|
27
|
+
(r'\blog\.info\(', 'console.log('),
|
|
28
|
+
(r'\blog\.warn\(', 'console.warn('),
|
|
29
|
+
(r'\blog\.error\(', 'console.error('),
|
|
30
|
+
(r'\blog\.debug\(', 'console.log('),
|
|
31
|
+
|
|
32
|
+
# --- previous-sample result accessors (post-processor context) ---
|
|
33
|
+
(r'\bprev\.getResponseDataAsString\(\)', 'pm.response.text()'),
|
|
34
|
+
(r'\bprev\.getResponseCode\(\)', 'String(pm.response.code)'),
|
|
35
|
+
(r'\bprev\.getResponseMessage\(\)', 'pm.response.status'),
|
|
36
|
+
(r'\bprev\.getResponseHeaders\(\)', 'pm.response.headers.toString()'),
|
|
37
|
+
(r'\bprev\.getTime\(\)', 'pm.response.responseTime'),
|
|
38
|
+
(r'\bctx\.getPreviousResult\(\)', 'pm.response'),
|
|
39
|
+
|
|
40
|
+
# --- sampler mutation (pre-processor context) ---
|
|
41
|
+
(r'\bsampler\.getUrl\(\)', 'pm.request.url.toString()'),
|
|
42
|
+
(r'\bsampler\.addArgument\(', '// [manual] sampler.addArgument -> set a query/body param; '),
|
|
43
|
+
|
|
44
|
+
# --- JDK calls that have JS equivalents ---
|
|
45
|
+
(r'\bSystem\.currentTimeMillis\(\)', 'Date.now()'),
|
|
46
|
+
(r'\bUUID\.randomUUID\(\)\.toString\(\)', 'require("uuid").v4()'),
|
|
47
|
+
(r'\bInteger\.parseInt\(', 'parseInt('),
|
|
48
|
+
(r'\bLong\.parseLong\(', 'parseInt('),
|
|
49
|
+
(r'\bDouble\.parseDouble\(', 'parseFloat('),
|
|
50
|
+
(r'\bString\.valueOf\(', 'String('),
|
|
51
|
+
(r'\bMath\.', 'Math.'), # identity, keeps it explicit
|
|
52
|
+
|
|
53
|
+
# --- declarations: Groovy def / typed decls -> let ---
|
|
54
|
+
(r'\bdef\s+', 'let '),
|
|
55
|
+
(r'\b(?:String|int|long|double|float|boolean|Object|var)\s+(\w+\s*=)', r'let \1'),
|
|
56
|
+
|
|
57
|
+
# --- Groovy GString interpolation "${x}" stays valid-ish; leave for model ---
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
# Tokens that, if present after translation, almost certainly need human review.
|
|
61
|
+
REVIEW_MARKERS = [
|
|
62
|
+
'import ', 'new ', 'class ', '.each', '.collect', '->', 'bsh.', 'ctx.',
|
|
63
|
+
'OUT.', 'SampleResult', 'Thread.sleep', 'try {', 'catch', '@', '::',
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def translate(source: str, lang: str = 'groovy') -> dict:
|
|
68
|
+
"""Return {'js': <translated>, 'needs_review': bool, 'reasons': [...]}."""
|
|
69
|
+
if source is None:
|
|
70
|
+
return {'js': '', 'needs_review': False, 'reasons': []}
|
|
71
|
+
js = source
|
|
72
|
+
for pat, repl in RULES:
|
|
73
|
+
js = re.sub(pat, repl, js)
|
|
74
|
+
|
|
75
|
+
# BeanShell implicit result vars have no clean pm.* mapping.
|
|
76
|
+
reasons = []
|
|
77
|
+
if lang and 'beanshell' in lang.lower():
|
|
78
|
+
reasons.append('BeanShell script: implicit vars (ResponseCode, IsSuccess, bsh.args) have no pm.* equivalent')
|
|
79
|
+
for marker in REVIEW_MARKERS:
|
|
80
|
+
if marker in js:
|
|
81
|
+
reasons.append(f'contains "{marker.strip()}" which needs manual translation')
|
|
82
|
+
needs_review = True # always true: no mechanical pass is trustworthy for scripts
|
|
83
|
+
return {'js': js, 'needs_review': needs_review, 'reasons': sorted(set(reasons))}
|