test(cache): 添加 stale_if_error 和 stale_if_timeout 测试
覆盖 ProxyCache、DiskCache、TieredCache 的 GetStale 方法, 测试场景包括:错误时可用、超时时可用、窗口过期不可用、未过期直接返回。 同步更新 NewProxyCache 调用签名。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
be974b2e18
commit
0de153bb24
6
internal/cache/cache_bench_test.go
vendored
6
internal/cache/cache_bench_test.go
vendored
@ -194,7 +194,7 @@ func BenchmarkFileCacheLRUTouch(b *testing.B) {
|
||||
|
||||
// BenchmarkProxyCacheGet 测试代理缓存 Get 性能。
|
||||
func BenchmarkProxyCacheGet(b *testing.B) {
|
||||
pc := NewProxyCache(nil, false, 0)
|
||||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||||
|
||||
// 预填充缓存
|
||||
for i := 0; i < 1000; i++ {
|
||||
@ -219,7 +219,7 @@ func BenchmarkProxyCacheGet(b *testing.B) {
|
||||
|
||||
// BenchmarkProxyCacheSet 测试代理缓存 Set 性能。
|
||||
func BenchmarkProxyCacheSet(b *testing.B) {
|
||||
pc := NewProxyCache(nil, false, 0)
|
||||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||||
data := []byte("response body")
|
||||
headers := map[string]string{"Content-Type": "application/json"}
|
||||
|
||||
@ -234,7 +234,7 @@ func BenchmarkProxyCacheSet(b *testing.B) {
|
||||
// BenchmarkProxyCacheConcurrent 测试代理缓存并发混合负载。
|
||||
// 使用 90% Get / 10% Set 的混合负载。
|
||||
func BenchmarkProxyCacheConcurrent(b *testing.B) {
|
||||
pc := NewProxyCache(nil, false, 0)
|
||||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||||
|
||||
// 预填充缓存
|
||||
for i := 0; i < 1000; i++ {
|
||||
|
||||
116
internal/cache/cache_test.go
vendored
116
internal/cache/cache_test.go
vendored
@ -89,7 +89,7 @@ func TestFileCacheLRUEviction(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAcquireLockWithTimeout(t *testing.T) {
|
||||
pc := NewProxyCache(nil, true, 0)
|
||||
pc := NewProxyCache(nil, true, 0, 0, 0)
|
||||
key := hashKey("timeout-test")
|
||||
|
||||
// 测试获取锁
|
||||
@ -114,7 +114,7 @@ func TestAcquireLockWithTimeout(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRefreshTTL(t *testing.T) {
|
||||
pc := NewProxyCache(nil, false, 0)
|
||||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||||
key := hashKey("refresh-test")
|
||||
origKey := "refresh-test"
|
||||
|
||||
@ -140,7 +140,7 @@ func TestRefreshTTL(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetValidationHeaders(t *testing.T) {
|
||||
pc := NewProxyCache(nil, false, 0)
|
||||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||||
key := hashKey("validation-test")
|
||||
origKey := "validation-test"
|
||||
|
||||
@ -182,7 +182,7 @@ func TestMatchRulePathVariants(t *testing.T) {
|
||||
rules := []ProxyCacheRule{
|
||||
{Path: tt.rulePath, Methods: []string{"GET"}, MaxAge: time.Minute},
|
||||
}
|
||||
pc := NewProxyCache(rules, false, 0)
|
||||
pc := NewProxyCache(rules, false, 0, 0, 0)
|
||||
rule := pc.MatchRule(tt.reqPath, "GET", 0)
|
||||
if (rule != nil) != tt.want {
|
||||
t.Errorf("MatchRule(%s, %s) = %v, want %v", tt.rulePath, tt.reqPath, rule != nil, tt.want)
|
||||
@ -192,7 +192,7 @@ func TestMatchRulePathVariants(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMinUsesThreshold(t *testing.T) {
|
||||
pc := NewProxyCache(nil, false, 0)
|
||||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||||
key := hashKey("minuses-test")
|
||||
origKey := "minuses-test"
|
||||
|
||||
@ -283,14 +283,14 @@ func TestNewProxyCache(t *testing.T) {
|
||||
{Path: "/api/", Methods: []string{"GET"}, MaxAge: 10 * time.Minute},
|
||||
}
|
||||
|
||||
pc := NewProxyCache(rules, true, 60*time.Second)
|
||||
pc := NewProxyCache(rules, true, 60*time.Second, 0, 0)
|
||||
if pc == nil {
|
||||
t.Error("Expected non-nil ProxyCache")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxyCacheSetGet(t *testing.T) {
|
||||
pc := NewProxyCache(nil, false, 0)
|
||||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||||
|
||||
key := "test-key"
|
||||
data := []byte("response body")
|
||||
@ -314,7 +314,7 @@ func TestProxyCacheSetGet(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProxyCacheExpiration(t *testing.T) {
|
||||
pc := NewProxyCache(nil, false, 0)
|
||||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||||
|
||||
key := "expire-test"
|
||||
pc.Set(hashKey(key), key, []byte("data"), nil, 200, 100*time.Millisecond)
|
||||
@ -335,7 +335,7 @@ func TestProxyCacheExpiration(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProxyCacheStaleWhileRevalidate(t *testing.T) {
|
||||
pc := NewProxyCache(nil, false, 200*time.Millisecond)
|
||||
pc := NewProxyCache(nil, false, 200*time.Millisecond, 0, 0)
|
||||
|
||||
key := "stale-test"
|
||||
pc.Set(hashKey(key), key, []byte("data"), nil, 200, 100*time.Millisecond)
|
||||
@ -356,7 +356,7 @@ func TestProxyCacheStaleWhileRevalidate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProxyCacheLock(t *testing.T) {
|
||||
pc := NewProxyCache(nil, true, 0)
|
||||
pc := NewProxyCache(nil, true, 0, 0, 0)
|
||||
|
||||
key := "lock-test"
|
||||
|
||||
@ -388,7 +388,7 @@ func TestProxyCacheMatchRule(t *testing.T) {
|
||||
{Path: "/static/*", Methods: []string{"GET"}, MaxAge: 1 * time.Hour},
|
||||
}
|
||||
|
||||
pc := NewProxyCache(rules, false, 0)
|
||||
pc := NewProxyCache(rules, false, 0, 0, 0)
|
||||
|
||||
tests := []struct {
|
||||
path string
|
||||
@ -416,7 +416,7 @@ func TestProxyCacheMatchRule(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProxyCacheDelete(t *testing.T) {
|
||||
pc := NewProxyCache(nil, false, 0)
|
||||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||||
|
||||
key := "key1"
|
||||
pc.Set(hashKey(key), key, []byte("data"), nil, 200, 10*time.Minute)
|
||||
@ -429,7 +429,7 @@ func TestProxyCacheDelete(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProxyCacheClear(t *testing.T) {
|
||||
pc := NewProxyCache(nil, false, 0)
|
||||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||||
|
||||
pc.Set(hashKey("a"), "a", []byte("a"), nil, 200, 10*time.Minute)
|
||||
pc.Set(hashKey("b"), "b", []byte("b"), nil, 200, 10*time.Minute)
|
||||
@ -470,3 +470,93 @@ func TestPathMatch(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxyCacheGetStaleIfError(t *testing.T) {
|
||||
pc := NewProxyCache(nil, false, 0, 200*time.Millisecond, 0)
|
||||
|
||||
key := "stale-error-test"
|
||||
pc.Set(hashKey(key), key, []byte("data"), nil, 200, 100*time.Millisecond)
|
||||
|
||||
// 等待过期但仍在 stale_if_error 窗口内
|
||||
time.Sleep(150 * time.Millisecond)
|
||||
|
||||
// isTimeout=false,应该使用 staleIfError 窗口
|
||||
entry, ok := pc.GetStale(hashKey(key), key, false)
|
||||
if !ok {
|
||||
t.Error("stale entry should be usable on error")
|
||||
}
|
||||
if entry == nil {
|
||||
t.Error("expected stale entry data")
|
||||
}
|
||||
if string(entry.Data) != "data" {
|
||||
t.Errorf("entry.Data = %q, want %q", entry.Data, "data")
|
||||
}
|
||||
|
||||
// isTimeout=true,staleIfTimeout=0,不应该可用
|
||||
if _, ok2 := pc.GetStale(hashKey(key), key, true); ok2 {
|
||||
t.Error("stale entry should NOT be usable on timeout when staleIfTimeout=0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxyCacheGetStaleIfTimeout(t *testing.T) {
|
||||
pc := NewProxyCache(nil, false, 0, 0, 300*time.Millisecond)
|
||||
|
||||
key := "stale-timeout-test"
|
||||
pc.Set(hashKey(key), key, []byte("data"), nil, 200, 100*time.Millisecond)
|
||||
|
||||
// 等待过期但仍在 stale_if_timeout 窗口内
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
|
||||
// isTimeout=true,应该使用 staleIfTimeout 窗口
|
||||
entry, ok := pc.GetStale(hashKey(key), key, true)
|
||||
if !ok {
|
||||
t.Error("stale entry should be usable on timeout")
|
||||
}
|
||||
if entry == nil {
|
||||
t.Error("expected stale entry data")
|
||||
}
|
||||
|
||||
// isTimeout=false,staleIfError=0,不应该可用
|
||||
if _, ok2 := pc.GetStale(hashKey(key), key, false); ok2 {
|
||||
t.Error("stale entry should NOT be usable on error when staleIfError=0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxyCacheGetStaleExpired(t *testing.T) {
|
||||
pc := NewProxyCache(nil, false, 0, 100*time.Millisecond, 100*time.Millisecond)
|
||||
|
||||
key := "stale-expired-test"
|
||||
pc.Set(hashKey(key), key, []byte("data"), nil, 200, 50*time.Millisecond)
|
||||
|
||||
// 等待超过 stale 窗口
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
// 两种情况都不应该可用
|
||||
if _, ok := pc.GetStale(hashKey(key), key, false); ok {
|
||||
t.Error("stale entry should NOT be usable after stale window expired")
|
||||
}
|
||||
|
||||
if _, ok2 := pc.GetStale(hashKey(key), key, true); ok2 {
|
||||
t.Error("stale entry should NOT be usable on timeout after stale window expired")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxyCacheGetStaleNotExpired(t *testing.T) {
|
||||
pc := NewProxyCache(nil, false, 0, 100*time.Millisecond, 100*time.Millisecond)
|
||||
|
||||
key := "stale-fresh-test"
|
||||
pc.Set(hashKey(key), key, []byte("data"), nil, 200, 200*time.Millisecond)
|
||||
|
||||
// 未过期,两种情况都应该可用(返回新鲜数据)
|
||||
entry, ok := pc.GetStale(hashKey(key), key, false)
|
||||
if !ok {
|
||||
t.Error("fresh entry should be usable")
|
||||
}
|
||||
if string(entry.Data) != "data" {
|
||||
t.Errorf("entry.Data = %q, want %q", entry.Data, "data")
|
||||
}
|
||||
|
||||
if _, ok2 := pc.GetStale(hashKey(key), key, true); !ok2 {
|
||||
t.Error("fresh entry should be usable on timeout")
|
||||
}
|
||||
}
|
||||
|
||||
111
internal/cache/disk_cache_test.go
vendored
111
internal/cache/disk_cache_test.go
vendored
@ -384,3 +384,114 @@ func TestDiskCacheRestart(t *testing.T) {
|
||||
t.Errorf("Data = %q, want %q", entry.Data, data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiskCacheGetStaleIfError(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
cfg := &DiskCacheConfig{
|
||||
Path: tmpDir,
|
||||
Levels: "1:2",
|
||||
StaleIfError: 200 * time.Millisecond,
|
||||
StaleIfTimeout: 0,
|
||||
}
|
||||
|
||||
dc, err := NewDiskCache(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("NewDiskCache failed: %v", err)
|
||||
}
|
||||
defer dc.Stop()
|
||||
<-dc.loadCh
|
||||
|
||||
hashKey := uint64(12345)
|
||||
origKey := "GET:/api/test"
|
||||
dc.Set(hashKey, origKey, []byte("data"), nil, 200, 100*time.Millisecond)
|
||||
|
||||
// 等待过期但仍在 stale_if_error 窗口内
|
||||
time.Sleep(150 * time.Millisecond)
|
||||
|
||||
// isTimeout=false,应该使用 staleIfError 窗口
|
||||
entry, ok := dc.GetStale(hashKey, origKey, false)
|
||||
if !ok {
|
||||
t.Error("stale entry should be usable on error")
|
||||
}
|
||||
if entry == nil || string(entry.Data) != "data" {
|
||||
t.Errorf("entry.Data = %v, want %q", entry, "data")
|
||||
}
|
||||
|
||||
// isTimeout=true,staleIfTimeout=0,不应该可用
|
||||
if _, ok2 := dc.GetStale(hashKey, origKey, true); ok2 {
|
||||
t.Error("stale entry should NOT be usable on timeout when staleIfTimeout=0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiskCacheGetStaleIfTimeout(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
cfg := &DiskCacheConfig{
|
||||
Path: tmpDir,
|
||||
Levels: "1:2",
|
||||
StaleIfError: 0,
|
||||
StaleIfTimeout: 300 * time.Millisecond,
|
||||
}
|
||||
|
||||
dc, err := NewDiskCache(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("NewDiskCache failed: %v", err)
|
||||
}
|
||||
defer dc.Stop()
|
||||
<-dc.loadCh
|
||||
|
||||
hashKey := uint64(12345)
|
||||
origKey := "GET:/api/test"
|
||||
dc.Set(hashKey, origKey, []byte("data"), nil, 200, 100*time.Millisecond)
|
||||
|
||||
// 等待过期但仍在 stale_if_timeout 窗口内
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
|
||||
// isTimeout=true,应该使用 staleIfTimeout 窗口
|
||||
entry, ok := dc.GetStale(hashKey, origKey, true)
|
||||
if !ok {
|
||||
t.Error("stale entry should be usable on timeout")
|
||||
}
|
||||
if entry == nil {
|
||||
t.Error("expected stale entry data")
|
||||
}
|
||||
|
||||
// isTimeout=false,staleIfError=0,不应该可用
|
||||
if _, ok2 := dc.GetStale(hashKey, origKey, false); ok2 {
|
||||
t.Error("stale entry should NOT be usable on error when staleIfError=0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiskCacheGetStaleExpired(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
cfg := &DiskCacheConfig{
|
||||
Path: tmpDir,
|
||||
Levels: "1:2",
|
||||
StaleIfError: 100 * time.Millisecond,
|
||||
StaleIfTimeout: 100 * time.Millisecond,
|
||||
}
|
||||
|
||||
dc, err := NewDiskCache(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("NewDiskCache failed: %v", err)
|
||||
}
|
||||
defer dc.Stop()
|
||||
<-dc.loadCh
|
||||
|
||||
hashKey := uint64(12345)
|
||||
origKey := "GET:/api/test"
|
||||
dc.Set(hashKey, origKey, []byte("data"), nil, 200, 50*time.Millisecond)
|
||||
|
||||
// 等待超过 stale 窗口
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
if _, ok := dc.GetStale(hashKey, origKey, false); ok {
|
||||
t.Error("stale entry should NOT be usable after stale window expired")
|
||||
}
|
||||
|
||||
if _, ok2 := dc.GetStale(hashKey, origKey, true); ok2 {
|
||||
t.Error("stale entry should NOT be usable on timeout after stale window expired")
|
||||
}
|
||||
}
|
||||
|
||||
26
internal/cache/purge_test.go
vendored
26
internal/cache/purge_test.go
vendored
@ -109,7 +109,7 @@ func TestNewPurgeAPI(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("with cache", func(t *testing.T) {
|
||||
pc := NewProxyCache(nil, false, 0)
|
||||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||||
cfg := &config.CacheAPIConfig{
|
||||
Path: "/custom/purge",
|
||||
Allow: []string{"127.0.0.1"},
|
||||
@ -276,7 +276,7 @@ func TestPurgeAPI_ServeHTTP_AccessForbidden(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPurgeAPI_ServeHTTP_Unauthorized(t *testing.T) {
|
||||
pc := NewProxyCache(nil, false, 0)
|
||||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||||
cfg := &config.CacheAPIConfig{
|
||||
Allow: []string{"127.0.0.1"},
|
||||
Auth: config.CacheAPIAuthConfig{
|
||||
@ -303,7 +303,7 @@ func TestPurgeAPI_ServeHTTP_Unauthorized(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPurgeAPI_ServeHTTP_BadRequest(t *testing.T) {
|
||||
pc := NewProxyCache(nil, false, 0)
|
||||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||||
cfg := &config.CacheAPIConfig{
|
||||
Allow: []string{},
|
||||
}
|
||||
@ -338,7 +338,7 @@ func TestPurgeAPI_ServeHTTP_BadRequest(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPurgeAPI_ServeHTTP_PurgeByPath(t *testing.T) {
|
||||
pc := NewProxyCache(nil, false, 0)
|
||||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||||
key := "GET:/api/test"
|
||||
pc.Set(hashKey(key), key, []byte("data"), nil, 200, 10*60*time.Second)
|
||||
|
||||
@ -376,7 +376,7 @@ func TestPurgeAPI_ServeHTTP_PurgeByPath(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPurgeAPI_ServeHTTP_PurgeByPath_NotFound(t *testing.T) {
|
||||
pc := NewProxyCache(nil, false, 0)
|
||||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||||
cfg := &config.CacheAPIConfig{
|
||||
Allow: []string{},
|
||||
}
|
||||
@ -405,7 +405,7 @@ func TestPurgeAPI_ServeHTTP_PurgeByPath_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPurgeAPI_ServeHTTP_PurgeByPattern(t *testing.T) {
|
||||
pc := NewProxyCache(nil, false, 0)
|
||||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||||
pc.Set(hashKey("GET:/api/users"), "GET:/api/users", []byte("users"), nil, 200, 10*60*time.Second)
|
||||
pc.Set(hashKey("GET:/api/posts"), "GET:/api/posts", []byte("posts"), nil, 200, 10*60*time.Second)
|
||||
pc.Set(hashKey("GET:/static/css"), "GET:/static/css", []byte("css"), nil, 200, 10*60*time.Second)
|
||||
@ -444,7 +444,7 @@ func TestPurgeAPI_ServeHTTP_PurgeByPattern(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPurgeAPI_ServeHTTP_PurgeByPattern_Wildcard(t *testing.T) {
|
||||
pc := NewProxyCache(nil, false, 0)
|
||||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||||
pc.Set(hashKey("GET:/a"), "GET:/a", []byte("a"), nil, 200, 10*60*time.Second)
|
||||
pc.Set(hashKey("GET:/b"), "GET:/b", []byte("b"), nil, 200, 10*60*time.Second)
|
||||
|
||||
@ -472,7 +472,7 @@ func TestPurgeAPI_ServeHTTP_PurgeByPattern_Wildcard(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPurgeAPI_ServeHTTP_PurgeByPattern_DirPrefix(t *testing.T) {
|
||||
pc := NewProxyCache(nil, false, 0)
|
||||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||||
pc.Set(hashKey("GET:/api/v1/users"), "GET:/api/v1/users", []byte("u"), nil, 200, 10*60*time.Second)
|
||||
pc.Set(hashKey("GET:/api/v2/posts"), "GET:/api/v2/posts", []byte("p"), nil, 200, 10*60*time.Second)
|
||||
pc.Set(hashKey("GET:/other"), "GET:/other", []byte("o"), nil, 200, 10*60*time.Second)
|
||||
@ -501,7 +501,7 @@ func TestPurgeAPI_ServeHTTP_PurgeByPattern_DirPrefix(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPurgeAPI_ServeHTTP_ContentType(t *testing.T) {
|
||||
pc := NewProxyCache(nil, false, 0)
|
||||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||||
cfg := &config.CacheAPIConfig{
|
||||
Allow: []string{},
|
||||
}
|
||||
@ -535,7 +535,7 @@ func TestPurgeAPI_ServeHTTP_ContentType(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPurgeAPI_ServeHTTP_AccessAllowed(t *testing.T) {
|
||||
pc := NewProxyCache(nil, false, 0)
|
||||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||||
cfg := &config.CacheAPIConfig{
|
||||
Allow: []string{"10.0.0.0/8"},
|
||||
}
|
||||
@ -558,7 +558,7 @@ func TestPurgeAPI_ServeHTTP_AccessAllowed(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPurgeAPI_ServeHTTP_TokenAuth(t *testing.T) {
|
||||
pc := NewProxyCache(nil, false, 0)
|
||||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||||
cfg := &config.CacheAPIConfig{
|
||||
Allow: []string{},
|
||||
Auth: config.CacheAPIAuthConfig{
|
||||
@ -599,7 +599,7 @@ func TestPurgeAPI_ServeHTTP_TokenAuth(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPurgeAPI_ServeHTTP_AuthTypeNone(t *testing.T) {
|
||||
pc := NewProxyCache(nil, false, 0)
|
||||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||||
cfg := &config.CacheAPIConfig{
|
||||
Allow: []string{},
|
||||
Auth: config.CacheAPIAuthConfig{
|
||||
@ -711,7 +711,7 @@ func TestPurgeAPI_ErrorResponse(t *testing.T) {
|
||||
|
||||
func TestPurgeAPI_PurgeByPath_WrongMethod(t *testing.T) {
|
||||
// Test that hashPath only uses GET, so purging a POST-cached entry won't work
|
||||
pc := NewProxyCache(nil, false, 0)
|
||||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||||
// Set a cache entry with GET:/api/test key
|
||||
pc.Set(hashKey("GET:/api/test"), "GET:/api/test", []byte("data"), nil, 200, 10*60*time.Second)
|
||||
|
||||
|
||||
72
internal/cache/tiered_cache_test.go
vendored
72
internal/cache/tiered_cache_test.go
vendored
@ -357,3 +357,75 @@ func TestTieredCacheCacheStats(t *testing.T) {
|
||||
t.Errorf("Entries = %d, should be >= 1", stats.Entries)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTieredCacheGetStaleL1Hit(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
cfg := &TieredCacheConfig{
|
||||
L1MaxEntries: 100,
|
||||
L1MaxSize: 1024 * 1024,
|
||||
L2Config: &DiskCacheConfig{
|
||||
Path: tmpDir,
|
||||
Levels: "1:2",
|
||||
},
|
||||
StaleIfError: 200 * time.Millisecond,
|
||||
StaleIfTimeout: 0,
|
||||
}
|
||||
|
||||
tc, err := NewTieredCache(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("NewTieredCache failed: %v", err)
|
||||
}
|
||||
defer tc.Stop()
|
||||
|
||||
hashKey := uint64(54321)
|
||||
origKey := "GET:/api/tiered"
|
||||
tc.Set(hashKey, origKey, []byte("l1data"), nil, 200, 100*time.Millisecond)
|
||||
|
||||
// 等待过期但仍在 stale_if_error 窗口内
|
||||
time.Sleep(150 * time.Millisecond)
|
||||
|
||||
// isTimeout=false,应该从 L1 获取 stale 缓存
|
||||
entry, ok := tc.GetStale(hashKey, origKey, false)
|
||||
if !ok {
|
||||
t.Error("stale entry should be usable on error from L1")
|
||||
}
|
||||
if entry == nil || string(entry.Data) != "l1data" {
|
||||
t.Errorf("entry.Data = %v, want %q", entry, "l1data")
|
||||
}
|
||||
|
||||
// isTimeout=true,staleIfTimeout=0,不应该可用
|
||||
if _, ok2 := tc.GetStale(hashKey, origKey, true); ok2 {
|
||||
t.Error("stale entry should NOT be usable on timeout when staleIfTimeout=0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTieredCacheGetStaleMiss(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
cfg := &TieredCacheConfig{
|
||||
L1MaxEntries: 100,
|
||||
L1MaxSize: 1024 * 1024,
|
||||
L2Config: &DiskCacheConfig{
|
||||
Path: tmpDir,
|
||||
Levels: "1:2",
|
||||
},
|
||||
StaleIfError: 200 * time.Millisecond,
|
||||
StaleIfTimeout: 200 * time.Millisecond,
|
||||
}
|
||||
|
||||
tc, err := NewTieredCache(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("NewTieredCache failed: %v", err)
|
||||
}
|
||||
defer tc.Stop()
|
||||
|
||||
// 不存在的 key
|
||||
if _, ok := tc.GetStale(99999, "nonexistent", false); ok {
|
||||
t.Error("should not find nonexistent key")
|
||||
}
|
||||
|
||||
if _, ok2 := tc.GetStale(99999, "nonexistent", true); ok2 {
|
||||
t.Error("should not find nonexistent key on timeout")
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user