Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/rest-api-vectors/absent-caps.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"contract": "docs/rest-api.md#get-apistatus",
"says": "The default build reports `[\"patterns\",\"params\",\"sleep\"]` and nothing else; each [edition](EDITIONS.md) adds its own — Audio adds `osc` and `audio`, Performance adds `weather`, `mqtt` and `shows`.",
"features": [],
"steps": [{ "send": { "http": "GET /api/status" }, "expect": { "reply": { "status": 200, "body": { "caps": ["patterns", "params", "sleep"] } } } }]
}
6 changes: 6 additions & 0 deletions docs/rest-api-vectors/absent-feature-nav.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"contract": "docs/rest-api.md#get-apistatus",
"says": "What the console header and home screen build their feature links from; empty on the default build.",
"features": [],
"steps": [{ "send": { "http": "GET /api/status" }, "expect": { "reply": { "status": 200, "body": { "featureNav": [] } } } }]
}
16 changes: 16 additions & 0 deletions docs/rest-api-vectors/absent-routes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"contract": "docs/rest-api.md#conventions-for-future-additions",
"says": "A feature's endpoints exist only when that feature is composed in — the route itself is absent otherwise, so clients probe `caps` rather than expecting a soft 404.",
"features": [],
"steps": [
{ "send": { "http": "GET /api/mqtt" }, "expect": { "reply": { "status": 404, "headers": { "content-type": "text/plain" } } } },
{ "send": { "http": "POST /api/mqtt?role=subscriber" }, "expect": { "reply": { "status": 404, "headers": { "content-type": "text/plain" } } } },
{ "send": { "http": "GET /api/midi" }, "expect": { "reply": { "status": 404, "headers": { "content-type": "text/plain" } } } },
{ "send": { "http": "POST /api/midi?outMode=rel" }, "expect": { "reply": { "status": 404, "headers": { "content-type": "text/plain" } } } },
{ "send": { "http": "GET /api/audio" }, "expect": { "reply": { "status": 404, "headers": { "content-type": "text/plain" } } } },
{ "send": { "http": "POST /api/audio?on=1" }, "expect": { "reply": { "status": 404, "headers": { "content-type": "text/plain" } } } },
{ "send": { "http": "GET /api/audio-in" }, "expect": { "reply": { "status": 404, "headers": { "content-type": "text/plain" } } } },
{ "send": { "http": "GET /api/clock" }, "expect": { "reply": { "status": 404, "headers": { "content-type": "text/plain" } } } },
{ "send": { "http": "GET /api/shows" }, "expect": { "reply": { "status": 404, "headers": { "content-type": "text/plain" } } } }
]
}
10 changes: 10 additions & 0 deletions docs/rest-api-vectors/device-compressed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"contract": "docs/rest-api.md#transport",
"says": "Nothing under `/api/` is compressed.",
"features": [],
"steps": [
{ "send": { "http": { "request": "GET /api/status", "headers": { "Accept-Encoding": "gzip, deflate" } } }, "expect": { "reply": { "status": 200, "headers": { "content-encoding": "<absent>" } } } },
{ "send": { "http": { "request": "GET /api/patterns", "headers": { "Accept-Encoding": "gzip, deflate" } } }, "expect": { "reply": { "status": 200, "headers": { "content-encoding": "<absent>" } } } },
{ "send": { "http": { "request": "GET /api/patterns/file?slug=wave_saw", "headers": { "Accept-Encoding": "gzip, deflate" } } }, "expect": { "reply": { "status": 200, "headers": { "content-encoding": "<absent>" } } } }
]
}
15 changes: 15 additions & 0 deletions docs/rest-api-vectors/device-cors.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"contract": "docs/rest-api.md#transport",
"says": "Only `/api/patterns/select` and `/api/display` send `Access-Control-Allow-Origin: *`.",
"features": [],
"steps": [
{ "send": { "http": "GET /api/display" }, "expect": { "reply": { "status": 200, "headers": { "access-control-allow-origin": "*" } } } },
{ "send": { "http": "GET /api/patterns/select?index=0" }, "expect": { "reply": { "status": 200, "headers": { "access-control-allow-origin": "*" } } } },
{ "send": { "http": "GET /api/status" }, "expect": { "reply": { "headers": { "access-control-allow-origin": "<absent>" } } } },
{ "send": { "http": "GET /api/patterns" }, "expect": { "reply": { "headers": { "access-control-allow-origin": "<absent>" } } } },
{ "send": { "http": "GET /api/knobs" }, "expect": { "reply": { "headers": { "access-control-allow-origin": "<absent>" } } } },
{ "send": { "http": "POST /api/params?p1=500" }, "expect": { "reply": { "headers": { "access-control-allow-origin": "<absent>" } } } },
{ "send": { "http": "GET /api/patterns/file?slug=wave_saw&ext=json" }, "expect": { "reply": { "headers": { "access-control-allow-origin": "<absent>" } } } },
{ "send": { "http": "GET /api/nothing" }, "expect": { "reply": { "headers": { "access-control-allow-origin": "<absent>" } } } }
]
}
9 changes: 9 additions & 0 deletions docs/rest-api-vectors/device-crash.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"contract": "docs/rest-api.md#delete-apicrash",
"says": "`{\"ok\":true}`, or `404` with `{\"ok\":false,\"error\":\"no crash recorded\"}` when there was nothing to clear.",
"features": [],
"steps": [
{ "send": { "http": "GET /api/status" }, "expect": { "reply": { "body": { "crash": "<absent>" } } } },
{ "send": { "http": "DELETE /api/crash" }, "expect": { "reply": { "status": 404, "body": { "ok": false, "error": "no crash recorded" } } } }
]
}
9 changes: 9 additions & 0 deletions docs/rest-api-vectors/device-display-brightness.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"contract": "docs/rest-api.md#get-apistatus",
"says": "K1's long-press session and `GET /api/display?brightness=` move the same value; what the power clamp lets through is `power_applied` on `/api/display`.",
"features": [],
"steps": [
{ "send": { "http": "GET /api/display?brightness=77" }, "expect": { "reply": { "body": { "brightness": 77 } }, "status": { "brightness": 77 } } },
{ "send": { "http": "GET /api/display?brightness=255" }, "expect": { "reply": { "body": { "brightness": 255 } }, "status": { "brightness": 255 } } }
]
}
9 changes: 9 additions & 0 deletions docs/rest-api-vectors/device-display-read-only.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"contract": "docs/rest-api.md#get-apidisplay",
"says": "Read-only in the response: `calib`, `sleep`, `power_ma`, `power_demand`, `power_limiting`, `power_applied`.",
"features": [],
"steps": [
{ "send": { "http": "GET /api/display?sleep=1" }, "expect": { "reply": { "status": 200, "body": { "sleep": "<any>" } } } },
{ "send": { "wait": 20 }, "expect": { "status": { "sleep": false } } }
]
}
15 changes: 15 additions & 0 deletions docs/rest-api-vectors/device-display-state.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"contract": "docs/rest-api.md#get-apidisplay",
"says": "Any subset of these may be set in the query string; the response always reports the full state afterwards.",
"features": [],
"steps": [
{
"send": { "http": "GET /api/display?sat=2&gamma_g=2.5" },
"expect": { "reply": { "status": 200, "body": {
"wb_r": "<number>", "wb_g": "<number>", "wb_b": "<number>", "gamma_r": "<number>", "gamma_g": 2.5, "gamma_b": "<number>", "sat": 2,
"brightness": "<integer>", "screen": "<integer>", "level": "<integer>", "power_budget": "<integer>", "power_limit": "<integer>",
"calib": "<any>", "sleep": "<any>", "power_ma": "<any>", "power_demand": "<any>", "power_limiting": "<any>", "power_applied": "<any>" } } }
},
{ "send": { "http": "GET /api/display?brightness=128" }, "expect": { "reply": { "body": { "gamma_g": 2.5, "sat": 2, "brightness": 128 } } } }
]
}
9 changes: 9 additions & 0 deletions docs/rest-api-vectors/device-favicon.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"contract": "docs/rest-api.md#transport",
"says": "`GET /favicon.ico` answers `204`, with `Cache-Control: public, max-age=31536000, immutable` on a panel `Host` and `no-store` on any other (see Host gating); pages carry their icon inline.",
"features": [],
"steps": [
{ "send": { "http": { "request": "GET /favicon.ico", "headers": { "Host": "patternflow.local" } } }, "expect": { "reply": { "status": 204, "headers": { "cache-control": "public, max-age=31536000, immutable" } } } },
{ "send": { "http": { "request": "GET /favicon.ico", "headers": { "Host": "patternflow.fritz.box" } } }, "expect": { "reply": { "status": 204, "headers": { "cache-control": "no-store" } } } }
]
}
15 changes: 15 additions & 0 deletions docs/rest-api-vectors/device-host.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"contract": "docs/rest-api.md#transport",
"says": "Pages, the chrome and the favicon are cacheable only when the request's `Host` can only be the panel: an IPv4 address, a name with no dot (`patternflow`) or a `.local` name, with or without a port.",
"features": [],
"steps": [
{ "send": { "http": { "request": "GET /favicon.ico", "headers": { "Host": "192.168.1.42" } } }, "expect": { "reply": { "headers": { "cache-control": "public, max-age=31536000, immutable" } } } },
{ "send": { "http": { "request": "GET /favicon.ico", "headers": { "Host": "192.168.1.42:80" } } }, "expect": { "reply": { "headers": { "cache-control": "public, max-age=31536000, immutable" } } } },
{ "send": { "http": { "request": "GET /favicon.ico", "headers": { "Host": "patternflow" } } }, "expect": { "reply": { "headers": { "cache-control": "public, max-age=31536000, immutable" } } } },
{ "send": { "http": { "request": "GET /favicon.ico", "headers": { "Host": "patternflow:8080" } } }, "expect": { "reply": { "headers": { "cache-control": "public, max-age=31536000, immutable" } } } },
{ "send": { "http": { "request": "GET /favicon.ico", "headers": { "Host": "patternflow-a1b2.local" } } }, "expect": { "reply": { "headers": { "cache-control": "public, max-age=31536000, immutable" } } } },
{ "send": { "http": { "request": "GET /favicon.ico", "headers": { "Host": "patternflow.local:80" } } }, "expect": { "reply": { "headers": { "cache-control": "public, max-age=31536000, immutable" } } } },
{ "send": { "http": { "request": "GET /favicon.ico", "headers": { "Host": "patternflow.fritz.box" } } }, "expect": { "reply": { "headers": { "cache-control": "no-store" } } } },
{ "send": { "http": { "request": "GET /favicon.ico", "headers": { "Host": "patternflow.work:80" } } }, "expect": { "reply": { "headers": { "cache-control": "no-store" } } } }
]
}
16 changes: 16 additions & 0 deletions docs/rest-api-vectors/device-knobs-get.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"contract": "docs/rest-api.md#get-apiknobs-and-post-apiknobs",
"says": "`GET` returns `inv` (four booleans: reversed or not), `sub` (edges per click per knob: 4, 2 or 1 — most encoders are 4), `clicks` (each knob's click count as the frame sees it) and `raw` (the edge counters underneath, for telling a 2-edge part from a 4-edge one).",
"features": [],
"steps": [
{ "send": { "hand": { "knob": 1, "turn": 2 } } },
{ "send": { "hand": { "knob": 3, "turn": -1 } } },
{ "send": { "wait": 20 }, "expect": { "status": { "knobs": [2, 0, -1, 0] } } },
{
"send": { "http": "GET /api/knobs" },
"expect": { "reply": { "status": 200, "body": {
"inv": ["<boolean>", "<boolean>", "<boolean>", "<boolean>"], "sub": ["<integer>", "<integer>", "<integer>", "<integer>"],
"clicks": [2, 0, -1, 0], "raw": ["<integer>", "<integer>", "<integer>", "<integer>"] } } }
}
]
}
15 changes: 15 additions & 0 deletions docs/rest-api-vectors/device-knobs-post.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"contract": "docs/rest-api.md#get-apiknobs-and-post-apiknobs",
"says": "`POST` takes any subset of `inv0`..`inv3` (`0`/`1`) and `sub0`..`sub3` (`4`/`2`/`1`), or `inv=` / `sub=` for all four, form-encoded; a change is applied at a frame boundary without moving the knob, stored on the device (NVS `pf_knobs`) and answered as `GET`.",
"features": [],
"steps": [
{ "send": { "hand": { "knob": 2, "turn": 3 } } },
{ "send": { "wait": 20 }, "expect": { "status": { "knobs": [0, 3, 0, 0] } } },
{ "send": { "http": { "request": "POST /api/knobs", "form": "inv=0&sub=4" } }, "expect": { "reply": { "body": { "inv": [false, false, false, false], "sub": [4, 4, 4, 4], "clicks": [0, 3, 0, 0], "raw": "<array>" } } } },
{ "send": { "http": { "request": "POST /api/knobs", "form": "inv1=1&sub2=2" } }, "expect": { "reply": { "body": { "inv": [false, true, false, false], "sub": [4, 4, 2, 4], "clicks": [0, 3, 0, 0], "raw": "<array>" } } } },
{ "send": { "wait": 20 }, "expect": { "status": { "knobs": [0, 3, 0, 0] } } },
{ "send": { "hand": { "knob": 2, "turn": 1 } } },
{ "send": { "hand": { "knob": 3, "turn": 1 } } },
{ "send": { "wait": 20 }, "expect": { "status": { "knobs": [0, 2, 2, 0] } } }
]
}
19 changes: 19 additions & 0 deletions docs/rest-api-vectors/device-no-store.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"contract": "docs/rest-api.md#transport",
"says": "Every JSON endpoint sends `Cache-Control: no-store`, and so does the plain-text `404`.",
"features": [],
"steps": [
{ "send": { "http": "GET /api/status" }, "expect": { "reply": { "headers": { "cache-control": "no-store" } } } },
{ "send": { "http": "GET /api/display" }, "expect": { "reply": { "headers": { "cache-control": "no-store" } } } },
{ "send": { "http": "GET /api/knobs" }, "expect": { "reply": { "headers": { "cache-control": "no-store" } } } },
{ "send": { "http": { "request": "POST /api/knobs", "form": "inv=0" } }, "expect": { "reply": { "headers": { "cache-control": "no-store" } } } },
{ "send": { "http": "POST /api/params?p1=500" }, "expect": { "reply": { "headers": { "cache-control": "no-store" } } } },
{ "send": { "http": "POST /api/sleep?on=0" }, "expect": { "reply": { "headers": { "cache-control": "no-store" } } } },
{ "send": { "http": "DELETE /api/crash" }, "expect": { "reply": { "status": 404, "headers": { "cache-control": "no-store" } } } },
{ "send": { "http": "GET /api/patterns" }, "expect": { "reply": { "headers": { "cache-control": "no-store" } } } },
{ "send": { "http": "GET /api/patterns/select?index=0" }, "expect": { "reply": { "headers": { "cache-control": "no-store" } } } },
{ "send": { "http": "GET /api/patterns/select?index=-1" }, "expect": { "reply": { "status": 404, "headers": { "cache-control": "no-store" } } } },
{ "send": { "http": "GET /api/patterns/file?slug=wave_saw&ext=json" }, "expect": { "reply": { "headers": { "cache-control": "no-store" } } } },
{ "send": { "http": "GET /api/nothing" }, "expect": { "reply": { "status": 404, "headers": { "content-type": "text/plain", "cache-control": "no-store" } } } }
]
}
11 changes: 11 additions & 0 deletions docs/rest-api-vectors/device-removed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"contract": "docs/rest-api.md#knobs-and-parameters",
"says": "`/api/knob` and `/remote` existed and were removed as unused alongside the `/api/frame` incident; do not look for them.",
"features": [],
"steps": [
{ "send": { "http": "GET /api/knob" }, "expect": { "reply": { "status": 404 } } },
{ "send": { "http": "POST /api/knob?knob=1&delta=1" }, "expect": { "reply": { "status": 404 } } },
{ "send": { "http": "GET /remote" }, "expect": { "reply": { "status": 404 } } },
{ "send": { "http": "GET /api/frame" }, "expect": { "reply": { "status": 404 } } }
]
}
10 changes: 10 additions & 0 deletions docs/rest-api-vectors/patterns-file-404.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"contract": "docs/rest-api.md#get-apipatternsfile",
"says": "`404` if the file is not there, `409` if storage is not mounted.",
"features": [],
"steps": [
{ "send": { "http": "GET /api/patterns/file?slug=no_such_module" }, "expect": { "reply": { "status": 404 } } },
{ "send": { "http": "GET /api/patterns/file?slug=no_such_module&ext=json" }, "expect": { "reply": { "status": 404 } } },
{ "send": { "http": "GET /api/patterns/file?slug=no_such_module&ext=thumb" }, "expect": { "reply": { "status": 404 } } }
]
}
9 changes: 9 additions & 0 deletions docs/rest-api-vectors/patterns-file-download.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"contract": "docs/rest-api.md#get-apipatternsfile",
"says": "This is a file download (`Content-Disposition: attachment`), one file at a time — the server is single-connection.",
"features": [],
"steps": [
{ "send": { "http": "GET /api/patterns/file?slug=wave_saw" }, "expect": { "reply": { "status": 200, "headers": { "content-disposition": "<string>" } } } },
{ "send": { "http": "GET /api/patterns/file?slug=wave_saw&ext=json" }, "expect": { "reply": { "status": 200, "headers": { "content-disposition": "<string>" } } } }
]
}
6 changes: 6 additions & 0 deletions docs/rest-api-vectors/patterns-file-preset.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"contract": "docs/rest-api.md#get-apipatternsfile",
"says": "Presets have no sidecar.",
"features": [],
"steps": [{ "send": { "http": "GET /api/patterns/file?slug=origin&ext=json" }, "expect": { "reply": { "status": 404 } } }]
}
9 changes: 9 additions & 0 deletions docs/rest-api-vectors/patterns-file-sidecar.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"contract": "docs/rest-api.md#get-apipatternsfile",
"says": "`ext=json` returns the module's **sidecar** — the only place per-pattern metadata is exposed over HTTP:",
"features": [],
"steps": [
{ "send": { "http": "GET /api/patterns/file?slug=wave_saw&ext=json" }, "expect": { "reply": { "status": 200, "body": {
"name": "<string>", "abi": "<integer>", "knobs": ["<string>", "<string>", "<string>", "<string>"], "slug": "wave_saw" } } } }
]
}
15 changes: 15 additions & 0 deletions docs/rest-api-vectors/patterns-list.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"contract": "docs/rest-api.md#get-apipatterns",
"says": "`active` is an index into `patterns`, or `-1` while nothing is loaded.",
"features": [],
"steps": [
{
"send": { "http": "GET /api/patterns" },
"expect": { "reply": { "status": 200, "body": {
"active": "<integer>", "presets": 1, "mounted": true, "free": "<integer>", "patterns": "<array>", "pendingRev": "<integer>", "pending": "<array>" } } }
},
{ "send": { "http": "GET /api/patterns/select?index=2" } },
{ "send": { "wait": 20 } },
{ "send": { "http": "GET /api/patterns" }, "expect": { "reply": { "body": { "active": 2 } } } }
]
}
13 changes: 13 additions & 0 deletions docs/rest-api-vectors/patterns-select-404.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"contract": "docs/rest-api.md#get-apipatternsselect",
"says": "`404` for an out-of-range index or an unknown name.",
"features": [],
"steps": [
{ "send": { "http": "GET /api/patterns/select?index=0" } },
{ "send": { "wait": 20 } },
{ "send": { "http": "GET /api/patterns/select?index=-1" }, "expect": { "reply": { "status": 404 } } },
{ "send": { "http": "GET /api/patterns/select?index=9999" }, "expect": { "reply": { "status": 404 } } },
{ "send": { "http": "GET /api/patterns/select?name=No%20such%20pattern" }, "expect": { "reply": { "status": 404 } } },
{ "send": { "wait": 20 }, "expect": { "status": { "active": "Origin" } } }
]
}
12 changes: 12 additions & 0 deletions docs/rest-api-vectors/patterns-select-name.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"contract": "docs/rest-api.md#get-apipatternsselect",
"says": "Display name, exact match. Only if you have no index.",
"features": [],
"steps": [
{ "send": { "http": "GET /api/patterns/select?index=1" } },
{ "send": { "wait": 20 }, "expect": { "status": { "activeIsModule": true } } },
{ "send": { "http": "GET /api/patterns/select?name=origin" }, "expect": { "reply": { "status": 404 } } },
{ "send": { "http": "GET /api/patterns/select?name=Origin" }, "expect": { "reply": { "status": 200, "body": { "ok": true, "index": 0, "name": "Origin" } } } },
{ "send": { "wait": 20 }, "expect": { "status": { "active": "Origin" } } }
]
}
16 changes: 16 additions & 0 deletions docs/rest-api-vectors/patterns-select-step.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"contract": "docs/rest-api.md#get-apipatternsselect",
"says": "`+1` or `-1`: the next or previous pattern that is not hidden, wrapping, from wherever the panel is now.",
"features": [],
"steps": [
{ "send": { "http": "GET /api/patterns/select?index=0" } },
{ "send": { "wait": 20 } },
{ "send": { "http": "GET /api/patterns/select?step=%2B1" }, "expect": { "reply": { "status": 200, "body": { "index": 1 } } } },
{ "send": { "wait": 20 } },
{ "send": { "http": "GET /api/patterns/select?step=-1" }, "expect": { "reply": { "status": 200, "body": { "index": 0 } } } },
{ "send": { "wait": 20 } },
{ "send": { "http": "GET /api/patterns/select?step=-1" }, "expect": { "reply": { "status": 200, "body": { "index": "<integer>" } } } },
{ "send": { "wait": 20 } },
{ "send": { "http": "GET /api/patterns/select?step=%2B1" }, "expect": { "reply": { "status": 200, "body": { "index": 0 } } } }
]
}
13 changes: 13 additions & 0 deletions docs/rest-api-vectors/patterns-select.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"contract": "docs/rest-api.md#get-apipatternsselect",
"says": "Queued and applied in `loop()`, for the same reason as sleep: activating a module reads FATFS and runs the relocator.",
"features": [],
"steps": [
{ "send": { "http": "GET /api/patterns/select?index=0" } },
{ "send": { "wait": 20 }, "expect": { "status": { "active": "Origin", "activeIsModule": false } } },
{ "send": { "http": "GET /api/patterns/select?index=1" }, "expect": { "reply": { "status": 200, "body": { "ok": true, "index": 1, "name": "<string>" } } } },
{ "send": { "http": "GET /api/patterns" }, "expect": { "reply": { "body": { "active": 0 } } } },
{ "send": { "wait": 20 }, "expect": { "status": { "activeIsModule": true } } },
{ "send": { "http": "GET /api/patterns" }, "expect": { "reply": { "body": { "active": 1 } } } }
]
}
Loading
Loading