2026-09-27·by Sijie Wang#standmeet#architecture#design#events

events-test-plan

事件总线的测试计划

上级:events

状态: 已在 v0.1.76 发布(2026-09-27)—— 设计与落地记录见 StandMeet 仓库的 docs/design/event-bus-outbox-webhooks.md。下面的全套验收已于 2026-09-27 通过。

内部机制靠真 Postgres 上的 UT 证明,对外行为靠黑盒 e2e 证明,每一期收尾前都要回归受影响的现有 spec。UT 用真 Postgres、httptest 端点,不 mock 数据库。

UT:已有的

套件覆盖
River 一致性(约 27)Jobs / Inspector / Runtime 的契约:生命周期、尝试次数、snooze、discard、按参数唯一、查看、等待;现在对 River 跑,将来换实现原样跑
events 的 relay / 触发器 / recorder(约 26)按行领取;交错提交不丢;批大小;poison 标记;多个 relay 并发不重复扇出;不合并的订阅方收到同一主体的每条事件;每个被监视列都出事件、空更新不出;未声明类型报错
饱和套件连接池耗尽;磁盘满且 relay 退避;429 冷却;Meili 挂掉后积压排空;积压暴增告警;等待者上限;队列饥饿;任务超时(saturation-degrades-gracefully)
注册表驱动的幂等性(cmd/server/wire)每个已注册订阅方收同一事件两次,效果只发生一次;遇到不知道怎么驱动的订阅方就失败
事件类型注册表每个类型只声明一次、名词加动词命名、有描述和主体模式、Exposure 没被无意停在零值
每个用例的事件耦合每个产生事件的用例都在写入的同一事务里记下事件
webhook 签名、分类、范围Standard Webhooks 测试向量;每种状态码和传输错误 → retryable / snoozed / discarded;raw 永不出、已发布切片、码的 deny、embed 范围
embed hook upsertembed 的 update_hook_url 创建或更新它挂的端点
schema 一致性TestMigrationsAddNothingToASchemaSQLDatabase;TestSQLPathSegmentMatchesGo

“交错提交不丢”来自 message-loss-guarantees;注册表驱动的幂等性测试来自 no-bypass-by-structure。

新增 e2e:已有的

期spec证明什么
P1events-index-via-bus用 MCP corpus.create 创建的条目立即可搜;更新和删除立即反映,且回执如实说明;Meili 故障期间的写入返回回执,Meili 恢复后被索引,中间重启 backend 也不丢
P1events-bulk-import-bound导入的笔记全部可搜,索引任务不超过 N + 余量,且没有 discarded
P1tasks-panel、tasks-panel-moretasks-panel 里的 12 条,走真实 UI
P1upgrade-events-outbox总线出现之前的实例重启即升级:启动时跑迁移和 River 的迁移器,之后编辑的旧笔记被索引,周期任务跑在 River 上
P2webhooks签名事件送达且可验;只发已发布切片(哨兵式);raw 永不出;500×2 后成功且只收一次;429 带 Retry-After 不计次数;410 立即 discarded,之后可重投;send_test;轮换密钥;停用的端点收不到;指向内网地址的端点被拒
P2webhook-event-types每种开放类型由真实动作产生,都到达 * 端点
P2events-fault-injection比超时还慢的端点被重试,之后送达;连接被拒立即 retryable 并进入冷却;Postgres 重启后,编辑仍到达接收方和搜索
P3embed-update-hookembed 表单的 hook 挂上端点,编辑带签名到达;范围就是码的范围(哨兵式);再存同一个 URL 保留原端点且不给新密钥;删除带最后已知的主体;cards 带 updated_at
P4events-side-effects-durable失败过一次的申请通知仍送达;批准从发送中变已发送,邮件发出后才标记已回复;永久失败时显示失败且不标记已回复;重启前排队的通知重启后送达;重启中的预约仍通知 owner
P4events-build-settledbackend 重启后才完成的构建仍能到达打开着的预览
  • webhook sink 是外部 mock 上的一组路由(mock-stack/job-board/webhook_sink.go),/__mock/set_delay 用来模拟慢接收方。
  • 现有 spec 只改“什么时候看”,不改“看到什么”(completion-hooks)。norm-outward-toolset 的 golden 加上了 tasks.*、events.*、webhooks.* 和 jobs.fetch_result 工具。
  • 门禁不是 e2e,也不留自测脚本:7 条各在临时副本里植入的样本上证红过一次(no-bypass-by-structure)。
  • 从计划里去掉的:招聘源定时抓取(按决定不做)、webhook 防抖(webhook 从不合并)。

回归

  • 每期:跑受影响的现有 spec(embed、corpus、access、booking、microsite、obsidian、jobs、monitor、mail、search、supplier、calendar、upgrade…),REPEAT=5 排除闪烁。
  • 顺序:每个组件先写 UT,每个验收项先写 e2e 并在原代码上证红(make test-asis),再写实现。

最终验收:这个仓的所有测试全绿(2026-09-27 已通过)

命令覆盖
make lintsecrets、env、backend(含全部 check-*.sh 门禁,含本计划的 7 条)、no-mock、app、sdk、e2e、im-bridge、verify-items
make backend-testGo 测试,含新增 UT
make test-unit前端 / SDK 单测
make im-bridge-testim-bridge
make stack-test整栈测试
make dsh-plugin-test每个 block 装进真 dsh 跑通
make test-fresh全套 e2e,从空卷起,验证 schema.sql 与迁移一致
make eval-*真 agent 循环的评测(blocks、owner-mcp、ghost、summary 等)

一次过,不靠重跑刷绿。 以上全部一次性全绿;失败先读日志定位机制,不靠重跑刷绿。然后才合并 main、发版、升级 sijie.xyz、部署 standmeet.com,最后做 P3 的真环境验收并归档到 docs/real-env-verification/。分期状态见 events-roadmap。

about this entry

One of sijie's wiki entries. The AI on this site is grounded in the same corpus and answers in sijie's voice, with citations back to entries like this one — answering costs sijie money, so it waits behind a code: enter an access code →