Skip to content
Talk to our solutions team

Scripting

Both runners embed the Testing multi-language script engine for logic that doesn’t fit the declarative schema: custom validation, computed test data, side-band checks against files or databases.

steps:
- name: validate-order-totals
script:
language: js # default
execute: |
const r = response; // parsed body of the previous HTTP step
const bad = r.items.filter(i => i.price <= 0);
if (bad.length > 0) {
test.fail("items with non-positive price: " + bad.length, 400);
}
test.set_variable("item_count", r.items.length);
test.success("totals ok", 200);
timeout: 10s # default 5s
params: # extra variables merged over the scope
max_items: 50

language: accepts js / javascript (goja, default), js:v8 / javascript-v8 / v8, lua, starlark, cel / celgo, expr. (wasm appears in a comment but is rejected.) function: selects an entry point for callable-contract languages (Starlark); it’s ignored for JS.

execute: (inline source) and file: (path relative to the test YAML’s directory) are mutually exclusive.

Scripts see the entire test scope as top-level variables, plus params. When a previous HTTP step exists in the test, they also get:

VariableContents
status_codeHTTP status of the last HTTP step
response_bodyraw body string
responseJSON-parsed body
headersresponse headers, lower-cased keys
duration_mslast step duration
FunctionDescription
test.success(msg, code?)mark the step passed (code defaults 200)
test.fail(msg, code?)mark the step failed (code defaults 400)
test.skip(reason)record a skip (reported as 200 + message)
test.set_variable(k, v)write to the test scope — visible to later steps/assertions
test.get_variable(k)read from scope
test.log(args...)write to the test log
test.run_id() / test.test_name() / test.step_name() / test.suite_name() / test.suite_path() / test.source_file()identity/context accessors
test.now_unix_ms() / test.now_iso8601()clocks
test.uuid() / test.ulid()id generators
test.random_int(lo, hi) / test.random_string(n)randomness
test.env(name)read an OS environment variable
test.fixture(relpath)read a file relative to the test’s directory
test.sleep(ms)sleep

Flat aliases exist for legacy scripts: success, fail, set_variable, get_variable, logwriter, log, print, println, uuid, ulid, sleep.

The eval.* namespace is also registered — comparison predicates and model-free metrics from the Testing eval package (eval.equals, eval.json_path_equals, eval.f1, …), plus fail-fast eval.must.* forms.

  • test.fail(...) or a non-2xx code passed to success/fail → step failed.
  • A runtime error in the script → step errored.
  • test.skip(...) → recorded as 200 with the skip message.
  • Script finishes without calling anything → 200, passed.
  • Timeout (default 5s) fails the step with [script: timed out after X] in the log.

Assertions on a script step evaluate against the scope + duration_ms only — do validation inside the script, or set variables and assert on them.

steps:
- name: js-hello
plugin:
payload:
language: js # js | js:v8 | cel | celgo
execute: |
logwriter("hello from the plugin engine");
success("ok", 200);
FunctionDescription
success(message, statuscode)return success
fail(message, statuscode)return failure
logwriter(args...)write to the test log
getdbconnection(url)open a PostgreSQL connection (driver is hardcoded), returns a connection ID
dbquery(id, query)run a query → {result: [...rows]}
dbexecute(id, query)run a statement
closedbconnection(id)close the connection
hash.string(data, algo)hash a string — md5, sha1, sha256, sha384, sha512, sha3_256, sha3_384, sha3_512
hash.file(path, algo)hash file contents
fs.read(path) / fs.glob(pattern)read a file / read all files matching a glob (via the Testing filesystem utility)

DB example:

- name: verify-in-db
plugin:
payload:
language: js
execute: |
const id = getdbconnection("postgres://user:pass@localhost:5432/db?sslmode=disable");
const out = dbquery(id, "SELECT count(*) AS n FROM users");
closedbconnection(id);
if (out.result[0].n > 0) { success("rows present", 200); }
else { fail("no rows", 500); }

Separate from plugin steps, rest: steps in the legacy format run response.validate expressions in the language named by the step’s language: key — js, jq, or celgo — see the legacy tests mode response-validation reference.

Scripts run with real capabilities: filesystem reads, OS env access, and (legacy) database connections. Test definitions are code — review them like code, and don’t run untrusted collections.