Repository navigation
fix(testing): dispatch json nested-Promise guard + scoped routeMap for flight primitives (#582) - #583
Merged
Merged
Conversation
7 tasks
commit: |
…r flight/handler primitives (#582) Two `@rangojs/router/testing` parity gaps from #582 (split from #572). 1. dispatch() json route lacked production's nested-Promise guard. Production (response-route-handler) serializes a json() result with a replacer that throws RESPONSE_NOT_SERIALIZABLE on a nested unresolved Promise (the forgotten-await footgun: `() => ({ data: fetchSomething() })`); dispatch did a bare JSON.stringify and shipped `{"data":{}}` green while production 500s. Extracted the replacer into a shared lightweight module (rsc/json-route-result.ts, depends only on errors.js) used by BOTH, so a dispatch json test fails exactly where production fails. dispatch's catch already maps the thrown RouterError to the same problem+json 500. The helper lives in its OWN module, not response-route-handler.ts: importing the latter into dispatch dragged its heavy runtime graph, which transitively reaches a Vite virtual module and breaks a plain (non-Vite) vitest import (caught: run-in-request-context.test.ts went red). 2. The testing primitives leaked ctx.reverse to the GLOBAL route map (order-dependent on whatever router registered last). Added a scoped `routeMap` option to renderToFlightString/renderServerTree (mirroring renderHandler), threaded through setRequestContextParams(params, routeName, routeMap?) -- backward compatible (production callers omit it and keep getGlobalRouteMap()). renderHandler now also passes routeMap to setRequestContextParams, so a nested server component reading getRequestContext().reverse scopes to the same map as the handler's ctx.reverse -- all three primitives now scope reverse uniformly. Tests (red-before-green): a dispatch json route returning a nested Promise -> a problem+json 500 (not a green {}); ctx.reverse() scoped to a provided routeMap under renderToFlightString, renderServerTree, AND a nested server component in renderHandler (vs "Unknown route" against the empty global map). Docs: flight skill routeMap option, feature-file-map new module. Out of #582 this PR: item 8 (renderRoute synthetic-id stamping) is already addressed by #581 via a stable WeakMap + a documented input-mutation contract; item 9 (dispatch executionContext=undefined) is correct for an in-process harness with no platform execution context. Cross-seam + self-review items remain in #582. Full gate green: typecheck, test:unit:all (incl. RSC suites), lint, format.
ivogt
force-pushed
the
fix/testing-primitive-parity-582
branch
from
June 14, 2026 13:37
d108a9a to
7553b6a
Compare
Collaborator
Author
|
Skipped (noted, not applied): the repeated thenable duck-check idiom (no exported helper exists; codebase-wide, out of scope) and a top-of-file overview comment that is still accurate. Full gate re-run green: typecheck, test:unit:all (incl. RSC), lint, format. |
4 tasks done
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.
Addresses two
@rangojs/router/testingparity gaps from #582 (the testing-refinements split from #572). Both are cases where a testing primitive diverged from production, so a test could pass while the real router would behave differently — defeating the dogfood guarantee.1.
dispatch()json route lacked production's nested-Promise guardProblem (consumer-observable): A
json()response-route handler with a forgottenawaitreturns a nested unresolved Promise. Production (response-route-handler.ts) serializes with a replacer that throwsRESPONSE_NOT_SERIALIZABLE→ a problem+json 500.dispatch()did a bareJSON.stringify, which serializes a Promise to{}— so the test went green with{"data":{}}while production 500s.Fix: extracted production's replacer into a shared lightweight module
rsc/json-route-result.tsand routed BOTH production anddispatchthrough it, so they cannot drift.dispatch's existing catch already maps the thrownRouterErrorto the same problem+json 500.2.
renderToFlightString/renderServerTreeleakedctx.reverseto the global route mapProblem: these primitives rebuilt
ctx.reverseagainstgetGlobalRouteMap(), so a server component that callsctx.reverse()/href()was order-dependent on whatever router registered last in the test process.renderHandleralready has a scopedrouteMapoption; the flight primitives did not.Fix: added a
routeMap?: Record<string, string>option (mirroringrenderHandler), threaded throughsetRequestContextParams(params, routeName, routeMap?). Backward compatible — every production caller omits it and keepsgetGlobalRouteMap().Semantics
dispatchjson + nested Promise200 {"data":{}}(silent)500problem+jsonRESPONSE_NOT_SERIALIZABLE(= production)renderToFlightString/renderServerTreectx.reverserouteMapoption when provided; global otherwisegetGlobalRouteMap()default)Test plan
dispatchjson route returning a nested Promise → problem+json 500 (not a green{}); proven red against the pre-fix bareJSON.stringify.renderToFlightStringANDrenderServerTreectx.reverse()scoped to arouteMapresolves/scoped/products/:id; proven red (Unknown route: "product") against the empty global map without the pass-through.virtual:import leak (run-in-request-context.test.ts).test:unit:all(incl. RSC suites —test:unit:rscruns both flight files), lint, format.Notes / deferred (still in #582)
WeakMap+ a documented input-mutation contract (render-route.tsx), not re-opened here (cloning a handle risks breaking method/identity).executionContext=undefined) — correct for an in-process harness with no platform execution context.loaderParentIdregex) items remain tracked in Gap-review follow-ups (remaining): @rangojs/router/testing refinements + cross-seam + self-review #582.