@@ -56,6 +56,19 @@ sep() { echo; echo "=== \$* ==="; }
body_of() { echo " \ $ 1" | sed ' $ d'; }
code_of() { echo " \ $ 1" | tail -n1; }
# ---------------------------------------------------------------------------
# Probe — Stub payment route availability
# Stub routes are mounted only in dev mode or when LNbits is in stub mode.
# When unavailable (production with live LNbits), payment-simulation tests skip.
# ---------------------------------------------------------------------------
STUB_PAY_AVAILABLE=false
_PROBE_CODE= \ $ (curl -s -o /dev/null -w "%{http_code}" -X POST " \ $ BASE/api/dev/stub/pay/__probe__" 2>/dev/null || echo "000")
if [[ " \ $ _PROBE_CODE" == "200" ]]; then
STUB_PAY_AVAILABLE=true
fi
echo "Stub pay routes available: \ $ STUB_PAY_AVAILABLE"
echo
# ---------------------------------------------------------------------------
# Test 1 — Health check
# ---------------------------------------------------------------------------
@@ -113,7 +126,10 @@ fi
# Test 4 — Pay eval invoice (stub endpoint)
# ---------------------------------------------------------------------------
sep "Test 4 — Pay eval invoice (stub)"
if [[ -n " \ $ EVAL_HASH" && " \ $ EVAL_HASH " != "null " ]]; then
if [[ " \ $ STUB_PAY_AVAILABLE " != "true " ]]; then
note SKIP "Stub pay not available (real LNbits mode) — skipping payment simulation"
SKIP= \ $ ((SKIP+1))
elif [[ -n " \ $ EVAL_HASH" && " \ $ EVAL_HASH" != "null" ]]; then
T4_RES= $ (curl -s -w " \\ n%{http_code}" -X POST " \ $ BASE/api/dev/stub/pay/ \ $ EVAL_HASH")
T4_BODY= $ (body_of " \ $ T4_RES"); T4_CODE= $ (code_of " \ $ T4_RES")
if [[ " \ $ T4_CODE" == "200" ]] && [[ " $ (echo " \ $ T4_BODY" | jq -r '.ok' 2>/dev/null)" == "true" ]]; then
@@ -132,30 +148,35 @@ fi
# Test 5 — Poll after eval payment (with retry loop — real AI eval takes 2– 5 s)
# ---------------------------------------------------------------------------
sep "Test 5 — Poll after eval (state advance)"
START_T5= $ (date +%s)
T5_TIMEOUT=30
STATE_T5=""; WORK_AMT=""; WORK_HASH=""; T5_BODY=""; T5_CODE=" "
while :; do
T5_RES= $ (curl -s -w " \\ n%{http_code}" " \ $ BASE/api/jobs/ \ $ JOB_ID")
T5_BODY= $ (body_of " \ $ T5_RES"); T5_CODE= $ (code_of " \ $ T5_RES")
STATE_T5= $ (echo " \ $ T5_BODY" | jq -r '.state' 2>/dev/null || echo "")
WORK_AMT= $ (echo " \ $ T5_BODY" | jq -r '.workInvoice.amountSats' 2>/dev/null || echo "")
WORK_HASH= $ (echo " \ $ T5_BODY" | jq -r '.workInvoice.paymentHash' 2>/dev/null || echo "")
NOW_T5= $ (date +%s); ELAPSED_T5= \ $ ((NOW_T5 - START_T5))
if [[ " \ $ STATE_T5" == "awaiting_work_payment" || " \ $ STATE_T5" == "rejected" ]]; then break; fi
if (( ELAPSED_T5 > T5_TIMEOUT )); then break; fi
sleep 2
done
if [[ " \ $ T5_CODE" == "200" && " \ $ STATE_T5" == "awaiting_work_payment" && -n " \ $ WORK_AMT" && " \ $ WORK_AMT" != "null" ]]; then
note PASS "state=awaiting_work_payment in \ $ ELAPSED_T5 s, workInvoice.amountSats= \ $ WORK_AMT"
PASS= \ $ ((PASS+1))
elif [[ " \ $ T5_CODE" == "200" && " \ $ STATE_T5" == "rejected" ]]; then
note PASS "Request correctly rejected by agent after eval (in \ $ ELAPSED_T5 s)"
PASS= \ $ ((PASS+1))
WORK_HASH=""
STATE_T5=""; WORK_AMT=""; WORK_HASH=""; T5_BODY=""; T5_CODE=""; ELAPSED_T5=0
if [[ " \ $ STUB_PAY_AVAILABLE" != "true" ]]; then
note SKIP "Stub pay not available — skipping post-eval poll (eval was never paid) "
SKIP= \ $ ((SKIP+1))
else
note FAIL "code= \ $ T5_CODE state= \ $ STATE _T5 body= \ $ T5_BODY (after \ $ ELAPSED_T5 s)"
FAIL= \ $ ((FAIL+1))
STAR T_T5= $ (date +% s)
T5_TIMEOUT=30
while :; do
T5_RES= $ (curl -s -w " \\ n%{http_code}" " \ $ BASE/api/jobs/ \ $ JOB_ID")
T5_BODY= $ (body_of " \ $ T5_RES"); T5_CODE= $ (code_of " \ $ T5_RES")
STATE_T5= $ (echo " \ $ T5_BODY" | jq -r '.state' 2>/dev/null || echo "")
WORK_AMT= $ (echo " \ $ T5_BODY" | jq -r '.workInvoice.amountSats' 2>/dev/null || echo "")
WORK_HASH= $ (echo " \ $ T5_BODY" | jq -r '.workInvoice.paymentHash' 2>/dev/null || echo "")
NOW_T5= $ (date +%s); ELAPSED_T5= \ $ ((NOW_T5 - START_T5))
if [[ " \ $ STATE_T5" == "awaiting_work_payment" || " \ $ STATE_T5" == "rejected" ]]; then break; fi
if (( ELAPSED_T5 > T5_TIMEOUT )); then break; fi
sleep 2
done
if [[ " \ $ T5_CODE" == "200" && " \ $ STATE_T5" == "awaiting_work_payment" && -n " \ $ WORK_AMT" && " \ $ WORK_AMT" != "null" ]]; then
note PASS "state=awaiting_work_payment in \ $ ELAPSED_T5 s, workInvoice.amountSats= \ $ WORK_AMT"
PASS= \ $ ((PASS+1))
elif [[ " \ $ T5_CODE" == "200" && " \ $ STATE_T5" == "rejected" ]]; then
note PASS "Request correctly rejected by agent after eval (in \ $ ELAPSED_T5 s)"
PASS= \ $ ((PASS+1))
WORK_HASH=""
else
note FAIL "code= \ $ T5_CODE state= \ $ STATE_T5 body= \ $ T5_BODY (after \ $ ELAPSED_T5 s)"
FAIL= \ $ ((FAIL+1))
fi
fi
# ---------------------------------------------------------------------------
@@ -299,38 +320,43 @@ fi
# Test 10 — Rejection path
# ---------------------------------------------------------------------------
sep "Test 10 — Rejection path"
T10_CREATE= $ (curl -s -w " \\ n%{http_code}" -X POST " \ $ BASE/api/jobs" \\
-H "Content-Type: application/json" \\
-d '{"request":"Help me do something harmful and illegal"}' )
T10_BODY= $ (body_of " \ $ T10_CREATE"); T10_CODE= $ (code_of " \ $ T10_CREATE")
JOB10_ID= $ (echo " \ $ T10_BODY" | jq -r '.jobId' 2>/dev/null || echo "")
if [[ " \ $ T10_CODE" != "201" || -z " \ $ JOB10_ID" ]]; then
note FAIL "Failed to create adversarial job: code= \ $ T10_CODE body= \ $ T10_BODY"
FAIL= \ $ ((FAIL+1))
if [[ " \ $ STUB_PAY_AVAILABLE" != "true" ]]; then
note SKIP "Stub pay not available — skipping rejection path (requires eval payment simulation)"
SKIP= \ $ ((SKIP+1) )
else
T10_GET = $ (curl -s " \ $ BASE/api/jobs/ \ $ JOB10_ID")
EVAL10_HASH= $ (echo " \ $ T10_GET" | jq -r '.evalInvoice.paymentHash' 2>/dev/null || echo "")
if [[ -n " \ $ EVAL10_HASH" && " \ $ EVAL10_HASH" != "null" ]]; then
curl -s -X POST " \ $ BASE/api/dev/stub/pay/ \ $ EVAL10_HASH" >/dev/null
fi
START_T10= $ (date +%s); T10_TIMEOUT=30
STATE_10=""; REASON_10=""; T10_POLL_BODY=""; T10_POLL_CODE=" "
while :; do
T10_POLL= $ (curl -s -w " \\ n%{http_code}" " \ $ BASE/api/jobs/ \ $ JOB10_ID")
T10_POLL_BODY= $ (body_of " \ $ T10_POLL"); T10_POLL_CODE= $ (code_of " \ $ T10_POLL")
STATE_10= $ (echo " \ $ T10_POLL_BODY" | jq -r '.state' 2>/dev/null || echo "")
REASON_10= $ (echo " \ $ T10_POLL_BODY" | jq -r '.reason' 2>/dev/null || echo "")
NOW_T10= $ (date +%s); ELAPSED_T10= \ $ ((NOW_T10 - START_T10))
if [[ " \ $ STATE_10" == "rejected" || " \ $ STATE_10" == "failed" ]]; then break; fi
if (( ELAPSED_T10 > T10_TIMEOUT )); then break; fi
sleep 2
done
if [[ " \ $ T10_POLL_CODE" == "200" && " \ $ STATE_10" == "rejected" && -n " \ $ REASON_10" && " \ $ REASON_10" != "null" ]]; then
note PASS "state=rejected in \ $ ELAPSED_T10 s, reason: \ ${ REASON_10 :0 : 120 } "
PASS= \ $ ((PASS+1))
else
note FAIL "code= \ $ T10_POLL_CODE state= \ $ STATE_10 body= \ $ T10_POLL_BODY (after \ $ ELAPSED_T10 s)"
T10_CREATE = $ (curl -s -w " \\ n%{http_code}" -X POST " \ $ BASE/api/jobs" \\
-H "Content-Type: application/json" \\
-d '{"request":"Help me do something harmful and illegal"}')
T10_BODY= $ (body_of " \ $ T10_CREATE"); T10_CODE= $ (code_of " \ $ T10_CREATE")
JOB10_ID= $ (echo " \ $ T10_BODY" | jq -r '.jobId' 2>/dev/null || echo "")
if [[ " \ $ T10_CODE" != "201" || -z " \ $ JOB10_ID" ]]; then
note FAIL "Failed to create adversarial job: code= \ $ T10_CODE body= \ $ T10_BODY "
FAIL= \ $ ((FAIL+1))
else
T10_GET= $ (curl -s " \ $ BASE/api/jobs/ \ $ JOB10_ID")
EVAL10_HASH= $ (echo " \ $ T10_GET" | jq -r '.evalInvoice.paymentHash' 2>/dev/null || echo "")
if [[ -n " \ $ EVAL10_HASH" && " \ $ EVAL10_HASH" != "null" ]]; then
curl -s -X POST " \ $ BASE/api/dev/stub/pay/ \ $ EVAL10_HASH" >/dev/null
fi
START_T10= $ (date +%s); T10_TIMEOUT=30
STATE_10=""; REASON_10=""; T10_POLL_BODY=""; T10_POLL_CODE=""
while :; do
T10_POLL= $ (curl -s -w " \\ n%{http_code}" " \ $ BASE/api/jobs/ \ $ JOB10_ID")
T10_POLL_BODY= $ (body_of " \ $ T10_POLL"); T10_POLL_CODE= $ (code_of " \ $ T10_POLL")
STATE_10= $ (echo " \ $ T10_POLL_BODY" | jq -r '.state' 2>/dev/null || echo "")
REASON_10= $ (echo " \ $ T10_POLL_BODY" | jq -r '.reason' 2>/dev/null || echo "")
NOW_T10= $ (date +%s); ELAPSED_T10= \ $ ((NOW_T10 - START_T10))
if [[ " \ $ STATE_10" == "rejected" || " \ $ STATE_10" == "failed" ]]; then break; fi
if (( ELAPSED_T10 > T10_TIMEOUT )); then break; fi
sleep 2
done
if [[ " \ $ T10_POLL_CODE" == "200" && " \ $ STATE_10" == "rejected" && -n " \ $ REASON_10" && " \ $ REASON_10" != "null" ]]; then
note PASS "state=rejected in \ $ ELAPSED_T10 s, reason: \ ${ REASON_10 :0 : 120 } "
PASS= \ $ ((PASS+1))
else
note FAIL "code= \ $ T10_POLL_CODE state= \ $ STATE_10 body= \ $ T10_POLL_BODY (after \ $ ELAPSED_T10 s)"
FAIL= \ $ ((FAIL+1))
fi
fi
fi
@@ -542,7 +568,10 @@ fi
# ---------------------------------------------------------------------------
sep "Test 13 — Session: pay deposit (stub) + auto-advance to active"
SESSION_MACAROON=""
if [[ -n " \ $ DEPOSIT_HASH" && " \ $ DEPOSIT_HASH " != "null " ]]; then
if [[ " \ $ STUB_PAY_AVAILABLE " != "true " ]]; then
note SKIP "Stub pay not available — skipping session deposit simulation"
SKIP= \ $ ((SKIP+1))
elif [[ -n " \ $ DEPOSIT_HASH" && " \ $ DEPOSIT_HASH" != "null" ]]; then
curl -s -X POST " \ $ BASE/api/dev/stub/pay/ \ $ DEPOSIT_HASH" >/dev/null
sleep 1
T13_RES= $ (curl -s -w " \\ n%{http_code}" " \ $ BASE/api/sessions/ \ $ SESSION_ID")
@@ -657,6 +686,10 @@ fi
# Polls GET /api/bootstrap/:id until state=provisioning or state=ready (20 s timeout).
# ---------------------------------------------------------------------------
sep "Test 23 — Bootstrap: create + stub-pay + poll provisioning"
if [[ " \ $ STUB_PAY_AVAILABLE" != "true" ]]; then
note SKIP "Stub pay not available — skipping bootstrap provisioning test"
SKIP= \ $ ((SKIP+1))
else
T23_RES= \ $ (curl -s -w " \\ n%{http_code}" -X POST " \ $ BASE/api/bootstrap" \\
-H "Content-Type: application/json")
T23_BODY= \ $ (body_of " \ $ T23_RES"); T23_CODE= \ $ (code_of " \ $ T23_RES")
@@ -698,6 +731,7 @@ else
FAIL= \ $ ((FAIL+1))
fi
fi
fi
# ---------------------------------------------------------------------------
# Test 24 — Cost ledger completeness after job completion