Skip to Content
ReferenceBuilt-in Functions

Built-in Functions

Probe evaluates expressions with expr-lang/expr  (v1.17). Every function on this page is either a built-in of that language or a function Probe registers on top of it.

Overview

Expressions appear in two shapes:

  • Template expressions - a {{ ... }} placeholder inside a string, replaced by the evaluated value.
  • Boolean expressions - a bare expression, evaluated as a condition.

Where Expressions Can Be Used

LevelFieldTemplateBooleanNotes
workflowvarsYes-Global variables. Environment variables are referenced by bare name here
jobnameYes-Job name
jobskipif-YesSkip condition for the job
stepnameYes-Step name
stepwithYes-Action arguments
steptest-YesAssertion
stepechoYes-Report output
stepvarsYes-Step variables
stepoutputsYes-Values shared with later steps and jobs
stepskipif-YesSkip condition for the step

Syntax

Functions are called directly, or chained with the pipe operator. The pipe passes the left-hand value as the first argument.

vars: service: "{{SERVICE_NAME | upper}}" slug: "{{replace(lower(SERVICE_NAME), ' ', '-')}}" path: "{{BASE_URL | trimSuffix('/')}}/api"

Arithmetic and comparison use operators, not functions: +, -, *, /, %, **, ==, !=, <, >, &&, ||, !, ?? (nil coalescing), in, contains, startsWith, endsWith, matches (regular expression).

test: | res.code == 200 && res.body.status contains "ok" && rt.sec < 1

Probe Functions

These functions are registered by Probe itself.

match_json

Compares two objects strictly. Every key and value must match on both sides - extra or missing keys make it fail.

Syntax: match_json(src, target) Returns: Boolean

test: match_json(res.body, {"status": "ok", "count": 3})

diff_json

Compares two objects the same way as match_json and describes the differences. Returns the string No diff when they match.

Syntax: diff_json(src, target) Returns: String

echo: "{{diff_json(res.body, vars.expected)}}"

parse_json

Parses a JSON string into an object.

Syntax: parse_json(string) Returns: Any

outputs: meta: "{{parse_json(res.body.metadata)}}" test: parse_json(res.body.metadata).version == "1.0"

parse_int

Converts a string or a number to a 64-bit integer. Fails on a string that is not a base-10 integer.

Syntax: parse_int(value) Returns: Integer

test: parse_int(res.headers["Content-Length"]) > 0

parse_float

Converts a string to a floating-point number.

Syntax: parse_float(string) Returns: Float

test: parse_float(res.body.score) >= 8.5

encode_base64

Encodes a string with standard base64.

Syntax: encode_base64(string) Returns: String

with: headers: Authorization: "Basic {{encode_base64(vars.user + ':' + vars.pass)}}"

decode_base64

Decodes a standard base64 string.

Syntax: decode_base64(string) Returns: String

outputs: payload: "{{decode_base64(res.body.data)}}"

unixtime

Returns the current time as a Unix timestamp in seconds.

Syntax: unixtime() Returns: Integer

with: headers: X-Timestamp: "{{unixtime()}}"

random_int

Returns a random integer in the range [0, n). n must be positive.

Syntax: random_int(n) Returns: Integer

with: url: "{{vars.base_url}}/test?seed={{random_int(1000)}}"

random_str

Returns a random alphanumeric string of the given length ([a-zA-Z0-9], at most 1000000 characters).

Syntax: random_str(length) Returns: String

vars: email: "user-{{random_str(8)}}@example.com"

String Functions

FunctionDescription
upper(s)Uppercase
lower(s)Lowercase
trim(s) / trim(s, chars)Strip whitespace, or the given characters, from both ends
trimPrefix(s, prefix)Strip a leading prefix
trimSuffix(s, suffix)Strip a trailing suffix
replace(s, old, new)Replace every occurrence
split(s, sep) / split(s, sep, n)Split into an array
splitAfter(s, sep)Split, keeping the separator on each element
join(array, sep)Join an array into a string
repeat(s, n)Repeat a string
indexOf(s, sub)Index of the first occurrence, or -1
lastIndexOf(s, sub)Index of the last occurrence, or -1
hasPrefix(s, prefix)Prefix test
hasSuffix(s, suffix)Suffix test
len(s)Length in bytes
vars: host: "{{trimPrefix(API_URL, 'https://')}}" slug: "{{replace(lower(TITLE), ' ', '-')}}" test: hasSuffix(res.body.filename, ".json")

