621 lines
17 KiB
Go
621 lines
17 KiB
Go
// Package cache 提供缓存功能的测试。
|
||
//
|
||
// 该文件测试缓存模块的各项功能,包括:
|
||
// - 文件缓存创建和配置
|
||
// - 代理缓存规则和匹配
|
||
// - 缓存设置和获取
|
||
// - 过期和淘汰策略
|
||
// - 路径匹配功能
|
||
//
|
||
// 作者:xfy
|
||
package cache
|
||
|
||
import (
|
||
"hash/fnv"
|
||
"testing"
|
||
"time"
|
||
)
|
||
|
||
// hashKey 计算字符串的 FNV-64a 哈希值,用于测试。
|
||
func hashKey(s string) uint64 {
|
||
h := fnv.New64a()
|
||
h.Write([]byte(s))
|
||
return h.Sum64()
|
||
}
|
||
|
||
func TestNewFileCache(t *testing.T) {
|
||
fc := NewFileCache(100, 1024*1024, 30*time.Second)
|
||
if fc == nil {
|
||
t.Error("Expected non-nil FileCache")
|
||
}
|
||
}
|
||
|
||
func TestFileCacheSetGet(t *testing.T) {
|
||
fc := NewFileCache(10, 1024, 1*time.Hour)
|
||
|
||
path := "/test/file.txt"
|
||
data := []byte("Hello, World!")
|
||
|
||
err := fc.Set(path, data, int64(len(data)), time.Now(), "text/plain")
|
||
if err != nil {
|
||
t.Errorf("Set() error: %v", err)
|
||
}
|
||
|
||
entry, ok := fc.Get(path)
|
||
if !ok {
|
||
t.Error("Expected to find cached entry")
|
||
}
|
||
if string(entry.Data) != "Hello, World!" {
|
||
t.Errorf("Expected data 'Hello, World!', got %s", entry.Data)
|
||
}
|
||
}
|
||
|
||
func TestFileCacheDelete(t *testing.T) {
|
||
fc := NewFileCache(10, 1024, 1*time.Hour)
|
||
|
||
_ = fc.Set("/test.txt", []byte("data"), 4, time.Now(), "text/plain")
|
||
|
||
fc.Delete("/test.txt")
|
||
|
||
_, ok := fc.Get("/test.txt")
|
||
if ok {
|
||
t.Error("Expected entry to be deleted")
|
||
}
|
||
}
|
||
|
||
func TestFileCacheLRUEviction(t *testing.T) {
|
||
// 最大 3 个条目
|
||
fc := NewFileCache(3, 0, 1*time.Hour)
|
||
|
||
_ = fc.Set("/a", []byte("a"), 1, time.Now(), "text/plain")
|
||
_ = fc.Set("/b", []byte("b"), 1, time.Now(), "text/plain")
|
||
_ = fc.Set("/c", []byte("c"), 1, time.Now(), "text/plain")
|
||
|
||
// 再添加一个,应该淘汰 /a
|
||
_ = fc.Set("/d", []byte("d"), 1, time.Now(), "text/plain")
|
||
|
||
_, ok := fc.Get("/a")
|
||
if ok {
|
||
t.Error("Expected /a to be evicted")
|
||
}
|
||
|
||
// b, c, d 应该还在
|
||
for _, path := range []string{"b", "c", "d"} {
|
||
_, ok := fc.Get("/" + path)
|
||
if !ok {
|
||
t.Errorf("Expected /%s to exist", path)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestAcquireLockWithTimeout(t *testing.T) {
|
||
pc := NewProxyCache(nil, true, 0, 0, 0)
|
||
key := hashKey("timeout-test")
|
||
|
||
// 测试获取锁
|
||
waitCh, timedOut := pc.AcquireLockWithTimeout(key, 100*time.Millisecond)
|
||
if waitCh != nil || timedOut {
|
||
t.Error("Expected to acquire lock immediately")
|
||
}
|
||
|
||
// 测试等待超时
|
||
done := make(chan struct{})
|
||
go func() {
|
||
time.Sleep(200 * time.Millisecond)
|
||
pc.ReleaseLock(key, nil)
|
||
close(done)
|
||
}()
|
||
|
||
_, timedOut = pc.AcquireLockWithTimeout(key, 50*time.Millisecond)
|
||
if !timedOut {
|
||
t.Error("Expected timeout when waiting for lock")
|
||
}
|
||
<-done
|
||
}
|
||
|
||
func TestRefreshTTL(t *testing.T) {
|
||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||
key := hashKey("refresh-test")
|
||
origKey := "refresh-test"
|
||
|
||
pc.Set(key, origKey, []byte("data"), nil, 200, 10*time.Minute)
|
||
|
||
newHeaders := map[string]string{
|
||
"Last-Modified": "Wed, 21 Oct 2015 07:28:00 GMT",
|
||
"ETag": "\"abc123\"",
|
||
}
|
||
|
||
ok := pc.RefreshTTL(key, origKey, newHeaders)
|
||
if !ok {
|
||
t.Error("Expected RefreshTTL to succeed")
|
||
}
|
||
|
||
entry, _, _ := pc.Get(key, origKey)
|
||
if entry.LastModified != newHeaders["Last-Modified"] {
|
||
t.Errorf("Expected Last-Modified to be updated")
|
||
}
|
||
if entry.ETag != newHeaders["ETag"] {
|
||
t.Errorf("Expected ETag to be updated")
|
||
}
|
||
}
|
||
|
||
func TestSetValidationHeaders(t *testing.T) {
|
||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||
key := hashKey("validation-test")
|
||
origKey := "validation-test"
|
||
|
||
pc.Set(key, origKey, []byte("data"), nil, 200, 10*time.Minute)
|
||
|
||
ok := pc.SetValidationHeaders(key, origKey, "Mon, 01 Jan 2024 00:00:00 GMT", "\"xyz789\"")
|
||
if !ok {
|
||
t.Error("Expected SetValidationHeaders to succeed")
|
||
}
|
||
|
||
entry, _, _ := pc.Get(key, origKey)
|
||
if entry.LastModified != "Mon, 01 Jan 2024 00:00:00 GMT" {
|
||
t.Errorf("Expected LastModified to be set")
|
||
}
|
||
if entry.ETag != "\"xyz789\"" {
|
||
t.Errorf("Expected ETag to be set")
|
||
}
|
||
}
|
||
|
||
func TestMatchRulePathVariants(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
rulePath string
|
||
reqPath string
|
||
want bool
|
||
}{
|
||
{"prefix_match", "/api/", "/api/users", true},
|
||
{"prefix_no_match", "/api/", "/other", false},
|
||
{"wildcard_match", "/static/*", "/static/css/style.css", true},
|
||
{"wildcard_no_match", "/static/*", "/api/users", false},
|
||
{"exact_match", "/api", "/api", true},
|
||
{"prefix_with_slash", "/api", "/api/users", true},
|
||
{"prefix_with_query", "/api", "/api?query=value", true},
|
||
{"no_match_similar", "/api", "/apiother", false},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
rules := []ProxyCacheRule{
|
||
{Path: tt.rulePath, Methods: []string{"GET"}, MaxAge: time.Minute},
|
||
}
|
||
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)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestMinUsesThreshold(t *testing.T) {
|
||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||
key := hashKey("minuses-test")
|
||
origKey := "minuses-test"
|
||
|
||
// 首次设置
|
||
pc.Set(key, origKey, []byte("data"), nil, 200, 10*time.Minute)
|
||
|
||
// 首次 Get,Uses = 1
|
||
entry, _, _ := pc.Get(key, origKey)
|
||
if entry.Uses.Load() != 1 {
|
||
t.Errorf("Expected Uses=1 after first Get, got %d", entry.Uses.Load())
|
||
}
|
||
|
||
// 第二次 Get,Uses = 2
|
||
pc.Get(key, origKey)
|
||
if entry.Uses.Load() != 2 {
|
||
t.Errorf("Expected Uses=2 after second Get, got %d", entry.Uses.Load())
|
||
}
|
||
}
|
||
|
||
func TestFileCacheSizeEviction(t *testing.T) {
|
||
// 最大 10 字节
|
||
fc := NewFileCache(0, 10, 1*time.Hour)
|
||
|
||
_ = fc.Set("/a", []byte("12345"), 5, time.Now(), "text/plain")
|
||
_ = fc.Set("/b", []byte("12345"), 5, time.Now(), "text/plain")
|
||
|
||
// 再添加 6 字节,应该淘汰一个
|
||
_ = fc.Set("/c", []byte("123456"), 6, time.Now(), "text/plain")
|
||
|
||
stats := fc.Stats()
|
||
if stats.Size > 10 {
|
||
t.Errorf("Expected size <= 10, got %d", stats.Size)
|
||
}
|
||
}
|
||
|
||
func TestFileCacheInactiveEviction(t *testing.T) {
|
||
fc := NewFileCache(10, 1024, 100*time.Millisecond)
|
||
|
||
_ = fc.Set("/test", []byte("data"), 4, time.Now(), "text/plain")
|
||
|
||
// 立即获取应该成功
|
||
_, ok := fc.Get("/test")
|
||
if !ok {
|
||
t.Error("Expected entry to exist")
|
||
}
|
||
|
||
// 等待过期
|
||
time.Sleep(150 * time.Millisecond)
|
||
|
||
// 再次获取应该失败(因过期被删除)
|
||
_, ok = fc.Get("/test")
|
||
if ok {
|
||
t.Error("Expected entry to be expired")
|
||
}
|
||
}
|
||
|
||
func TestFileCacheStats(t *testing.T) {
|
||
fc := NewFileCache(100, 1024, 1*time.Hour)
|
||
|
||
_ = fc.Set("/a", []byte("12345"), 5, time.Now(), "text/plain")
|
||
_ = fc.Set("/b", []byte("12345"), 5, time.Now(), "text/plain")
|
||
|
||
stats := fc.Stats()
|
||
if stats.Entries != 2 {
|
||
t.Errorf("Expected 2 entries, got %d", stats.Entries)
|
||
}
|
||
if stats.Size != 10 {
|
||
t.Errorf("Expected size 10, got %d", stats.Size)
|
||
}
|
||
}
|
||
|
||
func TestNewProxyCache(t *testing.T) {
|
||
rules := []ProxyCacheRule{
|
||
{Path: "/api/", Methods: []string{"GET"}, MaxAge: 10 * time.Minute},
|
||
}
|
||
|
||
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, 0, 0)
|
||
|
||
key := "test-key"
|
||
data := []byte("response body")
|
||
headers := map[string]string{"Content-Type": "application/json"}
|
||
|
||
pc.Set(hashKey(key), key, data, headers, 200, 10*time.Minute)
|
||
|
||
entry, ok, stale := pc.Get(hashKey(key), key)
|
||
if !ok {
|
||
t.Error("Expected to find cached entry")
|
||
}
|
||
if stale {
|
||
t.Error("Expected entry to be fresh")
|
||
}
|
||
if string(entry.Data) != "response body" {
|
||
t.Errorf("Expected data 'response body', got %s", entry.Data)
|
||
}
|
||
if entry.Status != 200 {
|
||
t.Errorf("Expected status 200, got %d", entry.Status)
|
||
}
|
||
}
|
||
|
||
func TestProxyCacheExpiration(t *testing.T) {
|
||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||
|
||
key := "expire-test"
|
||
pc.Set(hashKey(key), key, []byte("data"), nil, 200, 100*time.Millisecond)
|
||
|
||
// 立即获取应该成功
|
||
_, ok, _ := pc.Get(hashKey(key), key)
|
||
if !ok {
|
||
t.Error("Expected entry to exist")
|
||
}
|
||
|
||
// 等待过期
|
||
time.Sleep(150 * time.Millisecond)
|
||
|
||
_, ok, _ = pc.Get(hashKey(key), key)
|
||
if ok {
|
||
t.Error("Expected entry to be expired")
|
||
}
|
||
}
|
||
|
||
func TestProxyCacheStaleWhileRevalidate(t *testing.T) {
|
||
pc := NewProxyCache(nil, false, 200*time.Millisecond, 0, 0)
|
||
|
||
key := "stale-test"
|
||
pc.Set(hashKey(key), key, []byte("data"), nil, 200, 100*time.Millisecond)
|
||
|
||
// 等待过期但仍在 stale 时间内
|
||
time.Sleep(150 * time.Millisecond)
|
||
|
||
entry, ok, stale := pc.Get(hashKey(key), key)
|
||
if !ok {
|
||
t.Error("Expected stale entry to be usable")
|
||
}
|
||
if !stale {
|
||
t.Error("Expected entry to be marked as stale")
|
||
}
|
||
if entry == nil {
|
||
t.Error("Expected stale entry data")
|
||
}
|
||
}
|
||
|
||
func TestProxyCacheMatchRule(t *testing.T) {
|
||
rules := []ProxyCacheRule{
|
||
{Path: "/api/", Methods: []string{"GET"}, Statuses: []int{200}, MaxAge: 10 * time.Minute},
|
||
{Path: "/static/*", Methods: []string{"GET"}, MaxAge: 1 * time.Hour},
|
||
}
|
||
|
||
pc := NewProxyCache(rules, false, 0, 0, 0)
|
||
|
||
tests := []struct {
|
||
path string
|
||
method string
|
||
status int
|
||
want bool
|
||
}{
|
||
{"api/users", "GET", 200, true},
|
||
{"api/users", "POST", 200, false}, // POST 不在 Methods
|
||
{"api/users", "GET", 404, false}, // 404 不在 Statuses
|
||
{"static/css/style.css", "GET", 200, true},
|
||
{"other/path", "GET", 200, false}, // 不匹配任何规则
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.path, func(t *testing.T) {
|
||
// 添加前缀 / 到 path
|
||
fullPath := "/" + tt.path
|
||
rule := pc.MatchRule(fullPath, tt.method, tt.status)
|
||
if (rule != nil) != tt.want {
|
||
t.Errorf("MatchRule(%s, %s, %d) want %v", fullPath, tt.method, tt.status, tt.want)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestProxyCacheDelete(t *testing.T) {
|
||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||
|
||
key := "key1"
|
||
pc.Set(hashKey(key), key, []byte("data"), nil, 200, 10*time.Minute)
|
||
pc.Delete(hashKey(key))
|
||
|
||
_, ok, _ := pc.Get(hashKey(key), key)
|
||
if ok {
|
||
t.Error("Expected entry to be deleted")
|
||
}
|
||
}
|
||
|
||
func TestPathMatch(t *testing.T) {
|
||
tests := []struct {
|
||
pattern string
|
||
path string
|
||
want bool
|
||
}{
|
||
{"*", "/anything", true},
|
||
{"api/*", "/api/users", true},
|
||
{"api/*", "/api/", true},
|
||
{"api/*", "/other", false},
|
||
{"/exact", "/exact", true},
|
||
{"/exact", "/exact/other", false},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.pattern+"_"+tt.path, func(t *testing.T) {
|
||
// 添加前缀 / 如果 pattern 没有
|
||
pattern := tt.pattern
|
||
if pattern[0] != '/' && pattern != "*" {
|
||
pattern = "/" + pattern
|
||
}
|
||
result := MatchPattern(pattern, tt.path)
|
||
if result != tt.want {
|
||
t.Errorf("MatchPattern(%s, %s) = %v, want %v", pattern, tt.path, result, tt.want)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
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")
|
||
}
|
||
}
|
||
|
||
// TestFileCacheRefreshCachedAt 测试 RefreshCachedAt 方法。
|
||
func TestFileCacheRefreshCachedAt(t *testing.T) {
|
||
fc := NewFileCache(10, 1024, 1*time.Hour)
|
||
|
||
path := "/test/refresh.txt"
|
||
data := []byte("test data")
|
||
|
||
// 设置缓存
|
||
_ = fc.Set(path, data, int64(len(data)), time.Now(), "text/plain")
|
||
|
||
// 获取原始 CachedAt 时间
|
||
entry, ok := fc.Get(path)
|
||
if !ok {
|
||
t.Fatal("Expected to find cached entry")
|
||
}
|
||
originalCachedAt := entry.CachedAt
|
||
|
||
// 等待一小段时间
|
||
time.Sleep(10 * time.Millisecond)
|
||
|
||
// 刷新 CachedAt
|
||
fc.RefreshCachedAt(path)
|
||
|
||
// 再次获取,验证 CachedAt 已更新
|
||
entry, ok = fc.Get(path)
|
||
if !ok {
|
||
t.Fatal("Expected to find cached entry after refresh")
|
||
}
|
||
if !entry.CachedAt.After(originalCachedAt) {
|
||
t.Errorf("CachedAt not updated: %v <= %v", entry.CachedAt, originalCachedAt)
|
||
}
|
||
}
|
||
|
||
// TestFileCacheRefreshCachedAtNonExistent 测试刷新不存在的条目。
|
||
func TestFileCacheRefreshCachedAtNonExistent(t *testing.T) {
|
||
fc := NewFileCache(10, 1024, 1*time.Hour)
|
||
|
||
// 刷新不存在的条目应该静默忽略
|
||
fc.RefreshCachedAt("/nonexistent/path")
|
||
|
||
// 验证没有副作用
|
||
stats := fc.Stats()
|
||
if stats.Entries != 0 {
|
||
t.Errorf("Expected 0 entries, got %d", stats.Entries)
|
||
}
|
||
}
|
||
|
||
// TestProxyCacheDeleteByPatternWithMethod 测试按模式和方法的删除。
|
||
func TestProxyCacheDeleteByPatternWithMethod(t *testing.T) {
|
||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||
|
||
// 添加多个缓存条目,带不同方法前缀
|
||
pc.Set(hashKey("GET:/api/users"), "GET:/api/users", []byte("users"), nil, 200, 10*time.Minute)
|
||
pc.Set(hashKey("POST:/api/users"), "POST:/api/users", []byte("create"), nil, 200, 10*time.Minute)
|
||
pc.Set(hashKey("GET:/api/posts"), "GET:/api/posts", []byte("posts"), nil, 200, 10*time.Minute)
|
||
pc.Set(hashKey("DELETE:/api/users/1"), "DELETE:/api/users/1", []byte("delete"), nil, 200, 10*time.Minute)
|
||
|
||
// 删除所有 GET:/api/users* 的条目(模式匹配 OrigKey,包含方法前缀)
|
||
deleted := pc.DeleteByPatternWithMethod("GET:/api/users*", "GET")
|
||
if deleted != 1 {
|
||
t.Errorf("Expected 1 deleted, got %d", deleted)
|
||
}
|
||
|
||
// 验证 GET:/api/users 被删除
|
||
if _, ok, _ := pc.Get(hashKey("GET:/api/users"), "GET:/api/users"); ok {
|
||
t.Error("GET:/api/users should be deleted")
|
||
}
|
||
|
||
// 验证 POST:/api/users 还在
|
||
if _, ok, _ := pc.Get(hashKey("POST:/api/users"), "POST:/api/users"); !ok {
|
||
t.Error("POST:/api/users should still exist")
|
||
}
|
||
|
||
// 验证 GET:/api/posts 还在
|
||
if _, ok, _ := pc.Get(hashKey("GET:/api/posts"), "GET:/api/posts"); !ok {
|
||
t.Error("GET:/api/posts should still exist")
|
||
}
|
||
}
|
||
|
||
// TestProxyCacheDeleteByPatternAllMethods 测试删除所有方法。
|
||
func TestProxyCacheDeleteByPatternAllMethods(t *testing.T) {
|
||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||
|
||
// 添加多个缓存条目
|
||
pc.Set(hashKey("GET:/api/test"), "GET:/api/test", []byte("get"), nil, 200, 10*time.Minute)
|
||
pc.Set(hashKey("POST:/api/test"), "POST:/api/test", []byte("post"), nil, 200, 10*time.Minute)
|
||
pc.Set(hashKey("PUT:/api/test"), "PUT:/api/test", []byte("put"), nil, 200, 10*time.Minute)
|
||
|
||
// 删除所有 *:/api/test* 的条目(不限制方法,使用 * 通配符)
|
||
deleted := pc.DeleteByPatternWithMethod("*", "")
|
||
if deleted != 3 {
|
||
t.Errorf("Expected 3 deleted, got %d", deleted)
|
||
}
|
||
}
|
||
|
||
// TestProxyCacheDeleteByPatternNoMatch 测试无匹配删除。
|
||
func TestProxyCacheDeleteByPatternNoMatch(t *testing.T) {
|
||
pc := NewProxyCache(nil, false, 0, 0, 0)
|
||
|
||
pc.Set(hashKey("GET:/api/users"), "GET:/api/users", []byte("users"), nil, 200, 10*time.Minute)
|
||
|
||
// 删除不匹配的模式
|
||
deleted := pc.DeleteByPatternWithMethod("/other/*", "")
|
||
if deleted != 0 {
|
||
t.Errorf("Expected 0 deleted, got %d", deleted)
|
||
}
|
||
|
||
// 验证原条目还在
|
||
if _, ok, _ := pc.Get(hashKey("GET:/api/users"), "GET:/api/users"); !ok {
|
||
t.Error("Original entry should still exist")
|
||
}
|
||
}
|