f325299d-c1a0-4376-98ae-ddb747d23da5

This commit is contained in:
Z User committed 2026-10-26 17:00:08 +00:00
1 parent 9c58e29472
commit be34f88822
22 files changed
+1361 -1

No files matched your search

+125
View File
@@ -0,0 +1,125 @@
#!/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 ==="