Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

SDK conformance suite

Every FireMQ SDK runs the same conformance scenarios. The runner, firemq-conformance, starts an in-memory broker and drives the SDK through a small driver: a program, written with the SDK, that reads JSON commands on stdin and writes JSON replies on stdout. A driver is usually 150 to 300 lines.

cargo run -p firemq-conformance -- run --driver "go run ./conformance/driver"

The runner prints one line per scenario and exits with status 1 if any scenario fails. --only <name> runs one scenario and --list lists them.

Driver protocol

The runner writes one JSON object per line. Each has an id, an op, and the op's fields. The driver writes exactly one reply line per command, with the same id, in order:

{"id": 3, "op": "publish", "exchange": "", "key": "jobs", "body": "hi", "persistent": true, "mandatory": false, "properties": {}, "headers": {}}
{"id": 3, "ok": true, "result": {}}

A failed command replies with ok: false and an error:

{"id": 4, "ok": false, "error": {"kind": "server", "code": 406, "message": "PRECONDITION_FAILED - ..."}}
kindWhen
serverThe broker refused a request; code is the AMQP reply code.
unroutableA mandatory publish reached no queue.
nackedThe broker rejected a publish.
closedThe connection is closed.
unsupportedThe SDK does not implement the op.
otherAnything else.

The driver must not write anything else to stdout. It may log to stderr. It exits when stdin closes.

Ops

The runner always sends every field listed, so drivers need no defaults.

OpFieldsResult
hello{"sdk": "firemq-go", "version": "0.1.0", "capabilities": []}
connecturl{}
close{}
declare_queuename, durable, exclusive, auto_delete, args{"name", "messages", "consumers"}
delete_queuename{"messages"}
purge_queuename{"messages"}
declare_exchangename, type, durable{}
delete_exchangename{}
bind_queuequeue, exchange, key, args{}
unbind_queuequeue, exchange, key{}
bind_exchangedestination, source, key{}
publishexchange, key, body or body_base64, persistent, mandatory, properties, headers{} after the broker confirms
subscribequeue, prefetch, no_ack{"sub": "<id>"}
nextsub, timeout_ms{"message": <message or null>}
acktag{}
nacktag, requeue{}
unsubscribesub{}
getqueue, no_ack{"message": <message or null>}

args and headers are JSON objects whose values are strings, numbers, booleans, nulls, arrays or objects. properties uses the keys of the protocol's message properties: content_type, content_encoding, correlation_id, reply_to, expiration (milliseconds), message_id, timestamp, type, user_id, app_id and priority.

A message is:

{
  "body": "text, or null when the body is not UTF-8",
  "body_base64": "the body, base64-encoded",
  "exchange": "orders",
  "routing_key": "order.created",
  "redelivered": false,
  "tag": "17",
  "properties": {"content_type": "application/json"},
  "headers": {"trace": "abc"}
}

tag is an opaque string the runner passes back to ack and nack.

Scenarios

ScenarioChecks
connect_and_closeHandshake, hello, clean close
wrong_passwordConnecting with a bad password fails with code 403
publish_and_getPublish with confirm; get returns body, exchange and key; empty queue returns null
consume_and_ack100 messages consumed in order and acknowledged; the queue ends empty
prefetch_limits_deliveriesWith prefetch 2, a third delivery waits for an ack
nack_requeueA nacked message comes back with redelivered: true
nack_dead_lettersA message nacked without requeue goes to the dead-letter exchange
mandatory_unroutableA mandatory publish to nowhere fails with kind unroutable
overflow_nackA publish to a full reject-publish queue fails with kind nacked
topic_routingTopic patterns route as specified
exchange_to_exchangeExchange-to-exchange bindings route
properties_and_headersEvery property and header type round-trips
binary_and_large_bodiesA non-UTF-8 body and a 1 MiB body arrive intact
error_codesRedeclaring with other properties gives 406; binding to a missing exchange gives 404; the connection stays usable
purge_and_delete_countsPurge and delete report message counts
unacked_requeued_on_closeUnacknowledged messages return when the connection closes
exclusive_queue_deleted_on_closeAn exclusive queue disappears with its connection
unsubscribe_stops_deliveryNo deliveries after unsubscribe
amqp_interopA message published by the SDK reaches an AMQP consumer, and back