refactor: перемещены все тестовые скрипты в scripts/
This commit is contained in:
Executable
+326
@@ -0,0 +1,326 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
BASE="https://fission.kube5s.ru/console/api"
|
||||
ROOT_DIR=$(cd "$(dirname "$0")" && pwd)
|
||||
NS_CLEANUP_SCRIPT="${ROOT_DIR}/cleanup_test_namespaces.sh"
|
||||
RUN_ID="ui$(date +%s | tail -c 6)"
|
||||
PASS=0
|
||||
FAIL=0
|
||||
|
||||
LOGIN_SUB="ui-login-${RUN_ID}@test.local"
|
||||
LANG_SUB="ui-lang-${RUN_ID}@test.local"
|
||||
NODE_SUB="ui-node-${RUN_ID}@test.local"
|
||||
STACK_SUB="ui-stack-${RUN_ID}@test.local"
|
||||
NEG_SUB="ui-neg-${RUN_ID}@test.local"
|
||||
|
||||
pass() { PASS=$((PASS+1)); echo " PASS: $1"; }
|
||||
fail() { FAIL=$((FAIL+1)); echo " FAIL: $1"; }
|
||||
|
||||
json_quote() {
|
||||
python3 -c 'import json,sys; print(json.dumps(sys.argv[1]))' "$1"
|
||||
}
|
||||
|
||||
json_field() {
|
||||
local payload="$1"
|
||||
local key="$2"
|
||||
printf '%s' "$payload" | python3 -c 'import json,sys
|
||||
key=sys.argv[1]
|
||||
try:
|
||||
data=json.load(sys.stdin)
|
||||
value=data.get(key, "")
|
||||
print(value if not isinstance(value, (dict,list)) else json.dumps(value))
|
||||
except Exception:
|
||||
print("")' "$key"
|
||||
}
|
||||
|
||||
cleanup_namespaces() {
|
||||
if [ -x "$NS_CLEANUP_SCRIPT" ]; then
|
||||
echo ""
|
||||
echo ">>> Cleanup UI test namespaces..."
|
||||
KUBECTL_DIRECT=1 "$NS_CLEANUP_SCRIPT" "$LOGIN_SUB" "$LANG_SUB" "$NODE_SUB" "$STACK_SUB" "$NEG_SUB" || true
|
||||
fi
|
||||
}
|
||||
trap cleanup_namespaces EXIT
|
||||
|
||||
auth_call() {
|
||||
local token="$1"
|
||||
local env_name="${2:-test}"
|
||||
curl -s -w "\n%{http_code}" --max-time 45 -X POST "${BASE}/auth" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "{\"token\":\"${token}\",\"env\":\"${env_name}\"}"
|
||||
}
|
||||
|
||||
auth_get() {
|
||||
local path="$1"
|
||||
local token="$2"
|
||||
local env_name="${3:-test}"
|
||||
curl -s -w "\n%{http_code}" --max-time 45 "${BASE}${path}" \
|
||||
-H "X-Auth-Token: ${token}" \
|
||||
-H "X-Auth-Env: ${env_name}"
|
||||
}
|
||||
|
||||
create_call() {
|
||||
local sub="$1"
|
||||
local body="$2"
|
||||
curl -s -w "\n%{http_code}" --max-time 60 -X POST "${BASE}/functions" \
|
||||
-H "X-Test-Sub: ${sub}" -H 'Content-Type: application/json' -d "$body"
|
||||
}
|
||||
|
||||
invoke_call() {
|
||||
local sub="$1"
|
||||
local name="$2"
|
||||
local body="${3:-{}}"
|
||||
local out_file err_file http_code rc resp err_text
|
||||
out_file=$(mktemp)
|
||||
err_file=$(mktemp)
|
||||
rc=0
|
||||
http_code=$(curl -sS -o "$out_file" -w "%{http_code}" --max-time 60 -X POST "${BASE}/functions/${name}/invoke" \
|
||||
-H "X-Test-Sub: ${sub}" -H 'Content-Type: application/json' -d "$body" 2>"$err_file") || rc=$?
|
||||
resp=$(cat "$out_file" 2>/dev/null || true)
|
||||
err_text=$(tr '\n' ' ' <"$err_file" | sed 's/[[:space:]]\+/ /g')
|
||||
rm -f "$out_file" "$err_file"
|
||||
|
||||
if [ "$rc" -ne 0 ]; then
|
||||
printf '{"error":%s,"status":"","response_raw":"","http_status":"%s"}\n' \
|
||||
"$(json_quote "curl rc=${rc} http=${http_code}: ${err_text}")" "$http_code"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if printf '%s' "$resp" | python3 -c 'import json,sys; json.load(sys.stdin)' >/dev/null 2>&1; then
|
||||
printf '%s' "$resp"
|
||||
return 0
|
||||
fi
|
||||
|
||||
printf '{"error":%s,"status":"","response_raw":%s,"http_status":"%s"}\n' \
|
||||
"$(json_quote "non-json invoke response http=${http_code}")" "$(json_quote "$resp")" "$http_code"
|
||||
}
|
||||
|
||||
wait_invoke_ok() {
|
||||
local sub="$1"
|
||||
local name="$2"
|
||||
local expected="$3"
|
||||
local tries="$4"
|
||||
local delay="$5"
|
||||
local try status raw err latency http_status
|
||||
for try in $(seq 1 "$tries"); do
|
||||
resp=$(invoke_call "$sub" "$name")
|
||||
status=$(json_field "$resp" status)
|
||||
raw=$(json_field "$resp" response_raw)
|
||||
err=$(json_field "$resp" error)
|
||||
latency=$(json_field "$resp" latency_ms)
|
||||
http_status=$(json_field "$resp" http_status)
|
||||
echo " TRY=${try} fn=${name} status=${status} http=${http_status} latency_ms=${latency} raw=${raw} err=${err}"
|
||||
if [ "$status" = "200" ] && printf '%s' "$raw" | grep -q "$expected"; then
|
||||
return 0
|
||||
fi
|
||||
if [ "$try" -lt "$tries" ]; then
|
||||
sleep "$delay"
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
wait_init_ready() {
|
||||
local token="$1"
|
||||
local env_name="${2:-test}"
|
||||
local tries="$3"
|
||||
local delay="$4"
|
||||
local try debug_raw debug_code debug_body status_raw status_code status_body ready done dbg_ready exec_ready exec_pods router_ready router_pods
|
||||
for try in $(seq 1 "$tries"); do
|
||||
debug_raw=$(auth_get "/ns/debug" "$token" "$env_name")
|
||||
debug_code=$(printf '%s' "$debug_raw" | tail -1)
|
||||
debug_body=$(printf '%s' "$debug_raw" | sed '$d')
|
||||
status_raw=$(auth_get "/ns/status" "$token" "$env_name")
|
||||
status_code=$(printf '%s' "$status_raw" | tail -1)
|
||||
status_body=$(printf '%s' "$status_raw" | sed '$d')
|
||||
ready=$(json_field "$status_body" ready)
|
||||
done=$(printf '%s' "$status_body" | python3 -c 'import json,sys
|
||||
try:
|
||||
data=json.load(sys.stdin)
|
||||
print(sum(1 for s in data.get("stages", []) if s.get("done")))
|
||||
except Exception:
|
||||
print(0)')
|
||||
dbg_ready=$(printf '%s' "$debug_body" | python3 -c 'import json,sys
|
||||
try:
|
||||
data=json.load(sys.stdin)
|
||||
print(str(data.get("controlPlane", {}).get("ready", "")))
|
||||
except Exception:
|
||||
print("")')
|
||||
exec_ready=$(printf '%s' "$debug_body" | python3 -c 'import json,sys
|
||||
try:
|
||||
data=json.load(sys.stdin)
|
||||
print(str(data.get("controlPlane", {}).get("executor", {}).get("ready", "")))
|
||||
except Exception:
|
||||
print("")')
|
||||
exec_pods=$(printf '%s' "$debug_body" | python3 -c 'import json,sys
|
||||
try:
|
||||
data=json.load(sys.stdin)
|
||||
print(data.get("controlPlane", {}).get("executor", {}).get("podCount", ""))
|
||||
except Exception:
|
||||
print("")')
|
||||
router_ready=$(printf '%s' "$debug_body" | python3 -c 'import json,sys
|
||||
try:
|
||||
data=json.load(sys.stdin)
|
||||
print(str(data.get("controlPlane", {}).get("router", {}).get("ready", "")))
|
||||
except Exception:
|
||||
print("")')
|
||||
router_pods=$(printf '%s' "$debug_body" | python3 -c 'import json,sys
|
||||
try:
|
||||
data=json.load(sys.stdin)
|
||||
print(data.get("controlPlane", {}).get("router", {}).get("podCount", ""))
|
||||
except Exception:
|
||||
print("")')
|
||||
echo " INIT_TRY=${try} status_http=${status_code} debug_http=${debug_code} ready=${ready} done=${done}/3 cp_ready=${dbg_ready} executor_ready=${exec_ready} executor_pods=${exec_pods} router_ready=${router_ready} router_pods=${router_pods}"
|
||||
if [ "$status_code" = "200" ] && [ "$debug_code" = "200" ] && [ "$ready" = "True" -o "$ready" = "true" ]; then
|
||||
return 0
|
||||
fi
|
||||
if [ "$try" -lt "$tries" ]; then
|
||||
sleep "$delay"
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo " UI-like user flow tests"
|
||||
echo " Run ID: ${RUN_ID}"
|
||||
echo "========================================"
|
||||
|
||||
echo ""
|
||||
echo ">>> T01: login с невалидным токеном"
|
||||
R=$(auth_call "not-an-email")
|
||||
CODE=$(printf '%s' "$R" | tail -1)
|
||||
[ "$CODE" = "401" ] && pass "login invalid token -> 401" || fail "login invalid token -> got $CODE"
|
||||
|
||||
echo ""
|
||||
echo ">>> T02: login c валидным test token"
|
||||
R=$(auth_call "$LOGIN_SUB")
|
||||
CODE=$(printf '%s' "$R" | tail -1)
|
||||
BODY=$(printf '%s' "$R" | sed '$d')
|
||||
NS=$(json_field "$BODY" namespace)
|
||||
[ "$CODE" = "200" ] && pass "login valid token -> 200" || fail "login valid token -> got $CODE"
|
||||
printf '%s' "$NS" | grep -q '^fission-' && pass "login returns namespace" || fail "login namespace malformed: ${NS}"
|
||||
|
||||
echo ""
|
||||
echo ">>> T02b: первый вход ждёт init overlay до 3/3"
|
||||
if wait_init_ready "$LOGIN_SUB" "test" 12 5; then
|
||||
pass "init overlay path -> namespace ready 3/3"
|
||||
else
|
||||
fail "init overlay path did not reach 3/3"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo ">>> T03: ошибки create, которые типичны для UI"
|
||||
R=$(create_call "$NEG_SUB" '{"name":"","language":"python","code":"def main():\n return 1"}')
|
||||
CODE=$(printf '%s' "$R" | tail -1)
|
||||
[ "$CODE" = "400" ] && pass "empty name -> 400" || fail "empty name -> got $CODE"
|
||||
|
||||
R=$(create_call "$NEG_SUB" '{"name":"empty-code","language":"python","code":" "}')
|
||||
CODE=$(printf '%s' "$R" | tail -1)
|
||||
[ "$CODE" = "400" ] && pass "empty code -> 400" || fail "empty code -> got $CODE"
|
||||
|
||||
R=$(create_call "$NEG_SUB" '{"name":"bad ttl","language":"python","code":"def main():\n return 1","ttl":"99z"}')
|
||||
CODE=$(printf '%s' "$R" | tail -1)
|
||||
[ "$CODE" = "400" ] && pass "invalid ttl -> 400" || fail "invalid ttl -> got $CODE"
|
||||
|
||||
R=$(create_call "$NEG_SUB" '{"name":"bad name fn","language":"python","code":"def main():\n return 1"}')
|
||||
CODE=$(printf '%s' "$R" | tail -1)
|
||||
[ "$CODE" != "201" ] && [ "$CODE" != "200" ] && pass "invalid name with spaces rejected" || fail "invalid name unexpectedly created"
|
||||
|
||||
R=$(create_call "$NEG_SUB" '{"name":"bad-lang","language":"cobol","code":"x"}')
|
||||
CODE=$(printf '%s' "$R" | tail -1)
|
||||
[ "$CODE" = "400" ] && pass "invalid language -> 400" || fail "invalid language -> got $CODE"
|
||||
|
||||
echo ""
|
||||
echo ">>> T04: duplicate create и повторный delete"
|
||||
R=$(create_call "$NEG_SUB" '{"name":"dup-ui","language":"python","entrypoint":"main.main","code":"def main():\n return \"dup-ui\""}')
|
||||
CODE=$(printf '%s' "$R" | tail -1)
|
||||
[ "$CODE" = "201" ] && pass "first create -> 201" || fail "first create -> got $CODE"
|
||||
|
||||
R=$(create_call "$NEG_SUB" '{"name":"dup-ui","language":"python","entrypoint":"main.main","code":"def main():\n return \"dup-ui-2\""}')
|
||||
CODE=$(printf '%s' "$R" | tail -1)
|
||||
([ "$CODE" = "409" ] || [ "$CODE" = "502" ]) && pass "duplicate create rejected" || fail "duplicate create -> got $CODE"
|
||||
|
||||
DC=$(curl -s -o /dev/null -w "%{http_code}" --max-time 45 -X DELETE "${BASE}/functions/dup-ui" -H "X-Test-Sub: ${NEG_SUB}")
|
||||
[ "$DC" = "200" ] && pass "first delete -> 200" || fail "first delete -> got $DC"
|
||||
DC2=$(curl -s -o /dev/null -w "%{http_code}" --max-time 45 -X DELETE "${BASE}/functions/dup-ui" -H "X-Test-Sub: ${NEG_SUB}")
|
||||
[ "$DC2" = "404" ] && pass "second delete -> 404" || fail "second delete -> got $DC2"
|
||||
|
||||
echo ""
|
||||
echo ">>> T05: Python create/invoke как из UI"
|
||||
R=$(create_call "$LANG_SUB" '{"name":"py-ui","language":"python","entrypoint":"main.main","code":"def main():\n return \"py-ui-ok\""}')
|
||||
CODE=$(printf '%s' "$R" | tail -1)
|
||||
[ "$CODE" = "201" ] && pass "python create -> 201" || fail "python create -> got $CODE"
|
||||
if wait_invoke_ok "$LANG_SUB" "py-ui" "py-ui-ok" 8 5; then
|
||||
pass "python invoke -> 200"
|
||||
else
|
||||
fail "python invoke did not stabilize"
|
||||
fi
|
||||
|
||||
PY_PAR1=$(invoke_call "$LANG_SUB" "py-ui")
|
||||
PY_PAR2=$(invoke_call "$LANG_SUB" "py-ui")
|
||||
[ "$(json_field "$PY_PAR1" status)" = "200" ] && [ "$(json_field "$PY_PAR2" status)" = "200" ] && pass "python double invoke -> both 200" || fail "python double invoke did not return two 200s"
|
||||
|
||||
echo ""
|
||||
echo ">>> T06: NodeJS create/invoke и edit/update"
|
||||
R=$(create_call "$NODE_SUB" '{"name":"node-ui","language":"nodejs","entrypoint":"main","code":"module.exports = async function() { return { status: 200, body: \"node-ui-ok\" }; }"}')
|
||||
CODE=$(printf '%s' "$R" | tail -1)
|
||||
[ "$CODE" = "201" ] && pass "nodejs create -> 201" || fail "nodejs create -> got $CODE"
|
||||
if wait_invoke_ok "$NODE_SUB" "node-ui" "node-ui-ok" 16 5; then
|
||||
pass "nodejs invoke -> 200"
|
||||
else
|
||||
fail "nodejs invoke did not stabilize"
|
||||
fi
|
||||
UPD=$(curl -s -w "\n%{http_code}" --max-time 60 -X PUT "${BASE}/functions/node-ui/code" \
|
||||
-H "X-Test-Sub: ${NODE_SUB}" -H 'Content-Type: application/json' \
|
||||
-d '{"code":"module.exports = async function() { return { status: 200, body: \"node-ui-updated\" }; }"}')
|
||||
CODE=$(printf '%s' "$UPD" | tail -1)
|
||||
[ "$CODE" = "200" ] && pass "nodejs update -> 200" || fail "nodejs update -> got $CODE"
|
||||
if wait_invoke_ok "$NODE_SUB" "node-ui" "node-ui-updated" 16 5; then
|
||||
pass "nodejs invoke after edit -> updated body"
|
||||
else
|
||||
fail "nodejs invoke after edit did not return updated body"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo ">>> T07: PHP и Ruby в одном namespace"
|
||||
PHP_CODE=$(python3 -c 'import json; print(json.dumps("<?php\nfunction handler($context) {\n $context[\"response\"]->getBody()->write(\"php-ui-ok\");\n}"))')
|
||||
RUBY_CODE=$(python3 -c 'import json; print(json.dumps("def handler\n \"ruby-ui-ok\"\nend"))')
|
||||
|
||||
R=$(create_call "$STACK_SUB" "{\"name\":\"php-ui\",\"language\":\"php\",\"entrypoint\":\"main.php::handler\",\"code\":${PHP_CODE}}")
|
||||
CODE=$(printf '%s' "$R" | tail -1)
|
||||
[ "$CODE" = "201" ] && pass "php create -> 201" || fail "php create -> got $CODE"
|
||||
if wait_invoke_ok "$STACK_SUB" "php-ui" "php-ui-ok" 24 10; then
|
||||
pass "php invoke -> 200"
|
||||
else
|
||||
fail "php invoke did not stabilize"
|
||||
fi
|
||||
|
||||
R=$(create_call "$STACK_SUB" "{\"name\":\"ruby-ui\",\"language\":\"ruby\",\"entrypoint\":\"handler\",\"code\":${RUBY_CODE}}")
|
||||
CODE=$(printf '%s' "$R" | tail -1)
|
||||
[ "$CODE" = "201" ] && pass "ruby create -> 201" || fail "ruby create -> got $CODE"
|
||||
if wait_invoke_ok "$STACK_SUB" "ruby-ui" "ruby-ui-ok" 24 10; then
|
||||
pass "ruby invoke -> 200"
|
||||
else
|
||||
fail "ruby invoke did not stabilize"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo ">>> T08: плохой entrypoint как пользовательский промах"
|
||||
R=$(create_call "$NEG_SUB" '{"name":"node-bad-entry","language":"nodejs","entrypoint":"missing","code":"module.exports = async function() { return { status: 200, body: \"bad\" }; }"}')
|
||||
CODE=$(printf '%s' "$R" | tail -1)
|
||||
[ "$CODE" = "201" ] && pass "bad entrypoint function created" || fail "bad entrypoint create -> got $CODE"
|
||||
RESP=$(invoke_call "$NEG_SUB" "node-bad-entry")
|
||||
STATUS=$(json_field "$RESP" status)
|
||||
ERR=$(json_field "$RESP" error)
|
||||
[ "$STATUS" != "200" ] && pass "bad entrypoint invoke fails as expected" || fail "bad entrypoint unexpectedly invoked"
|
||||
printf '%s' "$ERR" | grep -Eiq 'timeout|specialization|error|not found' && pass "bad entrypoint error is visible" || fail "bad entrypoint error text weak: ${ERR}"
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo " RESULT: PASS=${PASS} FAIL=${FAIL}"
|
||||
echo "========================================"
|
||||
|
||||
[ "$FAIL" -eq 0 ]
|
||||
Reference in New Issue
Block a user