Skip to content

Assertions Examples


Basic Assertions

# Example: Basic Assertions
# Use the `assert` function to validate conditions in your workflows.
# OrchStep supports two syntax styles for conditions:
#
# 1. Go template syntax: {{ eq vars.status "ok" }}
# 2. JavaScript syntax: vars.status === "ok"
#
# Detection is automatic: if the expression starts with {{ it uses
# Go templates; otherwise it uses JavaScript (goja VM).
#
# Try: orchstep run
# Try: orchstep run js_assertions
name: basic-assertions
desc: "Simple condition checking with the assert function"
defaults:
environment: "prod"
count: 10
status: "ok"
message: "deployment successful"
tasks:
# --- Go Template assertions ---
go_template_assertions:
desc: "Assertions using Go template syntax"
steps:
- name: equality
desc: "Check exact equality"
func: assert
args:
condition: '{{ eq vars.environment "prod" }}'
desc: "Environment must be production"
- name: numeric
desc: "Numeric comparison"
func: assert
args:
condition: "{{ gt vars.count 5 }}"
desc: "Count must be greater than 5"
- name: string_contains
desc: "Check if a string contains a substring"
func: assert
args:
condition: '{{ contains "successful" vars.message }}'
desc: "Message must contain 'successful'"
- name: logical_and
desc: "Combine conditions with AND"
func: assert
args:
condition: '{{ and (eq vars.status "ok") (gt vars.count 5) }}'
desc: "Status must be ok AND count > 5"
- name: logical_or
desc: "Combine conditions with OR"
func: assert
args:
condition: '{{ or (eq vars.environment "prod") (eq vars.environment "staging") }}'
desc: "Environment must be prod or staging"
- name: negation
desc: "Negate a condition"
func: assert
args:
condition: '{{ not (eq vars.environment "dev") }}'
desc: "Environment must not be dev"
# --- JavaScript assertions ---
js_assertions:
desc: "Assertions using JavaScript syntax"
steps:
- name: equality
desc: "Check exact equality"
func: assert
args:
condition: 'vars.environment === "prod"'
desc: "Environment must be production"
- name: numeric
desc: "Numeric comparison"
func: assert
args:
condition: "vars.count > 5"
desc: "Count must be greater than 5"
- name: string_contains
desc: "Check if a string contains a substring"
func: assert
args:
condition: 'vars.message.includes("successful")'
desc: "Message must contain 'successful'"
- name: logical_and
desc: "Combine conditions with &&"
func: assert
args:
condition: 'vars.status === "ok" && vars.count > 5'
desc: "Status must be ok AND count > 5"
- name: negation
desc: "Negate a condition"
func: assert
args:
condition: 'vars.environment !== "dev"'
desc: "Environment must not be dev"
# --- Validate step outputs ---
validate_outputs:
desc: "Assert against step outputs"
steps:
- name: run_check
func: shell
do: 'echo "Status: healthy | Latency: 42ms"'
outputs:
result: "{{ result.output }}"
- name: check_healthy
desc: "Verify the health check output"
func: assert
args:
condition: '{{ contains "healthy" steps.run_check.result }}'
desc: "Service must report healthy status"
main:
desc: "Run all assertion demos"
steps:
- name: run_go_templates
task: go_template_assertions
- name: run_javascript
task: js_assertions
- name: run_output_check
task: validate_outputs
- name: summary
func: shell
do: |
echo "=== Assertion Syntax Comparison ==="
echo ""
echo "Go Template: JavaScript:"
echo " eq .a .b a === b"
echo " gt .n 5 n > 5"
echo " contains .s text s.includes('text')"
echo " and (c1) (c2) c1 && c2"
echo " or (c1) (c2) c1 || c2"
echo " not (c) !c"

Multiple Conditions in a Single Assertion

