# 测试 没有构建步骤,没有打包器,没有任何东西对 `lib/client.js` 做类型检查。所以测试是唯一的安全网, 它的形状也就跟着这个事实长:**纯规则用断言钉死,UI 用真实渲染跑一遍**。 ```bash npm test # node --test test/*.test.mjs npm run test:render # node test/render-smoke.mjs ``` 单个文件:`node --test test/tree.test.mjs`。 测试全部用 `node:test` + `node:assert`,没有第三方测试框架、没有 DOM 实现依赖—— `render-smoke` 自带一个极小的假 document。 ## 1. 五十一个测试文件,各自钉住什么 | 文件 | 行数 | 钉住的东西 | |---|---|---| | `test/tree.test.mjs` | 580 | 树投影的规则 R1–R5:嵌套、计数、降级、环、`sessionHome` | | `test/tree-mirror.test.mjs` | 178 | 客户端只持有**遍历**、不持有**规则**——同一问题在两处必须同答案 | | `test/sessions.test.mjs` | 229 | 合并视图:一个会话"是什么"的唯一判定处 | | `test/store.test.mjs` | 422 | 侧车存储、原子写、revision、损坏隔离,以及改它的两个规则模块 | | `test/revision.test.mjs` | 183 | 乐观锁的两端:侧车拒绝过期写,HTTP 层把 409 送到客户端 | | `test/shortcuts.test.mjs` | 159 | 快捷方式永不移动目标;主题 store 的往返 | | `test/flags.test.mjs` | 183 | 收藏/待办标记 → 快捷方式的一次性迁移:幂等、写失败先留旧标记、过期标记不中断启动 | | `test/theme.test.mjs` | 104 | 主题 store 只交出窗口用的 token,且拒绝注入 | | `test/theme-report.test.mjs` | 132 | 面板**上报**的那一半:DSH 把调色板声明在 `body` 上、值是 `var()` 引用 | | `test/bridge.test.mjs` | 143 | 总线的边界:容量、30 秒时效、游标、坏订阅者 | | `test/settings.test.mjs` | 295 | 设置文档的默认值、合并、归一化;日期格式、大小单位、搜索索引的开关都是封闭词表。旧文档里的 `search.indexCache` 作为未知键被丢弃,不生效 | | `test/settings-page.test.mjs` | 1277 | 设置页真实渲染:打开方式、搜索索引组(**只剩「空闲时自动建立」一个开关**、进度、重建/删除、失败文案)、AI 字段组(模型与强度都是从宿主目录画出来的 `