feat: v0.1.18 — TagQueue/UntagQueue/ListQueueTags/ChangeMessageVisibilityBatch, JWT auth in UI API tests, hardcore_test JWT fix, Helm chart v0.1.18
This commit is contained in:
@@ -18,7 +18,6 @@ type MessageAttribute struct {
|
|||||||
StringValue string `json:"StringValue,omitempty" xml:"StringValue,omitempty"`
|
StringValue string `json:"StringValue,omitempty" xml:"StringValue,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type SqsMessage struct {
|
type SqsMessage struct {
|
||||||
MessageBody string
|
MessageBody string
|
||||||
Uuid string
|
Uuid string
|
||||||
|
|||||||
+16
-16
@@ -97,23 +97,23 @@ func encodeResponse(w http.ResponseWriter, req *http.Request, statusCode int, bo
|
|||||||
|
|
||||||
// routingTableV1 — только SQS actions (SNS удалён)
|
// routingTableV1 — только SQS actions (SNS удалён)
|
||||||
var routingTableV1 = map[string]func(r *http.Request) (int, interfaces.AbstractResponseBody){
|
var routingTableV1 = map[string]func(r *http.Request) (int, interfaces.AbstractResponseBody){
|
||||||
"CreateQueue": sqs.CreateQueueV1,
|
"CreateQueue": sqs.CreateQueueV1,
|
||||||
"ListQueues": sqs.ListQueuesV1,
|
"ListQueues": sqs.ListQueuesV1,
|
||||||
"GetQueueAttributes": sqs.GetQueueAttributesV1,
|
"GetQueueAttributes": sqs.GetQueueAttributesV1,
|
||||||
"SetQueueAttributes": sqs.SetQueueAttributesV1,
|
"SetQueueAttributes": sqs.SetQueueAttributesV1,
|
||||||
"SendMessage": sqs.SendMessageV1,
|
"SendMessage": sqs.SendMessageV1,
|
||||||
"ReceiveMessage": sqs.ReceiveMessageV1,
|
"ReceiveMessage": sqs.ReceiveMessageV1,
|
||||||
"ChangeMessageVisibility": sqs.ChangeMessageVisibilityV1,
|
"ChangeMessageVisibility": sqs.ChangeMessageVisibilityV1,
|
||||||
"DeleteMessage": sqs.DeleteMessageV1,
|
"DeleteMessage": sqs.DeleteMessageV1,
|
||||||
"GetQueueUrl": sqs.GetQueueUrlV1,
|
"GetQueueUrl": sqs.GetQueueUrlV1,
|
||||||
"PurgeQueue": sqs.PurgeQueueV1,
|
"PurgeQueue": sqs.PurgeQueueV1,
|
||||||
"DeleteQueue": sqs.DeleteQueueV1,
|
"DeleteQueue": sqs.DeleteQueueV1,
|
||||||
"SendMessageBatch": sqs.SendMessageBatchV1,
|
"SendMessageBatch": sqs.SendMessageBatchV1,
|
||||||
"DeleteMessageBatch": sqs.DeleteMessageBatchV1,
|
"DeleteMessageBatch": sqs.DeleteMessageBatchV1,
|
||||||
"ChangeMessageVisibilityBatch": sqs.ChangeMessageVisibilityBatchV1,
|
"ChangeMessageVisibilityBatch": sqs.ChangeMessageVisibilityBatchV1,
|
||||||
"TagQueue": sqs.TagQueueV1,
|
"TagQueue": sqs.TagQueueV1,
|
||||||
"UntagQueue": sqs.UntagQueueV1,
|
"UntagQueue": sqs.UntagQueueV1,
|
||||||
"ListQueueTags": sqs.ListQueueTagsV1,
|
"ListQueueTags": sqs.ListQueueTagsV1,
|
||||||
}
|
}
|
||||||
|
|
||||||
func health(w http.ResponseWriter, req *http.Request) {
|
func health(w http.ResponseWriter, req *http.Request) {
|
||||||
|
|||||||
+150
-62
@@ -1,10 +1,10 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# hardcore_test.sh — расширенный тест shared-sqs
|
# hardcore_test.sh — расширенный тест shared-sqs
|
||||||
# Изменено: 2026-04-09
|
# Created: 2026-04-09
|
||||||
# Покрывает: Admin API, AWS CLI CRUD, awscurl CRUD, кросс-доставка,
|
# Updated: 2026-04-11 — добавлены TagQueue/UntagQueue/ListQueueTags/ChangeMessageVisibilityBatch,
|
||||||
# изоляция тенантов, невалидный ввод, спецсимволы,
|
# UI API с JWT авторизацией (nubes api_token).
|
||||||
# visibility timeout, batch, лимиты очередей, cleanup тенанта,
|
# Покрывает: все 17 SQS команд, Admin API, AWS CLI + awscurl, кросс-доставка,
|
||||||
# UI API (create/delete queue, send/peek/purge messages, изоляция).
|
# изоляция тенантов, невалидный ввод, batch, лимиты, UI API с JWT.
|
||||||
# Запуск: BASE_URL=https://qu.kube5s.ru ADMIN_TOKEN=... bash tests/hardcore_test.sh
|
# Запуск: BASE_URL=https://qu.kube5s.ru ADMIN_TOKEN=... bash tests/hardcore_test.sh
|
||||||
|
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
@@ -15,6 +15,8 @@ set -uo pipefail
|
|||||||
BASE_URL="${BASE_URL:-https://qu.kube5s.ru}"
|
BASE_URL="${BASE_URL:-https://qu.kube5s.ru}"
|
||||||
ADMIN_TOKEN="${ADMIN_TOKEN:-sqs-admin-7a7d8bd0c060a75c198d48680f34077a}"
|
ADMIN_TOKEN="${ADMIN_TOKEN:-sqs-admin-7a7d8bd0c060a75c198d48680f34077a}"
|
||||||
AWSCURL="${AWSCURL:-/home/naeel/.local/bin/awscurl}"
|
AWSCURL="${AWSCURL:-/home/naeel/.local/bin/awscurl}"
|
||||||
|
# JWT-токен для авторизации в UI API (/ui/api/*); получается через nubes.ru → api_token
|
||||||
|
NUBES_JWT="${NUBES_JWT:-eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhdXRoLWFwaSIsInN1YiI6IjAxOTllMzI1LTFjZGYtN2NkYS05MzE5LWU1MzAyYTg1ZTI5MSIsImV4cCI6MTc4NjkzMjI2MCwiaWF0IjoxNzcxMzgwMjYwLCJqdGkiOiIzOTQ3ZTgyMy0yNjljLTQ0MTAtYmU0My1iNGVkNTc1Njg0ZTQiLCJhdXRoX3RpbWUiOjAsInR5cCI6IiIsImF6cCI6IiIsInNlc3Npb25fc3RhdGUiOiIiLCJhY3IiOiIiLCJhbGxvd2VkLW9yaWdpbnMiOm51bGwsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6bnVsbH0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpudWxsfX0sInNjb3BlIjoiIiwic2lkIjoiIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJuYW1lIjoiIiwiQ2xpZW50SUQiOiIiLCJncm91cHMiOm51bGwsInByZWZlcnJlZF91c2VybmFtZSI6IiIsImdpdmVuX25hbWUiOiIiLCJmYW1pbHlfbmFtZSI6IiIsImVtYWlsIjoidGF6ZXRAbmFyb2QucnUifQ.hzpIIqNWkKIoYUXDaLY7DLyGKH70rz0ZTqanv19qxF10i3N1t1g_KknA4Qsw1MduTyLzIz7y5SRSr4PSQ1gzR0vB_C0GudSFUhyBNNKkS4ClhRDWW9eN_IIEljbiJMLQi2L07XJ7Y5DQ0sIHRAPLkreCDFMKQ0yTCrKoScCJIDuUqzaTcOaX-hfjaxW8iV0SZMDxl0C5O3tke0btxkaLBaAcWH0V-1yu2r2m29fyU33FqikF0xAcDXiuZphfsrShKQYArZjKAphYCP_Vpmr-1sdjinkn8sPSk1qZny0rka8G6WVZUGaZSOnW8SYNLVUwdqtuQmK-Y18o7U0Suzrsjg}"
|
||||||
TS=$(date +%s)
|
TS=$(date +%s)
|
||||||
|
|
||||||
PASS=0
|
PASS=0
|
||||||
@@ -77,6 +79,21 @@ sqs() {
|
|||||||
aws --endpoint-url "$BASE_URL" --output json sqs "$@" 2>&1
|
aws --endpoint-url "$BASE_URL" --output json sqs "$@" 2>&1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ui_jwt — UI API запрос с JWT авторизацией
|
||||||
|
ui_jwt() {
|
||||||
|
local method="$1" path="$2" body="${3:-}"
|
||||||
|
if [[ -n "$body" ]]; then
|
||||||
|
curl -s --max-time 15 -X "$method" \
|
||||||
|
-H "Authorization: Bearer $NUBES_JWT" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "$body" "${BASE_URL}/ui/api${path}" 2>&1
|
||||||
|
else
|
||||||
|
curl -s --max-time 15 -X "$method" \
|
||||||
|
-H "Authorization: Bearer $NUBES_JWT" \
|
||||||
|
"${BASE_URL}/ui/api${path}" 2>&1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# acurl — awscurl враппер для SQS Query Protocol
|
# acurl — awscurl враппер для SQS Query Protocol
|
||||||
# $1=ak, $2=sk, остальное — form-encoded body (Action=...&...)
|
# $1=ak, $2=sk, остальное — form-encoded body (Action=...&...)
|
||||||
acurl() {
|
acurl() {
|
||||||
@@ -516,6 +533,85 @@ sqs "$T1_AK" "$T1_SK" delete-queue --queue-url "$BQURL" >/dev/null 2>&1 || true
|
|||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════
|
||||||
|
echo "── 10б. TagQueue / UntagQueue / ListQueueTags ──"
|
||||||
|
# ═══════════════════════════════════════════
|
||||||
|
|
||||||
|
TAG_Q="hc-tag-${TS}"
|
||||||
|
TAG_QURL=$(qurl "$T1_ID" "$TAG_Q")
|
||||||
|
sqs "$T1_AK" "$T1_SK" create-queue --queue-name "$TAG_Q" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
# TagQueue — добавить теги
|
||||||
|
R=$(sqs "$T1_AK" "$T1_SK" tag-queue --queue-url "$TAG_QURL" --tags env=prod,team=backend 2>&1 && echo "OK" || echo "FAIL")
|
||||||
|
check "TagQueue: добавить теги → OK" "$R" "OK"
|
||||||
|
|
||||||
|
# ListQueueTags — теги видны
|
||||||
|
R=$(sqs "$T1_AK" "$T1_SK" list-queue-tags --queue-url "$TAG_QURL")
|
||||||
|
check "ListQueueTags: env=prod видна" "$R" "env|prod"
|
||||||
|
check "ListQueueTags: team=backend видна" "$R" "team|backend"
|
||||||
|
|
||||||
|
# TagQueue — обновить существующий тег (merge)
|
||||||
|
R=$(sqs "$T1_AK" "$T1_SK" tag-queue --queue-url "$TAG_QURL" --tags env=staging 2>&1 && echo "OK" || echo "FAIL")
|
||||||
|
check "TagQueue: обновить тег env → OK" "$R" "OK"
|
||||||
|
|
||||||
|
R=$(sqs "$T1_AK" "$T1_SK" list-queue-tags --queue-url "$TAG_QURL")
|
||||||
|
check "ListQueueTags: env обновлён до staging" "$R" "staging"
|
||||||
|
check "ListQueueTags: team не удалён при обновлении" "$R" "backend"
|
||||||
|
|
||||||
|
# UntagQueue — удалить один тег
|
||||||
|
R=$(sqs "$T1_AK" "$T1_SK" untag-queue --queue-url "$TAG_QURL" --tag-keys env 2>&1 && echo "OK" || echo "FAIL")
|
||||||
|
check "UntagQueue: удалить тег env → OK" "$R" "OK"
|
||||||
|
|
||||||
|
R=$(sqs "$T1_AK" "$T1_SK" list-queue-tags --queue-url "$TAG_QURL")
|
||||||
|
check_not "ListQueueTags: env удалён" "$R" '"env"'
|
||||||
|
check "ListQueueTags: team остался" "$R" "backend"
|
||||||
|
|
||||||
|
sqs "$T1_AK" "$T1_SK" delete-queue --queue-url "$TAG_QURL" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════
|
||||||
|
echo "── 10в. ChangeMessageVisibilityBatch ──"
|
||||||
|
# ═══════════════════════════════════════════
|
||||||
|
|
||||||
|
CMVB_Q="hc-cmvb-${TS}"
|
||||||
|
CMVB_QURL=$(qurl "$T1_ID" "$CMVB_Q")
|
||||||
|
sqs "$T1_AK" "$T1_SK" create-queue --queue-name "$CMVB_Q" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
# Отправляем 3 сообщения
|
||||||
|
for i in 1 2 3; do
|
||||||
|
sqs "$T1_AK" "$T1_SK" send-message --queue-url "$CMVB_QURL" --message-body "cmvb-msg-${i}" >/dev/null 2>&1 || true
|
||||||
|
done
|
||||||
|
|
||||||
|
# Получаем все 3 (visibility=60 чтобы не конкурировали)
|
||||||
|
R=$(sqs "$T1_AK" "$T1_SK" receive-message --queue-url "$CMVB_QURL" --max-number-of-messages 3 --visibility-timeout 60)
|
||||||
|
CMVB_RECEIPTS=$(echo "$R" | jq -r '.Messages[]?.ReceiptHandle' 2>/dev/null || true)
|
||||||
|
|
||||||
|
if [[ -n "$CMVB_RECEIPTS" ]]; then
|
||||||
|
CMVB_ENTRIES=$(echo "$CMVB_RECEIPTS" | python3 -c "
|
||||||
|
import sys
|
||||||
|
entries = []
|
||||||
|
for i, rh in enumerate(sys.stdin.read().strip().split('\n'), 1):
|
||||||
|
if rh:
|
||||||
|
entries.append('{\"Id\":\"e%d\",\"ReceiptHandle\":\"%s\",\"VisibilityTimeout\":120}' % (i, rh))
|
||||||
|
print('[' + ','.join(entries) + ']')
|
||||||
|
" 2>/dev/null || echo "[]")
|
||||||
|
if [[ "$CMVB_ENTRIES" != "[]" ]]; then
|
||||||
|
R=$(sqs "$T1_AK" "$T1_SK" change-message-visibility-batch \
|
||||||
|
--queue-url "$CMVB_QURL" \
|
||||||
|
--entries "$CMVB_ENTRIES")
|
||||||
|
check "ChangeMessageVisibilityBatch → Successful" "$R" "Successful"
|
||||||
|
check_not "ChangeMessageVisibilityBatch → нет Failed" "$R" '"Failed"'
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
TOTAL=$((TOTAL + 1)); FAIL=$((FAIL + 1))
|
||||||
|
echo " ❌ ChangeMessageVisibilityBatch (нет сообщений для теста)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
sqs "$T1_AK" "$T1_SK" delete-queue --queue-url "$CMVB_QURL" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
# ═══════════════════════════════════════════
|
# ═══════════════════════════════════════════
|
||||||
echo "── 11. Лимит очередей (max_queues=2) ──"
|
echo "── 11. Лимит очередей (max_queues=2) ──"
|
||||||
# ═══════════════════════════════════════════
|
# ═══════════════════════════════════════════
|
||||||
@@ -900,28 +996,37 @@ sqs "$T1_AK" "$T1_SK" delete-queue --queue-url "$ENC_QURL" >/dev/null 2>&1 || tr
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ═══════════════════════════════════════════
|
# ═══════════════════════════════════════════
|
||||||
echo "── 24. UI API — создание очереди через /ui/api ──"
|
echo "── 24. UI API — авторизация через JWT ──"
|
||||||
# ═══════════════════════════════════════════
|
# ═══════════════════════════════════════════
|
||||||
|
|
||||||
UI_T=$(admin_api POST /admin/tenants '{"name":"ui-test-'"$TS"'","max_queues":10}')
|
# Авторизуемся через nubes JWT
|
||||||
UI_TID=$(echo "$UI_T" | jq -r '.id')
|
UI_AUTH=$(curl -s --max-time 15 -X POST \
|
||||||
UI_AK=$(echo "$UI_T" | jq -r '.access_key')
|
|
||||||
UI_SK=$(echo "$UI_T" | jq -r '.secret_key')
|
|
||||||
|
|
||||||
# Создать очередь через UI API
|
|
||||||
R=$(curl -s --max-time 15 -X POST "${BASE_URL}/ui/api/tenants/${UI_TID}/queues" \
|
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{"name":"ui-queue-1"}')
|
-d "{\"token\":\"$NUBES_JWT\"}" \
|
||||||
|
"${BASE_URL}/ui/api/auth" 2>&1)
|
||||||
|
check "UI API: POST /auth → tenant_id" "$UI_AUTH" "tenant_id"
|
||||||
|
UI_TID=$(echo "$UI_AUTH" | jq -r '.tenant_id' 2>/dev/null || true)
|
||||||
|
UI_AK=$(echo "$UI_AUTH" | jq -r '.access_key' 2>/dev/null || true)
|
||||||
|
UI_SK=$(echo "$UI_AUTH" | jq -r '.secret_key' 2>/dev/null || true)
|
||||||
|
|
||||||
|
echo " UI Tenant ID: $UI_TID"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════
|
||||||
|
echo "── 25б. UI API — создание очереди ──"
|
||||||
|
# ═══════════════════════════════════════════
|
||||||
|
|
||||||
|
# Создать очередь через UI API с JWT
|
||||||
|
R=$(ui_jwt POST "/tenants/${UI_TID}/queues" '{"name":"ui-queue-1"}')
|
||||||
check "UI API: создать очередь ui-queue-1 → имя" "$R" "ui-queue-1"
|
check "UI API: создать очередь ui-queue-1 → имя" "$R" "ui-queue-1"
|
||||||
|
|
||||||
# Дубль — та же очередь, не ошибка (идемпотентно)
|
# Дубль — та же очередь, не ошибка (идемпотентно)
|
||||||
R=$(curl -s --max-time 15 -X POST "${BASE_URL}/ui/api/tenants/${UI_TID}/queues" \
|
R=$(ui_jwt POST "/tenants/${UI_TID}/queues" '{"name":"ui-queue-1"}')
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"name":"ui-queue-1"}')
|
|
||||||
check "UI API: повторное создание — idempotent (нет 500)" "$R" "ui-queue-1|error"
|
check "UI API: повторное создание — idempotent (нет 500)" "$R" "ui-queue-1|error"
|
||||||
|
|
||||||
# Список очередей через /ui/api содержит созданную
|
# Список очередей через /ui/api содержит созданную
|
||||||
R=$(curl -s --max-time 15 "${BASE_URL}/ui/api/tenants/${UI_TID}/queues")
|
R=$(ui_jwt GET "/tenants/${UI_TID}/queues")
|
||||||
check "UI API: GET /queues содержит ui-queue-1" "$R" "ui-queue-1"
|
check "UI API: GET /queues содержит ui-queue-1" "$R" "ui-queue-1"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
@@ -931,27 +1036,20 @@ echo "── 25. UI API — отправка и peek сообщений ──"
|
|||||||
# ═══════════════════════════════════════════
|
# ═══════════════════════════════════════════
|
||||||
|
|
||||||
# Отправить сообщение через UI
|
# Отправить сообщение через UI
|
||||||
R=$(curl -s --max-time 15 -X POST \
|
R=$(ui_jwt POST "/tenants/${UI_TID}/queues/ui-queue-1/messages" '{"body":"hello-from-ui"}')
|
||||||
"${BASE_URL}/ui/api/tenants/${UI_TID}/queues/ui-queue-1/messages" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"body":"hello-from-ui"}')
|
|
||||||
check "UI API: send message → id" "$R" '"id"'
|
check "UI API: send message → id" "$R" '"id"'
|
||||||
check "UI API: send message → status sent" "$R" "sent"
|
check "UI API: send message → status sent" "$R" "sent"
|
||||||
|
|
||||||
# Отправить ещё одно
|
# Отправить ещё одно
|
||||||
curl -s --max-time 15 -X POST \
|
ui_jwt POST "/tenants/${UI_TID}/queues/ui-queue-1/messages" '{"body":"second-message"}' >/dev/null 2>&1 || true
|
||||||
"${BASE_URL}/ui/api/tenants/${UI_TID}/queues/ui-queue-1/messages" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"body":"second-message"}' >/dev/null 2>&1
|
|
||||||
|
|
||||||
# Peek — должны видеть оба, без ReceiptHandle
|
# Peek — должны видеть оба, без ReceiptHandle
|
||||||
R=$(curl -s --max-time 15 \
|
R=$(ui_jwt GET "/tenants/${UI_TID}/queues/ui-queue-1/messages")
|
||||||
"${BASE_URL}/ui/api/tenants/${UI_TID}/queues/ui-queue-1/messages")
|
|
||||||
check "UI API: peek — hello-from-ui" "$R" "hello-from-ui"
|
check "UI API: peek — hello-from-ui" "$R" "hello-from-ui"
|
||||||
check "UI API: peek — second-message" "$R" "second-message"
|
check "UI API: peek — second-message" "$R" "second-message"
|
||||||
check_not "UI API: peek — НЕТ receipt_handle" "$R" "receipt_handle"
|
check_not "UI API: peek — НЕТ receipt_handle" "$R" "receipt_handle"
|
||||||
|
|
||||||
# Peek не даёт возможности ReceiveMessage через SQS (сообщения не зафиксированы)
|
# Peek не consumed — SQS receive-message тоже видит сообщения
|
||||||
QURL_UI=$(sqs "$UI_AK" "$UI_SK" list-queues 2>&1 \
|
QURL_UI=$(sqs "$UI_AK" "$UI_SK" list-queues 2>&1 \
|
||||||
| python3 -c "import sys,json; d=json.load(sys.stdin); urls=d.get('QueueUrls',[]); print(next((u for u in urls if 'ui-queue-1' in u),''))" 2>/dev/null || true)
|
| python3 -c "import sys,json; d=json.load(sys.stdin); urls=d.get('QueueUrls',[]); print(next((u for u in urls if 'ui-queue-1' in u),''))" 2>/dev/null || true)
|
||||||
if [[ -n "$QURL_UI" ]]; then
|
if [[ -n "$QURL_UI" ]]; then
|
||||||
@@ -965,24 +1063,18 @@ echo ""
|
|||||||
echo "── 26. UI API — purge очереди ──"
|
echo "── 26. UI API — purge очереди ──"
|
||||||
# ═══════════════════════════════════════════
|
# ═══════════════════════════════════════════
|
||||||
|
|
||||||
R=$(curl -s --max-time 15 -X DELETE \
|
R=$(ui_jwt DELETE "/tenants/${UI_TID}/queues/ui-queue-1/messages")
|
||||||
"${BASE_URL}/ui/api/tenants/${UI_TID}/queues/ui-queue-1/messages")
|
|
||||||
check "UI API: purge → 200/204 (нет ошибки)" "$R" "^\s*$|purged|ok|{}"
|
check "UI API: purge → 200/204 (нет ошибки)" "$R" "^\s*$|purged|ok|{}"
|
||||||
|
|
||||||
# После purge peek должен вернуть пустой массив
|
# После purge peek должен вернуть пустой массив
|
||||||
R=$(curl -s --max-time 15 \
|
R=$(ui_jwt GET "/tenants/${UI_TID}/queues/ui-queue-1/messages")
|
||||||
"${BASE_URL}/ui/api/tenants/${UI_TID}/queues/ui-queue-1/messages")
|
|
||||||
check_not "UI API: после purge peek пуст (нет hello-from-ui)" "$R" "hello-from-ui"
|
check_not "UI API: после purge peek пуст (нет hello-from-ui)" "$R" "hello-from-ui"
|
||||||
|
|
||||||
# Отправим ещё одно после purge — должно работать
|
# Отправим ещё одно после purge — должно работать
|
||||||
R=$(curl -s --max-time 15 -X POST \
|
R=$(ui_jwt POST "/tenants/${UI_TID}/queues/ui-queue-1/messages" '{"body":"after-purge"}')
|
||||||
"${BASE_URL}/ui/api/tenants/${UI_TID}/queues/ui-queue-1/messages" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"body":"after-purge"}')
|
|
||||||
check "UI API: send после purge работает" "$R" '"id"'
|
check "UI API: send после purge работает" "$R" '"id"'
|
||||||
|
|
||||||
R=$(curl -s --max-time 15 \
|
R=$(ui_jwt GET "/tenants/${UI_TID}/queues/ui-queue-1/messages")
|
||||||
"${BASE_URL}/ui/api/tenants/${UI_TID}/queues/ui-queue-1/messages")
|
|
||||||
check "UI API: peek после purge → after-purge" "$R" "after-purge"
|
check "UI API: peek после purge → after-purge" "$R" "after-purge"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
@@ -992,15 +1084,14 @@ echo "── 27. UI API — удаление очереди ──"
|
|||||||
# ═══════════════════════════════════════════
|
# ═══════════════════════════════════════════
|
||||||
|
|
||||||
# Создать ещё одну очередь чтобы удалить
|
# Создать ещё одну очередь чтобы удалить
|
||||||
curl -s --max-time 15 -X POST "${BASE_URL}/ui/api/tenants/${UI_TID}/queues" \
|
ui_jwt POST "/tenants/${UI_TID}/queues" '{"name":"ui-to-delete"}' >/dev/null 2>&1 || true
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"name":"ui-to-delete"}' >/dev/null 2>&1
|
|
||||||
|
|
||||||
R=$(curl -s --max-time 15 "${BASE_URL}/ui/api/tenants/${UI_TID}/queues")
|
R=$(ui_jwt GET "/tenants/${UI_TID}/queues")
|
||||||
check "UI API: ui-to-delete создана" "$R" "ui-to-delete"
|
check "UI API: ui-to-delete создана" "$R" "ui-to-delete"
|
||||||
|
|
||||||
# Удалить
|
# Удалить
|
||||||
HTTP=$(curl -s --max-time 15 -o /dev/null -w "%{http_code}" -X DELETE \
|
HTTP=$(curl -s --max-time 15 -o /dev/null -w "%{http_code}" -X DELETE \
|
||||||
|
-H "Authorization: Bearer $NUBES_JWT" \
|
||||||
"${BASE_URL}/ui/api/tenants/${UI_TID}/queues/ui-to-delete")
|
"${BASE_URL}/ui/api/tenants/${UI_TID}/queues/ui-to-delete")
|
||||||
if [[ "$HTTP" == "204" || "$HTTP" == "200" ]]; then
|
if [[ "$HTTP" == "204" || "$HTTP" == "200" ]]; then
|
||||||
PASS=$((PASS+1)); TOTAL=$((TOTAL+1))
|
PASS=$((PASS+1)); TOTAL=$((TOTAL+1))
|
||||||
@@ -1010,11 +1101,12 @@ else
|
|||||||
echo " ❌ UI API: DELETE /queues/ui-to-delete → HTTP $HTTP"
|
echo " ❌ UI API: DELETE /queues/ui-to-delete → HTTP $HTTP"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
R=$(curl -s --max-time 15 "${BASE_URL}/ui/api/tenants/${UI_TID}/queues")
|
R=$(ui_jwt GET "/tenants/${UI_TID}/queues")
|
||||||
check_not "UI API: ui-to-delete больше не в списке" "$R" "ui-to-delete"
|
check_not "UI API: ui-to-delete больше не в списке" "$R" "ui-to-delete"
|
||||||
|
|
||||||
# Удаление несуществующей очереди → 404
|
# Удаление несуществующей очереди → 404
|
||||||
HTTP=$(curl -s --max-time 15 -o /dev/null -w "%{http_code}" -X DELETE \
|
HTTP=$(curl -s --max-time 15 -o /dev/null -w "%{http_code}" -X DELETE \
|
||||||
|
-H "Authorization: Bearer $NUBES_JWT" \
|
||||||
"${BASE_URL}/ui/api/tenants/${UI_TID}/queues/no-such-queue")
|
"${BASE_URL}/ui/api/tenants/${UI_TID}/queues/no-such-queue")
|
||||||
if [[ "$HTTP" == "404" ]]; then
|
if [[ "$HTTP" == "404" ]]; then
|
||||||
PASS=$((PASS+1)); TOTAL=$((TOTAL+1))
|
PASS=$((PASS+1)); TOTAL=$((TOTAL+1))
|
||||||
@@ -1030,28 +1122,24 @@ echo ""
|
|||||||
echo "── 28. UI API — изоляция: чужой тенант недоступен ──"
|
echo "── 28. UI API — изоляция: чужой тенант недоступен ──"
|
||||||
# ═══════════════════════════════════════════
|
# ═══════════════════════════════════════════
|
||||||
|
|
||||||
# Создать второго тенанта и попробовать смотреть его очереди через UI API чужим ID
|
# Создать второго admin тенанта для теста изоляции
|
||||||
UI_T2=$(admin_api POST /admin/tenants '{"name":"ui-other-'"$TS"'","max_queues":5}')
|
UI_T2=$(admin_api POST /admin/tenants '{"name":"ui-other-'"$TS"'","max_queues":5}')
|
||||||
UI_T2ID=$(echo "$UI_T2" | jq -r '.id')
|
UI_T2ID=$(echo "$UI_T2" | jq -r '.id')
|
||||||
curl -s --max-time 15 -X POST "${BASE_URL}/ui/api/tenants/${UI_T2ID}/queues" \
|
UI_T2AK=$(echo "$UI_T2" | jq -r '.access_key')
|
||||||
-H "Content-Type: application/json" \
|
UI_T2SK=$(echo "$UI_T2" | jq -r '.secret_key')
|
||||||
-d '{"name":"secret-queue"}' >/dev/null 2>&1
|
|
||||||
curl -s --max-time 15 -X POST \
|
|
||||||
"${BASE_URL}/ui/api/tenants/${UI_T2ID}/queues/secret-queue/messages" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"body":"secret-data"}' >/dev/null 2>&1
|
|
||||||
|
|
||||||
# T1 пытается peek очереди T2 — должен получить 404 (тенант не найден)
|
# Создаём очередь во втором тенанте через admin API (не через UI JWT — JWT привязан к одному тенанту)
|
||||||
HTTP=$(curl -s --max-time 15 -o /dev/null -w "%{http_code}" \
|
T2_Q_URL="${BASE_URL}/${UI_T2ID}/secret-queue"
|
||||||
"${BASE_URL}/ui/api/tenants/${UI_T2ID}/queues/secret-queue/messages")
|
sqs "$UI_T2AK" "$UI_T2SK" create-queue --queue-name "secret-queue" >/dev/null 2>&1 || true
|
||||||
# UI API публичный (без auth), но очередь принадлежит другому тенанту — данные не должны пересекаться
|
sqs "$UI_T2AK" "$UI_T2SK" send-message --queue-url "$T2_Q_URL" --message-body "secret-data" >/dev/null 2>&1 || true
|
||||||
# Проверяем что данные T2 недоступны через UI T1
|
|
||||||
R=$(curl -s --max-time 15 "${BASE_URL}/ui/api/tenants/${UI_TID}/queues")
|
|
||||||
check_not "UI API: T1 не видит очереди T2" "$R" "secret-queue"
|
|
||||||
|
|
||||||
R_T1_PEEK=$(curl -s --max-time 15 \
|
# Текущий JWT тенант не видит очереди другого admin-created тенанта
|
||||||
"${BASE_URL}/ui/api/tenants/${UI_TID}/queues/secret-queue/messages")
|
R=$(ui_jwt GET "/tenants/${UI_TID}/queues")
|
||||||
check "UI API: peek чужой очереди через ID T1 → 404/error" "$R_T1_PEEK" "not found|error|404"
|
check_not "UI API: JWT тенант не видит очереди T2" "$R" "secret-queue"
|
||||||
|
|
||||||
|
# peek чужой очереди через JWT тенант → ошибка
|
||||||
|
R=$(ui_jwt GET "/tenants/${UI_TID}/queues/secret-queue/messages")
|
||||||
|
check "UI API: peek несуществующей очереди → ошибка" "$R" "not found|error|404"
|
||||||
|
|
||||||
# Cleanup UI тенантов
|
# Cleanup UI тенантов
|
||||||
admin_api DELETE "/admin/tenants/${UI_T2ID}" >/dev/null 2>&1 || true
|
admin_api DELETE "/admin/tenants/${UI_T2ID}" >/dev/null 2>&1 || true
|
||||||
|
|||||||
+148
-57
@@ -1,17 +1,20 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# tests/quick_test.sh — Быстрая проверка shared-sqs (smoke test)
|
# tests/quick_test.sh — Быстрая проверка shared-sqs (smoke test)
|
||||||
# Created: 2026-04-09
|
# Created: 2026-04-09
|
||||||
# Покрывает: создание тенанта + очереди, send/receive/delete сообщения,
|
# Updated: 2026-04-11 — полная поддержка JWT (nubes api_token), все 17 SQS команд,
|
||||||
# UI API peek/send/purge, удаление очереди и тенанта.
|
# TagQueue/UntagQueue/ListQueueTags/ChangeMessageVisibilityBatch,
|
||||||
|
# UI API с авторизацией через Bearer JWT.
|
||||||
|
# Покрывает: health, admin CRUD, все SQS команды, UI API с JWT.
|
||||||
# Требования: curl, aws CLI, python3
|
# Требования: curl, aws CLI, python3
|
||||||
# Запуск:
|
# Запуск:
|
||||||
# bash tests/quick_test.sh
|
# bash tests/quick_test.sh
|
||||||
# BASE_URL=https://qu.kube5s.ru ADMIN_TOKEN=... bash tests/quick_test.sh
|
# BASE_URL=https://qu.kube5s.ru ADMIN_TOKEN=... NUBES_JWT=... bash tests/quick_test.sh
|
||||||
|
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
|
|
||||||
BASE_URL="${BASE_URL:-https://qu.kube5s.ru}"
|
BASE_URL="${BASE_URL:-https://qu.kube5s.ru}"
|
||||||
ADMIN_TOKEN="${ADMIN_TOKEN:-sqs-admin-7a7d8bd0c060a75c198d48680f34077a}"
|
ADMIN_TOKEN="${ADMIN_TOKEN:-sqs-admin-7a7d8bd0c060a75c198d48680f34077a}"
|
||||||
|
NUBES_JWT="${NUBES_JWT:-eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhdXRoLWFwaSIsInN1YiI6IjAxOTllMzI1LTFjZGYtN2NkYS05MzE5LWU1MzAyYTg1ZTI5MSIsImV4cCI6MTc4NjkzMjI2MCwiaWF0IjoxNzcxMzgwMjYwLCJqdGkiOiIzOTQ3ZTgyMy0yNjljLTQ0MTAtYmU0My1iNGVkNTc1Njg0ZTQiLCJhdXRoX3RpbWUiOjAsInR5cCI6IiIsImF6cCI6IiIsInNlc3Npb25fc3RhdGUiOiIiLCJhY3IiOiIiLCJhbGxvd2VkLW9yaWdpbnMiOm51bGwsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6bnVsbH0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpudWxsfX0sInNjb3BlIjoiIiwic2lkIjoiIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJuYW1lIjoiIiwiQ2xpZW50SUQiOiIiLCJncm91cHMiOm51bGwsInByZWZlcnJlZF91c2VybmFtZSI6IiIsImdpdmVuX25hbWUiOiIiLCJmYW1pbHlfbmFtZSI6IiIsImVtYWlsIjoidGF6ZXRAbmFyb2QucnUifQ.hzpIIqNWkKIoYUXDaLY7DLyGKH70rz0ZTqanv19qxF10i3N1t1g_KknA4Qsw1MduTyLzIz7y5SRSr4PSQ1gzR0vB_C0GudSFUhyBNNKkS4ClhRDWW9eN_IIEljbiJMLQi2L07XJ7Y5DQ0sIHRAPLkreCDFMKQ0yTCrKoScCJIDuUqzaTcOaX-hfjaxW8iV0SZMDxl0C5O3tke0btxkaLBaAcWH0V-1yu2r2m29fyU33FqikF0xAcDXiuZphfsrShKQYArZjKAphYCP_Vpmr-1sdjinkn8sPSk1qZny0rka8G6WVZUGaZSOnW8SYNLVUwdqtuQmK-Y18o7U0Suzrsjg}"
|
||||||
REGION="us-east-1"
|
REGION="us-east-1"
|
||||||
TS=$(date +%s)
|
TS=$(date +%s)
|
||||||
|
|
||||||
@@ -23,7 +26,7 @@ fail() { echo " ❌ $1"; FAIL=$((FAIL+1)); }
|
|||||||
|
|
||||||
check() {
|
check() {
|
||||||
local label="$1" body="$2" pattern="$3"
|
local label="$1" body="$2" pattern="$3"
|
||||||
if echo "$body" | grep -qE "$pattern"; then ok "$label"; else fail "$label"; fi
|
if echo "$body" | grep -qE "$pattern"; then ok "$label"; else fail "$label: $(echo "$body" | head -3)"; fi
|
||||||
}
|
}
|
||||||
|
|
||||||
check_not() {
|
check_not() {
|
||||||
@@ -36,13 +39,14 @@ check_http() {
|
|||||||
if [[ "$got" == "$want" ]]; then ok "$label (HTTP $got)"; else fail "$label — ожидалось $want, получено $got"; fi
|
if [[ "$got" == "$want" ]]; then ok "$label (HTTP $got)"; else fail "$label — ожидалось $want, получено $got"; fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# aws CLI с credentials тенанта
|
# sqs — AWS CLI враппер
|
||||||
sqs() {
|
sqs() {
|
||||||
local ak="$1" sk="$2"; shift 2
|
local ak="$1" sk="$2"; shift 2
|
||||||
AWS_ACCESS_KEY_ID="$ak" AWS_SECRET_ACCESS_KEY="$sk" AWS_DEFAULT_REGION="$REGION" \
|
AWS_ACCESS_KEY_ID="$ak" AWS_SECRET_ACCESS_KEY="$sk" AWS_DEFAULT_REGION="$REGION" \
|
||||||
aws --endpoint-url "$BASE_URL" --output json sqs "$@" 2>&1
|
aws --endpoint-url "$BASE_URL" --output json sqs "$@" 2>&1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# admin — Admin API запрос с Bearer admin token
|
||||||
admin() {
|
admin() {
|
||||||
local method="$1" path="$2" body="${3:-}"
|
local method="$1" path="$2" body="${3:-}"
|
||||||
if [[ -n "$body" ]]; then
|
if [[ -n "$body" ]]; then
|
||||||
@@ -57,15 +61,18 @@ admin() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# ui_api — UI API без авторизации (публичный)
|
# ui — UI API запрос с Bearer JWT (nubes токен)
|
||||||
ui() {
|
ui() {
|
||||||
local method="$1" path="$2" body="${3:-}"
|
local method="$1" path="$2" body="${3:-}"
|
||||||
if [[ -n "$body" ]]; then
|
if [[ -n "$body" ]]; then
|
||||||
curl -sf --max-time 15 -X "$method" \
|
curl -sf --max-time 15 -X "$method" \
|
||||||
|
-H "Authorization: Bearer $NUBES_JWT" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d "$body" "${BASE_URL}/ui/api${path}" 2>&1
|
-d "$body" "${BASE_URL}/ui/api${path}" 2>&1
|
||||||
else
|
else
|
||||||
curl -sf --max-time 15 -X "$method" "${BASE_URL}/ui/api${path}" 2>&1
|
curl -sf --max-time 15 -X "$method" \
|
||||||
|
-H "Authorization: Bearer $NUBES_JWT" \
|
||||||
|
"${BASE_URL}/ui/api${path}" 2>&1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,90 +85,174 @@ echo ""
|
|||||||
# ── 1. Health ──
|
# ── 1. Health ──
|
||||||
echo "── 1. Health ──"
|
echo "── 1. Health ──"
|
||||||
R=$(curl -sf --max-time 10 "${BASE_URL}/health" 2>&1)
|
R=$(curl -sf --max-time 10 "${BASE_URL}/health" 2>&1)
|
||||||
check "GET /health → OK" "$R" "[Oo][Kk]|status"
|
check "GET /health → ok" "$R" "[Oo][Kk]|status"
|
||||||
R=$(ui GET /health)
|
|
||||||
check "GET /ui/api/health → ok" "$R" "ok"
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ── 2. Создание тенанта ──
|
# ── 2. UI API авторизация (JWT auth) ──
|
||||||
echo "── 2. Создание тенанта ──"
|
echo "── 2. UI API авторизация ──"
|
||||||
RESP=$(admin POST /admin/tenants '{"name":"quick-'"$TS"'","max_queues":5}')
|
R=$(curl -sf --max-time 15 -X POST \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"token\":\"$NUBES_JWT\"}" \
|
||||||
|
"${BASE_URL}/ui/api/auth" 2>&1)
|
||||||
|
check "POST /ui/api/auth → tenant_id" "$R" "tenant_id"
|
||||||
|
UI_TID=$(echo "$R" | python3 -c "import sys,json; print(json.load(sys.stdin).get('tenant_id',''))" 2>/dev/null || true)
|
||||||
|
echo " UI Tenant ID: $UI_TID"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── 3. Создание тенанта через Admin API ──
|
||||||
|
echo "── 3. Admin API — создание тенанта ──"
|
||||||
|
RESP=$(admin POST /admin/tenants '{"name":"quick-'"$TS"'","max_queues":10}')
|
||||||
check "POST /admin/tenants → access_key" "$RESP" "access_key"
|
check "POST /admin/tenants → access_key" "$RESP" "access_key"
|
||||||
AK=$(echo "$RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['access_key'])")
|
AK=$(echo "$RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['access_key'])")
|
||||||
SK=$(echo "$RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['secret_key'])")
|
SK=$(echo "$RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['secret_key'])")
|
||||||
TID=$(echo "$RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
|
TID=$(echo "$RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
|
||||||
echo " Tenant ID: $TID"
|
echo " Tenant ID: $TID"
|
||||||
echo " Access Key: $AK"
|
echo " Access Key: $AK"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ── 3. AWS CLI CRUD ──
|
|
||||||
echo "── 3. AWS CLI CRUD ──"
|
|
||||||
QNAME="quick-q-$TS"
|
QNAME="quick-q-$TS"
|
||||||
|
QURL="${BASE_URL}/${TID}/${QNAME}"
|
||||||
|
|
||||||
|
# ── 4. SQS Queue CRUD ──
|
||||||
|
echo "── 4. SQS Queue CRUD ──"
|
||||||
R=$(sqs "$AK" "$SK" create-queue --queue-name "$QNAME")
|
R=$(sqs "$AK" "$SK" create-queue --queue-name "$QNAME")
|
||||||
check "CreateQueue → QueueUrl" "$R" "QueueUrl"
|
check "CreateQueue → QueueUrl" "$R" "QueueUrl"
|
||||||
QURL=$(echo "$R" | python3 -c "import sys,json; print(json.load(sys.stdin)['QueueUrl'])")
|
|
||||||
|
|
||||||
R=$(sqs "$AK" "$SK" list-queues)
|
R=$(sqs "$AK" "$SK" list-queues)
|
||||||
check "ListQueues → очередь $QNAME в списке" "$R" "$QNAME"
|
check "ListQueues → очередь в списке" "$R" "$QNAME"
|
||||||
|
|
||||||
R=$(sqs "$AK" "$SK" send-message --queue-url "$QURL" --message-body "hello-quick-$TS")
|
R=$(sqs "$AK" "$SK" get-queue-url --queue-name "$QNAME")
|
||||||
|
check "GetQueueUrl → QueueUrl" "$R" "QueueUrl"
|
||||||
|
|
||||||
|
R=$(sqs "$AK" "$SK" get-queue-attributes --queue-url "$QURL" --attribute-names All)
|
||||||
|
check "GetQueueAttributes → VisibilityTimeout" "$R" "VisibilityTimeout"
|
||||||
|
|
||||||
|
R=$(sqs "$AK" "$SK" set-queue-attributes --queue-url "$QURL" --attributes '{"VisibilityTimeout":"60"}' 2>&1 && echo "OK" || echo "FAIL")
|
||||||
|
check "SetQueueAttributes → OK" "$R" "OK"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── 5. TagQueue / UntagQueue / ListQueueTags ──
|
||||||
|
echo "── 5. Tags API ──"
|
||||||
|
R=$(sqs "$AK" "$SK" tag-queue --queue-url "$QURL" --tags env=test,owner=qa 2>&1 && echo "OK" || echo "FAIL")
|
||||||
|
check "TagQueue → OK" "$R" "OK"
|
||||||
|
|
||||||
|
R=$(sqs "$AK" "$SK" list-queue-tags --queue-url "$QURL")
|
||||||
|
check "ListQueueTags → env=test" "$R" "env|test"
|
||||||
|
|
||||||
|
R=$(sqs "$AK" "$SK" untag-queue --queue-url "$QURL" --tag-keys env 2>&1 && echo "OK" || echo "FAIL")
|
||||||
|
check "UntagQueue → OK" "$R" "OK"
|
||||||
|
|
||||||
|
R=$(sqs "$AK" "$SK" list-queue-tags --queue-url "$QURL")
|
||||||
|
check_not "ListQueueTags → env удалён" "$R" '"env"'
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── 6. Сообщения: Send / Receive / Delete ──
|
||||||
|
echo "── 6. Send / Receive / Delete ──"
|
||||||
|
R=$(sqs "$AK" "$SK" send-message --queue-url "$QURL" --message-body "hello-$TS")
|
||||||
check "SendMessage → MessageId" "$R" "MessageId"
|
check "SendMessage → MessageId" "$R" "MessageId"
|
||||||
|
check "SendMessage → MD5" "$R" "MD5OfMessageBody"
|
||||||
|
|
||||||
R=$(sqs "$AK" "$SK" receive-message --queue-url "$QURL")
|
R=$(sqs "$AK" "$SK" receive-message --queue-url "$QURL" --visibility-timeout 30)
|
||||||
check "ReceiveMessage → тело сообщения" "$R" "hello-quick-$TS"
|
check "ReceiveMessage → тело" "$R" "hello-$TS"
|
||||||
RECEIPT=$(echo "$R" | python3 -c "import sys,json; msgs=json.load(sys.stdin).get('Messages',[]); print(msgs[0]['ReceiptHandle'] if msgs else '')" 2>/dev/null || true)
|
RECEIPT=$(echo "$R" | python3 -c "import sys,json; msgs=json.load(sys.stdin).get('Messages',[]); print(msgs[0]['ReceiptHandle'] if msgs else '')" 2>/dev/null || true)
|
||||||
|
|
||||||
if [[ -n "$RECEIPT" ]]; then
|
if [[ -n "$RECEIPT" ]]; then
|
||||||
|
R=$(sqs "$AK" "$SK" change-message-visibility --queue-url "$QURL" --receipt-handle "$RECEIPT" --visibility-timeout 60 2>&1 && echo "OK" || echo "FAIL")
|
||||||
|
check "ChangeMessageVisibility → OK" "$R" "OK"
|
||||||
|
|
||||||
sqs "$AK" "$SK" delete-message --queue-url "$QURL" --receipt-handle "$RECEIPT" >/dev/null 2>&1
|
sqs "$AK" "$SK" delete-message --queue-url "$QURL" --receipt-handle "$RECEIPT" >/dev/null 2>&1
|
||||||
ok "DeleteMessage → без ошибок"
|
ok "DeleteMessage → без ошибок"
|
||||||
else
|
else
|
||||||
fail "DeleteMessage — нет ReceiptHandle"
|
fail "ReceiptHandle не получен"
|
||||||
fi
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ── 4. UI API — создание очереди ──
|
# ── 7. Batch операции ──
|
||||||
echo "── 4. UI API очереди ──"
|
echo "── 7. Batch ──"
|
||||||
R=$(ui POST "/tenants/$TID/queues" '{"name":"ui-quick-q"}')
|
R=$(sqs "$AK" "$SK" send-message-batch --queue-url "$QURL" \
|
||||||
check "UI POST /queues → создана" "$R" "ui-quick-q"
|
--entries '[{"Id":"b1","MessageBody":"batch-1"},{"Id":"b2","MessageBody":"batch-2"},{"Id":"b3","MessageBody":"batch-3"}]')
|
||||||
|
check "SendMessageBatch → Successful" "$R" "Successful"
|
||||||
|
|
||||||
R=$(ui GET "/tenants/$TID/queues")
|
R=$(sqs "$AK" "$SK" receive-message --queue-url "$QURL" --max-number-of-messages 3)
|
||||||
check "UI GET /queues → ui-quick-q в списке" "$R" "ui-quick-q"
|
RECEIPTS=$(echo "$R" | python3 -c "import sys,json; msgs=json.load(sys.stdin).get('Messages',[]); print('\n'.join(m['ReceiptHandle'] for m in msgs))" 2>/dev/null || true)
|
||||||
|
|
||||||
|
# ChangeMessageVisibilityBatch
|
||||||
|
if [[ -n "$RECEIPTS" ]]; then
|
||||||
|
CMV_ENTRIES=$(echo "$RECEIPTS" | python3 -c "
|
||||||
|
import sys
|
||||||
|
entries = []
|
||||||
|
for i, rh in enumerate(sys.stdin.read().strip().split('\n'), 1):
|
||||||
|
if rh:
|
||||||
|
entries.append('{\"Id\":\"cmv%d\",\"ReceiptHandle\":\"%s\",\"VisibilityTimeout\":60}' % (i, rh))
|
||||||
|
print('[' + ','.join(entries) + ']')
|
||||||
|
" 2>/dev/null || echo "[]")
|
||||||
|
if [[ "$CMV_ENTRIES" != "[]" ]]; then
|
||||||
|
R=$(sqs "$AK" "$SK" change-message-visibility-batch --queue-url "$QURL" --entries "$CMV_ENTRIES")
|
||||||
|
check "ChangeMessageVisibilityBatch → Successful" "$R" "Successful"
|
||||||
|
fi
|
||||||
|
|
||||||
|
DEL_ENTRIES=$(echo "$RECEIPTS" | python3 -c "
|
||||||
|
import sys
|
||||||
|
entries = []
|
||||||
|
for i, rh in enumerate(sys.stdin.read().strip().split('\n'), 1):
|
||||||
|
if rh:
|
||||||
|
entries.append('{\"Id\":\"d%d\",\"ReceiptHandle\":\"%s\"}' % (i, rh))
|
||||||
|
print('[' + ','.join(entries) + ']')
|
||||||
|
" 2>/dev/null || echo "[]")
|
||||||
|
if [[ "$DEL_ENTRIES" != "[]" ]]; then
|
||||||
|
R=$(sqs "$AK" "$SK" delete-message-batch --queue-url "$QURL" --entries "$DEL_ENTRIES")
|
||||||
|
check "DeleteMessageBatch → Successful" "$R" "Successful"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ── 5. UI API — send/peek/purge ──
|
# ── 8. PurgeQueue ──
|
||||||
echo "── 5. UI API send/peek/purge ──"
|
echo "── 8. PurgeQueue ──"
|
||||||
R=$(ui POST "/tenants/$TID/queues/ui-quick-q/messages" '{"body":"msg-a"}')
|
sqs "$AK" "$SK" send-message --queue-url "$QURL" --message-body "purge-me" >/dev/null 2>&1 || true
|
||||||
check "UI POST /messages → id" "$R" '"id"'
|
R=$(sqs "$AK" "$SK" purge-queue --queue-url "$QURL" 2>&1 && echo "OK" || echo "FAIL")
|
||||||
ui POST "/tenants/$TID/queues/ui-quick-q/messages" '{"body":"msg-b"}' >/dev/null 2>&1
|
check "PurgeQueue → OK" "$R" "OK"
|
||||||
|
|
||||||
R=$(ui GET "/tenants/$TID/queues/ui-quick-q/messages")
|
|
||||||
check "UI GET /messages → msg-a" "$R" "msg-a"
|
|
||||||
check "UI GET /messages → msg-b" "$R" "msg-b"
|
|
||||||
check_not "UI GET /messages → нет receipt_handle" "$R" "receipt_handle"
|
|
||||||
|
|
||||||
# Purge
|
|
||||||
ui DELETE "/tenants/$TID/queues/ui-quick-q/messages" >/dev/null 2>&1
|
|
||||||
R=$(ui GET "/tenants/$TID/queues/ui-quick-q/messages")
|
|
||||||
check_not "UI DELETE /messages (purge) → msg-a исчезло" "$R" "msg-a"
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ── 6. UI API — удаление очереди ──
|
# ── 9. UI API с JWT ──
|
||||||
echo "── 6. UI API удаление очереди ──"
|
echo "── 9. UI API с JWT ──"
|
||||||
HTTP=$(curl -sf --max-time 15 -o /dev/null -w "%{http_code}" -X DELETE \
|
R=$(ui GET /health)
|
||||||
"${BASE_URL}/ui/api/tenants/${TID}/queues/ui-quick-q")
|
check "GET /ui/api/health → ok" "$R" "ok|status"
|
||||||
check_http "UI DELETE /queues/ui-quick-q → 204/200" "204" "$HTTP" 2>/dev/null || \
|
|
||||||
check_http "UI DELETE /queues/ui-quick-q → 200" "200" "$HTTP"
|
|
||||||
|
|
||||||
R=$(ui GET "/tenants/$TID/queues")
|
if [[ -n "$UI_TID" ]]; then
|
||||||
check_not "Очередь ui-quick-q исчезла из списка" "$R" "ui-quick-q"
|
R=$(ui GET "/tenants/${UI_TID}/queues")
|
||||||
|
check "UI GET /tenants/{id}/queues → ответ" "$R" "\[|\]|name|queues"
|
||||||
|
|
||||||
|
R=$(ui POST "/tenants/${UI_TID}/queues" '{"name":"ui-q-'"$TS"'"}')
|
||||||
|
check "UI POST /queues → создана" "$R" "ui-q-$TS|name"
|
||||||
|
|
||||||
|
R=$(ui POST "/tenants/${UI_TID}/queues/ui-q-${TS}/messages" '{"body":"ui-msg-'"$TS"'"}')
|
||||||
|
check "UI POST /messages → id" "$R" '"id"'
|
||||||
|
|
||||||
|
R=$(ui GET "/tenants/${UI_TID}/queues/ui-q-${TS}/messages")
|
||||||
|
check "UI GET /messages → тело" "$R" "ui-msg-$TS"
|
||||||
|
check_not "UI GET /messages → нет receipt_handle" "$R" "receipt_handle"
|
||||||
|
|
||||||
|
R=$(ui DELETE "/tenants/${UI_TID}/queues/ui-q-${TS}/messages" "")
|
||||||
|
check_not "UI DELETE /messages (purge) → сообщение исчезло" \
|
||||||
|
"$(ui GET "/tenants/${UI_TID}/queues/ui-q-${TS}/messages")" "ui-msg-$TS"
|
||||||
|
|
||||||
|
HTTP=$(curl -sf --max-time 15 -o /dev/null -w "%{http_code}" -X DELETE \
|
||||||
|
-H "Authorization: Bearer $NUBES_JWT" \
|
||||||
|
"${BASE_URL}/ui/api/tenants/${UI_TID}/queues/ui-q-${TS}")
|
||||||
|
if [[ "$HTTP" == "204" || "$HTTP" == "200" ]]; then
|
||||||
|
ok "UI DELETE /queues/ui-q → HTTP $HTTP"
|
||||||
|
else
|
||||||
|
fail "UI DELETE /queues/ui-q → HTTP $HTTP (ожидалось 200/204)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
fail "UI API: JWT auth не дал tenant_id — пропускаем UI тесты"
|
||||||
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ── 7. AWS CLI DeleteQueue ──
|
# ── 10. DeleteQueue ──
|
||||||
echo "── 7. DeleteQueue ──"
|
echo "── 10. DeleteQueue ──"
|
||||||
AWS_ACCESS_KEY_ID="$AK" AWS_SECRET_ACCESS_KEY="$SK" AWS_DEFAULT_REGION="$REGION" \
|
R=$(sqs "$AK" "$SK" delete-queue --queue-url "$QURL" 2>&1 && echo "OK" || echo "FAIL")
|
||||||
aws --endpoint-url "$BASE_URL" --output json sqs delete-queue --queue-url "$QURL" >/dev/null 2>&1
|
check "DeleteQueue → OK" "$R" "OK"
|
||||||
if [[ $? -eq 0 ]]; then ok "DeleteQueue → без ошибки"; else fail "DeleteQueue → ошибка"; fi
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ── Cleanup ──
|
# ── Cleanup ──
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
T=$(curl -s -X POST https://qu.kube5s.ru/admin/tenants \
|
||||||
|
-H "Authorization: Bearer sqs-admin-7a7d8bd0c060a75c198d48680f34077a" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"name":"tag-debug","max_queues":5}')
|
||||||
|
echo "Tenant response: $T"
|
||||||
|
AK=$(echo "$T" | jq -r '.access_key')
|
||||||
|
SK=$(echo "$T" | jq -r '.secret_key')
|
||||||
|
TID=$(echo "$T" | jq -r '.id')
|
||||||
|
echo "AK=$AK TID=$TID"
|
||||||
|
|
||||||
|
Q=$(AWS_ACCESS_KEY_ID="$AK" AWS_SECRET_ACCESS_KEY="$SK" AWS_DEFAULT_REGION=us-east-1 \
|
||||||
|
aws --endpoint-url https://qu.kube5s.ru --output json sqs create-queue --queue-name dbg-q 2>&1)
|
||||||
|
echo "CreateQueue: $Q"
|
||||||
|
QURL=$(echo "$Q" | jq -r '.QueueUrl' 2>/dev/null || true)
|
||||||
|
echo "QURL=$QURL"
|
||||||
|
|
||||||
|
echo "--- TagQueue raw response ---"
|
||||||
|
AWS_ACCESS_KEY_ID="$AK" AWS_SECRET_ACCESS_KEY="$SK" AWS_DEFAULT_REGION=us-east-1 \
|
||||||
|
aws --endpoint-url https://qu.kube5s.ru --debug sqs tag-queue --queue-url "$QURL" --tags env=test 2>&1 | grep -E "HTTP|body|Error|400|tag" | head -30
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
curl -s -X DELETE "https://qu.kube5s.ru/admin/tenants/${TID}" \
|
||||||
|
-H "Authorization: Bearer sqs-admin-7a7d8bd0c060a75c198d48680f34077a" > /dev/null
|
||||||
|
echo "Cleaned up"
|
||||||
Reference in New Issue
Block a user