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  }