feat: SDK conformance wave 3 — Node SDKs via bun - #66
Merged
Conversation
New suites under conformance/node/ (just conformance-node; bun added to CI) that boot the REAL stunt binary — stunt up, runtime file, probe-bound ports — so they double as end-to-end tests of the CLI: - stripe-node: typed creates, autoPagingEach walking has_more, PI create+confirm, webhook verification through constructEventAsync (signature verifies, data.object parses, tamper rejected). - octokit: issue CRUD and octokit.paginate following the adapter's Link headers page by page. - twilio-node: lifecycle driven by SDK fetches (queued->sent/ delivered), the +15005550001 magic number -> failed, and callbacks verified by twilio-node's own validateRequest. Findings: - REAL BUG: github-style's Link pagination headers hardcoded https://api.github.com absolute URLs — clients that FOLLOW the header (octokit.paginate) were sent to the real GitHub and 401'd; go-github survived only because it parses page params without following. Link targets now build from the serving host (req["host"]), like a real GitHub Enterprise. - just hazard: conformance-node used /tmp/stunt-ci, which never writes — only the lint recipes do — so the recipe could run a stale binary from an old lint run (that's exactly what produced a session-long ECONNRESET mystery: the stale pre-v0.45 binary still had the form-parser panic). The recipe builds fresh now. - Known-environment quirk documented in the suite: under bun, stripe-node's fetch layer drops the url form param when it names a live same-process listener (reproduced with a fresh client against an echo server, independent of stunt; plain bun fetch does not do it). Registration tries the SDK first and falls back to a raw one-shot POST; everything else is SDK-driven. 24 expects across 3 suites, green twice consecutively; Go conformance + full main-module suite green.
Review findings on PR #66, all fixed: - Link scheme: handlers now read X-Forwarded-Proto (the subdomain proxy sets it) with the loopback-host inference only as the port-mode fallback — the heuristic alone was wrong for no-TLS subdomain mode. Same fix applied to shopify's page_info URL (hardcoded https) and dune's next_uri (hardcoded http). - github Link round-trips every filter/query param except page — a Link-following client no longer loses state=closed&q= between pages. A missing host now omits the Link instead of redirecting to the real api.github.com. - justfile: bun install --frozen-lockfile fails loudly (the silent fallback could silently resolve newer semver in CI). - harness: stunt-up stderr is captured and included in boot failures (was a bare 20s timeout); failure paths stop the sink and remove the tmpdir; readiness fetches carry a 500ms abort and a hard failure after the loop (was silent proceed); the port probe stays bound until just before spawn. - suites: stripe paging asserts distinct ids with no valve trip (catches both has_more=false and ignored starting_after); the registration fallback warns when it fires; twilio drives to delivered and REQUIRES the second callback (a dropped unsigned terminal callback now fails instead of slipping through); timeouts bumped to 120s for CI headroom. 27 expects green twice consecutively; Go conformance + engine suites on all three touched adapters green.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
SDK conformance wave 3 — the Node SDKs, via bun (
just conformance-node; bun added to CI). The suites boot the realstuntbinary —stunt up, runtime file, probe-bound ports — so they double as end-to-end tests of the CLI itself, not just the engine.autoPagingEachwalkinghas_more, PaymentIntent create+confirm, and webhook verification through the SDK'sconstructEventAsync— signature verifies,data.objectparses, tampered payloads rejectedoctokit.paginatefollowing the adapter'sLinkheaders page by pagequeued → sent/delivered), the+15005550001magic number →failed, and status callbacks verified by twilio-node's ownvalidateRequestFindings
Linkpagination headers hardcodedhttps://api.github.comabsolute URLs. Any client that follows the header —octokit.paginate— was sent to the real GitHub and 401'd. (go-github survived only because it parses the page params without following the URL, which is why the Go wave never saw it.) Link targets are now built from the serving host (req["host"]) — what a real GitHub Enterprise does.justhazard worth knowing: the recipe originally ran/tmp/stunt-ci, whichjust buildnever writes — only the lint recipes do — so it executed a stale binary from an ancient lint run, still carrying the v0.45-era form-parser panic. That stale binary produced anECONNRESETred herring that cost a long diagnostic detour. The recipe now builds its own binary explicitly.urlform param when its value names a live same-process listener — reproduced with a fresh client against a bare echo server, with the param present at the SDK's own encode step. Webhook registration tries the SDK first and falls back to a raw one-shot POST; everything downstream of registration is SDK-driven.Verification