github.com/wfusion/gofusion@v1.1.14/test/cache/cases/redis_test.go (about)

     1  package cases
     2  
     3  import (
     4  	"context"
     5  	"math/rand"
     6  	"sync"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/go-faker/faker/v4"
    11  	"github.com/spf13/cast"
    12  	"github.com/stretchr/testify/suite"
    13  
    14  	"github.com/wfusion/gofusion/cache"
    15  	"github.com/wfusion/gofusion/common/utils"
    16  	"github.com/wfusion/gofusion/common/utils/serialize"
    17  	"github.com/wfusion/gofusion/log"
    18  	"github.com/wfusion/gofusion/test/internal/mock"
    19  
    20  	testCache "github.com/wfusion/gofusion/test/cache"
    21  )
    22  
    23  func TestRedis(t *testing.T) {
    24  	testingSuite := &Redis{Test: new(testCache.Test)}
    25  	testingSuite.Init(testingSuite)
    26  	suite.Run(t, testingSuite)
    27  }
    28  
    29  type Redis struct {
    30  	*testCache.Test
    31  }
    32  
    33  func (t *Redis) BeforeTest(suiteName, testName string) {
    34  	t.Catch(func() {
    35  		log.Info(context.Background(), "right before %s %s", suiteName, testName)
    36  	})
    37  }
    38  
    39  func (t *Redis) AfterTest(suiteName, testName string) {
    40  	t.Catch(func() {
    41  		log.Info(context.Background(), "right after %s %s", suiteName, testName)
    42  	})
    43  }
    44  
    45  func (t *Redis) TestRedis() {
    46  	t.Catch(func() {
    47  		// Given
    48  		num := 15
    49  		ctx := context.Background()
    50  		algo := serialize.AlgorithmGob
    51  		instance := cache.New[string, *mock.RandomObj, []*mock.RandomObj](redis, cache.AppName(t.AppName()))
    52  		objList := mock.GenObjListBySerializeAlgo(algo, num).([]*mock.RandomObj)
    53  		stringObjMap := make(map[string]*mock.RandomObj, num)
    54  		for i := 0; i < num; i++ {
    55  			stringObjMap[cast.ToString(i+1)] = objList[i]
    56  		}
    57  		defer instance.Clear(ctx)
    58  
    59  		// When
    60  		instance.Set(ctx, stringObjMap)
    61  
    62  		// Then
    63  		keys := []string{"13"}
    64  		rs := instance.Get(ctx, keys, t.randomObjCallback(stringObjMap, algo, false))
    65  		t.NotEmpty(rs)
    66  
    67  		keys = []string{"1"}
    68  		rs = instance.Get(ctx, keys, t.randomObjCallback(stringObjMap, algo, false))
    69  		t.NotEmpty(rs)
    70  
    71  		keys = []string{"1"}
    72  		rs = instance.Get(ctx, keys, t.randomObjCallback(stringObjMap, algo, false))
    73  		t.NotEmpty(rs)
    74  
    75  		time.Sleep(5 * time.Second)
    76  		keys = []string{"1"}
    77  		rs = instance.Get(ctx, keys, t.randomObjCallback(stringObjMap, algo, true))
    78  		t.NotEmpty(rs)
    79  	})
    80  }
    81  
    82  func (t *Redis) TestClear() {
    83  	t.Catch(func() {
    84  		// Given
    85  		ctx := context.Background()
    86  		algo := serialize.AlgorithmGob
    87  		instance := cache.New[string, *mock.RandomObj, []*mock.RandomObj](redis, cache.AppName(t.AppName()))
    88  		stringObjMap := map[string]*mock.RandomObj{
    89  			"1": mock.GenObjBySerializeAlgo(algo).(*mock.RandomObj),
    90  			"2": mock.GenObjBySerializeAlgo(algo).(*mock.RandomObj),
    91  			"3": mock.GenObjBySerializeAlgo(algo).(*mock.RandomObj),
    92  		}
    93  		defer instance.Clear(ctx)
    94  
    95  		// When
    96  		instance.Set(ctx, stringObjMap)
    97  
    98  		// Then
    99  		keys := []string{"1", "2", "3"}
   100  		rs := instance.Get(ctx, keys, t.randomObjCallback(stringObjMap, algo, false))
   101  		t.NotEmpty(rs)
   102  
   103  		instance.Clear(ctx)
   104  		rs = instance.Get(ctx, keys, t.randomObjCallback(stringObjMap, algo, true))
   105  		t.NotEmpty(rs)
   106  	})
   107  }
   108  
   109  func (t *Redis) TestDel() {
   110  	t.Catch(func() {
   111  		// Given
   112  		ctx := context.Background()
   113  		algo := serialize.AlgorithmGob
   114  		instance := cache.New[string, *mock.RandomObj, []*mock.RandomObj](redis, cache.AppName(t.AppName()))
   115  		stringObjMap := map[string]*mock.RandomObj{
   116  			"1": mock.GenObjBySerializeAlgo(algo).(*mock.RandomObj),
   117  			"2": mock.GenObjBySerializeAlgo(algo).(*mock.RandomObj),
   118  			"3": mock.GenObjBySerializeAlgo(algo).(*mock.RandomObj),
   119  		}
   120  		defer instance.Clear(ctx)
   121  
   122  		// When
   123  		instance.Set(ctx, stringObjMap)
   124  
   125  		// Then
   126  		keys := []string{"1", "2", "3"}
   127  		rs := instance.Get(ctx, keys, t.randomObjCallback(stringObjMap, algo, false))
   128  		t.NotEmpty(rs)
   129  
   130  		instance.Del(ctx, keys...)
   131  		rs = instance.Get(ctx, keys, t.randomObjCallback(stringObjMap, algo, true))
   132  		t.NotEmpty(rs)
   133  	})
   134  }
   135  
   136  func (t *Redis) TestDelWithFailureKeys() {
   137  	t.Catch(func() {
   138  		// Given
   139  		ctx := context.Background()
   140  		algo := serialize.AlgorithmGob
   141  		instance := cache.New[string, *mock.RandomObj, []*mock.RandomObj](redis, cache.AppName(t.AppName()))
   142  		stringObjMap := map[string]*mock.RandomObj{
   143  			"1": mock.GenObjBySerializeAlgo(algo).(*mock.RandomObj),
   144  			"2": mock.GenObjBySerializeAlgo(algo).(*mock.RandomObj),
   145  			"3": mock.GenObjBySerializeAlgo(algo).(*mock.RandomObj),
   146  		}
   147  		defer instance.Clear(ctx)
   148  
   149  		// When
   150  		keys := []string{"1", "2"}
   151  		instance.Set(ctx, stringObjMap)
   152  		failureKeys := instance.Del(ctx, keys...)
   153  		t.Empty(failureKeys)
   154  
   155  		// Then
   156  		failureKeys = instance.Del(ctx, keys...)
   157  		t.Empty(failureKeys)
   158  	})
   159  }
   160  
   161  func (t *Redis) TestSetGetInParallel() {
   162  	t.Catch(func() {
   163  		// Given
   164  		ctx := context.Background()
   165  		instance := cache.New[string, *mock.CommonObj, []*mock.CommonObj](redisJson, cache.AppName(t.AppName()))
   166  		defer instance.Clear(ctx)
   167  
   168  		wg := new(sync.WaitGroup)
   169  		for i := 0; i < 500; i++ {
   170  			wg.Add(1)
   171  			go func() {
   172  				defer wg.Done()
   173  				key := faker.UUIDHyphenated()
   174  				val := mock.GenObjBySerializeAlgo(serialize.AlgorithmJson).(*mock.CommonObj)
   175  				instance.Set(ctx, map[string]*mock.CommonObj{key: val})
   176  				rs := instance.Get(ctx, []string{key}, commonObjCallback)
   177  				t.EqualValues(val, rs[0])
   178  			}()
   179  		}
   180  		wg.Wait()
   181  	})
   182  }
   183  
   184  func (t *Redis) TestLocalWithCompress() {
   185  	t.Catch(func() {
   186  		// Given
   187  		ctx := context.Background()
   188  
   189  		type cases struct {
   190  			name      string
   191  			cacheName string
   192  		}
   193  
   194  		testCases := []cases{
   195  			{
   196  				name:      "zstd",
   197  				cacheName: redisWithZstdCompress,
   198  			},
   199  			{
   200  				name:      "zlib",
   201  				cacheName: redisWithZlibCompress,
   202  			},
   203  			{
   204  				name:      "s2",
   205  				cacheName: redisWithS2Compress,
   206  			},
   207  			{
   208  				name:      "gzip",
   209  				cacheName: redisWithGzipCompress,
   210  			},
   211  			{
   212  				name:      "deflate",
   213  				cacheName: redisWithDeflateCompress,
   214  			},
   215  		}
   216  
   217  		algo := serialize.AlgorithmGob
   218  		for _, cs := range testCases {
   219  			instance := cache.New[string, *mock.RandomObj, []*mock.RandomObj](
   220  				cs.cacheName, cache.AppName(t.AppName()))
   221  			t.Run(cs.name, func() {
   222  				defer instance.Clear(ctx)
   223  				t.runInParallel(func() {
   224  					randomKey := faker.UUIDHyphenated()
   225  					stringObjMap := map[string]*mock.RandomObj{
   226  						randomKey: mock.GenObjBySerializeAlgo(algo).(*mock.RandomObj),
   227  					}
   228  					instance.Set(ctx, stringObjMap)
   229  
   230  					// When
   231  					randomKeys := [3]string{}
   232  					t.NoError(faker.FakeData(&randomKeys))
   233  					keys := append([]string{randomKey}, randomKeys[:]...)
   234  					rs := instance.Get(ctx, keys, t.randomObjCallback(stringObjMap, algo, true))
   235  
   236  					// Then
   237  					t.Equal(len(rs), len(keys))
   238  					t.EqualValues(stringObjMap[randomKey], rs[0])
   239  					for i := 0; i < len(rs); i++ {
   240  						t.NotEmpty(rs[i])
   241  					}
   242  				})
   243  			})
   244  		}
   245  	})
   246  }
   247  
   248  func (t *Redis) runInParallel(exec func()) {
   249  	wg := new(sync.WaitGroup)
   250  	for i := 0; i < 100; i++ {
   251  		wg.Add(1)
   252  		go func() {
   253  			defer wg.Done()
   254  			exec()
   255  			time.Sleep(time.Duration(float64(time.Millisecond) * rand.Float64()))
   256  		}()
   257  	}
   258  	wg.Wait()
   259  }
   260  
   261  func (t *Redis) randomObjCallback(origin map[string]*mock.RandomObj, algo serialize.Algorithm, mayMissing bool) (
   262  	cb func(context.Context, []string) (map[string]*mock.RandomObj, []utils.OptionExtender)) {
   263  	return func(ctx context.Context, missed []string) (rs map[string]*mock.RandomObj, opts []utils.OptionExtender) {
   264  		if !mayMissing {
   265  			t.FailNow("cache missing!", missed)
   266  		}
   267  
   268  		rs = make(map[string]*mock.RandomObj, len(missed))
   269  		for _, key := range missed {
   270  			if v, ok := origin[key]; ok {
   271  				rs[key] = v
   272  			} else {
   273  				rs[key] = mock.GenObjBySerializeAlgo(algo).(*mock.RandomObj)
   274  			}
   275  		}
   276  		return
   277  	}
   278  }
   279  
   280  func (t *Redis) commonObjCallback(origin map[string]*mock.CommonObj, algo serialize.Algorithm, mayMissing bool) (
   281  	cb func(context.Context, []string) (map[string]*mock.CommonObj, []utils.OptionExtender)) {
   282  	return func(ctx context.Context, missed []string) (rs map[string]*mock.CommonObj, opts []utils.OptionExtender) {
   283  		if !mayMissing {
   284  			t.FailNow("cache missing!", missed)
   285  		}
   286  
   287  		rs = make(map[string]*mock.CommonObj, len(missed))
   288  		for _, key := range missed {
   289  			if v, ok := origin[key]; ok {
   290  				rs[key] = v
   291  			} else {
   292  				rs[key] = mock.GenObjBySerializeAlgo(algo).(*mock.CommonObj)
   293  			}
   294  		}
   295  		return
   296  	}
   297  }