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")],
)
| Parameter | Type | Description |
|---|---|---|
topic | string | Routing-key glob ("orders.*", "events.#", "*") — falls back to exchange name if routing key is empty |
message | string | Error 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.publishis 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, usedrop(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).