Repository navigation
version bump - #721
Merged
Merged
version bump#721
Conversation
RestHandler.onEntityNotFoundException() throws MissingArgumentException on Adobe ColdFusion 2023 Resolve the incoming id into a local variable before calling setData(), since ACF 2023 drops the inline Elvis expression and the required data argument ends up missing. Adds a regression spec asserting the id is returned in the 404 response data. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lg4ySgho8Woxb5SQN9eqep
COLDBOX-1453 RestHandler.onEntityNotFoundException() fails on Adobe ColdFusion 2023
… and its async announce thread InterceptorService.getLazyBuffer()'s per-request buffer pool only skipped pooling for the announce() call that itself spawns an async/ asyncAll thread. It missed that code running *inside* that spawned thread can make its own synchronous announce() calls - most commonly WireBox's afterInstanceAutowire announcement, fired by every getInstance() call - which still pool against the same request-scoped array as the original request thread. `request` scope, and thus the pool array, is shared between a request and any cfthread spawned from it. CFML/BoxLang arrays aren't thread-safe, so two real concurrent threads (the request thread and an async announce()'s thread) popping/releasing the same array can corrupt it, throwing "can not pop Element from array, array is empty" mid-request. Also check controller.getUtil().inThread() (the same check InterceptorState.process() already uses for its own async dispatch decision) so a synchronous announce() executing inside any cfthread always gets a fresh, unpooled buffer - never touching the array the request thread is using. Added regression coverage for both the pooled (reused) and never-pooled (inside a thread / not asked to pool) paths. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KiK6DMek9iJMuYk2PzcjPj
An async announce()'s thread can itself become unbound from the request that spawned it - a fire-and-forget async announce, or one nobody joined, can keep running after the request has already ended. By the time that orphaned thread's own work touches the buffer pool, `request` scope may no longer be the one the pool was built against, or may not be usable at all. The pool is a performance nicety, never load-bearing for correctness, so getLazyBuffer()/releaseLazyBuffer() now treat any failure reading or writing it the same as "no pool available": fall back to a fresh, unpooled buffer (get) or silently drop it (release), rather than letting that propagate out of announce(). Added regression tests for both directions, and hardened the test file's setup() to clear any leftover pool state between tests, since request scope - and thus the pool - persists across every test in a TestBox run. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KiK6DMek9iJMuYk2PzcjPj
… in tests CI showed 16 errors on lucee@5/6, all in InterceptorserviceTest.cfc: mockController.$( "getUtil", ... ) made MockBox generate a stub method whose inferred return type was "string", then fail to cast the real Util component (or, in one test, a TestBox Stub) to it. Switched to mockController.setUtil( ... ), a real accessor-generated setter rather than a MockBox $() stub - the same pattern setLogBox()/ setWireBox()/setCacheBox() already use for their own properties in this same setup(), and which doesn't go through MockBox's return-type inference at all. Lucee 5/6 aren't available in this sandbox (no cached artifact, and ForgeBox is unreachable here) to reproduce directly, but this removes the exact code path CI's stack trace pointed at, in favor of a pattern already proven safe on every engine in this same file. Re-verified 27/27 passing on Lucee 7 across repeated runs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KiK6DMek9iJMuYk2PzcjPj
COLDBOX-1454: fix interceptor buffer pool race between a request and its async announce thread
event.route( "name@module" ) joined the module inherited entry point and the route pattern without a separator, so "login@BrowserTesting" built "__browser-testinglogin/3/". ModuleService stores inherited entry points without a trailing slash, so the link now joins them with a single slash. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UU9aLossoYUkQNwubnitBi
…dpoints GET /__browser-testing/login/:id and GET /__browser-testing/logout call the login and logout closures of moduleSettings.browserTesting. Every request gets a plain 404 unless the environment is testing, the module is enabled, a token is configured and matches the X-Browser-Testing-Token header or the token variable (compared in constant time), and the closure is set. The test harness enables it, and runs in its testing environment on 127.0.0.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UU9aLossoYUkQNwubnitBi
coldbox.system.testing.BrowserTestCase (BoxLang) extends BaseTestCase and delegates to TestBox's BrowserSupport like BrowserSpec: browse(), this.playwright(), browserAvailable(), the browserProfile and baseURL annotations, the browser matchers and the afterAll closeBrowser() hook. It adds routeURL(), visitRoute() and assertRouteIs() for named routes, and loginAs() and logout() through the BrowserTesting core module. The specs live in tests/specs/browser, which tests/runner.cfm skips on engines other than BoxLang; the browser specs skip without bx-playwright. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UU9aLossoYUkQNwubnitBi
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UU9aLossoYUkQNwubnitBi
…corator mocks on Adobe BrowserTestCase registered testbox.system.browser.BrowserMatchers in its pseudo constructor, so every runner.cfm request on BoxLang failed with a 500 on released TestBox builds that do not ship testbox.system.browser yet. It now detects TestBox browser support, registers the matchers only when present, and skips browser specs with a clear reason otherwise. The route helpers still run. RequestContext.getMemento() returned the `this` reference that Adobe keeps in the variables scope. RequestContextDecorator restored it, so on Adobe the decorator's inherited methods ran with `this` pointing to the original context and mocked methods failed with "Element _MOCKRESULTS is undefined in THIS" (BrowserTestingModuleSpec header token spec). The memento now leaves it out, with regression specs in RequestContextDecoratorTest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UU9aLossoYUkQNwubnitBi
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UU9aLossoYUkQNwubnitBi
The test-only login endpoints were a backdoor. Logged-in browser tests now
use bx-playwright saved sessions: log in through the login page once with
this.playwright().session(), then browse( ..., { session : "name" } ).
The test harness gets a plain login form for the saved session specs, and
goes back to its development environment on 127.0.0.1. Module route specs
use the resourcesTest module.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UU9aLossoYUkQNwubnitBi
- visitRoute() no longer skips the spec without browser support: it only visits the page it gets, and pages come from browse(), which skips. - assertRouteIs() matches every route registered with the name, so a route with optional placeholders (/posts/:id?, registered as /posts/:id and /posts) matches with and without them. Module routes use the module routing table. - The harness login form renders the logged-in page instead of relocating: the harness runs with the test controller, which intercepts relocations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UU9aLossoYUkQNwubnitBi
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 5 to 6. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](actions/setup-java@v5...v6) --- updated-dependencies: - dependency-name: actions/setup-java dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [EndBug/add-and-commit](https://github.com/endbug/add-and-commit) from 10.0.0 to 11.1.1. - [Release notes](https://github.com/endbug/add-and-commit/releases) - [Commits](EndBug/add-and-commit@v10.0.0...v11.1.1) --- updated-dependencies: - dependency-name: EndBug/add-and-commit dependency-version: 11.1.1 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [ortus-boxlang/setup-boxlang](https://github.com/ortus-boxlang/setup-boxlang) from 1.4.0 to 1.5.0. - [Release notes](https://github.com/ortus-boxlang/setup-boxlang/releases) - [Changelog](https://github.com/ortus-boxlang/setup-boxlang/blob/development/changelog.md) - [Commits](ortus-boxlang/setup-boxlang@1.4.0...1.5.0) --- updated-dependencies: - dependency-name: ortus-boxlang/setup-boxlang dependency-version: 1.5.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [mikepenz/action-junit-report](https://github.com/mikepenz/action-junit-report) from 6.4.2 to 6.5.0. - [Release notes](https://github.com/mikepenz/action-junit-report/releases) - [Commits](mikepenz/action-junit-report@v6.4.2...v6.5.0) --- updated-dependencies: - dependency-name: mikepenz/action-junit-report dependency-version: 6.5.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
…ting to the scheduler's restart time
syncScheduleWithCluster() unconditionally realigned a server-fixation
task's initial delay to the cluster lock's scheduleStart anchor
whenever a lock already existed, with no regard for whether the task
already had its own deterministic, wall-clock-computed delay.
That anchor is getScheduler().getStartedAt() - the scheduler's own
startup timestamp - which has no relationship to a calendar task's
configured time of day. Calendar DSL methods (everyDayAt(),
everyHourAt(), everyWeekOn(), everyMonthOn(), everyYearOn(),
onFirstBusinessDayOfTheMonth(), onLastBusinessDayOfTheMonth()) already
compute their own initial delay from the real clock at DSL-call time,
independently and identically on every server. syncScheduleWithCluster()
was stomping on that correct delay and replacing it with one aligned to
the scheduler's last restart time instead - so a task declared
everyDayAt("04:00") would, after any restart that left a prior cluster
lock in place, start firing every day at whatever time-of-day the
scheduler happened to (re)start at, and that time-of-day compounds on
every subsequent restart.
Plain every(n, unit) tasks have no inherent wall-clock anchor (every
server starts at a different moment), so they still need this sync to
avoid drifting relative to each other - that behavior is preserved.
The fix skips the sync only when the task already has a positive,
deterministically-computed initial delay (getDelay() > 0), which is
exactly and only true for the calendar DSL methods.
Added regression coverage for everyDayAt() staying put across a sync
with a stale/wrong anchor, while confirming plain every() still syncs
as before.
Verified directly against a live BoxLang 1.17.5 engine (standalone,
bypassing this sandbox's MySQL-dependent integration test harness):
reverting the fix and re-running reproduced the exact reported
drift (an everyDayAt("04:00") task's delay changing from the correct
~16.5h to a bogus ~15h matching a simulated stale 9-hours-ago anchor);
restoring the fix leaves the delay untouched across the same scenario,
while a plain every(2,"hours") task is still realigned as designed.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KiK6DMek9iJMuYk2PzcjPj
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KiK6DMek9iJMuYk2PzcjPj
Fix onOneServer() + calendar tasks drifting to the scheduler's restart time after a restart
…s/setup-java-6 chore(deps): bump actions/setup-java from 5 to 6
…nz/action-junit-report-6.5.0 chore(deps): bump mikepenz/action-junit-report from 6.4.2 to 6.5.0
…boxlang/setup-boxlang-1.5.0 chore(deps): bump ortus-boxlang/setup-boxlang from 1.4.0 to 1.5.0
…/add-and-commit-11.1.1 chore(deps): bump EndBug/add-and-commit from 10.0.0 to 11.1.1
execute(), and the get()/post()/etc helpers built on it, ran the global preProcess/postProcess interceptors but never Router.middleware() targets, so a route protected by middleware looked unprotected in integration tests. Run them in the same order as the Bootstrap: route middleware right after the global preProcess announce, and before the global postProcess announce. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E8yy4oK5vnaVucmYLg4UtY
group() already shared middleware, but a `meta` option was silently ignored: the withClosure merge skips complex values that the route already declares, and addRoute() always declares `meta`. Inherit it through its own stack, like the group middleware, so one group can declare route metadata once. Keys merge shallowly, outer group first, and the route's own meta() wins. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E8yy4oK5vnaVucmYLg4UtY
…hreads Singleton, engine (application, session, server) and CacheBox scopes store an object before wiring so circular dependencies resolve, but other threads read it outside the lock and got a half-wired instance. Each scope now tracks keys currently wiring. A thread seeing such a key queues on one shared per-injector lock. The wiring thread re-enters its own lock and gets the stored instance, so circular dependencies still resolve. One lock for all scopes makes lock-ordering deadlocks impossible. Also renames CFScopes to EngineScopes. Supersedes #706. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015YodhiJEvwNCVBSDJEnRTZ
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015YodhiJEvwNCVBSDJEnRTZ
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015YodhiJEvwNCVBSDJEnRTZ
A statement starting with an array literal does not parse on Adobe 2023. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015YodhiJEvwNCVBSDJEnRTZ
COLDBOX-1455 WireBox scopes: do not hand an unwired object to other threads
…initiated During a reinit loadColdBox() puts a new, not yet initiated controller in application scope. onSessionStart() only checked that the key existed, so a new session in that window ran the session start handler against a half-loaded controller and returned a 500 instead of the fail-fast 503. Check getColdboxInitiated() as well, the same readiness test reloadChecks() and onSessionEnd() use. A not-ready controller is skipped, the same as the existing missing-key case. Supersedes #707. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015YodhiJEvwNCVBSDJEnRTZ
COLDBOX-1456 Bootstrap: skip a session start until the controller is initiated
…scope build completes A singleton, engine scope or CacheBox object built inside another build left the wiring set when its own wiring ended, while holding a reference to the outer object that was still wiring. Other threads then took the lock-free path and reached the half-wired outer object. The injector now owns the set of keys in flight and a build depth counter, shared by all scopes, and clears the set when the outermost build returns. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0155rEwu5EVDK6HdcLfgczu7
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0155rEwu5EVDK6HdcLfgczu7
…ning mocks are valid on Lucee Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0155rEwu5EVDK6HdcLfgczu7
WireBox: hold circular partners as wiring until the outermost scope build completes
…bda and instance middleware COLDBOX-1460. Registers a closure, lambda, object instance or WireBox ID under a name, singly or as a struct of name : target pairs. It is sugar over middlewareGroup(): the name is stored as a one-member bundle in the same namespace, so middleware(), group() and withoutMiddleware() resolve it with no new code. Duplicate names throw Router.DuplicateMiddleware unless force is true. Also fixes normalizeMiddlewareEntries() treating a component instance as a struct on engines where isStruct() is true for components. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E8yy4oK5vnaVucmYLg4UtY
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E8yy4oK5vnaVucmYLg4UtY
COLDBOX-1460 Add Router.registerMiddleware() for named closure, lambda and instance middleware
…port When closeBrowser() never runs (an afterAll() that throws, an aborted request), TestBox's BrowserSupport closes the browser the next time a run opens one (Ortus-Solutions/TestBox#212). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UU9aLossoYUkQNwubnitBi
…Case TestBox now attaches browser support to any spec annotated with @browser, @browserprofile or @baseurl (Ortus-Solutions/TestBox#222), so ColdBox no longer needs a parallel BrowserTestCase hierarchy. BaseTestCase keeps loading the app and gains the named route helpers: routeURL(), visitRoute() and assertRouteIs(). BrowserTestCase is removed and its spec becomes BrowserTestingSpec, a BaseTestCase with @browser. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UU9aLossoYUkQNwubnitBi
…@browser support Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UU9aLossoYUkQNwubnitBi
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UU9aLossoYUkQNwubnitBi
Adobe ColdFusion cannot call a member function on a parenthesized expression, so `( route.regexPattern ?: "" ).reReplace(...)` failed to compile on Adobe 2023/2025. The helpers now call the reReplace() function. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UU9aLossoYUkQNwubnitBi
…an-ddfqpm COLDBOX-1457 Browser testing for ColdBox apps: @browser on BaseTestCase and named route helpers
…-app error - routeURL(), visitRoute() and assertRouteIs() build paths from the web root: the appMapping is a mapping, not a web path, so it is no longer part of them. A webMapping keeps the base path for apps served from a sub folder. - With params, a route with optional placeholders resolves to the longest variant the params fill, instead of the base route. A root route gives "/", not "//". - The helpers throw BaseTestCase.NoColdBoxApp when no ColdBox app is loaded, and InvalidArgumentException for a module that is not loaded. - assertRouteIs() rethrows unexpected errors as they are and reports the page URL as is when it is not a valid URI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UU9aLossoYUkQNwubnitBi
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UU9aLossoYUkQNwubnitBi
…an-ddfqpm BaseTestCase route helpers: web root paths, optional placeholders, no-app error
Implicit Views Disabled by default for security
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.
version bump