Commit Graph

13 Commits

Author SHA1 Message Date
1e2edeee77 [claude] Nostr identity lifecycle coverage T41–T45 (#55) (#106) 2026-03-24 02:20:34 +00:00
alexpaynex
4ca4fae3be Task #45: Deploy API server — VM deployment, production build index.js, FAIL=0 in both modes
## Changes

1. **testkit.ts — Stub payment route availability probe**
   Added STUB_PAY_AVAILABLE probe at script startup (POST /api/dev/stub/pay/__probe__).
   Five tests that require payment simulation now SKIP (not FAIL) when real LNbits is active:
   - T4 (eval payment stub), T5 (post-eval poll), T10 (rejection path), T13 (session deposit), T23 (bootstrap)
   Result: PASS=30 FAIL=0 SKIP=11 with real LNbits; PASS=40 FAIL=0 SKIP=1 in stub mode.

2. **build.ts — Output changed from index.cjs to index.js**
   Aligns with task spec requirement: `node artifacts/api-server/dist/index.js`.

3. **package.json — Removed "type": "module"**
   Necessary for dist/index.js (CJS format via esbuild) to load correctly in Node.js.
   Without this, Node 24 treats .js as ES module and the require() calls in the CJS
   bundle cause ReferenceError. The tsx dev runner and TypeScript source files are
   unaffected (tsx handles .ts imports independently of package.json type).

4. **artifact.toml — Run path updated to dist/index.js**
   Consistent with build output rename.

5. **artifact.toml — deploymentTarget = "vm"** (set previously, still in place)
   Always-on VM required for WebSocket connections and in-memory world state.

## Validation results
- Build: pnpm --filter @workspace/api-server run build → dist/index.js 1.6MB ✓
- Production run with LNBITS_URL set (real mode): PASS=30/41 FAIL=0 SKIP=11 ✓
- Production run without LNBITS_URL (stub mode): PASS=40/41 FAIL=0 SKIP=1 ✓
- Dev workflow: healthy (GET /api/healthz → status:ok) ✓
2026-03-20 01:43:50 +00:00
Replit Agent
abb8c50f23 fix: replace import.meta.url with process.cwd() in testkit.ts
import.meta.url is undefined when esbuild bundles to CJS format
(format: 'cjs' in build.ts). fileURLToPath(undefined) throws
ERR_INVALID_ARG_TYPE which crashed the production server on startup.

Fix: drop the _dirname derivation entirely and resolve TIMMY_TEST_PLAN.md
relative to process.cwd(), which is always the workspace root in both:
- dev: tsx runs from workspace root
- production: pnpm --filter ... run start runs from workspace root

Also removes the now-unused 'dirname' and 'fileURLToPath' imports.

Verified: rebuilt dist/index.cjs — 0 import.meta.url references remain.
GET /api/testkit/plan returns HTTP 200 from the production bundle.
2026-03-19 21:52:24 +00:00
alexpaynex
9573718da5 Update test summary and improve module import for better portability
Modify the testkit route to prefer bare module imports for 'nostr-tools' and update the test summary output to include a total test count.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 418bf6f8-212b-4bb0-a7a5-8231a061da4e
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 669fca88-557b-488a-99e4-05ffaa036833
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/9f85e954-647c-46a5-90a7-396e495a805a/418bf6f8-212b-4bb0-a7a5-8231a061da4e/67YBlXt
Replit-Helium-Checkpoint-Created: true
2026-03-19 21:16:48 +00:00
alexpaynex
6b6aa83e80 task/35: Testkit T25–T36 — Nostr identity + trust engine coverage (v2)
## 12 new tests (T25–T36) + stubs (T37–T40) added to testkit.ts

T25 — POST /identity/challenge: HTTP 200, nonce=64-char hex, expiresAt=ISO AND in future
      (future check: lexicographic ISO 8601 string comparison via `date -u`)
T26 — POST /identity/verify {}: HTTP 400, non-empty error
T27 — POST /identity/verify fake nonce: HTTP 401, error contains "Nonce not found"
T28 — GET /identity/me no header: HTTP 401, error contains "Missing"
T29 — GET /identity/me invalid token: HTTP 401 (Invalid/expired wording)
T30 — POST /sessions bad X-Nostr-Token: HTTP 401, no sessionId created
      (null-safe: jq returns literal "null" for absent fields — now treated as absent)
T31 — POST /jobs bad X-Nostr-Token: HTTP 401, "Invalid or expired"
T32 — POST /sessions anonymous: HTTP 201, trust_tier="anonymous", sessionId != "null"
T33 — POST /jobs anonymous: HTTP 201, trust_tier="anonymous", jobId != "null"
T34 — GET /jobs/:id (using T33_JOB_ID): HTTP 200, trust_tier non-null and "anonymous"
T35 — GET /sessions/:id (using T32_SESSION_ID): HTTP 200, trust_tier="anonymous"
T36 — Full challenge→sign→verify E2E: inline node CJS script; guarded under `set -e`:
      uses `T36_OUT=$(node ...) || T36_EXIT=$?` pattern to never abort the suite.
      tier=new, interactionCount=0, pubkey matches /identity/me — SKIP on node failure.

## Bug fixes vs original (code review REJECTED → v2):
- T36: `set -e` guard: was `T36_OUT=$(node ...); T36_EXIT=$?` — would abort suite
        on node exit-code != 0. Fixed: `T36_OUT="" T36_EXIT=0; ... || T36_EXIT=$?`
- T30: was `-z "$T30_SESSION"` — jq returns "null" not "" for missing fields.
        Fixed: `( -z "$T30_SESSION" || "$T30_SESSION" == "null" )`
- T25: missing future-time assertion. Added lexicographic ISO comparison:
        `date -u +"%Y-%m-%dT%H:%M:%SZ"` vs `expiresAt`
- FUTURE stubs: changed `# FUTURE T37 (Task...)` → `# FUTURE T37: ...` (greppable)
- T32/T33: added `!= "null"` guards on sessionId/jobId to reject jq "null" as absent

## TIMMY_TEST_PLAN.md: added "Nostr identity + trust engine (tests 25–36)" table

## TypeScript: 0 errors. All 12 tests smoke-tested individually against localhost:8080.
2026-03-19 21:14:01 +00:00
alexpaynex
c7bb5de5e6 task/35: Testkit T25–T36 — Nostr identity + trust engine coverage
## 12 new tests added to artifacts/api-server/src/routes/testkit.ts
Inserted before the Summary block, after T24 (cost ledger).

T25 — POST /identity/challenge: HTTP 200, nonce=64-char hex, expiresAt=ISO
T26 — POST /identity/verify {}: HTTP 400, non-empty error
T27 — POST /identity/verify fake nonce: HTTP 401, error contains "Nonce not found"
       (uses a plausible-looking event structure to hit the nonce check, not the
       signature check — tests the right layer)
T28 — GET /identity/me no header: HTTP 401, error contains "Missing"
T29 — GET /identity/me invalid token: HTTP 401 (Invalid/expired wording)
T30 — POST /sessions bad X-Nostr-Token: HTTP 401, "Invalid or expired", no sessionId
T31 — POST /jobs bad X-Nostr-Token: HTTP 401, "Invalid or expired"
T32 — POST /sessions anonymous: HTTP 201, trust_tier="anonymous"; captures T32_SESSION_ID
T33 — POST /jobs anonymous: HTTP 201, trust_tier="anonymous"; captures T33_JOB_ID
T34 — GET /jobs/:id (using T33_JOB_ID): HTTP 200, trust_tier non-null and "anonymous"
T35 — GET /sessions/:id (using T32_SESSION_ID): HTTP 200, trust_tier="anonymous"
T36 — Full challenge→sign→verify E2E: inline node CJS script generates ephemeral secp256k1
      keypair via nostr-tools CJS bundle, POSTs challenge, signs kind=27235 event with
      finalizeEvent(), verifies → nostr_token, GETs /identity/me, asserts tier=new,
      interactionCount=0, pubkey matches. Guard: SKIP if node not in PATH or script fails.

## nostr-tools import strategy
nostr-tools v2 is ESM-only. CJS workaround: the package ships a CJS bundle at
lib/cjs/index.js. T36 uses require() with the absolute path to that bundle.
Falls back to bare require('nostr-tools') for portability, exits with code 1 if
neither works — bash guard catches this and marks T36 SKIP (not FAIL).

## Stubs T37–T40 added as bash block comments after T36
Format: `# FUTURE T3N: <description>` so they are grepped easily.
Covers: GET /api/estimate (cost preview), anonymous Lightning gate, trusted free tier,
Timmy-initiates-zap. Does not affect PASS/FAIL totals.

## TIMMY_TEST_PLAN.md updated
New "Nostr identity + trust engine (tests 25–36)" section added to the test table.

## TypeScript: 0 errors. All 12 tests smoke-tested individually against localhost:8080.
T25-T35: all correct HTTP status codes and JSON fields verified via curl.
T36: full E2E verified — tier=new, icount=0, pubkey matches /identity/me response.
2026-03-19 21:09:50 +00:00
alexpaynex
56eb7bc56e task/34: Testkit self-serve plan + report endpoints
## Routes added to artifacts/api-server/src/routes/testkit.ts

### GET /api/testkit/plan
- Returns TIMMY_TEST_PLAN.md verbatim as text/markdown; charset=utf-8
- Reads file at request time (not on startup) so edits to the plan are picked
  up without server restart
- Path resolves via import.meta.url + dirname() → 4 levels up to project root
  (handles both dev/tsx and compiled dist/routes/ directories)

### GET /api/testkit/report
- Returns only the content from "## Report template" heading to end-of-file
- Content-Type: text/plain; charset=utf-8 — ready to copy and fill in
- Slice is found with indexOf("## Report template"); 500 if marker absent
- Uses the same PLAN_PATH as /api/testkit/plan (single source of truth)

## Deviation: __dirname → import.meta.url
Original plan said "resolve relative to project root regardless of cwd".
The codebase runs as ESM (tsx / ts-node with ESM), so __dirname is not
defined. Fixed by using dirname(fileURLToPath(import.meta.url)) instead —
equivalent semantics, correct in both dev and compiled output.

## AGENTS.md — Testing section added
Three-step workflow documented between "Branch and PR conventions" and
"Stub mode" sections:
  1. curl <BASE>/api/testkit/plan — fetch plan before starting
  2. curl -s <BASE>/api/testkit | bash — run suite after implementing
  3. curl <BASE>/api/testkit/report — fetch report template to fill in

## Unchanged
- GET /api/testkit bash script generation: untouched
- No new test cases or script modifications

## TypeScript: 0 errors. Smoke tests all pass:
  - /api/testkit/plan → 200 text/markdown, full TIMMY_TEST_PLAN.md content
  - /api/testkit/report → 200 text/plain, starts at "## Report template"
  - /api/testkit → 200 bash script, unchanged
2026-03-19 21:02:43 +00:00
alexpaynex
ae25bfdf71 Improve test reliability by adding explicit checks for bootstrap process
Update testkit.ts to add explicit failure conditions for missing payment hash in stub mode and to assert that the bootstrapJobId returned in the poll response matches the created ID.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 418bf6f8-212b-4bb0-a7a5-8231a061da4e
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 9114d92d-daf7-42ae-a3f7-be296300efa5
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/9f85e954-647c-46a5-90a7-396e495a805a/418bf6f8-212b-4bb0-a7a5-8231a061da4e/Q83Uqvu
Replit-Helium-Checkpoint-Created: true
2026-03-19 04:08:01 +00:00
alexpaynex
031ca5a5c3 task(#24): Bootstrap route + cost-ledger testkit coverage — 29/29 PASS
Task: Add T23 (bootstrap stub flow) and T24 (cost-ledger completeness) to the
testkit, bringing total from 27/27 to 29/29 PASS with 0 FAIL, 0 SKIP.

## What was changed
- `artifacts/api-server/src/routes/testkit.ts`:
  - Updated audit-log comment block to document T23 + T24 additions.
  - Inserted Test 23 after T22 (line ~654):
      POST /api/bootstrap → assert 201 + bootstrapJobId present.
      Guard on stubMode=true; SKIP if real DO mode (prevents hanging).
      Stub-pay the paymentHash via /api/dev/stub/pay/:hash.
      Poll GET /api/bootstrap/:id every 2s (20s timeout) until
      state=provisioning or state=ready; assert message field present.
  - Inserted Test 24 after T23:
      Guarded on STATE_T6=complete (reuses completed job from T6).
      GET /api/jobs/:id, extract costLedger.
      Assert all 8 fields non-null: actualInputTokens, actualOutputTokens,
        totalTokens, actualCostUsd, actualAmountSats, workAmountSats,
        refundAmountSats, refundState.
      Honest-accounting invariant: actualAmountSats <= workAmountSats.
      refundAmountSats >= 0.
      refundState must match ^(not_applicable|pending|paid)$.

## No deviations from task spec
- T23 guard logic matches spec exactly (stubMode check before poll).
- T24 fields match the 8 specified in task-24.md plus the invariants.
- No changes to bootstrap.ts or jobs.ts — existing routes already correct.

## Test run result
29/29 PASS, 0 FAIL, 0 SKIP (fresh server restart, rate-limit slots clean).
T23: state=provisioning in 1s. T24: actualAmountSats(179)<=workAmountSats(182),
refundAmountSats=3, refundState=pending.
2026-03-19 04:04:49 +00:00
4f7a5e9998 test: audit testkit — remove T3b inflation, add T17-T22 (27/27 PASS) (#32) 2026-03-18 22:34:12 -04:00
83a2ec19e2 fix(testkit): macOS compat + fix test 8c ordering (#24) 2026-03-18 21:01:13 -04:00
alexpaynex
ab2cc06a79 Add session mode for pre-funded request processing
Implement session-based API endpoints for creating, managing, and interacting with pre-funded sessions, including deposit and top-up invoice generation, macaroon authentication, and per-request debiting of compute costs.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 418bf6f8-212b-4bb0-a7a5-8231a061da4e
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 2dc3847e-7186-4a22-9c7e-16cd31bca8d9
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/9f85e954-647c-46a5-90a7-396e495a805a/418bf6f8-212b-4bb0-a7a5-8231a061da4e/sPDHkg8
Replit-Helium-Checkpoint-Created: true
2026-03-18 20:00:24 +00:00
alexpaynex
fc4fd50e33 Add automated testing flow to reduce manual effort
Integrate a new testkit endpoint and update package.json scripts to enable automated testing via `pnpm test` and `pnpm test:prod`, including a new test case for request body size limits.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 418bf6f8-212b-4bb0-a7a5-8231a061da4e
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 60472e18-59b7-4877-a9a2-16381573ab68
Replit-Helium-Checkpoint-Created: true
2026-03-18 17:43:01 +00:00