camel-integration-test 0.41.0

Scenario document model, parser, and integration-tier test harness for rust-camel
Documentation

camel-integration-test

Scenario-tier test support for rust-camel: .test.yaml documents that declare a scenario: section

Owns the scenario document model and parser behind the integration tier of camel test (ADR-0069). A scenario document runs one action vocabulary: send, receive, sleep, and validate. A send takes an optional method HTTP token, uppercased at load. An invalid token fails doc validation with exit 2. A send with a body defaults to POST. A bodyless send defaults to GET. The parser bans the unit-tier keys (inputs, expects, intercepts) at load time, and it rejects env keys that collide with a declared bindVar. The harness provisions each http: partner on 127.0.0.1:0 and folds the partner's bindVar into a layered environment. Config and route ${env:} placeholders resolve through that environment, never the process environment. Every action prints a PASS or FAIL row. Exit codes follow the ADR-0069 taxonomy: 0 when all actions pass, 1 on a verdict failure, 2 on a parse, boot, or apparatus failure.

Usage

A minimal scenario document (orders.test.yaml):

routeFiles:
  - routes/bridge.yaml
scenario:
  - send:
      to: direct:start
      method: PUT
      body: order-payload-7f3a
  - receive:
      from:
        endpoint: http://127.0.0.1:0/orders
        provisioning: harness
        bindVar: PARTNER
      deadline: 2s
      extract:
        body: body
  - validate:
      target: { lastReceived: http://127.0.0.1:0/orders }
      expectation: order-payload-7f3a

The partner endpoint binds on the harness and injects PARTNER=http://127.0.0.1:<bound> into the layered environment, so the route's ${env:PARTNER} reaches the local listener. Run the document with the CLI; the http: partner adapter rides the integration-http Cargo feature, default-on in camel-cli since 2026-09-05:

cargo run -p camel-cli -- test --integration orders.test.yaml

partners: section

A partners: section scripts how a harness partner answers requests. It is a map. Each key is the exact declared endpoint string, the :0 URI as written in the scenario. The value is a sequence of script entries.

Each entry carries optional method and path matchers. A request matches an entry when its method and path match. An entry with no method or path matches any request.

An entry declares exactly one of response or fault: close. The response holds optional status (100-599), headers, and body. The fault: close drops the connection without an HTTP response. The request is still recorded before the fault.

An entry may also declare times and delay. The times is an integer of 1 or more. It defaults to 1. An entry with no times keeps the shipped serve-once behavior. The entry serves its first times matching requests, then it is spent. The delay is a humantime string. The harness holds for the delay before it serves the response or commits the fault.

The harness serves the first unspent matching entry in order. A request no entry matches serves status 500 with an empty body. A document with no partners: section is permissive: every request gets status 200 with an empty body.

For example, a timed delayed response entry can precede a fault entry for the same request:

partners:
  http://127.0.0.1:0/orders:
  - method: PUT
    path: /orders
    times: 2
    delay: 100ms
    response:
      status: 201
      body: A
  - method: PUT
    path: /orders
    fault: close

The first entry serves the first two matching requests. Each response arrives after the delay with status 201 and body A. The entry is then spent. The third matching request hits the fault entry. Its connection drops without an HTTP response.

Every partners: key must equal a declared harness http endpoint reference. A key that matches no wired reference fails load with a doc-validation error, exit 2, naming the key. The check runs before any partner binds. A typo of a real key, for example http://127.0.0.1:0/order for :0/orders, fails here. It never falls silently to permissive.

validate targets

A validate action asserts against one of three targets, chosen by the single key of its target map:

  • lastReceived: the last message a receive action collected on that endpoint. The expectation is a subset match, as in the usage example above.
  • variable: a scenario variable set by an earlier extract. Variable existence is checked at run time. The expectation is a subset match against the variable's value.
  • partner: the recorded-request count of a harness partner, described next.

A partner target asserts the recorded-request count of a harness partner. The expectation holds count, an exact non-negative integer, plus optional method and path filters. The method filter compares ASCII-case-insensitively. The path filter compares the path-and-query exactly. The count counts only the recorded requests that pass both filters.

Without a deadline, the assertion reads one immediate snapshot. With a deadline, it polls a fresh snapshot every 100 ms until the filtered count equals the expectation or the deadline passes; the final snapshot then decides. Arrivals only add, so a count above the expectation fails at every snapshot and never settles back.

Partner expectations are exact-count, not subset like message expectations: the count must equal the filtered arrivals, never a lower bound.

${name} interpolation

Scenario strings interpolate ${name}. The surface covers three places:

  • endpoint strings in send and receive,
  • body string leaves,
  • header values.

Substitution is string-only. A string leaf with no placeholder stays as is. Raw substitution applies, with no percent-encoding. An unset variable at send time fails scenario-var-unresolved, exit 1, naming the variable. Exit 1 is a verdict failure, not a parse error. In CI, a document that fails this way is a failed test run, not a harness error.

$${ escapes a literal ${. The escape applies to body leaves and header values too. For example, a JSON body leaf that must reach the wire as ${literal} is written $${literal}.

The receive resolves by the interpolated authority. The path and query need not match the send. A receive declared as http://${PARTNER}/orders finds the roundtrip a map-form send parked.

Minimal route source

An integration document may declare routeFiles: []. The empty list is the minimal route source: no routes boot, and the scenario drives only harness partners.

Two layers, one name: PARTNER

The same name can carry two forms in one run.

Layer Form Usage
scenario variable host:port http://${PARTNER}/orders
route env http://host:port ${env:PARTNER}

One-line rule: scenario = authority, route env = full URI. ${env:} deliberately does not resolve in scenario strings.

The Testing chapter documents the full action grammar, the partner adapters, and the exit contract. examples/integration-testing/ is a runnable example. The partner-retry-route.test.yaml pair there runs a real retrying route against a faulted partner and asserts the two wire attempts.

Related crates

  • camel-cli: camel test, which parses and runs the documents
  • camel-bundles: the boot cascade the scenario boot composes (ADR-0069 section 10)
  • camel-test: unit-tier harness; the scenario tier never depends on it (ADR-0055)