checkmate-cli 0.4.1

Checkmate - API Testing Framework CLI
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
# Checkmate

**AI-native API testing framework with YAML specs and git-connected history.**

Checkmate makes API testing simple for both humans and AI agents. Write declarative test specs in YAML, run assertions with Clove queries, and track results with full git context.

## Quick Start

```bash
# Build from source
cargo build --release

# Initialize in your project
cm init --url http://localhost:8080

# Create a test spec
cat > .checkmate/tests/health.yaml << 'EOF'
name: "Health Check"
tests:
  api_responds:
    endpoint: /health
    assertions:
      - query: "$[doc][status]"
        expect: "ok"
EOF

# Run tests
cm test run
```

## Features

- **YAML Test Specs**: Declarative, readable test definitions
- **Clove Query Language**: Powerful JSON assertions with `$[field]` and `$[@prev]` scopes
- **API Version Diff**: Compare responses across endpoints to detect structural changes (`cm diff run`)
- **Variable Extraction**: Extract values from responses for multi-step auth flows
- **Universal Env Vars**: `${VAR}` expansion in endpoints, headers, bodies, and query params
- **Git-Connected History**: Every test run captures commit, branch, and dirty status
- **Auto-Discovery**: Run `cm test run` from any subdirectory
- **Configuration Hierarchy**: Defaults → User config → Project config → Environment
- **Hook System**: Run custom scripts on test lifecycle events
- **AI-Native**: Built-in documentation, Claude plugin, and agent-friendly CLI

## Installation

### From Source

```bash
git clone https://github.com/your-org/checkmate
cd checkmate
cargo build --release
# Binary at target/release/cm
```

### Add to PATH

```bash
export PATH="$PATH:/path/to/checkmate/target/release"
```

## Initialization

### Standard Mode

```bash
cm init --url http://localhost:8080
```

Creates `.checkmate/` directory structure:
```
.checkmate/
├── config.toml    # Project configuration
├── tests/         # Test specifications
├── runs/          # Test run history (JSONL)
└── hooks/         # Custom hook scripts
```

### Stealth Mode

For personal use on shared projects without committing Checkmate files:

```bash
cm init --url http://localhost:8080 --stealth
```

This adds `.checkmate/` to `.git/info/exclude`, keeping it local to your machine.

### Interactive vs Non-Interactive

- **Terminal (TTY)**: `cm init` prompts for base URL interactively
- **Non-TTY (agents)**: Requires `--url` flag with clear error message

## Test Specifications

Test specs are YAML files in `.checkmate/tests/`:

```yaml
name: "User API Tests"
description: "Test user CRUD operations"

env:
  base_url: "http://localhost:8080"  # Optional, uses config if not set
  timeout_ms: 5000

requests:
  create_user:
    body:
      name: "Test User"
      email: "test@example.com"
    headers:
      Authorization: "Bearer ${TOKEN}"  # ${VAR} works in headers, bodies, endpoints, query params

tests:
  user_creation:
    description: "Create a new user"
    endpoint: /api/users
    method: POST
    requests: [create_user]
    expect_status: 201
    assertions:
      - query: "$[id]"
        expect_type: string
        message: "User ID should be returned"
      - query: "$[name]"
        expect: "Test User"
```

### Assertion Types

```yaml
assertions:
  # Exact value match
  - query: "$[doc][status]"
    expect: "active"

  # Type checking
  - query: "$[doc][count]"
    expect_type: number  # string, number, boolean, array, object, null

  # Existence check
  - query: "$[doc][field]?"
    expect: true

  # Numeric comparisons
  - query: "$[doc][count]"
    expect_gte: 1
    expect_lte: 100

  # Array operations
  - query: "$[doc][items].length()"
    expect_gte: 1

  # Compare to previous response
  - query: "$[doc][counter]"
    expect_gt: "$[@prev][counter]"
```

### Multi-Request Tests

For testing stateful behavior (counters, rate limits, sessions):

