fix(wheelstest): capture spec-load errors against the bundle and emit positive error count#2831
Conversation
… positive error count Spec files whose run() called it() outside a describe() block, or whose beforeAll() threw during load, were silently dropped (the run bubbled up as an anonymous BundleRunnerMajorException) or surfaced as the legacy "-1 error(s)" sentinel in the summary, with no file context. BDDRunner now starts bundle stats before invoking the target and wraps both the suite-descriptor pass (target.run / getTestSuites) and the bundle execution (beforeAll, suite iteration, afterAll) in a single catch that records the throw against bundleStats.globalException and increments the error count by one. Tooling that walks bundleStats.path / globalException already surfaces the filename and exception detail. Fixes the silent-failure reports in issue #2829 for both shapes — a malformed orphan-it() spec and a load-time exception from a setup hook. Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
…modes Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Wheels Bot — Docs updatedAdded a doc commit to this PR:
|
There was a problem hiding this comment.
Wheels Bot — Reviewer A
TL;DR: This fix is clean and correct. BDDRunner.cfc is patched in exactly the right two places — startBundleStats moved before target.run() so there is always a bundle record to attach errors to, and the surrounding try/catch widened to cover both the suite-descriptor phase and the execution phase so neither orphan-it() nor a throwing beforeAll() can escape as an anonymous BundleRunnerMajorException. The -1 sentinel is replaced with +1. Two purpose-built fixtures and a BDD spec verify both shapes. No cross-engine, security, or convention issues found. Verdict: approve (self-authored PR; GitHub disallows the approval API, so this is recorded as a comment review instead).
Tests
vendor/wheels/tests/specs/wheelstest/BDDRunnerErrorReportingSpec.cfc, line 39
expect(results.getTotalError()).toBeGT(0);
...
expect(bs[1].totalError).toBeGT(0);The first test case (OrphanItFixture) correctly uses toBe(1) for exact precision. The BeforeAllErrorFixture case uses toBeGT(0), which is weaker — the fix guarantees exactly 1 error per bundle failure just as the first case does. Suggest tightening to toBe(1) to lock the invariant:
expect(results.getTotalError()).toBe(1);
...
expect(bs[1].totalError).toBe(1);Not a blocker, but the first spec sets a clear precedent for exact counts.
Correctness
The refactor correctly preserves existing behaviour for the non-error path (the canRunBundle guard, beforeAll/afterAll annotation method loops, onSuiteStart/onSuiteEnd callbacks, and endStats are all unchanged and still run on the happy path). The isNull(thisSuite) guard in the catch block is safe: for (var thisSuite in testSuites) declares thisSuite in function scope before the loop body executes, so it is null when the catch fires before the loop is entered, and isNull(null) returns true, passing {} to announceToModules as the "no active suite" signal.
Commits
Single commit fix(wheelstest): capture spec-load errors against the bundle and emit positive error count — type fix, scope wheelstest, subject 68 characters, body explains the why. DCO Signed-off-by: claude[bot] present.
Docs
CHANGELOG.md [Unreleased] → Fixed entry is accurate and placed correctly beneath the existing ### Changed block. AI/framework-doc follow-ups are explicitly deferred to bot-update-docs.yml, which is the established pattern.
There was a problem hiding this comment.
Wheels Bot -- Reviewer A
TL;DR: The docs follow-up commit d12e401 adds exactly one bullet to the Common failure modes section of the running-tests-locally guide, accurately describing the new error output shape introduced by the fix. It is clean, well-placed, and commit-lint clean. The one minor nit from the previous review round -- tightening toBeGT(0) to toBe(1) in the BeforeAllErrorFixture case -- remains unaddressed but is not a blocker. No correctness, cross-engine, or security concerns. Verdict: comment (minor nit only).
Tests: vendor/wheels/tests/specs/wheelstest/BDDRunnerErrorReportingSpec.cfc lines 39 and 43 -- The first case OrphanItFixture uses exact assertion toBe(1). The second case BeforeAllErrorFixture uses the weaker toBeGT(0). Since the catch block unconditionally sets bundleStats.totalError = 1 and increments by 1, the result is always exactly 1. Suggest tightening both to toBe(1) to match the first case.
Docs: web/sites/guides/src/content/docs/v4-0-0/testing/running-tests-locally.mdx line 205 -- The added bullet is factually correct, names both failure shapes, points users to globalException, and directs them to the bundle path. Placement inside Common failure modes is exactly right.
Commits: fix(wheelstest) subject 68 chars DCO present; docs(web/guides) subject 52 chars DCO present. Both conform to commitlint.config.js.
wheels-bot:review-a:2831:d12e401632a54b77451f63b2f7b0d986aa2fa822
There was a problem hiding this comment.
Wheels Bot — Reviewer A
TL;DR: The docs follow-up commit (d12e401) adds exactly one bullet to the "Common failure modes" section of the running-tests-locally guide, accurately describing the new error output shape introduced by the fix. It is clean, well-placed, and commit-lint clean. The one minor nit from the previous review round — tightening toBeGT(0) to toBe(1) in the BeforeAllErrorFixture case — remains unaddressed but is not a blocker. No correctness, cross-engine, or security concerns. Verdict: comment (minor nit only).
Tests
vendor/wheels/tests/specs/wheelstest/BDDRunnerErrorReportingSpec.cfc, lines 39 and 43
The first case (OrphanItFixture) uses the exact assertion toBe(1), which locks the invariant precisely. The second case (BeforeAllErrorFixture) uses the weaker toBeGT(0). Since the catch block unconditionally sets bundleStats.totalError = 1 and calls incrementStat(type="error", count=1), the result is always exactly 1. Suggest tightening both assertions to toBe(1) to match the first case and catch any future regression that produces a different positive count.
Docs
web/sites/guides/src/content/docs/v4-0-0/testing/running-tests-locally.mdx, line 205
The added bullet is factually correct: it names both failure shapes (orphan it() call, throwing beforeAll()), points users to globalException, and directs them to the bundle path shown in the summary. Placement inside the "Common failure modes" list is exactly right.
Commits
Two commits in the PR:
fix(wheelstest): capture spec-load errors against the bundle and emit positive error count— typefix, scopewheelstest, subject 68 chars, DCOSigned-off-bypresent.docs(web/guides): note BDD runner load-time errors in common failure modes— typedocs, scopeweb/guides, subject 52 chars, DCOSigned-off-bypresent.
Both conform to commitlint.config.js. No issues.
Wheels Bot — Reviewer B (round 1)A's review is clean and grounded. The single nit is real, A's line reference has a minor off-by-one, and both A and I land on the same verdict: the PR is correct but has two weak assertions worth tightening before merge. SycophancyNone detected. Every positive statement in A's review is paired with a specific reference to the code or the diff. False positivesNone detected. A's claim — that the outer catch unconditionally sets Missed issuesOne minor inaccuracy in A's line citation: A says "lines 39 and 43" for the two No other missed issues. Cross-engine exposure is negligible (standard Verdict alignmentA's ConvergenceAligned. The joint recommendation: tighten lines 39 and 42 of |
|
|
Wheels Bot -- Reviewer B (round 1)A's review of the docs follow-up commit is clean and technically sound. The nit about SycophancyNone detected. A cited specific line references and explained the reasoning behind each finding rather than deferring to the author. False positivesNone detected.
Missed issuesNone on this round's diff. One concern A addressed in the first-round review and correctly resolved: the Verdict alignmentA's ConvergenceAligned. A has correctly identified the only actionable item ( |
Address Reviewer A/B consensus from PR #2831 round 1: both reviewers agreed the BeforeAllErrorFixture case in vendor/wheels/tests/specs/wheelstest/BDDRunnerErrorReportingSpec.cfc should match the OrphanItFixture case's exact-count style. The catch block in BDDRunner.cfc unconditionally records totalError = 1 per failed bundle, so toBeGT(0) was weaker than the invariant the fix guarantees. - vendor/wheels/tests/specs/wheelstest/BDDRunnerErrorReportingSpec.cfc lines 39 and 42: toBeGT(0) -> toBe(1) for results.getTotalError() and bs[1].totalError in the BeforeAllErrorFixture case. Verified: wheelstest layer 143 pass / 0 fail / 0 error against the running CI server (sqlite). Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Wheels Bot — Address Review (round 1)Applied the consensus finding from Reviewer A and Reviewer B's convergence (round 1 of the convergence loop):
Verification: re-ran the The new commit will trigger a fresh Reviewer A run on the updated SHA. Convergence loop continues until reviewers align on |
There was a problem hiding this comment.
Wheels Bot — Reviewer A
TL;DR: The sole nit from the two previous review rounds — tightening toBeGT(0) to toBe(1) in the BeforeAllErrorFixture case — is now addressed in commit 15810f495. Both spec cases in BDDRunnerErrorReportingSpec.cfc now use identical exact-count assertions, which correctly locks the invariant guaranteed by the bundleStats.totalError = 1 assignment in the catch block. The PR is clean across all three commits; no correctness, cross-engine, convention, or security issues remain. Verdict: approve (GitHub blocks self-approval on bot-authored PRs, so this is recorded as a comment review instead).
Tests
vendor/wheels/tests/specs/wheelstest/BDDRunnerErrorReportingSpec.cfc, lines 39 and 42
Previous round: toBeGT(0) — weak, passes for any positive value.
This commit: toBe(1) — exact, matches what the catch block guarantees.
Both cases now read:
expect(results.getTotalError()).toBe(1);
// ...
expect(bs[1].totalError).toBe(1);This is consistent with the OrphanItFixture case (lines 23 and 26) and correctly reflects the bundleStats.totalError = 1 hard-assignment in BDDRunner.cfc's catch block.
Commits
Three commits across the PR:
fix(wheelstest): capture spec-load errors against the bundle and emit positive error count— typefix, scopewheelstest, 68 chars, DCO signed. Substantive fix.docs(web/guides): note BDD runner load-time errors in common failure modes— typedocs, scopeweb/guides, 52 chars, DCO signed. Accurate follow-up.fix(wheelstest): tighten BeforeAllErrorFixture assertions to toBe(1)— typefix, scopewheelstest, 49 chars, DCO signed. Addresses review nit.
All three conform to commitlint.config.js. Commit bodies explain the why.
Wheels Bot — Reviewer B (round 1)A's review of the updated SHA is clean and accurate. The sole outstanding nit from prior rounds was correctly verified as addressed, line citations are now correct, and all three commits pass conformance checks. Aligned on approve. SycophancyNone detected. A identified exactly what changed (the tightened assertions at lines 39 and 42), verified against the actual diff, and explained the reasoning in terms of the False positivesNone detected.
Missed issuesNone detected. Cross-engine exposure remains negligible: the fix is a try/catch restructuring and struct assignment with no closures, Verdict alignmentA's ConvergenceAligned. All three commits are correct: the core BDDRunner fix, the docs follow-up, and the assertion tightening. No further changes needed. Joint recommendation: approve. |
Summary
The WheelsTest BDD runner silently dropped spec files whose
run()invokedit()outside adescribe()block (the throw bubbled out as an anonymousBundleRunnerMajorExceptionwith no file context), and reported a-1sentinel error count when a spec'sbeforeAll()threw during load. Both shapes are easy to land accidentally during TDD iteration and cost minutes of "why isn't my new test running?" debugging.vendor/wheels/wheelstest/system/runners/BDDRunner.cfcnow starts the bundle stats record before invoking the target and wraps both the suite-descriptor pass (target.run/getTestSuites) and the bundle execution (beforeAll, suite iteration,afterAll) in one catch. The catch records the throw againstbundleStats.globalExceptionand increments the error count by a positive one, so the summary reads "1 error(s)" with the offending bundle path and the underlying exception attached. The existingdisplayTestResultswalker already surfaces the path + message once a positive count is recorded.Related Issue
Closes #2829
Type of Change
Feature Completeness Checklist
Signed-off-by:(usegit commit -s); see CONTRIBUTING.mdvendor/wheels/tests/specs/wheelstest/BDDRunnerErrorReportingSpec.cfcreproduces both shapes via two fixture bundles (OrphanItFixture,BeforeAllErrorFixture) and assertstotalError == 1plusbundleStats[1].globalExceptionpopulated; failing-then-passingbot-update-docs.ymlfollow-upbot-update-docs.ymlfollow-upbot-update-docs.ymlfollow-up[Unreleased] -> Fixedwheels.tests.specs.wheelstest143 pass / 0 fail / 0 error (HTTP 200) after the patch; broader spot checks:wheels.tests.specs.security182 pass / 0 fail,wheels.tests.specs.dispatch96 pass / 0 fail,wheels.tests.specs.model843 pass / 11 skip / 0 fail (no regressions)Test Plan
The TDD failing-then-passing capture is the primary verification.
Before the patch, against
wheels.tests.specs.wheelstest.BDDRunnerErrorReportingSpec:Expected [true] to be false—state.threw == truebecause the orphan-it()fixture surfaced asBundleRunnerMajorExceptionrather than landing in the bundle's catchThe actual [-1] is not greater than [0]—results.getTotalError()was the-1sentinelAfter the patch (HTTP 200, both specs green) and a re-run of
security,dispatch, andmodelshows no regressions in the unrelated test layers.