package filestore import ( "errors" "fmt" "io" "strings" "testing" "github.com/stretchr/testify/require" ) const filecontent = "the content of my file" type errorReader struct{} func (errorReader) Read(_ []byte) (int, error) { return 0, errors.New("reading error") } var _ io.Reader = errorReader{} func nameGenAddress(f NameGenerator) string { return fmt.Sprintf("%p", f) } func TestFilestore(t *testing.T) { t.Parallel() const bucket = "foo" t.Run("Initialize a store", func(t *testing.T) { t.Parallel() assert := require.New(t) fs, err := NewFilestore() assert.NoError(err) assert.NotNil(fs) }) t.Run("Selecting a new bucket", func(t *testing.T) { t.Parallel() assert := require.New(t) fs, err := NewFilestore() assert.NoError(err) b := fs.Bucket(bucket) assert.NotNil(b) }) t.Run("Select again a bucket", func(t *testing.T) { t.Parallel() assert := require.New(t) fs, err := NewFilestore() assert.NoError(err) b := fs.Bucket(bucket) assert.NotNil(b) b2 := fs.Bucket(bucket) assert.NotNil(b2) assert.Same(b, b2) }) t.Run("The Default name generator is identity", func(t *testing.T) { t.Parallel() assert := require.New(t) fs, err := NewFilestore() assert.NoError(err) assert.Equal(nameGenAddress(NameIdentity), nameGenAddress(fs.nameGenerator)) }) t.Run("A custom name generator can be used", func(t *testing.T) { t.Parallel() nameGen := func(s string) (string, error) { return s, nil } assert := require.New(t) fs, err := NewFilestore(WithNameGen(nameGen)) assert.NoError(err) assert.Equal(nameGenAddress(nameGen), nameGenAddress(fs.nameGenerator)) }) t.Run("The Default backend is memory", func(t *testing.T) { t.Parallel() assert := require.New(t) fs, err := NewFilestore() assert.NoError(err) assert.IsType(fs.dataDir, &MemoryBackend{}) }) t.Run("A custom backend can be used", func(t *testing.T) { t.Parallel() assert := require.New(t) fs, err := NewFilestore(WithBackend(NewOSBackend("foo"))) assert.NoError(err) assert.IsType(fs.dataDir, &OSBackend{}) }) } func TestBucket(t *testing.T) { t.Parallel() const bucket = "foo" t.Run("the name generator of a bucket can be overloaded", func(t *testing.T) { t.Parallel() nameGen := func(s string) (string, error) { return s, nil } assert := require.New(t) fs, err := NewFilestore() assert.NoError(err) b := fs.Bucket(bucket) assert.NotNil(b) b.SetNameGenerator(nameGen) assert.NotEqual(nameGenAddress(fs.nameGenerator), nameGenAddress(b.nameGenerator)) assert.Equal(nameGenAddress(nameGen), nameGenAddress(b.nameGenerator)) }) t.Run("the backend is the same as the file store", func(t *testing.T) { t.Parallel() assert := require.New(t) fs, err := NewFilestore() assert.NoError(err) b := fs.Bucket(bucket) assert.NotNil(b) assert.IsType(b.dataDir, fs.dataDir) fs, err = NewFilestore(WithBackend(NewOSBackend("foo"))) assert.NoError(err) b = fs.Bucket(bucket) assert.NotNil(b) assert.IsType(b.dataDir, fs.dataDir) }) } func TestBucketPut(t *testing.T) { t.Parallel() const ( bucket = "foo" filename = "foo.bar" ) t.Run("It should store a file", func(t *testing.T) { t.Parallel() assert := require.New(t) fs, err := NewFilestore() assert.NoError(err) b := fs.Bucket(bucket) fr := strings.NewReader(filecontent) name, err := b.Put(filename, fr) assert.NoError(err, "cannot store file") assert.Equal("foo.bar", name) assert.True(b.Exists(name), "the file does not exist in the store") }) t.Run("It returns an error if the name cannot be generated", func(t *testing.T) { t.Parallel() errNameGen := func(_ string) (string, error) { return "", errFilenameGeneration } assert := require.New(t) fs, err := NewFilestore(WithNameGen(errNameGen)) assert.NoError(err) fr := strings.NewReader(filecontent) name, err := fs.Bucket(bucket).Put(filename, fr) assert.Error(err) assert.Equal("", name) }) t.Run("It returns an error if the file cannot be stored", func(t *testing.T) { t.Parallel() t.Skip() assert := require.New(t) fs, err := NewFilestore() assert.NoError(err) fr := strings.NewReader(filecontent) name, err := fs.Bucket(bucket).Put(filename, fr) assert.Error(err) assert.Equal("", name) }) t.Run("It returns an error if the file cannot be written", func(t *testing.T) { t.Parallel() assert := require.New(t) fs, err := NewFilestore() assert.NoError(err) fr := errorReader{} name, err := fs.Bucket(bucket).Put(filename, fr) assert.Error(err) assert.Equal("", name) }) } func TestBucketGet(t *testing.T) { t.Parallel() const ( bucket = "foo" filename = "foo.bar" ) t.Run("It should retrieve a file", func(t *testing.T) { t.Parallel() assert := require.New(t) fs, err := NewFilestore() assert.NoError(err) b := fs.Bucket(bucket) r := strings.NewReader(filecontent) _, err = b.Put(filename, r) assert.NoError(err) rc, err := b.Get(filename) assert.NoError(err) defer func() { _ = rc.Close() }() content, err := io.ReadAll(rc) assert.NoError(err) assert.Equal(filecontent, string(content)) }) t.Run("It returns an error if the file is not found", func(t *testing.T) { t.Parallel() assert := require.New(t) fs, err := NewFilestore() assert.NoError(err) b := fs.Bucket(bucket) r, err := b.Get(filename) assert.ErrorIs(err, errFileNotFound) assert.Nil(r) }) t.Run("It returns an error if the file cannot be read", func(t *testing.T) { t.Parallel() t.Skip() assert := require.New(t) fs, err := NewFilestore() assert.NoError(err) b := fs.Bucket(bucket) r, err := b.Get(filename) assert.Error(err) assert.Nil(r) }) } func TestBucketExists(t *testing.T) { t.Parallel() const ( bucket = "foo" filename = "foo.bar" ) t.Run("It returns true if the file exists", func(t *testing.T) { t.Parallel() assert := require.New(t) fs, err := NewFilestore() assert.NoError(err) b := fs.Bucket(bucket) wc, err := b.dataDir.Create(filename) assert.NoError(err) wc.Close() assert.True(b.Exists(filename)) }) t.Run("It returns false if the file does not exist", func(t *testing.T) { t.Parallel() assert := require.New(t) fs, err := NewFilestore() assert.NoError(err) b := fs.Bucket(bucket) assert.False(b.Exists(filename)) }) } func TestBucketDelete(t *testing.T) { t.Parallel() const ( bucket = "foo" filename = "foo.bar" ) t.Run("Removing a file", func(t *testing.T) { t.Parallel() assert := require.New(t) fs, err := NewFilestore() assert.NoError(err) fr := strings.NewReader(filecontent) name, err := fs.Bucket(bucket).Put(filename, fr) assert.NoError(err) defer fs.Bucket(bucket).Delete(filename) err = fs.Bucket(bucket).Delete(name) assert.NoError(err) assert.False(fs.Bucket(bucket).Exists(name)) }) t.Run("Removing a file that do not exists", func(t *testing.T) { t.Parallel() assert := require.New(t) fs, err := NewFilestore() assert.NoError(err) err = fs.Bucket(bucket).Delete(filename) assert.NoError(err) }) t.Run("Removing a file with errors", func(t *testing.T) { t.Skip() t.Parallel() assert := require.New(t) fs, err := NewFilestore() assert.NoError(err) err = fs.Bucket(bucket).Delete(filename) assert.Error(err) }) } func TestIdentityNameGenerator(t *testing.T) { t.Parallel() name, err := NameIdentity("foo.txt") require.NoError(t, err) require.Equal(t, "foo.txt", name) }