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  }