#!/usr/bin/env bash # test-gitea-skill.sh -- Integration tests for Gitea agent skill # Runs 10 tests against a live Gitea instance. No mocks. # # Usage: # source ~/op_zesticai_non_prod.sh # export GITEA_TOKEN=$(op read "op://TerraphimPlatform/git.terraphim.cloud-admin-token/credential") # ./test-gitea-skill.sh # # Environment: # GITEA_URL -- Gitea instance (default: https://git.terraphim.cloud) # GITEA_TOKEN -- API token (required) set -euo pipefail GITEA_URL="${GITEA_URL:-https://git.terraphim.cloud}" TEST_OWNER="" TEST_REPO="skill-test-$(date +%s)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # Counters PASS=0 FAIL=0 TOTAL=0 # --- Colors --- RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m' NC='\033[0m' # No Color # --- Helpers --- gitea_api() { local method="$1" endpoint="$2" data="${3:-}" local args=(-s -H "Authorization: token $GITEA_TOKEN" -H "Content-Type: application/json") if [ -n "$data" ]; then args+=(-X "$method" -d "$data") else args+=(-X "$method") fi curl "${args[@]}" "$GITEA_URL/api/v1$endpoint" } gitea_api_code() { local method="$1" endpoint="$2" data="${3:-}" local args=(-s -o /dev/null -w '%{http_code}' -H "Authorization: token $GITEA_TOKEN" -H "Content-Type: application/json") if [ -n "$data" ]; then args+=(-X "$method" -d "$data") else args+=(-X "$method") fi curl "${args[@]}" "$GITEA_URL/api/v1$endpoint" } log_pass() { PASS=$((PASS + 1)) TOTAL=$((TOTAL + 1)) echo -e " ${GREEN}[PASS]${NC} $1" } log_fail() { FAIL=$((FAIL + 1)) TOTAL=$((TOTAL + 1)) echo -e " ${RED}[FAIL]${NC} $1" if [ -n "${2:-}" ]; then echo -e " Detail: $2" fi } assert_json_field() { local json="$1" field="$2" expected="$3" msg="$4" local actual actual=$(echo "$json" | jq -r "$field" 2>/dev/null) if [ "$actual" = "$expected" ]; then log_pass "$msg" else log_fail "$msg" "expected '$expected', got '$actual'" fi } assert_json_nonzero() { local json="$1" field="$2" msg="$3" local actual actual=$(echo "$json" | jq -r "$field" 2>/dev/null) if [ -n "$actual" ] && [ "$actual" != "null" ] && [ "$actual" != "0" ]; then log_pass "$msg" else log_fail "$msg" "field '$field' is empty, null, or 0" fi } assert_http_code() { local actual="$1" expected="$2" msg="$3" if [ "$actual" = "$expected" ]; then log_pass "$msg" else log_fail "$msg" "expected HTTP $expected, got HTTP $actual" fi } # --- Pre-flight checks --- preflight() { echo "=== Pre-flight Checks ===" if [ -z "${GITEA_TOKEN:-}" ]; then echo -e "${RED}GITEA_TOKEN is not set. Run:${NC}" echo " source ~/op_zesticai_non_prod.sh" echo ' export GITEA_TOKEN=$(op read "op://TerraphimPlatform/git.terraphim.cloud-admin-token/credential")' exit 1 fi # Check authentication and get username local user_info user_info=$(gitea_api GET "/user") TEST_OWNER=$(echo "$user_info" | jq -r '.login // empty') if [ -z "$TEST_OWNER" ]; then echo -e "${RED}Authentication failed. Check GITEA_TOKEN.${NC}" exit 1 fi echo -e " Instance: $GITEA_URL" echo -e " User: $TEST_OWNER" echo -e " Test repo: $TEST_OWNER/$TEST_REPO" echo "" } # --- Cleanup --- cleanup() { echo "" echo "=== Cleanup ===" local code code=$(gitea_api_code DELETE "/repos/$TEST_OWNER/$TEST_REPO") if [ "$code" = "204" ] || [ "$code" = "404" ]; then echo -e " ${GREEN}Cleaned up${NC} $TEST_OWNER/$TEST_REPO" else echo -e " ${YELLOW}Warning:${NC} Cleanup returned HTTP $code for $TEST_OWNER/$TEST_REPO" fi } # Register cleanup on exit trap cleanup EXIT # === Tests === test_01_create_repo() { echo "--- Test 1: Create test repository ---" local result result=$(gitea_api POST "/user/repos" \ "{\"name\": \"$TEST_REPO\", \"description\": \"Temporary test repo for Gitea skill tests\", \"auto_init\": true, \"private\": true}") assert_json_field "$result" ".name" "$TEST_REPO" "Repo name matches" assert_json_nonzero "$result" ".id" "Repo has id" assert_json_field "$result" ".owner.login" "$TEST_OWNER" "Owner matches" } test_02_create_labels() { echo "--- Test 2: Create labels via setup-labels.sh ---" # Run setup-labels.sh against test repo local output output=$(GITEA_OWNER="$TEST_OWNER" GITEA_REPO="$TEST_REPO" "$SCRIPT_DIR/setup-labels.sh" 2>&1) # Verify all 12 labels exist local labels labels=$(gitea_api GET "/repos/$TEST_OWNER/$TEST_REPO/labels") local count count=$(echo "$labels" | jq 'length') if [ "$count" -ge 12 ]; then log_pass "Created $count labels (expected >= 12)" else log_fail "Label count" "expected >= 12, got $count" fi # Verify specific scoped labels local workflow_ready workflow_ready=$(echo "$labels" | jq -r '.[] | select(.name=="workflow:ready") | .exclusive') assert_json_field "{\"val\": $workflow_ready}" ".val" "true" "workflow:ready is exclusive" local workflow_ip workflow_ip=$(echo "$labels" | jq -r '.[] | select(.name=="workflow:in-progress") | .exclusive') assert_json_field "{\"val\": $workflow_ip}" ".val" "true" "workflow:in-progress is exclusive" } test_03_create_milestone() { echo "--- Test 3: Create milestone (project) ---" local result result=$(gitea_api POST "/repos/$TEST_OWNER/$TEST_REPO/milestones" \ "$(jq -n --arg t "Test Sprint $(date +%Y-W%V)" --arg d "Integration test milestone" \ '{title: $t, description: $d}')") MILESTONE_ID=$(echo "$result" | jq -r '.id // empty') if [ -n "$MILESTONE_ID" ] && [ "$MILESTONE_ID" != "null" ]; then log_pass "Milestone created with id=$MILESTONE_ID" else log_fail "Milestone creation" "no id returned" fi assert_json_nonzero "$result" ".title" "Milestone has title" } test_04_create_issue() { echo "--- Test 4: Create issue (task) with milestone ---" # Get label IDs for workflow:ready and type:task local labels labels=$(gitea_api GET "/repos/$TEST_OWNER/$TEST_REPO/labels") local ready_id type_task_id ready_id=$(echo "$labels" | jq -r '.[] | select(.name=="workflow:ready") | .id') type_task_id=$(echo "$labels" | jq -r '.[] | select(.name=="type:task") | .id') local result result=$(gitea_api POST "/repos/$TEST_OWNER/$TEST_REPO/issues" \ "$(jq -n \ --arg t "Test task: implement feature X" \ --arg b "This is a test issue for the Gitea skill integration test." \ --argjson m "$MILESTONE_ID" \ --argjson labels "[$ready_id, $type_task_id]" \ '{title: $t, body: $b, milestone: $m, labels: $labels}')") ISSUE_NUMBER=$(echo "$result" | jq -r '.number // empty') if [ -n "$ISSUE_NUMBER" ] && [ "$ISSUE_NUMBER" != "null" ]; then log_pass "Issue created with number=#$ISSUE_NUMBER" else log_fail "Issue creation" "no number returned" fi assert_json_field "$result" ".state" "open" "Issue state is open" # Verify milestone assignment local milestone_id milestone_id=$(echo "$result" | jq -r '.milestone.id // empty') assert_json_field "{\"val\": \"$milestone_id\"}" ".val" "$MILESTONE_ID" "Milestone assigned" } test_05_mark_ready() { echo "--- Test 5: Mark task ready (workflow:ready) ---" # Use the swap pattern: get current labels, remove workflow:*, add workflow:ready, PUT local repo_labels issue ready_id kept_ids all_ids repo_labels=$(gitea_api GET "/repos/$TEST_OWNER/$TEST_REPO/labels") ready_id=$(echo "$repo_labels" | jq -r '.[] | select(.name=="workflow:ready") | .id') issue=$(gitea_api GET "/repos/$TEST_OWNER/$TEST_REPO/issues/$ISSUE_NUMBER") kept_ids=$(echo "$issue" | jq '[.labels[] | select(.name | startswith("workflow:") | not) | .id]') all_ids=$(echo "$kept_ids" | jq --argjson nid "$ready_id" '. + [$nid]') gitea_api PUT "/repos/$TEST_OWNER/$TEST_REPO/issues/$ISSUE_NUMBER/labels" \ "{\"labels\": $all_ids}" > /dev/null # Verify label is set issue=$(gitea_api GET "/repos/$TEST_OWNER/$TEST_REPO/issues/$ISSUE_NUMBER") local has_ready has_ready=$(echo "$issue" | jq '[.labels[].name] | any(. == "workflow:ready")') assert_json_field "{\"val\": $has_ready}" ".val" "true" "Issue has workflow:ready label" } test_06_claim_task() { echo "--- Test 6: Claim task (workflow:in-progress) ---" # Use the swap pattern: get current labels, remove workflow:*, add workflow:in-progress, PUT local repo_labels issue ip_id kept_ids all_ids repo_labels=$(gitea_api GET "/repos/$TEST_OWNER/$TEST_REPO/labels") ip_id=$(echo "$repo_labels" | jq -r '.[] | select(.name=="workflow:in-progress") | .id') issue=$(gitea_api GET "/repos/$TEST_OWNER/$TEST_REPO/issues/$ISSUE_NUMBER") kept_ids=$(echo "$issue" | jq '[.labels[] | select(.name | startswith("workflow:") | not) | .id]') all_ids=$(echo "$kept_ids" | jq --argjson nid "$ip_id" '. + [$nid]') gitea_api PUT "/repos/$TEST_OWNER/$TEST_REPO/issues/$ISSUE_NUMBER/labels" \ "{\"labels\": $all_ids}" > /dev/null # Assign to current user via PATCH (POST /assignees does not exist in Gitea 1.22.6) gitea_api PATCH "/repos/$TEST_OWNER/$TEST_REPO/issues/$ISSUE_NUMBER" \ "{\"assignees\": [\"$TEST_OWNER\"]}" > /dev/null # Verify issue=$(gitea_api GET "/repos/$TEST_OWNER/$TEST_REPO/issues/$ISSUE_NUMBER") local has_ip has_ip=$(echo "$issue" | jq '[.labels[].name] | any(. == "workflow:in-progress")') assert_json_field "{\"val\": $has_ip}" ".val" "true" "Issue has workflow:in-progress label" # Check that workflow:ready was removed by label swap local has_ready has_ready=$(echo "$issue" | jq '[.labels[].name] | any(. == "workflow:ready")') assert_json_field "{\"val\": $has_ready}" ".val" "false" "workflow:ready removed by label swap" # Check assignee local assignee assignee=$(echo "$issue" | jq -r '.assignees[0].login // empty') assert_json_field "{\"val\": \"$assignee\"}" ".val" "$TEST_OWNER" "Assignee set" } test_07_check_progress() { echo "--- Test 7: Check project progress (0% complete) ---" local milestone milestone=$(gitea_api GET "/repos/$TEST_OWNER/$TEST_REPO/milestones/$MILESTONE_ID") local open closed open=$(echo "$milestone" | jq '.open_issues') closed=$(echo "$milestone" | jq '.closed_issues') assert_json_field "{\"val\": $open}" ".val" "1" "1 open issue" assert_json_field "{\"val\": $closed}" ".val" "0" "0 closed issues" # Calculate progress local total pct total=$((open + closed)) if [ "$total" -gt 0 ]; then pct=$((closed * 100 / total)) else pct=0 fi assert_json_field "{\"val\": $pct}" ".val" "0" "Progress is 0%" } test_08_close_task() { echo "--- Test 8: Close task ---" # Use the swap pattern: get current labels, remove workflow:*, add workflow:done, PUT local repo_labels issue done_id kept_ids all_ids repo_labels=$(gitea_api GET "/repos/$TEST_OWNER/$TEST_REPO/labels") done_id=$(echo "$repo_labels" | jq -r '.[] | select(.name=="workflow:done") | .id') issue=$(gitea_api GET "/repos/$TEST_OWNER/$TEST_REPO/issues/$ISSUE_NUMBER") kept_ids=$(echo "$issue" | jq '[.labels[] | select(.name | startswith("workflow:") | not) | .id]') all_ids=$(echo "$kept_ids" | jq --argjson nid "$done_id" '. + [$nid]') gitea_api PUT "/repos/$TEST_OWNER/$TEST_REPO/issues/$ISSUE_NUMBER/labels" \ "{\"labels\": $all_ids}" > /dev/null # Close the issue gitea_api PATCH "/repos/$TEST_OWNER/$TEST_REPO/issues/$ISSUE_NUMBER" \ '{"state": "closed"}' > /dev/null # Add closing comment gitea_api POST "/repos/$TEST_OWNER/$TEST_REPO/issues/$ISSUE_NUMBER/comments" \ "$(jq -n --arg b "Closed by integration test" '{body: $b}')" > /dev/null # Verify issue=$(gitea_api GET "/repos/$TEST_OWNER/$TEST_REPO/issues/$ISSUE_NUMBER") assert_json_field "$issue" ".state" "closed" "Issue state is closed" local has_done has_done=$(echo "$issue" | jq '[.labels[].name] | any(. == "workflow:done")') assert_json_field "{\"val\": $has_done}" ".val" "true" "Issue has workflow:done label" # Check that workflow:in-progress was removed by label swap local has_ip has_ip=$(echo "$issue" | jq '[.labels[].name] | any(. == "workflow:in-progress")') assert_json_field "{\"val\": $has_ip}" ".val" "false" "workflow:in-progress removed by label swap" } test_09_progress_after_close() { echo "--- Test 9: Check progress after close (100% complete) ---" local milestone milestone=$(gitea_api GET "/repos/$TEST_OWNER/$TEST_REPO/milestones/$MILESTONE_ID") local open closed open=$(echo "$milestone" | jq '.open_issues') closed=$(echo "$milestone" | jq '.closed_issues') assert_json_field "{\"val\": $open}" ".val" "0" "0 open issues" assert_json_field "{\"val\": $closed}" ".val" "1" "1 closed issue" # Calculate progress local total pct total=$((open + closed)) if [ "$total" -gt 0 ]; then pct=$((closed * 100 / total)) else pct=0 fi assert_json_field "{\"val\": $pct}" ".val" "100" "Progress is 100%" } test_10_cleanup() { echo "--- Test 10: Delete test repository ---" local code code=$(gitea_api_code DELETE "/repos/$TEST_OWNER/$TEST_REPO") assert_http_code "$code" "204" "Repository deleted (HTTP 204)" # Verify it's gone local verify_code verify_code=$(gitea_api_code GET "/repos/$TEST_OWNER/$TEST_REPO") assert_http_code "$verify_code" "404" "Repository confirmed deleted (HTTP 404)" # Disable the trap since we already cleaned up trap - EXIT } # === Main === echo "============================================" echo " Gitea Skill Integration Tests" echo "============================================" echo "" preflight test_01_create_repo test_02_create_labels test_03_create_milestone test_04_create_issue test_05_mark_ready test_06_claim_task test_07_check_progress test_08_close_task test_09_progress_after_close test_10_cleanup echo "" echo "============================================" echo -e " Results: ${GREEN}$PASS passed${NC}, ${RED}$FAIL failed${NC} (${TOTAL} total)" echo "============================================" if [ "$FAIL" -gt 0 ]; then exit 1 fi