# 测试分档(本仓库的交付标准) 本仓库的测试按「跑起来时有多少真东西」分三档。每个测试文件的第一行都标了自己属于哪档 (`// L0` / `// L1` / `// L2`),所以「`pnpm test` 必须含三档」不用靠记忆,可以直接验证: ```bash grep -h '^// L[012]' tests/*.spec.ts | sort | uniq -c # 应看到三档都有 ``` ## 三档是什么 | 档 | 这档在测什么 | 怎么认出来 | | --- | --- | --- | | **L0** | 纯函数:没有 IO、不碰宿主,输入输出一对一 | 文件里没有文件系统、网络、真实宿主对象 | | **L1** | 搭起真实结构再测:真装载、真读写文件、真跑协议处理,只把宿主对象换成假的 | 用真实 loader、真实临时目录,或伪造的请求/响应对象 | | **L2** | harness 边界回归:拿真实会话持久化栈跑一整个冷加载回路(事故防线,见下) | 真实 `SessionStore` + 真实 JSONL 持久化 + 临时根 | > `L1` 包含两类,都算:**真实装配**(真 loader 把插件装起来)与**真实 IO / 协议语义** > (真在临时目录写 sidecar、真跑七条路由与回环护栏)。判定看的是「被测对象跑在真实结构上」, > 不是「有没有用真 loader」——只按后者判,`service.spec.ts` 与 `routes.spec.ts` 这 95 个用例 > 会无处安放。 ## 每个测试文件属于哪档(共 233 例) **L0 — 9 个文件 / 132 例** | 文件 | 例 | 测什么 | | --- | --- | --- | | `tests/validate.spec.ts` | 8 | 条目结构校验、控制字符检测与消毒 | | `tests/commands.spec.ts` | 22 | `/ref-lib` 参数解析、`~` 与相对路径解析、来源会话匹配 | | `tests/logic.spec.ts` | 36 | 列表增删改、可用性探测与写回判定、导入计划、归档/隐藏/分组过滤 | | `tests/render.spec.ts` | 16 | 注入模板、`/ref-lib list` 输出、导入来源清单 | | `tests/glyph.spec.ts` | 7 | 图标跨代取值回退(用哨兵表,不加载真实 ui-primitives) | | `tests/refresh-guard.spec.ts` | 7 | 并发读的乱序与作废守卫 | | `tests/refresh-triggers.spec.ts` | 10 | 会话节点 → 刷新计数的信号语义 | | `tests/client-data.spec.ts` | 24 | 路由响应解析、路径基名、导入冲突分类 | | `tests/locales.spec.ts` | 2 | 中文/英文键集对等 + 关键文案抽查 | **L1 — 4 个文件 / 99 例** | 文件 | 例 | 测什么 | | --- | --- | --- | | `tests/plugin.spec.ts` | 2 | `ctx.plugin(RefLibPlugin)` 之后 `ctx.refLibs` 可用、列表为空;重复加载被拒 | | `tests/loader.spec.ts` | 2 | 真实 `cordis-plugin-loader` 装载与用户安装一致的组合 | | `tests/service.spec.ts` | 63 | 真实临时目录上的 sidecar 读写、探测写回、分支会话继承、跨会话导入、重复添加、隐藏会话过滤 | | `tests/routes.spec.ts` | 32 | 回环护栏、七条路由的语义与状态码、请求体上限(伪造请求/响应 + 假服务,不起真实 server) | **L2 — 1 个文件 / 2 例** | 文件 | 例 | 测什么 | | --- | --- | --- | | `tests/harness-roundtrip.spec.ts` | 2 | 真实持久化栈跑「写会话 → flush 落盘 → 全新实例冷加载」;外加一条陷阱守卫 | ## L2 这道防线是怎么来的(2026-08-17 事故) **经过**:v1/v2 把每个会话的参考库列表写成自定义会话事件 `ref-lib/set`。但 harness 的会话 加载器只认白名单 `KNOWN_SESSION_EVENT_TYPES` 里的事件类型,白名单外的必须带 `ignorable: true` 信封标记,而 `session.append()` 没有写入该标记的途径——于是**任何包含该事件的 会话日志被整体拒读**,连写它的 harness 自己也读不回来。 **连带事实**:v0→v1 的历史迁移边拒绝未知事件类型,**即使事件带了 `ignorable: true`**。所以 「补过标记的旧日志仍可读」只对 ≤0.1.2-rc.1 的宿主成立;在 ≥0.1.3-alpha.2 的宿主上,含 `ref-lib/set` 的旧会话读不出来,那批会话的参考库列表只能重新添加。 **修法**:v3 起 per-session 状态一律存 sidecar(`src/service.ts` 头部有说明),插件不再写任何 会话事件。详见 `AGENTS.md` 的「硬约束」第 1 条。 **防线**:`tests/harness-roundtrip.spec.ts` 的两条用例——① 插件操作之后日志冷加载成功、事件 往返一致、不含 `ref-lib/set`;② **陷阱守卫**:故意往会话写一个白名单外事件,冷加载**必须**抛 `SessionFormatUnsupportedError`(即事故的精确复现)。第 ② 条的意义是:万一有人把设计改回 「状态写进会话日志」,它会立刻变红。 ## 交付要求 1. `pnpm test` 必须含三档。新增测试文件时,在**文件第一行**标档(`// L0` / `// L1` / `// L2`); 测试性质变了就改标注,别让标注和内容对不上。 2. 任何**往会话日志或持久化写入**的改动,必须带着 L2 用例一起交付——没有真实 `SessionStore` 的替代品不算数。 3. 「联调一律用隔离环境」「动真实 `~/.dsh` 前先整目录备份」这两条是**开发流程**约定,不是测试 档位,写在 `docs/agents/dev-env.md` 里。 > 本仓库只用 L0 / L1 / L2 三个编号。不要再引入 L3、L4 之类的说法——那属于流程约定,用中文直说 > 「隔离联调」「动手前备份」更清楚。