Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,18 @@ Run it against a local backend:
./scripts/smoke-test.sh http://localhost:8080
```

Local Compose and staging runs can keep using one backend URL. For an ingress
deployment where the public URL exposes application APIs but keeps Actuator on
the backend service, set a separate Actuator target:

```bash
ACTUATOR_BASE_URL=http://skillhub-server:8080 \
./scripts/smoke-test.sh https://skillhub.example.com
```

The health check requires an Actuator JSON response, so an HTML SPA fallback is
reported as a routing or target error instead of a successful health response.

Admin label-management smoke checks run only when current admin credentials are
supplied explicitly:

Expand Down
63 changes: 58 additions & 5 deletions scripts/smoke-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@
set -euo pipefail

BASE_URL="${1:-http://localhost:8080}"
ACTUATOR_BASE_URL="${ACTUATOR_BASE_URL:-$BASE_URL}"
PASS=0
FAIL=0
COOKIE_JAR="$(mktemp)"
TMP_DIR="$(mktemp -d)"
COOKIE_JAR="$TMP_DIR/cookies"
USERNAME="smoketest_$(date +%s)"
EMAIL="${USERNAME}@example.com"
PASSWORD="Smoke@2026"
NEW_PASSWORD="Smoke@2027"

cleanup() {
rm -f "$COOKIE_JAR"
rm -rf "$TMP_DIR"
}

trap cleanup EXIT
Expand All @@ -31,18 +33,69 @@ check() {
fi
}

check_health() {
local desc="$1"
local url="$2"
local body_file="$TMP_DIR/health-body"
local result
local status
local content_type
result="$(curl --retry 3 --retry-delay 1 --max-time 10 -sS -o "$body_file" \
-w "%{http_code}|%{content_type}" "$url" || true)"
status="${result%%|*}"
content_type="${result#*|}"

if [[ "$content_type" == text/html* ]]; then
echo "FAIL: $desc (routing/target error: received $content_type from $url)"
FAIL=$((FAIL + 1))
elif [[ "$status" == "200" \
&& ( "$content_type" == application/json* || "$content_type" == application/*+json* ) \
&& -f "$body_file" \
&& "$(grep -Ec '"status"[[:space:]]*:' "$body_file" || true)" -gt 0 ]]; then
echo "PASS: $desc (HTTP $status, $content_type)"
PASS=$((PASS + 1))
else
echo "FAIL: $desc (expected HTTP 200 actuator JSON, got HTTP $status, ${content_type:-no content type})"
FAIL=$((FAIL + 1))
fi
}

check_protected_actuator() {
local desc="$1"
local url="$2"
local result
local status
local content_type
result="$(curl --retry 3 --retry-delay 1 --max-time 10 -sS -o /dev/null \
-w "%{http_code}|%{content_type}" "$url" || true)"
status="${result%%|*}"
content_type="${result#*|}"

if [[ "$content_type" == text/html* ]]; then
echo "FAIL: $desc (routing/target error: received $content_type from $url)"
FAIL=$((FAIL + 1))
elif [[ "$status" == "401" ]]; then
echo "PASS: $desc (HTTP $status)"
PASS=$((PASS + 1))
else
echo "FAIL: $desc (expected 401, got $status)"
FAIL=$((FAIL + 1))
fi
}

finish() {
echo
echo "Results: $PASS passed, $FAIL failed"
[[ "$FAIL" -eq 0 ]]
}

echo "=== SkillHub Smoke Test ==="
echo "Target: $BASE_URL"
echo "API target: $BASE_URL"
echo "Actuator target: $ACTUATOR_BASE_URL"
echo

check "Health endpoint" "$BASE_URL/actuator/health" "200"
check "Prometheus metrics requires auth" "$BASE_URL/actuator/prometheus" "401"
check_health "Health endpoint" "$ACTUATOR_BASE_URL/actuator/health"
check_protected_actuator "Prometheus metrics requires auth" "$ACTUATOR_BASE_URL/actuator/prometheus"
check "Namespaces API requires auth" "$BASE_URL/api/v1/namespaces" "401"
check "Auth required" "$BASE_URL/api/v1/auth/me" "401"

Expand Down
51 changes: 45 additions & 6 deletions scripts/tests/smoke-test-admin-mode-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ data=""
cookie_in=""
cookie_out=""
write_code=false
write_format=""
output_file=""
while (($#)); do
case "$1" in
Expand All @@ -47,6 +48,7 @@ while (($#)); do
;;
-w)
write_code=true
write_format="$2"
shift 2
;;
-o)
Expand Down Expand Up @@ -76,8 +78,16 @@ fi
printf '%s\n' "$method $url $data" >>"${SMOKE_CURL_LOG:?SMOKE_CURL_LOG is required}"

status=200
content_type="application/json"
body='{}'
case "$url" in
*/actuator/health) status=200 ;;
https://public.example/actuator/health|https://public.example/actuator/prometheus)
content_type="text/html"
body='<html>SkillHub</html>'
;;
*/actuator/health)
body='{"status":"UP"}'
;;
*/actuator/prometheus) status=401 ;;
*/api/v1/namespaces)
if [[ -n "$cookie_in" && -f "$cookie_in.session" ]]; then status=200; else status=401; fi
Expand Down Expand Up @@ -108,24 +118,35 @@ case "$url" in
esac

if [[ "$output_file" != "/dev/null" && -n "$output_file" ]]; then
printf '{}\n' >"$output_file"
printf '%s\n' "$body" >"$output_file"
fi
if [[ "$write_code" == true ]]; then
printf '%s' "$status"
if [[ "$write_format" == *content_type* ]]; then
printf '%s|%s' "$status" "$content_type"
else
printf '%s' "$status"
fi
fi
EOF
chmod +x "$TMP_DIR/bin/curl"

run_smoke() {
run_smoke_at() {
local name="$1"
shift
local base_url="$2"
shift 2
local log="$TMP_DIR/$name.curl.log"
local out="$TMP_DIR/$name.out"
local status=0
env PATH="$TMP_DIR/bin:$PATH" SMOKE_CURL_LOG="$log" "$@" "$SMOKE_SCRIPT" http://skillhub.test >"$out" 2>&1 || status=$?
env PATH="$TMP_DIR/bin:$PATH" SMOKE_CURL_LOG="$log" "$@" "$SMOKE_SCRIPT" "$base_url" >"$out" 2>&1 || status=$?
printf '%s\n' "$status"
}

run_smoke() {
local name="$1"
shift
run_smoke_at "$name" http://skillhub.test "$@"
}

status="$(run_smoke skip-admin env)"
[[ "$status" == "0" ]] || fail "default smoke without admin credentials should pass"
grep -Fq "SKIP: Admin label management" "$TMP_DIR/skip-admin.out" \
Expand Down Expand Up @@ -153,4 +174,22 @@ if grep -Fq 'ChangeMe!2026' "$TMP_DIR/explicit-admin.curl.log"; then
fail "admin login must not fall back to the bootstrap default password"
fi

status="$(run_smoke_at split-targets https://public.example env \
ACTUATOR_BASE_URL=http://actuator.internal:8080 SMOKE_ADMIN_CHECKS=false)"
[[ "$status" == "0" ]] || fail "split public and actuator targets should pass"
grep -Fq "GET http://actuator.internal:8080/actuator/health" "$TMP_DIR/split-targets.curl.log" \
|| fail "health check should use ACTUATOR_BASE_URL"
grep -Fq "GET http://actuator.internal:8080/actuator/prometheus" "$TMP_DIR/split-targets.curl.log" \
|| fail "Prometheus check should use ACTUATOR_BASE_URL"
if grep -Fq "https://public.example/actuator/" "$TMP_DIR/split-targets.curl.log"; then
fail "actuator checks must not use the public API target when ACTUATOR_BASE_URL is set"
fi
grep -Fq "GET https://public.example/api/v1/auth/me" "$TMP_DIR/split-targets.curl.log" \
|| fail "application API checks should continue using BASE_URL"

status="$(run_smoke_at html-fallback https://public.example env SMOKE_ADMIN_CHECKS=false)"
[[ "$status" != "0" ]] || fail "HTML SPA fallback must not pass as actuator health"
grep -Fq "routing/target error: received text/html" "$TMP_DIR/html-fallback.out" \
|| fail "HTML fallback should produce an actionable routing/target error"

echo "smoke-test-admin-mode-test passed"
Loading