diff --git a/artifacts/api-server/src/routes/testkit.ts b/artifacts/api-server/src/routes/testkit.ts index 35d9111..029c27d 100644 --- a/artifacts/api-server/src/routes/testkit.ts +++ b/artifacts/api-server/src/routes/testkit.ts @@ -757,14 +757,17 @@ T25_RES=\$(curl -s -w "\\n%{http_code}" -X POST "\$BASE/api/identity/challenge") T25_BODY=\$(body_of "\$T25_RES"); T25_CODE=\$(code_of "\$T25_RES") T25_NONCE=\$(echo "\$T25_BODY" | jq -r '.nonce' 2>/dev/null || echo "") T25_EXP=\$(echo "\$T25_BODY" | jq -r '.expiresAt' 2>/dev/null || echo "") -T25_NONCE_OK=false; T25_EXP_OK=false +T25_NONCE_OK=false; T25_EXP_OK=false; T25_EXP_FUTURE=false [[ "\$T25_NONCE" =~ ^[0-9a-f]{64}\$ ]] && T25_NONCE_OK=true || true [[ "\$T25_EXP" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}T ]] && T25_EXP_OK=true || true -if [[ "\$T25_CODE" == "200" && "\$T25_NONCE_OK" == "true" && "\$T25_EXP_OK" == "true" ]]; then - note PASS "HTTP 200, nonce=64-char-hex, expiresAt=ISO" +# Verify expiresAt is actually in the future (ISO 8601 sorts lexicographically) +T25_NOW=\$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || echo "") +[[ -n "\$T25_NOW" && "\$T25_EXP" > "\$T25_NOW" ]] && T25_EXP_FUTURE=true || true +if [[ "\$T25_CODE" == "200" && "\$T25_NONCE_OK" == "true" && "\$T25_EXP_OK" == "true" && "\$T25_EXP_FUTURE" == "true" ]]; then + note PASS "HTTP 200, nonce=64-char-hex, expiresAt=ISO and in future (\$T25_EXP)" PASS=\$((PASS+1)) else - note FAIL "code=\$T25_CODE nonce='\$T25_NONCE' expiresAt='\$T25_EXP'" + note FAIL "code=\$T25_CODE nonce_ok=\$T25_NONCE_OK exp_ok=\$T25_EXP_OK exp_future=\$T25_EXP_FUTURE exp='\$T25_EXP'" FAIL=\$((FAIL+1)) fi @@ -844,7 +847,8 @@ T30_RES=\$(curl -s -w "\\n%{http_code}" -X POST "\$BASE/api/sessions" \\ T30_BODY=\$(body_of "\$T30_RES"); T30_CODE=\$(code_of "\$T30_RES") T30_ERR=\$(echo "\$T30_BODY" | jq -r '.error' 2>/dev/null || echo "") T30_SESSION=\$(echo "\$T30_BODY" | jq -r '.sessionId' 2>/dev/null || echo "") -if [[ "\$T30_CODE" == "401" && "\$T30_ERR" == *"Invalid or expired"* && -z "\$T30_SESSION" ]]; then +# jq returns literal "null" for absent fields; treat both empty and "null" as absent +if [[ "\$T30_CODE" == "401" && "\$T30_ERR" == *"Invalid or expired"* && ( -z "\$T30_SESSION" || "\$T30_SESSION" == "null" ) ]]; then note PASS "HTTP 401, error contains 'Invalid or expired', no sessionId created" PASS=\$((PASS+1)) else @@ -881,7 +885,7 @@ T32_RES=\$(curl -s -w "\\n%{http_code}" -X POST "\$BASE/api/sessions" \\ T32_BODY=\$(body_of "\$T32_RES"); T32_CODE=\$(code_of "\$T32_RES") T32_SESSION_ID=\$(echo "\$T32_BODY" | jq -r '.sessionId' 2>/dev/null || echo "") T32_TIER=\$(echo "\$T32_BODY" | jq -r '.trust_tier' 2>/dev/null || echo "") -if [[ "\$T32_CODE" == "201" && "\$T32_TIER" == "anonymous" && -n "\$T32_SESSION_ID" ]]; then +if [[ "\$T32_CODE" == "201" && "\$T32_TIER" == "anonymous" && -n "\$T32_SESSION_ID" && "\$T32_SESSION_ID" != "null" ]]; then note PASS "HTTP 201, trust_tier=anonymous, sessionId=\${T32_SESSION_ID:0:8}..." PASS=\$((PASS+1)) else @@ -900,7 +904,7 @@ T33_RES=\$(curl -s -w "\\n%{http_code}" -X POST "\$BASE/api/jobs" \\ T33_BODY=\$(body_of "\$T33_RES"); T33_CODE=\$(code_of "\$T33_RES") T33_JOB_ID=\$(echo "\$T33_BODY" | jq -r '.jobId' 2>/dev/null || echo "") T33_TIER=\$(echo "\$T33_BODY" | jq -r '.trust_tier' 2>/dev/null || echo "") -if [[ "\$T33_CODE" == "201" && "\$T33_TIER" == "anonymous" && -n "\$T33_JOB_ID" ]]; then +if [[ "\$T33_CODE" == "201" && "\$T33_TIER" == "anonymous" && -n "\$T33_JOB_ID" && "\$T33_JOB_ID" != "null" ]]; then note PASS "HTTP 201, trust_tier=anonymous, jobId=\${T33_JOB_ID:0:8}..." PASS=\$((PASS+1)) else @@ -1029,8 +1033,10 @@ async function main() { main().catch(err => { process.stderr.write(String(err) + '\n'); process.exit(1); }); NODESCRIPT - T36_OUT=\$(node "\$T36_TMPFILE" "\$BASE" 2>/dev/null) - T36_EXIT=\$? + # Capture output and exit code safely under set -e. + # "|| T36_EXIT=$?" prevents errexit if node exits non-zero. + T36_OUT="" T36_EXIT=0 + T36_OUT=\$(node "\$T36_TMPFILE" "\$BASE" 2>/dev/null) || T36_EXIT=\$? rm -f "\$T36_TMPFILE" if [[ \$T36_EXIT -ne 0 || -z "\$T36_OUT" ]]; then @@ -1059,21 +1065,21 @@ fi # These are bash comments only. They document planned tests so future tasks # can implement them with the correct numbering context. # -# FUTURE T37 (Task #27 — cost routing): GET /api/estimate returns cost preview +# FUTURE T37: GET /api/estimate returns cost preview # GET \$BASE/api/estimate?request= # Assert HTTP 200, estimatedSats is a positive integer # Assert model, inputTokens, outputTokens are present # -# FUTURE T38 (Task #27 — cost routing): Anonymous job always hits Lightning gate +# FUTURE T38: Anonymous job always hits Lightning gate # Create anonymous job, poll to awaiting_work_payment # Assert response.free_tier is absent or false in all poll responses # -# FUTURE T39 (Task #27 — free tier): Nostr-identified trusted identity → free response +# FUTURE T39: Nostr-identified trusted identity → free response # Requires identity with trust_score >= 50 (trusted tier) and daily budget not exhausted # Submit request with identity token # Assert HTTP 200, response.free_tier == true, no invoice created # -# FUTURE T40 (Task #29 — Timmy as peer): Timmy initiates a zap +# FUTURE T40: Timmy initiates a zap # POST to /api/identity/me/tip (or similar) # Assert Timmy initiates a Lightning outbound payment to caller's LNURL