github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/framework/cache/redis/redis_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 package redis 20 21 import ( 22 "context" 23 "errors" 24 "fmt" 25 "os" 26 "testing" 27 "time" 28 29 "github.com/gomodule/redigo/redis" 30 "github.com/stretchr/testify/assert" 31 32 "github.com/mdaxf/iac/framework/berror" 33 "github.com/mdaxf/iac/framework/cache" 34 ) 35 36 func TestRedisCache(t *testing.T) { 37 redisAddr := os.Getenv("REDIS_ADDR") 38 if redisAddr == "" { 39 redisAddr = "127.0.0.1:6379" 40 } 41 42 bm, err := cache.NewCache("redis", fmt.Sprintf(`{"conn": "%s"}`, redisAddr)) 43 assert.Nil(t, err) 44 timeoutDuration := 3 * time.Second 45 46 assert.Nil(t, bm.Put(context.Background(), "astaxie", 1, timeoutDuration)) 47 48 res, _ := bm.IsExist(context.Background(), "astaxie") 49 assert.True(t, res) 50 51 time.Sleep(5 * time.Second) 52 53 res, _ = bm.IsExist(context.Background(), "astaxie") 54 assert.False(t, res) 55 56 assert.Nil(t, bm.Put(context.Background(), "astaxie", 1, timeoutDuration)) 57 58 val, _ := bm.Get(context.Background(), "astaxie") 59 v, _ := redis.Int(val, err) 60 assert.Equal(t, 1, v) 61 62 assert.Nil(t, bm.Incr(context.Background(), "astaxie")) 63 val, _ = bm.Get(context.Background(), "astaxie") 64 v, _ = redis.Int(val, err) 65 assert.Equal(t, 2, v) 66 67 assert.Nil(t, bm.Decr(context.Background(), "astaxie")) 68 69 val, _ = bm.Get(context.Background(), "astaxie") 70 v, _ = redis.Int(val, err) 71 assert.Equal(t, 1, v) 72 assert.Nil(t, bm.Delete(context.Background(), "astaxie")) 73 74 res, _ = bm.IsExist(context.Background(), "astaxie") 75 assert.False(t, res) 76 77 assert.Nil(t, bm.Put(context.Background(), "astaxie", "author", timeoutDuration)) 78 // test string 79 80 res, _ = bm.IsExist(context.Background(), "astaxie") 81 assert.True(t, res) 82 83 val, _ = bm.Get(context.Background(), "astaxie") 84 vs, _ := redis.String(val, err) 85 assert.Equal(t, "author", vs) 86 87 // test GetMulti 88 assert.Nil(t, bm.Put(context.Background(), "astaxie1", "author1", timeoutDuration)) 89 90 res, _ = bm.IsExist(context.Background(), "astaxie1") 91 assert.True(t, res) 92 93 vv, _ := bm.GetMulti(context.Background(), []string{"astaxie", "astaxie1"}) 94 assert.Equal(t, 2, len(vv)) 95 vs, _ = redis.String(vv[0], nil) 96 assert.Equal(t, "author", vs) 97 98 vs, _ = redis.String(vv[1], nil) 99 assert.Equal(t, "author1", vs) 100 101 vv, _ = bm.GetMulti(context.Background(), []string{"astaxie0", "astaxie1"}) 102 assert.Nil(t, vv[0]) 103 104 vs, _ = redis.String(vv[1], nil) 105 assert.Equal(t, "author1", vs) 106 107 // test clear all 108 assert.Nil(t, bm.ClearAll(context.Background())) 109 } 110 111 func TestCacheScan(t *testing.T) { 112 timeoutDuration := 10 * time.Second 113 114 addr := os.Getenv("REDIS_ADDR") 115 if addr == "" { 116 addr = "127.0.0.1:6379" 117 } 118 119 // init 120 bm, err := cache.NewCache("redis", fmt.Sprintf(`{"conn": "%s"}`, addr)) 121 122 assert.Nil(t, err) 123 // insert all 124 for i := 0; i < 100; i++ { 125 assert.Nil(t, bm.Put(context.Background(), fmt.Sprintf("astaxie%d", i), fmt.Sprintf("author%d", i), timeoutDuration)) 126 } 127 time.Sleep(time.Second) 128 // scan all for the first time 129 keys, err := bm.(*Cache).Scan(DefaultKey + ":*") 130 assert.Nil(t, err) 131 132 assert.Equal(t, 100, len(keys), "scan all error") 133 134 // clear all 135 assert.Nil(t, bm.ClearAll(context.Background())) 136 137 // scan all for the second time 138 keys, err = bm.(*Cache).Scan(DefaultKey + ":*") 139 assert.Nil(t, err) 140 assert.Equal(t, 0, len(keys)) 141 } 142 143 func TestReadThroughCache_redis_Get(t *testing.T) { 144 bm, err := cache.NewCache("redis", fmt.Sprintf(`{"conn": "%s"}`, "127.0.0.1:6379")) 145 assert.Nil(t, err) 146 147 testReadThroughCacheGet(t, bm) 148 } 149 150 func testReadThroughCacheGet(t *testing.T, bm cache.Cache) { 151 testCases := []struct { 152 name string 153 key string 154 value string 155 cache cache.Cache 156 wantErr error 157 }{ 158 { 159 name: "Get load err", 160 key: "key0", 161 cache: func() cache.Cache { 162 kvs := map[string]any{"key0": "value0"} 163 db := &MockOrm{kvs: kvs} 164 loadfunc := func(ctx context.Context, key string) (any, error) { 165 v, er := db.Load(key) 166 if er != nil { 167 return nil, er 168 } 169 val := []byte(v.(string)) 170 return val, nil 171 } 172 c, err := cache.NewReadThroughCache(bm, 3*time.Second, loadfunc) 173 assert.Nil(t, err) 174 return c 175 }(), 176 wantErr: func() error { 177 err := errors.New("the key not exist") 178 return berror.Wrap( 179 err, cache.LoadFuncFailed, "cache unable to load data") 180 }(), 181 }, 182 { 183 name: "Get cache exist", 184 key: "key1", 185 value: "value1", 186 cache: func() cache.Cache { 187 keysMap := map[string]int{"key1": 1} 188 kvs := map[string]any{"key1": "value1"} 189 db := &MockOrm{keysMap: keysMap, kvs: kvs} 190 loadfunc := func(ctx context.Context, key string) (any, error) { 191 v, er := db.Load(key) 192 if er != nil { 193 return nil, er 194 } 195 val := []byte(v.(string)) 196 return val, nil 197 } 198 c, err := cache.NewReadThroughCache(bm, 3*time.Second, loadfunc) 199 assert.Nil(t, err) 200 err = c.Put(context.Background(), "key1", "value1", 3*time.Second) 201 assert.Nil(t, err) 202 return c 203 }(), 204 }, 205 { 206 name: "Get loadFunc exist", 207 key: "key2", 208 value: "value2", 209 cache: func() cache.Cache { 210 keysMap := map[string]int{"key2": 1} 211 kvs := map[string]any{"key2": "value2"} 212 db := &MockOrm{keysMap: keysMap, kvs: kvs} 213 loadfunc := func(ctx context.Context, key string) (any, error) { 214 v, er := db.Load(key) 215 if er != nil { 216 return nil, er 217 } 218 val := []byte(v.(string)) 219 return val, nil 220 } 221 c, err := cache.NewReadThroughCache(bm, 3*time.Second, loadfunc) 222 assert.Nil(t, err) 223 return c 224 }(), 225 }, 226 } 227 _, err := cache.NewReadThroughCache(bm, 3*time.Second, nil) 228 assert.Equal(t, berror.Error(cache.InvalidLoadFunc, "loadFunc cannot be nil"), err) 229 for _, tc := range testCases { 230 t.Run(tc.name, func(t *testing.T) { 231 bs := []byte(tc.value) 232 c := tc.cache 233 val, err := c.Get(context.Background(), tc.key) 234 if err != nil { 235 assert.EqualError(t, tc.wantErr, err.Error()) 236 return 237 } 238 assert.Equal(t, bs, val) 239 }) 240 } 241 } 242 243 type MockOrm struct { 244 keysMap map[string]int 245 kvs map[string]any 246 } 247 248 func (m *MockOrm) Load(key string) (any, error) { 249 _, ok := m.keysMap[key] 250 if !ok { 251 return nil, errors.New("the key not exist") 252 } 253 return m.kvs[key], nil 254 } 255 256 func TestCache_associate(t *testing.T) { 257 testCases := []struct { 258 name string 259 skipEmptyPrefix bool 260 prefix string 261 input string 262 wantRes string 263 }{ 264 { 265 name: "skip prefix", 266 skipEmptyPrefix: true, 267 prefix: "", 268 input: "my-key", 269 wantRes: "my-key", 270 }, 271 { 272 name: "skip prefix but prefix not empty", 273 skipEmptyPrefix: true, 274 prefix: "abc", 275 input: "my-key", 276 wantRes: "abc:my-key", 277 }, 278 { 279 name: "using empty prefix", 280 skipEmptyPrefix: false, 281 prefix: "", 282 input: "my-key", 283 wantRes: ":my-key", 284 }, 285 { 286 name: "using prefix", 287 prefix: "abc", 288 input: "my-key", 289 wantRes: "abc:my-key", 290 }, 291 } 292 293 for _, tc := range testCases { 294 t.Run(tc.name, func(t *testing.T) { 295 c := NewRedisCache().(*Cache) 296 c.skipEmptyPrefix = tc.skipEmptyPrefix 297 c.key = tc.prefix 298 res := c.associate(tc.input) 299 assert.Equal(t, tc.wantRes, res) 300 }) 301 } 302 } 303 304 func TestCache_parseConf(t *testing.T) { 305 tests := []struct { 306 name string 307 308 configStr string 309 310 wantCache Cache 311 wantErr error 312 }{ 313 { 314 name: "just conn", 315 configStr: `{ 316 "conn": "127.0.0.1:6379" 317 }`, 318 319 wantCache: Cache{ 320 conninfo: "127.0.0.1:6379", 321 dbNum: 0, 322 key: DefaultKey, 323 password: "", 324 maxIdle: defaultMaxIdle, 325 skipEmptyPrefix: false, 326 timeout: defaultTimeout, 327 }, 328 wantErr: nil, 329 }, 330 331 { 332 name: "all", 333 configStr: `{ 334 "dbNum": "2", 335 "skipEmptyPrefix": "true", 336 "key": "mykey", 337 "conn": "redis://mypwd@127.0.0.1:6379", 338 "maxIdle": "10", 339 "timeout": "30s" 340 }`, 341 342 wantCache: Cache{ 343 conninfo: "127.0.0.1:6379", 344 dbNum: 2, 345 key: "mykey", 346 password: "mypwd", 347 maxIdle: 10, 348 skipEmptyPrefix: true, 349 timeout: time.Second * 30, 350 }, 351 wantErr: nil, 352 }, 353 } 354 for _, tt := range tests { 355 t.Run(tt.name, func(t *testing.T) { 356 c := Cache{} 357 err := c.parseConf(tt.configStr) 358 assert.Equal(t, tt.wantErr, err) 359 if err != nil { 360 return 361 } 362 assert.Equal(t, tt.wantCache, c) 363 }) 364 } 365 }