```yaml
tests:
  counter_increments:
    description: "Counter should increment on each request"
    endpoint: /api/counter
    requests: [increment, increment, increment]
    skip_first: true  # Don't assert on baseline request
    assertions:
      - query: "$[doc][count]"
        expect_gte: 1
      - query: "$[@prev][count]"
        expect_lt: "$[doc][count]"
        message: "Count should increase"
```

### Variable Extraction

Extract values from responses and inject them into subsequent requests using `extract:` and `{{var}}`:

```yaml
requests:
  login:
    body:
      username: "${USERNAME}"
      password: "${PASSWORD}"
    extract:
      auth_token: "$[token]"
      user_id: "$[user][id]"

  get_profile:
    headers:
      Authorization: "Bearer {{auth_token}}"

tests:
  auth_flow:
    endpoint: /api/login
    method: POST
    requests: [login, get_profile]
    assertions:
      - query: "$[name]"
        expect_type: string
```

`${VAR}` expands environment variables. `{{var}}` injects values extracted from previous responses.

## API Version Diff

Compare responses across API versions to detect structural changes. Diffs use the same YAML spec files and discovery as tests, and runs are recorded to history.

```yaml
env:
  base_url: "http://localhost:8080"

requests:
  login:
    body: { user: "${USER}", pass: "${PASS}" }
    extract:
      token: "$[access_token]"
  auth_get:
    headers:
      Authorization: "Bearer {{token}}"
  v1_query:
    body: { format: "legacy" }
  v2_query:
    body: { format: "modern" }

diffs:
  user_endpoint_versions:
    description: "Compare v1 vs v2 user listing"
    setup:
      - request: login
        endpoint: /auth/login
    method: GET
    endpoints:
      - name: v1
        path: /api/v1/users
      - name: v2
        path: /api/v2/users
    requests:
      - auth_get                      # Sent to all endpoints
      - name: v1_query
        scope: [v1]                   # Only sent to v1
      - name: v2_query
        scope: [v2]                   # Only sent to v2
    assertions:
      - query: "$[additions]"
        expect: 0
        message: "No new fields expected"
      - query: "$[changes].any(@[change_type] == 'removed')"
        expect: false
        message: "No fields should be removed"
```

### Setup (Authentication)

Run auth/login requests before endpoint iteration. Extracted variables persist into diff requests:

```yaml
setup:
  - request: login          # Request name from the requests map
    endpoint: /auth/login   # Own endpoint (not from the endpoints list)
    method: POST            # Defaults to POST
```

### Named Endpoints

Give endpoints a name for use with scoped requests and assertions. Simple strings and named objects can be mixed:

```yaml
endpoints:
  - /api/v1/users                # name defaults to the path
  - name: v2
    path: /api/v2/users
```

### Scoped Requests

Different API versions often need different payloads. Scope requests to specific endpoints by name:

```yaml
requests:
  - common_request                # Unscoped — sent to all endpoints
  - name: v1_only
    scope: [v1]                   # Only sent to the v1 endpoint
  - name: v2_only
    scope: [v2]                   # Only sent to the v2 endpoint
```

### Scoped Assertions

When comparing 3+ endpoints, scope assertions to specific pairs:

```yaml
assertions:
  - query: "$[removals]"
    expect: 0                     # Applies to all pairs
  - query: "$[additions]"
    expect: 0
    scope: [v1, v2]              # Only for the v1↔v2 comparison
```

### Diff Result Document

Assertions query against a diff result with this structure:

```json
{
  "changes": [
    { "change_type": "added", "path": "total_pages", "to": 1 },
    { "change_type": "removed", "path": "legacy_flag", "from": true },
    { "change_type": "type_changed", "path": "count", "from": "string", "to": "number" },
    { "change_type": "value_changed", "path": "role", "from": "user", "to": "admin" }
  ],
  "additions": 1,
  "removals": 1,
  "type_changes": 1,
  "value_changes": 1,
  "base_response": { ... },
  "target_response": { ... }
}
```

### Running Diffs

