Syntax Reference
Syntax Reference
Section titled “Syntax Reference”This page provides a complete reference for the scaf DSL syntax with examples.
Imports
Section titled “Imports”Import shared fixtures and setup modules:
// Simple importimport "../../setup/db"
// Import with aliasimport fixtures "../shared/fixtures"
// Multiple importsimport "../../setup/db"import fixtures "../shared/fixtures"import utils "./utils"Function Definitions
Section titled “Function Definitions”Define named database functions using backtick-delimited raw strings:
fn GetUser `MATCH (u:User {id: $userId})RETURN u.name`
fn CreateUser `INSERT INTO users (name) VALUES ($name)`Setup and Teardown
Section titled “Setup and Teardown”Global Setup
Section titled “Global Setup”Setup blocks run before tests:
setup `CREATE (u:User {id: 1, name: "Alice"})`Named Setup
Section titled “Named Setup”Reference setup functions from imported modules:
import fixtures "../shared/fixtures"
// Simple named setupsetup fixtures.CreateUsers()
// With parameterssetup fixtures.CreatePosts(count: 10, authorId: 1)Teardown
Section titled “Teardown”Teardown blocks run after tests:
teardown `MATCH (n) DETACH DELETE n`Query Scopes
Section titled “Query Scopes”Group tests by the query they exercise:
GetUser { test "finds user" { $userId: 1 u.name: "Alice" }}Groups
Section titled “Groups”Organize related tests with hierarchical groups:
GetUser { group "existing users" { setup `CREATE (:Session)`
test "finds alice" { $userId: 1 }
test "finds bob" { $userId: 2 } }}Nested Groups
Section titled “Nested Groups”Groups can be nested arbitrarily deep:
GetUser { group "outer" { group "inner" { test "deep test" { } } }}Basic Test Structure
Section titled “Basic Test Structure”Tests contain inputs (prefixed with $), expected outputs, and optional assertions:
GetUser { test "finds user by id" { $userId: 1 u.name: "Alice" u.email: "alice@example.com" }}Test with Setup
Section titled “Test with Setup”Tests can have their own setup blocks:
GetUser { test "with setup" { setup `CREATE (:Data)` $id: 1 }}Values
Section titled “Values”Primitive Values
Section titled “Primitive Values”Q { test "primitives" { // String values name: "Alice"
// Integer values count: 42
// Float values price: 3.14
// Negative numbers balance: -100
// Boolean values active: true deleted: false
// Null values email: null }}Collections
Section titled “Collections”Q { test "collections" { // List values tags: ["go", "testing", "dsl"]
// Empty list items: []
// Map values user: {id: 1, name: "Alice"}
// Empty map data: {}
// Nested structures metadata: { author: { id: 1, name: "Alice" }, tags: ["go", "testing"] } }}Assertions
Section titled “Assertions”Expression Assertions
Section titled “Expression Assertions”Validate output values using expressions:
Q { test "expression assertions" { // Simple comparison assert (x > 0)
// Multiple assertions assert (x > 0) assert (y < 10) assert (z == 5)
// Logical expressions assert (x > 0 && y < 10)
// Function calls assert (len(items) > 0)
// Member access assert (u.age >= 18)
// Index access assert (items[0] == "first")
// Unary expressions assert (!done)
// String comparison assert (name == "Alice") }}Query Assertions
Section titled “Query Assertions”Run additional queries and validate their results:
Q { test "query assertions" { $id: 1
// Inline query assertion assert `MATCH (n) RETURN count(n) as c` (c > 0)
// Named query assertion (no params) assert CountNodes() (total > 0)
// Named query assertion with params assert CreatePost(title: "test", authorId: 1) ( p.title == "test" && p.authorId == 1 ) }}Multiple Assertions
Section titled “Multiple Assertions”Tests can have multiple assertion blocks:
Q { test "multiple assertions" { $id: 1 assert (x > 0) assert `CHECK` (y == 1) }}Complete Example
Section titled “Complete Example”Here’s a comprehensive example showing all features together:
// Importsimport fixtures "../shared/fixtures"import utils "./utils"
// Function definitionsfn GetUser `MATCH (u:User {id: $userId})RETURN u.name, u.email, u.age, u.verified`
fn CountUsers `MATCH (u:User)RETURN count(u) as cnt`
// Global setupsetup fixtures.CreateUsers()
// Global teardownteardown `MATCH (n) DETACH DELETE n`
// Tests for GetUser queryGetUser { setup `CREATE (:Session {userId: 1})`
group "existing users" { test "finds Alice with all fields" { $userId: 1
u.name: "Alice" u.email: "alice@example.com" u.age: 30 u.verified: true
assert (u.age >= 18) }
test "finds Bob" { $userId: 2
u.name: "Bob" u.age: 25 } }
group "edge cases" { test "missing user returns null" { $userId: 999
u.name: null u.email: null } }}
// Tests for CountUsers queryCountUsers { test "base count" { cnt: 2 }
test "with temp user" { setup `CREATE (:User {id: 999})`
cnt: 3
assert CountUsers() (cnt > 2) }}Comments
Section titled “Comments”Single-line comments start with //:
// This is a commentfn GetUser `...` // Inline comment
GetUser { // Comment inside scope test "example" { // Comment inside test $id: 1 }}