| 1 | #!/usr/bin/env bash |
| 2 | # End-to-end check of a self-hosted g1t: sign up, confirm the email, make a |
| 3 | # workspace and a repository, push and clone over HTTP, open an issue, and |
| 4 | # read the code back through the site. |
| 5 | # |
| 6 | # ./smoke.sh # against the compose stack's defaults |
| 7 | # G1T_URL=http://localhost:8787 MAIL_LOG=wrangler.log ./smoke.sh |
| 8 | # |
| 9 | # The confirmation link is read from Mailpit (MAILPIT_URL, the default) or, |
| 10 | # with MAIL_LOG set, from a log the mail Worker printed it to. |
| 11 | set -euo pipefail |
| 12 | |
| 13 | G1T_URL="${G1T_URL:-http://localhost:8787}" |
| 14 | MAILPIT_URL="${MAILPIT_URL:-http://localhost:8025}" |
| 15 | MAIL_LOG="${MAIL_LOG:-}" |
| 16 | RUN="$(date +%s)" |
| 17 | USER_NAME="smoke${RUN}" |
| 18 | EMAIL="${USER_NAME}@example.com" |
| 19 | PASSWORD="correct-horse-${RUN}" |
| 20 | WORKSPACE="ws${RUN}" |
| 21 | REPO="hello" |
| 22 | WORK="$(mktemp -d)" |
| 23 | JAR="$WORK/cookies" |
| 24 | trap 'rm -rf "$WORK"' EXIT |
| 25 | |
| 26 | step() { printf '\n== %s\n' "$*"; } |
| 27 | fail() { printf 'FAILED: %s\n' "$*" >&2; exit 1; } |
| 28 | |
| 29 | # A form POST as a browser sends it, with the Origin the site checks. |
| 30 | post() { |
| 31 | local path="$1"; shift |
| 32 | curl -sS -o "$WORK/body" -w '%{http_code} %{redirect_url}' -b "$JAR" -c "$JAR" \ |
| 33 | -H "Origin: $G1T_URL" "$@" "$G1T_URL$path" |
| 34 | } |
| 35 | get() { |
| 36 | curl -sS -o "$WORK/body" -w '%{http_code}' -b "$JAR" -c "$JAR" "$G1T_URL$1" |
| 37 | } |
| 38 | |
| 39 | step "site answers at $G1T_URL" |
| 40 | [ "$(get /)" = 200 ] || fail "GET / did not answer 200" |
| 41 | |
| 42 | step "sign up as $USER_NAME" |
| 43 | out="$(post /register --data-urlencode "username=$USER_NAME" --data-urlencode "email=$EMAIL" --data-urlencode "password=$PASSWORD")" |
| 44 | echo "$out" |
| 45 | case "$out" in 30[23]*) ;; *) fail "register: $out $(head -c 300 "$WORK/body")" ;; esac |
| 46 | # curl keeps Secure cookies only for https or localhost; carry it by hand. |
| 47 | grep -q g1t_session "$JAR" || fail "no session cookie" |
| 48 | |
| 49 | step "confirm the email" |
| 50 | link="" |
| 51 | for _ in $(seq 1 20); do |
| 52 | if [ -n "$MAIL_LOG" ]; then |
| 53 | link="$(grep -ao "[a-z]*://[^ \"<]*/verify?token=[0-9a-zA-Z_-]*" "$MAIL_LOG" | tail -1 || true)" |
| 54 | else |
| 55 | id="$(curl -sS "$MAILPIT_URL/api/v1/search?query=to:$EMAIL" | sed -n 's/.*"ID":"\([^"]*\)".*/\1/p' | head -1)" |
| 56 | [ -n "$id" ] && link="$(curl -sS "$MAILPIT_URL/api/v1/message/$id" | grep -ao '[a-z]*://[^ "<\\]*/verify?token=[0-9a-zA-Z_-]*' | head -1 || true)" |
| 57 | fi |
| 58 | [ -n "$link" ] && break |
| 59 | sleep 1 |
| 60 | done |
| 61 | [ -n "$link" ] || fail "no confirmation email arrived" |
| 62 | echo "$link" |
| 63 | [ "$(get "/verify?${link#*\?}")" = 200 ] || fail "verify" |
| 64 | grep -q "$USER_NAME" "$WORK/body" || fail "verify page does not name the account" |
| 65 | |
| 66 | step "create workspace $WORKSPACE" |
| 67 | out="$(post /workspaces/new --data-urlencode "slug=$WORKSPACE" --data-urlencode "displayName=Smoke $RUN")" |
| 68 | echo "$out" |
| 69 | case "$out" in 30[23]*) ;; *) fail "workspace: $out $(head -c 300 "$WORK/body")" ;; esac |
| 70 | |
| 71 | step "create repository $WORKSPACE/$REPO" |
| 72 | out="$(post /new --data-urlencode "workspace=$WORKSPACE" --data-urlencode "name=$REPO" --data-urlencode "description=Self-host smoke test" --data-urlencode "visibility=public" --data-urlencode "source=empty")" |
| 73 | echo "$out" |
| 74 | case "$out" in 30[23]*) ;; *) fail "repo: $out $(head -c 300 "$WORK/body")" ;; esac |
| 75 | |
| 76 | step "push over HTTP" |
| 77 | remote="${G1T_URL/:\/\//://$USER_NAME:$PASSWORD@}/$WORKSPACE/$REPO.git" |
| 78 | git init -q -b main "$WORK/src" |
| 79 | ( |
| 80 | cd "$WORK/src" |
| 81 | git config user.name "Smoke Test" |
| 82 | git config user.email "$EMAIL" |
| 83 | # A throwaway commit: never signed, whatever the global config says. |
| 84 | git config commit.gpgsign false |
| 85 | printf '# hello\n\nPushed to a self-hosted g1t.\n' > README.md |
| 86 | mkdir -p src && printf 'fn main() {\n println!("hello from g1t");\n}\n' > src/main.rs |
| 87 | git add . && git commit -qm "First commit" |
| 88 | git -c credential.helper= push -q "$remote" main |
| 89 | ) |
| 90 | echo "pushed $(git -C "$WORK/src" rev-parse --short HEAD)" |
| 91 | |
| 92 | step "clone over HTTP" |
| 93 | git -c credential.helper= clone -q "$G1T_URL/$WORKSPACE/$REPO.git" "$WORK/clone" |
| 94 | diff -q "$WORK/src/README.md" "$WORK/clone/README.md" || fail "clone differs" |
| 95 | echo "clone matches" |
| 96 | |
| 97 | step "open an issue" |
| 98 | out="$(post "/$WORKSPACE/$REPO/issues/new" --data-urlencode "title=It works" --data-urlencode "body=Opened by smoke.sh")" |
| 99 | echo "$out" |
| 100 | case "$out" in 30[23]*/issues/1) ;; *) fail "issue: $out $(head -c 300 "$WORK/body")" ;; esac |
| 101 | [ "$(get "/$WORKSPACE/$REPO/issues/1")" = 200 ] || fail "issue page" |
| 102 | grep -q "It works" "$WORK/body" || fail "issue page does not show the title" |
| 103 | |
| 104 | step "browse code in the site" |
| 105 | [ "$(get "/$WORKSPACE/$REPO/code")" = 200 ] || fail "code page" |
| 106 | grep -q "README.md" "$WORK/body" || fail "code page does not list README.md" |
| 107 | [ "$(get "/$WORKSPACE/$REPO/blob/main/src/main.rs")" = 200 ] || fail "blob page" |
| 108 | grep -q "hello from g1t" "$WORK/body" || fail "blob page does not show the file" |
| 109 | [ "$(get "/$WORKSPACE/$REPO/commits")" = 200 ] || fail "commits page" |
| 110 | grep -q "First commit" "$WORK/body" || fail "commits page does not show the commit" |
| 111 | |
| 112 | printf '\nAll checks passed: %s/%s/%s\n' "$G1T_URL" "$WORKSPACE" "$REPO" |