#!/usr/bin/env bash # test_functions_crud.sh — тестирует CRUD операции с функциями: # POST /functions, GET /functions, GET /functions/{name}, # DELETE /functions/{name}, валидация имён, дубликаты, размер кода. set -uo pipefail ROOT_DIR=$(cd "$(dirname "$0")" && pwd) source "${ROOT_DIR}/lib.sh" NS_CLEANUP_SCRIPT="${ROOT_DIR}/cleanup_test_namespaces.sh" RUN_ID=$(mk_run_id) SUB="test-crud-${RUN_ID}@test.local" cleanup() { if [ -x "$NS_CLEANUP_SCRIPT" ]; then KUBECTL_DIRECT=1 "$NS_CLEANUP_SCRIPT" "$SUB" >/dev/null 2>&1 || true fi } trap cleanup EXIT echo "" echo "========================================" echo " test_functions_crud.sh run=$RUN_ID" echo "========================================" JS_CODE='module.exports = async function(ctx) { return { status: 200, body: "crud-ok" }; }' # ── CREATE ─────────────────────────────────────────────────────────────────── echo "" echo ">>> CREATE: базовый nodejs → 201" R=$(curl -s -w "\n%{http_code}" -X POST "${BASE}/functions" \ -H "X-Auth-Token: ${SUB}" -H "Content-Type: application/json" \ -d "{\"name\":\"t-crud-fn-${RUN_ID}\",\"language\":\"nodejs\",\"code\":$(printf '%s' "$JS_CODE" | json_escape)}") CODE=$(echo "$R" | tail -1) BODY=$(echo "$R" | sed '$d') check_http "$CODE" "201" "CREATE nodejs → 201" FN_NAME=$(json_field "$BODY" name) FN_ROUTE=$(json_field "$BODY" route) FN_PKG=$(json_field "$BODY" package) [ "$FN_NAME" = "t-crud-fn-${RUN_ID}" ] && pass "CREATE: name в ответе" || fail "CREATE: name='$FN_NAME'" printf '%s' "$FN_ROUTE" | grep -q '/' && pass "CREATE: route непустой" || fail "CREATE: route='$FN_ROUTE'" [ -n "$FN_PKG" ] && pass "CREATE: package непустой" || fail "CREATE: package пустой" echo "" echo ">>> CREATE: дубликат → 409" R=$(curl -s -w "\n%{http_code}" -X POST "${BASE}/functions" \ -H "X-Auth-Token: ${SUB}" -H "Content-Type: application/json" \ -d "{\"name\":\"t-crud-fn-${RUN_ID}\",\"language\":\"nodejs\",\"code\":$(printf '%s' "$JS_CODE" | json_escape)}") check_http "$(echo "$R" | tail -1)" "409" "CREATE дубликат → 409" echo "" echo ">>> CREATE: без name → 400" R=$(curl -s -w "\n%{http_code}" -X POST "${BASE}/functions" \ -H "X-Auth-Token: ${SUB}" -H "Content-Type: application/json" \ -d '{"language":"nodejs","code":"x"}') check_http "$(echo "$R" | tail -1)" "400" "CREATE без name → 400" echo "" echo ">>> CREATE: без language → 400" R=$(curl -s -w "\n%{http_code}" -X POST "${BASE}/functions" \ -H "X-Auth-Token: ${SUB}" -H "Content-Type: application/json" \ -d '{"name":"no-lang","code":"x"}') check_http "$(echo "$R" | tail -1)" "400" "CREATE без language → 400" echo "" echo ">>> CREATE: без code → 400" R=$(curl -s -w "\n%{http_code}" -X POST "${BASE}/functions" \ -H "X-Auth-Token: ${SUB}" -H "Content-Type: application/json" \ -d '{"name":"no-code","language":"nodejs"}') check_http "$(echo "$R" | tail -1)" "400" "CREATE без code → 400" echo "" echo ">>> CREATE: невалидный язык → 400" R=$(curl -s -w "\n%{http_code}" -X POST "${BASE}/functions" \ -H "X-Auth-Token: ${SUB}" -H "Content-Type: application/json" \ -d '{"name":"bad-lang","language":"cobol","code":"x"}') check_http "$(echo "$R" | tail -1)" "400" "CREATE язык=cobol → 400" echo "" echo ">>> CREATE: валидация имени" # Заглавная буква R=$(curl -s -w "\n%{http_code}" -X POST "${BASE}/functions" \ -H "X-Auth-Token: ${SUB}" -H "Content-Type: application/json" \ -d '{"name":"BadName","language":"nodejs","code":"x"}') check_http "$(echo "$R" | tail -1)" "400" "CREATE имя с заглавной → 400" # Начинается с дефиса R=$(curl -s -w "\n%{http_code}" -X POST "${BASE}/functions" \ -H "X-Auth-Token: ${SUB}" -H "Content-Type: application/json" \ -d '{"name":"-bad","language":"nodejs","code":"x"}') check_http "$(echo "$R" | tail -1)" "400" "CREATE имя с ведущим дефисом → 400" # Заканчивается дефисом R=$(curl -s -w "\n%{http_code}" -X POST "${BASE}/functions" \ -H "X-Auth-Token: ${SUB}" -H "Content-Type: application/json" \ -d '{"name":"bad-","language":"nodejs","code":"x"}') check_http "$(echo "$R" | tail -1)" "400" "CREATE имя с завершающим дефисом → 400" # Пробел в имени R=$(curl -s -w "\n%{http_code}" -X POST "${BASE}/functions" \ -H "X-Auth-Token: ${SUB}" -H "Content-Type: application/json" \ -d '{"name":"bad name","language":"nodejs","code":"x"}') check_http "$(echo "$R" | tail -1)" "400" "CREATE имя с пробелом → 400" # 58 символов — должно быть 400 (лимит 57) NAME58=$(python3 -c "print('a'*58)") R=$(curl -s -w "\n%{http_code}" -X POST "${BASE}/functions" \ -H "X-Auth-Token: ${SUB}" -H "Content-Type: application/json" \ -d "{\"name\":\"${NAME58}\",\"language\":\"nodejs\",\"code\":\"x\"}") check_http "$(echo "$R" | tail -1)" "400" "CREATE имя 58 символов → 400" # 57 символов — должно быть 201 NAME57=$(python3 -c "print('a'*57)") R=$(curl -s -w "\n%{http_code}" -X POST "${BASE}/functions" \ -H "X-Auth-Token: ${SUB}" -H "Content-Type: application/json" \ -d "{\"name\":\"${NAME57}\",\"language\":\"nodejs\",\"code\":$(printf '%s' "$JS_CODE" | json_escape)}") check_http "$(echo "$R" | tail -1)" "201" "CREATE имя 57 символов → 201" echo "" echo ">>> CREATE: код > 1MB → 400 или 413" # Используем pipe чтобы избежать Argument list too long R=$(python3 -c " import json, sys payload = json.dumps({'name':'big-code','language':'nodejs','code':'x'*(1024*1024+1)}) print(payload) " | curl -s -w "\n%{http_code}" -X POST "${BASE}/functions" \ -H "X-Auth-Token: ${SUB}" -H "Content-Type: application/json" \ --data-binary @- --max-time 30) CODE=$(echo "$R" | tail -1) ( [ "$CODE" = "400" ] || [ "$CODE" = "413" ] || [ "$CODE" = "000" ] ) \ && pass "CREATE код > 1MB → отклонён ($CODE)" \ || fail "CREATE код > 1MB → got $CODE" # ── LIST ───────────────────────────────────────────────────────────────────── echo "" echo ">>> LIST: GET /functions → 200, массив" R=$(curl -s -w "\n%{http_code}" "${BASE}/functions" -H "X-Auth-Token: ${SUB}") CODE=$(echo "$R" | tail -1) BODY=$(echo "$R" | sed '$d') check_http "$CODE" "200" "LIST GET /functions → 200" COUNT=$(json_len "$BODY") [ "${COUNT:-0}" -ge 1 ] && pass "LIST: содержит ≥1 функцию (создали выше)" || fail "LIST: пустой массив count=$COUNT" echo "" echo ">>> LIST: новый пользователь → пустой массив" EMPTY_USER="empty-${RUN_ID}@test.local" R=$(curl -s -w "\n%{http_code}" "${BASE}/functions" -H "X-Auth-Token: ${EMPTY_USER}") CODE=$(echo "$R" | tail -1) BODY=$(echo "$R" | sed '$d') check_http "$CODE" "200" "LIST новый пользователь → 200" COUNT=$(json_len "$BODY") [ "${COUNT:-99}" -eq 0 ] && pass "LIST новый пользователь → []" || fail "LIST новый пользователь → count=$COUNT" # ── GET ────────────────────────────────────────────────────────────────────── echo "" echo ">>> GET: /functions/{name} → 200" R=$(curl -s -w "\n%{http_code}" "${BASE}/functions/t-crud-fn-${RUN_ID}" \ -H "X-Auth-Token: ${SUB}") CODE=$(echo "$R" | tail -1) BODY=$(echo "$R" | sed '$d') check_http "$CODE" "200" "GET /functions/t-crud-fn-${RUN_ID} → 200" GOT_NAME=$(json_field "$BODY" name) [ "$GOT_NAME" = "t-crud-fn-${RUN_ID}" ] && pass "GET: name совпадает" || fail "GET: name='$GOT_NAME'" GOT_ENV=$(json_field "$BODY" environment) [ -n "$GOT_ENV" ] && pass "GET: environment непустой ($GOT_ENV)" || fail "GET: environment пустой" echo "" echo ">>> GET: несуществующая функция → 404" R=$(curl -s -w "\n%{http_code}" "${BASE}/functions/does-not-exist-xyz" \ -H "X-Auth-Token: ${SUB}") check_http "$(echo "$R" | tail -1)" "404" "GET несуществующая → 404" # ── DELETE ─────────────────────────────────────────────────────────────────── echo "" echo ">>> DELETE: /functions/{name} → 200" R=$(curl -s -w "\n%{http_code}" -X DELETE "${BASE}/functions/t-crud-fn-${RUN_ID}" \ -H "X-Auth-Token: ${SUB}") CODE=$(echo "$R" | tail -1) BODY=$(echo "$R" | sed '$d') check_http "$CODE" "200" "DELETE crud-fn → 200" DELETED=$(json_field "$BODY" deleted) [ "$DELETED" = "True" ] || [ "$DELETED" = "true" ] && pass "DELETE: deleted=true" || fail "DELETE: deleted='$DELETED'" echo "" echo ">>> DELETE: повторное удаление → 404" R=$(curl -s -w "\n%{http_code}" -X DELETE "${BASE}/functions/t-crud-fn-${RUN_ID}" \ -H "X-Auth-Token: ${SUB}") check_http "$(echo "$R" | tail -1)" "404" "DELETE повторное → 404" echo "" echo ">>> GET: после DELETE → 404" R=$(curl -s -w "\n%{http_code}" "${BASE}/functions/t-crud-fn-${RUN_ID}" \ -H "X-Auth-Token: ${SUB}") check_http "$(echo "$R" | tail -1)" "404" "GET после DELETE → 404" echo "" echo ">>> DELETE: несуществующая функция → 404" R=$(curl -s -w "\n%{http_code}" -X DELETE "${BASE}/functions/does-not-exist-xyz" \ -H "X-Auth-Token: ${SUB}") check_http "$(echo "$R" | tail -1)" "404" "DELETE несуществующая → 404" print_summary