```bash
# Run all diffs
cm diff run

# Run specific spec
cm diff run api_versions

# Run specific diff by name
cm diff run --diff user_endpoint_versions

# List available diffs
cm diff list
```

**Output modes**: In a TTY (interactive terminal), diffs display with colored structural output. In non-TTY (piped to scripts/agents), diffs output structured JSON. Diff runs are recorded to history — view with `cm history`.

## Running Tests

### Run All Tests

```bash
cm test run
```

Auto-discovers all `.yaml` files in `.checkmate/tests/`.

### Run Specific Specs

```bash
# By name (resolves to .checkmate/tests/users.yaml)
cm test run users

# By path
cm test run ./custom/path/test.yaml

# Multiple specs
cm test run users orders payments
```

### Run Specific Test

```bash
cm test run users --test user_creation
```

### Verbose Output

```bash
cm test run -v
```

### Other Commands

```bash
# List all tests
cm test list

# Validate specs without running
cm test validate
```

## Configuration

### Hierarchy (lowest to highest priority)

1. **Built-in defaults**
2. **User config**: `~/.config/checkmate/config.toml`
3. **Project config**: `.checkmate/config.toml`
4. **Environment variables**: `CM_*`

### Project Configuration

`.checkmate/config.toml`:
```toml
[env]
base_url = "http://localhost:8080"
timeout_ms = 5000

[defaults]
fail_fast = false
expect_status = 200
```

### Environment Variables

Override any setting with `CM_` prefix and double underscore for nesting:

```bash
CM_ENV__BASE_URL="http://staging:8080" cm test run
CM_ENV__TIMEOUT_MS=10000 cm test run
CM_DEFAULTS__FAIL_FAST=true cm test run
```

### View Configuration

```bash
# Show current config
cm config show

# Show as JSON
cm config show --json

# Show config sources
cm config sources
```

## Test History

Every test run is recorded with git context in `.checkmate/runs/runs.jsonl`.

### View History

```bash
# Recent runs
cm history

# Filter by spec
cm history --spec users

# Filter by commit
cm history --commit abc123

# More results
cm history -n 20
```

### View Run Details

```bash
cm show cm-run-a3f
```

Output:
```
Run: cm-run-a3f
Time: 2024-01-15T10:30:00Z
Spec: users.yaml
Results: 5/5 passed, 0 failed, 0 errors (1234ms)

Git Context:
  Commit: abc1234
  Branch: feature/user-api
  Dirty: no
  Message: Add user validation
```

### History Format (JSONL)

Each line in `runs.jsonl` is a complete run record:
```json
{
  "id": "cm-run-a3f",
  "timestamp": "2024-01-15T10:30:00Z",
  "git": {
    "commit": "abc1234",
    "branch": "feature/user-api",
    "dirty": false,
    "message": "Add user validation"
  },
  "summary": {
    "total": 5,
    "passed": 5,
    "failed": 0,
    "errors": 0,
    "duration_ms": 1234
  },
  "spec_file": "users.yaml"
}
```

## Hooks

Run custom scripts at test lifecycle events. Place executable scripts in `.checkmate/hooks/`:

| Hook | When | Use Case |
|------|------|----------|
| `pre_run` | Before tests start | Setup, notifications |
| `post_run` | After tests complete | Cleanup, reporting |
| `on_pass` | All tests passed | Success notifications |
| `on_fail` | Any test failed | Alerts, CI integration |

### Environment Variables

Hooks receive context via environment:

| Variable | Description |
|----------|-------------|
| `CM_RUN_ID` | Run identifier (e.g., `cm-run-a3f`) |
| `CM_SPEC` | Spec file name |
| `CM_TOTAL` | Total test count |
| `CM_PASSED` | Passed count |
| `CM_FAILED` | Failed count |
| `CM_ERRORS` | Error count |
| `CM_DURATION_MS` | Duration in milliseconds |

### Example Hook

