diff --git a/shared-sqs/tests/shared_sqs_test.sh b/shared-sqs/tests/shared_sqs_test.sh new file mode 100755 index 0000000..153a7bc --- /dev/null +++ b/shared-sqs/tests/shared_sqs_test.sh @@ -0,0 +1,211 @@ +#!/usr/bin/env bash +# tests/shared_sqs_test.sh +# Интеграционные тесты shared-sqs: Admin API, изоляция тенантов, CRUD, лимиты +# Created: 2026-04-09 + +set -euo pipefail + +BASE_URL="${BASE_URL:-http://localhost:4100}" +ADMIN_TOKEN="${ADMIN_TOKEN:-dev-token-123}" +PASS=0 +FAIL=0 + +pass() { echo " [PASS] $1"; PASS=$((PASS+1)); } +fail() { echo " [FAIL] $1"; FAIL=$((FAIL+1)); } + +check_status() { + local label="$1" expected="$2" actual="$3" + if [[ "$actual" == "$expected" ]]; then + pass "$label (HTTP $actual)" + else + fail "$label -- expected HTTP $expected, got $actual" + fi +} + +check_contains() { + local label="$1" pattern="$2" body="$3" + if echo "$body" | grep -q "$pattern"; then + pass "$label" + else + fail "$label -- pattern '$pattern' not found in: $body" + fi +} + +check_not_contains() { + local label="$1" pattern="$2" body="$3" + if ! echo "$body" | grep -q "$pattern"; then + pass "$label" + else + fail "$label -- pattern '$pattern' FOUND but should be absent" + fi +} + +sqs_req() { + local access_key="$1" secret_key="$2" action="$3" + shift 3 + AWS_ACCESS_KEY_ID="$access_key" \ + AWS_SECRET_ACCESS_KEY="$secret_key" \ + AWS_DEFAULT_REGION="us-east-1" \ + aws --endpoint-url "$BASE_URL" --output json sqs "$action" "$@" 2>&1 +} + +sqs_req_status() { + local access_key="$1" secret_key="$2" action="$3" + shift 3 + AWS_ACCESS_KEY_ID="$access_key" \ + AWS_SECRET_ACCESS_KEY="$secret_key" \ + AWS_DEFAULT_REGION="us-east-1" \ + aws --endpoint-url "$BASE_URL" --output json sqs "$action" "$@" > /dev/null 2>&1 + echo $? +} + +echo "========================================" +echo " shared-sqs integration tests" +echo " BASE_URL=$BASE_URL" +echo "========================================" + +echo "" +echo "--- 1. Health check ---" +STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL/health") +check_status "GET /health" "200" "$STATUS" + +echo "" +echo "--- 2. Admin API -- без токена ---" +STATUS=$(curl -s -o /dev/null -w "%{http_code}" -X GET "$BASE_URL/admin/tenants") +check_status "GET /admin/tenants без токена -> 401" "401" "$STATUS" + +STATUS=$(curl -s -o /dev/null -w "%{http_code}" -X POST "$BASE_URL/admin/tenants" \ + -H "Content-Type: application/json" -d '{"name":"test"}') +check_status "POST /admin/tenants без токена -> 401" "401" "$STATUS" + +echo "" +echo "--- 3. Admin API -- создание тенантов ---" +RESP_A=$(curl -s -X POST "$BASE_URL/admin/tenants" \ + -H "Authorization: Bearer $ADMIN_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"name":"tenant-a","max_queues":10}') +check_contains "Создать tenant-a -- access_key" "access_key" "$RESP_A" +check_contains "Создать tenant-a -- secret_key" "secret_key" "$RESP_A" +check_contains "Создать tenant-a -- id" "\"id\"" "$RESP_A" + +ACCESS_A=$(echo "$RESP_A" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['access_key'])") +SECRET_A=$(echo "$RESP_A" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['secret_key'])") +ID_A=$(echo "$RESP_A" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['id'])") + +RESP_B=$(curl -s -X POST "$BASE_URL/admin/tenants" \ + -H "Authorization: Bearer $ADMIN_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"name":"tenant-b","max_queues":10}') +check_contains "Создать tenant-b -- access_key" "access_key" "$RESP_B" + +ACCESS_B=$(echo "$RESP_B" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['access_key'])") +SECRET_B=$(echo "$RESP_B" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['secret_key'])") +ID_B=$(echo "$RESP_B" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['id'])") + +echo "" +echo "--- 4. Admin API -- list/get ---" +LIST=$(curl -s "$BASE_URL/admin/tenants" -H "Authorization: Bearer $ADMIN_TOKEN") +check_contains "GET /admin/tenants -- tenant-a" "tenant-a" "$LIST" +check_contains "GET /admin/tenants -- tenant-b" "tenant-b" "$LIST" +check_not_contains "GET /admin/tenants -- НЕТ secret_key" "secret_key" "$LIST" + +GET_A=$(curl -s "$BASE_URL/admin/tenants/$ID_A" -H "Authorization: Bearer $ADMIN_TOKEN") +check_contains "GET /admin/tenants/{id} -- tenant-a" "tenant-a" "$GET_A" +check_not_contains "GET /admin/tenants/{id} -- нет secret_key" "secret_key" "$GET_A" + +STATUS404=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL/admin/tenants/no-such-id" \ + -H "Authorization: Bearer $ADMIN_TOKEN") +check_status "GET несуществующего тенанта -> 404" "404" "$STATUS404" + +echo "" +echo "--- 5. CRUD flow (tenant-a) ---" +QUEUE_URL_A=$(sqs_req "$ACCESS_A" "$SECRET_A" create-queue --queue-name my-queue \ + | python3 -c "import sys,json; print(json.load(sys.stdin)['QueueUrl'])") +check_contains "CreateQueue -> QueueUrl содержит my-queue" "my-queue" "$QUEUE_URL_A" +check_contains "CreateQueue -> QueueUrl содержит ID тенанта" "$ID_A" "$QUEUE_URL_A" + +MSG_ID=$(sqs_req "$ACCESS_A" "$SECRET_A" send-message \ + --queue-url "$QUEUE_URL_A" --message-body "hello-world" \ + | python3 -c "import sys,json; print(json.load(sys.stdin)['MessageId'])") +check_contains "SendMessage -> MessageId" "-" "$MSG_ID" + +RECV=$(sqs_req "$ACCESS_A" "$SECRET_A" receive-message --queue-url "$QUEUE_URL_A") +check_contains "ReceiveMessage -> тело hello-world" "hello-world" "$RECV" + +RECEIPT=$(echo "$RECV" | python3 -c "import sys,json; msgs=json.load(sys.stdin).get('Messages',[]); print(msgs[0]['ReceiptHandle'] if msgs else '')") +if [[ -n "$RECEIPT" ]]; then + sqs_req "$ACCESS_A" "$SECRET_A" delete-message \ + --queue-url "$QUEUE_URL_A" --receipt-handle "$RECEIPT" > /dev/null 2>&1 + pass "DeleteMessage -- без ошибок" +else + fail "DeleteMessage -- нет ReceiptHandle" +fi + +echo "" +echo "--- 6. Изоляция тенантов ---" +QUEUE_URL_B=$(sqs_req "$ACCESS_B" "$SECRET_B" create-queue --queue-name my-queue \ + | python3 -c "import sys,json; print(json.load(sys.stdin)['QueueUrl'])") +check_contains "tenant-B CreateQueue my-queue -> ID tenant-B в URL" "$ID_B" "$QUEUE_URL_B" + +sqs_req "$ACCESS_A" "$SECRET_A" send-message \ + --queue-url "$QUEUE_URL_A" --message-body "msg-for-A" > /dev/null 2>&1 + +RECV_B=$(sqs_req "$ACCESS_B" "$SECRET_B" receive-message --queue-url "$QUEUE_URL_B" 2>&1 || true) +check_not_contains "Изоляция: tenant-B НЕ получает msg-for-A" "msg-for-A" "$RECV_B" + +LIST_B=$(sqs_req "$ACCESS_B" "$SECRET_B" list-queues 2>&1 || true) +check_not_contains "ListQueues tenant-B -- нет ID tenant-A" "$ID_A" "$LIST_B" + +LIST_A=$(sqs_req "$ACCESS_A" "$SECRET_A" list-queues 2>&1 || true) +check_not_contains "ListQueues tenant-A -- нет ID tenant-B" "$ID_B" "$LIST_A" + +echo "" +echo "--- 7. SQS с невалидным ключом -> ошибка ---" +EXIT_FAKE=$(sqs_req_status "FAKE-KEY-0000000000" "fakesecret0000" list-queues) +if [[ "$EXIT_FAKE" != "0" ]]; then + pass "Невалидный access_key -> ошибка auth" +else + fail "Невалидный access_key -> ожидалась ошибка, получен 200" +fi + +echo "" +echo "--- 8. Лимит очередей (max_queues=2) ---" +RESP_LIM=$(curl -s -X POST "$BASE_URL/admin/tenants" \ + -H "Authorization: Bearer $ADMIN_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"name":"limited","max_queues":2}') +ACCESS_LIM=$(echo "$RESP_LIM" | python3 -c "import sys,json; print(json.load(sys.stdin)['access_key'])") +SECRET_LIM=$(echo "$RESP_LIM" | python3 -c "import sys,json; print(json.load(sys.stdin)['secret_key'])") + +sqs_req "$ACCESS_LIM" "$SECRET_LIM" create-queue --queue-name q1 > /dev/null 2>&1 +pass "Очередь 1 из 2 -- создана" +sqs_req "$ACCESS_LIM" "$SECRET_LIM" create-queue --queue-name q2 > /dev/null 2>&1 +pass "Очередь 2 из 2 -- создана" + +EXIT3=$(sqs_req_status "$ACCESS_LIM" "$SECRET_LIM" create-queue --queue-name q3) +if [[ "$EXIT3" != "0" ]]; then + pass "Очередь 3 при max_queues=2 -> лимит (ошибка)" +else + fail "Очередь 3 при max_queues=2 -> ожидалась ошибка, создалась!" +fi + +echo "" +echo "--- 9. DELETE tenant -> очереди удалены ---" +STATUS_DEL=$(curl -s -o /dev/null -w "%{http_code}" -X DELETE \ + "$BASE_URL/admin/tenants/$ID_B" \ + -H "Authorization: Bearer $ADMIN_TOKEN") +check_status "DELETE tenant-b -> 204" "204" "$STATUS_DEL" + +EXIT_AFTER=$(sqs_req_status "$ACCESS_B" "$SECRET_B" list-queues) +if [[ "$EXIT_AFTER" != "0" ]]; then + pass "После DELETE tenant -- его ключи -> ошибка auth" +else + fail "После DELETE tenant -- его ключи вернули 200" +fi + +echo "" +echo "========================================" +echo " Результат: PASS=$PASS FAIL=$FAIL" +echo "========================================" + +[[ $FAIL -eq 0 ]] && exit 0 || exit 1