diff --git a/.gitignore b/.gitignore index cd63188..e8c7bb2 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,6 @@ gitbackup dist/ testdata/**expected + +# Integration test secrets +test/.env diff --git a/client.go b/client.go index 2b0c8c8..bb72025 100644 --- a/client.go +++ b/client.go @@ -206,6 +206,7 @@ func newForgejoClient(gitHostURLParsed *url.URL) *forgejo.Client { } gitHostToken = forgejoToken + log.Println("Creating forgejo client", url) client, err := forgejo.NewClient(url, forgejo.SetToken(forgejoToken), forgejo.SetForgejoVersion("")) if err != nil { diff --git a/test/.env.example b/test/.env.example new file mode 100644 index 0000000..cb330c0 --- /dev/null +++ b/test/.env.example @@ -0,0 +1,8 @@ +# Copy this file to test/.env and fill in your tokens. +# See test/INTEGRATION_TESTING.md for setup instructions. + +GITHUB_TOKEN= +GITLAB_TOKEN= +BITBUCKET_USERNAME= +BITBUCKET_TOKEN= +FORGEJO_TOKEN= diff --git a/test/INTEGRATION_TESTING.md b/test/INTEGRATION_TESTING.md new file mode 100644 index 0000000..7520a0d --- /dev/null +++ b/test/INTEGRATION_TESTING.md @@ -0,0 +1,59 @@ +# Integration Testing + +Manual integration test script for verifying gitbackup works end-to-end against real git hosting services before releases. + +## Prerequisites + +- Go toolchain +- Git +- SSH keys configured for the services you want to test (unless testing HTTPS-only) + +## Test Account Setup + +Each contributor creates their own test accounts. The script expects a standard set of repos on each service. + +### GitHub + +1. Create a [personal access token](https://github.com/settings/tokens) with `repo` scope +2. Create two repositories: + - `gitbackup-test-public` (public) + - `gitbackup-test-private` (private) + +### GitLab + +1. Create a [personal access token](https://gitlab.com/-/user_settings/personal_access_tokens) with `read_api` scope +2. Create two projects: + - `gitbackup-test-public` (public) + - `gitbackup-test-private` (private) + +### Bitbucket + +1. Create an [API token](https://bitbucket.org/account/settings/app-passwords/) with `read:user:bitbucket`, `read:workspace:bitbucket`, and `read:repository:bitbucket` scopes +2. Create a workspace and two repositories: + - `gitbackup-test-public` (public) + - `gitbackup-test-private` (private) + +### Forgejo (Codeberg) + +1. Create an [access token](https://codeberg.org/user/settings/applications) with `read:repository` permission +2. Create two repositories: + - `gitbackup-test-public` (public) + - `gitbackup-test-private` (private) + +## Environment Setup + +``` +cp test/.env.example test/.env +``` + +Fill in your tokens in `test/.env`. + +## Running the Tests + +From the repository root: + +``` +bash test/integration-test.sh +``` + +Any services with missing environment variables will be skipped automatically. diff --git a/test/integration-test.sh b/test/integration-test.sh new file mode 100755 index 0000000..b95a351 --- /dev/null +++ b/test/integration-test.sh @@ -0,0 +1,248 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +BINARY="$REPO_ROOT/gitbackup" + +# Load env vars +if [[ -f "$SCRIPT_DIR/.env" ]]; then + set -a + source "$SCRIPT_DIR/.env" + set +a +fi + +VERBOSE=false +SERVICES=() + +for arg in "$@"; do + case "$arg" in + -v|--verbose) VERBOSE=true ;; + *) SERVICES+=("$arg") ;; + esac +done + +if [[ ${#SERVICES[@]} -eq 0 ]]; then + SERVICES=(github gitlab bitbucket forgejo) +fi + +# Expected repo names (same across all services) +EXPECTED_REPOS="gitbackup-test-public gitbackup-test-private" + +# --- Helpers --- + +pass() { + echo " PASS: $1" + RESULTS+=("PASS: $1") + PASSED=$((PASSED + 1)) +} + +fail() { + echo " FAIL: $1" + RESULTS+=("FAIL: $1") + FAILED=$((FAILED + 1)) +} + +check_env() { + local service="$1" + shift + for var in "$@"; do + if [[ -z "${!var:-}" ]]; then + echo "Skipping $service: $var is not set" + return 1 + fi + done + return 0 +} + +is_git_repo() { + git -C "$1" rev-parse 2>/dev/null +} + +is_bare_repo() { + [[ "$(git -C "$1" rev-parse --is-bare-repository 2>/dev/null)" == "true" ]] +} + +# Check that a repo with the given name exists under the backup dir and is a valid git repo +check_repo_exists() { + local backup_dir="$1" + local repo_name="$2" + local dir + dir=$(find "$backup_dir" -type d -name "$repo_name" -maxdepth 4 2>/dev/null | head -1) + [[ -n "$dir" ]] && is_git_repo "$dir" +} + +# Check that a bare repo with the given name exists under the backup dir +check_bare_repo_exists() { + local backup_dir="$1" + local repo_name="$2" + local dir + dir=$(find "$backup_dir" -type d -name "${repo_name}.git" -maxdepth 4 2>/dev/null | head -1) + [[ -n "$dir" ]] && is_bare_repo "$dir" +} + +run_gitbackup() { + if $VERBOSE; then + $BINARY "$@" 2>&1 + else + $BINARY "$@" >/dev/null 2>&1 + fi +} + +run_service_tests() { + local service="$1" + local label="$2" + local extra_flags="${3:-}" + local tmpdir + + echo "" + echo "=== $service ($label) ===" + + # Test 1: Fresh clone + tmpdir=$(mktemp -d) + trap "rm -rf $tmpdir" RETURN + + echo " Running fresh clone..." + if run_gitbackup -service "$service" -backupdir "$tmpdir" $extra_flags; then + all_found=true + for repo_name in $EXPECTED_REPOS; do + if check_repo_exists "$tmpdir" "$repo_name"; then + echo " Found $repo_name" + else + echo " Missing $repo_name" + all_found=false + fi + done + if $all_found; then + pass "$service ($label): fresh clone" + else + fail "$service ($label): fresh clone — expected repos not found" + fi + else + fail "$service ($label): fresh clone — gitbackup exited with error" + fi + + # Test 2: Update (run again into same directory) + echo " Running update..." + if run_gitbackup -service "$service" -backupdir "$tmpdir" $extra_flags; then + all_found=true + for repo_name in $EXPECTED_REPOS; do + if ! check_repo_exists "$tmpdir" "$repo_name"; then + all_found=false + fi + done + if $all_found; then + pass "$service ($label): update" + else + fail "$service ($label): update — repos missing after update" + fi + else + fail "$service ($label): update — gitbackup exited with error" + fi + + rm -rf "$tmpdir" + + # Test 3: Bare clone + tmpdir=$(mktemp -d) + + echo " Running bare clone..." + if run_gitbackup -service "$service" -backupdir "$tmpdir" -bare $extra_flags; then + all_found=true + for repo_name in $EXPECTED_REPOS; do + if check_bare_repo_exists "$tmpdir" "$repo_name"; then + echo " Found ${repo_name}.git (bare)" + else + echo " Missing ${repo_name}.git (bare)" + all_found=false + fi + done + if $all_found; then + pass "$service ($label): bare clone" + else + fail "$service ($label): bare clone — expected bare repos not found" + fi + else + fail "$service ($label): bare clone — gitbackup exited with error" + fi + + rm -rf "$tmpdir" + + # Test 4: Ignore private + tmpdir=$(mktemp -d) + + echo " Running ignore-private clone..." + if run_gitbackup -service "$service" -backupdir "$tmpdir" -ignore-private $extra_flags; then + if check_repo_exists "$tmpdir" "gitbackup-test-public"; then + echo " Found gitbackup-test-public" + else + echo " Missing gitbackup-test-public" + fail "$service ($label): ignore-private — public repo not found" + rm -rf "$tmpdir" + trap - RETURN + return + fi + if check_repo_exists "$tmpdir" "gitbackup-test-private"; then + echo " Found gitbackup-test-private (unexpected)" + fail "$service ($label): ignore-private — private repo should have been skipped" + else + echo " Correctly skipped gitbackup-test-private" + pass "$service ($label): ignore-private" + fi + else + fail "$service ($label): ignore-private — gitbackup exited with error" + fi + + rm -rf "$tmpdir" + trap - RETURN +} + +# --- Main --- + +PASSED=0 +FAILED=0 +RESULTS=() + +echo "Building gitbackup..." +(cd "$REPO_ROOT" && go build -o "$BINARY" .) + +for service in "${SERVICES[@]}"; do + case "$service" in + github) + check_env github GITHUB_TOKEN || continue + run_service_tests github "SSH" + run_service_tests github "HTTPS" "-use-https-clone" + ;; + gitlab) + check_env gitlab GITLAB_TOKEN || continue + run_service_tests gitlab "SSH" "-gitlab.projectVisibility all -gitlab.projectMembershipType owner" + run_service_tests gitlab "HTTPS" "-gitlab.projectVisibility all -gitlab.projectMembershipType owner -use-https-clone" + ;; + bitbucket) + check_env bitbucket BITBUCKET_USERNAME BITBUCKET_TOKEN || continue + run_service_tests bitbucket "SSH" + run_service_tests bitbucket "HTTPS" "-use-https-clone" + ;; + forgejo) + check_env forgejo FORGEJO_TOKEN || continue + run_service_tests forgejo "SSH" "-githost.url https://codeberg.org" + run_service_tests forgejo "HTTPS" "-githost.url https://codeberg.org -use-https-clone" + ;; + esac +done + +# --- Summary (verbose only) --- + +if $VERBOSE; then + echo "" + echo "==============================" + echo "Results: $PASSED passed, $FAILED failed" + echo "" + for r in "${RESULTS[@]}"; do + echo " $r" + done + echo "==============================" +fi + +if [[ $FAILED -gt 0 ]]; then + exit 1 +fi