mirror of
https://github.com/terraphim/gitea-infrastructure.git
synced 2026-07-16 01:00:33 +02:00
- Add test-gitea-skill.sh with 10 tests (26 assertions) against live Gitea
- Fix: API POST /labels only appends, does not enforce scoped exclusivity
- Fix: Use PUT with label swap pattern to atomically replace workflow labels
- Fix: Use PATCH /issues/{index} for assignees (POST /assignees not in 1.22.6)
- All 26 assertions pass against live Gitea 1.22.6
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
430 lines
14 KiB
Bash
Executable File
430 lines
14 KiB
Bash
Executable File
#!/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
|