test(regressions): pin PR #249 Bugbot fixes#259
Closed
aidenybai wants to merge 7 commits into
Closed
Conversation
|
🔴 React Review — 0/100 (unchanged) · Copy prompt for agentReviewed by react-review for commit fd1efb3. Configure here. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Two regressions land here: 1. `cli-and-output.test.ts` gains a "'score unavailable' message branches on offline vs API failure" describe block. The original bug (Bugbot #249) was `noScoreMessage` unconditionally set to "Score unavailable in offline mode" — so every null-score scan claimed the user was offline even when the user was online but the score API simply failed. Two tests cover the two reasons (with the API path stubbed via fetch-throws since the message branches on `options.offline`, not on the specific API failure mode). 2. New `package-boundary-integrity.test.ts` pins the post-extraction contract that `@react-doctor/core` re-exports SOURCE_FILE_PATTERN / GIT_LS_FILES_MAX_BUFFER_BYTES / IGNORED_DIRECTORIES from `@react-doctor/project-info` rather than re-declaring them. A single `.toBe(...)` reference-equality check per constant catches the silent-drift bug Bugbot caught on #249 (duplicate consts in two packages with no link between them). Both files follow the existing convention: file-header documents "Covered closed issues", describe blocks read as "<behavior> (#249)", and HACK: comments carry the historical provenance.
9681b48 to
b47ac6a
Compare
…o-discover shared exports - Lift the two "Score unavailable" messages to named constants in `cli/utils/constants.ts` so tests can import them instead of duplicating string literals (rename-safe assertions). - Parameterize the #249 noScoreMessage test into a scenario table that covers four distinct null-score paths, not just one: `--offline`, `fetch` throws, `response.ok === false`, and malformed body. All three online failure modes must produce the API-failure message — previously only the throwing path was pinned. - Auto-discover shared exports between `@react-doctor/core` and `@react-doctor/project-info` rather than hardcoding three names; a 4th duplicated constant added tomorrow is now caught with zero test maintenance. Defensively require both lookups to be `!== undefined` before reference-equality, so a `export type { ... }` slip can't smuggle drift past `undefined === undefined`.
…tive names, decoupled stubs) - Drop verbose comment block on the SCORE_UNAVAILABLE_* constants; the SCREAMING_SNAKE_CASE names plus values are self-documenting. - Decouple the console.warn silencer from `setupFetchStub`'s existence — pull it into a named helper and call it explicitly from each API-failure scenario so a future scenario can opt out. - Slim the package-boundary-integrity test: strip the file-head HACK block that duplicated the JSDoc, inline `collectSharedExportNames` at its single call site, rename `failures` -> `boundaryViolations` and the loop variable `name` -> `sharedExportName`. Keep only the genuinely non-obvious `undefined === undefined` defensive note.
…can (Bugbot #259) Bugbot identified a blind spot in the boundary integrity test: `Object.is` compares references, which works for object-typed shared constants (RegExp / Set / Map) but is identity-true for primitives — so a re-declared `GIT_LS_FILES_MAX_BUFFER_BYTES = 50 * 1024 * 1024` in core would slip past unnoticed. The same numeric value === itself. Augment with a source-level pattern scan: read every `.ts` file under `packages/core/src/` once at module init, then for each shared export name check whether any file contains a top-of-line `export\s+const\s+<name>\b`. Re-export lines like `export { NAME } from "..."` are skipped by the anchor. Verified the new check fires by simulating the exact regression Bugbot warned about (re-declaring `GIT_LS_FILES_MAX_BUFFER_BYTES` in core) — the test now reports `core declares its own copy in constants.ts`.
Rename `package-boundary-integrity.test.ts` -> `silent-drift-defenses.test.ts` and extend it with three additional drift classes, each empirically verified by simulating the regression: 1. Type-level boundary integrity (cross-package types). Parse the `@react-doctor/types` barrel to enumerate every exported type name, then scan every CLI package source for a top-of-line `(export\s+)?(interface|type)\s+NAME` declaration. TypeScript would happily accept two structurally-similar parallel declarations imported through different paths — the test pins the invariant that there's exactly one home for each type. Verified by adding `interface Diagnostic` to a canary file -> test fires. 2. Magic-string locality for the two `SCORE_UNAVAILABLE_*` messages. The literals must appear ONLY in their constants module. Catches the exact regression Bugbot flagged on PR #249 at the SOURCE level (a future contributor re-inlines `"Score unavailable in offline mode."` somewhere, bypassing the imported constant). Verified by inlining the literal in a canary file -> test fires with a remediation hint. 3. URL constants locality for `SCORE_API_URL` / `SHARE_BASE_URL`. Same defense applied to the score-API and share endpoints. Scope excludes `packages/website/` since it's deployed separately and legitimately re-declares some URLs for SSR. Verified by inlining the score-API URL in a canary file -> test fires. The four checks share one workspace-source scanner (`collectTypeScriptSources`) that walks the six CLI package source trees once at module init and caches per-file content. Each describe block consumes the same cached map, so adding a fifth drift class is a ~30-line addition. 8 tests total in this file (was 2). Full suite: 705 -> 711.
- H1: remove dead `absolutePath` field from `WorkspaceSourceFile` (was
assigned but never read).
- H2: fix the misleading remediation hints on the magic-string rules.
Drop the `remediation` field entirely and derive the hint at
assertion time from `allowedRepoRelativePath` — eliminates the bug
class of "hardcoded hint drifts from the path it points at".
- M1: unify naming. The two boundary describes now use per-test
`expect(...).toEqual([])` rather than mutable accumulator arrays
named differently in each block.
- M2: drop self-narrating comment ("Consumers = every CLI source
except types itself" restated the filter that follows).
- M3: HACK-prefix the remaining explanatory comments consistently.
Every kept comment now justifies a non-obvious design choice.
- L1: swap `let blockMatch + exec` for `matchAll` — no mutable loop
variable, no null guard.
- L2: convert the two boundary describes from "accumulator + single
assertion" to `it.each` over auto-discovered names. Each shared
constant / type gets its own test name; each failure has a
per-name `expect` message. Matches the magic-string describe's
pattern, so the file reads consistently end-to-end. 8 -> 35 tests
in this file (per-name granularity); all behaviorally equivalent
to the previous 8.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d81673d. Configure here.
…an works on Windows (Bugbot #259) Bugbot identified a false-positive failure on Windows: my filter `source.repoRelativePath !== allowedRepoRelativePath` compares values where the left side comes from `path.relative()` (OS-native: `\` on Windows) and the right side is a forward-slash literal in the rule table. On Windows the comparison never matches the allowed file, so the constants file is scanned, its content includes the literal, and the test fails for the contributor. Fix: store all repo-relative and package-relative paths in POSIX form (`split(path.sep).join("/")`). On macOS / Linux this is a no-op; on Windows it converts `\` -> `/` and the forward-slash literals in the rule table just work. `packageRoot` is left as OS-native because it's only ever compared via `endsWith(path.join(...))`, which is already cross-platform correct on both sides. Pin the invariant with an explicit regression test that scans every stored `repoRelativePath` for backslashes — fires on Windows the moment someone forgets the normalization (e.g. a refactor of the scanner).
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.

Two regression tests covering the Bugbot findings from #249. Both have been validated and rely on existing test helpers.
What's locked in
1.
'score unavailable' message branches on offline vs API failure (#249)Added to
cli-and-output.test.ts. Bugbot caught thatnoScoreMessagewas unconditionally"Score unavailable in offline mode.", so a scan that ran online but couldn't reach the score API would falsely tell the user they were offline. The fix branches the message onoptions.offline; these two tests pin both wordings:--offline: true→ stdout contains"Score unavailable in offline mode"and does not contain"could not reach the score API".--offline: false+ mockedfetchthrowing → stdout contains"could not reach the score API"and does not contain"in offline mode".Both go through real
inspect()runs via the existingcaptureScanOutputhelper — black-box, no string scrubbing in source.2.
shared constants between core and project-info (#249)New
package-boundary-integrity.test.ts. Bugbot also caught thatSOURCE_FILE_PATTERN,GIT_LS_FILES_MAX_BUFFER_BYTES, andIGNORED_DIRECTORIESwere independently re-declared in bothcore/src/constants.tsandproject-info/src/constants.ts— pure drift risk. The fix re-exports these from project-info through core's barrel; the test pins it with strict reference-equality:If anyone re-declares any of these in
core/constants.ts(even with the same value),.toBe()fails because the new RegExp/Set is a different reference. InlineHACK:comment points future failures at the correct fix (re-export, not the test).Convention compliance (vs AGENTS.md)
HACK:prefix ✓cli-and-output.test.ts"Covered closed issues" header to add#249✓it()blocks into 1 with 3 inline asserts in the dedup file ✓Test plan
pnpm test— 78 files, 713 tests passingpnpm typecheck— cleanpnpm lint— 0/0Note
Medium Risk
Mostly adds regression tests, but introduces repo-wide source scanning and strict export/reference checks that could be brittle across environments or future refactors.
Overview
Fixes
inspect()output so the "score unavailable" text distinguishes user-requested--offlinefrom score API failures, by centralizing both messages incli/utils/constants.tsand using them ininspect.ts.Adds regression coverage:
cli-and-output.test.tsnow parameterizes offline vs multiple API-failure modes (fetch throw, non-2xx, malformed body) and asserts the correct message, and a newsilent-drift-defenses.test.tsenforces "single source of truth" rules (no duplicated score messages/URLs, no re-declared@react-doctor/typestypes in consumers, and@react-doctor/coreshared exports must be true re-exports from@react-doctor/project-info).Reviewed by Cursor Bugbot for commit fd1efb3. Bugbot is set up for automated code reviews on this repo. Configure here.