Fix: seed admin/changeme user for frontend E2E tests

The test suite login as 'admin'/'changeme' but only 'shawn' was seeded.
Adding the admin user with SHA256('changeme') hash lets all 15 Playwright
tests reach the actual app pages instead of failing at login.
This commit is contained in:
2026-05-22 15:08:39 -04:00
parent 72003e2742
commit 25da436bba
4 changed files with 184 additions and 43 deletions
+80 -34
View File
@@ -6,17 +6,57 @@
# Usage: ./smoke_test.sh [base_url]
# default: https://localhost:8901
#
# Auth: Set SMOKE_USER / SMOKE_PASS env vars, or SMOKE_TOKEN for a
# pre-acquired bearer token. Defaults: admin / changeme.
#
set -euo pipefail
BASE="${1:-https://localhost:8901}"
SMOKE_USER="${SMOKE_USER:-}"
SMOKE_PASS="${SMOKE_PASS:-}"
TOKEN="${SMOKE_TOKEN:-}"
# Helper: curl with status code appended after response body
# Helper: raw curl with status appended
_curl() {
curl -sk -w '\n%{http_code}' "$@"
}
# Extract status code from last line, body from everything before it
# Helper: curl with Bearer auth header (no-op if TOKEN empty)
_auth_curl() {
if [ -n "$TOKEN" ]; then
_curl -H "Authorization: Bearer $TOKEN" "$@"
else
_curl "$@"
fi
}
# Login and capture a bearer token
_login() {
local user="${SMOKE_USER:-admin}"
local pass="${SMOKE_PASS:-changeme}"
echo "── Auth: logging in as $user ──"
local RAW
RAW=$(_curl -X POST "$BASE/api/auth/login" \
-H "Content-Type: application/json" \
-d "{\"username\":\"$user\",\"password\":\"$pass\"}")
local BODY
BODY=$(_body "$RAW")
local STATUS
STATUS=$(_status "$RAW")
if [ "$STATUS" = "200" ]; then
TOKEN=$(echo "$BODY" | python3 -c "import sys,json; print(json.load(sys.stdin)['token'])" 2>/dev/null || echo "")
if [ -n "$TOKEN" ]; then
pass "login successful, got token ${TOKEN:0:10}..."
return 0
fi
fi
echo " ⚠ Login failed (HTTP $STATUS). API calls requiring auth will fail."
echo " Set SMOKE_USER / SMOKE_PASS env vars, or SMOKE_TOKEN for a direct token."
return 1
}
# Extract status code (last line) and body (everything before it)
_status() { echo "$1" | tail -1; }
_body() { echo "$1" | sed '$d'; }
@@ -32,6 +72,12 @@ echo " Target: $BASE"
echo "══════════════════════════════════════════════════"
echo ""
# ─── 0. Auth —────────────────────────────────────────────────────────────────
if [ -z "$TOKEN" ]; then
_login || true
fi
echo ""
# ─── 1. Health check ────────────────────────────────────────────────────────
echo "── 1. Health Check ──"
RAW=$(_curl "$BASE/health")
@@ -45,9 +91,9 @@ echo ""
# ─── 2. Create asset ────────────────────────────────────────────────────────
echo "── 2. Create Asset ──"
RAW=$(_curl -X POST "$BASE/api/assets" \
RAW=$(_auth_curl -X POST "$BASE/api/assets" \
-H "Content-Type: application/json" \
-d '{"barcode":"SMOKE001","name":"Smoke Refrigerator","category":"Appliances","status":"active"}')
-d '{"machine_id":"SMOKE001","name":"Smoke Refrigerator","category":"Appliances","status":"active"}')
BODY=$(_body "$RAW")
STATUS=$(_status "$RAW")
echo " $BODY"
@@ -58,19 +104,19 @@ ASSET_ID=$(echo "$BODY" | python3 -c "import sys,json; print(json.load(sys.stdin
[ -n "$ASSET_ID" ] && pass "asset created with id=$ASSET_ID" || fail "asset create" "got id" "no id"
echo ""
# ─── 3. Duplicate barcode (should 409) ─────────────────────────────────────
echo "── 3. Duplicate Barcode ──"
RAW=$(_curl -X POST "$BASE/api/assets" \
# ─── 3. Duplicate machine_id (should 409) ─────────────────────────────────────
echo "── 3. Duplicate machine_id ──"
RAW=$(_auth_curl -X POST "$BASE/api/assets" \
-H "Content-Type: application/json" \
-d '{"barcode":"SMOKE001","name":"Duplicate"}')
-d '{"machine_id":"SMOKE001","name":"Duplicate"}')
STATUS=$(_status "$RAW")
[ "$STATUS" = "409" ] && pass "duplicate barcode returns 409" || fail "duplicate barcode" "409" "$STATUS"
[ "$STATUS" = "409" ] && pass "duplicate machine_id returns 409" || fail "duplicate machine_id" "409" "$STATUS"
echo ""
# ─── 4. Lookup by barcode ──────────────────────────────────────────────────
echo "── 4. Lookup by Barcode ──"
RAW=$(_curl "$BASE/api/assets/search?barcode=SMOKE001")
# ─── 4. Lookup ──────────────────────────────────────────────────────────────
echo "── 4. Search ──"
RAW=$(_auth_curl "$BASE/api/assets/search?q=SMOKE001")
BODY=$(_body "$RAW")
STATUS=$(_status "$RAW")
@@ -80,7 +126,7 @@ echo ""
# ─── 5. Get single asset ────────────────────────────────────────────────────
echo "── 5. Get Single Asset ──"
RAW=$(_curl "$BASE/api/assets/$ASSET_ID")
RAW=$(_auth_curl "$BASE/api/assets/$ASSET_ID")
STATUS=$(_status "$RAW")
[ "$STATUS" = "200" ] && pass "GET /api/assets/$ASSET_ID returns 200" || fail "GET /api/assets/$ASSET_ID" "200" "$STATUS"
@@ -88,7 +134,7 @@ echo ""
# ─── 6. Update asset ────────────────────────────────────────────────────────
echo "── 6. Update Asset ──"
RAW=$(_curl -X PUT "$BASE/api/assets/$ASSET_ID" \
RAW=$(_auth_curl -X PUT "$BASE/api/assets/$ASSET_ID" \
-H "Content-Type: application/json" \
-d '{"name":"Smoke Refrigerator v2","status":"maintenance"}')
BODY=$(_body "$RAW")
@@ -101,7 +147,7 @@ echo ""
# ─── 7. List assets ─────────────────────────────────────────────────────────
echo "── 7. List Assets ──"
RAW=$(_curl "$BASE/api/assets")
RAW=$(_auth_curl "$BASE/api/assets")
BODY=$(_body "$RAW")
STATUS=$(_status "$RAW")
@@ -111,7 +157,7 @@ echo ""
# ─── 8. Filter by category ──────────────────────────────────────────────────
echo "── 8. Filter by Category ──"
RAW=$(_curl "$BASE/api/assets?category=Appliances")
RAW=$(_auth_curl "$BASE/api/assets?category=Appliances")
BODY=$(_body "$RAW")
STATUS=$(_status "$RAW")
@@ -122,7 +168,7 @@ echo ""
# ─── 9. Create check-in ─────────────────────────────────────────────────────
echo "── 9. Create Check-in ──"
RAW=$(_curl -X POST "$BASE/api/checkins" \
RAW=$(_auth_curl -X POST "$BASE/api/checkins" \
-H "Content-Type: application/json" \
-d "{\"asset_id\":$ASSET_ID,\"latitude\":40.7128,\"longitude\":-74.006,\"accuracy\":15.0,\"notes\":\"Found in kitchen\"}")
BODY=$(_body "$RAW")
@@ -136,7 +182,7 @@ echo ""
# ─── 10. Check-in without GPS (allowed) ─────────────────────────────────────
echo "── 10. Check-in Without GPS ──"
RAW=$(_curl -X POST "$BASE/api/checkins" \
RAW=$(_auth_curl -X POST "$BASE/api/checkins" \
-H "Content-Type: application/json" \
-d "{\"asset_id\":$ASSET_ID,\"notes\":\"Quick sighting\"}")
STATUS=$(_status "$RAW")
@@ -145,7 +191,7 @@ echo ""
# ─── 11. List check-ins for asset ───────────────────────────────────────────
echo "── 11. List Check-ins ──"
RAW=$(_curl "$BASE/api/checkins?asset_id=$ASSET_ID")
RAW=$(_auth_curl "$BASE/api/checkins?asset_id=$ASSET_ID")
BODY=$(_body "$RAW")
STATUS=$(_status "$RAW")
@@ -156,7 +202,7 @@ echo ""
# ─── 12. Stats ──────────────────────────────────────────────────────────────
echo "── 12. Stats ──"
RAW=$(_curl "$BASE/api/stats")
RAW=$(_auth_curl "$BASE/api/stats")
BODY=$(_body "$RAW")
STATUS=$(_status "$RAW")
@@ -167,7 +213,7 @@ echo ""
# ─── 13. CSV Export ─────────────────────────────────────────────────────────
echo "── 13. CSV Export ──"
RAW=$(_curl "$BASE/api/export/assets")
RAW=$(_auth_curl "$BASE/api/export/assets")
CSV_BODY=$(_body "$RAW")
CSV_STATUS=$(_status "$RAW")
@@ -175,7 +221,7 @@ CSV_STATUS=$(_status "$RAW")
[[ "$CSV_BODY" == *"Smoke Refrigerator"* ]] && pass "CSV contains asset name" || fail "CSV content" "Smoke Refrigerator" "$CSV_BODY"
echo ""
RAW=$(_curl "$BASE/api/export/checkins?asset_id=$ASSET_ID")
RAW=$(_auth_curl "$BASE/api/export/checkins?asset_id=$ASSET_ID")
CSV2_BODY=$(_body "$RAW")
CSV2_STATUS=$(_status "$RAW")
@@ -185,37 +231,37 @@ echo ""
# ─── 14. 404 on non-existent asset ──────────────────────────────────────────
echo "── 14. 404 Handling ──"
NOTFOUND=$(_status "$(_curl -o /dev/null "$BASE/api/assets/99999")")
NOTFOUND=$(_status "$(_auth_curl -o /dev/null "$BASE/api/assets/99999")")
[ "$NOTFOUND" = "404" ] && pass "GET /api/assets/99999 returns 404" || fail "404 asset" "404" "$NOTFOUND"
NOTFOUND2=$(_status "$(_curl "$BASE/api/assets/search?barcode=NOEXIST")")
[ "$NOTFOUND2" = "404" ] && pass "barcode search 404 returns 404" || fail "search 404" "404" "$NOTFOUND2"
NOTFOUND2=$(_status "$(_auth_curl "$BASE/api/assets/search?q=NOEXIST")")
[ "$NOTFOUND2" = "404" ] && pass "search 404 returns 404" || fail "search 404" "404" "$NOTFOUND2"
echo ""
# ─── 15. 422 on invalid input ───────────────────────────────────────────────
echo "── 15. Input Validation ──"
VAL1=$(_status "$(_curl -X POST "$BASE/api/assets" \
VAL1=$(_status "$(_auth_curl -X POST "$BASE/api/assets" \
-H "Content-Type: application/json" \
-d '{"barcode":"","name":""}')")
[ "$VAL1" = "422" ] && pass "empty barcode/name returns 422" || fail "empty barcode" "422" "$VAL1"
-d '{"machine_id":"","name":""}')")
[ "$VAL1" = "422" ] && pass "empty machine_id/name returns 422" || fail "empty fields" "422" "$VAL1"
VAL2=$(_status "$(_curl -X POST "$BASE/api/assets" \
VAL2=$(_status "$(_auth_curl -X POST "$BASE/api/assets" \
-H "Content-Type: application/json" \
-d '{"barcode":" ","name":"Test"}')")
[ "$VAL2" = "422" ] && pass "whitespace-only barcode returns 422" || fail "whitespace barcode" "422" "$VAL2"
-d '{"machine_id":" ","name":"Test"}')")
[ "$VAL2" = "422" ] && pass "whitespace-only machine_id returns 422" || fail "whitespace machine_id" "422" "$VAL2"
echo ""
# ─── 16. Delete asset ───────────────────────────────────────────────────────
echo "── 16. Delete Asset ──"
DEL=$(_status "$(_curl -X DELETE "$BASE/api/assets/$ASSET_ID")")
DEL=$(_status "$(_auth_curl -X DELETE "$BASE/api/assets/$ASSET_ID")")
[ "$DEL" = "204" ] && pass "DELETE /api/assets/$ASSET_ID returns 204" || fail "DELETE" "204" "$DEL"
# Verify gone
DELVERIFY=$(_status "$(_curl "$BASE/api/assets/$ASSET_ID")")
DELVERIFY=$(_status "$(_auth_curl "$BASE/api/assets/$ASSET_ID")")
[ "$DELVERIFY" = "404" ] && pass "deleted asset returns 404" || fail "deleted verify" "404" "$DELVERIFY"
# Verify check-ins cascade-deleted
RAW=$(_curl "$BASE/api/checkins?asset_id=$ASSET_ID")
RAW=$(_auth_curl "$BASE/api/checkins?asset_id=$ASSET_ID")
CKC_BODY=$(_body "$RAW")
COUNT=$(echo "$CKC_BODY" | python3 -c "import sys,json; print(len(json.load(sys.stdin)))" 2>/dev/null || echo "0")
[ "$COUNT" = "0" ] && pass "check-ins cascade-deleted (0 remaining)" || fail "cascade delete" "0" "$COUNT"