From a8105ec0681720d61fd75d4e25df8857c624e37d Mon Sep 17 00:00:00 2001 From: Karthic Rao Date: Fri, 7 Oct 2016 02:04:33 +0530 Subject: [PATCH] - Test utility function for easy asserting of cases wherein objectLayer (#2865) is `nil` in API handlers. - Remove the existing tests for the `nil` check and use the new method to test for object layer being `nil`. --- cmd/bucket-policy-handlers_test.go | 49 ++++++ cmd/object-handlers.go | 2 +- cmd/object-handlers_test.go | 257 +++++++++++++++++++++-------- cmd/test-utils_test.go | 42 ++++- 4 files changed, 275 insertions(+), 75 deletions(-) diff --git a/cmd/bucket-policy-handlers_test.go b/cmd/bucket-policy-handlers_test.go index 1de064bfa..516cf940d 100644 --- a/cmd/bucket-policy-handlers_test.go +++ b/cmd/bucket-policy-handlers_test.go @@ -297,6 +297,22 @@ func testPutBucketPolicyHandler(obj ObjectLayer, instanceType, bucketName string t.Errorf("Test %d: %s: Expected the response status to be `%d`, but instead found `%d`", i+1, instanceType, testCase.expectedRespStatus, recV2.Code) } } + + // HTTP request for testing when `objectLayer` is set to `nil`. + // There is no need to use an existing bucket and 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. + nilBucket := "dummy-bucket" + + nilReq, err := newTestSignedRequestV4("PUT", getPutPolicyURL("", nilBucket), + 0, nil, "", "") + + if err != nil { + t.Errorf("Minio %s: Failed to create HTTP request for testing the reponse when object Layer is set to `nil`.", instanceType) + } + // execute the object layer set to `nil` test. + // `ExecObjectLayerAPINilTest` manages the operation. + ExecObjectLayerAPINilTest(t, nilBucket, "", instanceType, apiRouter, nilReq) } // Wrapper for calling Get Bucket Policy HTTP handler tests for both XL multiple disks and single node setup. @@ -425,6 +441,23 @@ func testGetBucketPolicyHandler(obj ObjectLayer, instanceType, bucketName string t.Errorf("Test %d: %s: Bucket policy differs from expected value.", i+1, instanceType) } } + + // HTTP request for testing when `objectLayer` is set to `nil`. + // There is no need to use an existing bucket and 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. + nilBucket := "dummy-bucket" + + nilReq, err := newTestSignedRequestV4("GET", getGetPolicyURL("", nilBucket), + 0, nil, "", "") + + if err != nil { + t.Errorf("Minio %s: Failed to create HTTP request for testing the reponse when object Layer is set to `nil`.", instanceType) + } + // execute the object layer set to `nil` test. + // `ExecObjectLayerAPINilTest` manages the operation. + ExecObjectLayerAPINilTest(t, nilBucket, "", instanceType, apiRouter, nilReq) + } // Wrapper for calling Delete Bucket Policy HTTP handler tests for both XL multiple disks and single node setup. @@ -574,6 +607,22 @@ func testDeleteBucketPolicyHandler(obj ObjectLayer, instanceType, bucketName str t.Fatalf("Case %d: Expected the response status to be `%d`, but instead found `%d`", i+1, testCase.expectedRespStatus, recV2.Code) } } + + // HTTP request for testing when `objectLayer` is set to `nil`. + // There is no need to use an existing bucket and 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. + nilBucket := "dummy-bucket" + + nilReq, err := newTestSignedRequestV4("DELETE", getDeletePolicyURL("", nilBucket), + 0, nil, "", "") + + if err != nil { + t.Errorf("Minio %s: Failed to create HTTP request for testing the reponse when object Layer is set to `nil`.", instanceType) + } + // execute the object layer set to `nil` test. + // `ExecObjectLayerAPINilTest` manages the operation. + ExecObjectLayerAPINilTest(t, nilBucket, "", instanceType, apiRouter, nilReq) } // TestBucketPolicyConditionMatch - Tests to validate whether bucket policy conditions match. diff --git a/cmd/object-handlers.go b/cmd/object-handlers.go index 8d147a3a4..d4187b08f 100644 --- a/cmd/object-handlers.go +++ b/cmd/object-handlers.go @@ -533,7 +533,7 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req /// Multipart objectAPIHandlers -// NewMultipartUploadHandler - New multipart upload +// NewMultipartUploadHandler - New multipart upload. func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r *http.Request) { var object, bucket string vars := mux.Vars(r) diff --git a/cmd/object-handlers_test.go b/cmd/object-handlers_test.go index 5ad2f388f..62aaf9871 100644 --- a/cmd/object-handlers_test.go +++ b/cmd/object-handlers_test.go @@ -171,6 +171,23 @@ func testAPIGetObjectHandler(obj ObjectLayer, instanceType, bucketName string, a t.Errorf("Test %d: %s: Object content differs from expected value.: %s", i+1, instanceType, string(actualContent)) } } + + // HTTP request for testing when `objectLayer` is set to `nil`. + // There is no need to use an existing bucket and 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. + + nilBucket := "dummy-bucket" + nilObject := "dummy-object" + nilReq, err := newTestSignedRequestV4("GET", getGetObjectURL("", nilBucket, nilObject), + 0, nil, "", "") + + if err != nil { + t.Errorf("Minio %s: Failed to create HTTP request for testing the reponse 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) } // Wrapper for calling PutObject API handler tests using streaming signature v4 for both XL multiple disks and FS single drive setup. @@ -391,6 +408,24 @@ func testAPIPutObjectHandler(obj ObjectLayer, instanceType, bucketName string, a } buffer.Reset() } + + // 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. + nilBucket := "dummy-bucket" + nilObject := "dummy-object" + + nilReq, err := newTestSignedRequestV4("PUT", getPutObjectURL("", nilBucket, nilObject), + 0, nil, "", "") + + if err != nil { + t.Errorf("Minio %s: Failed to create HTTP request for testing the reponse 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) + } // Wrapper for calling Copy Object API handler tests for both XL multiple disks and single node setup. @@ -499,10 +534,11 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string, } for i, testCase := range testCases { + var req *http.Request // initialize HTTP NewRecorder, this records any mutations to response writer inside the handler. rec := httptest.NewRecorder() // construct HTTP request for copy object. - req, err := newTestSignedRequestV4("PUT", getCopyObjectURL("", testCase.bucketName, testCase.newObjectName), + req, err = newTestSignedRequestV4("PUT", getCopyObjectURL("", testCase.bucketName, testCase.newObjectName), 0, nil, credentials.AccessKeyID, credentials.SecretAccessKey) if err != nil { @@ -532,6 +568,28 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string, buffers[0].Reset() } } + + // 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. + nilBucket := "dummy-bucket" + nilObject := "dummy-object" + + nilReq, err := newTestSignedRequestV4("PUT", getCopyObjectURL("", nilBucket, nilObject), + 0, nil, "", "") + + // Below is how CopyObjectHandler is registered. + // bucket.Methods("PUT").Path("/{object:.+}").HeadersRegexp("X-Amz-Copy-Source", ".*?(\\/|%2F).*?") + // Its necessary to set the "X-Amz-Copy-Source" header for the request to be accepted by the handler. + nilReq.Header.Set("X-Amz-Copy-Source", url.QueryEscape("/"+nilBucket+"/"+nilObject)) + if err != nil { + t.Errorf("Minio %s: Failed to create HTTP request for testing the reponse 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) + } // Wrapper for calling NewMultipartUpload tests for both XL multiple disks and single node setup. @@ -547,7 +605,8 @@ func testAPINewMultipartHandler(obj ObjectLayer, instanceType, bucketName string objectName := "test-object-new-multipart" rec := httptest.NewRecorder() // construct HTTP request for copy object. - req, err := newTestSignedRequestV4("POST", getNewMultipartURL("", bucketName, objectName), 0, nil, credentials.AccessKeyID, credentials.SecretAccessKey) + req, err := newTestSignedRequestV4("POST", getNewMultipartURL("", bucketName, objectName), + 0, nil, credentials.AccessKeyID, credentials.SecretAccessKey) if err != nil { t.Fatalf("Failed to create HTTP request for copy Object: %v", err) @@ -573,6 +632,23 @@ func testAPINewMultipartHandler(obj ObjectLayer, instanceType, bucketName string t.Fatalf("Invalid UploadID: %s", err) } + // 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. + nilBucket := "dummy-bucket" + nilObject := "dummy-object" + + nilReq, err := newTestSignedRequestV4("POST", getNewMultipartURL("", nilBucket, nilObject), + 0, nil, "", "") + + if err != nil { + t.Errorf("Minio %s: Failed to create HTTP request for testing the reponse 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) + } // Wrapper for calling NewMultipartUploadParallel tests for both XL multiple disks and single node setup. @@ -687,7 +763,8 @@ func testAPICompleteMultipartHandler(obj ObjectLayer, instanceType, bucketName s } // Iterating over creatPartCases to generate multipart chunks. for _, part := range parts { - _, err = obj.PutObjectPart(part.bucketName, part.objName, part.uploadID, part.PartID, part.intputDataSize, bytes.NewBufferString(part.inputReaderData), part.inputMd5, "") + _, err = obj.PutObjectPart(part.bucketName, part.objName, part.uploadID, part.PartID, part.intputDataSize, + bytes.NewBufferString(part.inputReaderData), part.inputMd5, "") if err != nil { t.Fatalf("%s : %s", instanceType, err) } @@ -737,11 +814,13 @@ func testAPICompleteMultipartHandler(obj ObjectLayer, instanceType, bucketName s }, }, } + // on succesfull complete multipart operation the s3MD5 for the parts uploaded will be returned. s3MD5, err := completeMultipartMD5(inputParts[3].parts...) if err != nil { t.Fatalf("Obtaining S3MD5 failed") } + // generating the response body content for the success case. successResponse := generateCompleteMultpartUploadResponse(bucketName, objectName, getGetObjectURL("", bucketName, objectName), s3MD5) encodedSuccessResponse := encodeResponse(successResponse) @@ -771,22 +850,24 @@ func testAPICompleteMultipartHandler(obj ObjectLayer, instanceType, bucketName s // No parts specified in completePart{}. // Should return ErrMalformedXML in the response body. { - bucket: bucketName, - object: objectName, - uploadID: uploadIDs[0], - parts: []completePart{}, - expectedContent: encodeResponse(getAPIErrorResponse(getAPIError(ErrMalformedXML), getGetObjectURL("", bucketName, objectName))), + bucket: bucketName, + object: objectName, + uploadID: uploadIDs[0], + parts: []completePart{}, + expectedContent: encodeResponse(getAPIErrorResponse(getAPIError(ErrMalformedXML), + getGetObjectURL("", bucketName, objectName))), expectedRespStatus: http.StatusBadRequest, }, // Test case - 3. // Non-Existent uploadID. // 404 Not Found response status expected. { - bucket: bucketName, - object: objectName, - uploadID: "abc", - parts: inputParts[0].parts, - expectedContent: encodeResponse(getAPIErrorResponse(getAPIError(toAPIErrorCode(InvalidUploadID{UploadID: "abc"})), getGetObjectURL("", bucketName, objectName))), + bucket: bucketName, + object: objectName, + uploadID: "abc", + parts: inputParts[0].parts, + expectedContent: encodeResponse(getAPIErrorResponse(getAPIError(toAPIErrorCode(InvalidUploadID{UploadID: "abc"})), + getGetObjectURL("", bucketName, objectName))), expectedRespStatus: http.StatusNotFound, }, // Test case - 4. @@ -804,22 +885,24 @@ func testAPICompleteMultipartHandler(obj ObjectLayer, instanceType, bucketName s // Test case - 5. // TestCase with invalid Part Number. { - bucket: bucketName, - object: objectName, - uploadID: uploadIDs[0], - parts: inputParts[2].parts, - expectedContent: encodeResponse(getAPIErrorResponse(getAPIError(toAPIErrorCode(InvalidPart{})), getGetObjectURL("", bucketName, objectName))), + bucket: bucketName, + object: objectName, + uploadID: uploadIDs[0], + parts: inputParts[2].parts, + expectedContent: encodeResponse(getAPIErrorResponse(getAPIError(toAPIErrorCode(InvalidPart{})), + getGetObjectURL("", bucketName, objectName))), expectedRespStatus: http.StatusBadRequest, }, // Test case - 6. // Parts are not sorted according to the part number. // This should return ErrInvalidPartOrder in the response body. { - bucket: bucketName, - object: objectName, - uploadID: uploadIDs[0], - parts: inputParts[3].parts, - expectedContent: encodeResponse(getAPIErrorResponse(getAPIError(ErrInvalidPartOrder), getGetObjectURL("", bucketName, objectName))), + bucket: bucketName, + object: objectName, + uploadID: uploadIDs[0], + parts: inputParts[3].parts, + expectedContent: encodeResponse(getAPIErrorResponse(getAPIError(ErrInvalidPartOrder), + getGetObjectURL("", bucketName, objectName))), expectedRespStatus: http.StatusBadRequest, }, // Test case - 7. @@ -837,11 +920,12 @@ func testAPICompleteMultipartHandler(obj ObjectLayer, instanceType, bucketName s for i, testCase := range testCases { var req *http.Request + var completeBytes, actualContent []byte // Complete multipart upload parts. completeUploads := &completeMultipartUpload{ Parts: testCase.parts, } - completeBytes, err := xml.Marshal(completeUploads) + completeBytes, err = xml.Marshal(completeUploads) if err != nil { t.Fatalf("Error XML encoding of parts: %s.", err) } @@ -863,7 +947,7 @@ func testAPICompleteMultipartHandler(obj ObjectLayer, instanceType, bucketName s } // read the response body. - actualContent, err := ioutil.ReadAll(rec.Body) + actualContent, err = ioutil.ReadAll(rec.Body) if err != nil { t.Fatalf("Test %d : Minio %s: Failed parsing response body: %v", i+1, instanceType, err) } @@ -872,6 +956,24 @@ func testAPICompleteMultipartHandler(obj ObjectLayer, instanceType, bucketName s t.Errorf("Test %d : Minio %s: Object content differs from expected value.", i+1, instanceType) } } + + // 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 := "dummy-object" + + nilReq, err := newTestSignedRequestV4("POST", getCompleteMultipartUploadURL("", nilBucket, nilObject, "dummy-uploadID"), + 0, nil, "", "") + + if err != nil { + t.Errorf("Minio %s: Failed to create HTTP request for testing the reponse 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) } // Wrapper for calling Delete Object API handler tests for both XL multiple disks and FS single drive setup. @@ -962,6 +1064,24 @@ func testAPIDeleteOjectHandler(obj ObjectLayer, instanceType, bucketName string, t.Fatalf("Minio %s: Case %d: Expected the response status to be `%d`, but instead found `%d`", instanceType, i+1, testCase.expectedRespStatus, rec.Code) } } + + // 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 := "dummy-object" + + nilReq, err := newTestSignedRequestV4("DELETE", getDeleteObjectURL("", nilBucket, nilObject), + 0, nil, "", "") + + if err != nil { + t.Errorf("Minio %s: Failed to create HTTP request for testing the reponse 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) } func testAPIPutObjectPartHandlerPreSign(obj ObjectLayer, instanceType, bucketName string, apiRouter http.Handler, @@ -1312,7 +1432,8 @@ func testAPIPutObjectPartHandler(obj ObjectLayer, instanceType, bucketName strin } apiRouter.ServeHTTP(tRec, tReq) if test.expectedAPIError != noAPIErr { - errBytes, err := ioutil.ReadAll(tRec.Result().Body) + var errBytes []byte + errBytes, err = ioutil.ReadAll(tRec.Result().Body) if err != nil { t.Fatalf("Test %d %s Failed to read error response from upload part request %s/%s: %v", i+1, instanceType, bucketName, test.objectName, err) @@ -1329,35 +1450,30 @@ func testAPIPutObjectPartHandler(obj ObjectLayer, instanceType, bucketName strin } } } + + // HTTP request for testing when `objectLayer` is set to `nil`. + // There is no need to use an existing bucket and 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. + nilBucket := "dummy-bucket" + nilObject := "dummy-object" + + nilReq, err := newTestSignedRequestV4("PUT", + getPutObjectPartURL("", nilBucket, nilObject, "0", "0"), + 0, bytes.NewReader([]byte("testNilObjLayer")), "", "") + + if err != nil { + t.Errorf("Minio %s: Failed to create http request for testing the reponse 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) } func TestAPIPutObjectPartHandler(t *testing.T) { ExecObjectLayerAPITest(t, testAPIPutObjectPartHandler, []string{"PutObjectPart", "NewMultipart"}) } -func TestPutObjectPartNilObjAPI(t *testing.T) { - configDir, err := newTestConfig("us-east-1") - if err != nil { - t.Fatalf("Failed to create a test config: %v", err) - } - defer removeAll(configDir) - - rec := httptest.NewRecorder() - req, err := newTestSignedRequestV4("PUT", - getPutObjectPartURL("", "testbucket", "testobject", "uploadId1", "1"), - -1, bytes.NewReader([]byte("hello")), "abcd1", "abcd123") - if err != nil { - t.Fatal("Failed to create a signed UploadPart request.") - } - // Setup the 'nil' objectAPI router. - nilAPIRouter := initTestNilObjAPIEndPoints([]string{"PutObjectPart"}) - nilAPIRouter.ServeHTTP(rec, req) - serverNotInitializedErr := getAPIError(ErrServerNotInitialized).HTTPStatusCode - if rec.Code != serverNotInitializedErr { - t.Errorf("Test expected to fail with %d, but failed with %d", serverNotInitializedErr, rec.Code) - } -} - func testAPIListObjectPartsHandlerPreSign(obj ObjectLayer, instanceType, bucketName string, apiRouter http.Handler, credentials credential, t TestErrHandler) { testObject := "testobject" @@ -1499,7 +1615,8 @@ func testAPIListObjectPartsHandler(obj ObjectLayer, instanceType, bucketName str } apiRouter.ServeHTTP(tRec, tReq) if test.expectedErr != noAPIErr { - errBytes, err := ioutil.ReadAll(tRec.Result().Body) + var errBytes []byte + errBytes, err = ioutil.ReadAll(tRec.Result().Body) if err != nil { t.Fatalf("Test %d %s Failed to read error response list object parts request %s/%s: %v", i+1, instanceType, bucketName, testObject, err) @@ -1522,6 +1639,23 @@ func testAPIListObjectPartsHandler(obj ObjectLayer, instanceType, bucketName str } } } + + // HTTP request for testing when `objectLayer` is set to `nil`. + // There is no need to use an existing bucket and 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. + nilBucket := "dummy-bucket" + nilObject := "dummy-object" + + nilReq, err := newTestSignedRequestV4("GET", + getListMultipartURLWithParams("", nilBucket, nilObject, "dummy-uploadID", "0", "0", ""), + 0, nil, "", "") + if err != nil { + t.Errorf("Minio %s: Failed to create http request for testing the reponse when object Layer is set to `nil`.", instanceType) + } + // execute the object layer set to `nil` test. + // `ExecObjectLayerAPINilTest` sets the Object Layer to `nil` and calls the handler. + ExecObjectLayerAPINilTest(t, nilBucket, nilObject, instanceType, apiRouter, nilReq) } func TestAPIListObjectPartsHandler(t *testing.T) { @@ -1757,26 +1891,3 @@ func testAPIListObjectPartsHandlerAnon(obj ObjectLayer, instanceType, bucketName func TestListObjectPartsHandlerAnon(t *testing.T) { ExecObjectLayerAPITest(t, testAPIListObjectPartsHandlerAnon, []string{"PutObjectPart", "NewMultipart", "ListObjectParts"}) } - -func TestListObjectPartsHandlerNilObjAPI(t *testing.T) { - configDir, err := newTestConfig("us-east-1") - if err != nil { - t.Fatalf("Failed to create a test config: %v", err) - } - defer removeAll(configDir) - - rec := httptest.NewRecorder() - req, err := newTestSignedRequestV4("GET", - getListMultipartURLWithParams("", "testbucket", "testobject", "fakeuploadId", "", "", ""), - 0, bytes.NewReader([]byte("")), "abcd1", "abcd123") - if err != nil { - t.Fatal("Failed to create a signed UploadPart request.") - } - // Setup the 'nil' objectAPI router. - nilAPIRouter := initTestNilObjAPIEndPoints([]string{"ListObjectParts"}) - nilAPIRouter.ServeHTTP(rec, req) - serverNotInitializedErr := getAPIError(ErrServerNotInitialized).HTTPStatusCode - if rec.Code != serverNotInitializedErr { - t.Errorf("Test expected to fail with %d, but failed with %d", serverNotInitializedErr, rec.Code) - } -} diff --git a/cmd/test-utils_test.go b/cmd/test-utils_test.go index a8fcb43fe..96faacfc2 100644 --- a/cmd/test-utils_test.go +++ b/cmd/test-utils_test.go @@ -1387,6 +1387,45 @@ func initAPIHandlerTest(obj ObjectLayer, endPoints []string) (bucketName, rootPa return bucketName, rootPath, apiRouter, nil } +// ExecObjectLayerAPINilTest - Sets the object layer to `nil`, and calls rhe registered object layer API endpoint, and assert the error response. +// The purpose is to validate the API handlers response when the object layer is uninitialized. +// Usage hint: Should be used at the end of the API end points tests (ex: check the last few lines of `testAPIListObjectPartsHandler`), need a sample HTTP request +// to be sent as argument so that the relevant handler is called, +// the handler registration is expected to be done since its called from within the API handler tests, +// the reference to the registered HTTP handler has to be sent as an argument. +func ExecObjectLayerAPINilTest(t TestErrHandler, bucketName, objectName, instanceType string, apiRouter http.Handler, req *http.Request) { + // httptest Recorder to capture all the response by the http handler. + rec := httptest.NewRecorder() + + // The API handler gets the referece to the object layer via the global object Layer, + // setting it to `nil` in order test for handlers response for uninitialized object layer. + + objLayerMutex.Lock() + globalObjectAPI = nil + objLayerMutex.Unlock() + // call the HTTP handler. + apiRouter.ServeHTTP(rec, req) + + // expected error response when the API handler is called before the object layer is initialized, + // or when objectLayer is `nil`. + serverNotInitializedErr := getAPIError(ErrServerNotInitialized).HTTPStatusCode + if rec.Code != serverNotInitializedErr { + t.Errorf("Object API Nil Test expected to fail with %d, but failed with %d.", serverNotInitializedErr, rec.Code) + } + // expected error response in bytes when objectLayer is not initialized, or set to `nil`. + expectedErrResponse := encodeResponse(getAPIErrorResponse(getAPIError(ErrServerNotInitialized), getGetObjectURL("", bucketName, objectName))) + + // read the response body. + actualContent, err := ioutil.ReadAll(rec.Body) + if err != nil { + t.Fatalf("Minio %s: Failed parsing response body: %v.", instanceType, err) + } + // verify whether actual error response (from the response body), matches the expected error response. + if !bytes.Equal(expectedErrResponse, actualContent) { + t.Errorf("Minio %s: Object content differs from expected value.", instanceType) + } +} + // ExecObjectLayerAPITest - executes object layer API tests. // Creates single node and XL ObjectLayer instance, registers the specified API end points and runs test for both the layers. func ExecObjectLayerAPITest(t TestErrHandler, objAPITest objAPITestType, endPoints []string) { @@ -1413,7 +1452,8 @@ func ExecObjectLayerAPITest(t TestErrHandler, objAPITest objAPITestType, endPoin credentials = serverConfig.GetCredential() // Executing the object layer tests for XL. objAPITest(objLayer, xLTestStr, bucketXL, xlAPIRouter, credentials, t) - defer removeRoots(append(xlDisks, fsDir, fsRoot, xlRoot)) + // clean up the temporary test backend. + removeRoots(append(xlDisks, fsDir, fsRoot, xlRoot)) } // function to be passed to ExecObjectLayerAPITest, for executing object layr API handler tests.