Repository navigation
Support nested route fragment separate testing - #7
Merged
Merged
Conversation
5 tasks
The route-fragment dispatch helpers on this branch required dispatch support that was previously only available on a yesodweb/yesod git branch. That support is now published as yesod-core-1.7.0.0 on Hackage, so the git pin is no longer needed. - stack.yaml: replace the git subdir pin with yesod-core-1.7.0.0 - hspec-yesod.cabal: bump lower bound to >= 1.7.0.0, since the new route helpers require the 1.7 dispatch API Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
parsonsmatt
marked this pull request as draft
June 16, 2026 21:59
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Replace the @SInCE TODO/blank tags on RequestBuilderFor and setUrlNested with 0.3.0.0 - Replace the scratch design-musings comment above mkApplicationFor with a real Haddock description - Delete notes.md scratch file - Expand the 0.3.0.0 changelog with the practical/breaking API changes Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses findings from the max-effort review of the route-fragment work. Resource/performance: - Cache the YesodRunnerEnv per test (new yedRunnerEnv field) instead of rebuilding it on every request. mkYesodRunnerEnv spawns auto-update worker threads, so per-request construction leaked threads proportional to the request count. The cache is invalidated whenever the site changes. - Swap yreGetMaxExpires for a thread-free recomputation. Its default is a 24h auto-update worker that, once read by dispatch, lingers for the life of the test process; the replacement lets the RTS reap the unused worker. - Add getRunnerEnv to expose the cached environment. Correctness / robustness: - Decode request bytes leniently in the debug/preview formatters so a UnicodeException can't mask the real assertion message. - failure now carries a real message and uses withFrozenCallStack, so the assertion is blamed on the caller; fix the "libary" typo. - Replace a partial `head` in addToken_ and a partial `error` in setUrl with descriptive failures. - voidRequestBuilderUrl maps the existing Maybe rather than forcing Just (). - Drop the unused RankNTypes pragma; scope away main.hs's module-wide -Wno-incomplete-uni-patterns. Tests / docs: - Fix the getFooShowR fixture label (was a getFooEditR copy-paste). - Split the double-setUrl nested-dispatch test so setUrl and setUrlNested are each exercised independently. - Document why NestedRouteDispatchSpec.YesodDispatch is kept (verifies the full app dispatch still compiles alongside the fragment dispatch). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The testGetMaxExpires replacement installed a max-expires getter whose RFC1123 timestamp used "%Z" (renders "UTC") instead of a literal "GMT", diverging from yesod-core's getCurrentMaxExpiresRFC1123. This caused a regression in downstream test suites (66 failures in mercury-web-backend). Back it out entirely and use the environment as built by mkYesodRunnerEnv; the lingering 24-hour max-expires auto-update worker will be addressed upstream in yesod-core instead. The per-test YesodRunnerEnv caching (yedRunnerEnv / getRunnerEnv) is retained. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The per-test YesodRunnerEnv cache was only invalidated by testModifyFoundationAndMiddleware. Downstream harnesses (e.g. mercury-web-backend's localApp / mockStartTime) replace the dispatched foundation via a direct state update, bypassing that invalidation, so getRunnerEnv served an environment built from the original site and dispatch ignored the swapped-in foundation (66 test failures involving mocked time and per-request app state). Key the cache on makeStableName of yedSite instead: any time the site is a different heap object than the one the cached env was built from, the env is rebuilt. yedSite is strict (always WHNF), so its StableName is stable across requests while the site is unchanged. StableName equality holds only for the same object, so a stale env is never served; the worst case is a redundant rebuild. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The cached YesodRunnerEnv was only an optimization, and it broke downstream test suites: the cache was invalidated only by testModifyFoundationAndMiddleware, but harnesses like mercury-web-backend swap the dispatched foundation by updating yedSite directly (localApp / mockStartTime), so dispatch ran against a stale site and ignored time stubs / per-request app state (66 failures). Keying the cache on the site's StableName would fix correctness but leak StableName into the exported YesodExampleData(..) API. Since the underlying issue is that mkYesodRunnerEnv's auto-update workers aren't cleanly closeable (a yesod-core concern), just build the env fresh per request via getRunnerEnv (the proven pre-caching behavior) and remove the yedRunnerEnv field entirely. Idle workers are reaped by the RTS. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
parsonsmatt
marked this pull request as ready for review
June 18, 2026 21:20
megaparsec-9.8.0 (pulled in transitively via yesod-form) fails to compile
in the CI build plan ("Variable not in scope" in Text.Megaparsec.Stream),
failing the Install-dependencies step across the whole GHC matrix. Pin
below it until a fixed megaparsec ships, then remove this cabal.project
constraint. Unrelated to this PR's code.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Motivation
This PR provides separate testing of route fragments: you can test a subset of routes without depending on the full
YesodDispatchfor your web application. OurYesodDispatchrequires compiling over 10k modules, but mostHandlermodules depend on only 2-3k modules in our codebase, so this makes testing dramatically nicer.It builds on the nested-route-separation work that shipped in
yesod-core-1.7.0.0(originally yesod #1887). The branch was previously pinned to ayesodweb/yesodgit commit; it now uses the released package from Hackage.What you get
setUrlNestedand the generalizedRequestBuilderFor url site, which carries the route-fragment type, so a handler spec can depend only on the cheapYesodDispatchNested FooRinstead of the whole app's dispatch.get/post/postBody/performMethod/requestnow dispatch viayesod-core'sUrlToDispatch url site.getRunnerEnv, which builds aYesodRunnerEnvfor the current test's site.Breaking changes
yesod-core >= 1.7.0.0.RequestBuilderDatagained aurltype parameter (RequestBuilderData url site);getLatestRequest/requireLatestRequestreturnRequestBuilderData () site.get/post/postBody/performMethod/requestrequireUrlToDispatch url sitein place ofRedirectUrl site url./, nowrequestfails with a clear error.TestApp,mkTestApp, and theyedCreateApplicationfield.Runner-env handling
The WAI application for each request is built from a
YesodRunnerEnvconstructed for the currentyedSiteviamkYesodRunnerEnv(exposed asgetRunnerEnv). It is built fresh per request so it always reflects the current foundation — including per-request changes made by harnesses that swap the site to mock time or adjust app state.mkYesodRunnerEnvspawnsauto-updateworker threads; idle ones are reaped by the RTS. The one that genuinely lingers — the 24-hourmax-expiresworker, which sleeps a full day before it can notice it's idle — is fixed upstream in yesodweb/yesod#1923 (a patch release that recomputes the value instead of caching it behind a thread). After that lands, per-request construction is fully thread-free.Review notes
This branch went through a full review pass; fixes included lenient decoding in debug formatters,
withFrozenCallStackon assertion failures, removal of partialhead/errorcalls, and assorted test/doc cleanups. Deferred follow-up: #9 (hardensetUrlURL parsing withnetwork-uri).See
ChangeLog.mdfor the full0.3.0.0entry.🤖 Generated with Claude Code