// Copyright (C) 2019 Storj Labs, Inc. // See LICENSE for copying information. package filestore_test import ( "errors" "io" "io/ioutil" "math/rand" "os" "path/filepath" "testing" "github.com/stretchr/testify/require" "storj.io/storj/internal/testcontext" "storj.io/storj/storage" "storj.io/storj/storage/filestore" ) func randomValue() []byte { var id [32]byte _, _ = rand.Read(id[:]) return id[:] } func TestStoreLoad(t *testing.T) { const blobSize = 8 << 10 const repeatCount = 16 ctx := testcontext.New(t) defer ctx.Cleanup() store, err := filestore.NewAt(ctx.Dir("store")) require.NoError(t, err) data := make([]byte, blobSize) temp := make([]byte, len(data)) _, _ = rand.Read(data) refs := []storage.BlobRef{} namespace := randomValue() // store without size for i := 0; i < repeatCount; i++ { ref := storage.BlobRef{ Namespace: namespace, Key: randomValue(), } refs = append(refs, ref) writer, err := store.Create(ctx, ref, -1) require.NoError(t, err) n, err := writer.Write(data) require.NoError(t, err) require.Equal(t, n, len(data)) require.NoError(t, writer.Commit()) // after committing we should be able to call cancel without an error require.NoError(t, writer.Cancel()) // two commits should fail require.Error(t, writer.Commit()) } namespace = randomValue() // store with size for i := 0; i < repeatCount; i++ { ref := storage.BlobRef{ Namespace: namespace, Key: randomValue(), } refs = append(refs, ref) writer, err := store.Create(ctx, ref, int64(len(data))) require.NoError(t, err) n, err := writer.Write(data) require.NoError(t, err) require.Equal(t, n, len(data)) require.NoError(t, writer.Commit()) } namespace = randomValue() // store with larger size { ref := storage.BlobRef{ Namespace: namespace, Key: randomValue(), } refs = append(refs, ref) writer, err := store.Create(ctx, ref, int64(len(data)*2)) require.NoError(t, err) n, err := writer.Write(data) require.NoError(t, err) require.Equal(t, n, len(data)) require.NoError(t, writer.Commit()) } namespace = randomValue() // store with error { ref := storage.BlobRef{ Namespace: namespace, Key: randomValue(), } writer, err := store.Create(ctx, ref, -1) require.NoError(t, err) n, err := writer.Write(data) require.NoError(t, err) require.Equal(t, n, len(data)) require.NoError(t, writer.Cancel()) // commit after cancel should return an error require.Error(t, writer.Commit()) _, err = store.Open(ctx, ref) require.Error(t, err) } // try reading all the blobs for _, ref := range refs { reader, err := store.Open(ctx, ref) require.NoError(t, err) size, err := reader.Size() require.NoError(t, err) require.Equal(t, size, int64(len(data))) _, err = io.ReadFull(reader, temp) require.NoError(t, err) require.NoError(t, reader.Close()) require.Equal(t, data, temp) } // delete the blobs for _, ref := range refs { err := store.Delete(ctx, ref) require.NoError(t, err) } // try reading all the blobs for _, ref := range refs { _, err := store.Open(ctx, ref) require.Error(t, err) } } func TestDeleteWhileReading(t *testing.T) { const blobSize = 8 << 10 const repeatCount = 16 ctx := testcontext.New(t) defer ctx.Cleanup() store, err := filestore.NewAt(ctx.Dir("store")) require.NoError(t, err) data := make([]byte, blobSize) _, _ = rand.Read(data) ref := storage.BlobRef{ Namespace: []byte{0}, Key: []byte{1}, } writer, err := store.Create(ctx, ref, -1) require.NoError(t, err) _, err = writer.Write(data) require.NoError(t, err) // loading uncommitted file should fail _, err = store.Open(ctx, ref) require.Error(t, err, "loading uncommitted file should fail") // commit the file err = writer.Commit() require.NoError(t, err, "commit the file") // open a reader reader, err := store.Open(ctx, ref) require.NoError(t, err, "open a reader") // double close, just in case defer func() { _ = reader.Close() }() // delete while reading err = store.Delete(ctx, ref) require.NoError(t, err, "delete while reading") // opening deleted file should fail _, err = store.Open(ctx, ref) require.Error(t, err, "opening deleted file should fail") // read all content result, err := ioutil.ReadAll(reader) require.NoError(t, err, "read all content") // finally close reader err = reader.Close() require.NoError(t, err) // should be able to read the full content require.Equal(t, data, result) // collect trash _ = store.GarbageCollect(ctx) // flaky test, for checking whether files have been actually deleted from disk err = filepath.Walk(ctx.Dir("store"), func(path string, info os.FileInfo, err error) error { if info.IsDir() { return nil } return errors.New("found file " + path) }) if err != nil { t.Fatal(err) } }