github.com/ethersphere/bee/v2@v2.2.0/pkg/statestore/test/store.go (about)

     1  // Copyright 2020 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package test
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	"strings"
    11  	"testing"
    12  
    13  	"github.com/ethersphere/bee/v2/pkg/storage"
    14  )
    15  
    16  const (
    17  	key1 = "key1" // stores the serialized type
    18  	key2 = "key2" // stores a json array
    19  )
    20  
    21  var (
    22  	value1 = &Serializing{value: "value1"}
    23  	value2 = []string{"a", "b", "c"}
    24  )
    25  
    26  type Serializing struct {
    27  	value           string
    28  	marshalCalled   bool
    29  	unmarshalCalled bool
    30  }
    31  
    32  func (st *Serializing) MarshalBinary() (data []byte, err error) {
    33  	d := []byte(st.value)
    34  	st.marshalCalled = true
    35  
    36  	return d, nil
    37  }
    38  
    39  func (st *Serializing) UnmarshalBinary(data []byte) (err error) {
    40  	st.value = string(data)
    41  	st.unmarshalCalled = true
    42  	return nil
    43  }
    44  
    45  // RunPersist is a specific test case for the persistent state store.
    46  // It tests that values persist across sessions.
    47  func RunPersist(t *testing.T, f func(t *testing.T, dir string) storage.StateStorer) {
    48  	t.Helper()
    49  	dir, err := os.MkdirTemp("", "statestore_test")
    50  	if err != nil {
    51  		t.Fatal(err)
    52  	}
    53  	defer os.RemoveAll(dir)
    54  
    55  	store := f(t, dir)
    56  
    57  	// insert some values
    58  	insert(t, store, "some_prefix", 1000)
    59  
    60  	// test that the iterator works
    61  	testStoreIterator(t, store, "some_prefix", 1000)
    62  
    63  	// close the store
    64  	if err := store.Close(); err != nil {
    65  		t.Fatal(err)
    66  	}
    67  
    68  	// bootstrap with the same old dir
    69  	persistedStore := f(t, dir)
    70  	defer persistedStore.Close()
    71  
    72  	// test that the iterator works
    73  	testStoreIterator(t, persistedStore, "some_prefix", 1000)
    74  
    75  	// insert some more random entries
    76  	insert(t, persistedStore, "some_other_prefix", 1000)
    77  
    78  	// check again
    79  	testStoreIterator(t, persistedStore, "some_other_prefix", 1000)
    80  }
    81  
    82  func Run(t *testing.T, f func(t *testing.T) storage.StateStorer) {
    83  	t.Helper()
    84  
    85  	t.Run("test_put_get", func(t *testing.T) { testPutGet(t, f) })
    86  	t.Run("test_delete", func(t *testing.T) { testDelete(t, f) })
    87  	t.Run("test_iterator", func(t *testing.T) { testIterator(t, f) })
    88  }
    89  
    90  func testDelete(t *testing.T, f func(t *testing.T) storage.StateStorer) {
    91  	t.Helper()
    92  
    93  	// create a store
    94  	store := f(t)
    95  
    96  	// insert some values
    97  	insertValues(t, store, key1, key2, value1, value2)
    98  
    99  	// check that the persisted values match
   100  	testPersistedValues(t, store, key1, key2, value1, value2)
   101  
   102  	err := store.Delete(key1)
   103  	if err != nil {
   104  		t.Fatal(err)
   105  	}
   106  	err = store.Delete(key2)
   107  	if err != nil {
   108  		t.Fatal(err)
   109  	}
   110  
   111  	// check that the store is empty
   112  	testEmpty(t, store)
   113  }
   114  
   115  func testPutGet(t *testing.T, f func(t *testing.T) storage.StateStorer) {
   116  	t.Helper()
   117  
   118  	// create a store
   119  	store := f(t)
   120  
   121  	// insert some values
   122  	insertValues(t, store, key1, key2, value1, value2)
   123  
   124  	// check that the persisted values match
   125  	testPersistedValues(t, store, key1, key2, value1, value2)
   126  }
   127  
   128  func testIterator(t *testing.T, f func(t *testing.T) storage.StateStorer) {
   129  	t.Helper()
   130  
   131  	// create a store
   132  	store := f(t)
   133  
   134  	// insert some values
   135  	insert(t, store, "some_prefix", 1000)
   136  
   137  	// test that the iterator works
   138  	testStoreIterator(t, store, "some_prefix", 1000)
   139  	testStoreIterator(t, store, "no_prefix", 0)
   140  }
   141  
   142  func insertValues(t *testing.T, store storage.StateStorer, key1, key2 string, value1 *Serializing, value2 []string) {
   143  	t.Helper()
   144  	err := store.Put(key1, value1)
   145  	if err != nil {
   146  		t.Fatal(err)
   147  	}
   148  
   149  	if !value1.marshalCalled {
   150  		t.Fatal("binaryMarshaller not called on serialized type")
   151  	}
   152  
   153  	err = store.Put(key2, value2)
   154  	if err != nil {
   155  		t.Fatal(err)
   156  	}
   157  }
   158  
   159  func insert(t *testing.T, store storage.StateStorer, prefix string, count int) {
   160  	t.Helper()
   161  
   162  	for i := 0; i < count; i++ {
   163  		k := prefix + fmt.Sprint(i)
   164  
   165  		err := store.Put(k, i)
   166  		if err != nil {
   167  			t.Fatal(err)
   168  		}
   169  	}
   170  }
   171  
   172  func testPersistedValues(t *testing.T, store storage.StateStorer, key1, key2 string, value1 *Serializing, value2 []string) {
   173  	t.Helper()
   174  
   175  	v := &Serializing{}
   176  	err := store.Get(key1, v)
   177  	if err != nil {
   178  		t.Fatal(err)
   179  	}
   180  
   181  	if !v.unmarshalCalled {
   182  		t.Fatal("unmarshaler not called")
   183  	}
   184  
   185  	if v.value != value1.value {
   186  		t.Fatalf("expected persisted to be %s but got %s", value1.value, v.value)
   187  	}
   188  
   189  	s := []string{}
   190  	err = store.Get(key2, &s)
   191  	if err != nil {
   192  		t.Fatal(err)
   193  	}
   194  
   195  	for i, ss := range value2 {
   196  		if s[i] != ss {
   197  			t.Fatalf("deserialized data mismatch. expected %s but got %s", ss, s[i])
   198  		}
   199  	}
   200  }
   201  
   202  func testStoreIterator(t *testing.T, store storage.StateStorer, prefix string, size int) {
   203  	t.Helper()
   204  
   205  	matching := 0
   206  	entriesIterFunction := func(key []byte, value []byte) (stop bool, err error) {
   207  		k := string(key)
   208  		if !strings.HasPrefix(k, prefix) {
   209  			return true, fmt.Errorf("iterator called callback with wrong key prefix. key: %s expected prefix: %s", k, prefix)
   210  		}
   211  		matching++
   212  		return false, nil
   213  	}
   214  
   215  	err := store.Iterate(prefix, entriesIterFunction)
   216  	if err != nil {
   217  		t.Fatal(err)
   218  	}
   219  
   220  	if matching != size {
   221  		t.Fatalf("entry number mismatch. want %d got %d", size, matching)
   222  	}
   223  }
   224  
   225  func testEmpty(t *testing.T, store storage.StateStorer) {
   226  	t.Helper()
   227  
   228  	testStoreIterator(t, store, "", 0)
   229  }