mirror of https://github.com/minio/minio.git
tests: add unit test for DeleteMultipleObjectsHandler. (#3267)
Fixes #3058
This commit is contained in:
parent
1b85302161
commit
61d67a061c
|
@ -19,8 +19,10 @@ package cmd
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -607,3 +609,165 @@ func testListBucketsHandler(obj ObjectLayer, instanceType, bucketName string, ap
|
|||
// `ExecObjectLayerAPINilTest` manages the operation.
|
||||
ExecObjectLayerAPINilTest(t, "", "", instanceType, apiRouter, nilReq)
|
||||
}
|
||||
|
||||
// Wrapper for calling DeleteMultipleObjects HTTP handler tests for both XL multiple disks and single node setup.
|
||||
func TestAPIDeleteMultipleObjectsHandler(t *testing.T) {
|
||||
ExecObjectLayerAPITest(t, testAPIDeleteMultipleObjectsHandler, []string{"DeleteMultipleObjects"})
|
||||
}
|
||||
|
||||
func testAPIDeleteMultipleObjectsHandler(obj ObjectLayer, instanceType, bucketName string, apiRouter http.Handler,
|
||||
credentials credential, t *testing.T) {
|
||||
initBucketPolicies(obj)
|
||||
|
||||
var err error
|
||||
// register event notifier.
|
||||
err = initEventNotifier(obj)
|
||||
if err != nil {
|
||||
t.Fatal("Notifier initialization failed.")
|
||||
}
|
||||
|
||||
contentBytes := []byte("hello")
|
||||
sha256sum := ""
|
||||
var objectNames []string
|
||||
for i := 0; i < 10; i++ {
|
||||
objectName := "test-object-" + strconv.Itoa(i)
|
||||
// uploading the object.
|
||||
_, err = obj.PutObject(bucketName, objectName, int64(len(contentBytes)), bytes.NewBuffer(contentBytes),
|
||||
make(map[string]string), sha256sum)
|
||||
// if object upload fails stop the test.
|
||||
if err != nil {
|
||||
t.Fatalf("Put Object %d: Error uploading object: <ERROR> %v", i, err)
|
||||
}
|
||||
|
||||
// object used for the test.
|
||||
objectNames = append(objectNames, objectName)
|
||||
}
|
||||
|
||||
getObjectIdentifierList := func(objectNames []string) (objectIdentifierList []ObjectIdentifier) {
|
||||
for _, objectName := range objectNames {
|
||||
objectIdentifierList = append(objectIdentifierList, ObjectIdentifier{objectName})
|
||||
}
|
||||
|
||||
return objectIdentifierList
|
||||
}
|
||||
|
||||
requestList := []DeleteObjectsRequest{
|
||||
{Quiet: false, Objects: getObjectIdentifierList(objectNames[:5])},
|
||||
{Quiet: true, Objects: getObjectIdentifierList(objectNames[5:])},
|
||||
}
|
||||
|
||||
// generate multi objects delete response.
|
||||
successRequest0 := encodeResponse(requestList[0])
|
||||
successResponse0 := generateMultiDeleteResponse(requestList[0].Quiet, requestList[0].Objects, nil)
|
||||
encodedSuccessResponse0 := encodeResponse(successResponse0)
|
||||
|
||||
successRequest1 := encodeResponse(requestList[1])
|
||||
successResponse1 := generateMultiDeleteResponse(requestList[1].Quiet, requestList[1].Objects, nil)
|
||||
encodedSuccessResponse1 := encodeResponse(successResponse1)
|
||||
|
||||
// generate multi objects delete response for errors.
|
||||
// errorRequest := encodeResponse(requestList[1])
|
||||
errorResponse := generateMultiDeleteResponse(requestList[1].Quiet, requestList[1].Objects, nil)
|
||||
encodedErrorResponse := encodeResponse(errorResponse)
|
||||
|
||||
testCases := []struct {
|
||||
bucket string
|
||||
objects []byte
|
||||
accessKey string
|
||||
secretKey string
|
||||
expectedContent []byte
|
||||
expectedRespStatus int
|
||||
}{
|
||||
// Test case - 1.
|
||||
// Delete objects with invalid access key.
|
||||
{
|
||||
bucket: bucketName,
|
||||
objects: successRequest0,
|
||||
accessKey: "Invalid-AccessID",
|
||||
secretKey: credentials.SecretAccessKey,
|
||||
expectedContent: nil,
|
||||
expectedRespStatus: http.StatusForbidden,
|
||||
},
|
||||
// Test case - 2.
|
||||
// Delete valid objects with quiet flag off.
|
||||
{
|
||||
bucket: bucketName,
|
||||
objects: successRequest0,
|
||||
accessKey: credentials.AccessKeyID,
|
||||
secretKey: credentials.SecretAccessKey,
|
||||
expectedContent: encodedSuccessResponse0,
|
||||
expectedRespStatus: http.StatusOK,
|
||||
},
|
||||
// Test case - 3.
|
||||
// Delete valid objects with quiet flag on.
|
||||
{
|
||||
bucket: bucketName,
|
||||
objects: successRequest1,
|
||||
accessKey: credentials.AccessKeyID,
|
||||
secretKey: credentials.SecretAccessKey,
|
||||
expectedContent: encodedSuccessResponse1,
|
||||
expectedRespStatus: http.StatusOK,
|
||||
},
|
||||
// Test case - 4.
|
||||
// Delete previously deleted objects.
|
||||
{
|
||||
bucket: bucketName,
|
||||
objects: successRequest1,
|
||||
accessKey: credentials.AccessKeyID,
|
||||
secretKey: credentials.SecretAccessKey,
|
||||
expectedContent: encodedErrorResponse,
|
||||
expectedRespStatus: http.StatusOK,
|
||||
},
|
||||
}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
var req *http.Request
|
||||
var actualContent []byte
|
||||
|
||||
// Indicating that all parts are uploaded and initiating completeMultipartUpload.
|
||||
req, err = newTestSignedRequestV4("POST", getDeleteMultipleObjectsURL("", bucketName),
|
||||
int64(len(testCase.objects)), bytes.NewReader(testCase.objects), testCase.accessKey, testCase.secretKey)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create HTTP request for DeleteMultipleObjects: <ERROR> %v", err)
|
||||
}
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler.
|
||||
// Call the ServeHTTP to executes the registered handler.
|
||||
apiRouter.ServeHTTP(rec, req)
|
||||
// Assert the response code with the expected status.
|
||||
if rec.Code != testCase.expectedRespStatus {
|
||||
t.Errorf("Case %d: Minio %s: Expected the response status to be `%d`, but instead found `%d`", i+1, instanceType, testCase.expectedRespStatus, rec.Code)
|
||||
}
|
||||
|
||||
// read the response body.
|
||||
actualContent, err = ioutil.ReadAll(rec.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("Test %d : Minio %s: Failed parsing response body: <ERROR> %v", i+1, instanceType, err)
|
||||
}
|
||||
|
||||
// Verify whether the bucket obtained object is same as the one created.
|
||||
if testCase.expectedContent != nil && !bytes.Equal(testCase.expectedContent, actualContent) {
|
||||
t.Errorf("Test %d : Minio %s: Object content differs from expected value.", i+1, instanceType)
|
||||
}
|
||||
}
|
||||
|
||||
// Currently anonymous user cannot delete multiple objects in Minio server, hence no test case is required.
|
||||
|
||||
// HTTP request to test the case of `objectLayer` being set to `nil`.
|
||||
// There is no need to use an existing bucket or valid input for creating the request,
|
||||
// since the `objectLayer==nil` check is performed before any other checks inside the handlers.
|
||||
// The only aim is to generate an HTTP request in a way that the relevant/registered end point is evoked/called.
|
||||
// Indicating that all parts are uploaded and initiating completeMultipartUpload.
|
||||
nilBucket := "dummy-bucket"
|
||||
nilObject := ""
|
||||
|
||||
nilReq, err := newTestSignedRequestV4("POST", getDeleteMultipleObjectsURL("", nilBucket), 0, nil, "", "")
|
||||
if err != nil {
|
||||
t.Errorf("Minio %s: Failed to create HTTP request for testing the response when object Layer is set to `nil`.", instanceType)
|
||||
}
|
||||
// execute the object layer set to `nil` test.
|
||||
// `ExecObjectLayerAPINilTest` manages the operation.
|
||||
ExecObjectLayerAPINilTest(t, nilBucket, nilObject, instanceType, apiRouter, nilReq)
|
||||
}
|
||||
|
|
|
@ -1416,6 +1416,13 @@ func getDeleteBucketURL(endPoint, bucketName string) string {
|
|||
return makeTestTargetURL(endPoint, bucketName, "", url.Values{})
|
||||
}
|
||||
|
||||
// return URL for deleting the bucket.
|
||||
func getDeleteMultipleObjectsURL(endPoint, bucketName string) string {
|
||||
queryValue := url.Values{}
|
||||
queryValue.Set("delete", "")
|
||||
return makeTestTargetURL(endPoint, bucketName, "", queryValue)
|
||||
}
|
||||
|
||||
// return URL For fetching location of the bucket.
|
||||
func getBucketLocationURL(endPoint, bucketName string) string {
|
||||
queryValue := url.Values{}
|
||||
|
@ -1970,6 +1977,9 @@ func registerBucketLevelFunc(bucket *router.Router, api objectAPIHandlers, apiFu
|
|||
case "HeadBucket":
|
||||
// Register HeadBucket handler.
|
||||
bucket.Methods("HEAD").HandlerFunc(api.HeadBucketHandler)
|
||||
case "DeleteMultipleObjects":
|
||||
// Register DeleteMultipleObjects handler.
|
||||
bucket.Methods("POST").HandlerFunc(api.DeleteMultipleObjectsHandler).Queries("delete", "")
|
||||
case "NewMultipart":
|
||||
// Register New Multipart upload handler.
|
||||
bucket.Methods("POST").Path("/{object:.+}").HandlerFunc(api.NewMultipartUploadHandler).Queries("uploads", "")
|
||||
|
|
Loading…
Reference in New Issue