From 380a1b44d5ae0d024e96dc7a1db654f0b039d387 Mon Sep 17 00:00:00 2001 From: Max Mayfield Date: Mon, 2 Mar 2026 04:49:30 +0000 Subject: [PATCH] Fix integration-test.sh payloads to match correct Zod schemas and Fastify empty body requirements --- products/integration-test.sh | 173 +++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100755 products/integration-test.sh diff --git a/products/integration-test.sh b/products/integration-test.sh new file mode 100755 index 0000000..d19f9ec --- /dev/null +++ b/products/integration-test.sh @@ -0,0 +1,173 @@ +#!/bin/bash + +# Integration Test Script for dd0c Stack +# Goes beyond smoke tests: exercises actual product flows (CRUD, webhooks, etc.) + +set -euo pipefail + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +BASE_URL="${BASE_URL:-192.168.86.11}" +PROTOCOL="${PROTOCOL:-http}" +TS=$(date +%s) +TOTAL=0 +PASSED=0 + +check() { + local name=$1 status=$2 + ((TOTAL++)) || true + if [ "$status" = "pass" ]; then + echo -e " ${GREEN}✓${NC} $name" >&2 + ((PASSED++)) || true + else + echo -e " ${RED}✗${NC} $name" >&2 + fi +} + +url() { echo "${PROTOCOL}://${BASE_URL}:$1$2"; } + +auth() { + local port=$1 + local resp + resp=$(curl -s -X POST "$(url "$port" /api/v1/auth/signup)" \ + -H "Content-Type: application/json" \ + -d "{\"email\":\"integ-${TS}-${port}@dd0c.dev\",\"password\":\"integtest123!\",\"tenant_name\":\"Integ ${TS} ${port}\"}") + echo "$resp" | grep -o '"token":"[^"]*' | cut -d'"' -f4 +} + +api() { + local method=$1 port=$2 path=$3 token=$4 + shift 4 + curl -s -X "$method" "$(url "$port" "$path")" \ + -H "Authorization: Bearer $token" \ + -H "Content-Type: application/json" \ + "$@" 2>/dev/null +} + +echo -e "${YELLOW}dd0c Integration Tests — $(date -u '+%Y-%m-%d %H:%M UTC')${NC}" +echo -e "Target: ${PROTOCOL}://${BASE_URL}\n" + +# ─── P3 Alert Intelligence (:3003) ─── +echo -e "${YELLOW}P3 Alert Intelligence (:3003)${NC}" +ALERT_TOKEN=$(auth 3003) + +# Summary endpoint +resp=$(api GET 3003 /api/v1/summary "$ALERT_TOKEN") +echo "$resp" | grep -q "open_total" && check "Summary endpoint" "pass" || check "Summary endpoint" "fail" + +# Notification config +resp=$(api PUT 3003 /api/v1/notifications/slack "$ALERT_TOKEN" \ + -d '{"enabled":true,"config":{"webhook_url":"https://hooks.slack.com/test"},"min_severity":"high"}') +code=$(curl -s -o /dev/null -w "%{http_code}" -X PUT "$(url 3003 /api/v1/notifications/slack)" \ + -H "Authorization: Bearer $ALERT_TOKEN" -H "Content-Type: application/json" \ + -d '{"enabled":true,"config":{"webhook_url":"https://hooks.slack.com/test"},"min_severity":"high"}') +[ "$code" = "200" ] || [ "$code" = "201" ] && check "Set notification config" "pass" || check "Set notification config (HTTP $code)" "fail" + +# List notifications +resp=$(api GET 3003 /api/v1/notifications "$ALERT_TOKEN") +echo "$resp" | grep -q "slack" && check "List notifications" "pass" || check "List notifications" "fail" + +# ─── P4 Portal / Service Catalog (:3004) ─── +echo -e "\n${YELLOW}P4 Portal / Service Catalog (:3004)${NC}" +PORTAL_TOKEN=$(auth 3004) + +# Create a service +resp=$(api PUT 3004 /api/v1/services "$PORTAL_TOKEN" \ + -d '{"name":"payment-api","description":"Handles payments","owner":"platform-team","tier":"critical","language":"typescript","repo_url":"https://github.com/dd0c/payment-api","tags":{"env":"prod"}}') +echo "$resp" | grep -q "payment-api" && check "Create service" "pass" || check "Create service" "fail" + +# List services +resp=$(api GET 3004 /api/v1/services "$PORTAL_TOKEN") +echo "$resp" | grep -q "payment-api" && check "List services (has created)" "pass" || check "List services" "fail" + +# Ownership report +code=$(curl -s -o /dev/null -w "%{http_code}" "$(url 3004 /api/v1/ownership)" \ + -H "Authorization: Bearer $PORTAL_TOKEN") +[ "$code" = "200" ] && check "Ownership report" "pass" || check "Ownership report (HTTP $code)" "fail" + +# Search +code=$(curl -s -o /dev/null -w "%{http_code}" "$(url 3004 '/api/v1/search?q=payment')" \ + -H "Authorization: Bearer $PORTAL_TOKEN") +[ "$code" = "200" ] && check "Search services" "pass" || check "Search services (HTTP $code)" "fail" + +# ─── P5 Cost Anomaly (:3007) ─── +echo -e "\n${YELLOW}P5 Cost Anomaly (:3007)${NC}" +COST_TOKEN=$(auth 3007) + +# Ingest cost data +resp=$(api POST 3007 /api/v1/ingest "$COST_TOKEN" \ + -d '{"events":[{"account_id":"123456789012","resource_type":"ec2","region":"us-east-1","hourly_cost":42.50,"currency":"USD","timestamp":"2026-03-01T00:00:00Z"}]}') +code=$(curl -s -o /dev/null -w "%{http_code}" -X POST "$(url 3007 /api/v1/ingest)" \ + -H "Authorization: Bearer $COST_TOKEN" -H "Content-Type: application/json" \ + -d '{"events":[{"account_id":"123456789012","resource_type":"ec2","region":"us-east-1","hourly_cost":42.50,"currency":"USD","timestamp":"2026-03-01T00:00:00Z"}]}') +[ "$code" = "200" ] || [ "$code" = "201" ] && check "Ingest cost data" "pass" || check "Ingest cost data (HTTP $code)" "fail" + +# Dashboard +code=$(curl -s -o /dev/null -w "%{http_code}" "$(url 3007 /api/v1/dashboard)" \ + -H "Authorization: Bearer $COST_TOKEN") +[ "$code" = "200" ] && check "Dashboard" "pass" || check "Dashboard (HTTP $code)" "fail" + +# Governance +code=$(curl -s -o /dev/null -w "%{http_code}" "$(url 3007 /api/v1/governance)" \ + -H "Authorization: Bearer $COST_TOKEN") +[ "$code" = "200" ] && check "Governance rules" "pass" || check "Governance rules (HTTP $code)" "fail" + +# ─── P6 Runbook Automation (:3006) ─── +echo -e "\n${YELLOW}P6 Runbook Automation (:3006)${NC}" +RUN_TOKEN=$(auth 3006) + +# Create a runbook +resp=$(api POST 3006 /api/v1/runbooks "$RUN_TOKEN" \ + -d '{"name":"restart-nginx","description":"Restart nginx on web servers","yaml_content":"steps:\n - name: restart\n command: systemctl restart nginx\n requires_approval: true"}') +echo "$resp" | grep -q "restart-nginx\|id" && check "Create runbook" "pass" || check "Create runbook" "fail" + +# List runbooks +resp=$(api GET 3006 /api/v1/runbooks "$RUN_TOKEN") +echo "$resp" | grep -q "restart-nginx" && check "List runbooks (has created)" "pass" || check "List runbooks" "fail" + +# List approvals +code=$(curl -s -o /dev/null -w "%{http_code}" "$(url 3006 /api/v1/approvals)" \ + -H "Authorization: Bearer $RUN_TOKEN") +[ "$code" = "200" ] && check "List approvals" "pass" || check "List approvals (HTTP $code)" "fail" + +# ─── P2 Drift Detection (:3002) ─── +echo -e "\n${YELLOW}P2 Drift Detection (:3002)${NC}" +DRIFT_TOKEN=$(auth 3002) + +# Dashboard +code=$(curl -s -o /dev/null -w "%{http_code}" "$(url 3002 /api/v1/dashboard)" \ + -H "Authorization: Bearer $DRIFT_TOKEN") +[ "$code" = "200" ] && check "Dashboard" "pass" || check "Dashboard (HTTP $code)" "fail" + +# List stacks (empty) +resp=$(api GET 3002 /api/v1/stacks "$DRIFT_TOKEN") +echo "$resp" | grep -q "\[\]" && check "List stacks (empty)" "pass" || check "List stacks" "fail" + +# ─── Cross-service: API Key generation ─── +echo -e "\n${YELLOW}Cross-service: API Key Auth${NC}" +resp=$(api POST 3003 /api/v1/auth/api-keys "$ALERT_TOKEN" -d "{}") +API_KEY=$(echo "$resp" | grep -o '"api_key":"[^"]*' | cut -d'"' -f4) +if [ -n "$API_KEY" ]; then + check "Generate API key" "pass" + # Use API key to hit an endpoint + code=$(curl -s -o /dev/null -w "%{http_code}" "$(url 3003 /api/v1/incidents)" \ + -H "X-API-Key: $API_KEY") + [ "$code" = "200" ] && check "Auth via API key" "pass" || check "Auth via API key (HTTP $code)" "fail" +else + check "Generate API key" "fail" + check "Auth via API key (skipped)" "fail" +fi + +# Summary +echo "" +echo -e "${YELLOW}═══════════════════════════${NC}" +if [ "$PASSED" -eq "$TOTAL" ]; then + echo -e "${GREEN}ALL PASSED: $PASSED/$TOTAL${NC}" + exit 0 +else + echo -e "${RED}$PASSED/$TOTAL passed ($((TOTAL - PASSED)) failed)${NC}" + exit 1 +fi