Co-authored-by: Claude (Opus 4.6) <claude@hermes.local> Co-committed-by: Claude (Opus 4.6) <claude@hermes.local>
This commit was merged in pull request #62.
This commit is contained in:
@@ -33,6 +33,16 @@ const router = Router();
|
||||
* - T38 ADDED: GET /api/metrics — full snapshot field verification, zero prior coverage.
|
||||
* - T39 ADDED: GET /api/estimate — cost-preview endpoint, estimatedSats + model + tokens.
|
||||
* - T40 ADDED: Demo 429 Retry-After header — RFC 7231 compliance on rate-limited response.
|
||||
* - T41 ADDED: GET /api/relay/policy health check — relay policy endpoint reachability.
|
||||
* - T42 ADDED: GET /api/admin/relay/stats — moderation stats fields (pending, totalAccounts, approvedToday).
|
||||
* - T43 ADDED: GET /api/admin/relay/queue — queue list returns total + events array.
|
||||
* - T44 ADDED: GET /api/admin/relay/queue?status=bogus → 400 — invalid filter guard.
|
||||
* - T45 ADDED: POST /api/admin/relay/queue/:id/approve non-existent → 404 — guard.
|
||||
* - T46 ADDED: POST /api/admin/relay/queue/:id/reject non-existent → 404 — guard.
|
||||
* - T47 ADDED: Admin grant write access + verify in accounts list.
|
||||
* - T48 ADDED: Admin revoke access — cleanup of T47 test pubkey.
|
||||
* - T49 ADDED: Admin grant with invalid pubkey → 400 — input validation.
|
||||
* - T50 ADDED: Admin grant with invalid access level → 400 — enum validation.
|
||||
*/
|
||||
router.get("/testkit", (req: Request, res: Response) => {
|
||||
const proto =
|
||||
@@ -1191,22 +1201,265 @@ else
|
||||
FAIL=\$((FAIL+1))
|
||||
fi
|
||||
|
||||
# ===========================================================================
|
||||
# Relay Moderation Tests (T41–T50)
|
||||
# ===========================================================================
|
||||
# These tests exercise the relay write-policy, moderation queue, and admin
|
||||
# relay endpoints. Admin routes accept localhost-only when ADMIN_TOKEN is not
|
||||
# set (dev mode). When an ADMIN_TOKEN is configured the testkit probes with
|
||||
# that token via the ADMIN_TOKEN env var; if unavailable, tests are skipped.
|
||||
|
||||
# Probe: can we reach admin relay endpoints?
|
||||
ADMIN_HDR=""
|
||||
if [[ -n "\${ADMIN_TOKEN:-}" ]]; then
|
||||
ADMIN_HDR="Authorization: Bearer \$ADMIN_TOKEN"
|
||||
fi
|
||||
# Try with token first, then without (localhost fallback for dev)
|
||||
_ADMIN_PROBE=\$(curl -s -o /dev/null -w "%{http_code}" \\
|
||||
\${ADMIN_HDR:+-H "\$ADMIN_HDR"} \\
|
||||
"\$BASE/api/admin/relay/stats" 2>/dev/null || echo "000")
|
||||
ADMIN_RELAY_OK=false
|
||||
if [[ "\$_ADMIN_PROBE" == "200" ]]; then
|
||||
ADMIN_RELAY_OK=true
|
||||
fi
|
||||
echo "Admin relay endpoints accessible: \$ADMIN_RELAY_OK"
|
||||
echo
|
||||
|
||||
# Helper: call admin relay endpoints with correct auth header
|
||||
admin_curl() {
|
||||
if [[ -n "\$ADMIN_HDR" ]]; then
|
||||
curl -s -w "\\n%{http_code}" -H "\$ADMIN_HDR" -H "Content-Type: application/json" "\$@"
|
||||
else
|
||||
curl -s -w "\\n%{http_code}" -H "Content-Type: application/json" "\$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test 41 — GET /api/relay/policy health check
|
||||
# ---------------------------------------------------------------------------
|
||||
sep "Test 41 — Relay policy health check"
|
||||
T41_RES=\$(curl -s -w "\\n%{http_code}" "\$BASE/api/relay/policy")
|
||||
T41_BODY=\$(body_of "\$T41_RES"); T41_CODE=\$(code_of "\$T41_RES")
|
||||
T41_OK=\$(echo "\$T41_BODY" | jq -r '.ok' 2>/dev/null || echo "")
|
||||
if [[ "\$T41_CODE" == "200" && "\$T41_OK" == "true" ]]; then
|
||||
note PASS "HTTP 200, ok=true"
|
||||
PASS=\$((PASS+1))
|
||||
else
|
||||
note FAIL "code=\$T41_CODE body=\$T41_BODY"
|
||||
FAIL=\$((FAIL+1))
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test 42 — GET /api/admin/relay/stats returns expected fields
|
||||
# ---------------------------------------------------------------------------
|
||||
sep "Test 42 — Admin relay stats"
|
||||
if [[ "\$ADMIN_RELAY_OK" != "true" ]]; then
|
||||
note SKIP "Admin relay endpoint not accessible"
|
||||
SKIP=\$((SKIP+1))
|
||||
else
|
||||
T42_RES=\$(admin_curl "\$BASE/api/admin/relay/stats")
|
||||
T42_BODY=\$(body_of "\$T42_RES"); T42_CODE=\$(code_of "\$T42_RES")
|
||||
T42_PENDING=\$(echo "\$T42_BODY" | jq '.pending' 2>/dev/null || echo "null")
|
||||
T42_ACCOUNTS=\$(echo "\$T42_BODY" | jq '.totalAccounts' 2>/dev/null || echo "null")
|
||||
T42_APPROVED_TODAY=\$(echo "\$T42_BODY" | jq '.approvedToday' 2>/dev/null || echo "null")
|
||||
if [[ "\$T42_CODE" == "200" && "\$T42_PENDING" != "null" && "\$T42_ACCOUNTS" != "null" && "\$T42_APPROVED_TODAY" != "null" ]]; then
|
||||
note PASS "HTTP 200, pending=\$T42_PENDING totalAccounts=\$T42_ACCOUNTS approvedToday=\$T42_APPROVED_TODAY"
|
||||
PASS=\$((PASS+1))
|
||||
else
|
||||
note FAIL "code=\$T42_CODE body=\$T42_BODY"
|
||||
FAIL=\$((FAIL+1))
|
||||
fi
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test 43 — GET /api/admin/relay/queue (list, default all statuses)
|
||||
# ---------------------------------------------------------------------------
|
||||
sep "Test 43 — Admin relay queue list"
|
||||
if [[ "\$ADMIN_RELAY_OK" != "true" ]]; then
|
||||
note SKIP "Admin relay endpoint not accessible"
|
||||
SKIP=\$((SKIP+1))
|
||||
else
|
||||
T43_RES=\$(admin_curl "\$BASE/api/admin/relay/queue")
|
||||
T43_BODY=\$(body_of "\$T43_RES"); T43_CODE=\$(code_of "\$T43_RES")
|
||||
T43_TOTAL=\$(echo "\$T43_BODY" | jq '.total' 2>/dev/null || echo "null")
|
||||
T43_EVENTS=\$(echo "\$T43_BODY" | jq '.events | type' 2>/dev/null || echo "null")
|
||||
if [[ "\$T43_CODE" == "200" && "\$T43_TOTAL" != "null" && "\$T43_EVENTS" == '"array"' ]]; then
|
||||
note PASS "HTTP 200, total=\$T43_TOTAL, events is array"
|
||||
PASS=\$((PASS+1))
|
||||
else
|
||||
note FAIL "code=\$T43_CODE body=\$T43_BODY"
|
||||
FAIL=\$((FAIL+1))
|
||||
fi
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test 44 — GET /api/admin/relay/queue?status=invalid → 400
|
||||
# ---------------------------------------------------------------------------
|
||||
sep "Test 44 — Admin relay queue invalid status filter → 400"
|
||||
if [[ "\$ADMIN_RELAY_OK" != "true" ]]; then
|
||||
note SKIP "Admin relay endpoint not accessible"
|
||||
SKIP=\$((SKIP+1))
|
||||
else
|
||||
T44_RES=\$(admin_curl "\$BASE/api/admin/relay/queue?status=bogus")
|
||||
T44_CODE=\$(code_of "\$T44_RES")
|
||||
if [[ "\$T44_CODE" == "400" ]]; then
|
||||
note PASS "HTTP 400 for invalid status filter"
|
||||
PASS=\$((PASS+1))
|
||||
else
|
||||
note FAIL "Expected 400, got \$T44_CODE"
|
||||
FAIL=\$((FAIL+1))
|
||||
fi
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test 45 — POST /api/admin/relay/queue/:eventId/approve for non-existent → 404
|
||||
# ---------------------------------------------------------------------------
|
||||
sep "Test 45 — Admin queue approve non-existent event → 404"
|
||||
if [[ "\$ADMIN_RELAY_OK" != "true" ]]; then
|
||||
note SKIP "Admin relay endpoint not accessible"
|
||||
SKIP=\$((SKIP+1))
|
||||
else
|
||||
T45_RES=\$(admin_curl -X POST "\$BASE/api/admin/relay/queue/0000000000000000000000000000000000000000000000000000000000000000/approve" -d '{"reason":"test"}')
|
||||
T45_CODE=\$(code_of "\$T45_RES")
|
||||
if [[ "\$T45_CODE" == "404" ]]; then
|
||||
note PASS "HTTP 404 for non-existent event"
|
||||
PASS=\$((PASS+1))
|
||||
else
|
||||
note FAIL "Expected 404, got \$T45_CODE"
|
||||
FAIL=\$((FAIL+1))
|
||||
fi
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test 46 — POST /api/admin/relay/queue/:eventId/reject for non-existent → 404
|
||||
# ---------------------------------------------------------------------------
|
||||
sep "Test 46 — Admin queue reject non-existent event → 404"
|
||||
if [[ "\$ADMIN_RELAY_OK" != "true" ]]; then
|
||||
note SKIP "Admin relay endpoint not accessible"
|
||||
SKIP=\$((SKIP+1))
|
||||
else
|
||||
T46_RES=\$(admin_curl -X POST "\$BASE/api/admin/relay/queue/0000000000000000000000000000000000000000000000000000000000000000/reject" -d '{"reason":"test"}')
|
||||
T46_CODE=\$(code_of "\$T46_RES")
|
||||
if [[ "\$T46_CODE" == "404" ]]; then
|
||||
note PASS "HTTP 404 for non-existent event"
|
||||
PASS=\$((PASS+1))
|
||||
else
|
||||
note FAIL "Expected 404, got \$T46_CODE"
|
||||
FAIL=\$((FAIL+1))
|
||||
fi
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test 47 — Admin grant access + verify in accounts list
|
||||
# Uses a deterministic test pubkey that is cleaned up via revoke after.
|
||||
# ---------------------------------------------------------------------------
|
||||
sep "Test 47 — Admin grant relay access + verify in accounts list"
|
||||
T47_PUBKEY="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa047"
|
||||
if [[ "\$ADMIN_RELAY_OK" != "true" ]]; then
|
||||
note SKIP "Admin relay endpoint not accessible"
|
||||
SKIP=\$((SKIP+1))
|
||||
else
|
||||
# Grant write access
|
||||
T47G_RES=\$(admin_curl -X POST "\$BASE/api/admin/relay/accounts/\$T47_PUBKEY/grant" \\
|
||||
-d '{"level":"write","notes":"testkit T47"}')
|
||||
T47G_CODE=\$(code_of "\$T47G_RES")
|
||||
T47G_BODY=\$(body_of "\$T47G_RES")
|
||||
T47G_OK=\$(echo "\$T47G_BODY" | jq -r '.ok' 2>/dev/null || echo "")
|
||||
T47G_LEVEL=\$(echo "\$T47G_BODY" | jq -r '.accessLevel' 2>/dev/null || echo "")
|
||||
|
||||
# Check it appears in accounts list
|
||||
T47L_RES=\$(admin_curl "\$BASE/api/admin/relay/accounts")
|
||||
T47L_BODY=\$(body_of "\$T47L_RES")
|
||||
T47L_FOUND=\$(echo "\$T47L_BODY" | jq "[.accounts[] | select(.pubkey==\"\$T47_PUBKEY\")] | length" 2>/dev/null || echo "0")
|
||||
|
||||
if [[ "\$T47G_CODE" == "200" && "\$T47G_OK" == "true" && "\$T47G_LEVEL" == "write" && "\$T47L_FOUND" -ge 1 ]]; then
|
||||
note PASS "Granted write access, found in accounts list"
|
||||
PASS=\$((PASS+1))
|
||||
else
|
||||
note FAIL "grant: code=\$T47G_CODE ok=\$T47G_OK level=\$T47G_LEVEL found=\$T47L_FOUND"
|
||||
FAIL=\$((FAIL+1))
|
||||
fi
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test 48 — Admin revoke access + verify
|
||||
# ---------------------------------------------------------------------------
|
||||
sep "Test 48 — Admin revoke relay access"
|
||||
if [[ "\$ADMIN_RELAY_OK" != "true" ]]; then
|
||||
note SKIP "Admin relay endpoint not accessible"
|
||||
SKIP=\$((SKIP+1))
|
||||
else
|
||||
T48_RES=\$(admin_curl -X POST "\$BASE/api/admin/relay/accounts/\$T47_PUBKEY/revoke" \\
|
||||
-d '{"reason":"testkit T48 cleanup"}')
|
||||
T48_CODE=\$(code_of "\$T48_RES")
|
||||
T48_BODY=\$(body_of "\$T48_RES")
|
||||
T48_OK=\$(echo "\$T48_BODY" | jq -r '.ok' 2>/dev/null || echo "")
|
||||
if [[ "\$T48_CODE" == "200" && "\$T48_OK" == "true" ]]; then
|
||||
note PASS "Access revoked successfully"
|
||||
PASS=\$((PASS+1))
|
||||
else
|
||||
note FAIL "code=\$T48_CODE body=\$T48_BODY"
|
||||
FAIL=\$((FAIL+1))
|
||||
fi
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test 49 — Admin grant invalid pubkey → 400
|
||||
# ---------------------------------------------------------------------------
|
||||
sep "Test 49 — Admin grant invalid pubkey → 400"
|
||||
if [[ "\$ADMIN_RELAY_OK" != "true" ]]; then
|
||||
note SKIP "Admin relay endpoint not accessible"
|
||||
SKIP=\$((SKIP+1))
|
||||
else
|
||||
T49_RES=\$(admin_curl -X POST "\$BASE/api/admin/relay/accounts/not-a-hex-pubkey/grant" \\
|
||||
-d '{"level":"write"}')
|
||||
T49_CODE=\$(code_of "\$T49_RES")
|
||||
if [[ "\$T49_CODE" == "400" ]]; then
|
||||
note PASS "HTTP 400 for invalid pubkey"
|
||||
PASS=\$((PASS+1))
|
||||
else
|
||||
note FAIL "Expected 400, got \$T49_CODE"
|
||||
FAIL=\$((FAIL+1))
|
||||
fi
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test 50 — Admin grant invalid access level → 400
|
||||
# ---------------------------------------------------------------------------
|
||||
sep "Test 50 — Admin grant invalid access level → 400"
|
||||
if [[ "\$ADMIN_RELAY_OK" != "true" ]]; then
|
||||
note SKIP "Admin relay endpoint not accessible"
|
||||
SKIP=\$((SKIP+1))
|
||||
else
|
||||
T50_PUBKEY="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb50"
|
||||
T50_RES=\$(admin_curl -X POST "\$BASE/api/admin/relay/accounts/\$T50_PUBKEY/grant" \\
|
||||
-d '{"level":"superadmin"}')
|
||||
T50_CODE=\$(code_of "\$T50_RES")
|
||||
if [[ "\$T50_CODE" == "400" ]]; then
|
||||
note PASS "HTTP 400 for invalid access level"
|
||||
PASS=\$((PASS+1))
|
||||
else
|
||||
note FAIL "Expected 400, got \$T50_CODE"
|
||||
FAIL=\$((FAIL+1))
|
||||
fi
|
||||
fi
|
||||
|
||||
# ===========================================================================
|
||||
# FUTURE STUBS — placeholders for upcoming tasks (do not affect PASS/FAIL)
|
||||
# ===========================================================================
|
||||
# These are bash comments only. They document planned tests so future tasks
|
||||
# can implement them with the correct numbering context.
|
||||
#
|
||||
# FUTURE T41: Anonymous job always hits Lightning gate
|
||||
# FUTURE T51: 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 T42: Nostr-identified trusted identity → free response
|
||||
# FUTURE T52: 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 T43: Timmy initiates a zap
|
||||
# FUTURE T53: Timmy initiates a zap
|
||||
# POST to /api/identity/me/tip (or similar)
|
||||
# Assert Timmy initiates a Lightning outbound payment to caller's LNURL
|
||||
|
||||
|
||||
Reference in New Issue
Block a user