96 lines
2.2 KiB
Go
96 lines
2.2 KiB
Go
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)
|
|
}
|
|
}
|