github.com/filecoin-project/lassie@v0.23.0/pkg/storage/storage_test.go (about)

     1  package storage
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"testing"
    10  
    11  	"github.com/filecoin-project/lassie/pkg/types"
    12  	"github.com/ipfs/go-cid"
    13  	"github.com/ipld/go-ipld-prime"
    14  	cidlink "github.com/ipld/go-ipld-prime/linking/cid"
    15  	"github.com/stretchr/testify/require"
    16  )
    17  
    18  func TestTempCarStorage(t *testing.T) {
    19  	// Testing both DeferredCarStorage and CachingTempStore here with just some
    20  	// additional pieces of logic to make sure the teeing version is actually
    21  	// teeing.
    22  	for _, teeing := range []bool{true, false} {
    23  		teeing := teeing
    24  		t.Run(fmt.Sprintf("teeing=%t", teeing), func(t *testing.T) {
    25  			t.Parallel()
    26  			ctx := context.Background()
    27  			testCid1, testData1 := randBlock()
    28  			testCid2, testData2 := randBlock()
    29  			testCid3, _ := randBlock()
    30  
    31  			tempDir := t.TempDir()
    32  
    33  			teeCollect := make(map[cid.Cid][]byte, 0)
    34  			var cw types.ReadableWritableStorage
    35  			if teeing {
    36  				bwo := func(ipld.LinkContext) (io.Writer, ipld.BlockWriteCommitter, error) {
    37  					var buf bytes.Buffer
    38  					return &buf, func(lnk ipld.Link) error {
    39  						teeCollect[lnk.(cidlink.Link).Cid] = buf.Bytes()
    40  						return nil
    41  					}, nil
    42  				}
    43  				cw = NewCachingTempStore(bwo, NewDeferredStorageCar(tempDir, testCid1))
    44  			} else {
    45  				cw = NewDeferredStorageCar(tempDir, testCid1)
    46  			}
    47  
    48  			ents, err := os.ReadDir(tempDir)
    49  			require.NoError(t, err)
    50  			require.Len(t, ents, 0)
    51  
    52  			has, err := cw.Has(ctx, testCid3.KeyString())
    53  			require.NoError(t, err)
    54  			require.False(t, has)
    55  			_, err = cw.Get(ctx, testCid1.KeyString())
    56  			require.Error(t, err)
    57  			enf, ok := err.(interface{ NotFound() bool })
    58  			require.True(t, ok)
    59  			require.True(t, enf.NotFound())
    60  			_, err = cw.GetStream(ctx, testCid1.KeyString())
    61  			require.Error(t, err)
    62  			enf, ok = err.(interface{ NotFound() bool })
    63  			require.True(t, ok)
    64  			require.True(t, enf.NotFound())
    65  
    66  			require.NoError(t, cw.Put(ctx, testCid1.KeyString(), testData1))
    67  			has, err = cw.Has(ctx, testCid1.KeyString())
    68  			require.NoError(t, err)
    69  			require.True(t, has)
    70  			got, err := cw.Get(ctx, testCid1.KeyString())
    71  			require.NoError(t, err)
    72  			require.Equal(t, testData1, got)
    73  			gotStream, err := cw.GetStream(ctx, testCid1.KeyString())
    74  			require.NoError(t, err)
    75  			got, err = io.ReadAll(gotStream)
    76  			require.NoError(t, err)
    77  			require.Equal(t, testData1, got)
    78  			require.NoError(t, cw.Put(ctx, testCid2.KeyString(), testData2))
    79  			has, err = cw.Has(ctx, testCid1.KeyString())
    80  			require.NoError(t, err)
    81  			require.True(t, has)
    82  			has, err = cw.Has(ctx, testCid2.KeyString())
    83  			require.NoError(t, err)
    84  			require.True(t, has)
    85  			got, err = cw.Get(ctx, testCid2.KeyString())
    86  			require.NoError(t, err)
    87  			require.Equal(t, testData2, got)
    88  			gotStream, err = cw.GetStream(ctx, testCid2.KeyString())
    89  			require.NoError(t, err)
    90  			got, err = io.ReadAll(gotStream)
    91  			require.NoError(t, err)
    92  			require.Equal(t, testData2, got)
    93  			has, err = cw.Has(ctx, testCid3.KeyString())
    94  			require.NoError(t, err)
    95  			require.False(t, has)
    96  			_, err = cw.Get(ctx, testCid3.KeyString())
    97  			require.Error(t, err)
    98  			enf, ok = err.(interface{ NotFound() bool })
    99  			require.True(t, ok)
   100  			require.True(t, enf.NotFound())
   101  
   102  			ents, err = os.ReadDir(tempDir)
   103  			require.NoError(t, err)
   104  			require.Len(t, ents, 1)
   105  			require.Contains(t, ents[0].Name(), "carstorage")
   106  			stat, err := os.Stat(tempDir + "/" + ents[0].Name())
   107  			require.NoError(t, err)
   108  			require.True(t, stat.Size() > int64(len(testData1)+len(testData2)))
   109  
   110  			closer, ok := cw.(io.Closer)
   111  			require.True(t, ok)
   112  			require.NoError(t, closer.Close())
   113  
   114  			// should be deleted
   115  			ents, err = os.ReadDir(tempDir)
   116  			require.NoError(t, err)
   117  			require.Len(t, ents, 0)
   118  
   119  			if teeing {
   120  				require.Len(t, teeCollect, 2)
   121  				require.Equal(t, testData1, teeCollect[testCid1])
   122  				require.Equal(t, testData2, teeCollect[testCid2])
   123  			}
   124  		})
   125  	}
   126  }
   127  
   128  type blk struct {
   129  	cid  cid.Cid
   130  	data []byte
   131  }
   132  
   133  func TestPreloadStore(t *testing.T) {
   134  	ctx := context.Background()
   135  
   136  	td := make([]blk, 0, 10)
   137  	for i := 0; i < 10; i++ {
   138  		c, d := randBlock()
   139  		td = append(td, blk{c, d})
   140  	}
   141  
   142  	bwoCnt := 0
   143  	bwo := func(ipld.LinkContext) (io.Writer, ipld.BlockWriteCommitter, error) {
   144  		var buf bytes.Buffer
   145  		return &buf, func(lnk ipld.Link) error {
   146  			c := lnk.(cidlink.Link).Cid
   147  			if bwoCnt >= len(td) {
   148  				require.Fail(t, "too many calls to bwo")
   149  			} else {
   150  				require.Equal(t, td[bwoCnt].cid, c)
   151  				require.Equal(t, td[bwoCnt].data, buf.Bytes())
   152  			}
   153  			bwoCnt++
   154  			return nil
   155  		}, nil
   156  	}
   157  	mainStore := NewCachingTempStore(bwo, NewDeferredStorageCar(t.TempDir(), td[0].cid))
   158  	t.Cleanup(func() {
   159  		require.NoError(t, mainStore.Close())
   160  	})
   161  	preload := mainStore.PreloadStore()
   162  
   163  	checkNotHas := func(blks []blk, stores ...types.ReadableWritableStorage) {
   164  		for _, d := range blks {
   165  			for _, s := range stores {
   166  				has, err := s.Has(ctx, d.cid.KeyString())
   167  				require.NoError(t, err)
   168  				require.False(t, has)
   169  				_, err = s.Get(ctx, d.cid.KeyString())
   170  				require.Error(t, err)
   171  				enf, ok := err.(interface{ NotFound() bool })
   172  				require.True(t, ok)
   173  				require.True(t, enf.NotFound())
   174  				_, err = s.GetStream(ctx, d.cid.KeyString())
   175  				require.Error(t, err)
   176  				enf, ok = err.(interface{ NotFound() bool })
   177  				require.True(t, ok)
   178  				require.True(t, enf.NotFound())
   179  			}
   180  		}
   181  	}
   182  
   183  	checkHas := func(blks []blk, stores ...types.ReadableWritableStorage) {
   184  		for _, d := range blks {
   185  			for _, s := range stores {
   186  				has, err := s.Has(ctx, d.cid.KeyString())
   187  				require.NoError(t, err)
   188  				require.True(t, has)
   189  				got, err := s.Get(ctx, d.cid.KeyString())
   190  				require.NoError(t, err)
   191  				require.Equal(t, d.data, got)
   192  				gotStream, err := s.GetStream(ctx, d.cid.KeyString())
   193  				require.NoError(t, err)
   194  				got, err = io.ReadAll(gotStream)
   195  				require.NoError(t, err)
   196  				require.Equal(t, d.data, got)
   197  			}
   198  		}
   199  	}
   200  
   201  	checkNotHas(td, mainStore, preload)
   202  	mainStore.Put(ctx, td[0].cid.KeyString(), td[0].data)
   203  	checkNotHas(td, preload)
   204  	checkNotHas(td[1:], mainStore)
   205  	checkHas(td[:1], mainStore)
   206  	for i := 5; i >= 1; i-- { // out of order, partial preload
   207  		preload.Put(ctx, td[i].cid.KeyString(), td[i].data)
   208  	}
   209  	checkNotHas(td[1:], mainStore)
   210  	checkNotHas(td[:1], preload)
   211  	checkHas(td[1:6], preload)
   212  	checkNotHas(td[6:], preload)
   213  	for _, d := range td[1:] { // in order, complete
   214  		mainStore.Put(ctx, d.cid.KeyString(), d.data)
   215  	}
   216  	checkHas(td, mainStore)
   217  	checkNotHas(td, preload)
   218  }