Real IMAP connection via imapflow library: - Connects to IMAP server (IMAPFlow, TLS on port 993) - Fetches recent messages (configurable limit, default 20) - Extracts: from, fromEmail, subject, date, snippet (200 chars) - Thread-to-contact matching (PRD §6.1): 1. Match sender email against persons table 2. If no person match, extract domain and match against company.website 3. Returns matchedCompany + matchedPerson for each email - Audit-logged (action: crm.mailsync:imap) - Zod validated (host, port, user, password, folder, limit) - 502 on IMAP connection failure with detail - 400 on validation failure Tested: invalid creds → 502 'IMAP connection failed', missing host → 400
102 lines
4.9 KiB
Bash
Executable File
102 lines
4.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# Verify Support Tickets (AI) + Estimates modules
|
||
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; pkill -f "tee dev.log" 2>/dev/null; sleep 2
|
||
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 "=== API tests ==="
|
||
echo -n " /api/aos/tickets -> "; curl -s -o /dev/null -w "HTTP %{http_code}\n" "$BASE/api/aos/tickets"
|
||
echo -n " /api/aos/estimates -> "; curl -s -o /dev/null -w "HTTP %{http_code}\n" "$BASE/api/aos/estimates"
|
||
curl -s "$BASE/api/aos/tickets" | python3 -c "import sys,json; d=json.load(sys.stdin); print(f' tickets: {len(d[\"tickets\"])}, stats: {d[\"stats\"]}')" 2>/dev/null
|
||
curl -s "$BASE/api/aos/estimates" | python3 -c "import sys,json; d=json.load(sys.stdin); print(f' estimates: {len(d[\"estimates\"])}, winRate: {d[\"stats\"][\"winRate\"]}%')" 2>/dev/null
|
||
|
||
echo ""
|
||
echo "=== AI ticket summarize API ==="
|
||
TKT_ID=$(curl -s "$BASE/api/aos/tickets" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['tickets'][0]['id'])" 2>/dev/null)
|
||
echo " ticket id: $TKT_ID"
|
||
SUM=$(curl -s -m 70 -X POST "$BASE/api/aos/tickets/$TKT_ID/summarize")
|
||
echo " summary (first 200 chars): $(echo "$SUM" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('summary','')[:200])" 2>/dev/null)"
|
||
|
||
echo ""
|
||
echo "=== AI ticket reply suggestion API ==="
|
||
REP=$(curl -s -m 70 -X POST "$BASE/api/aos/tickets/$TKT_ID/reply" -H 'content-type: application/json' -d '{"tone":"polite"}')
|
||
echo " reply (first 250 chars): $(echo "$REP" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('suggestion','')[:250])" 2>/dev/null)"
|
||
|
||
echo ""
|
||
echo "=== Browser: Support 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 find text "Support" click >/dev/null 2>&1
|
||
agent-browser wait 3000 >/dev/null 2>&1
|
||
H1=$(agent-browser get text h1 2>/dev/null | head -1)
|
||
echo " Support H1: $H1"
|
||
agent-browser screenshot --full "$SHOTS/v10-support.png" >/dev/null 2>&1 && echo " shot OK"
|
||
|
||
echo "=== Click AI summarize button ==="
|
||
agent-browser find text "Zusammenfassen" click >/dev/null 2>&1
|
||
agent-browser wait 10000 >/dev/null 2>&1
|
||
agent-browser screenshot --full "$SHOTS/v10-support-summarized.png" >/dev/null 2>&1 && echo " summarized shot OK"
|
||
# check if AI summary appeared
|
||
SUM_UI=$(agent-browser eval "document.querySelector('[class*=bg-muted\\/20]')?.innerText?.slice(0,150) || 'no summary'" 2>/dev/null | tail -1 | tr -d '"')
|
||
echo " AI summary in UI: $(echo "$SUM_UI" | head -c 120)"
|
||
|
||
echo ""
|
||
echo "=== Browser: Estimates view ==="
|
||
agent-browser find text "Angebote" click >/dev/null 2>&1
|
||
agent-browser wait 3000 >/dev/null 2>&1
|
||
H1=$(agent-browser get text h1 2>/dev/null | head -1)
|
||
echo " Estimates H1: $H1"
|
||
agent-browser screenshot --full "$SHOTS/v10-estimates.png" >/dev/null 2>&1 && echo " shot OK"
|
||
|
||
echo "=== Click first estimate → dialog ==="
|
||
agent-browser eval "document.querySelector('tbody tr')?.click()" >/dev/null 2>&1
|
||
agent-browser wait 2000 >/dev/null 2>&1
|
||
DIALOG=$(agent-browser eval "document.querySelector('[role=dialog]')?.textContent?.slice(0,120) || 'closed'" 2>/dev/null | tail -1 | tr -d '"')
|
||
echo " dialog: $(echo "$DIALOG" | head -c 120)"
|
||
agent-browser screenshot --full "$SHOTS/v10-estimate-detail.png" >/dev/null 2>&1 && echo " detail shot OK"
|
||
agent-browser press Escape >/dev/null 2>&1; agent-browser wait 500 >/dev/null 2>&1
|
||
|
||
echo ""
|
||
echo "=== Test accept + convert APIs ==="
|
||
# find a sent estimate
|
||
EST_ID=$(curl -s "$BASE/api/aos/estimates" | python3 -c "
|
||
import sys,json
|
||
d=json.load(sys.stdin)
|
||
for e in d['estimates']:
|
||
if e['status']=='sent':
|
||
print(e['id']); break
|
||
" 2>/dev/null)
|
||
echo " estimate to accept: $EST_ID"
|
||
if [ -n "$EST_ID" ]; then
|
||
ACC=$(curl -s -X PATCH "$BASE/api/aos/estimates/$EST_ID/accept" -H 'content-type: application/json' -d '{"acceptedBy":"Test"}')
|
||
echo " accept: $(echo "$ACC" | python3 -c "import sys,json; d=json.load(sys.stdin); print(f'status={d[\"estimate\"][\"status\"]}, projectId={d.get(\"projectId\",\"none\")}')" 2>/dev/null)"
|
||
echo " converting to invoice…"
|
||
CONV=$(curl -s -X POST "$BASE/api/aos/estimates/$EST_ID/convert")
|
||
echo " convert: $(echo "$CONV" | python3 -c "import sys,json; d=json.load(sys.stdin); print(f'invoice={d[\"invoice\"][\"number\"]} = {d[\"invoice\"][\"grossCents\"]/100:.2f}€')" 2>/dev/null)"
|
||
fi
|
||
|
||
echo ""
|
||
echo "=== Errors ==="
|
||
agent-browser errors 2>&1 | head -8
|
||
echo "--- console last 5 ---"
|
||
agent-browser console 2>&1 | tail -5
|
||
|
||
echo ""
|
||
agent-browser close >/dev/null 2>&1
|
||
kill $SRV 2>/dev/null
|
||
echo "=== dev.log errors ==="
|
||
grep -iE "error|⨯|500" dev.log | grep -v SIGTERM | tail -8
|
||
echo "=== DONE ==="
|
||
ls -la "$SHOTS"/v10-* 2>/dev/null | tail -6
|