contains, startsWith, endsWith and matches are operators rather than functions:

test: | res.headers["Content-Type"] contains "application/json" && res.body.id matches "^[0-9a-f]{8}"

Number Functions

FunctionDescription
abs(n)Absolute value
ceil(n)Round up
floor(n)Round down
round(n)Round to the nearest integer
max(a, b, ...)Largest value
min(a, b, ...)Smallest value
sum(array)Sum of an array
mean(array)Arithmetic mean
median(array)Median
bitnot(n)Bitwise NOT

There is no add / sub / mul / div / mod function - use the operators instead.

outputs: seconds: "{{round(rt.sec)}}" test: res.body.id % 2 == 0

Type Conversion Functions

FunctionDescription
int(v)Convert to an integer
float(v)Convert to a float
string(v)Convert to a string
type(v)Name of the value’s type, such as string or int

Probe’s parse_int and parse_float differ from int and float in that they always parse base-10 text and return a 64-bit value.

Date and Time Functions

FunctionDescription
now()Current time as a time value
date(s) / date(s, layout) / date(s, layout, tz)Parse a string into a time value
duration(s)Parse a duration such as "1h30m"
timezone(name)Look up a location such as "UTC" or "Asia/Tokyo"

now() returns a time value, not a number. Format it with Go layouts through Format, or take a Unix timestamp with Unix() - or use Probe’s unixtime().

outputs: today: "{{now().Format('2006-01-02')}}" started_at: "{{now().Format('2006-01-02T15:04:05Z07:00')}}" epoch: "{{unixtime()}}" test: date(res.body.expires_at) > now()

Array and Map Functions

FunctionDescription
len(v)Number of elements
first(array) / last(array)First or last element
take(array, n)First n elements
reverse(array)Reversed copy
uniq(array)Duplicates removed
concat(a, b, ...)Concatenate arrays
flatten(array)Flatten nested arrays
sort(array) / sort(array, "desc")Sort
sortBy(array, expr)Sort by a computed key
groupBy(array, expr)Group into a map by a computed key
filter(array, predicate)Elements matching a predicate
map(array, expr)Transform each element
find(array, predicate) / findLast(array, predicate)First or last matching element
findIndex(array, predicate) / findLastIndex(array, predicate)Index of a matching element
count(array, predicate)Number of matching elements
all / any / one / noneQuantifiers over a predicate
reduce(array, expr, initial)Fold an array into a single value
keys(map) / values(map)Keys or values of a map
get(map, key)Value for a key, or nil
toPairs(map) / fromPairs(array)Convert between a map and key/value pairs

Inside a predicate, # is the current element and #acc is the accumulator of reduce.

test: | len(res.body.items) > 0 && all(res.body.items, #.status == "active") outputs: names: "{{join(map(res.body.users, #.name), ', ')}}" total: "{{reduce(res.body.items, #acc + #.price, 0)}}"

JSON and Encoding Functions

FunctionDescription
toJSON(v)Serialize a value as indented JSON
fromJSON(s)Parse a JSON string
toBase64(s)Encode with base64
fromBase64(s)Decode base64

Note the camel case: there is no tojson or base64. Probe’s parse_json, encode_base64 and decode_base64 cover the same ground with stricter argument checking and Probe’s own error messages.

with: body: "{{toJSON(outputs.setup.user)}}" outputs: version: "{{fromJSON(res.body).version}}"

Limits

Expression evaluation is bounded for safety:

  • An expression may be at most 1000000 characters long.
  • Evaluation times out after 5 seconds.
  • parse_json, encode_base64 and decode_base64 reject an argument longer than 1000000 characters.

See Also

Updated at