Files
MeBox/internal/service/cloud115/url_cache_test.go
T
truewhile ca9a80b862 优化
2026-10-18 22:30:02 +08:00

102 lines
3.7 KiB
Go

package cloud115
import (
"fmt"
"testing"
"time"
)
func TestDownloadURLCache(t *testing.T) {
urlCacheMu.Lock()
urlCache = map[string]urlCacheEntry{}
urlCacheMu.Unlock()
if got := GetDownloadURLCache("pick1"); got != "" {
t.Fatalf("empty cache should return empty, got %q", got)
}
SetDownloadURLCache("pick1", "http://cdn/1")
if got := GetDownloadURLCache("pick1"); got != "http://cdn/1" {
t.Fatalf("cache miss, got %q", got)
}
// 过期条目按未命中处理
urlCacheMu.Lock()
urlCache["pick1"] = urlCacheEntry{url: "http://cdn/old", expiresAt: time.Now().Add(-time.Second)}
urlCacheMu.Unlock()
if got := GetDownloadURLCache("pick1"); got != "" {
t.Fatalf("expired entry should be a miss, got %q", got)
}
ClearDownloadURLCache("pick1")
if got := GetDownloadURLCache("pick1"); got != "" {
t.Fatalf("cleared entry should be a miss, got %q", got)
}
}
// 115 直链自带 `t=` 过期时间戳(实测为换取时刻 + 1800 秒)。缓存必须跟随它,
// 否则会出现「服务端认为还有效、CDN 已经 403」的窗口:直链被缓存到 45 分钟,
// 但第 30 分钟起客户端就必然拿到 403。
func TestDownloadURLCacheExpiryFollowsLinkTimestamp(t *testing.T) {
now := time.Now()
link := fmt.Sprintf("https://cdn/1/a.mkv?t=%d&u=1&k=abc", now.Add(30*time.Minute).Unix())
got := cacheExpiry(link, now)
want := now.Add(30*time.Minute - linkExpirySafety)
if diff := got.Sub(want); diff > time.Second || diff < -time.Second {
t.Fatalf("cacheExpiry = %v, want %v", got, want)
}
// 解析不出 t= 时退回兜底 TTL,保持旧行为。
fallback := cacheExpiry("https://cdn/1/a.mkv", now)
if diff := fallback.Sub(now.Add(downloadURLCacheMaxTTL)); diff > time.Second || diff < -time.Second {
t.Fatalf("fallback cacheExpiry = %v, want %v", fallback, now.Add(downloadURLCacheMaxTTL))
}
// 直链本身已经过期(或只剩不足提前量的寿命)时不进缓存。
if got := cacheExpiry(fmt.Sprintf("https://cdn/1/a.mkv?t=%d", now.Add(-time.Minute).Unix()), now); !got.IsZero() {
t.Fatalf("expired link must not be cached, got expiry %v", got)
}
if got := cacheExpiry(fmt.Sprintf("https://cdn/1/a.mkv?t=%d", now.Add(time.Minute).Unix()), now); !got.IsZero() {
t.Fatalf("link expiring inside the safety margin must not be cached, got %v", got)
}
}
// 客户端上报「直链被 CDN 拒绝」时按 pickcode 清缓存,但要挡住刚写入的条目,
// 避免重试风暴把 115 换链接口打出突发。
func TestClearDownloadURLCacheIfOlderThan(t *testing.T) {
urlCacheMu.Lock()
urlCache = map[string]urlCacheEntry{}
urlCacheMu.Unlock()
t.Cleanup(func() {
urlCacheMu.Lock()
urlCache = map[string]urlCacheEntry{}
urlCacheMu.Unlock()
})
future := time.Now().Add(30 * time.Minute)
link := fmt.Sprintf("https://cdn/1/a.mkv?t=%d", future.Unix())
SetDownloadURLCache("pick-fresh", link, "ua-a")
if ClearDownloadURLCacheIfOlderThan("pick-fresh", time.Minute) {
t.Fatal("fresh entry must survive the min-age guard")
}
if got := GetDownloadURLCache("pick-fresh", "ua-a"); got != link {
t.Fatalf("fresh entry lost, got %q", got)
}
SetDownloadURLCache("pick-old", link, "ua-a")
urlCacheMu.Lock()
entry := urlCache[urlCacheKey("pick-old", "ua-a")]
entry.createdAt = time.Now().Add(-10 * time.Minute)
urlCache[urlCacheKey("pick-old", "ua-a")] = entry
urlCacheMu.Unlock()
if !ClearDownloadURLCacheIfOlderThan("pick-old", time.Minute) {
t.Fatal("stale entry should be cleared")
}
if got := GetDownloadURLCache("pick-old", "ua-a"); got != "" {
t.Fatalf("stale entry should be gone, got %q", got)
}
// 无关 pickcode 不受影响。
if got := GetDownloadURLCache("pick-fresh", "ua-a"); got != link {
t.Fatalf("unrelated pickcode lost, got %q", got)
}
}