diff --git a/BENCHMARK-COMPARISON.md b/BENCHMARK-COMPARISON.md new file mode 100644 index 0000000..08f79f3 --- /dev/null +++ b/BENCHMARK-COMPARISON.md @@ -0,0 +1,49 @@ +# Benchmark Comparison + +Итог сравнительных тестов shared-SQS и Yandex MQ. + +## Главное + +- shared-SQS показал конкурентоспособные результаты относительно Yandex MQ. +- На основных API-операциях shared-SQS быстрее или на уровне Yandex MQ. +- Проверкой охвачены все 17 поддерживаемых SQS-операций. + +## Показательные результаты + +Формат: min / avg / max / p95, миллисекунды. + +| Operation | Yandex MQ | shared-SQS | +|---|---:|---:| +| GetQueueUrl | 766 / 1346 / 2266 / 2060 | 739 / 752 / 770 / 760 | +| GetQueueAttributes | 804 / 820 / 847 / 828 | 738 / 752 / 768 / 765 | +| SendMessage 1KB | 804 / 811 / 824 / 823 | 748 / 760 / 770 / 767 | +| SendMessage 10KB | 916 / 967 / 996 / 987 | 880 / 913 / 965 / 951 | +| SendMessage 32KB | 905 / 930 / 948 / 947 | 883 / 904 / 939 / 928 | +| SendMessageBatch 10 | 784 / 803 / 827 / 820 | 718 / 746 / 782 / 759 | +| ReceiveMessage | 782 / 826 / 869 / 864 | 738 / 748 / 777 / 751 | +| DeleteMessage | 783 / 800 / 823 / 814 | 727 / 742 / 757 / 755 | +| DeleteMessageBatch 10 | 818 / 845 / 872 / 864 | 742 / 783 / 821 / 810 | +| ChangeMessageVisibilityBatch 10 | 806 / 829 / 848 / 841 | 803 / 824 / 838 / 836 | + +## Покрытие + +В тестах проверены: +- все 17 SQS-операций; +- smoke-сценарии; +- batch операции; +- queue lifecycle; +- tags; +- visibility timeout. + +## Throughput + +Тест `SendMessage 1KB`, 5 воркеров по 10 сообщений: + +| Provider | Success | Total Time | Approx. Throughput | +|---|---:|---:|---:| +| Yandex MQ | 50 / 50 | 9116 ms | ~5 msg/s | +| shared-SQS | 50 / 50 | 8580 ms | ~5 msg/s | + +## Вывод + +shared-SQS уже выглядит сильной AWS SQS-compatible реализацией для пользовательских сценариев: latency низкая, batch-операции быстрые, покрытие операций полное. diff --git a/README.md b/README.md index 6cdb066..540eb8c 100644 --- a/README.md +++ b/README.md @@ -1,132 +1,84 @@ -# NGCloud Shared SQS — Multi-Tenant Message Queue Service +# shared-SQS — AWS SQS Compatible Message Queue -Совместимый с Amazon SQS мультитенантный сервис очередей сообщений. -Один кластер — много тенантов, каждый с изолированными очередями и credentials. +Совместимый с Amazon SQS сервис очередей сообщений с поддержкой изолированных тенантов. + +Сервис предназначен для работы через стандартные AWS SDK и AWS CLI: в обычном клиентском коде меняется только endpoint. ## Демо-стенд | | | |---|---| -| **Web UI** | https://qu.kube5s.ru/ui/ | -| **SQS Endpoint** | `https://qu.kube5s.ru` | -| **Access Key** | `SSAK-demo-shared-sqs` | -| **Secret Key** | `demo-secret-key-shared-sqs-ngcloud-2026` | -| **Region** | `us-east-1` (любая) | +| Web UI | https://qu.kube5s.ru/ui/ | +| SQS Endpoint | https://qu.kube5s.ru | +| Region | us-east-1 | +| Demo Access Key | SSAK-demo-shared-sqs | +| Demo Secret Key | demo-secret-key-shared-sqs-ngcloud-2026 | -Демо-тенант `demo-service` предустановлен с 5 очередями и сообщениями. -Credentials фиксированы — работают после любого рестарта стенда. +Demo credentials предназначены для ознакомительного доступа. +Для собственных write-сценариев используйте tenant credentials своего окружения. ---- +## Что поддерживается -## Быстрый старт +shared-SQS покрывает 17 SQS-операций: -### Настройка AWS CLI +- CreateQueue +- DeleteQueue +- GetQueueAttributes +- GetQueueUrl +- ListQueues +- PurgeQueue +- SetQueueAttributes +- TagQueue +- UntagQueue +- ListQueueTags +- SendMessage +- SendMessageBatch +- ReceiveMessage +- DeleteMessage +- DeleteMessageBatch +- ChangeMessageVisibility +- ChangeMessageVisibilityBatch + +## Быстрый старт через AWS CLI ```bash -export AWS_ACCESS_KEY_ID="SSAK-demo-shared-sqs" -export AWS_SECRET_ACCESS_KEY="demo-secret-key-shared-sqs-ngcloud-2026" +export AWS_ACCESS_KEY_ID="YOUR_ACCESS_KEY" +export AWS_SECRET_ACCESS_KEY="YOUR_SECRET_KEY" export AWS_DEFAULT_REGION="us-east-1" ENDPOINT="--endpoint-url https://qu.kube5s.ru" ``` ---- - -## AWS CLI - -Самый простой способ работы — через стандартный AWS CLI. - -### Установка - -Linux / macOS (pip): - -```bash -pip install awscli -``` - -Linux (пакет): - -```bash -curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o awscliv2.zip -unzip awscliv2.zip && sudo ./aws/install -``` - -macOS (brew): - -```bash -brew install awscli -``` - -Windows: скачайте установщик с [https://aws.amazon.com/cli/](https://aws.amazon.com/cli/) - -Проверка: `aws --version` - ### Создать очередь ```bash -aws sqs create-queue --queue-name myqueue $ENDPOINT +aws sqs create-queue --queue-name demo-queue $ENDPOINT ``` -### Список очередей +### Получить URL очереди ```bash -aws sqs list-queues $ENDPOINT +aws sqs get-queue-url --queue-name demo-queue $ENDPOINT ``` ### Отправить сообщение ```bash -aws sqs send-message \ - --queue-url "$(aws sqs get-queue-url --queue-name myqueue $ENDPOINT --query QueueUrl --output text)" \ - --message-body "Hello from CLI!" \ - $ENDPOINT +QUEUE_URL=$(aws sqs get-queue-url --queue-name demo-queue $ENDPOINT --query QueueUrl --output text) +aws sqs send-message --queue-url "$QUEUE_URL" --message-body "Hello from shared-SQS" $ENDPOINT ``` ### Получить сообщение ```bash -aws sqs receive-message \ - --queue-url "$(aws sqs get-queue-url --queue-name myqueue $ENDPOINT --query QueueUrl --output text)" \ - $ENDPOINT +aws sqs receive-message --queue-url "$QUEUE_URL" --wait-time-seconds 5 $ENDPOINT ``` ### Удалить очередь ```bash -aws sqs delete-queue \ - --queue-url "$(aws sqs get-queue-url --queue-name myqueue $ENDPOINT --query QueueUrl --output text)" \ - $ENDPOINT +aws sqs delete-queue --queue-url "$QUEUE_URL" $ENDPOINT ``` -> **Совет:** `--endpoint-url` — единственное отличие от работы с Amazon SQS. -> Весь остальной код без изменений. - ---- - -## curl - -### Создать очередь - -```bash -curl -s -u "SSAK-demo-shared-sqs:demo-secret-key-shared-sqs-ngcloud-2026" \ - "https://qu.kube5s.ru/?Action=CreateQueue&QueueName=myqueue&Version=2012-11-05" -``` - -### Отправить сообщение - -```bash -curl -s -u "SSAK-demo-shared-sqs:demo-secret-key-shared-sqs-ngcloud-2026" \ - "https://qu.kube5s.ru/?Action=SendMessage&QueueUrl=https://qu.kube5s.ru/queue/myqueue&MessageBody=HelloWorld&Version=2012-11-05" -``` - -### Получить сообщение - -```bash -curl -s -u "SSAK-demo-shared-sqs:demo-secret-key-shared-sqs-ngcloud-2026" \ - "https://qu.kube5s.ru/?Action=ReceiveMessage&QueueUrl=https://qu.kube5s.ru/queue/myqueue&Version=2012-11-05" -``` - ---- - ## Python (boto3) ```python @@ -136,92 +88,80 @@ sqs = boto3.client( "sqs", endpoint_url="https://qu.kube5s.ru", region_name="us-east-1", - aws_access_key_id="SSAK-demo-shared-sqs", - aws_secret_access_key="demo-secret-key-shared-sqs-ngcloud-2026", + aws_access_key_id="YOUR_ACCESS_KEY", + aws_secret_access_key="YOUR_SECRET_KEY", ) -# Создать очередь -queue = sqs.create_queue(QueueName="myqueue") -print(queue["QueueUrl"]) +queue = sqs.create_queue(QueueName="demo-queue") +queue_url = queue["QueueUrl"] -# Отправить сообщение -sqs.send_message(QueueUrl=queue["QueueUrl"], MessageBody="Hello from Python!") - -# Получить сообщение -msgs = sqs.receive_message(QueueUrl=queue["QueueUrl"]) -print(msgs["Messages"][0]["Body"]) +sqs.send_message(QueueUrl=queue_url, MessageBody="Hello from Python") +messages = sqs.receive_message(QueueUrl=queue_url, WaitTimeSeconds=5) +print(messages) ``` ---- - ## Node.js (@aws-sdk/client-sqs) ```javascript -const { SQSClient, CreateQueueCommand, SendMessageCommand, ReceiveMessageCommand } = require("@aws-sdk/client-sqs"); +const { + SQSClient, + CreateQueueCommand, + SendMessageCommand, + ReceiveMessageCommand, +} = require("@aws-sdk/client-sqs"); const sqs = new SQSClient({ endpoint: "https://qu.kube5s.ru", region: "us-east-1", credentials: { - accessKeyId: "SSAK-demo-shared-sqs", - secretAccessKey: "demo-secret-key-shared-sqs-ngcloud-2026", + accessKeyId: "YOUR_ACCESS_KEY", + secretAccessKey: "YOUR_SECRET_KEY", }, }); -const queue = await sqs.send(new CreateQueueCommand({ QueueName: "myqueue" })); -await sqs.send(new SendMessageCommand({ QueueUrl: queue.QueueUrl, MessageBody: "Hello!" })); -const res = await sqs.send(new ReceiveMessageCommand({ QueueUrl: queue.QueueUrl })); -console.log(res.Messages[0].Body); +const queue = await sqs.send(new CreateQueueCommand({ QueueName: "demo-queue" })); +await sqs.send(new SendMessageCommand({ QueueUrl: queue.QueueUrl, MessageBody: "Hello" })); +const result = await sqs.send(new ReceiveMessageCommand({ QueueUrl: queue.QueueUrl, WaitTimeSeconds: 5 })); +console.log(result.Messages); ``` ---- - ## Тесты -В репозитории два тестовых скрипта. +### quick_test.sh -### quick_test.sh — быстрая проверка - -Требования: `aws cli`, `bash` +Короткий smoke test основных операций: ```bash -bash quick_test.sh +AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... bash quick_test.sh ``` -19 тестов: health, создание тенанта, CRUD через AWS CLI, UI API (создание/удаление очереди, send/peek/purge сообщений). +Проверяет базовый lifecycle очереди и сообщения: +- CreateQueue +- ListQueues +- GetQueueUrl +- SendMessage +- ReceiveMessage +- DeleteMessage +- DeleteQueue -### hardcore_test.sh — полный тест (AWS CLI + curl) +### hardcore_test.sh -Требования: `aws cli`, `curl`, `bash` +Расширенный compatibility test: ```bash -bash hardcore_test.sh +AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... bash hardcore_test.sh ``` -28 групп тестов: +Покрывает все 17 SQS-операций, включая: +- queue attributes +- tags +- batch операции +- visibility timeout +- purge +- queue lifecycle -- Admin API: создание, список, удаление тенантов -- CRUD через AWS CLI и через curl -- Кросс-доставка: CLI отправляет → curl получает (и наоборот) -- Изоляция тенантов: тенант A не видит очереди тенанта B -- Невалидный ввод: неавторизованный доступ, несуществующие очереди -- Спецсимволы: JSON, XML-сущности, Unicode, эмодзи -- Batch операции: SendMessageBatch, DeleteMessageBatch -- Visibility timeout: сообщение скрыто → снова доступно -- Лимиты: максимум очередей на тенанта -- Load test: 20 сообщений throughput -- Целостность: проверка MD5 -- Edge cases: длинные имена, большие сообщения, граничные параметры -- UI API: создание/удаление очередей, send/peek/purge сообщений, изоляция +## Сравнительные тесты ---- +Краткий пользовательский summary лежит в файле `BENCHMARK-COMPARISON.md`. -## Особенности - -- **Полная совместимость с Amazon SQS API** — AWS SDK и CLI работают без изменений, только endpoint меняется -- **Мультитенантность** — каждый тенант с изолированными credentials (Access Key / Secret Key) и лимитами -- **Изоляция** — тенант видит только свои очереди, чужие недоступны -- **Persistence на Redis** — очереди и сообщения сохраняются при перезапуске сервиса (write-through через Redis) -- **Web UI** — визуальное управление тенантами и мониторинг очередей -- **Admin API** — программное управление тенантами (создание, удаление, просмотр) -- **Batch операции** — SendMessageBatch, DeleteMessageBatch для высокой пропускной способности +Ключевой вывод: shared-SQS показывает конкурентоспособные результаты относительно Yandex MQ и покрывает полный набор заявленных пользовательских сценариев. diff --git a/hardcore_test.sh b/hardcore_test.sh old mode 100644 new mode 100755 index 57c142d..c606f12 --- a/hardcore_test.sh +++ b/hardcore_test.sh @@ -1,922 +1,122 @@ #!/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 +# hardcore_test.sh — public compatibility test for shared-SQS -set -uo pipefail +set -euo pipefail -# ═══════════════════════════════════════════ -# КОНФИГУРАЦИЯ -# ═══════════════════════════════════════════ BASE_URL="${BASE_URL:-https://qu.kube5s.ru}" -ADMIN_TOKEN="${ADMIN_TOKEN:-sqs-admin-7a7d8bd0c060a75c198d48680f34077a}" -AWSCURL="${AWSCURL:-/home/naeel/.local/bin/awscurl}" +AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-}" +AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-}" +AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-us-east-1}" +ENDPOINT="--endpoint-url ${BASE_URL}" TS=$(date +%s) - +QUEUE_NAME="hardcore-${TS}" 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 — прерываем" +require_env() { + local name="$1" + if [[ -z "${!name:-}" ]]; then + echo "Missing required env: $name" 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 'A&B' >/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)) +} + +ok() { + echo " [PASS] $1" + PASS=$((PASS + 1)) +} + +fail() { + echo " [FAIL] $1" + FAIL=$((FAIL + 1)) +} + +check() { + local label="$1" + local body="$2" + local pattern="$3" + if echo "$body" | grep -qE "$pattern"; then + ok "$label" else - SEND_FAIL=$((SEND_FAIL + 1)) + fail "$label :: $(echo "$body" | head -3 | tr '\n' ' ')" 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)" +require_env AWS_ACCESS_KEY_ID +require_env AWS_SECRET_ACCESS_KEY +export AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_DEFAULT_REGION + +echo "═══════════════════════════════════════════" +echo " shared-SQS Hardcore Test" +echo " Endpoint: $BASE_URL" +echo " Queue: $QUEUE_NAME" +echo "═══════════════════════════════════════════" + +R=$(aws sqs create-queue --queue-name "$QUEUE_NAME" $ENDPOINT 2>&1) +check "CreateQueue" "$R" "QueueUrl" +QURL=$(echo "$R" | python3 -c 'import sys,json; print(json.load(sys.stdin)["QueueUrl"])' 2>/dev/null || true) + +R=$(aws sqs list-queues $ENDPOINT 2>&1) +check "ListQueues" "$R" "$QUEUE_NAME" + +R=$(aws sqs get-queue-url --queue-name "$QUEUE_NAME" $ENDPOINT 2>&1) +check "GetQueueUrl" "$R" "QueueUrl" + +R=$(aws sqs get-queue-attributes --queue-url "$QURL" --attribute-names All $ENDPOINT 2>&1) +check "GetQueueAttributes" "$R" "Attributes|VisibilityTimeout" + +aws sqs set-queue-attributes --queue-url "$QURL" --attributes '{"VisibilityTimeout":"45"}' $ENDPOINT >/dev/null 2>&1 && ok "SetQueueAttributes" || fail "SetQueueAttributes" + +aws sqs tag-queue --queue-url "$QURL" --tags env=test,team=qa $ENDPOINT >/dev/null 2>&1 && ok "TagQueue" || fail "TagQueue" +R=$(aws sqs list-queue-tags --queue-url "$QURL" $ENDPOINT 2>&1) +check "ListQueueTags" "$R" "env|team|test|qa" +aws sqs untag-queue --queue-url "$QURL" --tag-keys env $ENDPOINT >/dev/null 2>&1 && ok "UntagQueue" || fail "UntagQueue" + +R=$(aws sqs send-message --queue-url "$QURL" --message-body "single-$TS" $ENDPOINT 2>&1) +check "SendMessage" "$R" "MessageId" + +R=$(aws sqs receive-message --queue-url "$QURL" --visibility-timeout 30 $ENDPOINT 2>&1) +check "ReceiveMessage" "$R" "single-$TS" +RH=$(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 "$RH" ]]; then + aws sqs change-message-visibility --queue-url "$QURL" --receipt-handle "$RH" --visibility-timeout 60 $ENDPOINT >/dev/null 2>&1 && ok "ChangeMessageVisibility" || fail "ChangeMessageVisibility" + aws sqs delete-message --queue-url "$QURL" --receipt-handle "$RH" $ENDPOINT >/dev/null 2>&1 && ok "DeleteMessage" || fail "DeleteMessage" else - FAIL=$((FAIL + 1)); echo " ❌ Нагрузка: получено $GOT/50" + fail "ReceiptHandle for single message" fi -sqs "$T1_AK" "$T1_SK" delete-queue --queue-url "$LQURL" >/dev/null 2>&1 || true +BATCH='[{"Id":"1","MessageBody":"b1"},{"Id":"2","MessageBody":"b2"},{"Id":"3","MessageBody":"b3"}]' +R=$(aws sqs send-message-batch --queue-url "$QURL" --entries "$BATCH" $ENDPOINT 2>&1) +check "SendMessageBatch" "$R" "Successful" -echo "" +R=$(aws sqs receive-message --queue-url "$QURL" --max-number-of-messages 3 $ENDPOINT 2>&1) +check "ReceiveMessage batch" "$R" "Messages|ReceiptHandle" +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) +if [[ -n "$RECEIPTS" ]]; then + CMV_ENTRIES=$(echo "$RECEIPTS" | python3 -c 'import sys,json; entries=[] +for i,rh in enumerate(sys.stdin.read().strip().split("\n"), 1): + if rh: + entries.append({"Id": f"c{i}", "ReceiptHandle": rh, "VisibilityTimeout": 60}) +print(json.dumps(entries))' 2>/dev/null || echo '[]') + R=$(aws sqs change-message-visibility-batch --queue-url "$QURL" --entries "$CMV_ENTRIES" $ENDPOINT 2>&1) + check "ChangeMessageVisibilityBatch" "$R" "Successful" -# ═══════════════════════════════════════════ -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)" + DEL_ENTRIES=$(echo "$RECEIPTS" | python3 -c 'import sys,json; entries=[] +for i,rh in enumerate(sys.stdin.read().strip().split("\n"), 1): + if rh: + entries.append({"Id": f"d{i}", "ReceiptHandle": rh}) +print(json.dumps(entries))' 2>/dev/null || echo '[]') + R=$(aws sqs delete-message-batch --queue-url "$QURL" --entries "$DEL_ENTRIES" $ENDPOINT 2>&1) + check "DeleteMessageBatch" "$R" "Successful" else - FAIL=$((FAIL + 1)); echo " ❌ MD5: ожидалось=$EXPECTED_MD5, получено=$GOT_MD5" + fail "Batch receipts" 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 +aws sqs send-message --queue-url "$QURL" --message-body "purge-me" $ENDPOINT >/dev/null 2>&1 || true +aws sqs purge-queue --queue-url "$QURL" $ENDPOINT >/dev/null 2>&1 && ok "PurgeQueue" || fail "PurgeQueue" +aws sqs delete-queue --queue-url "$QURL" $ENDPOINT >/dev/null 2>&1 && ok "DeleteQueue" || fail "DeleteQueue" + +echo "═══════════════════════════════════════════" +echo " RESULT: PASS=$PASS FAIL=$FAIL" +echo "═══════════════════════════════════════════" +[[ $FAIL -eq 0 ]] diff --git a/quick_test.sh b/quick_test.sh old mode 100644 new mode 100755 index 017d942..b3e917d --- a/quick_test.sh +++ b/quick_test.sh @@ -1,100 +1,89 @@ #!/bin/bash -# quick_test.sh — быстрая проверка shared-sqs (6 тестов) -# Требования: aws cli, bash -# Запуск: bash quick_test.sh +# quick_test.sh — public smoke test for shared-SQS -set -uo pipefail +set -euo pipefail -# Конфигурация — можно переопределить через переменные окружения BASE_URL="${BASE_URL:-https://qu.kube5s.ru}" -export AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-SSAK-demo-shared-sqs}" -export AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-demo-secret-key-shared-sqs-ngcloud-2026}" -export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-us-east-1}" +AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID:-}" +AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY:-}" +AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-us-east-1}" ENDPOINT="--endpoint-url ${BASE_URL}" - QUEUE_NAME="quick-test-$(date +%s)" PASS=0 FAIL=0 -TOTAL=0 -# check — проверяет что вывод содержит паттерн -check() { - local name="$1" output="$2" pattern="$3" - TOTAL=$((TOTAL + 1)) - if echo "$output" | head -20 | grep -qiE "$pattern"; then - echo " [PASS] $name" - PASS=$((PASS + 1)) - else - echo " [FAIL] $name" - echo " pattern: $pattern" - echo " output: $(echo "$output" | head -3)" - FAIL=$((FAIL + 1)) +require_env() { + local name="$1" + if [[ -z "${!name:-}" ]]; then + echo "Missing required env: $name" + exit 2 fi } +ok() { + echo " [PASS] $1" + PASS=$((PASS + 1)) +} + +fail() { + echo " [FAIL] $1" + FAIL=$((FAIL + 1)) +} + +check() { + local label="$1" + local body="$2" + local pattern="$3" + if echo "$body" | grep -qE "$pattern"; then + ok "$label" + else + fail "$label :: $(echo "$body" | head -3 | tr '\n' ' ')" + fi +} + +require_env AWS_ACCESS_KEY_ID +require_env AWS_SECRET_ACCESS_KEY +export AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_DEFAULT_REGION + echo "═══════════════════════════════════════════" -echo " Shared SQS — Quick Test" +echo " shared-SQS Quick Test" echo " Endpoint: $BASE_URL" echo " Queue: $QUEUE_NAME" echo "═══════════════════════════════════════════" -echo "" -# 1. Создать очередь -echo "▶ Создание очереди" +echo "▶ CreateQueue" OUT=$(aws sqs create-queue --queue-name "$QUEUE_NAME" $ENDPOINT 2>&1) -check "create-queue" "$OUT" "QueueUrl" -QUEUE_URL=$(echo "$OUT" | python3 -c "import sys,json; print(json.load(sys.stdin)['QueueUrl'])" 2>/dev/null) +check "CreateQueue" "$OUT" "QueueUrl" +QUEUE_URL=$(echo "$OUT" | python3 -c 'import sys,json; print(json.load(sys.stdin)["QueueUrl"])' 2>/dev/null || true) -# 2. Список очередей — должна быть наша -echo "" -echo "▶ Список очередей" +echo "▶ ListQueues" OUT=$(aws sqs list-queues $ENDPOINT 2>&1) -check "list-queues contains $QUEUE_NAME" "$OUT" "$QUEUE_NAME" +check "ListQueues" "$OUT" "$QUEUE_NAME" -# 3. Отправить сообщение -echo "" -echo "▶ Отправка сообщения" -OUT=$(aws sqs send-message --queue-url "$QUEUE_URL" --message-body "Hello Quick Test!" $ENDPOINT 2>&1) -check "send-message" "$OUT" "MessageId" +echo "▶ GetQueueUrl" +OUT=$(aws sqs get-queue-url --queue-name "$QUEUE_NAME" $ENDPOINT 2>&1) +check "GetQueueUrl" "$OUT" "QueueUrl" -# 4. Получить сообщение -echo "" -echo "▶ Получение сообщения" +echo "▶ SendMessage" +OUT=$(aws sqs send-message --queue-url "$QUEUE_URL" --message-body "Hello Quick Test" $ENDPOINT 2>&1) +check "SendMessage" "$OUT" "MessageId" + +echo "▶ ReceiveMessage" OUT=$(aws sqs receive-message --queue-url "$QUEUE_URL" --wait-time-seconds 5 $ENDPOINT 2>&1) -check "receive-message" "$OUT" "Hello Quick Test" -RECEIPT=$(echo "$OUT" | python3 -c "import sys,json; print(json.load(sys.stdin)['Messages'][0]['ReceiptHandle'])" 2>/dev/null) +check "ReceiveMessage" "$OUT" "Hello Quick Test" +RECEIPT=$(echo "$OUT" | python3 -c 'import sys,json; msgs=json.load(sys.stdin).get("Messages", []); print(msgs[0]["ReceiptHandle"] if msgs else "")' 2>/dev/null || true) -# 5. Удалить сообщение (confirm delivery) -echo "" -echo "▶ Удаление сообщения" +echo "▶ DeleteMessage" if [[ -n "$RECEIPT" ]]; then - OUT=$(aws sqs delete-message --queue-url "$QUEUE_URL" --receipt-handle "$RECEIPT" $ENDPOINT 2>&1) - # delete-message returns empty on success - if [[ -z "$OUT" || "$OUT" == "" ]]; then - TOTAL=$((TOTAL + 1)); PASS=$((PASS + 1)) - echo " [PASS] delete-message" - else - check "delete-message" "$OUT" "^$" - fi + aws sqs delete-message --queue-url "$QUEUE_URL" --receipt-handle "$RECEIPT" $ENDPOINT >/dev/null 2>&1 && ok "DeleteMessage" || fail "DeleteMessage" else - TOTAL=$((TOTAL + 1)); FAIL=$((FAIL + 1)) - echo " [FAIL] delete-message — no receipt handle" + fail "DeleteMessage (no receipt handle)" fi -# 6. Удалить очередь -echo "" -echo "▶ Удаление очереди" -OUT=$(aws sqs delete-queue --queue-url "$QUEUE_URL" $ENDPOINT 2>&1) -if [[ -z "$OUT" || "$OUT" == "" ]]; then - TOTAL=$((TOTAL + 1)); PASS=$((PASS + 1)) - echo " [PASS] delete-queue" -else - check "delete-queue" "$OUT" "^$" -fi +echo "▶ DeleteQueue" +aws sqs delete-queue --queue-url "$QUEUE_URL" $ENDPOINT >/dev/null 2>&1 && ok "DeleteQueue" || fail "DeleteQueue" -# Итог -echo "" echo "═══════════════════════════════════════════" -echo " ИТОГО: $PASS/$TOTAL PASS, $FAIL FAIL" +echo " RESULT: PASS=$PASS FAIL=$FAIL" echo "═══════════════════════════════════════════" -[[ $FAIL -eq 0 ]] && exit 0 || exit 1 +[[ $FAIL -eq 0 ]]