# Commands

> Run commands, such as a command-line tool you ship or an operator's admin command, and check how they end and what they print.

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

Run commands, such as a command-line tool you ship or an operator's admin command, and check how they end and what they print.

Register a command with `the {word} command with the following properties:`: the program and its first arguments, split like an app's `command` in axx.yaml (double quotes group words; there is no shell), the folder it runs in, its environment and its timeout. Then run it, with more arguments and, if it reads them, lines of input, and check its exit code and its output (what it prints) or its error output (what it prints to stderr).

- **A command runs in a folder of its own for each scenario**, unless its `dir` says otherwise, so parallel scenarios never write over each other's files. The folder is removed when the scenario passes and kept when it fails.
- **A program named by a relative path** (`bin/parcels`) is found from the directory of axx.yaml; a bare name (`psql`) on the PATH.
- **The checks look at the command's last run** in the scenario. A command that exits with a failing code does not fail the step that runs it: check its exit code. Only a command that cannot start, or runs past its timeout, fails the run step, and it shows what the command printed.
- **Secrets stay secret:** `${env:..}` values in the properties, the arguments and the input are masked in logs, attachments and failures.
- **Stopping:** a command that runs past its timeout is stopped with everything it started. A command that runs a process elsewhere, such as `docker compose exec`, stops, but the process it started may run on.

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

## Steps

### `cli.command`

```gherkin
Given the {word} command 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 a command under a name: the program and its first arguments, where it runs, its environment and its timeout.

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

| Property | Takes | Default |
|---|---|---|
| `command` | the program and its first arguments, split like a service's `command` in axx.yaml: double quotes group words, and there is no shell. A relative path to the program is from the directory of axx.yaml | _required_ |
| `dir` | the folder it runs in, relative to the directory of axx.yaml | `a folder of the scenario's own` |
| `env.<NAME>` | an environment variable, set over Axx's own environment | |
| `timeout` | how long a run may take before the command is stopped, like `30s` | `1m` |

Values are expanded (`${env:..}`, `${sys:..}`); what `${env:..}` references expand to is masked everywhere.

**Example:**

```gherkin
Given the admin command with the following properties:
  | command | docker compose exec -T app parcels admin |
  | dir     | ../infra                                 |
```

_Since 0.1.5._

### `cli.run`

```gherkin
When the {word} command is run[[ with {string}]]
```

Run the command, with more arguments after its own, split the same way. The step waits for the command to end; it fails only when the command cannot start or runs past its timeout.

| 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:

- `the {word} command is run`
- `the {word} command is run with {string}`

**Example:**

```gherkin
When the admin command is run with 'label PX-ADM-6101'
```

_Since 0.1.5._

### `cli.run.input`

```gherkin
When the {word} command is run with[[ {string} and]] the input:
  """
  ...
  """
```

Run the command with the doc string as its input (stdin), and more arguments if the step names them.

| 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:

- `the {word} command is run with the input:`
- `the {word} command is run with {string} and the input:`

**Example:**

```gherkin
When the admin command is run with 'cancel -' and the input:
  """
  PX-ADM-6103
  PX-ADM-6104
  """
```

_Since 0.1.5._

### `cli.exit`

```gherkin
Then the {word} command's exit code is {int}
```

Check the exit code of the command's last run.

| Parameter | Takes | For example |
|---|---|---|
| `{word}` | one word, with no spaces | `parcels`, `PX-4101` |
| `{int}` | a whole number | `200`, `3` |

**Example:**

```gherkin
Then the admin command's exit code is 0
```

_Since 0.1.5._

### `cli.output.contains`

```gherkin
Then the {word} command's[[ error]] output contains {string}
```

Check that the command's output (or its error output) contains the text. Runs of spaces and line breaks count as one space, in both.

| 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:

- `the {word} command's output contains {string}`
- `the {word} command's error output contains {string}`

**Example:**

```gherkin
Then the admin command's error output contains 'PX-ADM-6102 is DISPATCHED'
```

_Since 0.1.5._

### `cli.output.line`

```gherkin
Then the {word} command's[[ error]] output has a line matching {string}
```

Check that a line of the command's output (or its error output) matches the regular expression (Java syntax), anywhere in the line; `^` and `$` are the line's start and end.

| 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:

- `the {word} command's output has a line matching {string}`
- `the {word} command's error output has a line matching {string}`

**Example:**

```gherkin
Then the admin command's output has a line matching 'cancelled PX-ADM-610[34]'
```

_Since 0.1.5._

### `cli.output.is`

```gherkin
Then the {word} command's[[ error]] output is:
  """
  ...
  """
```

Check the command's output (or its error output) is exactly the doc string. Windows line breaks count as plain ones, and so does one line break at the end.

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

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

- `the {word} command's output is:`
- `the {word} command's error output is:`

**Example:**

```gherkin
Then the admin command's output is:
  """
  cancelled PX-ADM-6103
  cancelled PX-ADM-6104
  """
```

_Since 0.1.5._

### `cli.output.empty`

```gherkin
Then the {word} command's[[ error]] output is empty
```

Check that the command printed nothing at all to its output (or its error output).

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

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

- `the {word} command's output is empty`
- `the {word} command's error output is empty`

**Example:**

```gherkin
Then the admin command's error output is empty
```

_Since 0.1.5._

### `cli.output.properties`

```gherkin
Then the {word} command's output 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 the JSON the command printed: each row is a path into it (a field name, a dotted path or a JSONPath) and the value it has.

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

Values compare as text: `null` is null and `undefined` is absent.

**Example:**

```gherkin
Then the admin command's output has the following properties:
  | shop                   | kestrel-books |
  | parcels[0].reference   | PX-ADM-6105   |
  | parcels[0].serviceLevel | EXPRESS       |
```

_Since 0.1.5._

### `cli.output.identical`

```gherkin
Then the {word} command's output is identical to the {filepath} file
```

Check that the command's output is, byte for byte, the file of the project.

| Parameter | Takes | For example |
|---|---|---|
| `{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` |

**Example:**

```gherkin
Then the admin command's output is identical to the labels/PX-ADM-6101.zpl file
```

_Since 0.1.5._
