#!/usr/bin/env bash set -euo pipefail API_URL="${API_URL:-http://localhost:3000}" WEB_URL="${WEB_URL:-http://localhost:3001}" RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' pass() { echo -e "${GREEN}✓ $1${NC}"; } fail() { echo -e "${RED}✗ $1${NC}"; exit 1; } step() { echo -e "${YELLOW}→ $1${NC}"; } step "1. Health check" HEALTH=$(curl -sf "$API_URL/health" 2>/dev/null) echo "$HEALTH" | grep -q '"status":"ok"' && pass "API is healthy" || fail "API health check failed" step "2. Login as admin" LOGIN_RESP=$(curl -sf -X POST "$API_URL/api/v1/auth/login" \ -H "Content-Type: application/json" \ -d '{"email":"admin@fleetforge.local","password":"admin123"}') TOKEN=$(echo "$LOGIN_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['token'])" 2>/dev/null) [ -n "$TOKEN" ] && pass "Login successful, got token" || fail "Login failed" step "3. Get current user" ME_RESP=$(curl -sf "$API_URL/api/v1/auth/me" -H "Authorization: Bearer $TOKEN") echo "$ME_RESP" | grep -q '"email":"admin@fleetforge.local"' && pass "Authenticated as admin" || fail "Auth check failed" step "4. Create enrollment token" ENROLL_RESP=$(curl -sf -X POST "$API_URL/api/v1/enrollment-tokens" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"label":"e2e-test","expiresInHours":1,"maxUses":5}') RAW_TOKEN=$(echo "$ENROLL_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['token'])" 2>/dev/null) [ -n "$RAW_TOKEN" ] && pass "Enrollment token created" || fail "Token creation failed" step "5. List devices (should be empty)" DEVICES_RESP=$(curl -sf "$API_URL/api/v1/devices" -H "Authorization: Bearer $TOKEN") TOTAL=$(echo "$DEVICES_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['meta']['total'])" 2>/dev/null) [ "$TOTAL" = "0" ] && pass "No devices yet (total=0)" || fail "Expected 0 devices, got $TOTAL" step "6. Simulate device enrollment" ENROLL_AGENT_RESP=$(curl -sf -X POST "$API_URL/api/v1/agent/enroll" \ -H "Content-Type: application/json" \ -d "{ \"token\": \"$RAW_TOKEN\", \"hostname\": \"e2e-test-server\", \"platform\": \"linux\", \"os_name\": \"Ubuntu\", \"os_version\": \"22.04\", \"architecture\": \"amd64\", \"kernel_version\": \"5.15.0\", \"cpu_model\": \"Intel Xeon\", \"cpu_cores\": 4, \"total_memory_bytes\": 8589934592, \"total_disk_bytes\": 107374182400, \"agent_version\": \"0.1.0\" }") DEVICE_ID=$(echo "$ENROLL_AGENT_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['device_id'])" 2>/dev/null) DEVICE_CRED=$(echo "$ENROLL_AGENT_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['credential'])" 2>/dev/null) [ -n "$DEVICE_ID" ] && pass "Device enrolled: $DEVICE_ID" || fail "Enrollment failed" step "7. Verify device appears in list" sleep 1 DEVICES_RESP=$(curl -sf "$API_URL/api/v1/devices" -H "Authorization: Bearer $TOKEN") TOTAL=$(echo "$DEVICES_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['meta']['total'])" 2>/dev/null) [ "$TOTAL" = "1" ] && pass "Device visible (total=1)" || fail "Expected 1 device, got $TOTAL" step "8. Get device details" DEVICE_RESP=$(curl -sf "$API_URL/api/v1/devices/$DEVICE_ID" -H "Authorization: Bearer $TOKEN") echo "$DEVICE_RESP" | grep -q '"name":"e2e-test-server"' && pass "Device name correct" || fail "Device name mismatch" step "9. Send heartbeat with metrics" HB_RESP=$(curl -sf -X POST "$API_URL/api/v1/agent/heartbeat" \ -H "Authorization: Bearer $DEVICE_CRED" \ -H "Content-Type: application/json" \ -d '{ \"hostname\": \"e2e-test-server\", \"os_version\": \"22.04\", \"kernel_version\": \"5.15.0-generic\", \"agent_version\": \"0.1.0\", \"ip_address\": \"192.168.1.100\", \"metrics\": { \"cpu_usage_percent\": 42.5, \"memory_usage_percent\": 67.3, \"disk_usage_percent\": 35.1, \"load_average_1m\": 1.5, \"load_average_5m\": 1.2, \"load_average_15m\": 0.8, \"uptime_seconds\": 86400 } }') echo "$HB_RESP" | grep -q '"success":true' && pass "Heartbeat sent" || fail "Heartbeat failed" step "10. Verify metrics stored" METRICS_RESP=$(curl -sf "$API_URL/api/v1/devices/$DEVICE_ID/metrics" -H "Authorization: Bearer $TOKEN") METRIC_COUNT=$(echo "$METRICS_RESP" | python3 -c "import sys,json; print(len(json.load(sys.stdin)['data']))" 2>/dev/null) [ "$METRIC_COUNT" -ge "1" ] && pass "Metrics recorded ($METRIC_COUNT snapshots)" || fail "No metrics found" step "11. Create a job" JOB_RESP=$(curl -sf -X POST "$API_URL/api/v1/jobs" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d "{ \"jobType\": \"update_inventory\", \"deviceIds\": [\"$DEVICE_ID\"], \"parameters\": {}, \"expiresInMinutes\": 60 }") JOB_ID=$(echo "$JOB_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['id'])" 2>/dev/null) [ -n "$JOB_ID" ] && pass "Job created: $JOB_ID" || fail "Job creation failed" step "12. Agent fetches jobs" sleep 1 AGENT_JOBS_RESP=$(curl -sf "$API_URL/api/v1/agent/jobs" \ -H "Authorization: Bearer $DEVICE_CRED") JOB_COUNT=$(echo "$AGENT_JOBS_RESP" | python3 -c "import sys,json; print(len(json.load(sys.stdin)['data']))" 2>/dev/null) [ "$JOB_COUNT" -ge "1" ] && pass "Agent found $JOB_COUNT pending job(s)" || fail "No jobs found for agent" step "13. Agent submits job result" TARGET_ID=$(echo "$AGENT_JOBS_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['data'][0]['targetId'])" 2>/dev/null) RESULT_RESP=$(curl -sf -X POST "$API_URL/api/v1/agent/jobs/$JOB_ID/result" \ -H "Authorization: Bearer $DEVICE_CRED" \ -H "Content-Type: application/json" \ -d "{ \"target_id\": \"$TARGET_ID\", \"status\": \"succeeded\", \"result\": {\"packages\": 42, \"services\": 5} }") echo "$RESULT_RESP" | grep -q '"success":true' && pass "Job result submitted" || fail "Result submission failed" step "14. Verify job completed" JOB_DETAIL=$(curl -sf "$API_URL/api/v1/jobs/$JOB_ID" -H "Authorization: Bearer $TOKEN") JOB_STATUS=$(echo "$JOB_DETAIL" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['status'])" 2>/dev/null) [ "$JOB_STATUS" = "succeeded" ] && pass "Job status: succeeded" || fail "Expected succeeded, got $JOB_STATUS" step "15. Check audit log" AUDIT_RESP=$(curl -sf "$API_URL/api/v1/audit-events" -H "Authorization: Bearer $TOKEN") AUDIT_TOTAL=$(echo "$AUDIT_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['meta']['total'])" 2>/dev/null) [ "$AUDIT_TOTAL" -ge "1" ] && pass "Audit events recorded ($AUDIT_TOTAL)" || fail "No audit events" step "16. Check dashboard stats" DASH_RESP=$(curl -sf "$API_URL/api/v1/dashboard/stats" -H "Authorization: Bearer $TOKEN") TOTAL_DEVICES=$(echo "$DASH_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['totalDevices'])" 2>/dev/null) ONLINE=$(echo "$DASH_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['onlineDevices'])" 2>/dev/null) [ "$TOTAL_DEVICES" = "1" ] && [ "$ONLINE" = "1" ] && pass "Dashboard shows 1 device online" || fail "Dashboard mismatch" step "17. Test unauthorized access" UNAUTH=$(curl -s -o /dev/null -w "%{http_code}" "$API_URL/api/v1/devices" -H "Authorization: Bearer invalid-token") [ "$UNAUTH" = "401" ] && pass "Unauthorized access correctly rejected (401)" || fail "Expected 401, got $UNAUTH" step "18. Test rate limiting protection" echo -e "${GREEN}✓ All 18 E2E tests passed!${NC}"