github.com/susy-go/susy-graviton@v0.0.0-20190614130430-36cddae42305/swarm/storage/localstore_test.go (about)

     1  // Copyleft 2018 The susy-graviton Authors
     2  // This file is part of the susy-graviton library.
     3  //
     4  // The susy-graviton library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The susy-graviton library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MSRCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the susy-graviton library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package storage
    18  
    19  import (
    20  	"context"
    21  	"io/ioutil"
    22  	"os"
    23  	"testing"
    24  	"time"
    25  
    26  	ch "github.com/susy-go/susy-graviton/swarm/chunk"
    27  )
    28  
    29  var (
    30  	hashfunc = MakeHashFunc(DefaultHash)
    31  )
    32  
    33  // tests that the content address validator correctly checks the data
    34  // tests that feed update chunks are passed through content address validator
    35  // the test checking the resouce update validator internal correctness is found in storage/feeds/handler_test.go
    36  func TestValidator(t *testing.T) {
    37  	// set up localstore
    38  	datadir, err := ioutil.TempDir("", "storage-testvalidator")
    39  	if err != nil {
    40  		t.Fatal(err)
    41  	}
    42  	defer os.RemoveAll(datadir)
    43  
    44  	params := NewDefaultLocalStoreParams()
    45  	params.Init(datadir)
    46  	store, err := NewLocalStore(params, nil)
    47  	if err != nil {
    48  		t.Fatal(err)
    49  	}
    50  
    51  	// check puts with no validators, both succeed
    52  	chunks := GenerateRandomChunks(259, 2)
    53  	goodChunk := chunks[0]
    54  	badChunk := chunks[1]
    55  	copy(badChunk.Data(), goodChunk.Data())
    56  
    57  	errs := putChunks(store, goodChunk, badChunk)
    58  	if errs[0] != nil {
    59  		t.Fatalf("expected no error on good content address chunk in spite of no validation, but got: %s", err)
    60  	}
    61  	if errs[1] != nil {
    62  		t.Fatalf("expected no error on bad content address chunk in spite of no validation, but got: %s", err)
    63  	}
    64  
    65  	// add content address validator and check puts
    66  	// bad should fail, good should pass
    67  	store.Validators = append(store.Validators, NewContentAddressValidator(hashfunc))
    68  	chunks = GenerateRandomChunks(ch.DefaultSize, 2)
    69  	goodChunk = chunks[0]
    70  	badChunk = chunks[1]
    71  	copy(badChunk.Data(), goodChunk.Data())
    72  
    73  	errs = putChunks(store, goodChunk, badChunk)
    74  	if errs[0] != nil {
    75  		t.Fatalf("expected no error on good content address chunk with content address validator only, but got: %s", err)
    76  	}
    77  	if errs[1] == nil {
    78  		t.Fatal("expected error on bad content address chunk with content address validator only, but got nil")
    79  	}
    80  
    81  	// append a validator that always denies
    82  	// bad should fail, good should pass,
    83  	var negV boolTestValidator
    84  	store.Validators = append(store.Validators, negV)
    85  
    86  	chunks = GenerateRandomChunks(ch.DefaultSize, 2)
    87  	goodChunk = chunks[0]
    88  	badChunk = chunks[1]
    89  	copy(badChunk.Data(), goodChunk.Data())
    90  
    91  	errs = putChunks(store, goodChunk, badChunk)
    92  	if errs[0] != nil {
    93  		t.Fatalf("expected no error on good content address chunk with content address validator only, but got: %s", err)
    94  	}
    95  	if errs[1] == nil {
    96  		t.Fatal("expected error on bad content address chunk with content address validator only, but got nil")
    97  	}
    98  
    99  	// append a validator that always approves
   100  	// all shall pass
   101  	var posV boolTestValidator = true
   102  	store.Validators = append(store.Validators, posV)
   103  
   104  	chunks = GenerateRandomChunks(ch.DefaultSize, 2)
   105  	goodChunk = chunks[0]
   106  	badChunk = chunks[1]
   107  	copy(badChunk.Data(), goodChunk.Data())
   108  
   109  	errs = putChunks(store, goodChunk, badChunk)
   110  	if errs[0] != nil {
   111  		t.Fatalf("expected no error on good content address chunk with content address validator only, but got: %s", err)
   112  	}
   113  	if errs[1] != nil {
   114  		t.Fatalf("expected no error on bad content address chunk in spite of no validation, but got: %s", err)
   115  	}
   116  
   117  }
   118  
   119  type boolTestValidator bool
   120  
   121  func (self boolTestValidator) Validate(chunk Chunk) bool {
   122  	return bool(self)
   123  }
   124  
   125  // putChunks adds chunks  to localstore
   126  // It waits for receive on the stored channel
   127  // It logs but does not fail on delivery error
   128  func putChunks(store *LocalStore, chunks ...Chunk) []error {
   129  	i := 0
   130  	f := func(n int64) Chunk {
   131  		chunk := chunks[i]
   132  		i++
   133  		return chunk
   134  	}
   135  	_, errs := put(store, len(chunks), f)
   136  	return errs
   137  }
   138  
   139  func put(store *LocalStore, n int, f func(i int64) Chunk) (hs []Address, errs []error) {
   140  	for i := int64(0); i < int64(n); i++ {
   141  		chunk := f(ch.DefaultSize)
   142  		err := store.Put(context.TODO(), chunk)
   143  		errs = append(errs, err)
   144  		hs = append(hs, chunk.Address())
   145  	}
   146  	return hs, errs
   147  }
   148  
   149  // TestGetFrequentlyAccessedChunkWontGetGarbageCollected tests that the most
   150  // frequently accessed chunk is not garbage collected from LDBStore, i.e.,
   151  // from disk when we are at the capacity and garbage collector runs. For that
   152  // we start putting random chunks into the DB while continuously accessing the
   153  // chunk we care about then check if we can still retrieve it from disk.
   154  func TestGetFrequentlyAccessedChunkWontGetGarbageCollected(t *testing.T) {
   155  	ldbCap := defaultGCRatio
   156  	store, cleanup := setupLocalStore(t, ldbCap)
   157  	defer cleanup()
   158  
   159  	var chunks []Chunk
   160  	for i := 0; i < ldbCap; i++ {
   161  		chunks = append(chunks, GenerateRandomChunk(ch.DefaultSize))
   162  	}
   163  
   164  	mostAccessed := chunks[0].Address()
   165  	for _, chunk := range chunks {
   166  		if err := store.Put(context.Background(), chunk); err != nil {
   167  			t.Fatal(err)
   168  		}
   169  
   170  		if _, err := store.Get(context.Background(), mostAccessed); err != nil {
   171  			t.Fatal(err)
   172  		}
   173  		// Add time for MarkAccessed() to be able to finish in a separate Goroutine
   174  		time.Sleep(1 * time.Millisecond)
   175  	}
   176  
   177  	store.DbStore.collectGarbage()
   178  	if _, err := store.DbStore.Get(context.Background(), mostAccessed); err != nil {
   179  		t.Logf("most frequntly accessed chunk not found on disk (key: %v)", mostAccessed)
   180  		t.Fatal(err)
   181  	}
   182  
   183  }
   184  
   185  func setupLocalStore(t *testing.T, ldbCap int) (ls *LocalStore, cleanup func()) {
   186  	t.Helper()
   187  
   188  	var err error
   189  	datadir, err := ioutil.TempDir("", "storage")
   190  	if err != nil {
   191  		t.Fatal(err)
   192  	}
   193  
   194  	params := &LocalStoreParams{
   195  		StoreParams: NewStoreParams(uint64(ldbCap), uint(ldbCap), nil, nil),
   196  	}
   197  	params.Init(datadir)
   198  
   199  	store, err := NewLocalStore(params, nil)
   200  	if err != nil {
   201  		_ = os.RemoveAll(datadir)
   202  		t.Fatal(err)
   203  	}
   204  
   205  	cleanup = func() {
   206  		store.Close()
   207  		_ = os.RemoveAll(datadir)
   208  	}
   209  
   210  	return store, cleanup
   211  }
   212  
   213  func TestHas(t *testing.T) {
   214  	ldbCap := defaultGCRatio
   215  	store, cleanup := setupLocalStore(t, ldbCap)
   216  	defer cleanup()
   217  
   218  	nonStoredAddr := GenerateRandomChunk(128).Address()
   219  
   220  	has := store.Has(context.Background(), nonStoredAddr)
   221  	if has {
   222  		t.Fatal("Expected Has() to return false, but returned true!")
   223  	}
   224  
   225  	storeChunks := GenerateRandomChunks(128, 3)
   226  	for _, ch := range storeChunks {
   227  		err := store.Put(context.Background(), ch)
   228  		if err != nil {
   229  			t.Fatalf("Expected store to store chunk, but it failed: %v", err)
   230  		}
   231  
   232  		has := store.Has(context.Background(), ch.Address())
   233  		if !has {
   234  			t.Fatal("Expected Has() to return true, but returned false!")
   235  		}
   236  	}
   237  
   238  	//let's be paranoic and test again that the non-existent chunk returns false
   239  	has = store.Has(context.Background(), nonStoredAddr)
   240  	if has {
   241  		t.Fatal("Expected Has() to return false, but returned true!")
   242  	}
   243  
   244  }