diff --git a/go.mod b/go.mod index 6cc49d4..6bb573b 100644 --- a/go.mod +++ b/go.mod @@ -14,5 +14,6 @@ require ( github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible github.com/sirupsen/logrus v1.8.1 + github.com/stretchr/testify v1.7.0 golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f ) diff --git a/go.sum b/go.sum index ebf04cd..d87d45f 100644 --- a/go.sum +++ b/go.sum @@ -67,6 +67,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f h1:aZp0e2vLN4MToVqnjNEYEtrEA8RH8U8FN1CU7JgqsPU= @@ -94,3 +96,5 @@ gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/s3/mock_test.go b/internal/s3/mock_test.go new file mode 100644 index 0000000..f2747f8 --- /dev/null +++ b/internal/s3/mock_test.go @@ -0,0 +1,162 @@ +package s3_test + +import ( + "context" + "io/ioutil" + "strings" + "testing" + "time" + + "git.kapelle.org/niklas/s3browser/internal/s3" + "git.kapelle.org/niklas/s3browser/internal/types" + "github.com/stretchr/testify/assert" +) + +func setup(t *testing.T) (s3.S3Service, context.Context, *assert.Assertions) { + service, _ := s3.NewMockS3([]string{"bucket1", "bucket2"}) + ctx := context.Background() + assert := assert.New(t) + + return service, ctx, assert +} + +func TestBuckets(t *testing.T) { + s3, ctx, assert := setup(t) + + buckets, err := s3.ListBuckets(ctx) + assert.NoError(err) + + assert.Len(buckets, 2) + + assert.Contains(buckets, "bucket1") + assert.Contains(buckets, "bucket2") +} + +func TestPut(t *testing.T) { + s3, ctx, assert := setup(t) + content := "FileContent" + + err := s3.PutObject(ctx, *types.ParseID("bucket1:/file1"), strings.NewReader(content), int64(len(content))) + assert.NoError(err) +} + +func TestPutAndGet(t *testing.T) { + s3, ctx, assert := setup(t) + + content := "FileContent" + id := *types.ParseID("bucket1:/file1") + + err := s3.PutObject(ctx, *types.ParseID("bucket1:/file1"), strings.NewReader(content), int64(len(content))) + assert.NoError(err) + + reader, err := s3.GetObject(ctx, id) + assert.NoError(err) + + readerContent, err := ioutil.ReadAll(reader) + assert.NoError(err) + + assert.Equal(content, string(readerContent)) +} + +func TestStat(t *testing.T) { + s3, ctx, assert := setup(t) + + content := "FileContent" + id := *types.ParseID("bucket1:/file1") + + now := time.Now() + + err := s3.PutObject(ctx, id, strings.NewReader(content), int64(len(content))) + assert.NoError(err) + + obj, err := s3.StatObject(ctx, id) + assert.NoError(err) + + assert.Equal(id.String(), obj.ID.String()) + assert.Equal(int64(len(content)), obj.Size) + assert.NotEmpty(obj.ETag) + assert.WithinDuration(now, obj.LastModified, time.Second*1) +} + +func TestRemove(t *testing.T) { + s3, ctx, assert := setup(t) + + content := "FileContent" + id := *types.ParseID("bucket1:/file1") + + err := s3.PutObject(ctx, id, strings.NewReader(content), int64(len(content))) + assert.NoError(err) + + err = s3.RemoveObject(ctx, id) + assert.NoError(err) + + _, err = s3.StatObject(ctx, id) + assert.Error(err) +} + +func TestList(t *testing.T) { + s3, ctx, assert := setup(t) + + content1 := "FileContent1" + id1 := *types.ParseID("bucket1:/file1") + + err := s3.PutObject(ctx, id1, strings.NewReader(content1), int64(len(content1))) + assert.NoError(err) + + content2 := "FileContent2" + id2 := *types.ParseID("bucket1:/file2") + + err = s3.PutObject(ctx, id2, strings.NewReader(content2), int64(len(content2))) + assert.NoError(err) + + listID := types.ParseID("bucket1:/") + + objects, err := s3.ListObjects(ctx, *listID) + assert.NoError(err) + + assert.Len(objects, 2) +} + +func TestListRecursive(t *testing.T) { + s3, ctx, assert := setup(t) + + s3.PutObject(ctx, *types.ParseID("bucket1:/file1"), strings.NewReader("content"), int64(len("content"))) + s3.PutObject(ctx, *types.ParseID("bucket1:/path1/file1"), strings.NewReader("content"), int64(len("content"))) + s3.PutObject(ctx, *types.ParseID("bucket1:/path1/file2"), strings.NewReader("content"), int64(len("content"))) + s3.PutObject(ctx, *types.ParseID("bucket1:/path1/path2/file1"), strings.NewReader("content"), int64(len("content"))) + s3.PutObject(ctx, *types.ParseID("bucket1:/path3/path4/file1"), strings.NewReader("content"), int64(len("content"))) + + objects, err := s3.ListObjectsRecursive(ctx, *types.ParseID("bucket1:/path1/")) + assert.NoError(err) + assert.Len(objects, 3) +} + +func TestCopy(t *testing.T) { + s3, ctx, assert := setup(t) + + id1 := *types.ParseID("bucket1:/file1") + id2 := *types.ParseID("bucket1:/file2") + + s3.PutObject(ctx, id1, strings.NewReader("content"), int64(len("content"))) + + err := s3.CopyObject(ctx, id1, id2) + assert.NoError(err) + + obj1, err := s3.StatObject(ctx, id1) + assert.NoError(err) + assert.NotNil(obj1) + + obj2, err := s3.StatObject(ctx, id1) + assert.NoError(err) + assert.NotNil(obj2) + + assert.Equal(obj1.ETag, obj2.ETag) + assert.Equal(obj1.Size, obj2.Size) + + obj2Reader, err := s3.GetObject(ctx, id2) + assert.NoError(err) + + obj2Content, err := ioutil.ReadAll(obj2Reader) + assert.NoError(err) + assert.Equal([]byte("content"), obj2Content) +} diff --git a/internal/types/id_test.go b/internal/types/id_test.go new file mode 100644 index 0000000..0b41b57 --- /dev/null +++ b/internal/types/id_test.go @@ -0,0 +1,109 @@ +package types_test + +import ( + "testing" + + "git.kapelle.org/niklas/s3browser/internal/types" + "github.com/stretchr/testify/assert" +) + +// TODO: test version component (not yet used in code) + +func TestIDParse(t *testing.T) { + assert := assert.New(t) + + id := types.ParseID("test:/path/key") + + assert.NotNil(id) + assert.True(id.Valid()) + assert.Equal("test", id.Bucket) + assert.Equal("/path/key", id.Key) + assert.False(id.IsDirectory()) + assert.Equal("key", id.Name()) + assert.Equal("test:/path/key", id.String()) +} + +func TestIDParseInvalid(t *testing.T) { + assert := assert.New(t) + + assert.Nil(types.ParseID("/asd/ad")) + assert.Nil(types.ParseID("test")) + assert.Nil(types.ParseID("test:")) + assert.Nil(types.ParseID("")) + assert.Nil(types.ParseID("/")) +} + +func TestIDIsDir(t *testing.T) { + assert := assert.New(t) + + idFile := types.ParseID("test:/path/key") + assert.NotNil(idFile) + assert.False(idFile.IsDirectory()) + + idDir := types.ParseID("test:/path/key/") + assert.NotNil(idDir) + assert.True(idDir.IsDirectory()) +} + +func TestIDRoot(t *testing.T) { + assert := assert.New(t) + + id := types.ParseID("test:/") + + assert.NotNil(id) + assert.True(id.Valid()) + assert.Equal("test", id.Bucket) + assert.Equal("/", id.Key) + assert.True(id.IsDirectory()) + assert.Equal("/", id.Name()) + assert.Equal("test:/", id.String()) + + assert.Nil(id.Parent()) +} + +func TestIDParentFromFile(t *testing.T) { + assert := assert.New(t) + + id := types.ParseID("test:/path1/path2/key") + + assert.NotNil(id) + + parent := id.Parent() + + assert.NotNil(parent) + assert.True(parent.Valid()) + assert.Equal("test", parent.Bucket) + assert.Equal("/path1/path2/", parent.Key) + assert.True(parent.IsDirectory()) + assert.Equal("path2", parent.Name()) + assert.Equal("test:/path1/path2/", parent.String()) +} + +func TestIDParentFromDir(t *testing.T) { + assert := assert.New(t) + + id := types.ParseID("test:/path1/path2/") + + assert.NotNil(id) + + parent := id.Parent() + + assert.NotNil(parent) + assert.True(parent.Valid()) + assert.Equal("test", parent.Bucket) + assert.Equal("/path1/", parent.Key) + assert.True(parent.IsDirectory()) + assert.Equal("path1", parent.Name()) + assert.Equal("test:/path1/", parent.String()) +} + +func TestIDParentRoot(t *testing.T) { + assert := assert.New(t) + + id := types.ParseID("test:/key1") + + parent := id.Parent() + + assert.NotNil(parent) + assert.Equal("/", parent.Key) +}