#!/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"