# Blob Storage

> Upload files to Blob Storage containers and check the blobs your services write there.

Source: https://axx.nimbusxr.us/next/references/packs/azure-blob/

Upload files to Blob Storage containers and check the blobs your services write there.

Register the storage account once, set up as the Azure SDK is set up for the real service; every blob step of the scenario uses it:

```gherkin
Given the customs azure storage account with the following properties:
  | connection string | ${env:AZURE_STORAGE_CONNECTION_STRING} |
```

Checks wait for the blob (10 seconds unless `within {duration}` says otherwise), since services write asynchronously. Values expand `${env:..}` and `${sys:..}`.

Add it to a project with `axx pack add azure-blob`.

## Steps

### `azure-blob.account`

```gherkin
Given the {word} azure storage account 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 storage account the blob steps talk to.

- The first account registered is the default.
- Give it a `connection string` or a `url`, not both.
- Values expand `${env:..}` and `${sys:..}`.

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

| Property | Takes |
|---|---|
| `connection string` | the account's connection string, or a local emulator's, like `${env:AZURE_STORAGE_CONNECTION_STRING}` |
| `url` | the account's blob endpoint, like `https://<account>.blob.core.windows.net`, signed in with the Azure default credential chain: environment, workload identity, managed identity, Azure CLI |

**Example:**

```gherkin
Given the customs azure storage account with the following properties:
  | connection string | ${env:AZURE_STORAGE_CONNECTION_STRING} |
Given the customs azure storage account with the following properties:
  | url | ${env:CUSTOMS_STORAGE_URL} |
```

_Since 0.1.0._

### `azure-blob.upload`

```gherkin
When the {filepath} file is uploaded to the {word} blob container[[ as {path}]]
```

Upload a file to the blob container, under the file's name or the name given. Its content type follows the file's extension.

| 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` |
| `{path}` | a path in a folder or a store, like `manifests/M-1/report.csv`, quoted when it has a space. A `*` stands for any characters but a slash, for a name a check cannot know (`snap-*.json`, named by the time): the path then names the one file it matches | `manifests/M-KESTREL-0412/report.csv`, `"Parcels/Depot desk/arrivals.json"` |

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

- `the {filepath} file is uploaded to the {word} blob container`
- `the {filepath} file is uploaded to the {word} blob container as {path}`

**Example:**

```gherkin
When the invoices/kestrel-2026-09.csv file is uploaded to the declarations blob container
When the invoices/kestrel-2026-09.csv file is uploaded to the declarations blob container as incoming/kestrel-2026-09.csv
```

_Since 0.1.0._

### `azure-blob.has`

```gherkin
Then [[within {duration} ]]the {word} blob container has a(n) blob named {path}
```

Check that the blob container has a blob with that name. The check waits for it: 10 seconds, or `within {duration}`.

| Parameter | Takes | For example |
|---|---|---|
| `{duration}` | a duration in seconds (`s`) or minutes (`m`) | `5s`, `2m` |
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{path}` | a path in a folder or a store, like `manifests/M-1/report.csv`, quoted when it has a space. A `*` stands for any characters but a slash, for a name a check cannot know (`snap-*.json`, named by the time): the path then names the one file it matches | `manifests/M-KESTREL-0412/report.csv`, `"Parcels/Depot desk/arrivals.json"` |

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

- `the {word} blob container has a(n) blob named {path}`
- `within {duration} the {word} blob container has a(n) blob named {path}`

**Example:**

```gherkin
Then within 30s the declarations blob container has a blob named disputes/kestrel-2026-09.csv
```

_Since 0.1.0._

### `azure-blob.identical`

```gherkin
Then [[within {duration} ]]the {path} blob in the {word} blob container is identical to the {filepath} file
```

Check that the blob exists with exactly the content of the file. The check waits for it: 10 seconds, or `within {duration}`.

