From gavel-skills
Create and run gavel fixture-based tests using markdown files with command blocks, tables, and CEL assertions
How this skill is triggered — by the user, by Claude, or both
Slash command
/gavel-skills:gavel-fixture-testerThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Create fixture-based tests for CLI commands using gavel's markdown fixture format. Fixtures define test cases as markdown files with YAML front-matter, command blocks, tables, and CEL validation expressions.
Create fixture-based tests for CLI commands using gavel's markdown fixture format. Fixtures define test cases as markdown files with YAML front-matter, command blocks, tables, and CEL validation expressions.
This file is itself a valid fixture. Run it to verify all examples parse and execute:
gavel fixtures .claude/skills/gavel-fixtures.md
Use gavel fixtures when:
Use regular Go tests when:
Every fixture file can start with --- delimited YAML front-matter:
---
build: go build -o myapp # Run once before all tests
exec: ./myapp # Default executable (default: bash)
args: [--verbose] # Default arguments
env: # Environment variables
LOG_LEVEL: debug
cwd: ./testdir # Working directory (resolved relative to fixture file location)
terminal: pty # Pseudo-terminal mode (merges stdout/stderr)
codeBlocks: [bash] # Languages to execute (default: [bash])
files: "**/*.go" # Glob: replicate tests per matching file
timeout: 30s # Total timeout
os: linux # OS constraint ("!darwin" = skip on macOS)
arch: amd64 # Architecture constraint
skip: "test -z $CI" # Bash command; exit 0 = skip fixture
---
Each row is a test. Column headers map to fixture fields. This is the preferred format — use it whenever tests share the same executable and differ only in arguments or expected output.
Supported column headers (case-insensitive):
Input: name, cli/command/exec, args, cwd, query, terminal/term, os, arch, skip
Expectations: exit code, expected output/output, expected error/error, format, count, cel/validation/expr
Unrecognized columns become custom template variables, usable in exec, args, and build fields via Go template syntax ({{.colName}}). They are also accessible in CEL via expectations.Properties["col"].
Custom keys in YAML frontmatter provide global defaults for template variables. Per-row column values override frontmatter defaults. Empty cells fall through to the frontmatter default.
Priority (highest to lowest): file expansion vars > table column values > frontmatter metadata
Use sections (## Section Name) to group related tables within a file.
The most common pattern: frontmatter defines a command template, table columns fill in the variables per row.
---
exec: bash
args: ["-c", "curl {{.flags}} {{.baseUrl}}{{.path}}"]
baseUrl: https://httpbin.flanksource.com
flags: "-s"
---
| Name | path | CEL Validation |
|---|---|---|
| get endpoint | /get | json.url.contains("httpbin") |
| get ip | /ip | json.origin != "" |
| get with headers | /get | stdout.contains("HTTP") |
| Name | Command | Exit Code | CEL Validation |
|---|---|---|---|
| Echo stdout | echo hello world | 0 | stdout.contains("hello") |
| Exit code zero | echo ok | 0 | exitCode == 0 |
| Non-zero exit | exit 1 | 1 | exitCode == 1 |
| Stderr output | echo "err msg" >&2 | 0 | stderr.contains("err msg") |
| Multiword contains | echo "the quick brown fox" | 0 | stdout.contains("quick") && stdout.contains("fox") |
| Negation check | echo "success" | 0 | !stdout.contains("error") |
| Regex match | echo "version 1.2.3" | 0 | stdout.matches("version [0-9]+\.[0-9]+\.[0-9]+") |
| Name | Command | Exit Code | CEL Validation |
|---|---|---|---|
| JSON object | echo '{"name":"test","count":3}' | 0 | json.name == "test" && json.count == 3.0 |
| JSON array | echo '[{"id":1},{"id":2}]' | 0 | size(json) == 2 && json[0].id == 1.0 |
| JSON nested | echo '{"data":{"items":["a","b"]}}' | 0 | size(json.data.items) == 2 |
| Name | Command | Exit Code | CEL Validation |
|---|---|---|---|
| Detect color codes | printf '\033[31mred text\033[0m' | 0 | ansi.has_color == true && ansi.has_any == true |
| Plain text no ANSI | echo "no colors here" | 0 | ansi.has_any == false && ansi.has_color == false |
| Cursor movement | printf '\033[2Amoved up' | 0 | ansi.has_updates == true |
Use ### command: <test name> headings for tests that need multi-line scripts, setup/teardown, or per-test YAML config.
YAML config block fields: cwd, exitCode, env, timeout, terminal, os, arch, skip
Validation bullet prefixes:
cel: <expr> — Raw CEL expressioncontains: <text> — Converts to stdout.contains("<text>")regex: <pattern> — Converts to stdout.matches("<pattern>")not: contains: <text> — Converts to !stdout.contains("<text>")not: <expr> — Converts to !(<expr>)Multiple validations are joined with &&.
exitCode: 0
TMPFILE=$(mktemp)
echo "hello from tempfile" > "$TMPFILE"
cat "$TMPFILE"
rm -f "$TMPFILE"
export MY_VAR="fixture_value"
echo "var is $MY_VAR"
exitCode: 1
echo "expected failure" >&2
exit 1
echo "line1: alpha"
echo "line2: beta"
echo "line3: gamma"
exitCode: 1
exit 1
exitCode: 0
timeout: 10
echo "completed within timeout"
Executable code blocks outside command: headings are auto-detected. Test name comes from the nearest preceding heading.
echo "standalone block detected"
Override YAML block values on the code fence info string: exitCode=N, timeout=N (seconds).
| Variable | Type | Description |
|---|---|---|
stdout / output | string | Process stdout |
stderr | string | Process stderr |
exitCode | int | Process exit code |
json | any | Auto-parsed JSON (when stdout starts with { or [) |
name | string | Test name |
sourceDir | string | Directory containing fixture file |
query | string | Query string |
expectations | object | Expected values |
executablePath | string | Path to gavel binary |
workDir | string | Working directory |
GIT_ROOT_DIR | string | Nearest parent with .git (also env var) |
GO_ROOT_DIR | string | Nearest parent with go.mod (also env var) |
ROOT_DIR | string | GIT_ROOT_DIR > GO_ROOT_DIR > workDir (also env var) |
ansi.has_color | bool | Output contains ANSI color codes (foreground/background) |
ansi.has_any | bool | Output contains any ANSI escape sequences |
ansi.has_updates | bool | Output contains cursor movement/screen update codes |
File expansion variables (when files: is set): file, filename, dir, absfile, absdir, basename, ext
Built-in: contains(), startsWith(), endsWith(), matches(regex), size(), all(), exists(), filter()
Extended (gomplate): strings.*, math.*, regexp.*, conv.*, coll.*, data.JSON, data.YAML, file.*, time.*
exec, build, and args support Go template syntax:
exec: bash
args: ["-c", "curl {{.flags}} {{.baseUrl}}{{.path}}"]
Sources (highest to lowest priority):
files: is set): .file, .filename, .dir, .absfile, .absdir, .basename, .ext.path, .flags).baseUrl).executablePath, .workDir, .name, .query, .GIT_ROOT_DIR, .GO_ROOT_DIR, .ROOT_DIR| Language | Executor |
|---|---|
| bash, sh, shell | bash -c |
| python, py, python3 | python -c |
| typescript, ts | ts-node -e |
| javascript, js | node -e |
| pwsh, powershell | pwsh -Command |
| go | go (direct) |
Non-executable (config): yaml, frontmatter, json
gavel fixtures <fixture-files...>
gavel fixtures tests.md
gavel fixtures fixtures/**/*.md
gavel fixtures -v tests.md # Verbose (stderr on pass, stdout+stderr on fail)
gavel fixtures -vv tests.md # More verbose (stdout+stderr always)
gavel fixtures --no-progress tests.md # Disable progress display
gavel fixtures --json tests.md # JSON output
gavel fixtures --json tests.md 2>/dev/null # JSON only, no logs
Tests run in parallel (2-minute timeout per test, 5-minute for build).
The working directory for each test is resolved with the following priority (highest wins):
cwd in a frontmatter code block, or cwd/dir/working directory table columncwd in the YAML front-matter at the top of the fixture file--path flag)Relative paths are resolved from SourceDir (the fixture file's directory). Absolute paths are used directly.
Environment variables set via env: in file-level front-matter or per-test frontmatter blocks are passed to the executed command.
Example — file-level CWD with per-test override:
---
cwd: ./src # default for all tests in this file
env:
NODE_ENV: test
---
| Name | CWD | Command | CEL |
|---|---|---|---|
| test from src | ls | stdout.contains("main.go") | |
| test from root | .. | ls | stdout.contains("src") |
In the table above, "test from src" inherits ./src from front-matter. "test from root" overrides to .. (resolved as <fixture-dir>/..).
Read the command or executable being tested. Identify:
Place fixture files alongside the code they test, typically in fixtures/testdata/ or a testdata/ directory near the relevant package.
File naming: <feature-name>.md using kebab-case.
Prefer CEL expressions for flexible validation:
stdout.contains("text") for substring checksjson.field == value when testing JSON outputexitCode == N for exit code checks!stdout.contains("text") for negative assertions&& for multiple conditionsgavel fixtures <your-fixture-file>.md
Fix failures and iterate until all tests pass.
args with {{.col}} placeholders, and vary inputs per row via columns### command: <name>) when tests need multi-line scripts, setup/teardown, or per-test YAML config that cannot be expressed as a single templated command## Section Name) to group related tables within a filecodeBlocks: [bash] in front-matter when mixing executable and non-executable code blocksexitCode when expecting non-zero{ or [ for auto-parsing2>/dev/null to suppress log output from stdout: gavel cmd --json 2>/dev/nullcontains: shorthand over cel: stdout.contains(...) for simple substring checks (command blocks only — tables use CEL Validation column)os: and arch: front-matter constraintsnpx claudepluginhub flanksource/gavel --plugin gavel-skillsProvides patterns and guidance for writing shell script tests using Bats (Bash Automated Testing System), including TDD, fixtures, and CI integration.
Generates and reviews production-ready Go tests with table-driven patterns, testify suites, fuzzing, goroutine leak detection, and snapshot testing. Use when writing, reviewing, or debugging Go tests.
Provides patterns and best practices for testing shell scripts with Bats (Bash Automated Testing System). Use for unit testing, TDD, and CI/CD pipeline integration.