#!/usr/bin/env bash # End-to-end checks of the vcs CLI on a project that lives on this machine. # Usage: tests/local.sh [path/to/vcs] set -uo pipefail VCS=${1:-$(cd "$(dirname "$0")/.." && pwd)/target/debug/vcs} WORK=$(mktemp -d) export VCS_HOME="$WORK/home" VCS_NAME="Test Person" VCS_EMAIL="test@example.com" trap 'rm -rf "$WORK"' EXIT cd "$WORK" pass=0 fail=0 check() { # name, command... local name=$1 shift if "$@" >/dev/null 2>&1; then pass=$((pass + 1)); else fail=$((fail + 1)); echo "FAIL: $name"; fi } code() { # expected exit code, command... local want=$1 shift "$@" >/dev/null 2>&1 [ "$?" = "$want" ] } json() { "$VCS" --json "$@" 2>/dev/null; } # A new project with a first version. mkdir app && cd app printf 'hello\n' > README.md mkdir src && printf 'a\nb\nc\nd\ne\nf\ng\n' > src/lib.txt printf '{\n "name": "app"\n}\n' > package.json printf 'SECRET=1\n' > .env check "init" "$VCS" init check "land first version" "$VCS" land -m "First version" check "main is version 1" test "$(json log | python3 -c 'import json,sys; print(json.load(sys.stdin)["versions"][0]["n"])')" = 1 check ".env never landed" test -z "$(json log 1 | python3 -c 'import json,sys; print([f for f in json.load(sys.stdin)["files"] if ".env" in f] or "")')" check "empty land refused (exit 1)" code 1 "$VCS" land -m "Nothing" # A second workspace gets the files and the .env. cd "$WORK" check "get second workspace" "$VCS" get ./app app2 check "files arrived" test "$(cat app2/src/lib.txt | head -1)" = a check ".env came along" test "$(cat app2/.env)" = "SECRET=1" # Parallel edits to different files both land, in any order. printf 'hello there\n' > app/README.md printf 'a\nb\nc\nd\ne\nf\nG\n' > app2/src/lib.txt check "land from app" "$VCS" -C app land -m "Greet better" check "land from app2 without syncing" "$VCS" -C app2 land -m "Capital G" check "main is version 3" test "$(json -C app log | python3 -c 'import json,sys; print(json.load(sys.stdin)["versions"][0]["n"])')" = 3 # JSON files merge by key. cd "$WORK" && "$VCS" -C app sync >/dev/null 2>&1 && "$VCS" -C app2 sync >/dev/null 2>&1 printf '{\n "name": "app",\n "version": "1.0.0"\n}\n' > app/package.json printf '{\n "name": "app",\n "license": "MIT"\n}\n' > app2/package.json check "land json key A" "$VCS" -C app land -m "Add version" check "land json key B (merged by key)" "$VCS" -C app2 land -m "Add license" "$VCS" -C app sync >/dev/null 2>&1 check "both keys on main" python3 -c 'import json; d=json.load(open("app/package.json")); assert d["version"]=="1.0.0" and d["license"]=="MIT"' # A real conflict: land refuses, sync marks it, fixing it lands. "$VCS" -C app2 sync >/dev/null 2>&1 sed -i 's/^b$/B from app/' app/src/lib.txt sed -i 's/^b$/B from app2/' app2/src/lib.txt check "land A" "$VCS" -C app land -m "B from app" check "land B conflicts (exit 3)" code 3 "$VCS" -C app2 land -m "B from app2" check "sync leaves markers (exit 3)" code 3 "$VCS" -C app2 sync check "markers in file" grep -q '<<<<<<< main' app2/src/lib.txt check "land refused while markers remain" code 3 "$VCS" -C app2 land python3 - "$WORK/app2/src/lib.txt" <<'EOF' import sys, re p = sys.argv[1] t = open(p).read() t = re.sub(r"<<<<<<< [^\n]*\n(.*?)=======\n(.*?)>>>>>>> [^\n]*\n", lambda m: "B from both\n", t, flags=re.S) open(p, "w").write(t) EOF check "land after fixing (reuses description)" "$VCS" -C app2 land check "status clean after land" test "$(json -C app2 status | python3 -c 'import json,sys; print(len(json.load(sys.stdin)["files"]))')" = 0 # Undo a land: main gets a version that takes it back out. cd "$WORK/app" "$VCS" sync >/dev/null 2>&1 printf 'oops\n' > oops.txt check "land oops" "$VCS" land -m "Oops" check "undo the land" "$VCS" undo check "oops is open again" test "$(json status | python3 -c 'import json,sys; print(json.load(sys.stdin)["change"]["state"])')" = open check "main took it back" test "$(json log -n 1 | python3 -c 'import json,sys; print(json.load(sys.stdin)["versions"][0]["title"][:13])')" = "Take back mai" check "drop it" "$VCS" drop check "undo the drop" "$VCS" undo check "drop again" "$VCS" drop # Restore a file from an earlier save. printf 'v1\n' > notes.txt "$VCS" status >/dev/null 2>&1 sleep 3 printf 'broken\n' > notes.txt "$VCS" status >/dev/null 2>&1 check "restore --at" "$VCS" restore notes.txt --at "2s ago" check "notes is v1" test "$(cat notes.txt)" = v1 check "undo the restore" "$VCS" undo check "notes is broken again" test "$(cat notes.txt)" = broken check "restore to base (removes the new file)" "$VCS" restore notes.txt check "notes gone" test ! -e notes.txt # New, switch, stacks. printf 'feature a\n' > a.txt check "describe" "$VCS" describe "Feature A" A=$(json status | python3 -c 'import json,sys; print(json.load(sys.stdin)["change"]["id"])') check "new stacked on A" "$VCS" new "Feature B" --on "$A" printf 'feature b\n' > b.txt check "a.txt visible in B" test -e a.txt check "switch back to A" "$VCS" switch "${A:0:6}" check "b.txt gone in A" test ! -e b.txt check "undo switch" "$VCS" undo check "b.txt back" test -e b.txt check "land B lands A then B" "$VCS" land check "main has both" test "$(json log -n 2 | python3 -c 'import json,sys; print(",".join(v["title"] for v in json.load(sys.stdin)["versions"]))')" = "Feature B,Feature A" # Secrets don't land. printf 'const key = "sk_live_%s";\n' "$(printf 'x%.0s' {1..24})" > pay.js check "secret refused (exit 8)" code 8 "$VCS" land -m "Payments" rm pay.js printf 'const key = process.env.STRIPE_KEY;\n' > pay.js check "lands without the secret" "$VCS" land -m "Payments" # No description. printf 'x\n' > x.txt check "no description (exit 2)" code 2 "$VCS" land check "describe then land" "$VCS" land -m "Add x" # Diff and log output. printf 'y\n' > x.txt check "diff shows the edit" sh -c "\"$VCS\" diff | grep -q '^+y'" check "log --saves" "$VCS" log --saves check "log --ops" "$VCS" log --ops check "log --changes" "$VCS" log --changes check "guide" sh -c "\"$VCS\" guide | grep -q 'vcs land'" # Mine: vcs access me. printf 'token\n' > secrets.local check "access me" "$VCS" access secrets.local me check "mine stays out of the change" sh -c "! \"$VCS\" --json status | grep -q secrets.local" echo "local: $pass passed, $fail failed" [ "$fail" = 0 ]