checkmate-cli 0.4.1

Checkmate - API Testing Framework CLI
# Clove Query Language

Clove is the JSON query language used for assertions in Checkmate.

## Overview

Clove expressions navigate and transform JSON data:

```
$[status]                  →  Access field at root
$[user][name]              →  Access nested field (user.name)
$[user][profile][bio]      →  Deep nesting supported
$[items].length()          →  Call method on value
$[items][0][id]            →  Array index then field
$[@prev][count]            →  Previous response (special scope)
```

## Response Access

### `$` - Response Root

The `$` symbol represents the response body root. Access fields directly:

```yaml
# Response: {"status": "ok", "count": 5}
assertions:
  - query: "$[status]"
    expect: "ok"
  - query: "$[count]"
    expect: 5
```

### Nested Access

Chain brackets for nested fields:

```yaml
# Response: {"user": {"profile": {"name": "Alice"}}}
assertions:
  - query: "$[user][profile][name]"
    expect: "Alice"
```

### Array Responses

When the API returns an array at root level:

```yaml
# Response: [{"id": "a"}, {"id": "b"}]
assertions:
  - query: "$.length()"
    expect: 2
  - query: "$[0][id]"
    expect: "a"
  - query: "$[1][id]"
    expect: "b"
```

### `$[@prev]` - Previous Response

In multi-request tests, access the previous response using the `@prev` scope:

```yaml
tests:
  counter_test:
    requests: [increment, increment]
    assertions:
      - query: "$[count]"
        expect_gt: "$[@prev][count]"
```

Note: The `@` prefix denotes special scopes. `[@prev]` is a scope selector, not a field name.

---

## Navigation

### Object Fields

Access fields with bracket notation:

```yaml
# Response: {"user": {"name": "John", "profile": {"bio": "Dev"}}}
assertions:
  - query: "$[user]"                    # {"name": "John", ...}
  - query: "$[user][name]"              # "John"
  - query: "$[user][profile][bio]"      # "Dev"
```

### Array Elements

Access by index (0-based):

```yaml
# Response: {"items": [{"id": 1}, {"id": 2}]}
assertions:
  - query: "$[items][0]"                # First element
  - query: "$[items][1]"                # Second element
  - query: "$[items][0][id]"            # First item's id
```

### Existence Check

Append `?` to check if field exists (returns true/false):

```yaml
- query: "$[optional_field]?"
  expect: true

- query: "$[user][middle_name]?"
  expect: false
```

---

## Methods

### Array Methods

| Method | Description | Example |
|--------|-------------|---------|
| `.length()` | Array length | `$[items].length()` |
| `.first()` | First element | `$[items].first()` |
| `.last()` | Last element | `$[items].last()` |
| `.reverse()` | Reverse array | `$[items].reverse()` |
| `.unique()` | Remove duplicates | `$[tags].unique()` |
| `.flatten()` | Flatten nested arrays | `$[matrix].flatten()` |

### String Methods

| Method | Description | Example |
|--------|-------------|---------|
| `.length()` | String length | `$[name].length()` |
| `.upper()` | Uppercase | `$[code].upper()` |
| `.lower()` | Lowercase | `$[email].lower()` |
| `.trim()` | Remove whitespace | `$[input].trim()` |
| `.split(sep)` | Split string | `$[tags].split(",")` |

### Number Methods

| Method | Description | Example |
|--------|-------------|---------|
| `.round()` | Round to integer | `$[price].round()` |
| `.floor()` | Round down | `$[rating].floor()` |
| `.ceil()` | Round up | `$[score].ceil()` |
| `.abs()` | Absolute value | `$[delta].abs()` |

### Object Methods

| Method | Description | Example |
|--------|-------------|---------|
| `.keys()` | Get keys | `$[config].keys()` |
| `.values()` | Get values | `$[config].values()` |

---

## Chaining

Chain multiple operations:

```
$[items].first()[name].upper()
$[tags].unique().length()
$[users].last()[email].lower()
```

---

## Common Response Patterns

### Object Response (most common)

```yaml
# POST /api/users returns: {"id": "abc", "name": "John", "created_at": "..."}
assertions:
  - query: "$[id]"
    expect_type: string
  - query: "$[name]"
    expect: "John"
  - query: "$[created_at]?"
    expect: true
```

### Array Response

```yaml
# GET /api/users returns: [{"id": "a"}, {"id": "b"}]
assertions:
  - query: "$.length()"
    expect_gte: 1
  - query: "$[0][id]"
    expect_type: string
```

### Nested Data Response

```yaml
# Response: {"data": {"user": {"name": "John"}}, "meta": {"page": 1}}
assertions:
  - query: "$[data][user][name]"
    expect: "John"
  - query: "$[meta][page]"
    expect: 1
```

### Wrapped Array Response

```yaml
# Response: {"items": [{"id": 1}, {"id": 2}], "total": 2}
assertions:
  - query: "$[items].length()"
    expect: 2
  - query: "$[total]"
    expect: 2
  - query: "$[items][0][id]"
    expect: 1
```

---

## Using in Assertions

### Basic Comparison

```yaml
- query: "$[status]"
  expect: "active"
```

### Type Check

```yaml
- query: "$[items]"
  expect_type: array
```

### Length Check

```yaml
- query: "$[items].length()"
  expect_gte: 1
```

### Existence Check

```yaml
- query: "$[optional_field]?"
  expect: true
```

### Compare to Previous

```yaml
- query: "$[counter]"
  expect_gt: "$[@prev][counter]"
```

---

## CLI Testing

Test Clove expressions from command line:

```bash
# Test query against JSON data
cm clove check '$[name]' -i '{"name": "John"}'

# Array at root
cm clove check '$.length()' -i '[1, 2, 3]'

# Nested access
cm clove check '$[user][name]' -i '{"user": {"name": "Alice"}}'
```

---

## Error Handling

If a path doesn't exist:
- Without `?`: Returns error
- With `?`: Returns `false`

```yaml
# This will error if field missing
- query: "$[required_field]"
  expect_type: string

# This handles missing gracefully
- query: "$[optional_field]?"
  expect: true
```

---

## See Also

- [Assertions](ASSERTIONS.md) - Using queries in assertions
- [Test Specs](TEST_SPECS.md) - Complete spec format