#!/usr/bin/env bats bats_require_minimum_version 1.5.0 # tests/unit/cli.bats — bin/josh-sync CLI surface tests # # Invokes the CLI as a subprocess (not by sourcing libs) so flag parsing, # command dispatch, help/usage text, and the `adopt → onboard --mode=adopt` # alias regress cleanly. These tests deliberately stay at the parse/dispatch # layer — anything that would reach out to git remotes is set up to fail at # target-lookup time, before any side effects. # # Uses `run --separate-stderr` so we can assert on stderr separately (most of # josh-sync's output is logged via core.sh's `log()` which writes to stderr). setup() { JOSH_SYNC_ROOT="$(cd "$BATS_TEST_DIRNAME/../.." && pwd)" JOSH_BIN="${JOSH_SYNC_ROOT}/bin/josh-sync" FIXTURES="${JOSH_SYNC_ROOT}/tests/fixtures" TEST_ROOT="$(mktemp -d)" } teardown() { cd "$BATS_TEST_DIRNAME" 2>/dev/null || true rm -rf "$TEST_ROOT" } # Drop the minimal fixture config into a clean cwd. Most cmd_* paths read # .josh-sync.yml from the working directory. seed_minimal_config() { cp "${FIXTURES}/minimal.yml" "${TEST_ROOT}/.josh-sync.yml" cd "$TEST_ROOT" } # ─── Global flags ─────────────────────────────────────────────────── @test "--version exits 0 and prints the version" { run "$JOSH_BIN" --version [ "$status" -eq 0 ] [[ "$output" == *"josh-sync"* ]] } @test "--help exits 0 and advertises onboard, the adopt alias, and --mode" { run "$JOSH_BIN" --help [ "$status" -eq 0 ] # Help text is printed to stderr via `cat >&2`, so combine with --separate. run --separate-stderr "$JOSH_BIN" --help [ "$status" -eq 0 ] [[ "$stderr" == *"onboard "* ]] [[ "$stderr" == *"adopt "* ]] [[ "$stderr" == *"Alias for 'onboard --mode=adopt'"* ]] [[ "$stderr" == *"--mode={reset,adopt}"* ]] } # ─── onboard flag parsing & dispatch ──────────────────────────────── @test "onboard with no target exits non-zero and prints usage including --mode" { seed_minimal_config run --separate-stderr "$JOSH_BIN" onboard [ "$status" -ne 0 ] [[ "$stderr" == *"Usage: josh-sync onboard"* ]] [[ "$stderr" == *"--mode=reset|adopt"* ]] } @test "onboard --mode=invalid is rejected with a clear error" { seed_minimal_config run --separate-stderr "$JOSH_BIN" onboard --mode=invalid example [ "$status" -ne 0 ] [[ "$stderr" == *"Invalid --mode"* ]] } @test "onboard --mode=adopt against an unknown target reports target-not-found" { seed_minimal_config # Proves --mode parses correctly: parse_config succeeds, then cmd_onboard # fails at target lookup, before any git/network activity. run --separate-stderr "$JOSH_BIN" onboard --mode=adopt nonexistent [ "$status" -ne 0 ] [[ "$stderr" == *"not found"* ]] } # ─── adopt alias ──────────────────────────────────────────────────── @test "adopt alias forwards to onboard --mode=adopt (visible via alias log line + target lookup)" { seed_minimal_config run --separate-stderr "$JOSH_BIN" adopt nonexistent [ "$status" -ne 0 ] # Alias notice from cmd_adopt: [[ "$stderr" == *"alias for: josh-sync onboard --mode=adopt"* ]] # And the forwarded call lands in cmd_onboard's target lookup: [[ "$stderr" == *"not found"* ]] } @test "onboard usage listing shows --restart and --mode flags together" { seed_minimal_config run --separate-stderr "$JOSH_BIN" onboard [ "$status" -ne 0 ] [[ "$stderr" == *"--restart"* ]] [[ "$stderr" == *"--mode"* ]] } # ─── --mode value-parsing guards ──────────────────────────────────── @test "onboard --mode (no value) fails with a helpful message instead of crashing on unbound \$2" { seed_minimal_config run --separate-stderr "$JOSH_BIN" onboard example --mode [ "$status" -ne 0 ] [[ "$stderr" == *"--mode requires a value"* ]] } @test "onboard --mode= (empty after equals) is rejected explicitly, not silently ignored" { seed_minimal_config run --separate-stderr "$JOSH_BIN" onboard example --mode= [ "$status" -ne 0 ] [[ "$stderr" == *"Empty --mode="* ]] } @test "adopt alias dies when a conflicting --mode=reset is also passed (instead of silently honouring reset)" { seed_minimal_config run --separate-stderr "$JOSH_BIN" adopt example --mode=reset [ "$status" -ne 0 ] [[ "$stderr" == *"alias for --mode=adopt"* ]] [[ "$stderr" == *"conflicting"* ]] }