Context
#3025 tracked the web test runner mutating the live application.wheels struct (swap → run → restore in vendor/wheels/tests/runner.cfm). Its titled approach — a request-scoped config overlay — was ruled infeasible by the adversarial analysis in that thread (blockers B1–B9: dialect adapter baked per model class at init, app-scoped model cache, ~24+ direct application.wheels.X reads bypassing $get, populate/migrator DDL outside any overlay, browser/parallel tests requiring the global swap). Blast radius of the full overlay ≈ 60–110 call sites.
PR #3373 shipped the maintainer-endorsed safe slice: the swap window is now serialized under an exclusive named lock (with ParallelRunner re-entrancy detection), and the orphaned RocketUnit twin (vendor/wheels/rocketunit_tests/Test.cfc) was deleted. #3025 was auto-closed by that PR's squash title; this issue carries the remainder.
Residual gap (what this issue tracks)
The lock serializes test-vs-test only. A normal request running concurrently with a test run still reads swapped (test) configuration for the duration of the run. True isolation requires the direction the #3025 analysis judged superior: running the test suite in a separate application context (own application name/scope, own model/dialect caches, own datasource wiring) so the live app's application.wheels is never touched.
Design questions to settle
- Bootstrapping a second Wheels application context inside the same servlet context (application name derivation,
onApplicationStart reuse, teardown).
- How browser/parallel specs (fresh top-level HTTP requests) address the isolated context (dedicated URL prefix vs header-based routing).
- Migrator/populate DDL targeting the test datasource without global state.
- CLI runner (
wheels test) parity — it uses a different chain than runner.cfm and must not regress.
References
Context
#3025 tracked the web test runner mutating the live
application.wheelsstruct (swap → run → restore invendor/wheels/tests/runner.cfm). Its titled approach — a request-scoped config overlay — was ruled infeasible by the adversarial analysis in that thread (blockers B1–B9: dialect adapter baked per model class at init, app-scoped model cache, ~24+ directapplication.wheels.Xreads bypassing$get, populate/migrator DDL outside any overlay, browser/parallel tests requiring the global swap). Blast radius of the full overlay ≈ 60–110 call sites.PR #3373 shipped the maintainer-endorsed safe slice: the swap window is now serialized under an exclusive named lock (with ParallelRunner re-entrancy detection), and the orphaned RocketUnit twin (
vendor/wheels/rocketunit_tests/Test.cfc) was deleted. #3025 was auto-closed by that PR's squash title; this issue carries the remainder.Residual gap (what this issue tracks)
The lock serializes test-vs-test only. A normal request running concurrently with a test run still reads swapped (test) configuration for the duration of the run. True isolation requires the direction the #3025 analysis judged superior: running the test suite in a separate application context (own application name/scope, own model/dialect caches, own datasource wiring) so the live app's
application.wheelsis never touched.Design questions to settle
onApplicationStartreuse, teardown).wheels test) parity — it uses a different chain thanrunner.cfmand must not regress.References