diff --git a/server/internal/push/push_test.go b/server/internal/push/push_test.go index 57f2678..940f8af 100644 --- a/server/internal/push/push_test.go +++ b/server/internal/push/push_test.go @@ -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) + } +}