Files
MeBox/internal/service/reader/rate_limit_test.go
T
truewhile db516ded61 优化
2026-10-29 00:09:00 +08:00

107 lines
3.2 KiB
Go

package reader
import (
"testing"
"time"
)
// TestParseConcurrentRate 书源 concurrentRate 的两种写法。
func TestParseConcurrentRate(t *testing.T) {
cases := []struct {
in string
want time.Duration
}{
{"", 0},
{"0", 0},
{"abc", 0},
{"1000", 1000 * time.Millisecond},
{" 500 ", 500 * time.Millisecond},
{"3/1000", 333 * time.Millisecond},
{"1/2000", 2000 * time.Millisecond},
{"3/0", 0},
{"0/1000", 0},
{"999999", time.Duration(maxRateGapMs) * time.Millisecond}, // 上限夹紧
}
for _, c := range cases {
if got := parseConcurrentRate(c.in); got != c.want {
t.Errorf("parseConcurrentRate(%q) = %v,期望 %v", c.in, got, c.want)
}
}
}
// TestRateLimiterSpacesRequests 同一个源连续两次请求之间至少间隔一个周期。
func TestRateLimiterSpacesRequests(t *testing.T) {
l := newSourceRateLimiter()
start := time.Now()
l.wait(t.Context(), "src", "120") // 120ms
l.wait(t.Context(), "src", "120")
if elapsed := time.Since(start); elapsed < 110*time.Millisecond {
t.Fatalf("两次请求只隔了 %v,没有按 concurrentRate 限速", elapsed)
}
// 不同源互不影响
start = time.Now()
l.wait(t.Context(), "other", "500")
if elapsed := time.Since(start); elapsed > 50*time.Millisecond {
t.Fatalf("换源后不该等待,实际 %v", elapsed)
}
}
// TestRateLimiterIgnoresEmptyRate 未声明 concurrentRate 时不引入任何等待。
func TestRateLimiterIgnoresEmptyRate(t *testing.T) {
l := newSourceRateLimiter()
start := time.Now()
for i := 0; i < 5; i++ {
l.wait(t.Context(), "src", "")
}
if elapsed := time.Since(start); elapsed > 50*time.Millisecond {
t.Fatalf("未声明限速却等待了 %v", elapsed)
}
}
// TestRetryableStatus 只有可恢复的失败才重试。
func TestRetryableStatus(t *testing.T) {
for _, code := range []int{403, 429, 500, 502, 503, 504} {
if !retryableStatus(code) {
t.Errorf("状态码 %d 应当可重试", code)
}
}
for _, code := range []int{200, 301, 400, 401, 404, 410} {
if retryableStatus(code) {
t.Errorf("状态码 %d 不该重试", code)
}
}
}
// TestMediaRetryableStatus 媒体直链的 410(对象已下线)与 403/429 一样值得原地重试,
// 但 401「签名票被拒」原地重试一定是同样的结果,只能靠回源换票。
func TestMediaRetryableStatus(t *testing.T) {
for _, code := range []int{403, 410, 429, 500, 503} {
if !mediaRetryableStatus(code) {
t.Errorf("媒体状态码 %d 应当可重试", code)
}
}
for _, code := range []int{200, 206, 301, 400, 401, 404} {
if mediaRetryableStatus(code) {
t.Errorf("媒体状态码 %d 不该原地重试", code)
}
}
// 规则请求的判定不能被放宽:登录态失效的 401 反复重试只会白等。
if retryableStatus(401) || retryableStatus(410) {
t.Error("retryableStatus 不应包含 401/410")
}
}
// TestMediaNeedsRefresh 只有「票被拒」这一类状态才值得回源换票。
func TestMediaNeedsRefresh(t *testing.T) {
for _, code := range []int{401, 403, 410} {
if !MediaNeedsRefresh(code) {
t.Errorf("状态码 %d 应当换票重试", code)
}
}
for _, code := range []int{200, 206, 304, 400, 404, 429, 500} {
if MediaNeedsRefresh(code) {
t.Errorf("状态码 %d 不该换票", code)
}
}
}