Sam Jeston
History
151 lines·
7.8 KB
·Bourne Again Shell (bash)
| 1 | #!/usr/bin/env bash |
| 2 | # End-to-end smoke test: starts a throwaway Openhub instance and exercises |
| 3 | # sign up, SSH keys, pushing, browsing, pull requests, merging and permissions. |
| 4 | # |
| 5 | # Usage: scripts/smoke-test.sh [path/to/openhub-binary] |
| 6 | set -euo pipefail |
| 7 | |
| 8 | ROOT="$(cd "$(dirname "$0")/.." && pwd)" |
| 9 | BIN="${1:-$ROOT/target/debug/openhub}" |
| 10 | HTTP_PORT="${HTTP_PORT:-38080}" |
| 11 | SSH_PORT="${SSH_PORT:-38022}" |
| 12 | BASE="http://127.0.0.1:$HTTP_PORT" |
| 13 | |
| 14 | if [[ ! -x "$BIN" ]]; then |
| 15 | (cd "$ROOT" && cargo build --quiet) |
| 16 | fi |
| 17 | |
| 18 | WORK="$(mktemp -d)" |
| 19 | SERVER_PID="" |
| 20 | cleanup() { |
| 21 | [[ -n "$SERVER_PID" ]] && kill "$SERVER_PID" 2>/dev/null || true |
| 22 | rm -rf "$WORK" |
| 23 | } |
| 24 | trap cleanup EXIT |
| 25 | |
| 26 | pass=0 |
| 27 | ok() { pass=$((pass + 1)); printf ' \033[32m✓\033[0m %s\n' "$1"; } |
| 28 | fail() { |
| 29 | printf ' \033[31m✗ %s\033[0m\n' "$1" |
| 30 | echo "--- server log ---" |
| 31 | tail -n 30 "$WORK/server.log" || true |
| 32 | exit 1 |
| 33 | } |
| 34 | expect_status() { # expect_status <code> <description> <curl args...> |
| 35 | local want="$1" desc="$2" |
| 36 | shift 2 |
| 37 | local got |
| 38 | got="$(curl -s -o /dev/null -w '%{http_code}' "$@")" |
| 39 | [[ "$got" == "$want" ]] && ok "$desc" || fail "$desc (expected $want, got $got)" |
| 40 | } |
| 41 | expect_body() { # expect_body <pattern> <description> <curl args...> |
| 42 | local pattern="$1" desc="$2" |
| 43 | shift 2 |
| 44 | curl -s "$@" | grep -q -- "$pattern" && ok "$desc" || fail "$desc (missing '$pattern')" |
| 45 | } |
| 46 | |
| 47 | echo "Starting Openhub in $WORK" |
| 48 | "$BIN" --data-dir "$WORK/data" --http-addr "127.0.0.1:$HTTP_PORT" --ssh-addr "127.0.0.1:$SSH_PORT" \ |
| 49 | --public-url "$BASE" >"$WORK/server.log" 2>&1 & |
| 50 | SERVER_PID=$! |
| 51 | for _ in $(seq 1 50); do |
| 52 | curl -s -o /dev/null "$BASE/" && break |
| 53 | sleep 0.2 |
| 54 | done |
| 55 | curl -s -o /dev/null "$BASE/" || fail "server did not start" |
| 56 | |
| 57 | ssh-keygen -q -t ed25519 -N "" -C alice@test -f "$WORK/alice_key" |
| 58 | ssh-keygen -q -t ed25519 -N "" -C bob@test -f "$WORK/bob_key" |
| 59 | ssh_cmd() { echo "ssh -i $WORK/$1_key -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR"; } |
| 60 | A_COOKIES="$WORK/alice.cookies" |
| 61 | B_COOKIES="$WORK/bob.cookies" |
| 62 | REMOTE="ssh://[email protected]:$SSH_PORT" |
| 63 | |
| 64 | echo "Accounts and keys" |
| 65 | expect_status 303 "register alice (first user)" -c "$A_COOKIES" -X POST \ |
| 66 | -d "username=alice&[email protected]&password=correct-horse" "$BASE/register" |
| 67 | expect_status 303 "register bob" -c "$B_COOKIES" -X POST \ |
| 68 | -d "username=bob&[email protected]&password=correct-horse" "$BASE/register" |
| 69 | expect_status 303 "alice adds SSH key" -b "$A_COOKIES" -X POST \ |
| 70 | --data-urlencode "key=$(cat "$WORK/alice_key.pub")" "$BASE/settings/keys" |
| 71 | expect_status 303 "bob adds SSH key" -b "$B_COOKIES" -X POST \ |
| 72 | --data-urlencode "key=$(cat "$WORK/bob_key.pub")" "$BASE/settings/keys" |
| 73 | expect_body "already in use" "the same key cannot be added twice" -b "$B_COOKIES" -X POST \ |
| 74 | --data-urlencode "key=$(cat "$WORK/alice_key.pub")" "$BASE/settings/keys" |
| 75 | expect_body "Incorrect username or password" "wrong password is rejected" -X POST \ |
| 76 | -d "login=alice&password=nope" "$BASE/login" |
| 77 | greeting="$($(ssh_cmd alice) -T -p "$SSH_PORT" [email protected] 2>&1 || true)" |
| 78 | [[ "$greeting" == *"Hi alice!"* ]] && ok "ssh -T greets alice" || fail "ssh -T greeting" |
| 79 | |
| 80 | echo "Repositories and pushing" |
| 81 | expect_status 303 "create public repo" -b "$A_COOKIES" -X POST -d "name=demo&visibility=public" "$BASE/new" |
| 82 | expect_status 303 "create private repo with README" -b "$A_COOKIES" -X POST \ |
| 83 | -d "name=secret&visibility=private&readme=1" "$BASE/new" |
| 84 | git init -q -b main "$WORK/demo" |
| 85 | cd "$WORK/demo" |
| 86 | git config user.name "Alice" && git config user.email [email protected] |
| 87 | printf '# Demo\n\nHello from **Openhub**.\n' >README.md |
| 88 | mkdir -p src && printf 'fn main() {\n println!("hi");\n}\n' >src/main.rs |
| 89 | git add -A && git commit -qm "Initial commit" |
| 90 | git remote add origin "$REMOTE/alice/demo.git" |
| 91 | GIT_SSH_COMMAND="$(ssh_cmd alice)" git push -q -u origin main 2>/dev/null && ok "push over SSH" || fail "push over SSH" |
| 92 | |
| 93 | expect_body "Hello from <strong>Openhub</strong>" "README renders on repo page" "$BASE/alice/demo" |
| 94 | expect_body 'tok-k' "source files are syntax highlighted" "$BASE/alice/demo/blob/main/src/main.rs" |
| 95 | expect_body "Initial commit" "commit history lists the commit" "$BASE/alice/demo/commits/main" |
| 96 | expect_status 200 "raw file download" "$BASE/alice/demo/raw/main/README.md" |
| 97 | expect_status 200 "zip archive download" "$BASE/alice/demo/archive/main.zip" |
| 98 | |
| 99 | echo "Pull requests" |
| 100 | git checkout -q -b feature |
| 101 | printf 'pub fn add(a: i32, b: i32) -> i32 {\n a + b\n}\n' >src/lib.rs |
| 102 | git add -A && git commit -qm "Add library" |
| 103 | GIT_SSH_COMMAND="$(ssh_cmd alice)" git push -u origin feature 2>"$WORK/push.txt" >/dev/null |
| 104 | grep -q "compare/main...feature" "$WORK/push.txt" && ok "push suggests opening a pull request" || fail "push hint" |
| 105 | expect_status 303 "open pull request" -b "$A_COOKIES" -X POST \ |
| 106 | -d "base=main&head=feature&title=Add+library&body=Adds+add()" "$BASE/alice/demo/pulls" |
| 107 | expect_body "no conflicts" "pull request is mergeable" -b "$A_COOKIES" "$BASE/alice/demo/pull/1" |
| 108 | expect_body "src/lib.rs" "files changed tab shows the diff" "$BASE/alice/demo/pull/1/files" |
| 109 | expect_status 303 "bob comments" -b "$B_COOKIES" -X POST -d "body=Nice+work" "$BASE/alice/demo/pull/1/comment" |
| 110 | expect_status 403 "bob cannot merge without write access" -b "$B_COOKIES" -X POST -d "method=merge" \ |
| 111 | "$BASE/alice/demo/pull/1/merge" |
| 112 | expect_status 303 "alice merges" -b "$A_COOKIES" -X POST -d "method=merge" "$BASE/alice/demo/pull/1/merge" |
| 113 | expect_body "successfully merged" "pull request shows as merged" -b "$A_COOKIES" "$BASE/alice/demo/pull/1" |
| 114 | GIT_SSH_COMMAND="$(ssh_cmd alice)" git fetch -q origin |
| 115 | git log --format=%s origin/main | grep -q "Merge pull request #1" && ok "merge commit landed on main" || fail "merge commit" |
| 116 | |
| 117 | git checkout -q -b cli origin/main |
| 118 | echo "x" >cli.txt && git add -A && git commit -qm "CLI change" |
| 119 | GIT_SSH_COMMAND="$(ssh_cmd alice)" git push -q -u origin cli 2>/dev/null |
| 120 | expect_status 303 "open second pull request" -b "$A_COOKIES" -X POST -d "base=main&head=cli&title=CLI" "$BASE/alice/demo/pulls" |
| 121 | git checkout -q -B main origin/main && git merge -q --no-ff cli -m "Merge cli locally" |
| 122 | GIT_SSH_COMMAND="$(ssh_cmd alice)" git push -q origin main 2>/dev/null |
| 123 | expect_body "state-merged" "pull request merged on the command line is detected" -b "$A_COOKIES" "$BASE/alice/demo/pull/2" |
| 124 | |
| 125 | echo "Permissions" |
| 126 | expect_status 404 "private repo hidden from anonymous users" "$BASE/alice/secret" |
| 127 | expect_status 404 "private repo hidden from non-collaborators" -b "$B_COOKIES" "$BASE/alice/secret" |
| 128 | GIT_SSH_COMMAND="$(ssh_cmd bob)" git clone -q "$REMOTE/alice/secret.git" "$WORK/nope" 2>/dev/null && |
| 129 | fail "bob cloned a private repo" || ok "bob cannot clone private repo over SSH" |
| 130 | cd "$WORK/demo" |
| 131 | GIT_SSH_COMMAND="$(ssh_cmd bob)" git push -q origin HEAD:refs/heads/bob 2>/dev/null && |
| 132 | fail "bob pushed without access" || ok "bob cannot push to alice/demo" |
| 133 | expect_status 303 "alice adds bob as collaborator" -b "$A_COOKIES" -X POST -d "username=bob" \ |
| 134 | "$BASE/alice/secret/settings/collaborators" |
| 135 | GIT_SSH_COMMAND="$(ssh_cmd bob)" git clone -q "$REMOTE/alice/secret.git" "$WORK/secret" 2>/dev/null && |
| 136 | ok "collaborator can clone private repo" || fail "collaborator clone" |
| 137 | GIT_SSH_COMMAND="$(ssh_cmd alice)" git push -q origin HEAD:refs/pull/1/head 2>/dev/null && |
| 138 | fail "pushed to refs/pull" || ok "refs/pull/* cannot be pushed to" |
| 139 | |
| 140 | echo "Git over HTTP" |
| 141 | git clone -q "$BASE/alice/demo.git" "$WORK/http-clone" && ok "anonymous HTTP clone of public repo" || fail "HTTP clone" |
| 142 | GIT_TERMINAL_PROMPT=0 git clone -q "$BASE/alice/secret.git" "$WORK/http-secret" 2>/dev/null && |
| 143 | fail "anonymous HTTP clone of private repo" || ok "private repo requires authentication over HTTP" |
| 144 | git clone -q "http://bob:[email protected]:$HTTP_PORT/alice/secret.git" "$WORK/http-secret2" && |
| 145 | ok "collaborator HTTP clone with password" || fail "authenticated HTTP clone" |
| 146 | cd "$WORK/http-clone" |
| 147 | git -c user.name=x -c user.email=x@x commit -q --allow-empty -m "nope" |
| 148 | git push -q origin main 2>/dev/null && fail "push over HTTP" || ok "push over HTTP is refused" |
| 149 | |
| 150 | echo |
| 151 | printf '\033[32mAll %d checks passed.\033[0m\n' "$pass" |