diff options
Diffstat (limited to 'workhorse/internal/upload/destination/objectstore/test/objectstore_stub_test.go')
-rw-r--r-- | workhorse/internal/upload/destination/objectstore/test/objectstore_stub_test.go | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/workhorse/internal/upload/destination/objectstore/test/objectstore_stub_test.go b/workhorse/internal/upload/destination/objectstore/test/objectstore_stub_test.go new file mode 100644 index 00000000000..8c0d52a2d79 --- /dev/null +++ b/workhorse/internal/upload/destination/objectstore/test/objectstore_stub_test.go @@ -0,0 +1,167 @@ +package test + +import ( + "fmt" + "io" + "net/http" + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +func doRequest(method, url string, body io.Reader) error { + req, err := http.NewRequest(method, url, body) + if err != nil { + return err + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + + return resp.Body.Close() +} + +func TestObjectStoreStub(t *testing.T) { + stub, ts := StartObjectStore() + defer ts.Close() + + require.Equal(t, 0, stub.PutsCnt()) + require.Equal(t, 0, stub.DeletesCnt()) + + objectURL := ts.URL + ObjectPath + + require.NoError(t, doRequest(http.MethodPut, objectURL, strings.NewReader(ObjectContent))) + + require.Equal(t, 1, stub.PutsCnt()) + require.Equal(t, 0, stub.DeletesCnt()) + require.Equal(t, ObjectMD5, stub.GetObjectMD5(ObjectPath)) + + require.NoError(t, doRequest(http.MethodDelete, objectURL, nil)) + + require.Equal(t, 1, stub.PutsCnt()) + require.Equal(t, 1, stub.DeletesCnt()) +} + +func TestObjectStoreStubDelete404(t *testing.T) { + stub, ts := StartObjectStore() + defer ts.Close() + + objectURL := ts.URL + ObjectPath + + req, err := http.NewRequest(http.MethodDelete, objectURL, nil) + require.NoError(t, err) + + resp, err := http.DefaultClient.Do(req) + require.NoError(t, err) + defer resp.Body.Close() + require.Equal(t, 404, resp.StatusCode) + + require.Equal(t, 0, stub.DeletesCnt()) +} + +func TestObjectStoreInitiateMultipartUpload(t *testing.T) { + stub, ts := StartObjectStore() + defer ts.Close() + + path := "/my-multipart" + err := stub.InitiateMultipartUpload(path) + require.NoError(t, err) + + err = stub.InitiateMultipartUpload(path) + require.Error(t, err, "second attempt to open the same MultipartUpload") +} + +func TestObjectStoreCompleteMultipartUpload(t *testing.T) { + stub, ts := StartObjectStore() + defer ts.Close() + + objectURL := ts.URL + ObjectPath + parts := []struct { + number int + content string + contentMD5 string + }{ + { + number: 1, + content: "first part", + contentMD5: "550cf6b6e60f65a0e3104a26e70fea42", + }, { + number: 2, + content: "second part", + contentMD5: "920b914bca0a70780b40881b8f376135", + }, + } + + stub.InitiateMultipartUpload(ObjectPath) + + require.True(t, stub.IsMultipartUpload(ObjectPath)) + require.Equal(t, 0, stub.PutsCnt()) + require.Equal(t, 0, stub.DeletesCnt()) + + // Workhorse knows nothing about S3 MultipartUpload, it receives some URLs + // from GitLab-rails and PUTs chunk of data to each of them. + // Then it completes the upload with a final POST + partPutURLs := []string{ + fmt.Sprintf("%s?partNumber=%d", objectURL, 1), + fmt.Sprintf("%s?partNumber=%d", objectURL, 2), + } + completePostURL := objectURL + + for i, partPutURL := range partPutURLs { + part := parts[i] + + require.NoError(t, doRequest(http.MethodPut, partPutURL, strings.NewReader(part.content))) + + require.Equal(t, i+1, stub.PutsCnt()) + require.Equal(t, 0, stub.DeletesCnt()) + require.Equal(t, part.contentMD5, stub.multipart[ObjectPath][part.number], "Part %d was not uploaded into ObjectStorage", part.number) + require.Empty(t, stub.GetObjectMD5(ObjectPath), "Part %d was mistakenly uploaded as a single object", part.number) + require.True(t, stub.IsMultipartUpload(ObjectPath), "MultipartUpload completed or aborted") + } + + completeBody := fmt.Sprintf(`<CompleteMultipartUpload> + <Part> + <PartNumber>1</PartNumber> + <ETag>%s</ETag> + </Part> + <Part> + <PartNumber>2</PartNumber> + <ETag>%s</ETag> + </Part> + </CompleteMultipartUpload>`, parts[0].contentMD5, parts[1].contentMD5) + require.NoError(t, doRequest(http.MethodPost, completePostURL, strings.NewReader(completeBody))) + + require.Equal(t, len(parts), stub.PutsCnt()) + require.Equal(t, 0, stub.DeletesCnt()) + require.False(t, stub.IsMultipartUpload(ObjectPath), "MultipartUpload is still in progress") +} + +func TestObjectStoreAbortMultipartUpload(t *testing.T) { + stub, ts := StartObjectStore() + defer ts.Close() + + stub.InitiateMultipartUpload(ObjectPath) + + require.True(t, stub.IsMultipartUpload(ObjectPath)) + require.Equal(t, 0, stub.PutsCnt()) + require.Equal(t, 0, stub.DeletesCnt()) + + objectURL := ts.URL + ObjectPath + require.NoError(t, doRequest(http.MethodPut, fmt.Sprintf("%s?partNumber=%d", objectURL, 1), strings.NewReader(ObjectContent))) + + require.Equal(t, 1, stub.PutsCnt()) + require.Equal(t, 0, stub.DeletesCnt()) + require.Equal(t, ObjectMD5, stub.multipart[ObjectPath][1], "Part was not uploaded into ObjectStorage") + require.Empty(t, stub.GetObjectMD5(ObjectPath), "Part was mistakenly uploaded as a single object") + require.True(t, stub.IsMultipartUpload(ObjectPath), "MultipartUpload completed or aborted") + + require.NoError(t, doRequest(http.MethodDelete, objectURL, nil)) + + require.Equal(t, 1, stub.PutsCnt()) + require.Equal(t, 1, stub.DeletesCnt()) + require.Empty(t, stub.GetObjectMD5(ObjectPath), "MultiUpload has been completed") + require.False(t, stub.IsMultipartUpload(ObjectPath), "MultiUpload is still in progress") +} |