# Example: Multiple Conditions in a Single Assertion
# Instead of writing many single-condition assert steps, you can
# validate multiple conditions in one step using `conditions:` (plural).
#
# All conditions must pass for the step to succeed. If any fail,
# OrchStep reports which specific conditions failed.
#
# You can mix Go template and JavaScript syntax across conditions.
#
# Try: orchstep run
# Try: orchstep run validate_deployment
name: multi-conditions
desc: "Multiple conditions in a single assertion step"
defaults:
app_name: "payments-api"
version: "2.4.1"
replicas: 3
features:
- "payments"
- "refunds"
- "webhooks"
tasks:
# --- Multiple conditions: all must pass ---
validate_deployment:
desc: "Validate a deployment meets all requirements"
steps:
- name: simulate_deploy
func: shell
do: |
echo "Deployed payments-api v2.4.1"
echo "Replicas: 3/3 ready"
echo "Health: OK"
outputs:
log: "{{ result.output }}"
status: "success"
exit_code: 0
- name: check_all_conditions
desc: "All conditions must pass"
func: assert
args:
conditions:
- condition: "{{ eq steps.simulate_deploy.exit_code 0 }}"
desc: "Deploy command must exit successfully"
- condition: '{{ contains "Health: OK" steps.simulate_deploy.log }}'
desc: "Health check must report OK"
- condition: '{{ contains "3/3 ready" steps.simulate_deploy.log }}'
desc: "All replicas must be ready"
- condition: 'steps.simulate_deploy.status === "success"'
desc: "Deployment status must be success"
# --- Mix Go template and JavaScript in one assert ---
mixed_syntax:
desc: "Go template and JavaScript conditions can coexist"
steps:
- name: check_config
desc: "Validate app config from multiple angles"
func: assert
args:
conditions:
# Go template style
- condition: '{{ eq vars.app_name "payments-api" }}'
desc: "App name matches"
# JavaScript style
- condition: 'vars.replicas >= 2'
desc: "At least 2 replicas configured"
# Go template with sprig function
- condition: "{{ gt (len vars.features) 2 }}"
desc: "At least 3 features enabled"
# JavaScript with array operations
- condition: 'vars.features.length === 3'
desc: "Exactly 3 features defined"
# --- Handling expected failures with catch ---
graceful_failure:
desc: "Handle assertion failures gracefully"
steps:
- name: risky_check
desc: "Some conditions may fail -- catch handles it"
func: assert
args:
conditions:
- condition: '{{ eq vars.app_name "payments-api" }}'
desc: "App name is correct"
- condition: "vars.replicas >= 10"
desc: "Need 10+ replicas for prod (will fail in dev)"
catch:
- name: handle_failure
func: shell
do: |
echo "Assertion failed: not enough replicas for production"
echo "Current replicas: {{ vars.replicas }}"
echo "Consider scaling up before promoting to production"
main:
desc: "Run all multi-condition demos"
steps:
- name: demo_deployment
task: validate_deployment
- name: demo_mixed
task: mixed_syntax
- name: demo_failure_handling
task: graceful_failure
- name: summary
func: shell
do: |
echo "=== Multi-Condition Assertions ==="
echo ""
echo "Use 'conditions:' (plural) for multiple checks:"
echo " func: assert"
echo " args:"
echo " conditions:"
echo " - condition: expr1"
echo " desc: what it checks"
echo " - condition: expr2"
echo " desc: what it checks"
echo ""
echo "Benefits:"
echo " - All conditions evaluated in one step"
echo " - Clear reporting of which conditions failed"
echo " - Mix Go template and JavaScript freely"

Unified Assertion Patterns

