github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/framework/cache/write_delete_test.go (about) 1 // The package is migrated from beego, you can get from following link: 2 // import( 3 // "github.com/beego/beego/v2/client/cache" 4 // ) 5 // Copyright 2023. All Rights Reserved. 6 // 7 // Licensed under the Apache License, Version 2.0 (the "License"); 8 // you may not use this file except in compliance with the License. 9 // You may obtain a copy of the License at 10 // 11 // http://www.apache.org/licenses/LICENSE-2.0 12 // 13 // Unless required by applicable law or agreed to in writing, software 14 // distributed under the License is distributed on an "AS IS" BASIS, 15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 // See the License for the specific language governing permissions and 17 // limitations under the License. 18 19 // nolint 20 package cache 21 22 import ( 23 "context" 24 "errors" 25 "fmt" 26 "testing" 27 "time" 28 29 "github.com/stretchr/testify/require" 30 31 "github.com/stretchr/testify/assert" 32 33 "github.com/mdaxf/iac/framework/berror" 34 ) 35 36 func TestWriteDoubleDeleteCache_Set(t *testing.T) { 37 mockDbStore := make(map[string]any) 38 39 cancels := make([]func(), 0) 40 defer func() { 41 for _, cancel := range cancels { 42 cancel() 43 } 44 }() 45 timeout := time.Second * 3 46 testCases := []struct { 47 name string 48 cache Cache 49 storeFunc func(ctx context.Context, key string, val any) error 50 ctx context.Context 51 interval time.Duration 52 sleepSecond time.Duration 53 key string 54 value any 55 wantErr error 56 }{ 57 { 58 name: "store key/value in db fail", 59 interval: time.Second, 60 cache: NewMemoryCache(), 61 storeFunc: func(ctx context.Context, key string, val any) error { 62 return errors.New("failed") 63 }, 64 ctx: context.TODO(), 65 wantErr: berror.Wrap(errors.New("failed"), PersistCacheFailed, 66 fmt.Sprintf("key: %s, val: %v", "", nil)), 67 }, 68 { 69 name: "store key/value success", 70 interval: time.Second * 2, 71 sleepSecond: time.Second * 3, 72 cache: func() Cache { 73 cache := NewMemoryCache() 74 err := cache.Put(context.Background(), "hello", "world", time.Second*2) 75 require.NoError(t, err) 76 return cache 77 }(), 78 storeFunc: func(ctx context.Context, key string, val any) error { 79 mockDbStore[key] = val 80 return nil 81 }, 82 ctx: context.TODO(), 83 key: "hello", 84 value: "world", 85 }, 86 { 87 name: "store key/value timeout", 88 interval: time.Second * 2, 89 sleepSecond: time.Second * 3, 90 cache: func() Cache { 91 cache := NewMemoryCache() 92 err := cache.Put(context.Background(), "hello", "hello", time.Second*2) 93 require.NoError(t, err) 94 return cache 95 }(), 96 storeFunc: func(ctx context.Context, key string, val any) error { 97 select { 98 case <-ctx.Done(): 99 return ctx.Err() 100 case <-time.After(3 * time.Second): 101 mockDbStore[key] = val 102 return nil 103 } 104 }, 105 ctx: func() context.Context { 106 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 107 cancels = append(cancels, cancel) 108 return ctx 109 110 }(), 111 key: "hello", 112 value: "hello", 113 }, 114 } 115 for _, tt := range testCases { 116 t.Run(tt.name, func(t *testing.T) { 117 cache := tt.cache 118 c, err := NewWriteDoubleDeleteCache(cache, tt.interval, timeout, tt.storeFunc) 119 if err != nil { 120 assert.EqualError(t, tt.wantErr, err.Error()) 121 return 122 } 123 124 err = c.Set(tt.ctx, tt.key, tt.value) 125 if err != nil { 126 assert.EqualError(t, tt.wantErr, err.Error()) 127 return 128 } 129 130 _, err = c.Get(tt.ctx, tt.key) 131 assert.Equal(t, ErrKeyNotExist, err) 132 133 err = cache.Put(tt.ctx, tt.key, tt.value, tt.interval) 134 require.NoError(t, err) 135 136 val, err := c.Get(tt.ctx, tt.key) 137 require.NoError(t, err) 138 assert.Equal(t, tt.value, val) 139 140 time.Sleep(tt.sleepSecond) 141 142 _, err = c.Get(tt.ctx, tt.key) 143 assert.Equal(t, ErrKeyNotExist, err) 144 }) 145 } 146 } 147 148 func TestNewWriteDoubleDeleteCache(t *testing.T) { 149 underlyingCache := NewMemoryCache() 150 storeFunc := func(ctx context.Context, key string, val any) error { return nil } 151 152 type args struct { 153 cache Cache 154 interval time.Duration 155 fn func(ctx context.Context, key string, val any) error 156 } 157 timeout := time.Second * 3 158 tests := []struct { 159 name string 160 args args 161 wantRes *WriteDoubleDeleteCache 162 wantErr error 163 }{ 164 { 165 name: "nil cache parameters", 166 args: args{ 167 cache: nil, 168 fn: storeFunc, 169 }, 170 wantErr: berror.Error(InvalidInitParameters, "cache or storeFunc can not be nil"), 171 }, 172 { 173 name: "nil storeFunc parameters", 174 args: args{ 175 cache: underlyingCache, 176 fn: nil, 177 }, 178 wantErr: berror.Error(InvalidInitParameters, "cache or storeFunc can not be nil"), 179 }, 180 { 181 name: "init write-though cache success", 182 args: args{ 183 cache: underlyingCache, 184 fn: storeFunc, 185 interval: time.Second, 186 }, 187 wantRes: &WriteDoubleDeleteCache{ 188 Cache: underlyingCache, 189 storeFunc: storeFunc, 190 interval: time.Second, 191 }, 192 }, 193 } 194 for _, tt := range tests { 195 t.Run(tt.name, func(t *testing.T) { 196 _, err := NewWriteDoubleDeleteCache(tt.args.cache, tt.args.interval, timeout, tt.args.fn) 197 assert.Equal(t, tt.wantErr, err) 198 if err != nil { 199 return 200 } 201 }) 202 } 203 } 204 205 func ExampleWriteDoubleDeleteCache() { 206 c := NewMemoryCache() 207 wtc, err := NewWriteDoubleDeleteCache(c, 1*time.Second, 3*time.Second, func(ctx context.Context, key string, val any) error { 208 fmt.Printf("write data to somewhere key %s, val %v \n", key, val) 209 return nil 210 }) 211 if err != nil { 212 panic(err) 213 } 214 err = wtc.Set(context.Background(), 215 "/biz/user/id=1", "I am user 1") 216 if err != nil { 217 panic(err) 218 } 219 // Output: 220 // write data to somewhere key /biz/user/id=1, val I am user 1 221 } 222 223 func TestWriteDeleteCache_Set(t *testing.T) { 224 mockDbStore := make(map[string]any) 225 226 cancels := make([]func(), 0) 227 defer func() { 228 for _, cancel := range cancels { 229 cancel() 230 } 231 }() 232 233 testCases := []struct { 234 name string 235 cache Cache 236 storeFunc func(ctx context.Context, key string, val any) error 237 ctx context.Context 238 key string 239 value any 240 wantErr error 241 before func(Cache) 242 after func() 243 }{ 244 { 245 name: "store key/value in db fail", 246 cache: NewMemoryCache(), 247 storeFunc: func(ctx context.Context, key string, val any) error { 248 return errors.New("failed") 249 }, 250 ctx: context.TODO(), 251 wantErr: berror.Wrap(errors.New("failed"), PersistCacheFailed, 252 fmt.Sprintf("key: %s, val: %v", "", nil)), 253 before: func(cache Cache) {}, 254 after: func() {}, 255 }, 256 { 257 name: "store key/value success", 258 cache: NewMemoryCache(), 259 storeFunc: func(ctx context.Context, key string, val any) error { 260 mockDbStore[key] = val 261 return nil 262 }, 263 ctx: context.TODO(), 264 key: "hello", 265 value: "world", 266 before: func(cache Cache) { 267 _ = cache.Put(context.Background(), "hello", "testVal", 10*time.Second) 268 }, 269 after: func() { 270 delete(mockDbStore, "hello") 271 }, 272 }, 273 { 274 name: "store key/value timeout", 275 cache: NewMemoryCache(), 276 storeFunc: func(ctx context.Context, key string, val any) error { 277 select { 278 case <-ctx.Done(): 279 return ctx.Err() 280 case <-time.After(3 * time.Second): 281 mockDbStore[key] = val 282 return nil 283 } 284 285 }, 286 ctx: func() context.Context { 287 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) 288 cancels = append(cancels, cancel) 289 return ctx 290 291 }(), 292 key: "hello", 293 value: nil, 294 before: func(cache Cache) { 295 _ = cache.Put(context.Background(), "hello", "testVal", 10*time.Second) 296 }, 297 after: func() {}, 298 }, 299 } 300 for _, tt := range testCases { 301 t.Run(tt.name, func(t *testing.T) { 302 w, err := NewWriteDeleteCache(tt.cache, tt.storeFunc) 303 if err != nil { 304 assert.EqualError(t, tt.wantErr, err.Error()) 305 return 306 } 307 308 tt.before(tt.cache) 309 defer func() { 310 tt.after() 311 }() 312 313 err = w.Set(tt.ctx, tt.key, tt.value) 314 if err != nil { 315 assert.EqualError(t, tt.wantErr, err.Error()) 316 return 317 } 318 319 _, err = w.Get(tt.ctx, tt.key) 320 assert.Equal(t, ErrKeyNotExist, err) 321 322 vv := mockDbStore[tt.key] 323 assert.Equal(t, tt.value, vv) 324 }) 325 } 326 } 327 328 func TestNewWriteDeleteCache(t *testing.T) { 329 underlyingCache := NewMemoryCache() 330 storeFunc := func(ctx context.Context, key string, val any) error { return nil } 331 332 type args struct { 333 cache Cache 334 fn func(ctx context.Context, key string, val any) error 335 } 336 tests := []struct { 337 name string 338 args args 339 wantRes *WriteDeleteCache 340 wantErr error 341 }{ 342 { 343 name: "nil cache parameters", 344 args: args{ 345 cache: nil, 346 fn: storeFunc, 347 }, 348 wantErr: berror.Error(InvalidInitParameters, "cache or storeFunc can not be nil"), 349 }, 350 { 351 name: "nil storeFunc parameters", 352 args: args{ 353 cache: underlyingCache, 354 fn: nil, 355 }, 356 wantErr: berror.Error(InvalidInitParameters, "cache or storeFunc can not be nil"), 357 }, 358 { 359 name: "init write-though cache success", 360 args: args{ 361 cache: underlyingCache, 362 fn: storeFunc, 363 }, 364 wantRes: &WriteDeleteCache{ 365 Cache: underlyingCache, 366 storeFunc: storeFunc, 367 }, 368 }, 369 } 370 for _, tt := range tests { 371 t.Run(tt.name, func(t *testing.T) { 372 _, err := NewWriteDeleteCache(tt.args.cache, tt.args.fn) 373 assert.Equal(t, tt.wantErr, err) 374 if err != nil { 375 return 376 } 377 }) 378 } 379 } 380 381 func ExampleNewWriteDeleteCache() { 382 c := NewMemoryCache() 383 wtc, err := NewWriteDeleteCache(c, func(ctx context.Context, key string, val any) error { 384 fmt.Printf("write data to somewhere key %s, val %v \n", key, val) 385 return nil 386 }) 387 if err != nil { 388 panic(err) 389 } 390 err = wtc.Set(context.Background(), 391 "/biz/user/id=1", "I am user 1") 392 if err != nil { 393 panic(err) 394 } 395 // Output: 396 // write data to somewhere key /biz/user/id=1, val I am user 1 397 }