On this page

AMQP 0-9-1 Protocol Reference

Faultbox proxies AMQP 0-9-1 traffic (RabbitMQ’s native protocol) and injects faults on basic.publish frames. It does not provide a Starlark-side client — your service-under-test uses its own AMQP library (amqp091-go, pika, rabbitmq-client, etc.) and Faultbox transparently proxies that traffic, matching on exchange or routing key to inject errors, delays, or drops on specific publishes.

Interface declaration:

mq = service("rabbitmq",
    interface("main", "amqp", 5672),
    image = "rabbitmq:3.13",
    healthcheck = tcp("localhost:5672"),
)

How faultbox sees AMQP traffic

The proxy decodes AMQP frames and watches specifically for basic.publish methods. When one is seen, it extracts the declared exchange and routing key, and runs them against the fault-rule table. All other frames (connection/channel setup, consume, ack, etc.) pass through untouched.

The topic= kwarg on a fault rule is matched against the routing key first, and falls back to the exchange name if the routing key is empty — matching both “routed publish” and “direct-to-exchange” use cases with the same rule shape.

Fault Rules

error(topic=, message=)

Inject a protocol-level error on matching publishes. Clients with publisher confirms enabled will see the message nacked; clients without confirms see a channel-level exception.

queue_full = fault_assumption("queue_full",
    target = mq.main,
    rules = [error(topic="orders.*", message="PRECONDITION_FAILED - queue full")],
)
ParameterTypeDescription
topicstringRouting-key glob ("orders.*", "events.#", "*") — falls back to exchange name if routing key is empty
messagestringError reason text; use canonical RabbitMQ prefixes (PRECONDITION_FAILED, RESOURCE_LOCKED, ACCESS_REFUSED, CONNECTION_FORCED, etc.) so client-side error-type checks recognize them

delay(topic=, delay=)

Delay matching publishes before forwarding. Tests publisher-confirm deadlines and back-pressure in message-producing code paths.

slow_publish = fault_assumption("slow_publish",
    target = mq.main,
    rules = [delay(topic="orders.*", delay="2s")],
)

drop(topic=)

Drop matching publishes — the message never reaches the broker. Exposes “I assumed my message got through” bugs in producers that don’t use publisher confirms.

dropped_publishes = fault_assumption("dropped_publishes",
    target = mq.main,
    rules = [drop(topic="events.audit")],
)

Recipes

Canonical AMQP failure wrappers ship in the stdlib. Load them via @faultbox/recipes/amqp.star:

load("@faultbox/recipes/amqp.star", "amqp")

broker_blip = fault_assumption("broker_blip",
    target = mq.main,
    rules = [amqp.connection_error(routing_key = "orders.*")],
)

Full list: @faultbox/recipes/amqp.star.

Covered scenarios: channel_error, connection_error, resource_locked, access_refused, precondition_failed, publish_nack, broker_unavailable, slow_publish, connection_drop.

Seed / Reset Patterns

RabbitMQ is stateful — queues, exchanges, and bindings persist across connections. For test isolation, tear down and redeclare topology on each reset, or use per-test vhosts.

mq = service("rabbitmq",
    interface("main", "amqp", 5672),
    image = "rabbitmq:3.13",
    healthcheck = tcp("localhost:5672"),
    reuse = True,
    reset = lambda: exec("rabbitmqctl purge_queue orders"),
)

Notes

  • Only basic.publish is intercepted. Consume frames, acks, channel open/close, and connection setup pass through untouched. Fault rules therefore only affect publishers. To simulate broker-down scenarios for consumers, use drop(topic="*") to sever the connection mid-stream.
  • No Starlark client methods. Unlike Redis or Postgres, there are no mq.main.publish() / mq.main.consume() helpers. Put a real AMQP client in your SUT; Faultbox only intercepts the wire protocol.
  • AMQP 1.0 is not supported. Only AMQP 0-9-1 (RabbitMQ’s default).