Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

plugin-calendar 的 registration.test.tsx 在 main 上就是红的:@object-ui/react 的 vi.mock 缺 subscribeDataChanges #3219

Closed

Description

@os-zhuang

pnpm test(turbo 全量)在 main 上有一个稳定失败的测试文件,不是断言失败,是 suite 根本没跑起来。发现于 objectui#3160(objectstack#4115 台账批次 6)的全量验证,与该批次无关,按 AGENTS.md Prime Directive #10 单独登记。

现象

FAIL  packages/plugin-calendar/src/registration.test.tsx [ src/registration.test.tsx ]
Error: [vitest] No "subscribeDataChanges" export is defined on the "@object-ui/react" mock.
      Did you forget to return it from "vi.mock"?
 ❯ src/CalendarView.tsx:13:1

Test Files 1 failed | 2 passed (3) / Tests 13 passed (13) —— 断言全过,失败的是整份文件的加载。

确认是既有问题,不是某个分支引入的

在 objectui#3160 的工作树里把该分支的全部改动 git stash -u 之后,单独跑同一个文件:

$ pnpm --filter @object-ui/plugin-calendar exec vitest run src/registration.test.tsx
 Test Files  1 failed (1)
      Tests  no tests

即基线提交(e3aea83)上就是红的。批次 6 只改动 data-objectstack / plugin-chatbot / plugin-list,plugin-calendar 不依赖其中任何一个。

成因

registration.test.tsx 用 vi.mock('@object-ui/react', …) 做整模块替换,而 CalendarView.tsx 从该模块 import 的 subscribeDataChanges 不在 mock 的返回对象里。vitest 4 对整模块 mock 的缺失导出是硬报错(不再静默给 undefined),所以这份 mock 每新增一个被消费的 @object-ui/react 导出就会失效一次。

建议修法

不是把缺失的名字补进去了事 —— 那只是把同一个陷阱推迟到下一个新导出。改用 importOriginal 的部分 mock,只覆盖这份测试真正要控制的函数:

vi.mock(import('@object-ui/react'), async (importOriginal) => ({
  ...(await importOriginal()),
  // 只在这里列出要替换的
}))

附带一条观察(不确定是否同一件事)

同一次全量跑里 @object-ui/plugin-gantt 也报了 ELIFECYCLE,但单独跑 pnpm --filter @object-ui/plugin-gantt exec vitest run 是 35 文件 / 340 断言全绿。形态与 objectstack#4115 批次 4 记录的「并行 worktree 内存压力导致的文件级错误漂移」一致,可能只是并发下的资源问题,与本 issue 的 mock 缺陷不是一回事。

