scaf uses expr-lang for assertion expressions. This page documents the available operators and functions.
Expressions appear in assert blocks:
assert (single_expression)
For single conditions, use the shorthand form. For multiple conditions, wrap each in parentheses. All must evaluate to true for the test to pass.
| Operator | Description | Example |
|---|
== | Equal | u.age == 30 |
!= | Not equal | u.status != "banned" |
< | Less than | u.age < 18 |
> | Greater than | u.age > 21 |
<= | Less than or equal | u.score <= 100 |
>= | Greater than or equal | u.age >= 18 |
| Operator | Description | Example |
|---|
&& | Logical AND | u.verified && u.active |
|| | Logical OR | u.admin || u.moderator |
! | Logical NOT | !u.deleted |
(u.verified && u.age >= 18)
(u.role == "admin" || u.role == "moderator")
Logical operators short-circuit:
assert (u.profile == nil || u.profile.bio != "")
| Operator | Description | Example |
|---|
+ | Addition | a + b |
- | Subtraction | a - b |
* | Multiplication | a * b |
/ | Division | a / b |
% | Modulo | a % b |
^ | Exponent | a ^ b |
(total == price * quantity)
(remainder == value % 10)
(percentage == (count / total) * 100)
| Operator | Description | Example |
|---|
+ | Concatenation | first + " " + last |
contains | Contains substring | email contains "@" |
startsWith | Starts with prefix | name startsWith "Dr." |
endsWith | Ends with suffix | file endsWith ".scaf" |
matches | Regex match | phone matches "^\\d{10}$" |
(u.name startsWith u.firstName)
(u.domain endsWith ".com")
| Operator | Description | Example |
|---|
in | Value in collection | "admin" in roles |
(u.status in ["active", "pending"])
| Operator | Description | Example |
|---|
.. | Range (inclusive) | 1..10 |
condition ? valueIfTrue : valueIfFalse
((u.age >= 18 ? u.category : "minor") == "adult")
(u.verified ? u.status == "active" : u.status == "pending")
Safe access that returns nil if the path doesn’t exist:
(u.settings?.notifications == true)
(data["config"]["timeout"] > 0)
(items[len(items) - 1] == "last")
| Function | Description | Example |
|---|
len(s) | String length | len(name) > 0 |
upper(s) | Uppercase | upper(code) == "ABC" |
lower(s) | Lowercase | lower(email) |
trim(s) | Trim whitespace | trim(input) != "" |
split(s, sep) | Split string | split(tags, ",") |
replace(s, old, new) | Replace | replace(phone, "-", "") |
(lower(u.email) == u.email)
| Function | Description | Example |
|---|
len(arr) | Array length | len(items) > 0 |
all(arr, predicate) | All match | all(scores, # >= 0) |
any(arr, predicate) | Any matches | any(roles, # == "admin") |
one(arr, predicate) | Exactly one | one(items, #.primary) |
none(arr, predicate) | None match | none(users, #.deleted) |
filter(arr, predicate) | Filter | filter(items, #.active) |
map(arr, expression) | Transform | map(users, #.name) |
count(arr, predicate) | Count matches | count(items, #.sold) |
first(arr) | First element | first(items) |
last(arr) | Last element | last(items) |
sort(arr) | Sort ascending | sort(scores) |
reverse(arr) | Reverse order | reverse(items) |
(any(u.roles, # == "admin"))
(all(scores, # >= 0 && # <= 100))
(count(items, #.inStock) >= 5)
| Function | Description | Example |
|---|
abs(n) | Absolute value | abs(diff) < 0.01 |
min(a, b) | Minimum | min(x, y) |
max(a, b) | Maximum | max(x, y) |
floor(n) | Round down | floor(3.7) == 3 |
ceil(n) | Round up | ceil(3.2) == 4 |
round(n) | Round | round(3.5) == 4 |
(abs(actual - expected) < 0.001)
| Function | Description | Example |
|---|
type(v) | Get type name | type(value) == "string" |
int(v) | Convert to int | int("42") == 42 |
float(v) | Convert to float | float("3.14") |
string(v) | Convert to string | string(42) == "42" |
(type(u.name) == "string")
| Function | Description | Example |
|---|
now() | Current time | u.createdAt < now() |
date(s) | Parse date | date(u.birthday) |
duration(s) | Parse duration | duration("24h") |
(date(u.expiresAt) > now())
((u.nickname ?? "Anonymous") != "")
(u.email contains "@" && u.email contains ".")
(len(u.email) >= 5 && len(u.email) <= 255)
(u.password matches "[A-Z]")
(u.password matches "[0-9]")
(u.age >= 13 && u.age < 150)
(all(order.items, #.quantity > 0 && #.price >= 0))
(any(team.members, #.role == "admin"))
(none(products, #.deleted))
(count(users, #.verified) >= 1)
(u.admin ? u.permissions contains "delete" : true)
(u.verified ? len(u.email) > 0 : true)
(subscription.tier == "premium" ?
len(subscription.features) >= 10 :
len(subscription.features) <= 3)