豆备 DOUBAK
← 开发日志

全绿,只是没在查

四个检查都写了,都写得不错,没有一个在该响的地方响:一个长在别的仓库里,一个长在已经不存在的路径后面,一个长在永远有常驻项的清单上,还有一个长在最后那句话前面。

九个仓库,四套测试,全绿。而这一天查出来的四件事, 每一件都有一个正确的检查在盯着它——只是没有一个在它该响的地方响。

一、检查在,但不在改动作者的那个仓库里

命令行的建站工具和浏览器扩展,共用同一份代码:扩展把上游三个仓库的 23 个纯函数文件按字节抄进自己的 src/vendor/, 再用一个脚本守着「有没有漂」。理由在 当初那篇里: 字节从哪儿来可以各写各的,字节怎么解释只能有一份。

然后 9 月 4 日那天是这样的:

19:55  doubak-extension       最后一次提交
20:05  doubak-site-generator  修好「删掉再重标会把旧那条整个盖掉」
                              —— 没有人再跑一次同步

接下来两天,命令行出的站点是修好的,而面板里「导出 → Markdown」 出的站点,仍然带着那个 bug:把一部电影 删掉重标之后, 2018 年那次的短评和标签会被静默盖掉。首页的广播预览也还是两条而不是五条。

检查存在的,而且写得比现在这条细。它在扩展仓库里。 而改动是在建站工具仓库里做的,那边的 npm test 才是作者会跑的那一个。

这与 9 月 4 日 那篇记下的是同一个形状: 每一句话都对,但做决定的人在按下去之前读不到它。 那次是两个对话框,这次是两个仓库。

修法是让三个上游各自也长一条同样的检查,读的是同一份名单 (抄第二份名单,两份就会漂,而漂的方向是「这边少列了一个」)。 代价照说:两个仓库没有原子提交,所以跨仓库的改动会红一会儿——先推上游, 上游红;再同步推到扩展,两边都绿。那段红是对的: 那段时间里两个宿主对同一份档案确实会给出不同结果。 上一次同样的不一致完全无声,持续了两天。

二、测试在,但它们一秒钟跑完,因为什么都没查

这个项目里几乎每一个真 bug,都不是靠读代码读出来的, 是靠喂给它没见过的真实数据—— 两条一直是绿的断言就是这么塌的。 所以解析器里有一批测试,跑的是真实档案而不是合成夹具。

它们指着 ~/downloads/20260806。而那些档案后来被归拢进了 ~/downloads/exports/

于是那批测试从「这台机器上没有档案,跳过」变成了永远跳过。 整套测试照样全绿,而且跑得更快了。

之前现在
通过205226
跳过180
耗时约 10 秒约 80 秒

那 18 条里包括「广播发出去就不能改」「分叉的目录合并起来恰好是并集」 ——正是这个项目最重要的几条不变量。

路径修好之后又撞上第二层:十几处测试各自把 26 份档案完整解析一遍, 一趟五分多钟,而且在并发下整体崩掉,报出来只有一句没有断言的 'test failed'一套要跑五分钟的测试,和一套永远跳过的 测试,在「有没有人跑它」这件事上是同一个结果。 一份档案解析一次、各条测试共用,回到 80 秒。

另外几条测试读的是手工另存在下载目录里的单张页面,那些文件早就没了。 现在改成从档案里读——那张页面本来就在里面,冻着,跑不掉。 顺带还多证明了一件事:它确实在档案里。

三、清单在,但它上面那两条永远消不掉

解析器跑完会印一句「可离线救回 N 条」:这些页面的字节已经在档案里, 只是当时没读出来,改好抽取器重跑就行,不必再去打扰豆瓣。 真实档案上,它常年印着 2 条。

顺着编号挖出来,是同一篇日记在同一次抓取里被抓的三遍:

#000010   判不出来(当时还不认 /topic/ 那套模板)
#001270   判不出来
#001564   ok

中途重载了扩展,第三遍就成了。那篇日记连正文带两张配图, 早就完完整整在档案里。而档案是冻结的——前两条捕获 再也不会变,所以那两行会一直挂在待办清单上,直到永远。

一个永远有条目的待办清单,是没人看的待办清单。 这个项目已经数到第六次了。两条常驻项,就足够让第三条真的挡不住人的眼。

判据是现成的,跟 缺口那次 一模一样:同一个网址的一次成功捕获,就把「这一条读不出来」证伪了。 但也不能抹掉——那一页确实少了一次观测,所以它折成一行说出来: 「另有 2 条判不出来的捕获,它们的网址在这份档案里另有成功捕获——内容不缺。」

四、校验器说了它跳过了一层,然后说「通过」

数据格式的校验器分两层,第二层要装一个可选的库才跑。没装的时候它这样说:

提示: 未安装 jsonschema,跳过 schema 层校验,仅运行结构性检查。

(……)

通过

被读进去的是最后那两个字。

这不是假想。写 bundle/1.4 那个新字段的时候,产出的档案有一小段时间是不合规的, 而本机的校验器从头到尾只说了「通过」——因为那种不合规 恰恰只有被跳过的那一层看得见

现在最后那句话自己带着它:

通过(**只跑了结构层** —— 没装 jsonschema,schema 层没查)

退出码没动:少装一个可选依赖不是档案的毛病,把它算成失败会让 「零依赖也能跑」这句话作废。要改的是那句话,不是那个数。

共同点

四件事里没有一件是「忘了写检查」。四个检查都写了,而且都写得不错。 问题全出在它长在哪儿

  • 长在另一个仓库里——作者跑不到;
  • 长在一条已经不存在的路径后面——它跑了,但没查任何东西;
  • 长在一张永远有两条常驻项的清单上——它响了,但没人再看;
  • 长在最后那句话前面——它说了,但那不是被读进去的那一句。

「有没有这个检查」是个容易回答的问题,而且答案通常是「有」。 「做这件事的人,在那个当口会不会读到它」——这才是要问的那个。

更早:同一份档案放了两处,出处就多了九千条 →