github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/storage/badger/operation/modifiers_test.go (about)

     1  package operation
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/dgraph-io/badger/v2"
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  	"github.com/vmihailenco/msgpack/v4"
    12  
    13  	"github.com/onflow/flow-go/storage"
    14  	"github.com/onflow/flow-go/utils/unittest"
    15  )
    16  
    17  func TestSkipDuplicates(t *testing.T) {
    18  	unittest.RunWithBadgerDB(t, func(db *badger.DB) {
    19  		e := Entity{ID: 1337}
    20  		key := []byte{0x01, 0x02, 0x03}
    21  		val, _ := msgpack.Marshal(e)
    22  
    23  		// persist first time
    24  		err := db.Update(insert(key, e))
    25  		require.NoError(t, err)
    26  
    27  		e2 := Entity{ID: 1338}
    28  
    29  		// persist again
    30  		err = db.Update(SkipDuplicates(insert(key, e2)))
    31  		require.NoError(t, err)
    32  
    33  		// ensure old value is still used
    34  		var act []byte
    35  		_ = db.View(func(tx *badger.Txn) error {
    36  			item, err := tx.Get(key)
    37  			require.NoError(t, err)
    38  			act, err = item.ValueCopy(nil)
    39  			require.NoError(t, err)
    40  			return nil
    41  		})
    42  
    43  		assert.Equal(t, val, act)
    44  	})
    45  }
    46  
    47  func TestRetryOnConflict(t *testing.T) {
    48  	unittest.RunWithBadgerDB(t, func(db *badger.DB) {
    49  		t.Run("good op", func(t *testing.T) {
    50  			goodOp := func(*badger.Txn) error {
    51  				return nil
    52  			}
    53  			err := RetryOnConflict(db.Update, goodOp)
    54  			require.NoError(t, err)
    55  		})
    56  
    57  		t.Run("conflict op should be retried", func(t *testing.T) {
    58  			n := 0
    59  			conflictOp := func(*badger.Txn) error {
    60  				n++
    61  				if n > 3 {
    62  					return nil
    63  				}
    64  				return badger.ErrConflict
    65  			}
    66  			err := RetryOnConflict(db.Update, conflictOp)
    67  			require.NoError(t, err)
    68  		})
    69  
    70  		t.Run("wrapped conflict op should be retried", func(t *testing.T) {
    71  			n := 0
    72  			conflictOp := func(*badger.Txn) error {
    73  				n++
    74  				if n > 3 {
    75  					return nil
    76  				}
    77  				return fmt.Errorf("wrap error: %w", badger.ErrConflict)
    78  			}
    79  			err := RetryOnConflict(db.Update, conflictOp)
    80  			require.NoError(t, err)
    81  		})
    82  
    83  		t.Run("other error should be returned", func(t *testing.T) {
    84  			otherError := errors.New("other error")
    85  			failOp := func(*badger.Txn) error {
    86  				return otherError
    87  			}
    88  
    89  			err := RetryOnConflict(db.Update, failOp)
    90  			require.Equal(t, otherError, err)
    91  		})
    92  	})
    93  }
    94  
    95  func TestSkipNonExists(t *testing.T) {
    96  	unittest.RunWithBadgerDB(t, func(db *badger.DB) {
    97  		t.Run("not found", func(t *testing.T) {
    98  			op := func(*badger.Txn) error {
    99  				return badger.ErrKeyNotFound
   100  			}
   101  
   102  			err := db.Update(SkipNonExist(op))
   103  			require.NoError(t, err)
   104  		})
   105  
   106  		t.Run("not exist", func(t *testing.T) {
   107  			op := func(*badger.Txn) error {
   108  				return storage.ErrNotFound
   109  			}
   110  
   111  			err := db.Update(SkipNonExist(op))
   112  			require.NoError(t, err)
   113  		})
   114  
   115  		t.Run("general error", func(t *testing.T) {
   116  			expectError := fmt.Errorf("random error")
   117  			op := func(*badger.Txn) error {
   118  				return expectError
   119  			}
   120  
   121  			err := db.Update(SkipNonExist(op))
   122  			require.Equal(t, expectError, err)
   123  		})
   124  	})
   125  }