Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 75 additions & 16 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,26 +22,85 @@ jobs:
with:
terraform_wrapper: false

- name: Verify required secrets
- name: Start Appwrite
run: docker compose -f testing/docker-compose.yaml up -d --remove-orphans

- name: Wait for Appwrite
run: |
missing=()
[ -z "$APPWRITE_ENDPOINT" ] && missing+=("APPWRITE_ENDPOINT")
[ -z "$APPWRITE_PROJECT_ID" ] && missing+=("APPWRITE_PROJECT_ID")
[ -z "$APPWRITE_API_KEY" ] && missing+=("APPWRITE_API_KEY")
if [ ${#missing[@]} -ne 0 ]; then
echo "::error::Missing required secrets: ${missing[*]}"
exit 1
fi
env:
APPWRITE_ENDPOINT: ${{ secrets.APPWRITE_ENDPOINT }}
APPWRITE_PROJECT_ID: ${{ secrets.APPWRITE_PROJECT_ID }}
APPWRITE_API_KEY: ${{ secrets.APPWRITE_API_KEY }}
echo "Waiting for Appwrite to be ready..."
for i in $(seq 1 60); do
if curl -sf http://localhost:20080/v1/health > /dev/null 2>&1; then
echo "Appwrite is ready!"
exit 0
fi
sleep 2
done
echo "Appwrite did not become ready"
docker compose -f testing/docker-compose.yaml logs appwrite
exit 1

- name: Bootstrap project and API key
run: |
APPWRITE_URL="http://localhost:20080/v1"
COOKIE_JAR=$(mktemp)

# Create admin account
curl -sf -X POST "$APPWRITE_URL/account" \
-H "Content-Type: application/json" \
-H "X-Appwrite-Project: console" \
-c "$COOKIE_JAR" \
-d '{"userId":"unique()","email":"admin@test.local","password":"password1234","name":"Admin"}' \
> /dev/null

# Login
curl -sf -X POST "$APPWRITE_URL/account/sessions/email" \
-H "Content-Type: application/json" \
-H "X-Appwrite-Project: console" \
-c "$COOKIE_JAR" \
-b "$COOKIE_JAR" \
-d '{"email":"admin@test.local","password":"password1234"}' \
> /dev/null

# Create team
TEAM_ID=$(curl -sf -X POST "$APPWRITE_URL/teams" \
-H "Content-Type: application/json" \
-H "X-Appwrite-Project: console" \
-b "$COOKIE_JAR" \
-d '{"teamId":"unique()","name":"Testing"}' | python3 -c "import sys,json; print(json.load(sys.stdin)['\$id'])")

# Create project
curl -sf -X POST "$APPWRITE_URL/projects" \
-H "Content-Type: application/json" \
-H "X-Appwrite-Project: console" \
-b "$COOKIE_JAR" \
-d "{\"projectId\":\"tf-test\",\"name\":\"Terraform Tests\",\"teamId\":\"$TEAM_ID\"}" \
> /dev/null

# Create API key with all scopes
ALL_SCOPES='["sessions.write","users.read","users.write","teams.read","teams.write","databases.read","databases.write","collections.read","collections.write","attributes.read","attributes.write","indexes.read","indexes.write","documents.read","documents.write","files.read","files.write","buckets.read","buckets.write","functions.read","functions.write","execution.read","execution.write","locale.read","health.read","avatars.read","webhooks.read","webhooks.write","rules.read","rules.write","messaging.read","messaging.write","providers.read","providers.write","topics.read","topics.write","subscribers.read","subscribers.write","targets.read","targets.write","backups.read","backups.write","sites.read","sites.write","vcs.read","vcs.write","migrations.read","migrations.write"]'

API_KEY=$(curl -sf -X POST "$APPWRITE_URL/projects/tf-test/keys" \
-H "Content-Type: application/json" \
-H "X-Appwrite-Project: console" \
-b "$COOKIE_JAR" \
-d "{\"name\":\"tf-acceptance-tests\",\"scopes\":$ALL_SCOPES}" | python3 -c "import sys,json; print(json.load(sys.stdin)['secret'])")

echo "APPWRITE_ENDPOINT=$APPWRITE_URL" >> "$GITHUB_ENV"
echo "APPWRITE_PROJECT_ID=tf-test" >> "$GITHUB_ENV"
echo "APPWRITE_API_KEY=$API_KEY" >> "$GITHUB_ENV"

rm -f "$COOKIE_JAR"

- name: Run acceptance tests
run: make acceptance-test
timeout-minutes: 20
env:
TF_ACC: "1"
APPWRITE_ENDPOINT: ${{ secrets.APPWRITE_ENDPOINT }}
APPWRITE_PROJECT_ID: ${{ secrets.APPWRITE_PROJECT_ID }}
APPWRITE_API_KEY: ${{ secrets.APPWRITE_API_KEY }}

- name: Dump logs on failure
if: failure()
run: docker compose -f testing/docker-compose.yaml logs --tail=100

- name: Tear down
if: always()
run: docker compose -f testing/docker-compose.yaml down -v --remove-orphans
14 changes: 12 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ test:
go test ./... -v -count=1 -timeout 10m

acceptance-test:
TF_ACC=1 go test ./... -v -count=1 $(TESTARGS) -timeout 120m
TF_ACC=$${TF_ACC:-1} go test ./... -v -count=1 $(TESTARGS) -timeout 120m

sweep:
@echo "WARNING: This will destroy infrastructure. Use only in development."
Expand Down Expand Up @@ -42,4 +42,14 @@ clean:
docs:
go generate ./...

.PHONY: build install test acceptance-test sweep test-compile vet fmt fmt-check lint clean docs
# Self-hosted Appwrite for testing
appwrite-up:
./testing/bootstrap.sh --up

appwrite-down:
./testing/bootstrap.sh --down

appwrite-test:
./testing/bootstrap.sh

.PHONY: build install test acceptance-test sweep test-compile vet fmt fmt-check lint clean docs appwrite-up appwrite-down appwrite-test
2 changes: 2 additions & 0 deletions testing/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.env.test
cookies.txt
167 changes: 167 additions & 0 deletions testing/bootstrap.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
#!/usr/bin/env bash
#
# Bootstrap a self-hosted Appwrite instance for acceptance testing.
#
# Usage:
# ./testing/bootstrap.sh # Start Appwrite + create project + run tests
# ./testing/bootstrap.sh --up # Just start Appwrite and print env vars
# ./testing/bootstrap.sh --down # Tear down the instance
#
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
COMPOSE_FILE="$SCRIPT_DIR/docker-compose.yaml"
APPWRITE_URL="http://localhost:20080/v1"
COOKIE_JAR=$(mktemp)

cleanup() { rm -f "$COOKIE_JAR"; }
trap cleanup EXIT

# ── Helpers ──────────────────────────────────────────────────────────────────

log() { echo "==> $*"; }
fail() { echo "ERROR: $*" >&2; exit 1; }

wait_for_appwrite() {
log "Waiting for Appwrite to be ready..."
local retries=60
while [ $retries -gt 0 ]; do
if curl -sf "$APPWRITE_URL/health" > /dev/null 2>&1; then
log "Appwrite is ready!"
return 0
fi
retries=$((retries - 1))
sleep 2
done
fail "Appwrite did not become ready within 120 seconds"
}

api() {
local method="$1" path="$2"
shift 2
curl -sf -X "$method" "$APPWRITE_URL$path" \
-H "Content-Type: application/json" \
-H "X-Appwrite-Project: console" \
-b "$COOKIE_JAR" \
-c "$COOKIE_JAR" \
"$@"
}

# ── Commands ─────────────────────────────────────────────────────────────────

cmd_down() {
log "Tearing down Appwrite..."
docker compose -f "$COMPOSE_FILE" down -v --remove-orphans
log "Done."
}

cmd_up() {
log "Starting Appwrite..."
docker compose -f "$COMPOSE_FILE" up -d --remove-orphans

wait_for_appwrite

# 1. Create admin account (first user = admin)
log "Creating admin account..."
api POST /account \
-d '{"userId":"unique()","email":"admin@test.local","password":"password1234","name":"Admin"}' \
> /dev/null 2>&1 || true

# 2. Create session
log "Logging in..."
api POST /account/sessions/email \
-d '{"email":"admin@test.local","password":"password1234"}' \
> /dev/null || fail "Could not create session"

# 3. Create a team (required for project)
log "Creating team..."
TEAM_ID=$(api POST /teams \
-d '{"teamId":"unique()","name":"Testing"}' \
2>/dev/null | python3 -c "import sys,json; print(json.load(sys.stdin)['\$id'])" 2>/dev/null || echo "")

if [ -z "$TEAM_ID" ]; then
# Team may already exist from a previous run, fetch it
TEAM_ID=$(api GET /teams \
2>/dev/null | python3 -c "import sys,json; print(json.load(sys.stdin)['teams'][0]['\$id'])" 2>/dev/null || echo "")
fi

[ -z "$TEAM_ID" ] && fail "Could not create or find a team"
log "Team ID: $TEAM_ID"

# 4. Create project
log "Creating project..."
PROJECT_ID=$(api POST /projects \
-d "{\"projectId\":\"tf-test\",\"name\":\"Terraform Tests\",\"teamId\":\"$TEAM_ID\"}" \
2>/dev/null | python3 -c "import sys,json; print(json.load(sys.stdin)['\$id'])" 2>/dev/null || echo "")

if [ -z "$PROJECT_ID" ]; then
PROJECT_ID="tf-test"
log "Project may already exist, using ID: $PROJECT_ID"
else
log "Project ID: $PROJECT_ID"
fi

# 5. Create API key with all scopes
log "Creating API key..."
ALL_SCOPES='["sessions.write","users.read","users.write","teams.read","teams.write","databases.read","databases.write","collections.read","collections.write","attributes.read","attributes.write","indexes.read","indexes.write","documents.read","documents.write","files.read","files.write","buckets.read","buckets.write","functions.read","functions.write","execution.read","execution.write","locale.read","health.read","avatars.read","webhooks.read","webhooks.write","rules.read","rules.write","messaging.read","messaging.write","providers.read","providers.write","topics.read","topics.write","subscribers.read","subscribers.write","targets.read","targets.write","backups.read","backups.write","sites.read","sites.write","vcs.read","vcs.write","migrations.read","migrations.write"]'

API_KEY=$(api POST "/projects/$PROJECT_ID/keys" \
-d "{\"name\":\"tf-acceptance-tests\",\"scopes\":$ALL_SCOPES}" \
2>/dev/null | python3 -c "import sys,json; print(json.load(sys.stdin)['secret'])" 2>/dev/null || echo "")

if [ -z "$API_KEY" ]; then
# Key may already exist, list keys
API_KEY=$(api GET "/projects/$PROJECT_ID/keys" \
2>/dev/null | python3 -c "import sys,json; print(json.load(sys.stdin)['keys'][0]['secret'])" 2>/dev/null || echo "")
fi

[ -z "$API_KEY" ] && fail "Could not create or find an API key"

# 6. Export env vars
cat <<EOF

────────────────────────────────────────────────────────
Appwrite is ready for acceptance testing!

Export these variables:

export APPWRITE_ENDPOINT="$APPWRITE_URL"
export APPWRITE_PROJECT_ID="$PROJECT_ID"
export APPWRITE_API_KEY="$API_KEY"

Then run tests:

make acceptance-test
────────────────────────────────────────────────────────
EOF

# Write to .env file for convenience
cat > "$SCRIPT_DIR/.env.test" <<EOF
APPWRITE_ENDPOINT=$APPWRITE_URL
APPWRITE_PROJECT_ID=$PROJECT_ID
APPWRITE_API_KEY=$API_KEY
EOF
log "Env vars also saved to testing/.env.test"
}

cmd_test() {
cmd_up

# Source the env file and run tests
log "Running acceptance tests..."
set -a
# shellcheck disable=SC1091
source "$SCRIPT_DIR/.env.test"
set +a

cd "$SCRIPT_DIR/.."
make acceptance-test
}

# ── Main ─────────────────────────────────────────────────────────────────────

case "${1:-}" in
--down) cmd_down ;;
--up) cmd_up ;;
*) cmd_test ;;
esac
Loading
Loading