Documentation
¶
Overview ¶
Package sidetest 副作用闸(sideeffect.Store)的**共享契约测试套件**。
为什么需要它 ¶
sideeffect.Store 的正确性无法靠「看起来对」保证:它要跨进程、靠一次原子 操作维持三个判据的联合,而**每一家宿主都会自己写一遍存储实现**。 各写一遍必然各判一套,语义漂移开到没人发现为止——这正是本仓 「跨包契约的规则只能有一份,且要有端到端用例」那条纪律要防的事。
套件把「宿主实现必须满足什么」固化成**一组可复跑的子测试**: 宿主写完存储实现后,把本套件接进自己的测试里跑一遍, 违反哪条契约就红在哪一条,不必等人肉 review 发现。
范式沿用上游 `oekit/pkg/cache/cachetest` 与 `pkg/singleflighttest` (同一家族的做法):共享套件 + **硬失败,不静默跳过**。
为什么是「硬失败」 ¶
套件入口一律 `t.Fatalf` 而不是 `t.Skip`。理由:调用方**显式接入**套件 就是在声明「我这个实现要满足副作用闸的契约」——此时跳过一个场景, 等于把「我没测过」伪装成「我通过了」。这与本仓反复出现的教训同型: **静默跳过是最贵的失败形态**,因为它让未验证的东西看起来像已验证。
覆盖了什么 ¶
套件覆盖的是**跨实现必须一致**的语义(每条子测试对应一个真实失效形态):
- apply_once 同一 Key 第二次 Acquire → replayed,且取回同一结果
- completed_wins 已完成优先于「资源正忙」(不因占用而看不到已完成)
- contended_while_held 租约未过期时,第二个持有者拿到 contended
- takeover_after_expiry 租约过期后可被接管,且新令牌更大
- fence_monotonic 同一 Resource 上令牌单调递增,不回退
- stale_complete_rejected 过期持有者的 Complete 被拒(ErrFenceStale)
- stale_release_rejected 过期持有者的 Release 被拒(不能放掉新持有者的闸)
- release_does_not_done Release 不把 Key 标记为已完成
另有两场景在 **renew.go**(续期一族,与「结果判据」是不同语义):
- renew_extends_lease Renew 延长有效期且不换令牌
- renew_after_takeover_rejected 闸已易主后 Renew 被拒(假续期的封口)
「结果未知」一族五场景在 **uncertain.go**(v0.32.0 新增,判失败路径上的 「至多一次」是否真的成立):
- uncertain_blocks_execution 结果未知的 Key 不得再被授予执行权
- resolve_not_applied_allows_retry 裁决「未生效」后该 Key 重新可执行
- resolve_applied_records_result 裁决「已生效」后该 Key 重放既有结果
- resolve_first_wins 同一 Key 的多次裁决先到者为准
- stale_fence_cannot_mark 过期持有者不得标记未知态
时钟 ¶
租约过期需要可控时钟,否则只能靠 sleep(慢且 flaky)。 套件提供 FakeClock;实现须通过 ClockedStore 接口暴露「用哪个时钟」, 且**过期判定必须读这个时钟**(不是 time.Now)—— 否则套件的过期场景测的是真实时间,恒不触发,套件就成了一组空断言。
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type ClockedStore ¶
type ClockedStore interface {
sideeffect.Store
// UseClock 指定存储实现读时钟的方式(通常是注入一个 func() time.Time)。
UseClock(now func() time.Time)
}
ClockedStore 实现须提供的能力:把时钟注入到存储实现里。
**为什么单独一个接口**:过期判定是存储实现内部的事(它自己知道 「租约还在不在」)。套件不能替它判,只能要求它**用可注入的时钟**。 没实现这个接口 → 套件硬失败(而不是跳过过期场景——跳过等于假绿)。
type Factory ¶
type Factory func(t *testing.T, clock *FakeClock) ClockedStore
Factory 构造被测存储实现。
factory 每个子测试调用一次,构造一份全新实现,并把套件的假时钟注入它。 实现的后台资源由 factory 经 t.Cleanup 清理。