docs: agent models summary, available models list, cluster migration guide (2026-04-13)
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
#!/bin/bash
|
||||
# quick_test.sh — Быстрая проверка SQS-сервиса
|
||||
# Проверяет: создание очереди, отправку, получение, удаление
|
||||
# Требования: curl
|
||||
# Запуск: bash quick_test.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SQS_ENDPOINT="https://sqs.kube5s.ru/sqs/test001"
|
||||
ACCESS_KEY="SQSAK-test001-ae4dc8"
|
||||
SECRET_KEY="J8mElXq8Nwww8iFRtGJCm8KfxM9XSTluTqS2A67xsEE"
|
||||
AUTH="${ACCESS_KEY}:${SECRET_KEY}"
|
||||
VERSION="2012-11-05"
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
QUEUE="quicktest-$$"
|
||||
|
||||
call() {
|
||||
curl -sf --max-time 15 -u "$AUTH" "${SQS_ENDPOINT}?${1}&Version=${VERSION}" 2>/dev/null
|
||||
}
|
||||
|
||||
check() {
|
||||
if echo "$2" | grep -q "$3"; then
|
||||
PASS=$((PASS + 1)); echo " ✅ $1"
|
||||
else
|
||||
FAIL=$((FAIL + 1)); echo " ❌ $1"
|
||||
fi
|
||||
}
|
||||
|
||||
echo "=== SQS Quick Test ==="
|
||||
echo ""
|
||||
|
||||
# 1. Создать очередь
|
||||
R=$(call "Action=CreateQueue&QueueName=${QUEUE}")
|
||||
check "Создание очереди" "$R" "CreateQueueResult"
|
||||
|
||||
# 2. Список очередей
|
||||
R=$(call "Action=ListQueues")
|
||||
check "Очередь в списке" "$R" "${QUEUE}"
|
||||
|
||||
# 3. Отправить сообщение
|
||||
R=$(call "Action=SendMessage&QueueUrl=${SQS_ENDPOINT}/test001/${QUEUE}&MessageBody=HelloSQS")
|
||||
check "Отправка сообщения" "$R" "MessageId"
|
||||
|
||||
# 4. Получить сообщение
|
||||
R=$(call "Action=ReceiveMessage&QueueUrl=${SQS_ENDPOINT}/test001/${QUEUE}")
|
||||
check "Получение сообщения" "$R" "HelloSQS"
|
||||
|
||||
# 5. Удалить очередь
|
||||
R=$(call "Action=DeleteQueue&QueueUrl=${SQS_ENDPOINT}/test001/${QUEUE}")
|
||||
check "Удаление очереди" "$R" "DeleteQueueResponse"
|
||||
|
||||
# 6. Очередь исчезла
|
||||
R=$(call "Action=ListQueues")
|
||||
if echo "$R" | grep -q "${QUEUE}"; then
|
||||
FAIL=$((FAIL + 1)); echo " ❌ Очередь удалена из списка"
|
||||
else
|
||||
PASS=$((PASS + 1)); echo " ✅ Очередь удалена из списка"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Результат: ${PASS} OK / ${FAIL} FAIL"
|
||||
[ "$FAIL" -eq 0 ] && echo "Всё работает!" || exit 1
|
||||
@@ -0,0 +1,486 @@
|
||||
#!/bin/bash
|
||||
# 2026-04-09 — Полный набор тестов SQS API для ручного / автоматического запуска
|
||||
# Покрывает: happy path, невалидный ввод, edge cases, безопасность
|
||||
# Запуск: bash sqs_api_tests.sh
|
||||
# Требования: curl, base64, доступ к https://sqs.kube5s.ru
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# КОНФИГУРАЦИЯ
|
||||
# ═══════════════════════════════════════════
|
||||
SQS_HOST="https://sqs.kube5s.ru"
|
||||
SQS_BASE="${SQS_HOST}/sqs/test001"
|
||||
AK="SQSAK-test001-ae4dc8"
|
||||
SK="J8mElXq8Nwww8iFRtGJCm8KfxM9XSTluTqS2A67xsEE"
|
||||
AUTH="${AK}:${SK}"
|
||||
VERSION="2012-11-05"
|
||||
|
||||
# Счётчики результатов
|
||||
PASS=0
|
||||
FAIL=0
|
||||
SKIP=0
|
||||
TOTAL=0
|
||||
|
||||
# Уникальный суффикс чтобы тесты не конфликтовали при повторном запуске
|
||||
TS=$(date +%s)
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# УТИЛИТЫ
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
sqs_call() {
|
||||
# $1 = параметры запроса (после ?)
|
||||
# $2 = auth (опционально, по умолчанию $AUTH)
|
||||
local auth="${2:-$AUTH}"
|
||||
curl -s --max-time 15 -u "$auth" "${SQS_BASE}?${1}&Version=${VERSION}" 2>&1
|
||||
}
|
||||
|
||||
assert_contains() {
|
||||
# $1 = название теста
|
||||
# $2 = ответ
|
||||
# $3 = ожидаемая подстрока
|
||||
TOTAL=$((TOTAL + 1))
|
||||
if echo "$2" | grep -q "$3"; then
|
||||
PASS=$((PASS + 1))
|
||||
echo " ✅ PASS: $1"
|
||||
else
|
||||
FAIL=$((FAIL + 1))
|
||||
echo " ❌ FAIL: $1"
|
||||
echo " Ожидалось: '$3'"
|
||||
echo " Получено: $(echo "$2" | head -3)"
|
||||
fi
|
||||
}
|
||||
|
||||
assert_not_contains() {
|
||||
# $1 = название теста
|
||||
# $2 = ответ
|
||||
# $3 = подстрока которой НЕ должно быть
|
||||
TOTAL=$((TOTAL + 1))
|
||||
if echo "$2" | grep -q "$3"; then
|
||||
FAIL=$((FAIL + 1))
|
||||
echo " ❌ FAIL: $1"
|
||||
echo " НЕ ожидалось: '$3'"
|
||||
echo " Получено: $(echo "$2" | head -3)"
|
||||
else
|
||||
PASS=$((PASS + 1))
|
||||
echo " ✅ PASS: $1"
|
||||
fi
|
||||
}
|
||||
|
||||
assert_http_code() {
|
||||
# $1 = название теста
|
||||
# $2 = URL (полный)
|
||||
# $3 = ожидаемый HTTP код
|
||||
# $4 = auth (опционально)
|
||||
TOTAL=$((TOTAL + 1))
|
||||
local auth="${4:-$AUTH}"
|
||||
local code
|
||||
code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 15 -u "$auth" "$2" 2>&1)
|
||||
if [ "$code" = "$3" ]; then
|
||||
PASS=$((PASS + 1))
|
||||
echo " ✅ PASS: $1 (HTTP $code)"
|
||||
else
|
||||
FAIL=$((FAIL + 1))
|
||||
echo " ❌ FAIL: $1 (ожидался HTTP $3, получен $code)"
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup_queue() {
|
||||
# Тихое удаление очереди
|
||||
sqs_call "Action=DeleteQueue&QueueUrl=${SQS_BASE}/test001/$1" >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
echo "╔══════════════════════════════════════════════════╗"
|
||||
echo "║ SQS API Test Suite — $(date '+%Y-%m-%d %H:%M:%S') ║"
|
||||
echo "║ Endpoint: ${SQS_BASE} ║"
|
||||
echo "╚══════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ГРУППА 1: БАЗОВЫЕ ОПЕРАЦИИ (Happy Path)
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── ГРУППА 1: Базовые операции (Happy Path) ──"
|
||||
|
||||
Q1="testq-hp-${TS}"
|
||||
|
||||
# T01: Создание очереди
|
||||
R=$(sqs_call "Action=CreateQueue&QueueName=${Q1}")
|
||||
assert_contains "T01 CreateQueue" "$R" "CreateQueueResult"
|
||||
assert_contains "T01 CreateQueue URL" "$R" "${Q1}"
|
||||
|
||||
# T02: Повторное создание той же очереди — не ошибка, возвращает ту же URL
|
||||
R=$(sqs_call "Action=CreateQueue&QueueName=${Q1}")
|
||||
assert_contains "T02 CreateQueue idempotent" "$R" "CreateQueueResult"
|
||||
|
||||
# T03: ListQueues — очередь видна
|
||||
R=$(sqs_call "Action=ListQueues")
|
||||
assert_contains "T03 ListQueues" "$R" "${Q1}"
|
||||
|
||||
# T04: SendMessage
|
||||
R=$(sqs_call "Action=SendMessage&QueueUrl=${SQS_BASE}/test001/${Q1}&MessageBody=Hello")
|
||||
assert_contains "T04 SendMessage" "$R" "MessageId"
|
||||
assert_contains "T04 SendMessage MD5" "$R" "MD5OfMessageBody"
|
||||
|
||||
# T05: ReceiveMessage
|
||||
R=$(sqs_call "Action=ReceiveMessage&QueueUrl=${SQS_BASE}/test001/${Q1}")
|
||||
assert_contains "T05 ReceiveMessage Body" "$R" "Hello"
|
||||
assert_contains "T05 ReceiveMessage ReceiptHandle" "$R" "ReceiptHandle"
|
||||
|
||||
# Извлекаем ReceiptHandle для T06
|
||||
RH=$(echo "$R" | grep -oP '(?<=<ReceiptHandle>)[^<]+' | head -1)
|
||||
|
||||
# T06: DeleteMessage
|
||||
# ReceiptHandle содержит +/= — URL-кодируем чтобы не сломать query string
|
||||
if [ -n "$RH" ]; then
|
||||
RH_ENC=$(python3 -c "import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1], safe=''))" "$RH")
|
||||
R=$(sqs_call "Action=DeleteMessage&QueueUrl=${SQS_BASE}/test001/${Q1}&ReceiptHandle=${RH_ENC}")
|
||||
assert_contains "T06 DeleteMessage" "$R" "DeleteMessageResponse"
|
||||
else
|
||||
TOTAL=$((TOTAL + 1)); SKIP=$((SKIP + 1))
|
||||
echo " ⚠️ SKIP: T06 DeleteMessage — не удалось получить ReceiptHandle"
|
||||
fi
|
||||
|
||||
# T07: ReceiveMessage из пустой очереди — пустой результат
|
||||
R=$(sqs_call "Action=ReceiveMessage&QueueUrl=${SQS_BASE}/test001/${Q1}")
|
||||
assert_not_contains "T07 ReceiveMessage empty queue" "$R" "<Body>"
|
||||
|
||||
# T08: GetQueueAttributes
|
||||
R=$(sqs_call "Action=GetQueueAttributes&QueueUrl=${SQS_BASE}/test001/${Q1}&AttributeName.1=All")
|
||||
assert_contains "T08 GetQueueAttributes" "$R" "GetQueueAttributesResponse"
|
||||
|
||||
# T09: PurgeQueue
|
||||
sqs_call "Action=SendMessage&QueueUrl=${SQS_BASE}/test001/${Q1}&MessageBody=ToPurge" >/dev/null
|
||||
R=$(sqs_call "Action=PurgeQueue&QueueUrl=${SQS_BASE}/test001/${Q1}")
|
||||
assert_contains "T09 PurgeQueue" "$R" "PurgeQueueResponse"
|
||||
|
||||
# T10: DeleteQueue
|
||||
R=$(sqs_call "Action=DeleteQueue&QueueUrl=${SQS_BASE}/test001/${Q1}")
|
||||
assert_contains "T10 DeleteQueue" "$R" "DeleteQueueResponse"
|
||||
|
||||
# T11: ListQueues — очередь исчезла
|
||||
R=$(sqs_call "Action=ListQueues")
|
||||
assert_not_contains "T11 ListQueues after delete" "$R" "${Q1}"
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ГРУППА 2: НЕВАЛИДНЫЙ ВВОД
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── ГРУППА 2: Невалидный ввод ──"
|
||||
|
||||
# T12: Неизвестный Action
|
||||
R=$(sqs_call "Action=BogusAction")
|
||||
assert_contains "T12 Unknown Action" "$R" "InvalidAction\|AWS.SimpleQueueService.NonExistentAction\|UnknownAction\|InvalidParameterValue"
|
||||
|
||||
# T13: Пустой Action
|
||||
R=$(sqs_call "Action=")
|
||||
assert_contains "T13 Empty Action" "$R" "Error\|MissingAction\|InvalidAction\|Value for parameter Action is not valid"
|
||||
|
||||
# T14: Без Action вообще
|
||||
R=$(curl -s --max-time 15 -u "$AUTH" "${SQS_BASE}?Version=${VERSION}" 2>&1)
|
||||
assert_contains "T14 Missing Action" "$R" "Error\|MissingAction\|request"
|
||||
|
||||
# T15: CreateQueue без QueueName
|
||||
R=$(sqs_call "Action=CreateQueue")
|
||||
assert_contains "T15 CreateQueue no name" "$R" "Error\|MissingParameter\|Value for parameter QueueName is not valid"
|
||||
|
||||
# T16: CreateQueue с пустым QueueName
|
||||
R=$(sqs_call "Action=CreateQueue&QueueName=")
|
||||
assert_contains "T16 CreateQueue empty name" "$R" "Error\|InvalidParameterValue"
|
||||
|
||||
# T17: CreateQueue с недопустимыми символами (пробелы, кириллица)
|
||||
# Akka HTTP (ElasticMQ) отвечает "Illegal request-target" на raw кириллицу
|
||||
R=$(sqs_call "Action=CreateQueue&QueueName=очередь%20тест")
|
||||
assert_contains "T17 CreateQueue invalid chars" "$R" "Error\|InvalidParameterValue\|Illegal"
|
||||
|
||||
# T18: CreateQueue с очень длинным именем (>80 символов)
|
||||
LONGNAME=$(printf 'q%.0s' {1..100})
|
||||
R=$(sqs_call "Action=CreateQueue&QueueName=${LONGNAME}")
|
||||
assert_contains "T18 CreateQueue name too long" "$R" "Error\|InvalidParameterValue\|CreateQueueResult"
|
||||
# Примечание: ElasticMQ может создать — это допустимо если не strict AWS
|
||||
|
||||
# T19: SendMessage без MessageBody
|
||||
R=$(sqs_call "Action=SendMessage&QueueUrl=${SQS_BASE}/test001/nonexist")
|
||||
assert_contains "T19 SendMessage no body" "$R" "Error\|MissingParameter\|The request must contain the parameter MessageBody"
|
||||
|
||||
# T20: SendMessage без QueueUrl
|
||||
R=$(sqs_call "Action=SendMessage&MessageBody=test")
|
||||
assert_contains "T20 SendMessage no QueueUrl" "$R" "Error\|MissingParameter\|AWS.SimpleQueueService.NonExistentQueue\|The request must contain"
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ГРУППА 3: НЕСУЩЕСТВУЮЩИЕ РЕСУРСЫ
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── ГРУППА 3: Несуществующие ресурсы ──"
|
||||
|
||||
# T21: SendMessage в несуществующую очередь (autoCreateQueues=false!)
|
||||
R=$(sqs_call "Action=SendMessage&QueueUrl=${SQS_BASE}/test001/nonexist-${TS}&MessageBody=fail")
|
||||
assert_contains "T21 SendMessage nonexistent queue" "$R" "NonExistentQueue\|AWS.SimpleQueueService.NonExistentQueue\|does not exist\|Error"
|
||||
|
||||
# T22: ReceiveMessage из несуществующей очереди
|
||||
R=$(sqs_call "Action=ReceiveMessage&QueueUrl=${SQS_BASE}/test001/nonexist-${TS}")
|
||||
assert_contains "T22 ReceiveMessage nonexistent queue" "$R" "NonExistentQueue\|AWS.SimpleQueueService.NonExistentQueue\|does not exist\|Error"
|
||||
|
||||
# T23: DeleteQueue несуществующей очереди
|
||||
R=$(sqs_call "Action=DeleteQueue&QueueUrl=${SQS_BASE}/test001/nonexist-${TS}")
|
||||
assert_contains "T23 DeleteQueue nonexistent" "$R" "NonExistentQueue\|AWS.SimpleQueueService.NonExistentQueue\|does not exist\|Error"
|
||||
|
||||
# T24: GetQueueAttributes несуществующей очереди
|
||||
R=$(sqs_call "Action=GetQueueAttributes&QueueUrl=${SQS_BASE}/test001/nonexist-${TS}&AttributeName.1=All")
|
||||
assert_contains "T24 GetQueueAttributes nonexistent" "$R" "NonExistentQueue\|AWS.SimpleQueueService.NonExistentQueue\|does not exist\|Error"
|
||||
|
||||
# T25: DeleteMessage с фейковым ReceiptHandle
|
||||
Q25="testq-rh-${TS}"
|
||||
sqs_call "Action=CreateQueue&QueueName=${Q25}" >/dev/null
|
||||
R=$(sqs_call "Action=DeleteMessage&QueueUrl=${SQS_BASE}/test001/${Q25}&ReceiptHandle=fake-receipt-handle-12345")
|
||||
assert_contains "T25 DeleteMessage fake receipt" "$R" "Error\|ReceiptHandleIsInvalid\|DeleteMessageResponse"
|
||||
# Примечание: ElasticMQ может молча игнорировать — это допустимо
|
||||
cleanup_queue "$Q25"
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ГРУППА 4: АУТЕНТИФИКАЦИЯ И БЕЗОПАСНОСТЬ
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── ГРУППА 4: Аутентификация и безопасность ──"
|
||||
|
||||
# T26: Неправильный Access Key
|
||||
R=$(sqs_call "Action=ListQueues" "WRONG-KEY:${SK}")
|
||||
assert_contains "T26 Wrong access key" "$R" "InvalidClientTokenId\|SignatureDoesNotMatch\|Error\|403\|401\|Unauthorized"
|
||||
|
||||
# T27: Неправильный Secret Key
|
||||
R=$(sqs_call "Action=ListQueues" "${AK}:WRONG-SECRET")
|
||||
assert_contains "T27 Wrong secret key" "$R" "SignatureDoesNotMatch\|Error\|403\|401\|Unauthorized"
|
||||
|
||||
# T28: Пустые credentials
|
||||
R=$(curl -s --max-time 15 "${SQS_BASE}?Action=ListQueues&Version=${VERSION}" 2>&1)
|
||||
assert_contains "T28 No credentials" "$R" "Error\|AuthFailure\|MissingAuthenticationToken\|ListQueuesResult\|403\|401"
|
||||
# Примечание: ElasticMQ может не требовать auth — если так, это проблема безопасности
|
||||
|
||||
# T29: SQL injection в QueueName
|
||||
R=$(sqs_call "Action=CreateQueue&QueueName=test';DROP%20TABLE%20queues;--")
|
||||
assert_contains "T29 SQL injection in QueueName" "$R" "Error\|InvalidParameterValue\|CreateQueueResult"
|
||||
# Если создалась — OK, ElasticMQ in-memory, нет SQL. Убираем.
|
||||
cleanup_queue "test';DROP TABLE queues;--"
|
||||
|
||||
# T30: XSS в MessageBody
|
||||
Q30="testq-xss-${TS}"
|
||||
sqs_call "Action=CreateQueue&QueueName=${Q30}" >/dev/null
|
||||
R=$(sqs_call "Action=SendMessage&QueueUrl=${SQS_BASE}/test001/${Q30}&MessageBody=%3Cscript%3Ealert(1)%3C/script%3E")
|
||||
assert_contains "T30 XSS in MessageBody — accepted" "$R" "MessageId"
|
||||
# SQS должен принять любой body — это не HTML, а очередь. Проверяем что возвращает как есть:
|
||||
R=$(sqs_call "Action=ReceiveMessage&QueueUrl=${SQS_BASE}/test001/${Q30}")
|
||||
assert_contains "T30 XSS body returned" "$R" "script\|<script"
|
||||
cleanup_queue "$Q30"
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ГРУППА 5: СПЕЦСИМВОЛЫ И КОДИРОВКИ
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── ГРУППА 5: Спецсимволы и кодировки ──"
|
||||
|
||||
Q5="testq-enc-${TS}"
|
||||
sqs_call "Action=CreateQueue&QueueName=${Q5}" >/dev/null
|
||||
|
||||
# T31: Unicode в MessageBody
|
||||
R=$(sqs_call "Action=SendMessage&QueueUrl=${SQS_BASE}/test001/${Q5}&MessageBody=%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82")
|
||||
assert_contains "T31 Unicode body" "$R" "MessageId"
|
||||
|
||||
# T32: Receive Unicode
|
||||
R=$(sqs_call "Action=ReceiveMessage&QueueUrl=${SQS_BASE}/test001/${Q5}")
|
||||
assert_contains "T32 Receive Unicode" "$R" "Привет\|%D0%9F"
|
||||
|
||||
# T33: Амперсанд и XML-спецсимволы в body
|
||||
R=$(sqs_call "Action=SendMessage&QueueUrl=${SQS_BASE}/test001/${Q5}&MessageBody=a%26b%3Cc%3Ed")
|
||||
assert_contains "T33 XML special chars" "$R" "MessageId"
|
||||
|
||||
# T34: JSON в body
|
||||
R=$(sqs_call "Action=SendMessage&QueueUrl=${SQS_BASE}/test001/${Q5}&MessageBody=%7B%22key%22%3A%22value%22%7D")
|
||||
assert_contains "T34 JSON body" "$R" "MessageId"
|
||||
|
||||
# T35: Пустой MessageBody
|
||||
R=$(sqs_call "Action=SendMessage&QueueUrl=${SQS_BASE}/test001/${Q5}&MessageBody=")
|
||||
assert_contains "T35 Empty body" "$R" "Error\|MissingParameter\|MessageId"
|
||||
|
||||
cleanup_queue "$Q5"
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ГРУППА 6: НАГРУЗКА И ПОРЯДОК
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── ГРУППА 6: Множественные сообщения ──"
|
||||
|
||||
Q6="testq-multi-${TS}"
|
||||
sqs_call "Action=CreateQueue&QueueName=${Q6}" >/dev/null
|
||||
|
||||
# T36: Отправить 10 сообщений подряд
|
||||
ALL_OK=true
|
||||
for i in $(seq 1 10); do
|
||||
R=$(sqs_call "Action=SendMessage&QueueUrl=${SQS_BASE}/test001/${Q6}&MessageBody=msg-${i}")
|
||||
if ! echo "$R" | grep -q "MessageId"; then
|
||||
ALL_OK=false
|
||||
break
|
||||
fi
|
||||
done
|
||||
TOTAL=$((TOTAL + 1))
|
||||
if $ALL_OK; then
|
||||
PASS=$((PASS + 1))
|
||||
echo " ✅ PASS: T36 Send 10 messages"
|
||||
else
|
||||
FAIL=$((FAIL + 1))
|
||||
echo " ❌ FAIL: T36 Send 10 messages"
|
||||
fi
|
||||
|
||||
# T37: Receive все 10 (может потребовать несколько вызовов)
|
||||
RECEIVED=0
|
||||
for _ in $(seq 1 15); do
|
||||
R=$(sqs_call "Action=ReceiveMessage&QueueUrl=${SQS_BASE}/test001/${Q6}&MaxNumberOfMessages=10")
|
||||
COUNT=$(echo "$R" | grep -c "<Message>" || true)
|
||||
RECEIVED=$((RECEIVED + COUNT))
|
||||
if [ "$RECEIVED" -ge 10 ]; then break; fi
|
||||
done
|
||||
TOTAL=$((TOTAL + 1))
|
||||
if [ "$RECEIVED" -ge 10 ]; then
|
||||
PASS=$((PASS + 1))
|
||||
echo " ✅ PASS: T37 Receive all 10 messages (got ${RECEIVED})"
|
||||
else
|
||||
FAIL=$((FAIL + 1))
|
||||
echo " ❌ FAIL: T37 Receive all 10 messages (got ${RECEIVED}/10)"
|
||||
fi
|
||||
|
||||
cleanup_queue "$Q6"
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ГРУППА 7: HTTP-УРОВЕНЬ
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── ГРУППА 7: HTTP-уровень ──"
|
||||
|
||||
# T38: Проверка HTTPS
|
||||
assert_http_code "T38 HTTPS endpoint" "${SQS_BASE}?Action=ListQueues&Version=${VERSION}" "200"
|
||||
|
||||
# T39: Health endpoint
|
||||
TOTAL=$((TOTAL + 1))
|
||||
HC=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "${SQS_BASE%/sqs/test001}/sqs/test001/health" 2>&1 || echo "000")
|
||||
# Примечание: health может быть на другом пути
|
||||
if [ "$HC" = "200" ] || [ "$HC" = "404" ]; then
|
||||
PASS=$((PASS + 1))
|
||||
echo " ✅ PASS: T39 Health endpoint (HTTP $HC)"
|
||||
else
|
||||
SKIP=$((SKIP + 1))
|
||||
echo " ⚠️ SKIP: T39 Health endpoint (HTTP $HC — может быть не на этом пути)"
|
||||
fi
|
||||
|
||||
# T40: Очень большой запрос (64KB body)
|
||||
Q40="testq-big-${TS}"
|
||||
sqs_call "Action=CreateQueue&QueueName=${Q40}" >/dev/null
|
||||
BIGBODY=$(python3 -c "print('A'*65000)" 2>/dev/null || printf 'A%.0s' $(seq 1 65000))
|
||||
R=$(curl -s --max-time 15 -u "$AUTH" -X POST "${SQS_BASE}?Action=SendMessage&QueueUrl=${SQS_BASE}/test001/${Q40}&Version=${VERSION}" --data-urlencode "MessageBody=${BIGBODY}" 2>&1)
|
||||
assert_contains "T40 Large body 64KB" "$R" "MessageId\|Error\|TooLong\|InvalidParameterValue"
|
||||
cleanup_queue "$Q40"
|
||||
|
||||
# T41: POST вместо GET
|
||||
R=$(curl -s --max-time 15 -u "$AUTH" -X POST "${SQS_BASE}?Action=ListQueues&Version=${VERSION}" 2>&1)
|
||||
assert_contains "T41 POST method" "$R" "ListQueuesResult\|ListQueuesResponse"
|
||||
|
||||
# T42: Несуществующий путь
|
||||
TOTAL=$((TOTAL + 1))
|
||||
HC=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 -u "$AUTH" "${SQS_HOST}/sqs/nonexistent-tenant?Action=ListQueues&Version=${VERSION}" 2>&1)
|
||||
if [ "$HC" = "404" ] || [ "$HC" = "403" ] || [ "$HC" = "502" ] || [ "$HC" = "503" ] || [ "$HC" = "302" ]; then
|
||||
PASS=$((PASS + 1))
|
||||
echo " ✅ PASS: T42 Wrong tenant path (HTTP $HC)"
|
||||
else
|
||||
FAIL=$((FAIL + 1))
|
||||
echo " ❌ FAIL: T42 Wrong tenant path (HTTP $HC — ожидался 302/404/403)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ГРУППА 8: ТАЙМАУТЫ И LONG POLLING
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── ГРУППА 8: Long polling и таймауты ──"
|
||||
|
||||
Q8="testq-poll-${TS}"
|
||||
sqs_call "Action=CreateQueue&QueueName=${Q8}" >/dev/null
|
||||
|
||||
# T43: WaitTimeSeconds=1 на пустой очереди — должен вернуть пустой через ~1 сек
|
||||
TOTAL=$((TOTAL + 1))
|
||||
T_START=$(date +%s)
|
||||
R=$(sqs_call "Action=ReceiveMessage&QueueUrl=${SQS_BASE}/test001/${Q8}&WaitTimeSeconds=1")
|
||||
T_END=$(date +%s)
|
||||
ELAPSED=$((T_END - T_START))
|
||||
if [ "$ELAPSED" -ge 1 ] && [ "$ELAPSED" -le 5 ]; then
|
||||
PASS=$((PASS + 1))
|
||||
echo " ✅ PASS: T43 Long poll 1s empty queue (${ELAPSED}s)"
|
||||
else
|
||||
FAIL=$((FAIL + 1))
|
||||
echo " ❌ FAIL: T43 Long poll 1s (${ELAPSED}s — ожидалось 1-5s)"
|
||||
fi
|
||||
|
||||
# T44: WaitTimeSeconds=0 — мгновенный ответ
|
||||
TOTAL=$((TOTAL + 1))
|
||||
T_START=$(date +%s)
|
||||
R=$(sqs_call "Action=ReceiveMessage&QueueUrl=${SQS_BASE}/test001/${Q8}&WaitTimeSeconds=0")
|
||||
T_END=$(date +%s)
|
||||
ELAPSED=$((T_END - T_START))
|
||||
if [ "$ELAPSED" -le 2 ]; then
|
||||
PASS=$((PASS + 1))
|
||||
echo " ✅ PASS: T44 Short poll instant response (${ELAPSED}s)"
|
||||
else
|
||||
FAIL=$((FAIL + 1))
|
||||
echo " ❌ FAIL: T44 Short poll (${ELAPSED}s — ожидалось <2s)"
|
||||
fi
|
||||
|
||||
cleanup_queue "$Q8"
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ГРУППА 9: VISIBILITY TIMEOUT
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── ГРУППА 9: Visibility timeout ──"
|
||||
|
||||
Q9="testq-vis-${TS}"
|
||||
sqs_call "Action=CreateQueue&QueueName=${Q9}" >/dev/null
|
||||
sqs_call "Action=SendMessage&QueueUrl=${SQS_BASE}/test001/${Q9}&MessageBody=VisTest" >/dev/null
|
||||
|
||||
# T45: Receive с VisibilityTimeout=2 — сообщение скрыто, повторный receive пуст
|
||||
R=$(sqs_call "Action=ReceiveMessage&QueueUrl=${SQS_BASE}/test001/${Q9}&VisibilityTimeout=2")
|
||||
assert_contains "T45a Receive with visibility" "$R" "VisTest"
|
||||
|
||||
R=$(sqs_call "Action=ReceiveMessage&QueueUrl=${SQS_BASE}/test001/${Q9}&WaitTimeSeconds=0")
|
||||
assert_not_contains "T45b Hidden during visibility" "$R" "VisTest"
|
||||
|
||||
# T46: После visibility timeout — сообщение снова доступно
|
||||
sleep 3
|
||||
R=$(sqs_call "Action=ReceiveMessage&QueueUrl=${SQS_BASE}/test001/${Q9}&WaitTimeSeconds=0")
|
||||
assert_contains "T46 Re-visible after timeout" "$R" "VisTest"
|
||||
|
||||
cleanup_queue "$Q9"
|
||||
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# РЕЗУЛЬТАТЫ
|
||||
# ═══════════════════════════════════════════
|
||||
echo "╔══════════════════════════════════════════════════╗"
|
||||
echo "║ РЕЗУЛЬТАТЫ ║"
|
||||
echo "╠══════════════════════════════════════════════════╣"
|
||||
echo "║ Всего: ${TOTAL} ║"
|
||||
echo "║ ✅ PASS: ${PASS} ║"
|
||||
echo "║ ❌ FAIL: ${FAIL} ║"
|
||||
echo "║ ⚠️ SKIP: ${SKIP} ║"
|
||||
echo "╚══════════════════════════════════════════════════╝"
|
||||
|
||||
if [ "$FAIL" -gt 0 ]; then
|
||||
exit 1
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
@@ -0,0 +1,426 @@
|
||||
#!/bin/bash
|
||||
# 2026-04-09 — Нагрузочное тестирование SQS API
|
||||
# Замеряет: throughput, latency, конкурентность, стабильность под нагрузкой
|
||||
# Запуск: bash sqs_load_test.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# КОНФИГУРАЦИЯ
|
||||
# ═══════════════════════════════════════════
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
SQS_HOST="https://sqs.kube5s.ru"
|
||||
SQS_BASE="${SQS_HOST}/sqs/test001"
|
||||
export AWS_ACCESS_KEY_ID="SQSAK-test001-ae4dc8"
|
||||
export AWS_SECRET_ACCESS_KEY="J8mElXq8Nwww8iFRtGJCm8KfxM9XSTluTqS2A67xsEE"
|
||||
export AWS_DEFAULT_REGION="us-east-1"
|
||||
ENDPOINT="--endpoint-url ${SQS_BASE}"
|
||||
AUTH="${AWS_ACCESS_KEY_ID}:${AWS_SECRET_ACCESS_KEY}"
|
||||
VERSION="2012-11-05"
|
||||
TS=$(date +%s)
|
||||
TMPDIR=$(mktemp -d /tmp/sqs-load-XXXXX)
|
||||
|
||||
# Параметры нагрузки
|
||||
MESSAGES_SINGLE=100 # send/receive в одну очередь
|
||||
MESSAGES_BURST=50 # burst за раз
|
||||
PARALLEL_WORKERS=5 # параллельные воркеры
|
||||
QUEUES_COUNT=10 # кол-во очередей для multi-queue теста
|
||||
|
||||
echo "╔══════════════════════════════════════════════════════╗"
|
||||
echo "║ SQS Load Test — $(date '+%Y-%m-%d %H:%M:%S') ║"
|
||||
echo "║ Endpoint: ${SQS_BASE} ║"
|
||||
echo "║ Temp: ${TMPDIR} ║"
|
||||
echo "╚══════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
|
||||
# Утилита замера времени (ms)
|
||||
ms_now() { date +%s%N | cut -b1-13; }
|
||||
|
||||
sqs_call() {
|
||||
curl -s --max-time 30 -u "$AUTH" "${SQS_BASE}?${1}&Version=${VERSION}" 2>&1
|
||||
}
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ТЕСТ 1: Latency отдельных операций
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── ТЕСТ 1: Latency отдельных операций ──"
|
||||
|
||||
Q1="loadq-latency-${TS}"
|
||||
sqs_call "Action=CreateQueue&QueueName=${Q1}" >/dev/null
|
||||
|
||||
# SendMessage latency (20 вызовов)
|
||||
SEND_TIMES=()
|
||||
for i in $(seq 1 20); do
|
||||
T1=$(ms_now)
|
||||
sqs_call "Action=SendMessage&QueueUrl=${SQS_BASE}/test001/${Q1}&MessageBody=latency-test-${i}" >/dev/null
|
||||
T2=$(ms_now)
|
||||
SEND_TIMES+=( $((T2 - T1)) )
|
||||
done
|
||||
|
||||
# Статистика
|
||||
SEND_TOTAL=0
|
||||
SEND_MIN=999999
|
||||
SEND_MAX=0
|
||||
for t in "${SEND_TIMES[@]}"; do
|
||||
SEND_TOTAL=$((SEND_TOTAL + t))
|
||||
[ "$t" -lt "$SEND_MIN" ] && SEND_MIN=$t
|
||||
[ "$t" -gt "$SEND_MAX" ] && SEND_MAX=$t
|
||||
done
|
||||
SEND_AVG=$((SEND_TOTAL / ${#SEND_TIMES[@]}))
|
||||
echo " SendMessage (20x): avg=${SEND_AVG}ms min=${SEND_MIN}ms max=${SEND_MAX}ms"
|
||||
|
||||
# ReceiveMessage latency (20 вызовов)
|
||||
RECV_TIMES=()
|
||||
for i in $(seq 1 20); do
|
||||
T1=$(ms_now)
|
||||
sqs_call "Action=ReceiveMessage&QueueUrl=${SQS_BASE}/test001/${Q1}&MaxNumberOfMessages=1" >/dev/null
|
||||
T2=$(ms_now)
|
||||
RECV_TIMES+=( $((T2 - T1)) )
|
||||
done
|
||||
|
||||
RECV_TOTAL=0
|
||||
RECV_MIN=999999
|
||||
RECV_MAX=0
|
||||
for t in "${RECV_TIMES[@]}"; do
|
||||
RECV_TOTAL=$((RECV_TOTAL + t))
|
||||
[ "$t" -lt "$RECV_MIN" ] && RECV_MIN=$t
|
||||
[ "$t" -gt "$RECV_MAX" ] && RECV_MAX=$t
|
||||
done
|
||||
RECV_AVG=$((RECV_TOTAL / ${#RECV_TIMES[@]}))
|
||||
echo " ReceiveMessage (20x): avg=${RECV_AVG}ms min=${RECV_MIN}ms max=${RECV_MAX}ms"
|
||||
|
||||
# ListQueues latency
|
||||
LIST_TIMES=()
|
||||
for i in $(seq 1 10); do
|
||||
T1=$(ms_now)
|
||||
sqs_call "Action=ListQueues" >/dev/null
|
||||
T2=$(ms_now)
|
||||
LIST_TIMES+=( $((T2 - T1)) )
|
||||
done
|
||||
LIST_TOTAL=0
|
||||
for t in "${LIST_TIMES[@]}"; do LIST_TOTAL=$((LIST_TOTAL + t)); done
|
||||
LIST_AVG=$((LIST_TOTAL / ${#LIST_TIMES[@]}))
|
||||
echo " ListQueues (10x): avg=${LIST_AVG}ms"
|
||||
|
||||
sqs_call "Action=PurgeQueue&QueueUrl=${SQS_BASE}/test001/${Q1}" >/dev/null
|
||||
sqs_call "Action=DeleteQueue&QueueUrl=${SQS_BASE}/test001/${Q1}" >/dev/null
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ТЕСТ 2: Последовательный throughput — 100 сообщений
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── ТЕСТ 2: Последовательный throughput (${MESSAGES_SINGLE} сообщений) ──"
|
||||
|
||||
Q2="loadq-seq-${TS}"
|
||||
sqs_call "Action=CreateQueue&QueueName=${Q2}" >/dev/null
|
||||
|
||||
# Отправка
|
||||
T_START=$(ms_now)
|
||||
SEND_OK=0
|
||||
SEND_ERR=0
|
||||
for i in $(seq 1 $MESSAGES_SINGLE); do
|
||||
R=$(sqs_call "Action=SendMessage&QueueUrl=${SQS_BASE}/test001/${Q2}&MessageBody=seq-msg-${i}")
|
||||
if echo "$R" | grep -q "MessageId"; then
|
||||
SEND_OK=$((SEND_OK + 1))
|
||||
else
|
||||
SEND_ERR=$((SEND_ERR + 1))
|
||||
fi
|
||||
done
|
||||
T_END=$(ms_now)
|
||||
SEND_ELAPSED=$((T_END - T_START))
|
||||
SEND_RPS=$(( MESSAGES_SINGLE * 1000 / (SEND_ELAPSED + 1) ))
|
||||
echo " Send: ${SEND_OK} OK / ${SEND_ERR} ERR за ${SEND_ELAPSED}ms (${SEND_RPS} msg/s)"
|
||||
|
||||
# Получение
|
||||
T_START=$(ms_now)
|
||||
RECV_OK=0
|
||||
EMPTY=0
|
||||
for attempt in $(seq 1 $((MESSAGES_SINGLE + 50))); do
|
||||
R=$(sqs_call "Action=ReceiveMessage&QueueUrl=${SQS_BASE}/test001/${Q2}&MaxNumberOfMessages=10")
|
||||
COUNT=$(echo "$R" | grep -c "<Message>" || true)
|
||||
if [ "$COUNT" -gt 0 ]; then
|
||||
RECV_OK=$((RECV_OK + COUNT))
|
||||
else
|
||||
EMPTY=$((EMPTY + 1))
|
||||
[ "$EMPTY" -ge 5 ] && break
|
||||
fi
|
||||
[ "$RECV_OK" -ge "$MESSAGES_SINGLE" ] && break
|
||||
done
|
||||
T_END=$(ms_now)
|
||||
RECV_ELAPSED=$((T_END - T_START))
|
||||
RECV_RPS=$(( RECV_OK * 1000 / (RECV_ELAPSED + 1) ))
|
||||
echo " Recv: ${RECV_OK}/${MESSAGES_SINGLE} за ${RECV_ELAPSED}ms (${RECV_RPS} msg/s)"
|
||||
|
||||
sqs_call "Action=DeleteQueue&QueueUrl=${SQS_BASE}/test001/${Q2}" >/dev/null
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ТЕСТ 3: Параллельная отправка (N воркеров)
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── ТЕСТ 3: Параллельная отправка (${PARALLEL_WORKERS} воркеров × ${MESSAGES_BURST} msg) ──"
|
||||
|
||||
Q3="loadq-par-${TS}"
|
||||
sqs_call "Action=CreateQueue&QueueName=${Q3}" >/dev/null
|
||||
|
||||
# Функция воркера — записывает результат в файл
|
||||
worker_send() {
|
||||
local wid=$1
|
||||
local ok=0
|
||||
local err=0
|
||||
for i in $(seq 1 $MESSAGES_BURST); do
|
||||
R=$(curl -s --max-time 30 -u "$AUTH" "${SQS_BASE}?Action=SendMessage&QueueUrl=${SQS_BASE}/test001/${Q3}&MessageBody=w${wid}-msg${i}&Version=${VERSION}" 2>&1)
|
||||
if echo "$R" | grep -q "MessageId"; then
|
||||
ok=$((ok + 1))
|
||||
else
|
||||
err=$((err + 1))
|
||||
fi
|
||||
done
|
||||
echo "${ok}:${err}" > "${TMPDIR}/worker-${wid}.result"
|
||||
}
|
||||
|
||||
T_START=$(ms_now)
|
||||
for w in $(seq 1 $PARALLEL_WORKERS); do
|
||||
worker_send $w &
|
||||
done
|
||||
wait
|
||||
T_END=$(ms_now)
|
||||
PAR_ELAPSED=$((T_END - T_START))
|
||||
|
||||
# Собираем результаты
|
||||
PAR_OK=0
|
||||
PAR_ERR=0
|
||||
for w in $(seq 1 $PARALLEL_WORKERS); do
|
||||
if [ -f "${TMPDIR}/worker-${w}.result" ]; then
|
||||
RES=$(cat "${TMPDIR}/worker-${w}.result")
|
||||
PAR_OK=$((PAR_OK + ${RES%%:*}))
|
||||
PAR_ERR=$((PAR_ERR + ${RES##*:}))
|
||||
fi
|
||||
done
|
||||
PAR_TOTAL=$((PARALLEL_WORKERS * MESSAGES_BURST))
|
||||
PAR_RPS=$(( PAR_OK * 1000 / (PAR_ELAPSED + 1) ))
|
||||
echo " Send: ${PAR_OK}/${PAR_TOTAL} OK, ${PAR_ERR} ERR за ${PAR_ELAPSED}ms (${PAR_RPS} msg/s)"
|
||||
|
||||
# Параллельное получение
|
||||
worker_recv() {
|
||||
local wid=$1
|
||||
local ok=0
|
||||
local empty=0
|
||||
for _ in $(seq 1 $((MESSAGES_BURST + 20))); do
|
||||
R=$(curl -s --max-time 30 -u "$AUTH" "${SQS_BASE}?Action=ReceiveMessage&QueueUrl=${SQS_BASE}/test001/${Q3}&MaxNumberOfMessages=10&Version=${VERSION}" 2>&1)
|
||||
COUNT=$(echo "$R" | grep -c "<Message>" || true)
|
||||
if [ "$COUNT" -gt 0 ]; then
|
||||
ok=$((ok + COUNT))
|
||||
else
|
||||
empty=$((empty + 1))
|
||||
[ "$empty" -ge 3 ] && break
|
||||
fi
|
||||
done
|
||||
echo "${ok}" > "${TMPDIR}/recv-${wid}.result"
|
||||
}
|
||||
|
||||
T_START=$(ms_now)
|
||||
for w in $(seq 1 $PARALLEL_WORKERS); do
|
||||
worker_recv $w &
|
||||
done
|
||||
wait
|
||||
T_END=$(ms_now)
|
||||
RECV_PAR_ELAPSED=$((T_END - T_START))
|
||||
|
||||
RECV_PAR_OK=0
|
||||
for w in $(seq 1 $PARALLEL_WORKERS); do
|
||||
if [ -f "${TMPDIR}/recv-${w}.result" ]; then
|
||||
RECV_PAR_OK=$((RECV_PAR_OK + $(cat "${TMPDIR}/recv-${w}.result")))
|
||||
fi
|
||||
done
|
||||
RECV_PAR_RPS=$(( RECV_PAR_OK * 1000 / (RECV_PAR_ELAPSED + 1) ))
|
||||
echo " Recv: ${RECV_PAR_OK}/${PAR_TOTAL} за ${RECV_PAR_ELAPSED}ms (${RECV_PAR_RPS} msg/s)"
|
||||
|
||||
sqs_call "Action=DeleteQueue&QueueUrl=${SQS_BASE}/test001/${Q3}" >/dev/null
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ТЕСТ 4: Много очередей одновременно
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── ТЕСТ 4: Создание ${QUEUES_COUNT} очередей + send/receive в каждую ──"
|
||||
|
||||
T_START=$(ms_now)
|
||||
CREATE_OK=0
|
||||
for i in $(seq 1 $QUEUES_COUNT); do
|
||||
R=$(sqs_call "Action=CreateQueue&QueueName=loadq-multi-${TS}-${i}")
|
||||
if echo "$R" | grep -q "CreateQueueResult"; then
|
||||
CREATE_OK=$((CREATE_OK + 1))
|
||||
fi
|
||||
done
|
||||
T_CRE=$(ms_now)
|
||||
echo " Create: ${CREATE_OK}/${QUEUES_COUNT} за $((T_CRE - T_START))ms"
|
||||
|
||||
# Send 5 msg в каждую параллельно
|
||||
multi_send() {
|
||||
local qi=$1
|
||||
local ok=0
|
||||
for m in $(seq 1 5); do
|
||||
R=$(curl -s --max-time 30 -u "$AUTH" "${SQS_BASE}?Action=SendMessage&QueueUrl=${SQS_BASE}/test001/loadq-multi-${TS}-${qi}&MessageBody=multi-${qi}-${m}&Version=${VERSION}" 2>&1)
|
||||
echo "$R" | grep -q "MessageId" && ok=$((ok + 1))
|
||||
done
|
||||
echo "$ok" > "${TMPDIR}/multi-send-${qi}.result"
|
||||
}
|
||||
|
||||
T_START=$(ms_now)
|
||||
for i in $(seq 1 $QUEUES_COUNT); do
|
||||
multi_send $i &
|
||||
done
|
||||
wait
|
||||
T_SEND=$(ms_now)
|
||||
|
||||
MULTI_SEND_OK=0
|
||||
for i in $(seq 1 $QUEUES_COUNT); do
|
||||
[ -f "${TMPDIR}/multi-send-${i}.result" ] && MULTI_SEND_OK=$((MULTI_SEND_OK + $(cat "${TMPDIR}/multi-send-${i}.result")))
|
||||
done
|
||||
echo " Send (5 × ${QUEUES_COUNT}): ${MULTI_SEND_OK}/$((QUEUES_COUNT * 5)) за $((T_SEND - T_START))ms"
|
||||
|
||||
# Receive из каждой
|
||||
multi_recv() {
|
||||
local qi=$1
|
||||
local ok=0
|
||||
for _ in $(seq 1 10); do
|
||||
R=$(curl -s --max-time 30 -u "$AUTH" "${SQS_BASE}?Action=ReceiveMessage&QueueUrl=${SQS_BASE}/test001/loadq-multi-${TS}-${qi}&MaxNumberOfMessages=10&Version=${VERSION}" 2>&1)
|
||||
COUNT=$(echo "$R" | grep -c "<Message>" || true)
|
||||
ok=$((ok + COUNT))
|
||||
[ "$COUNT" -eq 0 ] && break
|
||||
done
|
||||
echo "$ok" > "${TMPDIR}/multi-recv-${qi}.result"
|
||||
}
|
||||
|
||||
T_START=$(ms_now)
|
||||
for i in $(seq 1 $QUEUES_COUNT); do
|
||||
multi_recv $i &
|
||||
done
|
||||
wait
|
||||
T_RECV=$(ms_now)
|
||||
|
||||
MULTI_RECV_OK=0
|
||||
for i in $(seq 1 $QUEUES_COUNT); do
|
||||
[ -f "${TMPDIR}/multi-recv-${i}.result" ] && MULTI_RECV_OK=$((MULTI_RECV_OK + $(cat "${TMPDIR}/multi-recv-${i}.result")))
|
||||
done
|
||||
echo " Recv: ${MULTI_RECV_OK}/$((QUEUES_COUNT * 5)) за $((T_RECV - T_START))ms"
|
||||
|
||||
# Удаление
|
||||
T_START=$(ms_now)
|
||||
for i in $(seq 1 $QUEUES_COUNT); do
|
||||
sqs_call "Action=DeleteQueue&QueueUrl=${SQS_BASE}/test001/loadq-multi-${TS}-${i}" >/dev/null &
|
||||
done
|
||||
wait
|
||||
T_DEL=$(ms_now)
|
||||
echo " Delete ${QUEUES_COUNT} queues: $((T_DEL - T_START))ms"
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ТЕСТ 5: Стресс — быстрая send+receive петля
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── ТЕСТ 5: Stress loop — send+receive 60 сек ──"
|
||||
|
||||
Q5="loadq-stress-${TS}"
|
||||
sqs_call "Action=CreateQueue&QueueName=${Q5}" >/dev/null
|
||||
|
||||
STRESS_DURATION=60
|
||||
STRESS_SEND=0
|
||||
STRESS_RECV=0
|
||||
STRESS_ERR=0
|
||||
T_START=$(date +%s)
|
||||
|
||||
while true; do
|
||||
NOW=$(date +%s)
|
||||
ELAPSED=$((NOW - T_START))
|
||||
[ "$ELAPSED" -ge "$STRESS_DURATION" ] && break
|
||||
|
||||
# Send
|
||||
R=$(sqs_call "Action=SendMessage&QueueUrl=${SQS_BASE}/test001/${Q5}&MessageBody=stress-${STRESS_SEND}")
|
||||
if echo "$R" | grep -q "MessageId"; then
|
||||
STRESS_SEND=$((STRESS_SEND + 1))
|
||||
else
|
||||
STRESS_ERR=$((STRESS_ERR + 1))
|
||||
fi
|
||||
|
||||
# Receive
|
||||
R=$(sqs_call "Action=ReceiveMessage&QueueUrl=${SQS_BASE}/test001/${Q5}&MaxNumberOfMessages=10")
|
||||
COUNT=$(echo "$R" | grep -c "<Message>" || true)
|
||||
STRESS_RECV=$((STRESS_RECV + COUNT))
|
||||
done
|
||||
|
||||
T_END=$(date +%s)
|
||||
STRESS_REAL=$((T_END - T_START))
|
||||
STRESS_SEND_RPS=$((STRESS_SEND / (STRESS_REAL + 1)))
|
||||
STRESS_RECV_RPS=$((STRESS_RECV / (STRESS_REAL + 1)))
|
||||
echo " Duration: ${STRESS_REAL}s"
|
||||
echo " Sent: ${STRESS_SEND} (${STRESS_SEND_RPS} msg/s)"
|
||||
echo " Received: ${STRESS_RECV} (${STRESS_RECV_RPS} msg/s)"
|
||||
echo " Errors: ${STRESS_ERR}"
|
||||
|
||||
sqs_call "Action=DeleteQueue&QueueUrl=${SQS_BASE}/test001/${Q5}" >/dev/null
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ТЕСТ 6: Большие сообщения
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── ТЕСТ 6: Размер сообщений ──"
|
||||
|
||||
Q6="loadq-size-${TS}"
|
||||
sqs_call "Action=CreateQueue&QueueName=${Q6}" >/dev/null
|
||||
|
||||
for SIZE in 1 1024 10240 65536 131072 262144; do
|
||||
BODY=$(python3 -c "print('X' * ${SIZE})" 2>/dev/null)
|
||||
T1=$(ms_now)
|
||||
R=$(curl -s --max-time 30 -u "$AUTH" -X POST "${SQS_BASE}?Action=SendMessage&QueueUrl=${SQS_BASE}/test001/${Q6}&Version=${VERSION}" --data-urlencode "MessageBody=${BODY}" 2>&1)
|
||||
T2=$(ms_now)
|
||||
LAT=$((T2 - T1))
|
||||
if echo "$R" | grep -q "MessageId"; then
|
||||
echo " ${SIZE} bytes: ✅ OK (${LAT}ms)"
|
||||
else
|
||||
ERR_TYPE=$(echo "$R" | grep -oP '(?<=<Code>)[^<]+' | head -1)
|
||||
echo " ${SIZE} bytes: ❌ REJECTED — ${ERR_TYPE:-unknown} (${LAT}ms)"
|
||||
fi
|
||||
done
|
||||
|
||||
sqs_call "Action=PurgeQueue&QueueUrl=${SQS_BASE}/test001/${Q6}" >/dev/null
|
||||
sqs_call "Action=DeleteQueue&QueueUrl=${SQS_BASE}/test001/${Q6}" >/dev/null
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ТЕСТ 7: Целостность данных
|
||||
# ═══════════════════════════════════════════
|
||||
echo "── ТЕСТ 7: Целостность данных (MD5 проверка) ──"
|
||||
|
||||
Q7="loadq-md5-${TS}"
|
||||
sqs_call "Action=CreateQueue&QueueName=${Q7}" >/dev/null
|
||||
|
||||
MD5_OK=0
|
||||
MD5_FAIL=0
|
||||
for i in $(seq 1 20); do
|
||||
MSG="integrity-check-${i}-$(head -c 16 /dev/urandom | base64 | tr -d '/+=')"
|
||||
EXPECTED_MD5=$(echo -n "$MSG" | md5sum | awk '{print $1}')
|
||||
|
||||
R=$(sqs_call "Action=SendMessage&QueueUrl=${SQS_BASE}/test001/${Q7}&MessageBody=${MSG}")
|
||||
RETURNED_MD5=$(echo "$R" | grep -oP '(?<=<MD5OfMessageBody>)[^<]+' | head -1)
|
||||
|
||||
if [ "$EXPECTED_MD5" = "$RETURNED_MD5" ]; then
|
||||
MD5_OK=$((MD5_OK + 1))
|
||||
else
|
||||
MD5_FAIL=$((MD5_FAIL + 1))
|
||||
echo " MISMATCH msg=${MSG}: expected=${EXPECTED_MD5} got=${RETURNED_MD5}"
|
||||
fi
|
||||
done
|
||||
echo " MD5 check: ${MD5_OK}/20 OK, ${MD5_FAIL} FAIL"
|
||||
|
||||
sqs_call "Action=DeleteQueue&QueueUrl=${SQS_BASE}/test001/${Q7}" >/dev/null
|
||||
echo ""
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ИТОГО
|
||||
# ═══════════════════════════════════════════
|
||||
echo "╔══════════════════════════════════════════════════════╗"
|
||||
echo "║ НАГРУЗОЧНЫЙ ТЕСТ ЗАВЕРШЁН ║"
|
||||
echo "║ $(date '+%Y-%m-%d %H:%M:%S') ║"
|
||||
echo "╚══════════════════════════════════════════════════════╝"
|
||||
|
||||
# Очистка temp
|
||||
rm -rf "$TMPDIR"
|
||||
Reference in New Issue
Block a user