`.checkmate/hooks/on_fail`:
```bash
#!/bin/bash
echo "Tests failed! Run: $CM_RUN_ID"
echo "Results: $CM_PASSED/$CM_TOTAL passed"
# Send Slack notification, create issue, etc.
```

Make executable: `chmod +x .checkmate/hooks/on_fail`

## Clove Query Language

Checkmate uses Clove for JSON assertions. Key features:

### Scopes

- `$[doc]` - Current response body
- `$[@prev]` - Previous response (for multi-request tests)

### Navigation

```
$[doc][user][name]           # Nested object
$[doc][items][0]             # Array index
$[doc][items][0][id]         # Nested in array
```

### Methods

```
$[doc][items].length()       # Array length
$[doc][name].upper()         # String uppercase
$[doc][price].round()        # Number rounding
```

### Existence Check

```
$[doc][optional]?            # Returns true/false
```

For full documentation: `cm doc clove`

## CLI Reference

### Core Commands

| Command | Description |
|---------|-------------|
| `cm init` | Initialize Checkmate in current directory |
| `cm test run` | Run test specifications |
| `cm test list` | List available tests |
| `cm test validate` | Validate specs without running |
| `cm diff run` | Run diff specifications |
| `cm diff list` | List available diffs |
| `cm history` | Show test run history |
| `cm show <id>` | Show details of a specific run |
| `cm config show` | Display current configuration |

### Documentation

| Command | Description |
|---------|-------------|
| `cm docs` | Full documentation overview |
| `cm doc <category>` | Category-specific docs |
| `cm onboard` | Quick-start guide for AI agents |
| `cm prime` | Output project context (for hooks) |

### Categories for `cm doc`

- `assertions` - Assertion types and examples
- `requests` - Request definition and variable extraction
- `env` - Environment variables and configuration
- `diff` - API version diff comparison
- `scopes` - Clove query scopes
- `examples` - Complete examples
- `cli` - CLI reference

## AI Agent Integration

### Claude Plugin

Install the Checkmate Claude plugin to get automatic context injection:

```bash
# Plugin auto-runs 'cm prime' on session start
# Provides: test specs, recent runs, quick reference
```

### Agent Onboarding

```bash
cm onboard
```

Outputs a condensed guide for AI agents covering:
- Test spec format
- Running tests
- Interpreting results
- Common patterns

### AGENTS.md

Add to your project's `AGENTS.md`:
```markdown
## API Testing

This project uses Checkmate for API testing.

- Run tests: `cm test run`
- Run diffs: `cm diff run`
- View history: `cm history`
- Documentation: `cm docs`
```

## Project Structure

```
your-project/
├── .checkmate/
│   ├── config.toml      # Project settings
│   ├── tests/           # Test specifications
│   │   ├── users.yaml
│   │   ├── orders.yaml
│   │   └── ...
│   ├── runs/            # Test history
│   │   ├── runs.jsonl   # Run records
│   │   └── .gitignore   # Ignores JSONL by default
│   └── hooks/           # Lifecycle scripts
│       ├── pre_run
│       ├── on_pass
│       └── on_fail
└── ...
```

## Best Practices

1. **One concern per spec file** - Group related tests together
2. **Use descriptive test names** - `user_creation_with_valid_data` not `test1`
3. **Include failure messages** - Help debugging when assertions fail
4. **Commit test specs** - Track them alongside your code
5. **Use stealth mode** - For personal testing on shared projects
6. **Review history** - Track test stability across commits

## Troubleshooting

### "No .checkmate/ found"

Run `cm init --url <your-api-url>` in your project root.

### Tests not discovered

Ensure specs are in `.checkmate/tests/` with `.yaml` or `.yml` extension.

### Config not applied

Check priority: env vars > project config > user config > defaults.
Use `cm config sources` to see what's being loaded.

### Hooks not running

1. Check file exists in `.checkmate/hooks/`
2. Ensure it's executable: `chmod +x .checkmate/hooks/on_pass`
3. Check script has correct shebang: `#!/bin/bash`

## License

MIT