github.com/jxskiss/gopkg/v2@v2.14.9-0.20240514120614-899f3e7952b4/infra/acache/cache_test.go (about) 1 package acache 2 3 import ( 4 "errors" 5 "runtime" 6 "sync/atomic" 7 "testing" 8 "time" 9 10 "github.com/stretchr/testify/assert" 11 ) 12 13 func TestGet(t *testing.T) { 14 var key = "key" 15 var _val atomic.Value 16 _val.Store("val") 17 18 val := func() string { 19 return _val.Load().(string) 20 } 21 opt := Options{ 22 RefreshInterval: 50 * time.Millisecond, 23 Fetcher: FuncFetcher(func(key string) (any, error) { 24 return val(), nil 25 }), 26 } 27 c := newCacheWithTickInterval(opt, 10*time.Millisecond) 28 defer c.Close() 29 assert.False(t, c.Contains(key)) 30 31 got, err := c.Get(key) 32 assert.Nil(t, err) 33 assert.Equal(t, val(), got) 34 assert.True(t, c.Contains(key)) 35 36 time.Sleep(opt.RefreshInterval / 2) 37 _val.Store("newVal") 38 39 got, err = c.Get(key) 40 assert.Nil(t, err) 41 assert.NotEqual(t, val(), got) 42 assert.True(t, c.Contains(key)) 43 44 time.Sleep(opt.RefreshInterval + time.Second) 45 got, err = c.Get(key) 46 assert.Nil(t, err) 47 assert.Equal(t, val(), got) 48 } 49 50 func TestGetError(t *testing.T) { 51 var key, val = "key", "val" 52 var first = true 53 opt := Options{ 54 RefreshInterval: 50 * time.Millisecond, 55 Fetcher: FuncFetcher(func(key string) (any, error) { 56 if first { 57 first = false 58 return nil, errors.New("error") 59 } 60 return val, nil 61 }), 62 } 63 c := newCacheWithTickInterval(opt, 10*time.Millisecond) 64 defer c.Close() 65 66 got, err := c.Get(key) 67 assert.NotNil(t, err) 68 assert.Nil(t, got) 69 70 time.Sleep(opt.RefreshInterval / 2) 71 _, err2 := c.Get(key) 72 assert.Equal(t, err, err2) 73 74 time.Sleep(opt.RefreshInterval + 10*time.Millisecond) 75 got, err = c.Get(key) 76 assert.Nil(t, err) 77 assert.Equal(t, val, got) 78 } 79 80 func TestGetOrDefault(t *testing.T) { 81 var key, val, defaultVal = "key", "val", "default" 82 opt := Options{ 83 RefreshInterval: 50 * time.Millisecond, 84 Fetcher: FuncFetcher(func(key string) (any, error) { 85 return val, nil 86 }), 87 } 88 c := newCacheWithTickInterval(opt, 10*time.Millisecond) 89 defer c.Close() 90 91 got := c.GetOrDefault(key, defaultVal) 92 assert.Equal(t, val, got) 93 94 time.Sleep(opt.RefreshInterval / 2) 95 val = "newVal" 96 got = c.GetOrDefault(key, defaultVal) 97 assert.NotEqual(t, val, got) 98 99 time.Sleep(opt.RefreshInterval) 100 got = c.GetOrDefault(key, defaultVal) 101 assert.Equal(t, val, got) 102 } 103 104 func TestGetOrDefaultError(t *testing.T) { 105 var key, val, defaultVal1, defaultVal2 = "key", "val", "default1", "default2" 106 var first = true 107 opt := Options{ 108 RefreshInterval: 50 * time.Millisecond, 109 Fetcher: FuncFetcher(func(key string) (any, error) { 110 if first { 111 first = false 112 return nil, errors.New("error") 113 } 114 return val, nil 115 }), 116 } 117 c := newCacheWithTickInterval(opt, 10*time.Millisecond) 118 defer c.Close() 119 120 // First loading, error happens, should get defaultVal1. 121 got := c.GetOrDefault(key, defaultVal1) 122 assert.Equal(t, defaultVal1, got) 123 124 // The second loading has not been triggered, should get defaultVal2. 125 time.Sleep(opt.RefreshInterval / 2) 126 got = c.GetOrDefault(key, defaultVal2) 127 assert.Equal(t, defaultVal2, got) 128 129 // RefreshInterval has been passed, the second loading has been triggered, 130 // we should get "val" from the loader function. 131 time.Sleep(opt.RefreshInterval) 132 runtime.Gosched() 133 got = c.GetOrDefault(key, defaultVal1) 134 assert.Equal(t, val, got) 135 } 136 137 func TestSetDefault(t *testing.T) { 138 opt := Options{ 139 RefreshInterval: 50 * time.Millisecond, 140 Fetcher: FuncFetcher(func(key string) (any, error) { 141 return nil, errors.New("error") 142 }), 143 } 144 c := newCacheWithTickInterval(opt, 10*time.Millisecond) 145 defer c.Close() 146 147 got := c.GetOrDefault("key1", "default1") 148 assert.Equal(t, "default1", got) 149 150 exist := c.SetDefault("key2", "val2") 151 assert.False(t, exist) 152 got = c.GetOrDefault("key2", "default2") 153 assert.Equal(t, "default2", got) 154 155 // Only the first call of `SetDefault` take effect. 156 exist = c.SetDefault("key2", "val3") 157 assert.True(t, exist) 158 got, err := c.Get("key2") 159 assert.Nil(t, err) 160 assert.Equal(t, "val2", got) 161 got = c.GetOrDefault("key2", "default2") 162 assert.Equal(t, "default2", got) 163 } 164 165 func TestUpdate(t *testing.T) { 166 opt := Options{ 167 Fetcher: FuncFetcher(func(key string) (any, error) { 168 if key == "testError" { 169 return nil, errors.New("test error") 170 } 171 return "val", nil 172 }), 173 } 174 c := newCacheWithTickInterval(opt, 10*time.Millisecond) 175 defer c.Close() 176 177 got1, err1 := c.Get("key1") 178 assert.Nil(t, err1) 179 assert.Equal(t, "val", got1) 180 181 got2 := c.GetOrDefault("key2", "defaultVal") 182 assert.Equal(t, "val", got2) 183 184 c.SetDefault("key3", "defaultVal") 185 got3, err3 := c.Get("key3") 186 assert.Nil(t, err3) 187 assert.Equal(t, "defaultVal", got3) 188 189 got4 := c.GetOrDefault("testError", "defaultVal") 190 assert.Equal(t, "defaultVal", got4) 191 192 c.Update("key1", "updateVal") 193 c.Update("key2", "updateVal") 194 c.Update("key3", "updateVal") 195 c.Update("testError", "updateVal") 196 c.Update("key4", "updateVal") 197 198 _get := func(k string) any { 199 ret, _ := c.Get(k) 200 return ret 201 } 202 assert.Equal(t, "updateVal", _get("key1")) 203 assert.Equal(t, "updateVal", _get("key2")) 204 assert.Equal(t, "updateVal", _get("key3")) 205 assert.Equal(t, "updateVal", _get("testError")) 206 assert.Equal(t, "updateVal", _get("key4")) 207 } 208 209 func TestDeleteFunc(t *testing.T) { 210 opt := Options{ 211 RefreshInterval: 50 * time.Millisecond, 212 Fetcher: FuncFetcher(func(key string) (any, error) { 213 return nil, errors.New("error") 214 }), 215 } 216 c := newCacheWithTickInterval(opt, 10*time.Millisecond) 217 defer c.Close() 218 219 c.SetDefault("key", "val") 220 got := c.GetOrDefault("key", "default") 221 assert.Equal(t, "default", got) 222 223 c.DeleteFunc(func(string) bool { return true }) 224 225 got = c.GetOrDefault("key", "default") 226 assert.Equal(t, "default", got) 227 } 228 229 func TestClose(t *testing.T) { 230 var sleep = 200 * time.Millisecond 231 var count int64 232 opt := Options{ 233 RefreshInterval: sleep - 10*time.Millisecond, 234 Fetcher: FuncFetcher(func(key string) (any, error) { 235 x := atomic.AddInt64(&count, 1) 236 return int(x), nil 237 }), 238 } 239 c := newCacheWithTickInterval(opt, 10*time.Millisecond) 240 defer c.Close() 241 242 got := c.GetOrDefault("key", 10) 243 assert.Equal(t, 1, got) 244 245 time.Sleep(sleep) 246 got = c.GetOrDefault("key", 10) 247 assert.True(t, got == 1 || got == 2) 248 249 time.Sleep(sleep) 250 got = c.GetOrDefault("key", 10) 251 assert.True(t, got == 2 || got == 3) 252 253 c.Close() 254 255 time.Sleep(5 * sleep) 256 got = c.GetOrDefault("key", 10) 257 assert.True(t, got == 3 || got == 4) 258 } 259 260 func TestExpire(t *testing.T) { 261 // trigger is used to mark whether fetch is called 262 trigger := false 263 opt := Options{ 264 ExpireInterval: 3 * time.Minute, 265 RefreshInterval: time.Minute, 266 Fetcher: FuncFetcher(func(key string) (any, error) { 267 trigger = true 268 return "", nil 269 }), 270 } 271 c := NewCache(opt) 272 defer c.Close() 273 274 // GetOrDefault cannot trigger fetch after SetDefault 275 c.SetDefault("default", "") 276 c.SetDefault("alive", "") 277 c.GetOrDefault("alive", "") 278 assert.False(t, trigger) 279 280 c.Get("expire") 281 assert.True(t, trigger) 282 283 // first expire will mark entries as inactive 284 c.doExpire(true) 285 286 trigger = false 287 c.Get("alive") 288 assert.False(t, trigger) 289 290 // second expire, both default & expire will be removed 291 c.doExpire(true) 292 293 // make sure refresh does not affect expire 294 c.doRefresh() 295 296 trigger = false 297 c.Get("alive") 298 assert.False(t, trigger) 299 300 trigger = false 301 c.Get("default") 302 assert.True(t, trigger) 303 304 trigger = false 305 c.Get("expire") 306 assert.True(t, trigger) 307 }