# AMQP

> Send messages to AMQP queues and publish them to AMQP exchanges, and check the messages your services send there: RabbitMQ over AMQP 0-9-1, and brokers that speak AMQP 1.0, such as ActiveMQ Artemis.

Source: https://axx.nimbusxr.us/next/references/packs/amqp/

Send messages to AMQP queues and publish them to AMQP exchanges, and check the messages your services send there: RabbitMQ over AMQP 0-9-1, and brokers that speak AMQP 1.0, such as ActiveMQ Artemis.

Register the broker once, usually in the `Background`; every AMQP step of the scenario uses it:

```gherkin
Given the depot amqp broker with the following properties:
  | url | amqp://parcels:${env:RABBITMQ_PASSWORD}@localhost:5672/ |
```

Steps name a **queue** or an **exchange**: `the parcels.label-printed amqp queue`, `the printers amqp exchange`. Messages carry a routing key (`with the routing key '...'`, or a `routing key` row), headers (`header <name>` rows) and properties such as `content type`. A message whose body is JSON is sent as `application/json` unless a `content type` row says otherwise, and every message is persistent.

- **Checking a queue receives from it:** Axx acknowledges each message it receives, as any consumer would, so check the queues your services **write** to and nothing else reads.
- **Checking an exchange takes nothing from anyone:** for the exchanges a run checks, Axx declares a queue of its own (`axx-<run>.<exchange>`, exclusive, deleted with the run) once the services are up, and binds it with `#` and with the routing keys the checks name, which covers topic, fanout, headers and direct exchanges.
- **A check only looks at the messages received since its scenario started,** so scenarios running in parallel check their own messages, by data unique to them.
- **Publishing a message nothing receives fails the step:** the broker returns a message no queue is bound for, and the step says so, instead of the message going nowhere unnoticed.
- **AMQP 1.0:** with `protocol | 1.0`, a queue is an anycast address and an exchange a multicast one, and the routing key is the message's subject. RabbitMQ routes the same messages over 0-9-1 whatever protocol your services use, so keep the default for it.
- **Secrets stay secret:** `${env:..}` values in the URL are masked in logs and failures.

Add it to a project with `axx pack add amqp`.

## Steps

### `amqp.broker`

```gherkin
Given the {word} amqp broker with the following properties:
  | property | value |
```

The table may start with the row naming its columns, as above, or go straight to its rows.

Register the AMQP broker the steps talk to.

- The first broker registered is the one the scenario's AMQP steps use.
- Values expand `${env:..}` and `${sys:..}`; `${env:..}` values are masked.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |

| Property | Takes | Values | Default |
|---|---|---|---|
| `url` | the broker's URL, with its user, password and virtual host: `amqp://user:password@host:5672/vhost`, or `amqps://` for TLS |  | |
| `protocol` | the AMQP version the broker speaks | `0-9-1`, `1.0` | `0-9-1` |
| `asyncapi` | an AsyncAPI document, a file of the project or a URL, that the queues and exchanges follow: every message a scenario sends there, and every message a check finds, is checked against it (with the asyncapi pack) |  | |

**Example:**

```gherkin
Given the depot amqp broker with the following properties:
  | url | amqp://parcels:${env:RABBITMQ_PASSWORD}@localhost:5672/ |
Given the depot amqp broker with the following properties:
  | url      | amqp://artemis:${env:ARTEMIS_PASSWORD}@localhost:5672 |
  | protocol | 1.0                                                  |
```

_Since 0.1.5._

### `amqp.queue.send`

```gherkin
When a message is sent to the {word} amqp queue:
  """
  ...
  """
```

Send a message to the amqp queue; the doc string is its body.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |

**Example:**

```gherkin
When a message is sent to the parcels.label-printed amqp queue:
  """
  {"reference": "PX-8103", "printer": "LEJ-3", "printedAt": "2026-09-28T08:15:00Z"}
  """
```

_Since 0.1.5._

### `amqp.queue.send.file`

```gherkin
When the {filepath} message is sent to the {word} amqp queue[[ with the following properties:]]
```

Send a message whose body is the file to the amqp queue, with the properties of the table when the step has one.

| Parameter | Takes | For example |
|---|---|---|
| `{filepath}` | a file of the project: a path relative to the feature file's directory, the `resources` directories or axx.yaml's directory (looked for in that order), or an absolute path, quoted when it has a space | `seeds/parcels.yaml`, `kafka/scan-delivered.json` |
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |

| Name | Takes | Default |
|---|---|---|
| `header <name>` | a header sent with the message: its name after `header `, and its value | |
| `content type` | the body's media type | ``application/json` for a JSON body, `text/plain` otherwise` |
| `correlation id` | the message's correlation ID | |
| `message id` | the message's ID | |
| `reply to` | where replies go: a queue's name | |

**Variants**, the parts in `[[...]]` said or left out:

- `the {filepath} message is sent to the {word} amqp queue`
- `the {filepath} message is sent to the {word} amqp queue with the following properties:`

**Example:**

