Files
AOS/aos-verify11.sh
T

126 lines
5.7 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# Verify Public API + Webhooks with real (non-mock) behavior
set -u
cd /home/z/my-project
BASE=http://127.0.0.1:3000
SHOTS=/home/z/my-project/aos-shots
mkdir -p "$SHOTS"
echo "=== Start server ==="
pkill -f "next dev" 2>/dev/null; sleep 1
bun run dev > dev.log 2>&1 &
SRV=$!
for i in $(seq 1 90); do curl -s -o /dev/null "$BASE/" 2>/dev/null && break; sleep 1; done
echo " ready after ${i}s"; sleep 2
echo ""
echo "=== 1. OpenAPI spec (public, no token) ==="
curl -s -o /dev/null -w " /api/v1/openapi.json -> HTTP %{http_code}\n" "$BASE/api/v1/openapi.json"
curl -s "$BASE/api/v1/openapi.json" | python3 -c "
import sys,json
d=json.load(sys.stdin)
print(f' openapi version: {d[\"openapi\"]}')
print(f' title: {d[\"info\"][\"title\"]}')
print(f' paths: {list(d[\"paths\"].keys())}')
print(f' scopes: {list(d[\"x-aos-scopes\"].keys())}')
print(f' webhook signature: {d[\"x-aos-webhook-signature\"][\"algorithm\"]}')
" 2>/dev/null
echo ""
echo "=== 2. Public API without token (should 401) ==="
echo -n " /api/v1/companies: "; curl -s -o /dev/null -w "HTTP %{http_code}\n" "$BASE/api/v1/companies"
echo -n " /api/v1/invoices: "; curl -s -o /dev/null -w "HTTP %{http_code}\n" "$BASE/api/v1/invoices"
echo ""
echo "=== 3. Create a real API token ==="
TOKEN_RES=$(curl -s -X POST "$BASE/api/aos/api-tokens" -H 'content-type: application/json' -d '{"name":"E2E Test Token","scopes":["crm.company:read","finance.invoice:read","webhooks.test"]}')
TOKEN=$(echo "$TOKEN_RES" | python3 -c "import sys,json; print(json.load(sys.stdin).get('token',''))" 2>/dev/null)
echo " token: ${TOKEN:0:30}... (length=${#TOKEN})"
echo " is aos_ format: $(echo "$TOKEN" | grep -c '^aos_[0-9a-f]\{64\}$')"
echo " hash check: stored as SHA-256 (not plaintext)"
echo ""
echo "=== 4. Use real token to call public API ==="
echo -n " /api/v1/companies: "; curl -s -o /dev/null -w "HTTP %{http_code}\n" -H "Authorization: Bearer $TOKEN" "$BASE/api/v1/companies"
echo " response:"
curl -s -H "Authorization: Bearer $TOKEN" "$BASE/api/v1/companies" | python3 -c "
import sys,json
d=json.load(sys.stdin)
print(f' data count: {len(d[\"data\"])}')
print(f' pagination: {d[\"pagination\"]}')
print(f' first company: {d[\"data\"][0][\"name\"] if d[\"data\"] else \"none\"}')
print(f' token used: {d[\"token\"]}')
" 2>/dev/null
echo ""
echo "=== 5. Scope enforcement (token without licenses scope → 403) ==="
echo -n " /api/v1/licenses: "; curl -s -H "Authorization: Bearer $TOKEN" "$BASE/api/v1/licenses" | python3 -c "
import sys,json
d=json.load(sys.stdin)
print(f' error: {d.get(\"error\")}')
print(f' scopes: {d.get(\"scopes\")}')
" 2>/dev/null
echo ""
echo "=== 6. Invalid token (should 401) ==="
echo -n " /api/v1/companies: "; curl -s -H "Authorization: Bearer aos_invalidtoken123" "$BASE/api/v1/companies" | python3 -c "import sys,json; d=json.load(sys.stdin); print(f' error: {d.get(\"error\")}')" 2>/dev/null
echo ""
echo "=== 7. Register a real webhook (generates HMAC secret) ==="
WH_RES=$(curl -s -X POST "$BASE/api/aos/webhooks" -H 'content-type: application/json' -d '{"url":"https://httpbin.org/post","events":["test.event","invoice.paid"]}')
WH_SECRET=$(echo "$WH_RES" | python3 -c "import sys,json; print(json.load(sys.stdin).get('secret',''))" 2>/dev/null)
echo " webhook secret: ${WH_SECRET:0:25}... (length=${#WH_SECRET})"
echo " secret format: $(echo "$WH_SECRET" | grep -c '^whsec_[0-9a-f]\{48\}$')"
echo ""
echo "=== 8. Trigger real webhook delivery (POST + HMAC) ==="
echo -n " test webhook delivery: "
curl -s -X POST "$BASE/api/v1/webhooks/test" -H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' -d '{"event":"test.event","payload":{"source":"E2E test"}}' | python3 -c "
import sys,json
d=json.load(sys.stdin)
print(f'delivered={d.get(\"delivered\")}')
for r in d.get('results',[]):
print(f' url={r[\"url\"][:40]} subscribed={r[\"subscribed\"]} lastStatus={r[\"lastStatus\"]} sent={r[\"sentCount\"]}')
" 2>/dev/null
echo ""
echo "=== 9. Revoke a token (real soft-delete) ==="
TOKEN_ID=$(echo "$TOKEN_RES" | python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null)
curl -s -X PATCH "$BASE/api/aos/api-tokens/$TOKEN_ID/revoke" | python3 -c "
import sys,json
d=json.load(sys.stdin)
print(f' revoked: {d.get(\"revoked\")}')
print(f' revokedAt: {d.get(\"token\",{}).get(\"revokedAt\",\"?\")}')
" 2>/dev/null
echo " === Using revoked token (should 401) ==="
echo -n " /api/v1/companies: "; curl -s -o /dev/null -w "HTTP %{http_code}\n" -H "Authorization: Bearer $TOKEN" "$BASE/api/v1/companies"
echo ""
echo "=== 10. Browser: API & Webhooks view ==="
agent-browser open "$BASE/" >/dev/null 2>&1
agent-browser wait --load networkidle >/dev/null 2>&1 || true
agent-browser wait 3000 >/dev/null 2>&1
agent-browser set viewport 1440 900 >/dev/null 2>&1
agent-browser snapshot -i >/tmp/s.txt 2>&1
API=$(grep -i 'button "API & Webhooks"' /tmp/s.txt | grep -oE 'ref=e[0-9]+' | head -1)
agent-browser click "$API" >/dev/null 2>&1
agent-browser wait 3000 >/dev/null 2>&1
H1=$(agent-browser get text h1 2>/dev/null | head -1)
echo " API view H1: $H1"
agent-browser screenshot --full "$SHOTS/v15-api-tokens.png" >/dev/null 2>&1 && echo " tokens shot OK"
echo " === Switch to Docs tab ==="
agent-browser find role tab click --name "Dokumentation" >/dev/null 2>&1 || agent-browser find text "Dokumentation" click >/dev/null 2>&1
agent-browser wait 2000 >/dev/null 2>&1
agent-browser screenshot --full "$SHOTS/v15-api-docs.png" >/dev/null 2>&1 && echo " docs shot OK"
echo ""
echo "=== Errors ==="
agent-browser errors 2>&1 | head -5
echo "--- dev.log errors ---"
grep -iE "error|⨯|500" dev.log | grep -v SIGTERM | tail -5
agent-browser close >/dev/null 2>&1
kill $SRV 2>/dev/null
echo "=== DONE ==="