# Example: Unified Assertion Patterns
# Comprehensive assertion patterns covering variables, step outputs,
# objects, arrays, and helper functions.
#
# OrchStep provides JavaScript helper functions for common checks:
# exists("path") -- true if the path resolves to a value
# len(value) -- length of array or string
# contains(haystack, needle) -- check array/string containment
# matches(regex, str) -- regex match
# get("path") -- safely access nested values (returns null if missing)
#
# Try: orchstep run
name: unified-patterns
desc: "Unified assertion patterns for validation"
defaults:
app_name: "order-service"
version: "3.1.0"
max_retries: 5
tags:
- name: "api"
priority: 1
- name: "backend"
priority: 2
- name: "critical"
priority: 3
config:
database:
host: "db.example.com"
port: 5432
pool_size: 25
cache:
enabled: true
ttl: 300
tasks:
main:
desc: "Demonstrate unified assertion patterns"
steps:
# Setup: create step outputs to validate against
- name: health_check
func: shell
do: |
echo "Service: order-service"
echo "Status: healthy"
echo "Uptime: 72h"
echo "Active connections: 142"
outputs:
report: "{{ result.output }}"
active_users: 142
status: "healthy"
services: ["order-service", "payment-service", "notification-service"]
# --- Pattern 1: Variable validation ---
- name: validate_vars
desc: "Validate definition variables"
func: assert
args:
conditions:
- condition: '{{ eq vars.app_name "order-service" }}'
desc: "App name is set correctly"
- condition: "{{ gt vars.max_retries 3 }}"
desc: "Max retries is sufficient"
- condition: '{{ vars.version | upper | eq "3.1.0" }}'
desc: "Version string is correct (with sprig pipe)"
# --- Pattern 2: Step output validation ---
- name: validate_outputs
desc: "Validate step outputs from previous steps"
func: assert
args:
conditions:
- condition: 'steps.health_check.status === "healthy"'
desc: "Service reports healthy"
- condition: "steps.health_check.active_users > 100"
desc: "More than 100 active users"
- condition: '{{ contains "Uptime" steps.health_check.report }}'
desc: "Report includes uptime information"
# --- Pattern 3: Nested object validation ---
- name: validate_nested
desc: "Validate nested object properties"
func: assert
args:
conditions:
- condition: '{{ eq vars.config.database.host "db.example.com" }}'
desc: "Database host is configured"
- condition: "vars.config.database.pool_size >= 20"
desc: "Pool size is adequate"
- condition: "vars.config.cache.enabled === true"
desc: "Cache is enabled"
- condition: "vars.config.cache.ttl === 300"
desc: "Cache TTL is 5 minutes"
# --- Pattern 4: Array validation ---
- name: validate_arrays
desc: "Validate array contents and properties"
func: assert
args:
conditions:
- condition: "{{ eq (len vars.tags) 3 }}"
desc: "Exactly 3 tags defined"
- condition: 'vars.tags[0].name === "api"'
desc: "First tag is 'api'"
- condition: "vars.tags[2].priority === 3"
desc: "Critical tag has priority 3"
- condition: 'steps.health_check.services.length === 3'
desc: "Three services reported"
# --- Pattern 5: JavaScript helper functions ---
- name: validate_with_helpers
desc: "Use built-in helper functions"
func: assert
args:
conditions:
# exists() -- check if a value is defined
- condition: 'exists("steps.health_check.status")'
desc: "Health check status exists"
# len() -- get length of arrays or strings
- condition: "len(steps.health_check.services) === 3"
desc: "Three services returned"
# contains() -- check array or string containment
- condition: 'contains(steps.health_check.services, "order-service")'
desc: "Order service is in the list"
# matches() -- regex matching
- condition: 'matches("^order", vars.app_name)'
desc: "App name starts with 'order'"
# get() -- safe nested access (returns null for missing paths)
- condition: 'get("vars.config.database.host") === "db.example.com"'
desc: "Safely access nested config"
# --- Pattern 6: Complex expressions ---
- name: validate_complex
desc: "Complex multi-condition expressions"
func: assert
args:
conditions:
# Go template: combine multiple checks
- condition: '{{ and (eq vars.config.cache.enabled true) (gt vars.config.database.pool_size 10) }}'
desc: "Cache enabled and pool size adequate"
# JavaScript: rich expression with multiple checks
- condition: |
vars.config.cache.enabled &&
vars.config.database.pool_size > 10 &&
vars.app_name.includes("service") &&
len(vars.tags) >= 3
desc: "All infrastructure requirements met"
- name: summary
func: shell
do: |
echo "=== Assertion Helper Functions ==="
echo ""
echo " exists('path') -- value is defined"
echo " len(value) -- array/string length"
echo " contains(haystack, needle) -- array/string search"
echo " matches(regex, str) -- regex matching"
echo " get('path') -- safe nested access"