test(promotion): cover global download after approval (#792)

* test(promotion): cover global download after approval

Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>

* test(promotion): align smoke setup with current API

Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>

* test(promotion): make download smoke repeatable

Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>

* test(promotion): wait for member review readiness

Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>

* test(promotion): verify denied approval state

Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>

---------

Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
This commit is contained in:
XiaoSeS 2026-09-02 11:15:10 +08:00 • committed by GitHub
parent 6ab8faa6b9
commit 15dad68740
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -8,7 +8,7 @@ FAIL=0
USER_COOKIE="$(mktemp)"
ADMIN_COOKIE="$(mktemp)"
WORK_DIR="$(mktemp -d)"
SLUG="psmoke$(date +%s)"
SLUG="psmoke$(date +%s)${RANDOM}"
cleanup() {
rm -f "$USER_COOKIE" "$ADMIN_COOKIE"
@ -89,54 +89,132 @@ if [[ -z "$USER_CSRF" || -z "$ADMIN_CSRF" ]]; then
exit 1
fi
GLOBAL_RESPONSE="$(curl -sS -H "X-Mock-User-Id: local-user" -b "$USER_COOKIE" -c "$USER_COOKIE" \
GLOBAL_RESPONSE="$(curl -sS -H "X-Mock-User-Id: local-admin" -b "$ADMIN_COOKIE" -c "$ADMIN_COOKIE" \
"$BASE_URL/api/web/namespaces/global")"
assert_code "Global namespace detail is available" "$GLOBAL_RESPONSE" "0"
GLOBAL_NAMESPACE_ID="$(json_field "$GLOBAL_RESPONSE" "data.id")"
CREATE_NAMESPACE_RESPONSE="$(curl -sS -H "X-Mock-User-Id: local-user" -b "$USER_COOKIE" -c "$USER_COOKIE" \
-H "X-XSRF-TOKEN: $USER_CSRF" \
CREATE_NAMESPACE_RESPONSE="$(curl -sS -H "X-Mock-User-Id: local-admin" -b "$ADMIN_COOKIE" -c "$ADMIN_COOKIE" \
-H "X-XSRF-TOKEN: $ADMIN_CSRF" \
-H "Content-Type: application/json" \
-X POST "$BASE_URL/api/web/namespaces" \
-d "{\"slug\":\"$SLUG\",\"displayName\":\"Promotion Smoke $SLUG\",\"description\":\"promotion smoke test\"}")"
assert_code "Owner can create promotion smoke namespace" "$CREATE_NAMESPACE_RESPONSE" "0"
assert_code "Admin can create promotion smoke namespace" "$CREATE_NAMESPACE_RESPONSE" "0"
NAMESPACE_ID="$(json_field "$CREATE_NAMESPACE_RESPONSE" "data.id")"
cat > "$WORK_DIR/SKILL.md" <<'EOF'
ADD_MEMBER_RESPONSE="$(curl -sS -H "X-Mock-User-Id: local-admin" -b "$ADMIN_COOKIE" -c "$ADMIN_COOKIE" \
-H "X-XSRF-TOKEN: $ADMIN_CSRF" \
-H "Content-Type: application/json" \
-X POST "$BASE_URL/api/web/namespaces/$SLUG/members" \
-d '{"userId":"local-user","role":"MEMBER"}')"
assert_code "Admin can add the regular user as a namespace member" "$ADD_MEMBER_RESPONSE" "0"
cat > "$WORK_DIR/SKILL.md" <<EOF
---
name: Promotion Smoke Skill
name: Promotion Smoke $SLUG
description: Promotion smoke test
version: 1.0.0
---
Body
EOF
(cd "$WORK_DIR" && zip -q skill.zip SKILL.md)
python3 - "$WORK_DIR" <<'PY'
from pathlib import Path
import sys
import zipfile
work_dir = Path(sys.argv[1])
with zipfile.ZipFile(work_dir / "skill.zip", "w", zipfile.ZIP_DEFLATED) as archive:
archive.write(work_dir / "SKILL.md", "SKILL.md")
PY
PUBLISH_RESPONSE="$(curl -sS -H "X-Mock-User-Id: local-user" -b "$USER_COOKIE" -c "$USER_COOKIE" \
-H "X-XSRF-TOKEN: $USER_CSRF" \
-F "file=@$WORK_DIR/skill.zip;type=application/zip" \
-F "visibility=PUBLIC" \
"$BASE_URL/api/web/skills/$SLUG/publish")"
assert_code "Owner can publish a team skill" "$PUBLISH_RESPONSE" "0"
assert_code "Regular user can publish a team skill for review" "$PUBLISH_RESPONSE" "0"
SKILL_ID="$(json_field "$PUBLISH_RESPONSE" "data.skillId")"
SKILL_SLUG="$(json_field "$PUBLISH_RESPONSE" "data.slug")"
REVIEW_READY=false
SKILL_DETAIL_RESPONSE=""
for _ in $(seq 1 60); do
SKILL_DETAIL_RESPONSE="$(curl -sS -H "X-Mock-User-Id: local-user" -b "$USER_COOKIE" -c "$USER_COOKIE" \
"$BASE_URL/api/web/skills/$SLUG/$SKILL_SLUG")"
if JSON_INPUT="$SKILL_DETAIL_RESPONSE" python3 - <<'PY'
import json
import os
data = json.loads(os.environ["JSON_INPUT"]).get("data") or {}
versions = [data.get("ownerPreviewVersion") or {}, data.get("headlineVersion") or {}]
raise SystemExit(0 if any(version.get("status") == "PENDING_REVIEW" for version in versions) else 1)
PY
then
REVIEW_READY=true
break
fi
sleep 1
done
assert_code "Regular user can load the submitted team skill" "$SKILL_DETAIL_RESPONSE" "0"
if [[ "$REVIEW_READY" != "true" ]]; then
fail "Published team skill did not finish scanning within 60 seconds"
exit 1
fi
pass "Published team skill is ready for review"
PENDING_REVIEWS_RESPONSE="$(curl -sS -H "X-Mock-User-Id: local-admin" -b "$ADMIN_COOKIE" -c "$ADMIN_COOKIE" \
"$BASE_URL/api/web/reviews?status=PENDING&namespaceId=$NAMESPACE_ID")"
assert_code "Admin can list pending namespace reviews" "$PENDING_REVIEWS_RESPONSE" "0"
REVIEW_ID="$(json_field "$PENDING_REVIEWS_RESPONSE" "data.items.0.id")"
assert_code "Admin can list pending skill reviews" "$PENDING_REVIEWS_RESPONSE" "0"
REVIEW_ID="$(JSON_INPUT="$PENDING_REVIEWS_RESPONSE" python3 - "$SKILL_SLUG" <<'PY'
import json
import os
import sys
skill_slug = sys.argv[1]
items = json.loads(os.environ["JSON_INPUT"])["data"]["items"]
match = next((item for item in items if item["skillSlug"] == skill_slug), None)
print(match["id"] if match else "")
PY
)"
if [[ -z "$REVIEW_ID" ]]; then
fail "Pending skill reviews should contain the published team skill"
exit 1
fi
pass "Pending skill reviews contain the published team skill"
APPROVE_REVIEW_RESPONSE="$(curl -sS -H "X-Mock-User-Id: local-admin" -b "$ADMIN_COOKIE" -c "$ADMIN_COOKIE" \
-H "X-XSRF-TOKEN: $ADMIN_CSRF" \
-H "Content-Type: application/json" \
-X POST "$BASE_URL/api/web/reviews/$REVIEW_ID/approve" \
-d '{"comment":"ok"}')"
assert_code "Admin can approve team skill review" "$APPROVE_REVIEW_RESPONSE" "0"
-d '{"comment":"approved by promotion smoke"}')"
assert_code "Admin can approve the regular user's team skill" "$APPROVE_REVIEW_RESPONSE" "0"
SKILL_DETAIL_RESPONSE="$(curl -sS -H "X-Mock-User-Id: local-user" -b "$USER_COOKIE" -c "$USER_COOKIE" \
"$BASE_URL/api/web/skills/$SLUG/$SKILL_SLUG")"
assert_code "Owner can load team skill detail" "$SKILL_DETAIL_RESPONSE" "0"
VERSION_ID="$(json_field "$SKILL_DETAIL_RESPONSE" "data.latestVersionId")"
SKILL_DETAIL_RESPONSE=""
PROMOTION_READY=false
for _ in $(seq 1 60); do
SKILL_DETAIL_RESPONSE="$(curl -sS -H "X-Mock-User-Id: local-user" -b "$USER_COOKIE" -c "$USER_COOKIE" \
"$BASE_URL/api/web/skills/$SLUG/$SKILL_SLUG")"
if JSON_INPUT="$SKILL_DETAIL_RESPONSE" python3 - <<'PY'
import json
import os
response = json.loads(os.environ["JSON_INPUT"])
data = response.get("data") or {}
headline_version = data.get("headlineVersion") or {}
raise SystemExit(0 if headline_version.get("id") and data.get("canSubmitPromotion") else 1)
PY
then
PROMOTION_READY=true
break
fi
sleep 1
done
assert_code "Regular user can load team skill detail" "$SKILL_DETAIL_RESPONSE" "0"
if [[ "$PROMOTION_READY" != "true" ]]; then
fail "Published team skill did not become promotable within 60 seconds"
exit 1
fi
VERSION_ID="$(json_field "$SKILL_DETAIL_RESPONSE" "data.headlineVersion.id")"
CAN_SUBMIT_PROMOTION="$(json_field "$SKILL_DETAIL_RESPONSE" "data.canSubmitPromotion")"
if [[ "$CAN_SUBMIT_PROMOTION" == "True" || "$CAN_SUBMIT_PROMOTION" == "true" ]]; then
pass "Approved team skill is marked promotable"
@ -146,21 +224,22 @@ fi
MY_SKILLS_RESPONSE="$(curl -sS -H "X-Mock-User-Id: local-user" -b "$USER_COOKIE" -c "$USER_COOKIE" \
"$BASE_URL/api/web/me/skills")"
assert_code "Owner can list my skills with promotion metadata" "$MY_SKILLS_RESPONSE" "0"
assert_code "Regular user can list my skills with promotion metadata" "$MY_SKILLS_RESPONSE" "0"
if JSON_INPUT="$MY_SKILLS_RESPONSE" python3 - "$SKILL_ID" <<'PY'
import json
import os
import sys
skill_id = int(sys.argv[1])
items = json.loads(os.environ["JSON_INPUT"])["data"]
items = json.loads(os.environ["JSON_INPUT"])["data"]["items"]
match = next(item for item in items if item["id"] == skill_id)
raise SystemExit(0 if match["canSubmitPromotion"] and match["latestVersionId"] else 1)
headline_version = match.get("headlineVersion") or {}
raise SystemExit(0 if match["canSubmitPromotion"] and headline_version.get("id") else 1)
PY
then
pass "My skills response exposes promotion submission fields"
else
fail "My skills response should expose latestVersionId and canSubmitPromotion"
fail "My skills response should expose headlineVersion and canSubmitPromotion"
fi
SUBMIT_PROMOTION_RESPONSE="$(curl -sS -H "X-Mock-User-Id: local-user" -b "$USER_COOKIE" -c "$USER_COOKIE" \
@ -168,7 +247,7 @@ SUBMIT_PROMOTION_RESPONSE="$(curl -sS -H "X-Mock-User-Id: local-user" -b "$USER_
-H "Content-Type: application/json" \
-X POST "$BASE_URL/api/web/promotions" \
-d "{\"sourceSkillId\":$SKILL_ID,\"sourceVersionId\":$VERSION_ID,\"targetNamespaceId\":$GLOBAL_NAMESPACE_ID}")"
assert_code "Owner can submit promotion to global namespace" "$SUBMIT_PROMOTION_RESPONSE" "0"
assert_code "Regular user can submit promotion to global namespace" "$SUBMIT_PROMOTION_RESPONSE" "0"
PENDING_PROMOTIONS_RESPONSE="$(curl -sS -H "X-Mock-User-Id: local-admin" -b "$ADMIN_COOKIE" -c "$ADMIN_COOKIE" \
"$BASE_URL/api/web/promotions?status=PENDING")"
@ -188,6 +267,105 @@ else
fail "Pending promotions list should include submitted team skill"
fi
PROMOTION_ID="$(JSON_INPUT="$PENDING_PROMOTIONS_RESPONSE" python3 - "$SKILL_ID" <<'PY'
import json
import os
import sys
skill_id = int(sys.argv[1])
items = json.loads(os.environ["JSON_INPUT"])["data"]["items"]
match = next((item for item in items if item["sourceSkillId"] == skill_id), None)
print(match["id"] if match else "")
PY
)"
if [[ -z "$PROMOTION_ID" ]]; then
fail "Pending promotions should contain the submitted team skill"
exit 1
fi
UNAUTHORIZED_APPROVAL_RESPONSE="$(curl -sS -H "X-Mock-User-Id: local-user" -b "$USER_COOKIE" -c "$USER_COOKIE" \
-H "X-XSRF-TOKEN: $USER_CSRF" \
-H "Content-Type: application/json" \
-X POST "$BASE_URL/api/web/promotions/$PROMOTION_ID/approve" \
-d '{"comment":"unauthorized"}')"
assert_code "Regular user cannot approve a promotion" "$UNAUTHORIZED_APPROVAL_RESPONSE" "403"
PROMOTION_AFTER_DENIAL_RESPONSE="$(curl -sS -H "X-Mock-User-Id: local-admin" -b "$ADMIN_COOKIE" -c "$ADMIN_COOKIE" \
"$BASE_URL/api/web/promotions?status=PENDING")"
assert_code "Admin can reload pending promotions after denied approval" "$PROMOTION_AFTER_DENIAL_RESPONSE" "0"
if JSON_INPUT="$PROMOTION_AFTER_DENIAL_RESPONSE" python3 - "$PROMOTION_ID" <<'PY'
import json
import os
import sys
promotion_id = int(sys.argv[1])
items = json.loads(os.environ["JSON_INPUT"])["data"]["items"]
match = next((item for item in items if item["id"] == promotion_id), None)
raise SystemExit(0 if match and match["status"] == "PENDING" and match.get("targetSkillId") is None else 1)
PY
then
pass "Denied approval keeps the promotion pending without a target"
else
fail "Denied approval should keep the promotion pending without a target"
fi
APPROVE_PROMOTION_RESPONSE="$(curl -sS -H "X-Mock-User-Id: local-admin" -b "$ADMIN_COOKIE" -c "$ADMIN_COOKIE" \
-H "X-XSRF-TOKEN: $ADMIN_CSRF" \
-H "Content-Type: application/json" \
-X POST "$BASE_URL/api/web/promotions/$PROMOTION_ID/approve" \
-d '{"comment":"approved by promotion smoke"}')"
assert_code "Admin can approve promotion to global namespace" "$APPROVE_PROMOTION_RESPONSE" "0"
TARGET_SKILL_ID="$(json_field "$APPROVE_PROMOTION_RESPONSE" "data.targetSkillId")"
if [[ -n "$TARGET_SKILL_ID" && "$TARGET_SKILL_ID" != "None" && "$TARGET_SKILL_ID" != "null" ]]; then
pass "Approved promotion exposes target skill id"
else
fail "Approved promotion should expose target skill id"
fi
GLOBAL_VERSIONS_RESPONSE="$(curl -sS \
"$BASE_URL/api/web/skills/global/$SKILL_SLUG/versions")"
assert_code "Promoted global skill versions are visible" "$GLOBAL_VERSIONS_RESPONSE" "0"
if JSON_INPUT="$GLOBAL_VERSIONS_RESPONSE" python3 - <<'PY'
import json
import os
items = json.loads(os.environ["JSON_INPUT"])["data"]["items"]
raise SystemExit(0 if any(
item["version"] == "1.0.0"
and item["status"] == "PUBLISHED"
and item["downloadAvailable"]
for item in items
) else 1)
PY
then
pass "Promoted global version is published and downloadable"
else
fail "Promoted global version should be PUBLISHED with downloadAvailable=true"
fi
GLOBAL_BUNDLE="$WORK_DIR/global-skill.zip"
GLOBAL_DOWNLOAD_STATUS="$(curl -sS -L -o "$GLOBAL_BUNDLE" -w '%{http_code}' \
"$BASE_URL/api/web/skills/global/$SKILL_SLUG/versions/1.0.0/download")"
if [[ "$GLOBAL_DOWNLOAD_STATUS" == "200" ]]; then
pass "Promoted global version download returns HTTP 200"
else
fail "Promoted global version download should return HTTP 200 (got $GLOBAL_DOWNLOAD_STATUS)"
fi
if python3 - "$GLOBAL_BUNDLE" <<'PY'
import sys
import zipfile
with zipfile.ZipFile(sys.argv[1]) as archive:
content = archive.read("SKILL.md").decode("utf-8")
raise SystemExit(0 if "Body" in content.splitlines() else 1)
PY
then
pass "Promoted global bundle contains the source SKILL.md"
else
fail "Promoted global bundle should contain the source SKILL.md"
fi
echo
echo "Results: $PASS passed, $FAIL failed"
if [[ "$FAIL" -ne 0 ]]; then