test(push): 补 pi 建议的那一格 —— DailyLimit=1 时失败后仍要能发

上一提交补的是**内部计数器**(dayCount == 0)。pi 在报告里建议的是
**用户看得见的行为**:DailyLimit=1,一次 accessToken 失败后第二次
仍然要能发出去。

两层都要钉的理由:计数器对而行为错是可能的 —— 那会让运维收到
「达到每日推送上限」这种**误导性文案**,真实原因却是上一次网络抖动。

变异验证:把 reserveDaily 挪回 accessToken 之前 ⇒ 两格同时红。

    --- FAIL: TestHMSAccessTokenFailureDoesNotBurnQuota
    --- FAIL: TestHMSQuotaSurvivesTokenFailureWithLimitOne
This commit is contained in:
2026-09-28 09:50:24 +08:00
parent 186cf53804
commit bfc9d87b24

View File

@ -701,3 +701,38 @@ func TestHMSAccessTokenFailureDoesNotBurnQuota(t *testing.T) {
t.Fatalf("成功这一次应恰好计 1 条,实际 %d", used)
}
}
// TestHMSQuotaSurvivesTokenFailureWithLimitOne 用 pi 建议的形状再钉一遍:
// `DailyLimit=1` 时,一次 accessToken 失败之后**第二次仍然要能发出去**。
//
// 为什么单独一格而不是并进上面那格:上面断言的是内部计数器(dayCount),
// 这格断言的是**用户看得见的行为** —— 失败后收到的下一个错误不能是
// 「达到每日推送上限」这种误导性文案(它会让运维以为配额用完了,
// 而真实原因是上一次网络抖动)。计数器对、行为错是可能的,
// 所以两层都要钉。
func TestHMSQuotaSurvivesTokenFailureWithLimitOne(t *testing.T) {
s := newHMSStub(t)
h := s.hms()
h.DailyLimit = 1
s.mu.Lock()
s.failToken = true
s.mu.Unlock()
err1 := h.Send(context.Background(), []string{"tok-1"}, NewMail{MailID: "m1"})
if err1 == nil {
t.Fatal("accessToken 失败时必须返回错误")
}
s.mu.Lock()
s.failToken = false
s.mu.Unlock()
err2 := h.Send(context.Background(), []string{"tok-2"}, NewMail{MailID: "m2"})
if err2 != nil {
if strings.Contains(err2.Error(), "上限") {
t.Fatalf("★ 第二次被当成配额用尽拒绝(%v)—— 那次失败烧掉了当日配额,"+
"而用户看到的文案会指向错误的原因", err2)
}
t.Fatalf("恢复正常后第二次必须发出去,实际: %v", err2)
}
}