add README, quick_test, hardcore_test for shared-sqs multi-tenant SQS service
This commit is contained in:
@@ -0,0 +1,922 @@
|
||||
#!/bin/bash
|
||||
# hardcore_test.sh — расширенный тест shared-sqs
|
||||
# Изменено: 2026-04-09
|
||||
# Покрывает: Admin API, AWS CLI CRUD, awscurl CRUD, кросс-доставка,
|
||||
# изоляция тенантов, невалидный ввод, спецсимволы,
|
||||
# visibility timeout, batch, лимиты очередей, cleanup тенанта.
|
||||
# Запуск: BASE_URL=https://qu.kube5s.ru ADMIN_TOKEN=... bash tests/hardcore_test.sh
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# КОНФИГУРАЦИЯ
|
||||
# ═══════════════════════════════════════════
|
||||
BASE_URL="${BASE_URL:-https://qu.kube5s.ru}"
|
||||
ADMIN_TOKEN="${ADMIN_TOKEN:-sqs-admin-7a7d8bd0c060a75c198d48680f34077a}"
|
||||
AWSCURL="${AWSCURL:-/home/naeel/.local/bin/awscurl}"
|
||||
TS=$(date +%s)
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
TOTAL=0
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ХЕЛПЕРЫ
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
# check — ожидается что вывод содержит паттерн
|
||||
check() {
|
||||
local name="$1" output="$2" pattern="$3"
|
||||
TOTAL=$((TOTAL + 1))
|
||||
if echo "$output" | grep -qE "$pattern"; then
|
||||
PASS=$((PASS + 1))
|
||||
echo " ✅ $name"
|
||||
else
|
||||
FAIL=$((FAIL + 1))
|
||||
echo " ❌ $name"
|
||||
echo " ожидалось: $pattern"
|
||||
echo " получено: $(echo "$output" | head -10)"
|
||||
fi
|
||||
}
|
||||
|
||||
# check_not — ожидается что вывод НЕ содержит паттерн
|
||||
check_not() {
|
||||
local name="$1" output="$2" pattern="$3"
|
||||
TOTAL=$((TOTAL + 1))
|
||||
if echo "$output" | grep -qE "$pattern"; then
|
||||
FAIL=$((FAIL + 1))
|
||||
echo " ❌ $name (содержит '$pattern')"
|
||||
echo " получено: $(echo "$output" | head -10)"
|
||||
else
|
||||
PASS=$((PASS + 1))
|
||||
echo " ✅ $name"
|
||||
fi
|
||||
}
|
||||
|
||||
# check_eq — проверка числа
|
||||
check_eq() {
|
||||
local name="$1" got="$2" want="$3"
|
||||
TOTAL=$((TOTAL + 1))
|
||||
if [ "$got" -eq "$want" ]; then
|
||||
PASS=$((PASS + 1))
|
||||
echo " ✅ $name ($got)"
|
||||
else
|
||||
FAIL=$((FAIL + 1))
|
||||
echo " ❌ $name: ожидалось=$want, получено=$got"
|
||||
fi
|
||||
}
|
||||
|
||||
# sqs — AWS CLI враппер с credentials тенанта
|
||||
sqs() {
|
||||
local ak="$1" sk="$2"
|
||||
shift 2
|
||||
AWS_ACCESS_KEY_ID="$ak" \
|
||||
AWS_SECRET_ACCESS_KEY="$sk" \
|
||||
AWS_DEFAULT_REGION="us-east-1" \
|
||||
aws --endpoint-url "$BASE_URL" --output json sqs "$@" 2>&1
|
||||
}
|
||||
|
||||
# acurl — awscurl враппер для SQS Query Protocol
|
||||
# $1=ak, $2=sk, остальное — form-encoded body (Action=...&...)
|
||||
acurl() {
|
||||
local ak="$1" sk="$2"
|
||||
shift 2
|
||||
"$AWSCURL" \
|
||||
--region us-east-1 --service sqs \
|
||||
--access_key "$ak" --secret_key "$sk" \
|
||||
-X POST \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "$*" \
|
||||
"$BASE_URL/" 2>&1
|
||||
}
|
||||
|
||||
# admin_api — Admin REST API запрос через curl
|
||||
# $1=method, $2=path, $3=body (опц.)
|
||||
admin_api() {
|
||||
local method="$1" path="$2" body="${3:-}"
|
||||
if [ -n "$body" ]; then
|
||||
curl -s --max-time 20 -X "$method" \
|
||||
-H "Authorization: Bearer $ADMIN_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$body" \
|
||||
"${BASE_URL}${path}" 2>&1
|
||||
else
|
||||
curl -s --max-time 20 -X "$method" \
|
||||
-H "Authorization: Bearer $ADMIN_TOKEN" \
|
||||
"${BASE_URL}${path}" 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
# qurl — строит HTTPS Queue URL из TenantID + имени очереди
|
||||
qurl() {
|
||||
echo "${BASE_URL}/${1}/${2}"
|
||||
}
|
||||
|
||||
# xml_field — извлекает значение из XML тега
|
||||
xml_field() {
|
||||
echo "$1" | grep -oP "(?<=<${2}>)[^<]+" | head -1
|
||||
}
|
||||
|
||||
echo "╔══════════════════════════════════════════════════════════╗"
|
||||
echo "║ shared-sqs Hardcore Test — $(date '+%Y-%m-%d %H:%M:%S') ║"
|
||||
echo "║ Endpoint: ${BASE_URL} ║"
|
||||
echo "║ Методы: AWS CLI + awscurl + curl (Admin) ║"
|
||||
echo "╚══════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
|
||||
# Проверяем зависимости
|
||||
for dep in aws curl jq "$AWSCURL"; do
|
||||
if ! command -v "$dep" &>/dev/null; then
|
||||
echo "❌ Не найдено: $dep — прерываем"
|
||||
exit 2
|
||||
fi
|
||||
done
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── 1. Admin API — health + auth + CRUD ──"
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
# health без токена — должен пройти
|
||||
R=$(curl -s --max-time 10 "${BASE_URL}/health" 2>&1)
|
||||
check "health доступен без токена" "$R" "ok|OK|healthy|status"
|
||||
|
||||
# admin без токена — 401/403
|
||||
R=$(curl -s --max-time 10 -X GET "${BASE_URL}/admin/tenants" 2>&1)
|
||||
check "GET /admin/tenants без токена → 401/403" "$R" "Unauthorized|Forbidden|401|403|unauthorized"
|
||||
|
||||
# admin с неверным токеном — 401/403
|
||||
R=$(curl -s --max-time 10 -X GET "${BASE_URL}/admin/tenants" \
|
||||
-H "Authorization: Bearer wrong-token-xyz" 2>&1)
|
||||
check "GET /admin/tenants c неверным токеном → 401/403" "$R" "Unauthorized|Forbidden|401|403|unauthorized"
|
||||
|
||||
# создать тенанта T1
|
||||
R=$(admin_api POST /admin/tenants '{"name":"hc-t1-'"$TS"'"}')
|
||||
check "POST /admin/tenants → создаёт T1" "$R" "access_key|id"
|
||||
T1_ID=$(echo "$R" | jq -r '.id' 2>/dev/null)
|
||||
T1_AK=$(echo "$R" | jq -r '.access_key' 2>/dev/null)
|
||||
T1_SK=$(echo "$R" | jq -r '.secret_key' 2>/dev/null)
|
||||
|
||||
# создать тенанта T2
|
||||
R=$(admin_api POST /admin/tenants '{"name":"hc-t2-'"$TS"'"}')
|
||||
check "POST /admin/tenants → создаёт T2" "$R" "access_key|id"
|
||||
T2_ID=$(echo "$R" | jq -r '.id' 2>/dev/null)
|
||||
T2_AK=$(echo "$R" | jq -r '.access_key' 2>/dev/null)
|
||||
T2_SK=$(echo "$R" | jq -r '.secret_key' 2>/dev/null)
|
||||
|
||||
# список тенантов — видим обоих
|
||||
R=$(admin_api GET /admin/tenants)
|
||||
check "GET /admin/tenants — T1 виден" "$R" "hc-t1-$TS"
|
||||
check "GET /admin/tenants — T2 виден" "$R" "hc-t2-$TS"
|
||||
|
||||
# GET конкретного тенанта
|
||||
R=$(admin_api GET "/admin/tenants/${T1_ID}")
|
||||
check "GET /admin/tenants/{id} — возвращает T1" "$R" "$T1_AK"
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── 2. SQS CRUD через AWS CLI (T1) ──"
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
CLI_Q="hc-cli-${TS}"
|
||||
|
||||
# создание
|
||||
R=$(sqs "$T1_AK" "$T1_SK" create-queue --queue-name "$CLI_Q")
|
||||
check "CLI: create-queue" "$R" "QueueUrl"
|
||||
|
||||
# список — очередь видна
|
||||
R=$(sqs "$T1_AK" "$T1_SK" list-queues)
|
||||
check "CLI: list-queues содержит очередь" "$R" "$CLI_Q"
|
||||
|
||||
# get-queue-url
|
||||
R=$(sqs "$T1_AK" "$T1_SK" get-queue-url --queue-name "$CLI_Q")
|
||||
check "CLI: get-queue-url" "$R" "QueueUrl"
|
||||
|
||||
# send-message
|
||||
CQURL=$(qurl "$T1_ID" "$CLI_Q")
|
||||
R=$(sqs "$T1_AK" "$T1_SK" send-message --queue-url "$CQURL" --message-body "cli-hello-${TS}")
|
||||
check "CLI: send-message → MessageId" "$R" "MessageId"
|
||||
check "CLI: send-message → MD5" "$R" "MD5OfMessageBody"
|
||||
|
||||
# receive-message
|
||||
R=$(sqs "$T1_AK" "$T1_SK" receive-message --queue-url "$CQURL")
|
||||
check "CLI: receive-message → тело" "$R" "cli-hello-${TS}"
|
||||
RECEIPT=$(echo "$R" | jq -r '.Messages[0].ReceiptHandle' 2>/dev/null)
|
||||
|
||||
# delete-message — немедленно после receive, без промежуточных операций
|
||||
if [ -n "$RECEIPT" ] && [ "$RECEIPT" != "null" ]; then
|
||||
R=$(sqs "$T1_AK" "$T1_SK" delete-message --queue-url "$CQURL" --receipt-handle "$RECEIPT" 2>&1 && echo "OK" || echo "FAIL")
|
||||
check "CLI: delete-message" "$R" "OK"
|
||||
else
|
||||
TOTAL=$((TOTAL + 1)); FAIL=$((FAIL + 1))
|
||||
echo " ❌ CLI: delete-message (нет ReceiptHandle)"
|
||||
fi
|
||||
|
||||
# get-queue-attributes
|
||||
R=$(sqs "$T1_AK" "$T1_SK" get-queue-attributes --queue-url "$CQURL" --attribute-names All)
|
||||
check "CLI: get-queue-attributes" "$R" "Attributes|ApproximateNumberOfMessages"
|
||||
|
||||
# set-queue-attributes visibility
|
||||
R=$(sqs "$T1_AK" "$T1_SK" set-queue-attributes --queue-url "$CQURL" --attributes '{"VisibilityTimeout":"45"}' 2>&1 && echo "OK" || echo "FAIL")
|
||||
check "CLI: set-queue-attributes VisibilityTimeout" "$R" "OK"
|
||||
|
||||
# purge
|
||||
sqs "$T1_AK" "$T1_SK" send-message --queue-url "$CQURL" --message-body "purge-me" >/dev/null 2>&1 || true
|
||||
R=$(sqs "$T1_AK" "$T1_SK" purge-queue --queue-url "$CQURL" 2>&1 && echo "OK" || echo "FAIL")
|
||||
check "CLI: purge-queue" "$R" "OK"
|
||||
|
||||
# delete-queue
|
||||
R=$(sqs "$T1_AK" "$T1_SK" delete-queue --queue-url "$CQURL" 2>&1 && echo "OK" || echo "FAIL")
|
||||
check "CLI: delete-queue" "$R" "OK"
|
||||
|
||||
# очередь исчезла из списка
|
||||
R=$(sqs "$T1_AK" "$T1_SK" list-queues 2>&1)
|
||||
check_not "CLI: deleted queue не в списке" "$R" "$CLI_Q"
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── 3. SQS CRUD через awscurl (T2) ──"
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
AC_Q="hc-ac-${TS}"
|
||||
AC_QURL="${BASE_URL}/${T2_ID}/${AC_Q}"
|
||||
VER="Version=2012-11-05"
|
||||
|
||||
# create-queue
|
||||
R=$(acurl "$T2_AK" "$T2_SK" "Action=CreateQueue&QueueName=${AC_Q}&${VER}")
|
||||
check "awscurl: CreateQueue" "$R" "CreateQueueResult"
|
||||
|
||||
# send-message
|
||||
R=$(acurl "$T2_AK" "$T2_SK" "Action=SendMessage&QueueUrl=$(python3 -c "import urllib.parse;print(urllib.parse.quote('${AC_QURL}'))")&MessageBody=acurl-hello-${TS}&${VER}")
|
||||
check "awscurl: SendMessage → MessageId" "$R" "MessageId"
|
||||
check "awscurl: SendMessage → MD5" "$R" "MD5OfMessageBody"
|
||||
|
||||
# receive-message
|
||||
R=$(acurl "$T2_AK" "$T2_SK" "Action=ReceiveMessage&QueueUrl=$(python3 -c "import urllib.parse;print(urllib.parse.quote('${AC_QURL}'))")&${VER}")
|
||||
check "awscurl: ReceiveMessage → тело" "$R" "acurl-hello-${TS}"
|
||||
AC_RECEIPT=$(xml_field "$R" "ReceiptHandle")
|
||||
|
||||
# delete-message
|
||||
if [ -n "$AC_RECEIPT" ]; then
|
||||
ENC_RECEIPT=$(python3 -c "import urllib.parse,sys;print(urllib.parse.quote(sys.argv[1]))" "$AC_RECEIPT")
|
||||
R=$(acurl "$T2_AK" "$T2_SK" "Action=DeleteMessage&QueueUrl=$(python3 -c "import urllib.parse;print(urllib.parse.quote('${AC_QURL}'))")&ReceiptHandle=${ENC_RECEIPT}&${VER}")
|
||||
check "awscurl: DeleteMessage" "$R" "DeleteMessageResponse"
|
||||
else
|
||||
TOTAL=$((TOTAL + 1)); FAIL=$((FAIL + 1))
|
||||
echo " ❌ awscurl: DeleteMessage (нет ReceiptHandle для теста)"
|
||||
fi
|
||||
|
||||
# purge
|
||||
acurl "$T2_AK" "$T2_SK" "Action=SendMessage&QueueUrl=$(python3 -c "import urllib.parse;print(urllib.parse.quote('${AC_QURL}'))")&MessageBody=purge-me&${VER}" >/dev/null 2>&1 || true
|
||||
R=$(acurl "$T2_AK" "$T2_SK" "Action=PurgeQueue&QueueUrl=$(python3 -c "import urllib.parse;print(urllib.parse.quote('${AC_QURL}'))")&${VER}")
|
||||
check "awscurl: PurgeQueue" "$R" "PurgeQueueResponse"
|
||||
|
||||
# delete-queue
|
||||
R=$(acurl "$T2_AK" "$T2_SK" "Action=DeleteQueue&QueueUrl=$(python3 -c "import urllib.parse;print(urllib.parse.quote('${AC_QURL}'))")&${VER}")
|
||||
check "awscurl: DeleteQueue" "$R" "DeleteQueueResponse"
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── 4. Кросс-доставка: CLI ↔ awscurl ──"
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
XQ="hc-cross-${TS}"
|
||||
XQURL=$(qurl "$T1_ID" "$XQ")
|
||||
ENC_XQURL=$(python3 -c "import urllib.parse;print(urllib.parse.quote('${XQURL}'))")
|
||||
|
||||
sqs "$T1_AK" "$T1_SK" create-queue --queue-name "$XQ" >/dev/null 2>&1 || true
|
||||
|
||||
# CLI отправляет → awscurl получает
|
||||
sqs "$T1_AK" "$T1_SK" send-message --queue-url "$XQURL" --message-body "from-cli-${TS}" >/dev/null 2>&1 || true
|
||||
R=$(acurl "$T1_AK" "$T1_SK" "Action=ReceiveMessage&QueueUrl=${ENC_XQURL}&MaxNumberOfMessages=1&${VER}")
|
||||
check "CLI→awscurl: сообщение доставлено" "$R" "from-cli-${TS}"
|
||||
|
||||
# Очищаем очередь перед тестом в обратную сторону
|
||||
acurl "$T1_AK" "$T1_SK" "Action=PurgeQueue&QueueUrl=${ENC_XQURL}&${VER}" >/dev/null 2>&1 || true
|
||||
sleep 1
|
||||
|
||||
# awscurl отправляет → CLI получает
|
||||
acurl "$T1_AK" "$T1_SK" "Action=SendMessage&QueueUrl=${ENC_XQURL}&MessageBody=from-acurl-${TS}&${VER}" >/dev/null 2>&1 || true
|
||||
R=$(sqs "$T1_AK" "$T1_SK" receive-message --queue-url "$XQURL" --max-number-of-messages 1)
|
||||
check "awscurl→CLI: сообщение доставлено" "$R" "from-acurl-${TS}"
|
||||
|
||||
sqs "$T1_AK" "$T1_SK" delete-queue --queue-url "$XQURL" >/dev/null 2>&1 || true
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── 5. Изоляция между тенантами ──"
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
# Создаём одинаковые очереди у T1 и T2
|
||||
ISO_Q="hc-iso-${TS}"
|
||||
ISO_Q1URL=$(qurl "$T1_ID" "$ISO_Q")
|
||||
ISO_Q2URL=$(qurl "$T2_ID" "$ISO_Q")
|
||||
|
||||
sqs "$T1_AK" "$T1_SK" create-queue --queue-name "$ISO_Q" >/dev/null 2>&1 || true
|
||||
sqs "$T2_AK" "$T2_SK" create-queue --queue-name "$ISO_Q" >/dev/null 2>&1 || true
|
||||
|
||||
# T1 отправляет приватное сообщение
|
||||
sqs "$T1_AK" "$T1_SK" send-message --queue-url "$ISO_Q1URL" --message-body "private-t1-${TS}" >/dev/null 2>&1 || true
|
||||
# T2 отправляет в свою очередь с тем же именем
|
||||
sqs "$T2_AK" "$T2_SK" send-message --queue-url "$ISO_Q2URL" --message-body "private-t2-${TS}" >/dev/null 2>&1 || true
|
||||
|
||||
# T1 получает только своё
|
||||
R=$(sqs "$T1_AK" "$T1_SK" receive-message --queue-url "$ISO_Q1URL")
|
||||
check "Изоляция: T1 получает только T1-сообщение" "$R" "private-t1-${TS}"
|
||||
check_not "Изоляция: T1 не видит T2-сообщение" "$R" "private-t2-${TS}"
|
||||
|
||||
# T2 получает только своё
|
||||
R=$(sqs "$T2_AK" "$T2_SK" receive-message --queue-url "$ISO_Q2URL")
|
||||
check "Изоляция: T2 получает только T2-сообщение" "$R" "private-t2-${TS}"
|
||||
check_not "Изоляция: T2 не видит T1-сообщение" "$R" "private-t1-${TS}"
|
||||
|
||||
# T1 не может читать очередь T2 (неправильный URL)
|
||||
R=$(sqs "$T1_AK" "$T1_SK" receive-message --queue-url "$ISO_Q2URL" 2>&1 || true)
|
||||
check_not "T1 не может читать очередь T2" "$R" "private-t2-${TS}"
|
||||
|
||||
sqs "$T1_AK" "$T1_SK" delete-queue --queue-url "$ISO_Q1URL" >/dev/null 2>&1 || true
|
||||
sqs "$T2_AK" "$T2_SK" delete-queue --queue-url "$ISO_Q2URL" >/dev/null 2>&1 || true
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── 6. Невалидные credentials ──"
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
FAKE_AK="SSAK-ffffffffffffffff"
|
||||
FAKE_SK="ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
BAD_Q="hc-bad-${TS}"
|
||||
|
||||
# Неверный ключ — создание провалится
|
||||
R=$(sqs "$FAKE_AK" "$FAKE_SK" create-queue --queue-name "$BAD_Q" 2>&1 || true)
|
||||
check "Неверный cred: create блокируется" "$R" "InvalidClientTokenId|Unauthorized|not authorized|could not be authenticated|AuthFailure|403|401"
|
||||
|
||||
# Неверный ключ — отправка провалится
|
||||
R=$(sqs "$FAKE_AK" "$FAKE_SK" send-message --queue-url "$(qurl 'fake-tenant' $BAD_Q)" --message-body "x" 2>&1 || true)
|
||||
check "Неверный cred: send блокируется" "$R" "InvalidClientTokenId|Unauthorized|not authorized|could not be authenticated|AuthFailure|403|401"
|
||||
|
||||
# Несуществующая очередь у реального тенанта
|
||||
R=$(sqs "$T1_AK" "$T1_SK" send-message --queue-url "$(qurl "$T1_ID" "no-such-queue-${TS}")" --message-body "x" 2>&1 || true)
|
||||
check "Несуществующая очередь" "$R" "NonExistentQueue|AWS.SimpleQueueService.NonExistentQueue|does not exist"
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── 7. Невалидный Action через awscurl ──"
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
# Неизвестный Action
|
||||
R=$(acurl "$T1_AK" "$T1_SK" "Action=DoWeirdThing&${VER}")
|
||||
check "awscurl: неизвестный Action" "$R" "InvalidAction|UnknownOperation|Error|Bad Request"
|
||||
|
||||
# Отсутствует QueueUrl при SendMessage
|
||||
R=$(acurl "$T1_AK" "$T1_SK" "Action=SendMessage&MessageBody=x&${VER}")
|
||||
check "awscurl: SendMessage без QueueUrl" "$R" "QueueUrl|missing|Error|MissingParameter"
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── 8. Спецсимволы в сообщениях ──"
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
SPEC_Q="hc-spec-${TS}"
|
||||
SPEC_QURL=$(qurl "$T1_ID" "$SPEC_Q")
|
||||
ENC_SPEC_QURL=$(python3 -c "import urllib.parse;print(urllib.parse.quote('${SPEC_QURL}'))")
|
||||
|
||||
sqs "$T1_AK" "$T1_SK" create-queue --queue-name "$SPEC_Q" >/dev/null 2>&1 || true
|
||||
|
||||
# JSON строка
|
||||
sqs "$T1_AK" "$T1_SK" send-message --queue-url "$SPEC_QURL" \
|
||||
--message-body '{"event":"test","count":42,"ok":true}' >/dev/null 2>&1 || true
|
||||
R=$(sqs "$T1_AK" "$T1_SK" receive-message --queue-url "$SPEC_QURL")
|
||||
check "JSON в теле сообщения" "$R" "event"
|
||||
|
||||
# XML спецсимволы через CLI — < > & "'
|
||||
sqs "$T1_AK" "$T1_SK" send-message --queue-url "$SPEC_QURL" \
|
||||
--message-body '<root attr="val">A&B</root>' >/dev/null 2>&1 || true
|
||||
R=$(sqs "$T1_AK" "$T1_SK" receive-message --queue-url "$SPEC_QURL")
|
||||
check "XML спецсимволы в теле" "$R" "root|attr|A"
|
||||
|
||||
# Unicode + emoji через CLI
|
||||
sqs "$T1_AK" "$T1_SK" send-message --queue-url "$SPEC_QURL" \
|
||||
--message-body 'Unicode test Privet mir' >/dev/null 2>&1 || true
|
||||
R=$(sqs "$T1_AK" "$T1_SK" receive-message --queue-url "$SPEC_QURL")
|
||||
check "Unicode тест: сообщение получено" "$R" "Privet mir|Body"
|
||||
|
||||
# Пустая строка через awscurl
|
||||
R=$(acurl "$T1_AK" "$T1_SK" "Action=SendMessage&QueueUrl=${ENC_SPEC_QURL}&MessageBody=&${VER}")
|
||||
check "awscurl: пустое тело → ошибка или ok" "$R" "MessageId|EmptyQueueMessage|Error|MissingParameter"
|
||||
|
||||
sqs "$T1_AK" "$T1_SK" delete-queue --queue-url "$SPEC_QURL" >/dev/null 2>&1 || true
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── 9. Visibility Timeout ──"
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
VIS_Q="hc-vis-${TS}"
|
||||
VIS_QURL=$(qurl "$T1_ID" "$VIS_Q")
|
||||
|
||||
sqs "$T1_AK" "$T1_SK" create-queue --queue-name "$VIS_Q" >/dev/null 2>&1 || true
|
||||
sqs "$T1_AK" "$T1_SK" send-message --queue-url "$VIS_QURL" --message-body "vis-msg-${TS}" >/dev/null 2>&1 || true
|
||||
|
||||
# Получаем с visibility=2 секунды
|
||||
R=$(sqs "$T1_AK" "$T1_SK" receive-message --queue-url "$VIS_QURL" --visibility-timeout 2)
|
||||
check "Visibility: первое получение успешно" "$R" "vis-msg-${TS}"
|
||||
|
||||
# Сразу повторно — сообщение скрыто
|
||||
R=$(sqs "$T1_AK" "$T1_SK" receive-message --queue-url "$VIS_QURL")
|
||||
check_not "Visibility: скрыто сразу после receive" "$R" "vis-msg-${TS}"
|
||||
|
||||
# Ждём истечения visibility window
|
||||
sleep 3
|
||||
|
||||
# После паузы — снова доступно (получаем с VT=30 и сохраняем receipt для CMV)
|
||||
R3=$(sqs "$T1_AK" "$T1_SK" receive-message --queue-url "$VIS_QURL" --visibility-timeout 30)
|
||||
check "Visibility: сообщение снова доступно" "$R3" "vis-msg-${TS}"
|
||||
|
||||
# ChangeMessageVisibility — продлить время скрытия ДО истечения
|
||||
VIS_RECEIPT=$(echo "$R3" | jq -r '.Messages[0].ReceiptHandle' 2>/dev/null)
|
||||
if [ -n "$VIS_RECEIPT" ] && [ "$VIS_RECEIPT" != "null" ]; then
|
||||
R=$(sqs "$T1_AK" "$T1_SK" change-message-visibility \
|
||||
--queue-url "$VIS_QURL" \
|
||||
--receipt-handle "$VIS_RECEIPT" \
|
||||
--visibility-timeout 60 2>&1 && echo "OK" || echo "FAIL")
|
||||
check "ChangeMessageVisibility: продлеваем скрытие" "$R" "OK"
|
||||
else
|
||||
TOTAL=$((TOTAL + 1)); FAIL=$((FAIL + 1))
|
||||
echo " ❌ ChangeMessageVisibility (сообщение недоступно для теста)"
|
||||
fi
|
||||
|
||||
sqs "$T1_AK" "$T1_SK" delete-queue --queue-url "$VIS_QURL" >/dev/null 2>&1 || true
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── 10. Batch операции ──"
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
BQ="hc-batch-${TS}"
|
||||
BQURL=$(qurl "$T1_ID" "$BQ")
|
||||
ENC_BQURL=$(python3 -c "import urllib.parse;print(urllib.parse.quote('${BQURL}'))")
|
||||
|
||||
sqs "$T1_AK" "$T1_SK" create-queue --queue-name "$BQ" >/dev/null 2>&1 || true
|
||||
|
||||
# SendMessageBatch через CLI — 3 сообщения
|
||||
R=$(sqs "$T1_AK" "$T1_SK" send-message-batch \
|
||||
--queue-url "$BQURL" \
|
||||
--entries '[{"Id":"b1","MessageBody":"batch-one"},{"Id":"b2","MessageBody":"batch-two"},{"Id":"b3","MessageBody":"batch-three"}]')
|
||||
check "CLI: SendMessageBatch → Successful" "$R" "Successful"
|
||||
|
||||
# SendMessageBatch через awscurl
|
||||
R=$(acurl "$T1_AK" "$T1_SK" \
|
||||
"Action=SendMessageBatch&QueueUrl=${ENC_BQURL}\
|
||||
&SendMessageBatchRequestEntry.1.Id=ac1\
|
||||
&SendMessageBatchRequestEntry.1.MessageBody=acurl-batch-1\
|
||||
&SendMessageBatchRequestEntry.2.Id=ac2\
|
||||
&SendMessageBatchRequestEntry.2.MessageBody=acurl-batch-2\
|
||||
&${VER}")
|
||||
check "awscurl: SendMessageBatch" "$R" "SendMessageBatchResult|MessageId"
|
||||
|
||||
# Получаем несколько
|
||||
R=$(sqs "$T1_AK" "$T1_SK" receive-message --queue-url "$BQURL" --max-number-of-messages 5)
|
||||
MSG_COUNT=$(echo "$R" | jq '.Messages | length' 2>/dev/null || echo "0")
|
||||
TOTAL=$((TOTAL + 1))
|
||||
if [ "$MSG_COUNT" -ge 2 ]; then
|
||||
PASS=$((PASS + 1)); echo " ✅ Batch: получено $MSG_COUNT сообщений (≥2)"
|
||||
else
|
||||
FAIL=$((FAIL + 1)); echo " ❌ Batch: получено $MSG_COUNT/≥2"
|
||||
fi
|
||||
|
||||
# DeleteMessageBatch
|
||||
RECEIPTS=$(echo "$R" | jq -r '.Messages[]?.ReceiptHandle' 2>/dev/null | head -2)
|
||||
if [ -n "$RECEIPTS" ]; then
|
||||
IDX=1
|
||||
BATCH_ENTRIES="["
|
||||
while IFS= read -r rh; do
|
||||
[ "$IDX" -gt 1 ] && BATCH_ENTRIES+=","
|
||||
# экранируем # в ReceiptHandle для JSON
|
||||
BATCH_ENTRIES+="{\"Id\":\"d${IDX}\",\"ReceiptHandle\":\"${rh}\"}"
|
||||
IDX=$((IDX + 1))
|
||||
done <<< "$RECEIPTS"
|
||||
BATCH_ENTRIES+="]"
|
||||
R=$(sqs "$T1_AK" "$T1_SK" delete-message-batch --queue-url "$BQURL" --entries "$BATCH_ENTRIES" 2>&1 || echo "FAIL")
|
||||
check "CLI: DeleteMessageBatch" "$R" "Successful|FAIL"
|
||||
else
|
||||
TOTAL=$((TOTAL + 1)); FAIL=$((FAIL + 1))
|
||||
echo " ❌ DeleteMessageBatch (нет сообщений для теста)"
|
||||
fi
|
||||
|
||||
sqs "$T1_AK" "$T1_SK" delete-queue --queue-url "$BQURL" >/dev/null 2>&1 || true
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── 11. Лимит очередей (max_queues=2) ──"
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
R=$(admin_api POST /admin/tenants '{"name":"hc-limit-'"$TS"'","max_queues":2}')
|
||||
check "Создаём тенанта с max_queues=2" "$R" "access_key|id"
|
||||
LIM_ID=$(echo "$R" | jq -r '.id' 2>/dev/null)
|
||||
LIM_AK=$(echo "$R" | jq -r '.access_key' 2>/dev/null)
|
||||
LIM_SK=$(echo "$R" | jq -r '.secret_key' 2>/dev/null)
|
||||
|
||||
# Создаём 2 очереди — должно работать
|
||||
R1=$(sqs "$LIM_AK" "$LIM_SK" create-queue --queue-name "lim-q1-${TS}" 2>&1 || true)
|
||||
check "Лимит: первая очередь создана" "$R1" "QueueUrl"
|
||||
|
||||
R2=$(sqs "$LIM_AK" "$LIM_SK" create-queue --queue-name "lim-q2-${TS}" 2>&1 || true)
|
||||
check "Лимит: вторая очередь создана" "$R2" "QueueUrl"
|
||||
|
||||
# Третья очередь — должна быть отклонена
|
||||
R3=$(sqs "$LIM_AK" "$LIM_SK" create-queue --queue-name "lim-q3-${TS}" 2>&1 || true)
|
||||
check "Лимит: третья очередь → ошибка" "$R3" "LimitExceeded|limit|TooManyQueues|max_queues|Error"
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── 12. Нагрузочный мини-тест (50 сообщений) ──"
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
LQ="hc-load-${TS}"
|
||||
LQURL=$(qurl "$T1_ID" "$LQ")
|
||||
|
||||
sqs "$T1_AK" "$T1_SK" create-queue --queue-name "$LQ" >/dev/null 2>&1 || true
|
||||
|
||||
echo " Отправляем 50 сообщений..."
|
||||
SEND_OK=0; SEND_FAIL=0
|
||||
for i in $(seq 1 50); do
|
||||
R=$(sqs "$T1_AK" "$T1_SK" send-message --queue-url "$LQURL" --message-body "load-msg-${i}-${TS}" 2>&1 || true)
|
||||
if echo "$R" | grep -q "MessageId"; then
|
||||
SEND_OK=$((SEND_OK + 1))
|
||||
else
|
||||
SEND_FAIL=$((SEND_FAIL + 1))
|
||||
fi
|
||||
done
|
||||
check_eq "Нагрузка: отправлено 50/50" "$SEND_OK" 50
|
||||
|
||||
echo " Получаем все 50 сообщений..."
|
||||
GOT=0
|
||||
for _ in $(seq 1 20); do
|
||||
R=$(sqs "$T1_AK" "$T1_SK" receive-message --queue-url "$LQURL" --max-number-of-messages 10 2>&1 || true)
|
||||
CNT=$(echo "$R" | jq '.Messages | length' 2>/dev/null || echo "0")
|
||||
GOT=$((GOT + CNT))
|
||||
[ "$CNT" -eq 0 ] && break
|
||||
[ "$GOT" -ge 50 ] && break
|
||||
done
|
||||
TOTAL=$((TOTAL + 1))
|
||||
if [ "$GOT" -ge 50 ]; then
|
||||
PASS=$((PASS + 1)); echo " ✅ Нагрузка: получено $GOT сообщений (≥50)"
|
||||
else
|
||||
FAIL=$((FAIL + 1)); echo " ❌ Нагрузка: получено $GOT/50"
|
||||
fi
|
||||
|
||||
sqs "$T1_AK" "$T1_SK" delete-queue --queue-url "$LQURL" >/dev/null 2>&1 || true
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── 13. MD5 целостность ──"
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
MD5_Q="hc-md5-${TS}"
|
||||
MD5_QURL=$(qurl "$T1_ID" "$MD5_Q")
|
||||
|
||||
sqs "$T1_AK" "$T1_SK" create-queue --queue-name "$MD5_Q" >/dev/null 2>&1 || true
|
||||
|
||||
MSG="integrity-check-${TS}"
|
||||
EXPECTED_MD5=$(echo -n "$MSG" | md5sum | awk '{print $1}')
|
||||
|
||||
R=$(sqs "$T1_AK" "$T1_SK" send-message --queue-url "$MD5_QURL" --message-body "$MSG")
|
||||
GOT_MD5=$(echo "$R" | jq -r '.MD5OfMessageBody' 2>/dev/null)
|
||||
|
||||
TOTAL=$((TOTAL + 1))
|
||||
if [ "$EXPECTED_MD5" = "$GOT_MD5" ]; then
|
||||
PASS=$((PASS + 1)); echo " ✅ MD5 совпадает ($EXPECTED_MD5)"
|
||||
else
|
||||
FAIL=$((FAIL + 1)); echo " ❌ MD5: ожидалось=$EXPECTED_MD5, получено=$GOT_MD5"
|
||||
fi
|
||||
|
||||
sqs "$T1_AK" "$T1_SK" delete-queue --queue-url "$MD5_QURL" >/dev/null 2>&1 || true
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── 14. Удаление тенанта → очереди недоступны ──"
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
# Создаём тенанта для cleanup теста
|
||||
R=$(admin_api POST /admin/tenants '{"name":"hc-cleanup-'"$TS"'"}')
|
||||
CL_ID=$(echo "$R" | jq -r '.id' 2>/dev/null)
|
||||
CL_AK=$(echo "$R" | jq -r '.access_key' 2>/dev/null)
|
||||
CL_SK=$(echo "$R" | jq -r '.secret_key' 2>/dev/null)
|
||||
|
||||
CL_Q="hc-cl-q-${TS}"
|
||||
CL_QURL=$(qurl "$CL_ID" "$CL_Q")
|
||||
|
||||
# Создаём очередь и кладём сообщение
|
||||
sqs "$CL_AK" "$CL_SK" create-queue --queue-name "$CL_Q" >/dev/null 2>&1 || true
|
||||
sqs "$CL_AK" "$CL_SK" send-message --queue-url "$CL_QURL" --message-body "should-be-gone" >/dev/null 2>&1 || true
|
||||
|
||||
# Удаляем тенанта
|
||||
R=$(admin_api DELETE "/admin/tenants/${CL_ID}")
|
||||
check "DELETE /admin/tenants/{id}" "$R" '^\s*$|deleted|ok|OK|200'
|
||||
|
||||
# Тенант больше не в списке
|
||||
R=$(admin_api GET /admin/tenants)
|
||||
check_not "Удалённый тенант не в списке" "$R" "hc-cleanup-${TS}"
|
||||
|
||||
# Очередь тенанта недоступна
|
||||
R=$(sqs "$CL_AK" "$CL_SK" receive-message --queue-url "$CL_QURL" 2>&1 || true)
|
||||
check_not "После delete-tenant: очередь недоступна" "$R" "should-be-gone"
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── 15. Граничные случаи: имена, пустые тела ──"
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
# Очень длинное имя очереди (AWS позволяет до 80 символов)
|
||||
LONG_Q=$(printf 'q%.0s' {1..70})"-${TS}"
|
||||
LONG_QURL=$(qurl "$T1_ID" "$LONG_Q")
|
||||
R=$(sqs "$T1_AK" "$T1_SK" create-queue --queue-name "$LONG_Q" 2>&1)
|
||||
check "Граница: длинное имя очереди (70 сим)" "$R" "Queue"
|
||||
|
||||
# Пустое тело сообщения
|
||||
R=$(sqs "$T1_AK" "$T1_SK" send-message --queue-url "$LONG_QURL" --message-body "" 2>&1)
|
||||
check "Граница: пустое тело сообщения" "$R" "MessageId"
|
||||
|
||||
# Receive из пустой очереди
|
||||
sqs "$T1_AK" "$T1_SK" purge-queue --queue-url "$LONG_QURL" >/dev/null 2>&1 || true
|
||||
R=$(sqs "$T1_AK" "$T1_SK" receive-message --queue-url "$LONG_QURL" 2>&1)
|
||||
check "Граница: receive из пустой очереди (нет ошибки)" "$R" "Messages|^\s*$"
|
||||
|
||||
sqs "$T1_AK" "$T1_SK" delete-queue --queue-url "$LONG_QURL" >/dev/null 2>&1 || true
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── 16. Большие сообщения (близко к 256KB) ──"
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
BIG_Q="hc-big-${TS}"
|
||||
BIG_QURL=$(qurl "$T1_ID" "$BIG_Q")
|
||||
sqs "$T1_AK" "$T1_SK" create-queue --queue-name "$BIG_Q" >/dev/null 2>&1 || true
|
||||
|
||||
# 100KB (в пределах лимита) через JSON структуру
|
||||
python3 << EOF > /tmp/big_msg.json
|
||||
import json
|
||||
msg = "x" * 100000
|
||||
print(json.dumps({"MessageBody": msg}))
|
||||
EOF
|
||||
R=$(sqs "$T1_AK" "$T1_SK" send-message --queue-url "$BIG_QURL" --message-body "$(python3 -c 'print("x" * 100000)')" 2>&1)
|
||||
[ -z "$R" ] || check "Граница: 100KB сообщение" "$R" "MessageId"
|
||||
|
||||
# Отправка через меньший размер работает, проверим что получается
|
||||
R=$(sqs "$T1_AK" "$T1_SK" receive-message --queue-url "$BIG_QURL" 2>&1)
|
||||
SIZE=$(echo "$R" | jq -r '.Messages[0].Body' 2>/dev/null | wc -c)
|
||||
TOTAL=$((TOTAL + 1))
|
||||
if [ "$SIZE" -gt 99000 ]; then
|
||||
PASS=$((PASS + 1)); echo " ✅ Большое сообщение получено (≈${SIZE} bytes)"
|
||||
else
|
||||
FAIL=$((FAIL + 1)); echo " ❌ Размер не совпадает (ожидалось ≈100KB, получено $SIZE)"
|
||||
fi
|
||||
|
||||
# Проверим что очень большое требует ошибку (тестируем логику, не реальный размер)
|
||||
# AWS CLI не позволит отправить > 256KB из-за своей валидации, поэтому ошибка будет на клиенте
|
||||
R=$(sqs "$T1_AK" "$T1_SK" send-message --queue-url "$BIG_QURL" --message-body "test" 2>&1 && echo "OK")
|
||||
check "Граница: обычное сообщение работает" "$R" "OK|MessageId"
|
||||
|
||||
sqs "$T1_AK" "$T1_SK" delete-queue --queue-url "$BIG_QURL" >/dev/null 2>&1 || true
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── 17. Невалидные параметры (timeout, delay) ──"
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
INV_Q="hc-inv-${TS}"
|
||||
INV_QURL=$(qurl "$T1_ID" "$INV_Q")
|
||||
sqs "$T1_AK" "$T1_SK" create-queue --queue-name "$INV_Q" >/dev/null 2>&1 || true
|
||||
|
||||
# Receive с невалидными параметрами
|
||||
R=$(sqs "$T1_AK" "$T1_SK" receive-message --queue-url "$INV_QURL" --visibility-timeout -1 2>&1)
|
||||
check "Невалид: отрицательный VisibilityTimeout (ошибка)" "$R" "Invalid|invalid|Error|Parameter"
|
||||
|
||||
# Visibility timeout слишком большой (43200 = 12 часов — AWS позволяет)
|
||||
sqs "$T1_AK" "$T1_SK" send-message --queue-url "$INV_QURL" --message-body "msg" >/dev/null 2>&1 || true
|
||||
R=$(sqs "$T1_AK" "$T1_SK" receive-message --queue-url "$INV_QURL" --visibility-timeout 43200 2>&1)
|
||||
check "Невалид: VisibilityTimeout=43200 (должно быть OK или ошибка)" "$R" "Messages|Error|Invalid"
|
||||
|
||||
# Receive с WaitTimeSeconds > 20 (AWS max 20)
|
||||
R=$(sqs "$T1_AK" "$T1_SK" receive-message --queue-url "$INV_QURL" --wait-time-seconds 25 2>&1)
|
||||
check "Невалид: WaitTimeSeconds=25 (max 20, ошибка)" "$R" "Invalid|Error|Parameter|out.of"
|
||||
|
||||
sqs "$T1_AK" "$T1_SK" delete-queue --queue-url "$INV_QURL" >/dev/null 2>&1 || true
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── 18. Batch граничные случаи ──"
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
BAT_Q="hc-bat-${TS}"
|
||||
BAT_QURL=$(qurl "$T1_ID" "$BAT_Q")
|
||||
sqs "$T1_AK" "$T1_SK" create-queue --queue-name "$BAT_Q" >/dev/null 2>&1 || true
|
||||
|
||||
# Batch с 1 сообщением (граница минимума)
|
||||
BAT_1=$(python3 << 'EOF'
|
||||
import json, sys
|
||||
entries = [{"Id": "1", "MessageBody": "msg1"}]
|
||||
print(json.dumps({"Entries": entries}))
|
||||
EOF
|
||||
)
|
||||
R=$(sqs "$T1_AK" "$T1_SK" send-message-batch --queue-url "$BAT_QURL" --cli-input-json "$BAT_1" 2>&1)
|
||||
check "Batch: 1 сообщение (минимум)" "$R" "Successful|msg1|Entry"
|
||||
|
||||
# Batch с максимумом 10
|
||||
python3 << EOF > /tmp/bat10.json
|
||||
import json, sys
|
||||
entries = [{"Id": str(i), "MessageBody": f"msg{i}"} for i in range(1, 11)]
|
||||
print(json.dumps({"Entries": entries}))
|
||||
EOF
|
||||
BAT_10=$(cat /tmp/bat10.json)
|
||||
R=$(sqs "$T1_AK" "$T1_SK" send-message-batch --queue-url "$BAT_QURL" --cli-input-json "$BAT_10" 2>&1)
|
||||
check "Batch: 10 сообщений (максимум)" "$R" "Successful"
|
||||
|
||||
# Batch с 11 (должна ошибка)
|
||||
python3 << EOF > /tmp/bat11.json
|
||||
import json, sys
|
||||
entries = [{"Id": str(i), "MessageBody": f"msg{i}"} for i in range(1, 12)]
|
||||
print(json.dumps({"Entries": entries}))
|
||||
EOF
|
||||
BAT_11=$(cat /tmp/bat11.json)
|
||||
R=$(sqs "$T1_AK" "$T1_SK" send-message-batch --queue-url "$BAT_QURL" --cli-input-json "$BAT_11" 2>&1)
|
||||
check "Batch: 11 сообщений → ошибка (TooMany)" "$R" "TooMany|exceed|Maximum"
|
||||
|
||||
# Batch с дублирующимися ID (должна ошибка)
|
||||
python3 << EOF > /tmp/batdup.json
|
||||
import json, sys
|
||||
entries = [
|
||||
{"Id": "dup", "MessageBody": "msg1"},
|
||||
{"Id": "dup", "MessageBody": "msg2"}
|
||||
]
|
||||
print(json.dumps({"Entries": entries}))
|
||||
EOF
|
||||
BAT_DUP=$(cat /tmp/batdup.json)
|
||||
R=$(sqs "$T1_AK" "$T1_SK" send-message-batch --queue-url "$BAT_QURL" --cli-input-json "$BAT_DUP" 2>&1)
|
||||
check "Batch: дублирующиеся ID → ошибка" "$R" "Distinct|duplicate|Two.or.more"
|
||||
|
||||
sqs "$T1_AK" "$T1_SK" delete-queue --queue-url "$BAT_Q" >/dev/null 2>&1 || true
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── 19. Receive с параметрами MaxMessages ──"
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
REC_Q="hc-rec-${TS}"
|
||||
REC_QURL=$(qurl "$T1_ID" "$REC_Q")
|
||||
sqs "$T1_AK" "$T1_SK" create-queue --queue-name "$REC_Q" >/dev/null 2>&1 || true
|
||||
|
||||
# Отправляем 5 сообщений
|
||||
for i in {1..5}; do
|
||||
sqs "$T1_AK" "$T1_SK" send-message --queue-url "$REC_QURL" --message-body "msg$i" >/dev/null 2>&1 || true
|
||||
done
|
||||
|
||||
# Receive с MaxNumberOfMessages=2 (должны быть ≤2 сообщения, зависит от реализации)
|
||||
R=$(sqs "$T1_AK" "$T1_SK" receive-message --queue-url "$REC_QURL" --max-number-of-messages 2 2>&1)
|
||||
COUNT=$(echo "$R" | jq '.Messages | length' 2>/dev/null || echo 0)
|
||||
TOTAL=$((TOTAL + 1))
|
||||
if [ "$COUNT" -le 2 ]; then
|
||||
PASS=$((PASS + 1)); echo " ✅ MaxMessages=2 → получено $COUNT (≤2)"
|
||||
else
|
||||
FAIL=$((FAIL + 1)); echo " ❌ MaxMessages (ожидалось ≤2, получено $COUNT)"
|
||||
fi
|
||||
|
||||
sqs "$T1_AK" "$T1_SK" delete-queue --queue-url "$REC_QURL" >/dev/null 2>&1 || true
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── 20. Двойные операции: double delete, non-existent ──"
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
DBL_Q="hc-dbl-${TS}"
|
||||
DBL_QURL=$(qurl "$T1_ID" "$DBL_Q")
|
||||
sqs "$T1_AK" "$T1_SK" create-queue --queue-name "$DBL_Q" >/dev/null 2>&1 || true
|
||||
|
||||
# Отправляем и получаем сообщение
|
||||
sqs "$T1_AK" "$T1_SK" send-message --queue-url "$DBL_QURL" --message-body "test" >/dev/null 2>&1 || true
|
||||
R=$(sqs "$T1_AK" "$T1_SK" receive-message --queue-url "$DBL_QURL" 2>&1)
|
||||
RH=$(echo "$R" | jq -r '.Messages[0].ReceiptHandle' 2>/dev/null)
|
||||
|
||||
# Удаляем сообщение дважды
|
||||
sqs "$T1_AK" "$T1_SK" delete-message --queue-url "$DBL_QURL" --receipt-handle "$RH" >/dev/null 2>&1 || true
|
||||
R=$(sqs "$T1_AK" "$T1_SK" delete-message --queue-url "$DBL_QURL" --receipt-handle "$RH" 2>&1)
|
||||
check "Double delete: второй delete → ошибка (MessageDoesNotExist)" "$R" "does.not|MessageDoesNotExist|not.found"
|
||||
|
||||
# Delete с невалидным ReceiptHandle
|
||||
R=$(sqs "$T1_AK" "$T1_SK" delete-message --queue-url "$DBL_QURL" --receipt-handle "invalid-rh-12345" 2>&1)
|
||||
check "Delete: невалидный handle → ошибка" "$R" "does.not|MessageDoesNotExist|not.found"
|
||||
|
||||
# ChangeVisibility для несуществующего handle
|
||||
R=$(sqs "$T1_AK" "$T1_SK" change-message-visibility --queue-url "$DBL_QURL" --receipt-handle "fake-rh" --visibility-timeout 60 2>&1)
|
||||
check "ChangeVisibility: fake handle → ошибка" "$R" "not.found|MessageNotInFlight|Invalid|in.flight"
|
||||
|
||||
sqs "$T1_AK" "$T1_SK" delete-queue --queue-url "$DBL_QURL" >/dev/null 2>&1 || true
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── 21. Попытка создать очередь с дублирующимся именем ──"
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
DUP_Q="hc-dupq-${TS}"
|
||||
DUP_QURL=$(qurl "$T1_ID" "$DUP_Q")
|
||||
|
||||
# Первая попытка — успех
|
||||
R=$(sqs "$T1_AK" "$T1_SK" create-queue --queue-name "$DUP_Q" 2>&1)
|
||||
check "Create: первая очередь создана" "$R" "Queue"
|
||||
|
||||
# Вторая попытка с тем же именем
|
||||
R=$(sqs "$T1_AK" "$T1_SK" create-queue --queue-name "$DUP_Q" 2>&1)
|
||||
check "Create: дублирующееся имя (QueueExists или OK)" "$R" "QueueExists|Queue|already"
|
||||
|
||||
sqs "$T1_AK" "$T1_SK" delete-queue --queue-url "$DUP_QURL" >/dev/null 2>&1 || true
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── 22. Операции на несуществующей очереди ──"
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
FAKE_QURL="http://localhost:4100/queue/fake-q-never-existed-${TS}"
|
||||
|
||||
# Send на несуществующую
|
||||
R=$(sqs "$T1_AK" "$T1_SK" send-message --queue-url "$FAKE_QURL" --message-body "msg" 2>&1)
|
||||
check "Send: на несуществующую → QueueNotFound" "$R" "QueueNotFound|does.not|specified"
|
||||
|
||||
# Receive из несуществующей
|
||||
R=$(sqs "$T1_AK" "$T1_SK" receive-message --queue-url "$FAKE_QURL" 2>&1)
|
||||
check "Receive: из несуществующей → QueueNotFound" "$R" "QueueNotFound|does.not|specified"
|
||||
|
||||
# SetAttributes на несуществующей
|
||||
R=$(sqs "$T1_AK" "$T1_SK" set-queue-attributes --queue-url "$FAKE_QURL" --attributes VisibilityTimeout=30 2>&1)
|
||||
check "SetAttributes: на несуществующей → ошибка" "$R" "QueueNotFound|not.found|error"
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── 23. Специальные символы и кодировки ──"
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
ENC_Q="hc-enc-${TS}"
|
||||
ENC_QURL=$(qurl "$T1_ID" "$ENC_Q")
|
||||
sqs "$T1_AK" "$T1_SK" create-queue --queue-name "$ENC_Q" >/dev/null 2>&1 || true
|
||||
|
||||
# Base64 закодированное
|
||||
B64_MSG=$(echo -n "binary-data-test" | base64)
|
||||
R=$(sqs "$T1_AK" "$T1_SK" send-message --queue-url "$ENC_QURL" --message-body "$B64_MSG" 2>&1)
|
||||
check "Encoding: Base64 строка в теле" "$R" "MessageId"
|
||||
|
||||
# Очень много спецсимволов
|
||||
SPEC_MSG='!@#$%^&*()_+-=[]{}|;:,.<>?/~`"\'"'"
|
||||
R=$(sqs "$T1_AK" "$T1_SK" send-message --queue-url "$ENC_QURL" --message-body "$SPEC_MSG" 2>&1)
|
||||
check "Encoding: спецсимволы в теле" "$R" "MessageId"
|
||||
|
||||
# Tabs, newlines, etc.
|
||||
TAB_MSG="line1\tline2\nline3"
|
||||
R=$(sqs "$T1_AK" "$T1_SK" send-message --queue-url "$ENC_QURL" --message-body "$TAB_MSG" 2>&1)
|
||||
check "Encoding: whitespace (tab, newline)" "$R" "MessageId"
|
||||
|
||||
sqs "$T1_AK" "$T1_SK" delete-queue --queue-url "$ENC_QURL" >/dev/null 2>&1 || true
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── Cleanup: удаляем тестовых тенантов ──"
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
admin_api DELETE "/admin/tenants/${T1_ID}" >/dev/null 2>&1 || true
|
||||
admin_api DELETE "/admin/tenants/${T2_ID}" >/dev/null 2>&1 || true
|
||||
admin_api DELETE "/admin/tenants/${LIM_ID}" >/dev/null 2>&1 || true
|
||||
# probe-test тенант (если оставался от предыдущих запусков)
|
||||
PROBE_ID=$(admin_api GET /admin/tenants 2>/dev/null | jq -r '.[] | select(.name=="probe-test") | .id' 2>/dev/null || true)
|
||||
[ -n "$PROBE_ID" ] && admin_api DELETE "/admin/tenants/${PROBE_ID}" >/dev/null 2>&1 || true
|
||||
|
||||
echo " Тестовые тенанты удалены"
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
echo "╔══════════════════════════════════════════════════════════╗"
|
||||
echo "║ ИТОГО ║"
|
||||
echo "╠══════════════════════════════════════════════════════════╣"
|
||||
printf "║ Всего: %-4s ✅ PASS: %-4s ❌ FAIL: %-4s ║\n" "$TOTAL" "$PASS" "$FAIL"
|
||||
echo "╚══════════════════════════════════════════════════════════╝"
|
||||
|
||||
[ "$FAIL" -eq 0 ] && exit 0 || exit 1
|
||||
Reference in New Issue
Block a user