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  }