Files
sase.tr/scripts/test-pl24-flow.sh
Sase Dev 996d614d50 feat: admin user creation, Vite migration, dialog fix, pl24 integration
- Add POST /admin/users endpoint with password hashing and role support
- Add user creation dialog to admin users page
- Migrate web from Next.js to Vite + TanStack Router
- Fix Dialog component positioning for Tailwind CSS v4
- Add @source directive for @sase/ui package scanning
- Add pl24 integration parsers and vehicle decode flow
- Backup old Next.js app to apps/web-nj

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 14:24:58 +00:00

221 lines
7.4 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# PL24 Integration E2E Test Script
# Tests: Login → VIN Decode → Category Tree → Subgroups → Parts
#
set -euo pipefail
API="http://localhost:4000/api"
EMAIL="admin@sase.tr"
PASSWORD="Sase2026"
VIN="WVWZZZ1JZ3W597935"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
pass() { echo -e "${GREEN}$1${NC}"; }
fail() { echo -e "${RED}$1${NC}"; echo " $2"; exit 1; }
info() { echo -e "${CYAN}$1${NC}"; }
warn() { echo -e "${YELLOW}$1${NC}"; }
# ─── Step 0: Check API health ───
info "Checking API health..."
HEALTH=$(curl -sf "$API/health" 2>/dev/null || echo "")
if [ -z "$HEALTH" ]; then
fail "API not running" "Start with: pnpm --filter api dev"
fi
pass "API is running"
# ─── Step 1: Login ───
info "Logging in as $EMAIL..."
LOGIN_RESPONSE=$(curl -s -D - -X POST "$API/auth/sign-in/email" \
-H "Content-Type: application/json" \
-d "{\"email\":\"$EMAIL\",\"password\":\"$PASSWORD\"}" 2>&1)
COOKIE=$(echo "$LOGIN_RESPONSE" | grep -i 'set-cookie:.*session_token=' | head -1 | sed 's/.*set-cookie: //i' | cut -d';' -f1)
if [ -z "$COOKIE" ]; then
fail "Login failed" "$(echo "$LOGIN_RESPONSE" | tail -1)"
fi
pass "Logged in — cookie: ${COOKIE:0:40}..."
# ─── Step 2: Decode VIN ───
info "Decoding VIN: $VIN ..."
DECODE=$(curl -s -X POST "$API/vehicles/decode" \
-H "Cookie: $COOKIE" \
-H "Content-Type: application/json" \
-d "{\"vin\":\"$VIN\"}")
VEHICLE_ID=$(echo "$DECODE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('data',d).get('id','') if isinstance(d.get('data',d),dict) else '')" 2>/dev/null || echo "")
# Try flat response too
if [ -z "$VEHICLE_ID" ]; then
VEHICLE_ID=$(echo "$DECODE" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('id',''))" 2>/dev/null || echo "")
fi
if [ -z "$VEHICLE_ID" ]; then
fail "VIN decode failed" "$DECODE"
fi
BRAND=$(echo "$DECODE" | python3 -c "import json,sys; d=json.load(sys.stdin); v=d.get('data',d) if isinstance(d.get('data',d),dict) else d; print(f\"{v.get('brandName','')} {v.get('model','')} ({v.get('year','')})\")" 2>/dev/null || echo "unknown")
pass "VIN decoded → $BRAND (vehicle: $VEHICLE_ID)"
# ─── Step 3: Get Category Tree ───
info "Fetching category tree for vehicle $VEHICLE_ID ..."
TREE=$(curl -s "$API/categories/tree/$VEHICLE_ID" \
-H "Cookie: $COOKIE")
TREE_COUNT=$(echo "$TREE" | python3 -c "
import json,sys
d=json.load(sys.stdin)
items = d.get('data', d) if isinstance(d.get('data'), list) else (d if isinstance(d, list) else [])
print(len(items))
" 2>/dev/null || echo "0")
if [ "$TREE_COUNT" = "0" ]; then
fail "Category tree empty" "$TREE"
fi
pass "Category tree has $TREE_COUNT main groups"
# Print category names
echo "$TREE" | python3 -c "
import json,sys
d=json.load(sys.stdin)
items = d.get('data', d) if isinstance(d.get('data'), list) else (d if isinstance(d, list) else [])
for i, cat in enumerate(items[:5]):
print(f' {i+1}. {cat.get(\"name\",\"?\")} (id: {cat.get(\"id\",\"?\")[:8]}...)')
if len(items) > 5:
print(f' ... and {len(items)-5} more')
" 2>/dev/null
# ─── Step 4: Get Children (subgroups) of first category ───
FIRST_CAT_ID=$(echo "$TREE" | python3 -c "
import json,sys
d=json.load(sys.stdin)
items = d.get('data', d) if isinstance(d.get('data'), list) else (d if isinstance(d, list) else [])
print(items[0]['id'] if items else '')
" 2>/dev/null || echo "")
if [ -z "$FIRST_CAT_ID" ]; then
fail "No category ID found" ""
fi
info "Fetching children for category $FIRST_CAT_ID ..."
CHILDREN=$(curl -s "$API/categories/$FIRST_CAT_ID/children" \
-H "Cookie: $COOKIE")
CHILDREN_COUNT=$(echo "$CHILDREN" | python3 -c "
import json,sys
d=json.load(sys.stdin)
items = d.get('data', d) if isinstance(d.get('data'), list) else (d if isinstance(d, list) else [])
print(len(items))
" 2>/dev/null || echo "0")
if [ "$CHILDREN_COUNT" = "0" ]; then
warn "No subgroups returned for first category (may be a leaf)"
else
pass "Got $CHILDREN_COUNT subgroups"
# Check unique IDs
UNIQUE_IDS=$(echo "$CHILDREN" | python3 -c "
import json,sys
d=json.load(sys.stdin)
items = d.get('data', d) if isinstance(d.get('data'), list) else (d if isinstance(d, list) else [])
ids = [c['id'] for c in items]
print(f'{len(set(ids))}/{len(ids)}')
" 2>/dev/null || echo "?")
pass "Unique subgroup IDs: $UNIQUE_IDS"
# Print subgroups
echo "$CHILDREN" | python3 -c "
import json,sys
d=json.load(sys.stdin)
items = d.get('data', d) if isinstance(d.get('data'), list) else (d if isinstance(d, list) else [])
for i, sg in enumerate(items[:5]):
print(f' {i+1}. {sg.get(\"name\",\"?\")} (id: {sg.get(\"id\",\"?\")[:8]}...)')
if len(items) > 5:
print(f' ... and {len(items)-5} more')
" 2>/dev/null
fi
# ─── Step 5: Get category with parts (new endpoint) ───
# Use the first child (subgroup) if available, otherwise first main category
if [ "$CHILDREN_COUNT" != "0" ]; then
PARTS_CAT_ID=$(echo "$CHILDREN" | python3 -c "
import json,sys
d=json.load(sys.stdin)
items = d.get('data', d) if isinstance(d.get('data'), list) else (d if isinstance(d, list) else [])
print(items[0]['id'] if items else '')
" 2>/dev/null || echo "")
info "Fetching parts for subgroup $PARTS_CAT_ID ..."
else
PARTS_CAT_ID="$FIRST_CAT_ID"
info "Fetching parts for main category $PARTS_CAT_ID ..."
fi
PARTS_RESPONSE=$(curl -s "$API/vehicles/$VEHICLE_ID/categories/$PARTS_CAT_ID" \
-H "Cookie: $COOKIE")
echo "$PARTS_RESPONSE" | python3 -c "
import json,sys
d=json.load(sys.stdin)
data = d.get('data', d) if isinstance(d.get('data'), dict) else d
parts_count = len(data.get('parts', []))
pics_count = len(data.get('schemaPics', []))
hotspots_count = len(data.get('hotspots', []))
children_count = len(data.get('children', []))
name = data.get('name', '?')
print(f' Name: {name}')
print(f' Parts: {parts_count}')
print(f' Schema pics: {pics_count}')
print(f' Hotspots: {hotspots_count}')
print(f' Children: {children_count}')
if parts_count > 0:
first = data['parts'][0]
print(f' First part: {first.get(\"oemCode\",\"?\")} — {first.get(\"name\",\"?\")}')
" 2>/dev/null
PARTS_COUNT=$(echo "$PARTS_RESPONSE" | python3 -c "
import json,sys
d=json.load(sys.stdin)
data = d.get('data', d) if isinstance(d.get('data'), dict) else d
print(len(data.get('parts', [])))
" 2>/dev/null || echo "0")
CHILDREN_IN_RESPONSE=$(echo "$PARTS_RESPONSE" | python3 -c "
import json,sys
d=json.load(sys.stdin)
data = d.get('data', d) if isinstance(d.get('data'), dict) else d
print(len(data.get('children', [])))
" 2>/dev/null || echo "0")
if [ "$PARTS_COUNT" != "0" ]; then
pass "Got $PARTS_COUNT parts from category endpoint"
elif [ "$CHILDREN_IN_RESPONSE" != "0" ]; then
pass "Category is a parent with $CHILDREN_IN_RESPONSE children (no parts expected)"
else
warn "No parts and no children returned (may need deeper navigation)"
fi
# ─── Step 6: Frontend route check ───
info "Checking frontend routes..."
WEB_STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 2>/dev/null || echo "000")
if [ "$WEB_STATUS" = "200" ]; then
pass "Web frontend is running"
echo " Dashboard: http://localhost:3000/dashboard/search"
echo " Vehicle: http://localhost:3000/dashboard/vehicles/$VEHICLE_ID"
else
warn "Web frontend not running (status: $WEB_STATUS). Start with: pnpm --filter web dev"
fi
echo ""
echo -e "${GREEN}═══ Test complete ═══${NC}"
echo "Vehicle ID: $VEHICLE_ID"
echo "Test in browser: http://localhost:3000/dashboard/vehicles/$VEHICLE_ID"