package cache import ( "errors" "sync" "sync/atomic" "testing" "time" ) func TestGetSetExpiry(t *testing.T) { c := New(10) if _, ok := c.Get("k"); ok { t.Fatal("空缓存不应命中") } c.Set("k", "v", 50*time.Millisecond) if v, ok := c.Get("k"); !ok || v.(string) != "v" { t.Fatalf("Get = %v %v, want v true", v, ok) } // jitter ≤ TTL/10,60ms 覆盖 50+5ms 上限 time.Sleep(60 * time.Millisecond) if _, ok := c.Get("k"); ok { t.Error("过期后不应命中") } } func TestDeletePrefix(t *testing.T) { c := New(10) c.Set("t1|a", 1, time.Minute) c.Set("t1|b", 2, time.Minute) c.Set("t2|a", 3, time.Minute) c.DeletePrefix("t1|") if _, ok := c.Get("t1|a"); ok { t.Error("t1|a 应被前缀删除") } if _, ok := c.Get("t2|a"); !ok { t.Error("t2|a 不应被删除") } } func TestEvictAtMax(t *testing.T) { c := New(2) c.Set("a", 1, time.Minute) c.Set("b", 2, time.Minute) c.Set("c", 3, time.Minute) if n := len(c.items); n > 2 { t.Errorf("键数 = %d, 应不超过上限 2", n) } if _, ok := c.Get("c"); !ok { t.Error("最新写入的键应存在") } } func TestDoSingleflight(t *testing.T) { c := New(10) var calls atomic.Int32 var wg sync.WaitGroup for i := 0; i < 8; i++ { wg.Add(1) go func() { defer wg.Done() v, err := Do(c, "k", time.Minute, func() (int, error) { calls.Add(1) time.Sleep(30 * time.Millisecond) return 42, nil }) if err != nil || v != 42 { t.Errorf("Do = %d, %v", v, err) } }() } wg.Wait() if n := calls.Load(); n != 1 { t.Errorf("并发 miss 回源 %d 次, want 1", n) } // 命中缓存不再回源 if _, err := Do(c, "k", time.Minute, func() (int, error) { calls.Add(1); return 0, nil }); err != nil { t.Fatal(err) } if n := calls.Load(); n != 1 { t.Errorf("命中后回源 %d 次, want 1", n) } } func TestDoErrorNotCached(t *testing.T) { c := New(10) boom := errors.New("boom") if _, err := Do(c, "k", time.Minute, func() (int, error) { return 0, boom }); !errors.Is(err, boom) { t.Fatalf("err = %v, want boom", err) } v, err := Do(c, "k", time.Minute, func() (int, error) { return 7, nil }) if err != nil || v != 7 { t.Errorf("错误不应入缓存,重试 = %d, %v", v, err) } }