#!/usr/bin/env bash # # RTK Smoke Test Suite # Exercises every command to catch regressions after merge. # Exit code: number of failures (0 = all green) # set -euo pipefail PASS=0 FAIL=0 SKIP=0 FAILURES=() # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m' CYAN='\033[0;36m' BOLD='\033[1m' NC='\033[0m' # ── Helpers ────────────────────────────────────────── assert_ok() { local name="$1" shift local output if output=$("$@" 2>&1); then PASS=$((PASS + 1)) printf " ${GREEN}PASS${NC} %s\n" "$name" else FAIL=$((FAIL + 1)) FAILURES+=("$name") printf " ${RED}FAIL${NC} %s\n" "$name" printf " cmd: %s\n" "$*" printf " out: %s\n" "$(echo "$output" | head -3)" fi } assert_contains() { local name="$1" local needle="$2" shift 2 local output if output=$("$@" 2>&1) && echo "$output" | grep -q "$needle"; then PASS=$((PASS + 1)) printf " ${GREEN}PASS${NC} %s\n" "$name" else FAIL=$((FAIL + 1)) FAILURES+=("$name") printf " ${RED}FAIL${NC} %s\n" "$name" printf " expected: '%s'\n" "$needle" printf " got: %s\n" "$(echo "$output" | head -3)" fi } assert_exit_ok() { local name="$1" shift if "$@" >/dev/null 2>&1; then PASS=$((PASS + 1)) printf " ${GREEN}PASS${NC} %s\n" "$name" else FAIL=$((FAIL + 1)) FAILURES+=("$name") printf " ${RED}FAIL${NC} %s\n" "$name" printf " cmd: %s\n" "$*" fi } assert_fails() { local name="$1" shift if "$@" >/dev/null 2>&1; then FAIL=$((FAIL + 1)) FAILURES+=("$name (expected failure, got success)") printf " ${RED}FAIL${NC} %s (expected failure)\n" "$name" else PASS=$((PASS + 1)) printf " ${GREEN}PASS${NC} %s\n" "$name" fi } assert_help() { local name="$1" shift assert_contains "$name --help" "Usage:" "$@" --help } skip_test() { local name="$1" local reason="$2" SKIP=$((SKIP + 1)) printf " ${YELLOW}SKIP${NC} %s (%s)\n" "$name" "$reason" } section() { printf "\n${BOLD}${CYAN}── %s ──${NC}\n" "$1" } # ── Preamble ───────────────────────────────────────── RTK=$(command -v rtk || echo "") if [[ -z "$RTK" ]]; then echo "rtk not found in PATH. Run: cargo install --path ." exit 1 fi printf "${BOLD}RTK Smoke Test Suite${NC}\n" printf "Binary: %s\n" "$RTK" printf "Version: %s\n" "$(rtk --version)" printf "Date: %s\n" "$(date '+%Y-%m-%d %H:%M')" # Need a git repo to test git commands if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then echo "Must run from inside a git repository." exit 1 fi REPO_ROOT=$(git rev-parse --show-toplevel) # ── 1. Version & Help ─────────────────────────────── section "Version & Help" assert_contains "rtk --version" "rtk" rtk --version assert_contains "rtk --help" "Usage:" rtk --help # ── 2. Ls ──────────────────────────────────────────── section "Ls" assert_ok "rtk ls ." rtk ls . assert_ok "rtk ls -la ." rtk ls -la . assert_ok "rtk ls -lh ." rtk ls -lh . assert_ok "rtk ls -l src/" rtk ls -l src/ assert_ok "rtk ls src/ -l (flag after)" rtk ls src/ -l assert_ok "rtk ls multi paths" rtk ls src/ scripts/ assert_contains "rtk ls -a shows hidden" ".git" rtk ls -a . assert_contains "rtk ls shows sizes" "K" rtk ls src/ assert_contains "rtk ls shows dirs with /" "/" rtk ls . # ── 2b. Tree ───────────────────────────────────────── section "Tree" if command -v tree >/dev/null 2>&1; then assert_ok "rtk tree ." rtk tree . assert_ok "rtk tree -L 2 ." rtk tree -L 2 . assert_ok "rtk tree -d -L 1 ." rtk tree -d -L 1 . assert_contains "rtk tree shows src/" "src" rtk tree -L 1 . else skip_test "rtk tree" "tree not installed" fi # ── 3. Read ────────────────────────────────────────── section "Read" assert_ok "rtk read Cargo.toml" rtk read Cargo.toml assert_ok "rtk read --level none Cargo.toml" rtk read --level none Cargo.toml assert_ok "rtk read --level aggressive Cargo.toml" rtk read --level aggressive Cargo.toml assert_ok "rtk read -n Cargo.toml" rtk read -n Cargo.toml assert_ok "rtk read --max-lines 5 Cargo.toml" rtk read --max-lines 5 Cargo.toml section "Read (stdin support)" assert_ok "rtk read stdin pipe" bash -c 'echo "fn main() {}" | rtk read -' # ── 4. Git ─────────────────────────────────────────── section "Git (existing)" assert_ok "rtk git status" rtk git status assert_ok "rtk git status --short" rtk git status --short assert_ok "rtk git status -s" rtk git status -s assert_ok "rtk git status --porcelain" rtk git status --porcelain assert_ok "rtk git log" rtk git log assert_ok "rtk git log -5" rtk git log -- -5 assert_ok "rtk git diff" rtk git diff assert_ok "rtk git diff --stat" rtk git diff --stat section "Git (new: branch, fetch, stash, worktree)" assert_ok "rtk git branch" rtk git branch assert_ok "rtk git fetch" rtk git fetch assert_ok "rtk git stash list" rtk git stash list assert_ok "rtk git worktree" rtk git worktree section "Git (passthrough: unsupported subcommands)" assert_ok "rtk git tag --list" rtk git tag --list assert_ok "rtk git remote -v" rtk git remote -v assert_ok "rtk git rev-parse HEAD" rtk git rev-parse HEAD # ── 5. GitHub CLI ──────────────────────────────────── section "GitHub CLI" if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then assert_ok "rtk gh pr list" rtk gh pr list assert_ok "rtk gh run list" rtk gh run list assert_ok "rtk gh issue list" rtk gh issue list # pr create/merge/diff/comment/edit are write ops, test help only assert_help "rtk gh" rtk gh else skip_test "gh commands" "gh not authenticated" fi # ── 6. Cargo ───────────────────────────────────────── section "Cargo (new)" assert_ok "rtk cargo build" rtk cargo build assert_ok "rtk cargo clippy" rtk cargo clippy # cargo test exits non-zero due to pre-existing failures; check output ignoring exit code output_cargo_test=$(rtk cargo test 2>&1 || true) if echo "$output_cargo_test" | grep -q "FAILURES\|test result:\|passed"; then PASS=$((PASS + 1)) printf " ${GREEN}PASS${NC} %s\n" "rtk cargo test" else FAIL=$((FAIL + 1)) FAILURES+=("rtk cargo test") printf " ${RED}FAIL${NC} %s\n" "rtk cargo test" printf " got: %s\n" "$(echo "$output_cargo_test" | head -3)" fi assert_help "rtk cargo" rtk cargo # ── 7. Curl ────────────────────────────────────────── section "Curl (new)" assert_contains "rtk curl JSON detect" "string" rtk curl https://mockhttp.org/json assert_ok "rtk curl plain text" rtk curl https://mockhttp.org/robots.txt assert_help "rtk curl" rtk curl # ── 8. Npm / Npx ──────────────────────────────────── section "Npm / Npx (new)" assert_help "rtk npm" rtk npm assert_help "rtk npx" rtk npx # ── 9. Pnpm ───────────────────────────────────────── section "Pnpm" assert_help "rtk pnpm" rtk pnpm assert_help "rtk pnpm build" rtk pnpm build assert_help "rtk pnpm typecheck" rtk pnpm typecheck if command -v pnpm >/dev/null 2>&1; then assert_ok "rtk pnpm help" rtk pnpm help fi # ── 10. Grep ───────────────────────────────────────── section "Grep" assert_ok "rtk grep pattern" rtk grep "pub fn" src/ assert_contains "rtk grep finds results" "pub fn" rtk grep "pub fn" src/ assert_ok "rtk grep with file type" rtk grep "pub fn" src/ -t rust section "Grep (extra args passthrough)" assert_ok "rtk grep -i case insensitive" rtk grep "fn" src/ -i assert_ok "rtk grep -A context lines" rtk grep "fn run" src/ -A 2 # ── 11. Find ───────────────────────────────────────── section "Find" assert_ok "rtk find *.rs" rtk find "*.rs" src/ assert_contains "rtk find shows files" ".rs" rtk find "*.rs" src/ # ── 12. Json ───────────────────────────────────────── section "Json" # Create temp JSON file for testing TMPJSON=$(mktemp /tmp/rtk-test-XXXXX.json) echo '{"name":"test","count":42,"items":[1,2,3]}' > "$TMPJSON" assert_ok "rtk json file" rtk json "$TMPJSON" assert_contains "rtk json shows schema" "string" rtk json "$TMPJSON" rm -f "$TMPJSON" # ── 13. Deps ───────────────────────────────────────── section "Deps" assert_ok "rtk deps ." rtk deps . assert_contains "rtk deps shows Cargo" "Cargo" rtk deps . # ── 14. Env ────────────────────────────────────────── section "Env" assert_ok "rtk env" rtk env assert_ok "rtk env --filter PATH" rtk env --filter PATH # ── 16. Log ────────────────────────────────────────── section "Log" TMPLOG=$(mktemp /tmp/rtk-log-XXXXX.log) for i in $(seq 1 20); do echo "[2025-01-01 12:00:00] INFO: repeated message" >> "$TMPLOG" done echo "[2025-01-01 12:00:01] ERROR: something failed" >> "$TMPLOG" assert_ok "rtk log file" rtk log "$TMPLOG" rm -f "$TMPLOG" # ── 17. Summary ────────────────────────────────────── section "Summary" assert_ok "rtk summary echo hello" rtk summary echo hello # ── 18. Err ────────────────────────────────────────── section "Err" assert_ok "rtk err echo ok" rtk err echo ok # ── 19. Test runner ────────────────────────────────── section "Test runner" assert_ok "rtk test echo ok" rtk test echo ok # ── 20. Gain ───────────────────────────────────────── section "Gain" assert_ok "rtk gain" rtk gain assert_ok "rtk gain --history" rtk gain --history # ── 21. Config & Init ──────────────────────────────── section "Config & Init" assert_ok "rtk config" rtk config assert_ok "rtk init --show" rtk init --show # ── 22. Wget ───────────────────────────────────────── section "Wget" if command -v wget >/dev/null 2>&1; then assert_ok "rtk wget stdout" rtk wget https://mockhttp.org/robots.txt -O else skip_test "rtk wget" "wget not installed" fi # ── 23. Tsc / Lint / Prettier / Next / Playwright ─── section "JS Tooling (help only, no project context)" assert_help "rtk tsc" rtk tsc assert_help "rtk lint" rtk lint assert_help "rtk prettier" rtk prettier assert_help "rtk next" rtk next assert_help "rtk playwright" rtk playwright # ── 24. Prisma ─────────────────────────────────────── section "Prisma (help only)" assert_help "rtk prisma" rtk prisma # ── 25. Vitest ─────────────────────────────────────── section "Vitest (help only)" assert_help "rtk vitest" rtk vitest # ── 26. Docker / Kubectl (help only) ──────────────── section "Docker / Kubectl (help only)" assert_help "rtk docker" rtk docker assert_help "rtk kubectl" rtk kubectl # ── 27. Python (conditional) ──────────────────────── section "Python (conditional)" if command -v pytest &>/dev/null; then assert_help "rtk pytest" rtk pytest --help else skip_test "rtk pytest" "pytest not installed" fi if command -v ruff &>/dev/null; then assert_help "rtk ruff" rtk ruff --help else skip_test "rtk ruff" "ruff not installed" fi if command -v pip &>/dev/null; then assert_help "rtk pip" rtk pip --help else skip_test "rtk pip" "pip not installed" fi # ── 28. Go (conditional) ──────────────────────────── section "Go (conditional)" if command -v go &>/dev/null; then assert_help "rtk go" rtk go --help assert_help "rtk go test" rtk go test -h assert_help "rtk go build" rtk go build -h assert_help "rtk go vet" rtk go vet -h else skip_test "rtk go" "go not installed" fi if command -v golangci-lint &>/dev/null; then assert_help "rtk golangci-lint" rtk golangci-lint --help else skip_test "rtk golangci-lint" "golangci-lint not installed" fi # ── 29. Graphite (conditional) ───────────────────── section "Graphite (conditional)" if command -v gt &>/dev/null; then assert_help "rtk gt" rtk gt --help assert_ok "rtk gt log short" rtk gt log short else skip_test "rtk gt" "gt not installed" fi # ── 30. Ruby (conditional) ────────────────────────── section "Ruby (conditional)" if command -v rspec &>/dev/null; then assert_help "rtk rspec" rtk rspec --help else skip_test "rtk rspec" "rspec not installed" fi if command -v rubocop &>/dev/null; then assert_help "rtk rubocop" rtk rubocop --help else skip_test "rtk rubocop" "rubocop not installed" fi if command -v rake &>/dev/null; then assert_help "rtk rake" rtk rake --help else skip_test "rtk rake" "rake not installed" fi # ── 31. Global flags ──────────────────────────────── section "Global flags" assert_ok "rtk -u ls ." rtk -u ls . assert_ok "rtk --skip-env npm --help" rtk --skip-env npm --help # ── 32. CcEconomics ───────────────────────────────── section "CcEconomics" assert_ok "rtk cc-economics" rtk cc-economics # ── 33. Learn ─────────────────────────────────────── section "Learn" assert_ok "rtk learn --help" rtk learn --help assert_ok "rtk learn (no sessions)" rtk learn --since 0 2>&1 || true # ── 32. Rewrite ─────────────────────────────────────── section "Rewrite" assert_contains "rewrite git status" "rtk git status" rtk rewrite "git status" assert_contains "rewrite cargo test" "rtk cargo test" rtk rewrite "cargo test" assert_contains "rewrite compound &&" "rtk git status" rtk rewrite "git status && cargo test" assert_contains "rewrite pipe preserves" "| head" rtk rewrite "git log | head" section "Rewrite (#345: RTK_DISABLED skip)" assert_fails "rewrite RTK_DISABLED=1 skip" rtk rewrite "RTK_DISABLED=1 git status" assert_fails "rewrite env RTK_DISABLED skip" rtk rewrite "FOO=1 RTK_DISABLED=1 cargo test" section "Rewrite (#346: 2>&1 preserved)" assert_contains "rewrite 2>&1 preserved" "2>&1" rtk rewrite "cargo test 2>&1 | head" section "Rewrite (#196: gh --json skip)" assert_fails "rewrite gh --json skip" rtk rewrite "gh pr list --json number" assert_fails "rewrite gh --jq skip" rtk rewrite "gh api /repos --jq .name" assert_fails "rewrite gh --template skip" rtk rewrite "gh pr view 1 --template '{{.title}}'" assert_contains "rewrite gh normal works" "rtk gh pr list" rtk rewrite "gh pr list" # ── 33. Verify ──────────────────────────────────────── section "Verify" assert_ok "rtk verify" rtk verify # ── 34. Proxy ───────────────────────────────────────── section "Proxy" assert_ok "rtk proxy echo hello" rtk proxy echo hello assert_contains "rtk proxy passthrough" "hello" rtk proxy echo hello # ── 35. Discover ────────────────────────────────────── section "Discover" assert_ok "rtk discover" rtk discover # ── 36. Diff ────────────────────────────────────────── section "Diff" assert_ok "rtk diff identical files" rtk diff Cargo.toml Cargo.toml assert_fails "rtk diff differing files" rtk diff Cargo.toml LICENSE assert_contains "rtk diff shows changes" "added" rtk diff Cargo.toml LICENSE # ── 37. Wc ──────────────────────────────────────────── section "Wc" assert_ok "rtk wc Cargo.toml" rtk wc Cargo.toml # ── 38. Smart ───────────────────────────────────────── section "Smart" assert_ok "rtk smart src/main.rs" rtk smart src/main.rs # ── 39. Json edge cases ────────────────────────────── section "Json (edge cases)" assert_fails "rtk json on TOML (#347)" rtk json Cargo.toml # ── 40. Docker (conditional) ───────────────────────── section "Docker (conditional)" if command -v docker >/dev/null 2>&1 && docker info >/dev/null 2>&1; then assert_ok "rtk docker ps" rtk docker ps assert_ok "rtk docker images" rtk docker images else skip_test "rtk docker" "docker not running" fi # ── 41. Hook check ─────────────────────────────────── section "Hook check (#344)" assert_contains "rtk init --show hook version" "version" rtk init --show # ══════════════════════════════════════════════════════ # Report # ══════════════════════════════════════════════════════ printf "\n${BOLD}══════════════════════════════════════${NC}\n" printf "${BOLD}Results: ${GREEN}%d passed${NC}, ${RED}%d failed${NC}, ${YELLOW}%d skipped${NC}\n" "$PASS" "$FAIL" "$SKIP" if [[ ${#FAILURES[@]} -gt 0 ]]; then printf "\n${RED}Failures:${NC}\n" for f in "${FAILURES[@]}"; do printf " - %s\n" "$f" done fi printf "${BOLD}══════════════════════════════════════${NC}\n" exit "$FAIL"