Documentation

Testing rules

Validate syntax, dry-run a rule against sample data, and compare against expected outcomes.

Rules are easier to test than code: every rule is a pure function of its input. The Rule Engine ships three layers of test tooling — syntactic validation, single-rule dry runs, and full rule-set executions with monitoring.

Validate syntax

Run validation before saving any rule set. Three flavors:

POST/rules/validateJWT
Validate parsed rule objects.
POST/rules/validate-yamlJWT
Validate raw YAML content.
POST/rules/validate-jsonJWT
Validate raw JSON content.

curl -X POST https://appengine.appmint.io/rules/validate-yaml \
  -H "x-org-id: $ORG" -H "x-customer: $USER" \
  -H "Content-Type: application/json" \
  -d '{"yamlContent": "version: 2.0\nrules:\n  - id: TEST\n    when: order.total > 100\n    then:\n      add_message: hi"}'

The response lists structural errors (missing id, malformed condition, unknown operator), unknown helper references, and warnings (e.g. a rule with no then block).

Dry-run a single rule

POST/rules/test-ruleJWT

Pass the rule and a testData payload. The engine evaluates it and returns the resolved branches:

curl -X POST https://appengine.appmint.io/rules/test-rule \
  -H "x-org-id: $ORG" -H "x-customer: $USER" \
  -H "Content-Type: application/json" \
  -d '{
    "rule": {
      "id": "BULK",
      "when": { ">=": ["order.lineCount", 5] },
      "then": { "apply_discount": { "type": "percent", "value": 10 } },
      "else": { "apply_discount": { "type": "percent", "value": 0 } }
    },
    "testData": { "order": { "lineCount": 7 } }
  }'

Response:

{
  "passed": true,
  "branchTaken": "then",
  "actions": { "apply_discount": { "type": "percent", "value": 10 } },
  "evaluationTimeMs": 1.4,
  "vars": {}
}

Run a full rule set with monitoring

POST/rule-engine/executeJWT

Pass options.logExecution: true and options.trackPerformance: true to capture per-rule timings and write the run to execution history.

curl -X POST https://appengine.appmint.io/rule-engine/execute \
  -H "x-org-id: $ORG" -H "x-customer: $USER" \
  -d '{
    "rules": [ /* array of rules */ ],
    "data": { /* input */ },
    "options": { "logExecution": true, "trackPerformance": true, "enableDebug": true, "timeout": 5000 }
  }'

enableDebug: true returns intermediate variable bindings and which branch each rule took.

Sample data fixtures

The repo ships canonical examples to copy from. Validate your data against the shape these expect:

ExamplePath
Bulk discountsrc/rule-engine/examples/bulk-discount.yaml
Fraud gatesrc/rule-engine/examples/fraud-gate.yaml
Order pipelinesrc/rule-engine/examples/order-pipeline.yaml
Shipping pathsrc/rule-engine/examples/shipping-path.yaml
Order all-fields fixturesrc/rule-engine/examples/order-all-fields.json
Invalid order fixturesrc/rule-engine/examples/invalid-order.json

Read execution history

GET/rules/execution-historyJWT
QueryDescription
executionIdOne specific run
ruleSetIdFilter by rule set
dateFrom, dateToTime window
successOnly failures (false) or successes (true)
eventTrigger event filter
limit, pagePaging

Each record carries the rule set, input data hash, the resolved actions, and per-rule timings.

GET/rules/execution-statsJWT
Aggregate counts.
GET/rules/performance-metricsJWT
Latency, throughput, slow rules.

The x-customer header carries the user principal. The rule engine controllers are unusual in that they read this rather than relying on the global JWT guard alone — pass both the JWT and the customer record when integrating from a backend.