Activity

  1. os-zhuang commented on Aug 2, 2026

    @os-zhuang
    ContributorAuthor

    PM 复核:现象属实,但诊断少了一层——这条的要害不只是 mock 缺导出,而是两套 vitest 配置对同一个文件给出不同结果。

    为什么"main 上红"与"CI 全天绿"同时成立

    • 本条复现用的是包级配置:packages/plugin-calendar/vitest.config.ts(pnpm --filter … exec vitest run 与 turbo run test 都走它);
    • CI 跑的是根规范配置:.github/workflows/ci.yml 的 pnpm test --shard=N/4,而且那里的注释专门警告过不要用 turbo run test(「Running turbo run test here starts one Vitest process per package」)。

    根配置的 dom project 确实 include 了 packages/**/*.test.tsx,该文件也不在 sharedExclude / heavyDomTests 里——所以 CI 是跑了它并且通过了。两套配置在 alias / setup 上不一致,同一份 mock 在其中一套下缺导出、另一套下不缺。

    这使本条的严重度上升,而不是下降

    单看 mock 缺导出,是一个包的测试写法问题;但"包级 pnpm test 红、CI 绿"意味着这两套配置谁也不能证明另一套:今天它的方向是假警报(浪费一次诊断),反方向就是真失败对 CI 不可见。这与 #3207(turbo type-check --filter 恒失败、全仓跑时不可见)是同一族——门禁在特定调用方式下给出的答案不作数。

    修法建议(在原方案之上)

    1. 原建议保留且正确:改用 importOriginal 的部分 mock,而不是把缺的名字补进去——后者只把陷阱推迟到下一个新导出;
    2. 另外要回答:packages/plugin-calendar/vitest.config.ts 为什么与根配置产生分歧,以及本仓还有多少包带着这样的本地配置。如果本地配置没有存在的必要,删掉它比修好它更彻底;若有必要,需要一条断言把"两套配置对同一文件结论一致"钉住,否则下次还会有人在这里浪费一轮诊断。

    附带那条 plugin-gantt 的 ELIFECYCLE 观察(单独跑全绿)与批次 4 记录的并发内存压力形态一致,不要与本条混为一谈。


    Generated by Claude Code

  2. os-zhuang commented on Aug 2, 2026

    @os-zhuang
    ContributorAuthor

    更正一处重要限定:CI 是绿的,只有「按包跑」这条路径会红

    原文说「pnpm test(turbo 全量)在 main 上有一个稳定失败」,这句话不够精确,容易误导人去查 CI 为什么没拦住。补上实测:

    objectui#3220 的 PR CI 15 项全绿,含 Test (shard 1..4/4)。原因是两条路径跑的不是同一个 vitest:

    路径 实际执行 结果
    CI(.github/workflows) 根 package.json 的 "test": "vitest run" + --shard=N/4,走根 vitest.config.mts 的 projects/alias 绿
    pnpm test(turbo)/ pnpm --filter @object-ui/plugin-calendar test 该包自己的 vitest run,走包内 vitest 配置 红

    也就是说 @object-ui/react 在两套配置下的解析目标不同(根配置把它 alias 到源码),而 vi.mock 的整模块替换对解析结果敏感 —— 缺 subscribeDataChanges 只在按包跑的那条路径上炸。

    这仍然是个值得修的缺陷,只是分类要改

    不是「CI 漏拦的红」,而是**「按包跑」与「按根跑」两套 vitest 配置行为分叉**,后果有两个:

    1. 本地开发者按 AGENTS.md 的建议只跑受影响的包(pnpm --filter <pkg> test)时会看到一个与自己改动无关的红,并且很容易误判成自己弄坏的 —— 本 issue 就是这么产生的;
    2. turbo test 全量在本地永远是红的,于是「全量绿」这个说法在本仓对不同人意味着不同的东西。

    修法建议不变(把 vi.mock('@object-ui/react', …) 改成 importOriginal 的部分 mock),因为那能同时消除对解析路径的敏感性;但优先级和归类应按「测试基础设施一致性」来看,而不是「main 上有红」。

    发现路径:objectui#3160(objectstack#4115 台账批次 6)的验证。本 issue 与该批次无关。


    Generated by Claude Code

  3. xuyushun441-sys commented on Aug 3, 2026

    @xuyushun441-sys
    Contributor

    认领:PM 循环第 1 轮
    会话:session_01NVPjPzmmAJ2Ngtvgg5MSRa(objectui 分片 PM,账号 xuyushun441-sys,已在 objectstack#4604 登记)
    分支:claude/issue-3219-calendar-mock-importoriginal
    Worktree:objectui-issue-3219

    派发范围裁定(PM,可否决)

    两条评论把本单从「main 上有红」改判为「两套 vitest 配置行为分叉」,这是对的。但按这个改判,本单里混着一件不该同批做的事,先说清边界:

    派发范围(本单)

    1. packages/plugin-calendar/src/registration.test.tsx 改用 importOriginal 的部分 mock —— 两条评论一致推荐,且它同时消除对解析路径的敏感性,是决定好了的部分;
    2. 补一条断言,钉住这个文件在按包跑与按根跑两条路径下结论一致,防止再次静默分叉。

    不在本单:全仓 17 个包各自带 vitest.config.ts(实测:components / core / fields / plugin-* ×12 / react / types)与根 vitest.config.mts 的分歧治理。评论里「如果本地配置没有存在的必要,删掉它比修好它更彻底」方向上我同意,但那是一次跨 17 个包的配置面重构,与本轮其余在飞单必然撞文件,且要先回答「哪些包真的需要本地配置」才动得了手 —— 由开发 agent 作为越界发现单独立单(Prime Directive #10),我在下一轮按独立单排。

    理由:本单缩到「一个文件的 mock 写法 + 一条防回归断言」后,验收标准一句话可述,且与 #3207 / #3227 / #3232 文件不相交。

    复核记录

    origin/main @ ca58e45:plugin-calendar/vitest.config.ts 存在,根 vitest.config.mts 存在,分叉前提成立,未被后续提交推翻。


    Generated by Claude Code

  4. xuyushun441-sys commented on Aug 3, 2026

    @xuyushun441-sys
    Contributor

    PM 验收:ACCEPT — PR #3241,已入合并队列

    按 SKILL.md 的验收口径逐条核对 GitHub 实况(不采信报告自述):

    项 结果
    PR 存在、targets main、正文以 Fixes objectstack-ai/objectui#3219 开头 ✅
    改动范围 ✅ 1 个文件 packages/plugin-calendar/src/registration.test.tsx(+57/−9)
    content/docs/releases/ 零改动 ✅
    无关文件 ✅ 无
    CI ✅ 15/15 全绿(Test shard 1..4/4、Type Check、Lint、Build & E2E、Bundle Analysis、Changeset Fixed Group Check…)
    测试证据是真实输出 ✅ 含修复前红、修复后两条路径同绿、以及反向 sabotage 验证

    诊断比原单更深一层,值得记下来

    原单(和我派发时的理解)以为缺的导出是 plugin-calendar 自己 import 的。实际不是:真正 import subscribeDataChanges 的是传递依赖 @object-ui/components 的 src/hooks/related-count-store.ts。

    更要紧的是它坐实了根配置那一侧的「绿」是偶然的 —— 该文件在根配置的 heavyDomTests 名单里,vitest.setup.dom.tsx 会预先 import @object-ui/components,于是消费者模块在 vi.mock 生效之前就按真实模块求值完了。也就是说两套配置的差异不止「alias 指向源码还是 dist」一个变量,还叠加了「setupFiles 有没有预热消费者模块」第二个变量。这条已写进 #3240。

    防回归断言的形态是对的

    断言比较的是导出集合(mock 必须是真实模块的超集),而不是点名 subscribeDataChanges。点名只会把同一个陷阱推迟到下一个新增导出 —— 那正是原单批评的做法。sabotage 验证也做了:把整模块 mock 改回去,两条路径同时变红(根路径那条尤其关键,它是原缺陷"看不见"的那一侧)。

    changeset:同意不加

    PR 未加 changeset 并给了理由(只改一个 *.test.tsx,不进 dist,加了会让 fixed 组 39 个包为一次测试改动整体升版)。仓库自己的门禁 Changeset Fixed Group Check 判绿,与该判断一致。维护者若认为仍需登记,补一条 patch 即可。

    范围裁定被遵守

    全仓 17 个包的 vitest 配置治理已按认领评论的裁定另立 #3240(未认领),正文写明了本 PR 坐实的分叉机制、17 个包清单,以及动手前必须先回答的「哪些包真的需要本地配置」。附带的 plugin-gantt ELIFECYCLE 未追,正确 —— 那是并发内存漂移,不是同一件事。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions