Skip to content

Expression Language

scaf uses expr-lang for assertion expressions. This page documents the available operators and functions.

Expressions appear in assert blocks:

assert (single_expression)
assert {
(expression1)
(expression2)
}

For single conditions, use the shorthand form. For multiple conditions, wrap each in parentheses. All must evaluate to true for the test to pass.

OperatorDescriptionExample
==Equalu.age == 30
!=Not equalu.status != "banned"
<Less thanu.age < 18
>Greater thanu.age > 21
<=Less than or equalu.score <= 100
>=Greater than or equalu.age >= 18
assert {
(u.age >= 18)
(u.age < 120)
(u.score >= 0)
(u.status != "deleted")
}
OperatorDescriptionExample
&&Logical ANDu.verified && u.active
||Logical ORu.admin || u.moderator
!Logical NOT!u.deleted
assert {
(u.verified && u.age >= 18)
(u.role == "admin" || u.role == "moderator")
(!u.deleted)
}

Logical operators short-circuit:

assert (u.profile == nil || u.profile.bio != "")
OperatorDescriptionExample
+Additiona + b
-Subtractiona - b
*Multiplicationa * b
/Divisiona / b
%Moduloa % b
^Exponenta ^ b
assert {
(total == price * quantity)
(remainder == value % 10)
(percentage == (count / total) * 100)
}
OperatorDescriptionExample
+Concatenationfirst + " " + last
containsContains substringemail contains "@"
startsWithStarts with prefixname startsWith "Dr."
endsWithEnds with suffixfile endsWith ".scaf"
matchesRegex matchphone matches "^\\d{10}$"
assert {
(u.email contains "@")
(u.email contains ".")
(u.name startsWith u.firstName)
(u.domain endsWith ".com")
}
OperatorDescriptionExample
inValue in collection"admin" in roles
assert {
("admin" in u.roles)
(u.status in ["active", "pending"])
(userId in allowedIds)
}
OperatorDescriptionExample
..Range (inclusive)1..10
assert {
(u.age in 18..65)
(count in 1..100)
}
condition ? valueIfTrue : valueIfFalse
assert {
((u.age >= 18 ? u.category : "minor") == "adult")
(u.verified ? u.status == "active" : u.status == "pending")
}
u.name
u.profile.bio
u.settings.theme.color

Safe access that returns nil if the path doesn’t exist:

u.profile?.bio
u.settings?.theme?.color
assert {
(u.profile?.bio != "")
(u.settings?.notifications == true)
}
items[0]
matrix[0][1]
data["key"]
assert {
(u.roles[0] == "admin")
(data["config"]["timeout"] > 0)
(items[len(items) - 1] == "last")
}
FunctionDescriptionExample
len(s)String lengthlen(name) > 0
upper(s)Uppercaseupper(code) == "ABC"
lower(s)Lowercaselower(email)
trim(s)Trim whitespacetrim(input) != ""
split(s, sep)Split stringsplit(tags, ",")
replace(s, old, new)Replacereplace(phone, "-", "")
assert {
(len(u.name) > 0)
(len(u.name) <= 100)
(lower(u.email) == u.email)
(trim(u.input) != "")
}
FunctionDescriptionExample
len(arr)Array lengthlen(items) > 0
all(arr, predicate)All matchall(scores, # >= 0)
any(arr, predicate)Any matchesany(roles, # == "admin")
one(arr, predicate)Exactly oneone(items, #.primary)
none(arr, predicate)None matchnone(users, #.deleted)
filter(arr, predicate)Filterfilter(items, #.active)
map(arr, expression)Transformmap(users, #.name)
count(arr, predicate)Count matchescount(items, #.sold)
first(arr)First elementfirst(items)
last(arr)Last elementlast(items)
sort(arr)Sort ascendingsort(scores)
reverse(arr)Reverse orderreverse(items)
assert {
(len(u.roles) > 0)
(any(u.roles, # == "admin"))
(all(scores, # >= 0 && # <= 100))
(none(users, #.deleted))
(count(items, #.inStock) >= 5)
}
FunctionDescriptionExample
abs(n)Absolute valueabs(diff) < 0.01
min(a, b)Minimummin(x, y)
max(a, b)Maximummax(x, y)
floor(n)Round downfloor(3.7) == 3
ceil(n)Round upceil(3.2) == 4
round(n)Roundround(3.5) == 4
assert {
(abs(actual - expected) < 0.001)
(max(scores) <= 100)
(min(prices) > 0)
}
FunctionDescriptionExample
type(v)Get type nametype(value) == "string"
int(v)Convert to intint("42") == 42
float(v)Convert to floatfloat("3.14")
string(v)Convert to stringstring(42) == "42"
assert {
(type(u.age) == "int")
(type(u.name) == "string")
(int(u.score) >= 0)
}
FunctionDescriptionExample
now()Current timeu.createdAt < now()
date(s)Parse datedate(u.birthday)
duration(s)Parse durationduration("24h")
assert {
(u.createdAt < now())
(date(u.expiresAt) > now())
}
assert {
(u.profile != nil)
(u.deletedAt == nil)
(u.profile?.bio != "")
((u.nickname ?? "Anonymous") != "")
}
assert {
(u.email contains "@" && u.email contains ".")
(len(u.email) >= 5 && len(u.email) <= 255)
(len(u.password) >= 8)
(u.password matches "[A-Z]")
(u.password matches "[0-9]")
(u.age >= 13 && u.age < 150)
}
assert {
(all(order.items, #.quantity > 0 && #.price >= 0))
(any(team.members, #.role == "admin"))
(none(products, #.deleted))
(count(users, #.verified) >= 1)
}
assert {
(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)
}