Skip to content

test(client-react): give the package a test harness and pin realtime hook behavior (#4682) - #4692

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-4682-client-react-test-harness
Aug 2, 2026
Merged

test(client-react): give the package a test harness and pin realtime hook behavior (#4682)#4692
os-zhuang merged 2 commits into
mainfrom
claude/issue-4682-client-react-test-harness

Conversation

@os-zhuang

@os-zhuang os-zhuang commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Fixes #4682

问题

packages/client-react 导出 8 个公开 hook,而 package.json 里只有 buildtypecheck,src/一个测试文件都没有

tsc --noEmit 结构上就看不见 hook 真正会坏的地方——依赖数组是值,不是类型。少一项依赖、少一个 cleanup、回调根本没被调用,这三种缺陷全都能完美通过类型检查。

#4678 就是这个形状:useAutoRefresh 对谓词写纹丝不动(而谓词写恰恰是把列表弄脏得最厉害的那种写入),类型系统全程没有察觉。

改动

新增全工作区第一个 DOM 测试环境(jsdom + @testing-library/react,environment: 'jsdom';其余每个包都是 node),以及 17 条覆盖 realtime hooks 的测试,正对着类型检查看不见的三件事:

1. 依赖数组驱动的重订阅 —— 改 object 会在新名字上开订阅并释放旧的;无关的重渲染不产生任何 churn。还钉住了一点:hook 依赖的是原始值 options?.recordId / options?.packageId,而不是 options 对象的身份,所以「值相等但身份是新的」对象必须是 no-op。把那个依赖「顺手简化」成 options 会让这条测试变红。

2. 卸载时释放 —— 每个订阅 hook 都退订,包括持有两个订阅的 useAutoRefresh。另有一条:卸载后迟到的事件不得触发 state 更新(React 对这类缺陷是打 console.error 而非抛错,所以断言也盯着 console)。

3. 事件确实送达 —— 事件进到 state 和回调,且 useAutoRefresh逐记录流和批量流两条上都会 refetch(#4678 的回归钉)。

断言不是空的:破坏验证

绿色本身不证明什么,所以每一类断言都用破坏源码的方式验过:

破坏 结果
useDataSubscription 依赖数组拿掉 object 1 条红
删掉 useDataSubscription 的 cleanup 3 条红
useAutoRefresh 退回只订逐记录流(#4678 原缺陷) 3 条红
全部还原 17/17 绿

CI 无需接线

Test Core按包分片的(scripts/partition-test-shards.mjsturbo ls),所以加一个 test 脚本就够了。已用该脚本实测确认 client-react 落在分片 2

顺带说明:那段分片注释提到的 "vitest 4 对测试文件数少于分片数的包硬失败" 不适用于本包——分片是按包切的,每个包整套跑在一个分片上。

验证

按真实退出码(注意 cmd | tail 的退出码是 tail 的,不能当证据):

typecheck exit=0   build exit=0   test exit=0 (17/17)   eslint exit=0

tsconfig.json 的 exclude 补了 **/*.test.tsx —— 全仓统一把测试排除在 tsc 之外(**/*.test.ts),但那个 glob 匹配不到 .tsx

没有改动任何运行时代码。

用这套基座立刻抓到的两个缺陷

基座建起来不到一小时就抓到两个 typecheck 永远看不见的真实缺陷,都已单独立案、不夹带进本 PR:

两者同源(依赖数组按身份而非按值),且在同一条调用链上——useAutoRefresh 的 TSDoc 示例正是 const { refetch } = useQuery(...)。都是实测数字,不是读代码推断的。修复各自独立,本 PR 只交付基座本身。

🤖 Generated with Claude Code

https://claude.ai/code/session_01LYnZrTwXbrctB8E8HpJAPT

…hook behavior (#4682)

`packages/client-react` shipped 8 public hooks with `build` and `typecheck` as
its only scripts and zero test files. `tsc --noEmit` is structurally blind to
what actually breaks in a hook: a dependency array is a value, not a type, so a
missing entry, a missing cleanup and a callback that never fires all typecheck
perfectly. #4678 was exactly that shape — `useAutoRefresh` ignored predicate
writes, the case that dirties a list hardest, and no type noticed.

Adds the workspace's first DOM test environment (jsdom + @testing-library/react,
`environment: 'jsdom'`; every other package runs `node`) and 17 tests over the
realtime hooks covering the three things the type checker cannot see:

- re-subscription driven by the dependency array — changing `object` opens a
  subscription on the new name and releases the old one, an unrelated re-render
  churns nothing, and the hooks key on the primitive `options?.recordId` /
  `options?.packageId` rather than the options object's identity, so an
  equal-but-new object stays a no-op;
- release on unmount, including `useAutoRefresh`, which holds two subscriptions;
- delivery — events reach state and callbacks, and `useAutoRefresh` refetches on
  the per-record *and* the bulk stream (the #4678 regression pin).

Every assertion was verified by sabotage rather than assumed: dropping the
`object` dep (1 failure), deleting a cleanup (3) and reverting `useAutoRefresh`
to the single-stream version (3) each turn the suite red; reverting turns it
green again.

CI needs no wiring — `Test Core` partitions by package off `turbo ls`, and the
new `test` script puts client-react on shard 2 (verified with
scripts/partition-test-shards.mjs).

No runtime code changed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LYnZrTwXbrctB8E8HpJAPT
@vercel

vercel Bot commented Aug 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 2, 2026 6:50pm

Request Review

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation dependencies Pull requests that update a dependency file tests tooling labels Aug 2, 2026
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/client-react.

3 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/skills-reference.mdx (via packages/client-react)
  • content/docs/api/client-sdk.mdx (via @objectstack/client-react)
  • content/docs/plugins/packages.mdx (via @objectstack/client-react)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

…ding them

`check:type-check-coverage` failed on the first push, and it was right. The
initial commit added `**/*.test.tsx` to tsconfig's `exclude` to match the
`**/*.test.ts` entry every sibling package carries — but that entry is frozen
DEBT the repo is migrating away from (#4311), not a convention to copy. The
gate's own summary makes it explicit: 21 packages still exclude their tests,
carrying 2243 frozen raw errors in TEST_DEBT. Excluding one more would have
reported green over source `tsc` never read.

Drops the test exclusion entirely, so client-react typechecks its own tests and
stays out of that ledger.

Doing so immediately surfaced a real gap: the `METADATA_EVENT` fixture was
declared and never used, because `useMetadataSubscription` was covered for
re-subscription and unmount but not for delivery. Adds that assertion rather
than deleting the fixture — 18 tests now.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LYnZrTwXbrctB8E8HpJAPT
@os-zhuang
os-zhuang marked this pull request as ready for review August 2, 2026 19:09
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 2, 2026
Merged via the queue into main with commit bbb1192 Aug 2, 2026
22 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-4682-client-react-test-harness branch August 2, 2026 19:21
os-zhuang pushed a commit that referenced this pull request Aug 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

client-react 没有任何测试基座:8 个公开 hook 的行为全靠 typecheck 兜底

2 participants