#!/usr/bin/env bash # Manual E2E run against a live server on :8765. # Exercises the entire student happy path: register → skills → target role → # analyze → recommendations → checkpoint toggle → upgrade suggestions → apply # → re-analyze, plus the claim checks that live HTTP can see. set -u API="http://127.0.0.1:8765/api" say() { printf '\n\033[1;36m== %s ==\033[0m\n' "$*"; } ok() { printf '\033[32m[ok]\033[0m %s\n' "$*"; } fail(){ printf '\033[31m[FAIL]\033[0m %s\n' "$*"; } # Unique email so re-runs don't collide with the previous state. TAG="e2e$RANDOM" # --------------------------------------------------------------------------- # CLAIM #2 (weak password accepted) — live HTTP verification # --------------------------------------------------------------------------- say "CLAIM #2: weak password accepted at /register/" R=$(curl -s -o /tmp/r.json -w '%{http_code}' \ -H 'Content-Type: application/json' \ -d "{\"name\":\"w\",\"email\":\"weak_${TAG}@x.com\",\"password\":\"abc\",\"password_confirm\":\"abc\"}" \ "$API/auth/register/") [ "$R" = "201" ] && ok "weak password 'abc' was accepted (HTTP 201) — CLAIM CONFIRMED" \ || fail "unexpected HTTP $R" # --------------------------------------------------------------------------- # CLAIM #3 (email case-sensitive uniqueness) — live HTTP # --------------------------------------------------------------------------- say "CLAIM #3: email uniqueness is case-sensitive" UPPER="Case_${TAG}@x.com" LOWER="case_${TAG}@x.com" R1=$(curl -s -o /dev/null -w '%{http_code}' \ -H 'Content-Type: application/json' \ -d "{\"name\":\"u\",\"email\":\"$UPPER\",\"password\":\"StrongPass123!\",\"password_confirm\":\"StrongPass123!\"}" \ "$API/auth/register/") R2=$(curl -s -o /dev/null -w '%{http_code}' \ -H 'Content-Type: application/json' \ -d "{\"name\":\"l\",\"email\":\"$LOWER\",\"password\":\"StrongPass123!\",\"password_confirm\":\"StrongPass123!\"}" \ "$API/auth/register/") [ "$R1" = "201" ] && [ "$R2" = "201" ] && ok "Both $UPPER and $LOWER registered — CLAIM CONFIRMED" \ || fail "upper=$R1 lower=$R2" # --------------------------------------------------------------------------- # Full student happy path # --------------------------------------------------------------------------- say "REGISTER student" REG=$(curl -s -H 'Content-Type: application/json' \ -d "{\"name\":\"Student\",\"email\":\"student_${TAG}@x.com\",\"password\":\"StrongPass123!\",\"password_confirm\":\"StrongPass123!\"}" \ "$API/auth/register/") ACCESS=$(echo "$REG" | python -c "import json,sys;print(json.load(sys.stdin)['access'])") [ -n "$ACCESS" ] && ok "got JWT access token" || { fail "no access token"; exit 1; } H=(-H "Authorization: Bearer $ACCESS" -H 'Content-Type: application/json') say "LIST skills catalog" N_SKILLS=$(curl -s "${H[@]}" "$API/skills/?page_size=200" \ | python -c "import json,sys;d=json.load(sys.stdin);print(d['count'])") ok "skills count = $N_SKILLS" say "LIST roles" N_ROLES=$(curl -s "${H[@]}" "$API/roles/" \ | python -c "import json,sys;print(len(json.load(sys.stdin)))") ok "roles count = $N_ROLES" say "Pick Data Scientist role" DS_ID=$(curl -s "${H[@]}" "$API/roles/" \ | python -c "import json,sys;print(next(r['id'] for r in json.load(sys.stdin) if r['role_name']=='Data Scientist'))") ok "DS role_id = $DS_ID" say "SET TARGET ROLE -> Data Scientist" curl -s "${H[@]}" -d "{\"role_id\":$DS_ID}" "$API/target-role/" > /dev/null ok "target role set" say "SET USER SKILLS (Python=70, SQL=90)" PY_ID=$(curl -s "${H[@]}" "$API/skills/?page_size=200" \ | python -c "import json,sys;print(next(s['id'] for s in json.load(sys.stdin)['results'] if s['skill_name']=='Python'))") SQL_ID=$(curl -s "${H[@]}" "$API/skills/?page_size=200" \ | python -c "import json,sys;print(next(s['id'] for s in json.load(sys.stdin)['results'] if s['skill_name']=='SQL'))") curl -s "${H[@]}" -d "{\"skill_id\":$PY_ID,\"proficiency\":70}" "$API/user-skills/" > /dev/null curl -s "${H[@]}" -d "{\"skill_id\":$SQL_ID,\"proficiency\":90}" "$API/user-skills/" > /dev/null ok "Python=70, SQL=90 set" say "GAP ANALYSIS" GAP=$(curl -s "${H[@]}" "$API/analysis/") R=$(echo "$GAP" | python -c "import json,sys;d=json.load(sys.stdin);print(d['readiness'],d['band'],d['mandatory_cap_applied'])") ok "readiness/band/cap = $R" say "RECOMMENDATIONS" REC=$(curl -s "${H[@]}" "$API/recommendations/") N_GAPS=$(echo "$REC" | python -c "import json,sys;d=json.load(sys.stdin);print(len(d['recommendations']))") ok "gap-skill keys in recs = $N_GAPS" # Pick any skill that has recommendations for the checkpoint-toggle test. RES_TITLE=$(echo "$REC" | python -c " import json,sys d = json.load(sys.stdin) for sid, items in d['recommendations'].items(): if items: print(items[0]['resource_id'], items[0]['title']); break ") RES_ID=$(echo "$RES_TITLE" | awk '{print $1}') ok "top rec resource_id = $RES_ID" say "GET resource detail (checkpoints?)" DET=$(curl -s "${H[@]}" "$API/resources/$RES_ID/") N_CP=$(echo "$DET" | python -c "import json,sys;print(len(json.load(sys.stdin)['checkpoints']))") ok "checkpoints = $N_CP" if [ "$N_CP" -gt 0 ]; then say "TOGGLE all checkpoints" for CP in $(echo "$DET" | python -c "import json,sys;print(*(c['id'] for c in json.load(sys.stdin)['checkpoints']))"); do curl -s "${H[@]}" -X POST "$API/progress/checkpoint/$CP/toggle/" -d '{"completed":true}' > /dev/null done PROG=$(curl -s "${H[@]}" "$API/progress/resource/$RES_ID/" \ | python -c "import json,sys;d=json.load(sys.stdin);print(d['status'], d['progress'])") ok "resource status/progress after all toggles = $PROG" else say "MANUAL slider to 100 (no checkpoints on this resource)" curl -s "${H[@]}" -X POST "$API/progress/resource/$RES_ID/" -d '{"progress":100}' > /dev/null ok "marked COMPLETED via slider" fi say "UPGRADE SUGGESTIONS" SUG=$(curl -s "${H[@]}" "$API/progress/upgrade-suggestions/") N_SUG=$(echo "$SUG" | python -c "import json,sys;print(len(json.load(sys.stdin)['suggestions']))") ok "pending suggestions = $N_SUG" if [ "$N_SUG" -gt 0 ]; then FIRST_SKILL=$(echo "$SUG" | python -c "import json,sys;print(json.load(sys.stdin)['suggestions'][0]['skill_id'])") say "CLAIM #1: apply the upgrade THREE TIMES in a row on the same completion" for i in 1 2 3 4; do R=$(curl -s "${H[@]}" -X POST "$API/progress/upgrade-suggestions/$FIRST_SKILL/apply/") APPLIED=$(echo "$R" | python -c "import json,sys;d=json.load(sys.stdin);print(d.get('applied'), d.get('already_applied'), d.get('user_skill',{}).get('proficiency'))") ok "call #$i applied/already/prof = $APPLIED" done echo "→ If the spec held, only call #1 would show 'applied=True'." fi say "POST-UPGRADE GAP ANALYSIS" GAP2=$(curl -s "${H[@]}" "$API/analysis/") R2=$(echo "$GAP2" | python -c "import json,sys;d=json.load(sys.stdin);print(d['readiness'],d['band'],d['mandatory_cap_applied'])") ok "readiness/band/cap = $R2"