Skip to content

Latest commit

 

History

History

use-cases

README.md

Server-backed use cases

Each *.json file here is a complete, end-to-end scenario built on one of the example packages: the requests to send and the exact responses to expect from a live Orion server running that package's workflow. They are the data-driven half of the repo's e2e suite — and proof that the shipped examples behave as documented, asserted in CI on every PR.

The workflow under test is referenced, never copied: each case points at the package's workflow.json (the same way the offline ../workflow-tests/ cases do), so a package edit is automatically tested here and the two can't drift apart. The difference between the two suites: workflow-tests runs the workflow through the engine offline (orion-server test), while these cases exercise the whole runtime — HTTP channel routing, activation lifecycle, engine reloads — through the orion-cli binary against a real orion-server.

Cases that test runtime behaviour rather than a scenario (archive quarantine, dry-run traces, secret masking, connector error handling) live with the suite itself, in tests/e2e/cases/, using this same format.

Run them

just e2e                # the whole e2e suite (from the repo root)
./tests/e2e/run.sh 13   # just the use-case suite

The runner (tests/e2e/, suite 13_use_cases.sh) picks up every *.json file in this directory — adding a case needs no code changes. For each case it resets server state, creates and activates the connectors and workflows, creates an active channel for every channel name the tests reference (bound to the case's first workflow), reloads the engine, then runs the tests in order.

Case format

{
  "name": "E-Commerce Order Classification",
  "description": "What the scenario demonstrates",
  "plugins":    [ { "manifest": "../packages/…/plugin.toml" } ],  // optional
  "connectors": [ { /* connector create body */ } ],           // optional
  "workflows":  [ { "file": "../packages/order-classification/workflow.json" } ],
  "tests": [
    {
      "name": "VIP order gets tier and discount",
      "channel": "orders",                 // sync send to this channel
      "input": { "amount": 750 },          // request payload
      "expect": {                          // jq expr -> expected (string) value
        ".status": "ok",
        ".data.order.tier": "vip"
      }
    }
  ]
}

A workflows entry is either { "file": "<path>" } — a workflow JSON file, relative to the case file; use this for anything that exists as a package — or an inline workflow create body, for throwaway workflows only a behaviour test needs.

A plugins entry names a plugin.toml relative to the case file; the runner uploads and activates each one before the workflows, because a workflow naming a plugin function cannot activate until the plugin serving it is active. The component is read beside the manifest, as plugins create does. The server must have plugins.enabled = true — the e2e harness sets it.

An optional case-level "channel_config": { … } is applied as the config of every channel the case creates — how a case exercises a config-dependent ingress such as request.body_mode. It is case-level because the channels are all bound to the case's first workflow.

A case whose channels must not all bind to the first workflow declares them explicitly instead, which is what a channel_call composition needs:

"channels": [ { "name": "customer-lookup", "workflow_id": "customer-lookup" } ]

Each test sends input to channel and checks every expect entry: the key is a jq expression evaluated against the JSON response, the value is the expected result compared as a string (jq -r).

Test modes

Instead of the default sync send, a test can set one of:

Key Meaning
"dry_run_rule": <n> Run workflows test (server-side dry run) against the case's n-th workflow with input — no traffic served
"read_connector": <n> Assert on connectors get for the case's n-th connector (e.g. secret masking)
"raw": true Send input via send --raw, unwrapped — the only way to address a channel whose request.body_mode is "payload"

Lifecycle steps and failure paths

Key Meaning
"before": [...] Actions to run first: {"action": "archive_rule", "rule_index": 0}, {"action": "activate_rule", ...}, or {"action": "reload"}
"expect_error": true The command must fail (e.g. a send to a channel quarantined by archiving its workflow); expect is not evaluated

The cases

File Package under test Demonstrates
ecommerce-classification.json order-classification Value-tier classification with computed discounts
iot-sensor-alerts.json iot-sensor-alert Severity ranges with validation and filtering
webhook-transformation.json webhook-transform Payload normalization via null-safe var mapping
notification-routing.json notification-routing Progressive routing (log / email / SMS) by severity
channel-composition.json channel-composition One service calling another in-process via channel_call
fixed-width-statement.json fixed-width-statement A plugin function on the serving path: the case uploads and activates the codec before its workflow