| Parameter | Takes | For example |
|---|---|---|
| `{duration}` | a duration in seconds (`s`) or minutes (`m`) | `5s`, `2m` |
| `{path}` | a path in a folder or a store, like `manifests/M-1/report.csv`, quoted when it has a space. A `*` stands for any characters but a slash, for a name a check cannot know (`snap-*.json`, named by the time): the path then names the one file it matches | `manifests/M-KESTREL-0412/report.csv`, `"Parcels/Depot desk/arrivals.json"` |
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{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` |

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

- `the {path} blob in the {word} blob container is identical to the {filepath} file`
- `within {duration} the {path} blob in the {word} blob container is identical to the {filepath} file`

**Example:**

```gherkin
Then the disputes/kestrel-2026-09.csv blob in the declarations blob container is identical to the expected/kestrel-disputes.csv file
```

_Since 0.1.0._

### `azure-blob.properties`

```gherkin
Then [[within {duration} ]]the {path} blob in the {word} blob container has the following properties:
  | path | value |
```

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

Check that the blob holds JSON with those values at those paths. The check waits for it: 10 seconds, or `within {duration}`.

| Parameter | Takes | For example |
|---|---|---|
| `{duration}` | a duration in seconds (`s`) or minutes (`m`) | `5s`, `2m` |
| `{path}` | a path in a folder or a store, like `manifests/M-1/report.csv`, quoted when it has a space. A `*` stands for any characters but a slash, for a name a check cannot know (`snap-*.json`, named by the time): the path then names the one file it matches | `manifests/M-KESTREL-0412/report.csv`, `"Parcels/Depot desk/arrivals.json"` |
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |

Each row is a path into the JSON (a property name, a dotted path or a JSONPath) and the value it has, compared as text: `null` for null and `undefined` for absent, as in the other JSON property steps.

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

- `the {path} blob in the {word} blob container has the following properties:`
- `within {duration} the {path} blob in the {word} blob container has the following properties:`

**Example:**

```gherkin
Then the summaries/kestrel-2026-09.json blob in the declarations blob container has the following properties:
  | carrier         | KESTREL |
  | lines           | 14      |
  | totals.disputed | 5.25    |
```

_Since 0.1.0._

### `azure-blob.contains`

```gherkin
Then [[within {duration} ]]the {path} blob in the {word} blob container contains {string}
```

Check that the text of the blob contains the text.

- The check waits for it: 10 seconds, or `within {duration}`.
- Case matters; runs of spaces and line breaks count as one space.
- The text is read by the blob's type: the text of a PDF's pages, the paragraphs and tables of a Word document (.docx), the cells of every sheet of an Excel workbook (.xlsx), the text content (or the markup) of XML and HTML, or the text itself.

| Parameter | Takes | For example |
|---|---|---|
| `{duration}` | a duration in seconds (`s`) or minutes (`m`) | `5s`, `2m` |
| `{path}` | a path in a folder or a store, like `manifests/M-1/report.csv`, quoted when it has a space. A `*` stands for any characters but a slash, for a name a check cannot know (`snap-*.json`, named by the time): the path then names the one file it matches | `manifests/M-KESTREL-0412/report.csv`, `"Parcels/Depot desk/arrivals.json"` |
| `{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:

- `the {path} blob in the {word} blob container contains {string}`
- `within {duration} the {path} blob in the {word} blob container contains {string}`

**Example:**

```gherkin
Then the invoices/kestrel-2026-09.pdf blob in the declarations blob container contains "Total due: 1284.50 EUR"
```

_Since 0.1.1._

### `azure-blob.row`

```gherkin
Then [[within {duration} ]]the {path} blob in the {word} blob container has a row where:
  | column | value |
```

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

Check that the table of the blob has a row with those values in those columns.

- The check waits for it: 10 seconds, or `within {duration}`.
- The blob is a CSV or TSV file, or an Excel workbook (.xlsx; its first sheet), whose first row names the columns.
- Cells compare as text, as the workbook shows them, with runs of spaces as one space; an empty value matches an empty cell.

| Parameter | Takes | For example |
|---|---|---|
| `{duration}` | a duration in seconds (`s`) or minutes (`m`) | `5s`, `2m` |
| `{path}` | a path in a folder or a store, like `manifests/M-1/report.csv`, quoted when it has a space. A `*` stands for any characters but a slash, for a name a check cannot know (`snap-*.json`, named by the time): the path then names the one file it matches | `manifests/M-KESTREL-0412/report.csv`, `"Parcels/Depot desk/arrivals.json"` |
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |

Each row names a column, as the blob's first row names it, and the value in that column.

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

- `the {path} blob in the {word} blob container has a row where:`
- `within {duration} the {path} blob in the {word} blob container has a row where:`

**Example:**

```gherkin
Then within 30s the disputes/kestrel-2026-09.csv blob in the declarations blob container has a row where:
  | parcel | PX-5199          |
  | status | UNKNOWN_SHIPMENT |
  | billed | 4.10             |
```

_Since 0.1.1._