```gherkin
When the amqp/label-printed.json message is sent to the parcels.label-printed amqp queue
When the amqp/label-printed.json message is sent to the parcels.label-printed amqp queue with the following properties:
  | header printer | LEJ-3            |
  | content type   | application/json |
```

_Since 0.1.5._

### `amqp.queue.received`

```gherkin
Then [[within {duration} ]]the {word} amqp queue has a message where:
  | path | value |
```

The table may start with the row naming its columns, as above, or go straight to its rows.

Check that the amqp queue has a message with those values, received since the scenario started.

- The check waits for it: 10 seconds, or `within {duration}`.
- Axx acknowledges the messages of a queue it checks, so check the queues your services write to.

| Parameter | Takes | For example |
|---|---|---|
| `{duration}` | a duration in seconds (`s`) or minutes (`m`) | `5s`, `2m` |
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |

Each row is a path into the message's JSON body (a field name, a dotted path or a JSONPath) and the value it has, compared as text: `null` for null and `undefined` for absent. A row `header <name>` is instead on the header of that name sent with the message: `undefined` when there is none. Rows `routing key`, `content type`, `correlation id`, `message id` and `reply to` are on the message itself, not its body.

**Variants**, the parts in `[[...]]` said or left out:

- `the {word} amqp queue has a message where:`
- `within {duration} the {word} amqp queue has a message where:`

**Example:**

```gherkin
Then within 30s the parcels.label-printed.rejected amqp queue has a message where:
  | reference     | PX-8199        |
  | header reason | unknown parcel |
```

_Since 0.1.5._

### `amqp.exchange.send`

```gherkin
When a message is published to the {word} amqp exchange[[ with the routing key {string}]]:
  """
  ...
  """
```

Send a message to the amqp exchange, with the routing key the step gives; the doc string is its body.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{string}` | text in double or single quotes, which the step leaves out | `"Get a quote"`, `'Express'` |

**Variants**, the parts in `[[...]]` said or left out:

- `a message is published to the {word} amqp exchange:`
- `a message is published to the {word} amqp exchange with the routing key {string}:`

**Example:**

```gherkin
When a message is published to the printers amqp exchange:
  """
  {"reference": "PX-8102", "printer": "LEJ-3", "printedAt": "2026-09-28T08:15:00Z"}
  """
When a message is published to the printers amqp exchange with the routing key 'printed.LEJ':
  """
  {"reference": "PX-8102", "printer": "LEJ-3", "printedAt": "2026-09-28T08:15:00Z"}
  """
```

_Since 0.1.5._

### `amqp.exchange.send.file`

```gherkin
When the {filepath} message is published to the {word} amqp exchange[[ with the following properties:]]
```

Send a message whose body is the file to the amqp exchange, with the properties of the table when the step has one.

| Parameter | Takes | For example |
|---|---|---|
| `{filepath}` | a file of the project: a path relative to the feature file's directory, the `resources` directories or axx.yaml's directory (looked for in that order), or an absolute path, quoted when it has a space | `seeds/parcels.yaml`, `kafka/scan-delivered.json` |
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |

| Name | Takes | Default |
|---|---|---|
| `routing key` | the routing key the exchange routes the message by | |
| `header <name>` | a header sent with the message: its name after `header `, and its value | |
| `content type` | the body's media type | ``application/json` for a JSON body, `text/plain` otherwise` |
| `correlation id` | the message's correlation ID | |
| `message id` | the message's ID | |
| `reply to` | where replies go: a queue's name | |

**Variants**, the parts in `[[...]]` said or left out:

- `the {filepath} message is published to the {word} amqp exchange`
- `the {filepath} message is published to the {word} amqp exchange with the following properties:`

**Example:**

```gherkin
When the amqp/label-printed.json message is published to the printers amqp exchange
When the amqp/label-printed.json message is published to the printers amqp exchange with the following properties:
  | routing key    | printed.LEJ |
  | header printer | LEJ-3       |
```

_Since 0.1.5._

### `amqp.exchange.received`

```gherkin
Then [[within {duration} ]]the {word} amqp exchange has a message where:
  | path | value |
```

The table may start with the row naming its columns, as above, or go straight to its rows.

Check that the amqp exchange has a message with those values, received since the scenario started.

- The check waits for it: 10 seconds, or `within {duration}`.
- Axx binds a queue of its own to an exchange it checks, for the run.

| Parameter | Takes | For example |
|---|---|---|
| `{duration}` | a duration in seconds (`s`) or minutes (`m`) | `5s`, `2m` |
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |

Each row is a path into the message's JSON body (a field name, a dotted path or a JSONPath) and the value it has, compared as text: `null` for null and `undefined` for absent. A row `header <name>` is instead on the header of that name sent with the message: `undefined` when there is none. Rows `routing key`, `content type`, `correlation id`, `message id` and `reply to` are on the message itself, not its body.

**Variants**, the parts in `[[...]]` said or left out:

- `the {word} amqp exchange has a message where:`
- `within {duration} the {word} amqp exchange has a message where:`

**Example:**

```gherkin
Then within 30s the labels amqp exchange has a message where:
  | routing key   | print.express |
  | reference     | PX-8101       |
  | header sender | maple-crafts  |
```

_Since 0.1.5._
