A comprehensive test suite for @momics/iroh-http covering HTTP compliance, node lifecycle, error handling, and stress testing across all supported runtimes (Node.js, Deno, Tauri).
Data-driven tests validating HTTP semantics (RFC 9110). Extends the upstream corpus to 103 identified test cases:
| Category | Cases | What it tests |
|---|---|---|
| Status Codes | 22 | All major HTTP status codes across 2xx/3xx/4xx/5xx |
| HTTP Methods | 9 | GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD, custom verbs |
| Body Handling | 19 | Echo, UTF-8, emoji, JSON, newlines, special chars, sizes 1B→5MB |
| Request Headers | 9 | Custom, case-insensitive, empty, long (8KiB), content-type |
| Response Headers | 6 | Single/multiple headers, Content-Type forwarding |
| Peer-Id Security | 4 | Injection, anti-spoofing, consistency, format validation |
| Path Handling | 12 | Trailing slashes, query strings, URL encoding, dot segments |
| Concurrent | 4 | 3/5/10 parallel requests, concurrent POST with body |
| Sequential | 3 | Connection reuse, mixed methods, mixed status codes |
| Content-Type | 4 | JSON, text/plain, octet-stream, charset |
| Streaming | 4 | 64KB, 256KB, 1MB, 5MB streamed responses |
| Edge Cases | 7 | Unusual status codes, long paths, many headers |
Imperative tests for node creation, shutdown, and resource management:
createNode()returns valid node with publicKey;secretKeypresent only when akeyis supplied- publicKey is consistent across accesses
- Two nodes get different keys
node.addr()returns correct infonode.close()resolves cleanly,node.closedpromise settles- Double close is idempotent (no crash)
serve()returns handle, handler receives valid Request objectsfetch()returns valid Response with readable body- 10 sequential create/close cycles (leak detection)
Tests error scenarios and recovery:
- Handler throws → client gets error/500
- Handler returns rejected promise → client gets error
- Server stays alive after handler error (recovery)
- Fetch to unknown peer → rejection
- Fetch with pre-aborted signal → immediate rejection
- Fetch with mid-request abort → cancellation
- Handler returning non-Response → no crash
- Null body on 200 → no crash
- Peer-Id header cannot be spoofed by client
Tests behavior under load:
- 50 concurrent GETs → all succeed
- 20 concurrent POSTs with body → all echo correctly
- 100 sequential GETs → connection reuse
- 1MB body round-trip with byte-level verification
- 5 concurrent 256KB transfers
- 20 rapid create/close cycles with fetch in each
- 5 independent node pairs running concurrently
- 30 concurrent mixed-method requests
tests/
├── run-all.sh # Run all suites for Node + Deno (+ --tauri)
├── README.md
│
├── suites/ # Shared cross-runtime test suites
│ ├── lifecycle.mjs # Node creation, shutdown, serve lifecycle
│ ├── errors.mjs # Handler failures, unknown peers, abort
│ ├── stress.mjs # Concurrent/sequential load, regressions
│ ├── events.mjs # EventTarget, diagnostics
│ ├── sessions.mjs # WebTransport sessions
│ ├── keys.mjs # PublicKey/SecretKey, sign/verify
│ ├── discovery.mjs # mDNS/DNS peer discovery
│ ├── adapter-validation.mjs # shared adapter validation contract
│ └── self-fetch.mjs # same-node request behavior
│
├── runners/ # Thin per-runtime wrappers
│ ├── node.mjs # Binds suites to node:test + node:assert
│ ├── deno.ts # Binds suites to Deno.test + @std/assert
│ └── tauri.ts # Binds suites to Tauri webview
│
├── tauri-app/ # Tauri host app that runs the shared suites
│ ├── index.html # inside a real WebKit webview, driven by
│ ├── src/main.ts # WebdriverIO + tauri-driver on Linux CI.
│ ├── wdio.conf.js # WebdriverIO config (builds app, spawns driver)
│ ├── e2e/specs/ # WebDriver spec asserting zero failures
│ └── src-tauri/ # Minimal Tauri shell (+ exit_with_code command)
│
└── http-compliance/ # Data-driven HTTP tests
├── cases.json # 103 identified test cases (RFC 9110)
├── handler.mjs # Shared compliance server routes
├── assertions.mjs # Shared assertion engine
├── run-node.mjs # Node same-process runner
├── run-deno.ts # Deno same-process runner
├── run-tauri.ts # Tauri webview runner
├── run.sh # Cross-runtime orchestrator
├── runner.ts # Deno compliance runner helper
├── server.mjs # Standalone Node server
├── server.deno.ts # Standalone Deno server
├── client.mjs # Standalone Node client
├── client.deno.ts # Standalone Deno client
├── tauri-test.html # Tauri test entry page
└── deno.json # Deno workspace config
-
DRY — Shared suites in
tests/suites/contain all cross-runtime test logic. Thin runners intests/runners/bind them to each runtime's test API. -
Categorized — Each test concern lives in its own suite file. Adapter-specific tests live in each package's
test/adapter.test.*. -
CI-friendly — All scripts exit with code 1 on failure. The
run-all.shorchestrator runs everything. The CI workflow runs each category as a separate step for clear failure attribution.
./tests/run-all.sh./tests/run-all.sh --node # Node only
./tests/run-all.sh --deno # Deno only
./tests/run-all.sh --tauri # Tauri webview (requires tauri-driver)# Shared suites (Node)
node --test tests/runners/node.mjs
# Shared suites (Deno)
deno test -A tests/runners/deno.ts
# HTTP compliance (Node, same-process)
node tests/http-compliance/run-node.mjs --verbose
# HTTP compliance (Deno, same-process)
deno run -A tests/http-compliance/run-deno.ts
# Adapter-specific (Node)
node --test packages/iroh-http-node/test/adapter.test.mjs
# Adapter-specific (Deno)
deno test -A packages/iroh-http-deno/test/adapter.test.ts
# Cross-runtime compliance
bash tests/http-compliance/run.shThe shared suites also run inside a real Tauri webview via WebdriverIO +
tauri-driver. This is exercised automatically by the test-tauri job in
.github/workflows/extended-tests.yml and can be run locally on Linux:
# Prerequisites (Linux):
# cargo install tauri-driver --locked
# sudo apt-get install -y libwebkit2gtk-4.1-dev webkit2gtk-driver xvfb
./tests/run-all.sh --tauriThe legacy HTTP-compliance webview entry is still available for manual runs:
cd tauri && npm install
cargo tauri dev
# Navigate to /tests/http-compliance/tauri-test.htmlURL params: ?filter=status&verbose=true&bail=true&ci=true
node tests/http-compliance/run-node.mjs --filter status --verbose
node tests/http-compliance/run-node.mjs --bail
deno run -A tests/http-compliance/run-deno.ts --filter peer-id| Field | Type | Description |
|---|---|---|
concurrent |
number | Run N copies of the request in parallel |
sequential |
number | Run N copies sequentially |
repeat |
number | Run N copies with optional body equality assertion |
requests |
array | Multi-step sequential test with per-step expectations |
assertAllBodiesEqual |
boolean | Assert all repeated responses have identical bodies |
When adding new cross-runtime tests, add them to the appropriate suite file
in tests/suites/. Both runners (tests/runners/node.mjs and
tests/runners/deno.ts) will automatically pick them up.
For adapter-specific tests (e.g., Deno FFI functions not available in Node),
add them to the package's test/adapter.test.* file.
{ "id": "unique-test-id", "description": "Human-readable description", "request": { "method": "GET", "path": "/route", "headers": { "x-custom": "value" }, "body": null | "string" | { "fill": 65536 } }, "response": { "status": 200, "bodyExact": "exact match", "bodyNotEmpty": true, "bodyNot": "must not equal", "bodyContains": "substring", "bodyMatchesRegex": "^[a-z]+$", "bodyLengthExact": 1024, "bodyMinLength": 1, "headers": { "content-type": "application/json" } } }