Skip to content

fix(test): isolate the web runner in a separate application scope (#3374) - #3383

Merged
bpamiri merged 3 commits into
developfrom
cursor/issue-3374-test-runner-isolation-f85e
Aug 20, 2026
Merged

fix(test): isolate the web runner in a separate application scope (#3374)#3383
bpamiri merged 3 commits into
developfrom
cursor/issue-3374-test-runner-isolation-f85e

Conversation

@bpamiri

@bpamiri bpamiri commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

The web test runner no longer mutates the live application.wheels when Application.cfc includes the shipped snippet. Test-runner URLs, TestClient, and browser specs bind a separate CFML application name (_wheelsTest) so concurrent normal requests keep production config.

This is the #3374 successor to #3025 / #3373. A request-scoped overlay remains infeasible (blockers B1–B9). Isolation is a second application context inside the same servlet — verified against those blockers rather than guessed.

Related Issue

Closes #3374

Type of Change

  • Bug fix
  • Enhancement to existing feature
  • Documentation update

Approach (verified against B1–B9)

Changing this.name in Application.cfc's constructor is the only CFML mechanism that yields a separate application scope (own model/dialect caches, own routes, own datasource wiring). That sidesteps:

  • B1–B3 — dialect adapter, column schema, and automaticValidations stay baked per isolated app; no StructClear(models) of the live cache
  • B8–B9 — routes and path swaps live on the test application only
  • B6 — browser / ParallelRunner / TestClient address the isolated context via path (/wheels/core/tests, /wheels/app/tests) or X-Wheels-Test-Context or WHEELS_TEST_CONTEXT cookie. Header (not URL prefix) is required for Playwright: in-page navigations and form posts would drop a prefix/query marker; extraHTTPHeaders persists across the context.

$get() overlay / tenant datasource reuse was re-checked and rejected: the tenant override swaps only the datasource name after dialect SQL is generated, and cannot re-bake adapters (B1).

What shipped

  • vendor/wheels/events/testcontext.cfm — constructor include (no CFC new here: mappings are not guaranteed during Application.cfc construction)
  • vendor/wheels/events/TestContext.cfc — runtime twin (same suffix / header / cookie); unit-testable
  • Demo + wheels new + example Application.cfc include the snippet after config/app.cfm
  • WheelsTest.$testClient() sends the header + cookie by default; $testClient(testContext=false) hits the live app. Local is named httpClient (not client) so Lucee does not throw "client scope is not enabled" (anti-pattern 11)
  • BrowserTest sets Playwright extraHTTPHeaders + isolation cookie
  • ParallelRunner partition GETs send the header (path already matches)
  • fix(test): serialize web test-runner config swap under a named lock (#3025) #3373 named-lock swap remains as the fallback for apps that have not applied the include, and still serializes test-vs-test inside the isolated app
  • Runner swap now set(loadBrowserTestFixtures=false) and clears controller / object-file caches so isolated-app boot does not leave controller("wheels") resolving to the browser-fixture Controller.cfc stub
  • Interface specs instantiate controller("Test") (a real test-asset CFC) instead of the no-file wheels fallback
  • Specs in vendor/wheels/tests/specs/internal/TestRunnerIsolationSpec.cfc
  • Changelog fragment + MDX / .ai / CLAUDE.md updates
  • tools/test-local.sh internal / interfaces map to the matching wheels.tests.specs.* directories

Leftovers (not papered over)

  • Existing apps must add the Application.cfc include to opt in. Without it, behavior is unchanged (fix(test): serialize web test-runner config swap under a named lock (#3025) #3373 lock + live-scope swap).
  • Per-datasource application names (_wheelsTest_sqlite) were deferred: browser requests have no ?db= and would bind a different app than the runner.
  • Request-scoped overlay is still blocked by B1–B9 (not attempted).
  • Legacy in-process $wheelsRunner (wheels.Test) still swaps if invoked; wheels test uses HTTP /wheels/core|app/tests and is isolated.
  • First test request cold-starts the isolated application (onApplicationStart of the second name).

Feature Completeness Checklist

  • DCO sign-off
  • Tests — unit (path/header/cookie), structural (wiring), behavioral (live /wheels/info sees a different application name)
  • Framework Docsfixtures-and-test-data.mdx, running-tests-locally.mdx
  • AI Reference Docs.ai/wheels/testing/browser-testing.md
  • CLAUDE.md — Testing Quick Reference
  • Changelog fragmentchangelog.d/test-runner-app-isolation.fixed.md

Test Plan

Local Lucee 7 + SQLite via tools/test-local.sh:

  • internal — 92 passed (isolation + swap-lock + TestClient)
  • interfaces — 57 passed
  • full core suite — 4801 passed

Also verified over HTTP: /wheels/info?format=json returns wheels-dev; the same URL with X-Wheels-Test-Context: 1 or WHEELS_TEST_CONTEXT=1 returns wheels-dev_wheelsTest.

Open in Web Open in Cursor 

)

Bind test-runner, TestClient, and browser requests to
<this.name>_wheelsTest so the live application.wheels is never swapped.
Keep the #3373 named lock as a fallback for apps without the
Application.cfc include.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Peter Amiri <peter@alurium.com>
cursoragent and others added 2 commits August 20, 2026 13:55
Rename the $testClient local so Lucee no longer throws
"client scope is not enabled" on every TestClient call.
Resolve Application.cfc paths via the /config mapping
instead of walking GetDirectoryFromPath on a directory.
Map `internal` in tools/test-local.sh.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Peter Amiri <peter@alurium.com>
Isolated-app boot mounts browser-fixture controllers on
controllerPath. A later lookup of controller("wheels") then
falls through to that last-path Controller.cfc stub, which
has no mixins. Disable the fixture remount during the swap,
clear controller/file caches, and point interface specs at
Test.cfc.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Peter Amiri <peter@alurium.com>
@bpamiri
bpamiri marked this pull request as ready for review August 20, 2026 15:31
@bpamiri
bpamiri merged commit cc14b48 into develop Aug 20, 2026
23 checks passed
@bpamiri
bpamiri deleted the cursor/issue-3374-test-runner-isolation-f85e branch August 20, 2026 15:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test runner: true isolation for the web runner via a separate application context (successor to #3025)

2 participants