/* * Minio Cloud Storage, (C) 2016, 2017 Minio, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cmd import ( "archive/zip" "bytes" "crypto/md5" "encoding/hex" "encoding/json" "errors" "fmt" "io" "io/ioutil" "net/http" "net/http/httptest" "reflect" "strconv" "strings" "testing" humanize "github.com/dustin/go-humanize" "github.com/minio/minio-go/pkg/policy" "github.com/minio/minio-go/pkg/set" ) // Tests private function writeWebErrorResponse. func TestWriteWebErrorResponse(t *testing.T) { var buffer bytes.Buffer testCases := []struct { webErr error apiErrCode APIErrorCode }{ // List of various errors and their corresponding API errors. { webErr: StorageFull{}, apiErrCode: ErrStorageFull, }, { webErr: BucketNotFound{}, apiErrCode: ErrNoSuchBucket, }, { webErr: BucketNameInvalid{}, apiErrCode: ErrInvalidBucketName, }, { webErr: BadDigest{}, apiErrCode: ErrBadDigest, }, { webErr: IncompleteBody{}, apiErrCode: ErrIncompleteBody, }, { webErr: ObjectExistsAsDirectory{}, apiErrCode: ErrObjectExistsAsDirectory, }, { webErr: ObjectNotFound{}, apiErrCode: ErrNoSuchKey, }, { webErr: ObjectNameInvalid{}, apiErrCode: ErrNoSuchKey, }, { webErr: InsufficientWriteQuorum{}, apiErrCode: ErrWriteQuorum, }, { webErr: InsufficientReadQuorum{}, apiErrCode: ErrReadQuorum, }, } // Validate all the test cases. for i, testCase := range testCases { writeWebErrorResponse(newFlushWriter(&buffer), testCase.webErr) desc := getAPIError(testCase.apiErrCode).Description recvDesc := buffer.Bytes() // Check if the written desc is same as the one expected. if !bytes.Equal(recvDesc, []byte(desc)) { t.Errorf("Test %d: Unexpected response, expecting %s, got %s", i+1, desc, string(buffer.Bytes())) } buffer.Reset() } } // Authenticate and get JWT token - will be called before every webrpc handler invocation func getWebRPCToken(apiRouter http.Handler, accessKey, secretKey string) (token string, err error) { rec := httptest.NewRecorder() request := LoginArgs{Username: accessKey, Password: secretKey} reply := &LoginRep{} req, err := newTestWebRPCRequest("Web"+loginMethodName, "", request) if err != nil { return "", err } apiRouter.ServeHTTP(rec, req) if rec.Code != http.StatusOK { return "", errors.New("Auth failed") } err = getTestWebRPCResponse(rec, &reply) if err != nil { return "", err } if reply.Token == "" { return "", errors.New("Auth failed") } return reply.Token, nil } // Wrapper for calling Login Web Handler func TestWebHandlerLogin(t *testing.T) { ExecObjectLayerTest(t, testLoginWebHandler) } // testLoginWebHandler - Test login web handler func testLoginWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) credentials := serverConfig.GetCredential() // test cases with sample input and expected output. testCases := []struct { username string password string success bool }{ {"", "", false}, {"azerty", "foo", false}, {"", "foo", false}, {"azerty", "", false}, {"azerty", "foo", false}, {"azerty", "azerty123", false}, {credentials.AccessKey, credentials.SecretKey, true}, } // Iterating over the test cases, calling the function under test and asserting the response. for i, testCase := range testCases { _, err := getWebRPCToken(apiRouter, testCase.username, testCase.password) if err != nil && testCase.success { t.Fatalf("Test %d: Expected to succeed but it failed, %v", i+1, err) } if err == nil && !testCase.success { t.Fatalf("Test %d: Expected to fail but it didn't, %v", i+1, err) } } } // Wrapper for calling StorageInfo Web Handler func TestWebHandlerStorageInfo(t *testing.T) { ExecObjectLayerTest(t, testStorageInfoWebHandler) } // testStorageInfoWebHandler - Test StorageInfo web handler func testStorageInfoWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // get random bucket name. // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) credentials := serverConfig.GetCredential() authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey) if err != nil { t.Fatal("Cannot authenticate") } rec := httptest.NewRecorder() storageInfoRequest := AuthRPCArgs{} storageInfoReply := &StorageInfoRep{} req, err := newTestWebRPCRequest("Web.StorageInfo", authorization, storageInfoRequest) if err != nil { t.Fatalf("Failed to create HTTP request: %v", err) } apiRouter.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("Expected the response status to be 200, but instead found `%d`", rec.Code) } err = getTestWebRPCResponse(rec, &storageInfoReply) if err != nil { t.Fatalf("Failed %v", err) } if storageInfoReply.StorageInfo.Total <= 0 { t.Fatalf("Got a zero or negative total free space disk") } } // Wrapper for calling ServerInfo Web Handler func TestWebHandlerServerInfo(t *testing.T) { ExecObjectLayerTest(t, testServerInfoWebHandler) } // testServerInfoWebHandler - Test ServerInfo web handler func testServerInfoWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) credentials := serverConfig.GetCredential() authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey) if err != nil { t.Fatal("Cannot authenticate") } rec := httptest.NewRecorder() serverInfoRequest := AuthRPCArgs{} serverInfoReply := &ServerInfoRep{} req, err := newTestWebRPCRequest("Web.ServerInfo", authorization, serverInfoRequest) if err != nil { t.Fatalf("Failed to create HTTP request: %v", err) } apiRouter.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("Expected the response status to be 200, but instead found `%d`", rec.Code) } err = getTestWebRPCResponse(rec, &serverInfoReply) if err != nil { t.Fatalf("Failed, %v", err) } if serverInfoReply.MinioVersion != Version { t.Fatalf("Cannot get minio version from server info handler") } } // Wrapper for calling MakeBucket Web Handler func TestWebHandlerMakeBucket(t *testing.T) { ExecObjectLayerTest(t, testMakeBucketWebHandler) } // testMakeBucketWebHandler - Test MakeBucket web handler func testMakeBucketWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) credentials := serverConfig.GetCredential() authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey) if err != nil { t.Fatal("Cannot authenticate") } rec := httptest.NewRecorder() bucketName := getRandomBucketName() testCases := []struct { bucketName string success bool }{ {"", false}, {".", false}, {"ab", false}, {bucketName, true}, } for i, testCase := range testCases { makeBucketRequest := MakeBucketArgs{BucketName: testCase.bucketName} makeBucketReply := &WebGenericRep{} req, err := newTestWebRPCRequest("Web.MakeBucket", authorization, makeBucketRequest) if err != nil { t.Fatalf("Test %d: Failed to create HTTP request: %v", i+1, err) } apiRouter.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("Test %d: Expected the response status to be 200, but instead found `%d`", i+1, rec.Code) } err = getTestWebRPCResponse(rec, &makeBucketReply) if testCase.success && err != nil { t.Fatalf("Test %d: Should succeed but it didn't, %v", i+1, err) } if !testCase.success && err == nil { t.Fatalf("Test %d: Should fail but it didn't (%s)", i+1, testCase.bucketName) } } } // Wrapper for calling ListBuckets Web Handler func TestWebHandlerListBuckets(t *testing.T) { ExecObjectLayerTest(t, testListBucketsWebHandler) } // testListBucketsHandler - Test ListBuckets web handler func testListBucketsWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) credentials := serverConfig.GetCredential() authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey) if err != nil { t.Fatal("Cannot authenticate") } rec := httptest.NewRecorder() bucketName := getRandomBucketName() // Create bucket. err = obj.MakeBucket(bucketName) if err != nil { // failed to create newbucket, abort. t.Fatalf("%s : %s", instanceType, err) } listBucketsRequest := WebGenericArgs{} listBucketsReply := &ListBucketsRep{} req, err := newTestWebRPCRequest("Web.ListBuckets", authorization, listBucketsRequest) if err != nil { t.Fatalf("Failed to create HTTP request: %v", err) } apiRouter.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("Expected the response status to be 200, but instead found `%d`", rec.Code) } err = getTestWebRPCResponse(rec, &listBucketsReply) if err != nil { t.Fatalf("Failed, %v", err) } if len(listBucketsReply.Buckets) == 0 { t.Fatalf("Cannot find the bucket already created by MakeBucket") } if listBucketsReply.Buckets[0].Name != bucketName { t.Fatalf("Found another bucket other than already created by MakeBucket") } } // Wrapper for calling ListObjects Web Handler func TestWebHandlerListObjects(t *testing.T) { ExecObjectLayerTest(t, testListObjectsWebHandler) } // testListObjectsHandler - Test ListObjects web handler func testListObjectsWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) credentials := serverConfig.GetCredential() rec := httptest.NewRecorder() authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey) if err != nil { t.Fatal("Cannot authenticate") } bucketName := getRandomBucketName() objectName := "object" objectSize := 1 * humanize.KiByte // Create bucket. err = obj.MakeBucket(bucketName) if err != nil { // failed to create newbucket, abort. t.Fatalf("%s : %s", instanceType, err) } data := bytes.Repeat([]byte("a"), objectSize) _, err = obj.PutObject(bucketName, objectName, int64(len(data)), bytes.NewReader(data), map[string]string{"md5Sum": "c9a34cfc85d982698c6ac89f76071abd"}, "") if err != nil { t.Fatalf("Was not able to upload an object, %v", err) } test := func(token string) (error, *ListObjectsRep) { listObjectsRequest := ListObjectsArgs{BucketName: bucketName, Prefix: ""} listObjectsReply := &ListObjectsRep{} var req *http.Request req, err = newTestWebRPCRequest("Web.ListObjects", token, listObjectsRequest) if err != nil { t.Fatalf("Failed to create HTTP request: %v", err) } apiRouter.ServeHTTP(rec, req) if rec.Code != http.StatusOK { return fmt.Errorf("Expected the response status to be 200, but instead found `%d`", rec.Code), listObjectsReply } err = getTestWebRPCResponse(rec, &listObjectsReply) if err != nil { return err, listObjectsReply } return nil, listObjectsReply } verifyReply := func(reply *ListObjectsRep) { if len(reply.Objects) == 0 { t.Fatalf("Cannot find the object") } if reply.Objects[0].Key != objectName { t.Fatalf("Found another object other than already created by PutObject") } if reply.Objects[0].Size != int64(objectSize) { t.Fatalf("Found a object with the same name but with a different size") } } // Authenticated ListObjects should succeed. err, reply := test(authorization) if err != nil { t.Fatal(err) } verifyReply(reply) // Unauthenticated ListObjects should fail. err, _ = test("") if err == nil { t.Fatalf("Expected error `%s`", err) } policy := bucketPolicy{ Version: "1.0", Statements: []policyStatement{getReadOnlyObjectStatement(bucketName, "")}, } globalBucketPolicies.SetBucketPolicy(bucketName, policyChange{false, &policy}) // Unauthenticated ListObjects with READ bucket policy should succeed. err, reply = test("") if err != nil { t.Fatal(err) } verifyReply(reply) } // Wrapper for calling RemoveObject Web Handler func TestWebHandlerRemoveObject(t *testing.T) { ExecObjectLayerTest(t, testRemoveObjectWebHandler) } // testRemoveObjectWebHandler - Test RemoveObject web handler func testRemoveObjectWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) credentials := serverConfig.GetCredential() rec := httptest.NewRecorder() authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey) if err != nil { t.Fatal("Cannot authenticate") } bucketName := getRandomBucketName() objectName := "object" objectSize := 1 * humanize.KiByte // Create bucket. err = obj.MakeBucket(bucketName) if err != nil { // failed to create newbucket, abort. t.Fatalf("%s : %s", instanceType, err) } data := bytes.Repeat([]byte("a"), objectSize) _, err = obj.PutObject(bucketName, objectName, int64(len(data)), bytes.NewReader(data), map[string]string{"md5Sum": "c9a34cfc85d982698c6ac89f76071abd"}, "") if err != nil { t.Fatalf("Was not able to upload an object, %v", err) } removeObjectRequest := RemoveObjectArgs{BucketName: bucketName, ObjectName: objectName} removeObjectReply := &WebGenericRep{} req, err := newTestWebRPCRequest("Web.RemoveObject", authorization, removeObjectRequest) if err != nil { t.Fatalf("Failed to create HTTP request: %v", err) } apiRouter.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("Expected the response status to be 200, but instead found `%d`", rec.Code) } err = getTestWebRPCResponse(rec, &removeObjectReply) if err != nil { t.Fatalf("Failed, %v", err) } removeObjectRequest = RemoveObjectArgs{BucketName: bucketName, ObjectName: objectName} removeObjectReply = &WebGenericRep{} req, err = newTestWebRPCRequest("Web.RemoveObject", authorization, removeObjectRequest) if err != nil { t.Fatalf("Failed to create HTTP request: %v", err) } apiRouter.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("Expected the response status to be 200, but instead found `%d`", rec.Code) } err = getTestWebRPCResponse(rec, &removeObjectReply) if err != nil { t.Fatalf("Failed, %v", err) } } // Wrapper for calling Generate Auth Handler func TestWebHandlerGenerateAuth(t *testing.T) { ExecObjectLayerTest(t, testGenerateAuthWebHandler) } // testGenerateAuthWebHandler - Test GenerateAuth web handler func testGenerateAuthWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) credentials := serverConfig.GetCredential() rec := httptest.NewRecorder() authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey) if err != nil { t.Fatal("Cannot authenticate") } generateAuthRequest := WebGenericArgs{} generateAuthReply := &GenerateAuthReply{} req, err := newTestWebRPCRequest("Web.GenerateAuth", authorization, generateAuthRequest) if err != nil { t.Fatalf("Failed to create HTTP request: %v", err) } apiRouter.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("Expected the response status to be 200, but instead found `%d`", rec.Code) } err = getTestWebRPCResponse(rec, &generateAuthReply) if err != nil { t.Fatalf("Failed, %v", err) } if generateAuthReply.AccessKey == "" || generateAuthReply.SecretKey == "" { t.Fatalf("Failed to generate auth keys") } } // Wrapper for calling Set Auth Handler func TestWebHandlerSetAuth(t *testing.T) { ExecObjectLayerTest(t, testSetAuthWebHandler) } // testSetAuthWebHandler - Test SetAuth web handler func testSetAuthWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) credentials := serverConfig.GetCredential() rec := httptest.NewRecorder() authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey) if err != nil { t.Fatal("Cannot authenticate") } testCases := []struct { username string password string success bool }{ {"", "", false}, {"1", "1", false}, {"azerty", "foooooooooooooo", true}, } // Iterating over the test cases, calling the function under test and asserting the response. for i, testCase := range testCases { setAuthRequest := SetAuthArgs{AccessKey: testCase.username, SecretKey: testCase.password} setAuthReply := &SetAuthReply{} req, err := newTestWebRPCRequest("Web.SetAuth", authorization, setAuthRequest) if err != nil { t.Fatalf("Test %d: Failed to create HTTP request: %v", i+1, err) } apiRouter.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("Test %d: Expected the response status to be 200, but instead found `%d`", i+1, rec.Code) } err = getTestWebRPCResponse(rec, &setAuthReply) if testCase.success && err != nil { t.Fatalf("Test %d: Supposed to succeed but failed, %v", i+1, err) } if !testCase.success && err == nil { t.Fatalf("Test %d: Supposed to fail but succeeded, %v", i+1, err) } if testCase.success && setAuthReply.Token == "" { t.Fatalf("Test %d: Failed to set auth keys", i+1) } } } // Wrapper for calling Get Auth Handler func TestWebHandlerGetAuth(t *testing.T) { ExecObjectLayerTest(t, testGetAuthWebHandler) } // testGetAuthWebHandler - Test GetAuth web handler func testGetAuthWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) credentials := serverConfig.GetCredential() rec := httptest.NewRecorder() authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey) if err != nil { t.Fatal("Cannot authenticate") } getAuthRequest := WebGenericArgs{} getAuthReply := &GetAuthReply{} req, err := newTestWebRPCRequest("Web.GetAuth", authorization, getAuthRequest) if err != nil { t.Fatalf("Failed to create HTTP request: %v", err) } apiRouter.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("Expected the response status to be 200, but instead found `%d`", rec.Code) } err = getTestWebRPCResponse(rec, &getAuthReply) if err != nil { t.Fatalf("Failed, %v", err) } if getAuthReply.AccessKey != credentials.AccessKey || getAuthReply.SecretKey != credentials.SecretKey { t.Fatalf("Failed to get correct auth keys") } } // Wrapper for calling Upload Handler func TestWebHandlerUpload(t *testing.T) { ExecObjectLayerTest(t, testUploadWebHandler) } // testUploadWebHandler - Test Upload web handler func testUploadWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) credentials := serverConfig.GetCredential() content := []byte("temporary file's content") authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey) if err != nil { t.Fatal("Cannot authenticate") } objectName := "test.file" bucketName := getRandomBucketName() test := func(token string, sendContentLength bool) int { rec := httptest.NewRecorder() req, rErr := http.NewRequest("PUT", "/minio/upload/"+bucketName+"/"+objectName, nil) if rErr != nil { t.Fatalf("Cannot create upload request, %v", rErr) } req.Header.Set("x-amz-date", "20160814T114029Z") req.Header.Set("Accept", "*/*") req.Body = ioutil.NopCloser(bytes.NewReader(content)) if !sendContentLength { req.ContentLength = -1 } else { req.ContentLength = int64(len(content)) } if token != "" { req.Header.Set("Authorization", "Bearer "+authorization) } apiRouter.ServeHTTP(rec, req) return rec.Code } // Create bucket. err = obj.MakeBucket(bucketName) if err != nil { // failed to create newbucket, abort. t.Fatalf("%s : %s", instanceType, err) } // Authenticated upload should succeed. code := test(authorization, true) if code != http.StatusOK { t.Fatalf("Expected the response status to be 200, but instead found `%d`", code) } var byteBuffer bytes.Buffer err = obj.GetObject(bucketName, objectName, 0, int64(len(content)), &byteBuffer) if err != nil { t.Fatalf("Failed, %v", err) } if bytes.Compare(byteBuffer.Bytes(), content) != 0 { t.Fatalf("The upload file is different from the download file") } // Authenticated upload without content-length should fail code = test(authorization, false) if code != http.StatusBadRequest { t.Fatalf("Expected the response status to be 200, but instead found `%d`", code) } // Unauthenticated upload should fail. code = test("", true) if code != http.StatusForbidden { t.Fatalf("Expected the response status to be 403, but instead found `%d`", code) } policy := bucketPolicy{ Version: "1.0", Statements: []policyStatement{getWriteOnlyObjectStatement(bucketName, "")}, } globalBucketPolicies.SetBucketPolicy(bucketName, policyChange{false, &policy}) // Unauthenticated upload with WRITE policy should succeed. code = test("", true) if code != http.StatusOK { t.Fatalf("Expected the response status to be 200, but instead found `%d`", code) } } // Wrapper for calling Download Handler func TestWebHandlerDownload(t *testing.T) { ExecObjectLayerTest(t, testDownloadWebHandler) } // testDownloadWebHandler - Test Download web handler func testDownloadWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) credentials := serverConfig.GetCredential() authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey) if err != nil { t.Fatal("Cannot authenticate") } objectName := "test.file" bucketName := getRandomBucketName() test := func(token string) (int, []byte) { rec := httptest.NewRecorder() path := "/minio/download/" + bucketName + "/" + objectName + "?token=" if token != "" { path = path + token } var req *http.Request req, err = http.NewRequest("GET", path, nil) if err != nil { t.Fatalf("Cannot create upload request, %v", err) } apiRouter.ServeHTTP(rec, req) return rec.Code, rec.Body.Bytes() } // Create bucket. err = obj.MakeBucket(bucketName) if err != nil { // failed to create newbucket, abort. t.Fatalf("%s : %s", instanceType, err) } content := []byte("temporary file's content") _, err = obj.PutObject(bucketName, objectName, int64(len(content)), bytes.NewReader(content), map[string]string{"md5Sum": "01ce59706106fe5e02e7f55fffda7f34"}, "") if err != nil { t.Fatalf("Was not able to upload an object, %v", err) } // Authenticated download should succeed. code, bodyContent := test(authorization) if code != http.StatusOK { t.Fatalf("Expected the response status to be 200, but instead found `%d`", code) } if bytes.Compare(bodyContent, content) != 0 { t.Fatalf("The downloaded file is corrupted") } // Unauthenticated download should fail. code, _ = test("") if code != http.StatusForbidden { t.Fatalf("Expected the response status to be 403, but instead found `%d`", code) } policy := bucketPolicy{ Version: "1.0", Statements: []policyStatement{getReadOnlyObjectStatement(bucketName, "")}, } globalBucketPolicies.SetBucketPolicy(bucketName, policyChange{false, &policy}) // Unauthenticated download with READ policy should succeed. code, bodyContent = test("") if code != http.StatusOK { t.Fatalf("Expected the response status to be 200, but instead found `%d`", code) } if bytes.Compare(bodyContent, content) != 0 { t.Fatalf("The downloaded file is corrupted") } } // Test web.DownloadZip func TestWebHandlerDownloadZip(t *testing.T) { ExecObjectLayerTest(t, testWebHandlerDownloadZip) } func testWebHandlerDownloadZip(obj ObjectLayer, instanceType string, t TestErrHandler) { apiRouter := initTestWebRPCEndPoint(obj) credentials := serverConfig.GetCredential() authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey) if err != nil { t.Fatal("Cannot authenticate") } bucket := getRandomBucketName() fileOne := "aaaaaaaaaaaaaa" fileTwo := "bbbbbbbbbbbbbb" fileThree := "cccccccccccccc" // Create bucket. err = obj.MakeBucket(bucket) if err != nil { // failed to create newbucket, abort. t.Fatalf("%s : %s", instanceType, err) } obj.PutObject(bucket, "a/one", int64(len(fileOne)), strings.NewReader(fileOne), nil, "") obj.PutObject(bucket, "a/b/two", int64(len(fileTwo)), strings.NewReader(fileTwo), nil, "") obj.PutObject(bucket, "a/c/three", int64(len(fileThree)), strings.NewReader(fileThree), nil, "") test := func(token string) (int, []byte) { rec := httptest.NewRecorder() path := "/minio/zip" + "?token=" if token != "" { path = path + token } args := DownloadZipArgs{ Objects: []string{"one", "b/", "c/"}, Prefix: "a/", BucketName: bucket, } var argsData []byte argsData, err = json.Marshal(args) if err != nil { return 0, nil } var req *http.Request req, err = http.NewRequest("POST", path, bytes.NewBuffer(argsData)) if err != nil { t.Fatalf("Cannot create upload request, %v", err) } apiRouter.ServeHTTP(rec, req) return rec.Code, rec.Body.Bytes() } code, data := test("") if code != 403 { t.Fatal("Expected to receive authentication error") } code, data = test(authorization) if code != 200 { t.Fatal("web.DownloadsZip() failed") } reader, err := zip.NewReader(bytes.NewReader(data), int64(len(data))) if err != nil { t.Fatal(err) } h := md5.New() for _, file := range reader.File { fileReader, err := file.Open() if err != nil { t.Fatal(err) } io.Copy(h, fileReader) } // Verify the md5 of the response. if hex.EncodeToString(h.Sum(nil)) != "ac7196449b14bea42775d29e8bb29f50" { t.Fatal("Incorrect zip contents") } } // Wrapper for calling PresignedGet handler func TestWebHandlerPresignedGetHandler(t *testing.T) { ExecObjectLayerTest(t, testWebPresignedGetHandler) } func testWebPresignedGetHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) credentials := serverConfig.GetCredential() authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey) if err != nil { t.Fatal("Cannot authenticate") } rec := httptest.NewRecorder() bucketName := getRandomBucketName() objectName := "object" objectSize := 1 * humanize.KiByte // Create bucket. err = obj.MakeBucket(bucketName) if err != nil { // failed to create newbucket, abort. t.Fatalf("%s : %s", instanceType, err) } data := bytes.Repeat([]byte("a"), objectSize) _, err = obj.PutObject(bucketName, objectName, int64(len(data)), bytes.NewReader(data), map[string]string{"md5Sum": "c9a34cfc85d982698c6ac89f76071abd"}, "") if err != nil { t.Fatalf("Was not able to upload an object, %v", err) } presignGetReq := PresignedGetArgs{ HostName: "", BucketName: bucketName, ObjectName: objectName, Expiry: 1000, } presignGetRep := &PresignedGetRep{} req, err := newTestWebRPCRequest("Web.PresignedGet", authorization, presignGetReq) if err != nil { t.Fatalf("Failed to create HTTP request: %v", err) } apiRouter.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("Expected the response status to be 200, but instead found `%d`", rec.Code) } err = getTestWebRPCResponse(rec, &presignGetRep) if err != nil { t.Fatalf("Failed, %v", err) } // Register the API end points with XL/FS object layer. apiRouter = initTestAPIEndPoints(obj, []string{"GetObject"}) // Initialize a new api recorder. arec := httptest.NewRecorder() req, err = newTestRequest("GET", presignGetRep.URL, 0, nil) req.Header.Del("x-amz-content-sha256") if err != nil { t.Fatal("Failed to initialized a new request", err) } apiRouter.ServeHTTP(arec, req) if arec.Code != http.StatusOK { t.Fatalf("Expected the response status to be 200, but instead found `%d`", arec.Code) } savedData, err := ioutil.ReadAll(arec.Body) if err != nil { t.Fatal("Reading body failed", err) } if !bytes.Equal(data, savedData) { t.Fatal("Read data is not equal was what was expected") } // Register the API end points with XL/FS object layer. apiRouter = initTestWebRPCEndPoint(obj) presignGetReq = PresignedGetArgs{ HostName: "", BucketName: "", ObjectName: "", } presignGetRep = &PresignedGetRep{} req, err = newTestWebRPCRequest("Web.PresignedGet", authorization, presignGetReq) if err != nil { t.Fatalf("Failed to create HTTP request: %v", err) } apiRouter.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("Expected the response status to be 200, but instead found `%d`", rec.Code) } err = getTestWebRPCResponse(rec, &presignGetRep) if err == nil { t.Fatalf("Failed, %v", err) } if err.Error() != "Bucket and Object are mandatory arguments." { t.Fatalf("Unexpected, expected `Bucket and Object are mandatory arguments`, got %s", err) } } // Wrapper for calling GetBucketPolicy Handler func TestWebHandlerGetBucketPolicyHandler(t *testing.T) { ExecObjectLayerTest(t, testWebGetBucketPolicyHandler) } // testWebGetBucketPolicyHandler - Test GetBucketPolicy web handler func testWebGetBucketPolicyHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) credentials := serverConfig.GetCredential() authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey) if err != nil { t.Fatal("Cannot authenticate") } rec := httptest.NewRecorder() bucketName := getRandomBucketName() if err := obj.MakeBucket(bucketName); err != nil { t.Fatal("Unexpected error: ", err) } policyVal := bucketPolicy{ Version: "2012-10-17", Statements: []policyStatement{ { Actions: set.CreateStringSet("s3:GetBucketLocation", "s3:ListBucket"), Effect: "Allow", Principal: map[string][]string{"AWS": {"*"}}, Resources: set.CreateStringSet(bucketARNPrefix + bucketName), Sid: "", }, { Actions: set.CreateStringSet("s3:GetObject"), Effect: "Allow", Principal: map[string][]string{"AWS": {"*"}}, Resources: set.CreateStringSet(bucketARNPrefix + bucketName + "/*"), Sid: "", }, }, } if err := writeBucketPolicy(bucketName, obj, &policyVal); err != nil { t.Fatal("Unexpected error: ", err) } testCases := []struct { bucketName string prefix string expectedResult policy.BucketPolicy }{ {bucketName, "", policy.BucketPolicyReadOnly}, } for i, testCase := range testCases { args := &GetBucketPolicyArgs{BucketName: testCase.bucketName, Prefix: testCase.prefix} reply := &GetBucketPolicyRep{} req, err := newTestWebRPCRequest("Web.GetBucketPolicy", authorization, args) if err != nil { t.Fatalf("Test %d: Failed to create HTTP request: %v", i+1, err) } apiRouter.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("Test %d: Expected the response status to be 200, but instead found `%d`", i+1, rec.Code) } if err = getTestWebRPCResponse(rec, &reply); err != nil { t.Fatalf("Test %d: Should succeed but it didn't, %v", i+1, err) } if testCase.expectedResult != reply.Policy { t.Fatalf("Test %d: expected: %v, got: %v", i+1, testCase.expectedResult, reply.Policy) } } } // Wrapper for calling ListAllBucketPolicies Handler func TestWebHandlerListAllBucketPoliciesHandler(t *testing.T) { ExecObjectLayerTest(t, testWebListAllBucketPoliciesHandler) } // testWebListAllBucketPoliciesHandler - Test ListAllBucketPolicies web handler func testWebListAllBucketPoliciesHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) credentials := serverConfig.GetCredential() authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey) if err != nil { t.Fatal("Cannot authenticate") } rec := httptest.NewRecorder() bucketName := getRandomBucketName() if err := obj.MakeBucket(bucketName); err != nil { t.Fatal("Unexpected error: ", err) } policyVal := bucketPolicy{ Version: "2012-10-17", Statements: []policyStatement{ { Actions: set.CreateStringSet("s3:GetBucketLocation"), Effect: "Allow", Principal: map[string][]string{"AWS": {"*"}}, Resources: set.CreateStringSet(bucketARNPrefix + bucketName), Sid: "", }, { Actions: set.CreateStringSet("s3:ListBucket"), Conditions: map[string]map[string]set.StringSet{ "StringEquals": { "s3:prefix": set.CreateStringSet("hello"), }, }, Effect: "Allow", Principal: map[string][]string{"AWS": {"*"}}, Resources: set.CreateStringSet(bucketARNPrefix + bucketName), Sid: "", }, { Actions: set.CreateStringSet("s3:ListBucketMultipartUploads"), Effect: "Allow", Principal: map[string][]string{"AWS": {"*"}}, Resources: set.CreateStringSet(bucketARNPrefix + bucketName), Sid: "", }, { Actions: set.CreateStringSet("s3:AbortMultipartUpload", "s3:DeleteObject", "s3:GetObject", "s3:ListMultipartUploadParts", "s3:PutObject"), Effect: "Allow", Principal: map[string][]string{"AWS": {"*"}}, Resources: set.CreateStringSet(bucketARNPrefix + bucketName + "/hello*"), Sid: "", }, }, } if err := writeBucketPolicy(bucketName, obj, &policyVal); err != nil { t.Fatal("Unexpected error: ", err) } testCaseResult1 := []bucketAccessPolicy{{ Prefix: bucketName + "/hello*", Policy: policy.BucketPolicyReadWrite, }} testCases := []struct { bucketName string expectedResult []bucketAccessPolicy }{ {bucketName, testCaseResult1}, } for i, testCase := range testCases { args := &ListAllBucketPoliciesArgs{BucketName: testCase.bucketName} reply := &ListAllBucketPoliciesRep{} req, err := newTestWebRPCRequest("Web.ListAllBucketPolicies", authorization, args) if err != nil { t.Fatalf("Test %d: Failed to create HTTP request: %v", i+1, err) } apiRouter.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("Test %d: Expected the response status to be 200, but instead found `%d`", i+1, rec.Code) } if err = getTestWebRPCResponse(rec, &reply); err != nil { t.Fatalf("Test %d: Should succeed but it didn't, %v", i+1, err) } if !reflect.DeepEqual(testCase.expectedResult, reply.Policies) { t.Fatalf("Test %d: expected: %v, got: %v", i+1, testCase.expectedResult, reply.Policies) } } } // Wrapper for calling SetBucketPolicy Handler func TestWebHandlerSetBucketPolicyHandler(t *testing.T) { ExecObjectLayerTest(t, testWebSetBucketPolicyHandler) } // testWebSetBucketPolicyHandler - Test SetBucketPolicy web handler func testWebSetBucketPolicyHandler(obj ObjectLayer, instanceType string, t TestErrHandler) { // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) credentials := serverConfig.GetCredential() authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey) if err != nil { t.Fatal("Cannot authenticate") } rec := httptest.NewRecorder() // Create a bucket bucketName := getRandomBucketName() if err = obj.MakeBucket(bucketName); err != nil { t.Fatal("Unexpected error: ", err) } testCases := []struct { bucketName string prefix string policy string pass bool }{ // Invalid bucket name {"", "", "readonly", false}, // Invalid policy {bucketName, "", "foo", false}, // Valid parameters {bucketName, "", "readwrite", true}, // None is valid and policy should be removed. {bucketName, "", "none", true}, // Setting none again meants should return an error. {bucketName, "", "none", false}, } for i, testCase := range testCases { args := &SetBucketPolicyArgs{BucketName: testCase.bucketName, Prefix: testCase.prefix, Policy: testCase.policy} reply := &WebGenericRep{} // Call SetBucketPolicy RPC req, err := newTestWebRPCRequest("Web.SetBucketPolicy", authorization, args) if err != nil { t.Fatalf("Test %d: Failed to create HTTP request: %v", i+1, err) } apiRouter.ServeHTTP(rec, req) // Check if we have 200 OK if testCase.pass && rec.Code != http.StatusOK { t.Fatalf("Test %d: Expected the response status to be 200, but instead found `%d`", i+1, rec.Code) } // Parse RPC response err = getTestWebRPCResponse(rec, &reply) if testCase.pass && err != nil { t.Fatalf("Test %d: Should succeed but it didn't, %v", i+1, err) } if !testCase.pass && err == nil { t.Fatalf("Test %d: Should fail it didn't", i+1) } } } // TestWebCheckAuthorization - Test Authorization for all web handlers func TestWebCheckAuthorization(t *testing.T) { // Prepare XL backend obj, fsDirs, err := prepareXL() if err != nil { t.Fatalf("Initialization of object layer failed for XL setup: %s", err) } // Executing the object layer tests for XL. defer removeRoots(fsDirs) // Register the API end points with XL/FS object layer. apiRouter := initTestWebRPCEndPoint(obj) // initialize the server and obtain the credentials and root. // credentials are necessary to sign the HTTP request. rootPath, err := newTestConfig(globalMinioDefaultRegion) if err != nil { t.Fatal("Init Test config failed", err) } // remove the root directory after the test ends. defer removeAll(rootPath) rec := httptest.NewRecorder() // Check if web rpc calls return unauthorized request with an incorrect token webRPCs := []string{ "ServerInfo", "StorageInfo", "MakeBucket", "ListBuckets", "ListObjects", "RemoveObject", "GenerateAuth", "SetAuth", "GetAuth", "GetBucketPolicy", "SetBucketPolicy", "ListAllBucketPolicies", "PresignedGet", } for _, rpcCall := range webRPCs { args := &AuthRPCArgs{} reply := &WebGenericRep{} req, nerr := newTestWebRPCRequest("Web."+rpcCall, "Bearer fooauthorization", args) if nerr != nil { t.Fatalf("Test %s: Failed to create HTTP request: %v", rpcCall, nerr) } apiRouter.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("Test %s: Expected the response status to be 200, but instead found `%d`", rpcCall, rec.Code) } err = getTestWebRPCResponse(rec, &reply) if err == nil { t.Fatalf("Test %s: Should fail", rpcCall) } else { if !strings.Contains(err.Error(), errAuthentication.Error()) { t.Fatalf("Test %s: should fail with Unauthorized request. Found error: %v", rpcCall, err) } } } rec = httptest.NewRecorder() // Test authorization of Web.Download req, err := http.NewRequest("GET", "/minio/download/bucket/object?token=wrongauth", nil) if err != nil { t.Fatalf("Cannot create upload request, %v", err) } apiRouter.ServeHTTP(rec, req) if rec.Code != http.StatusForbidden { t.Fatalf("Expected the response status to be 403, but instead found `%d`", rec.Code) } resp := string(rec.Body.Bytes()) if !strings.EqualFold(resp, errAuthentication.Error()) { t.Fatalf("Unexpected error message, expected: %s, found: `%s`", errAuthentication, resp) } rec = httptest.NewRecorder() // Test authorization of Web.Upload content := []byte("temporary file's content") req, err = http.NewRequest("PUT", "/minio/upload/bucket/object", nil) req.Header.Set("Authorization", "Bearer foo-authorization") req.Header.Set("Content-Length", strconv.Itoa(len(content))) req.Header.Set("x-amz-date", "20160814T114029Z") req.Header.Set("Accept", "*/*") req.Body = ioutil.NopCloser(bytes.NewReader(content)) if err != nil { t.Fatalf("Cannot create upload request, %v", err) } apiRouter.ServeHTTP(rec, req) if rec.Code != http.StatusForbidden { t.Fatalf("Expected the response status to be 403, but instead found `%d`", rec.Code) } resp = string(rec.Body.Bytes()) if !strings.EqualFold(resp, errAuthentication.Error()) { t.Fatalf("Unexpected error message, expected: `%s`, found: `%s`", errAuthentication, resp) } } // TestWebObjectLayerNotReady - Test RPCs responses when disks are not ready func TestWebObjectLayerNotReady(t *testing.T) { // Initialize web rpc endpoint. apiRouter := initTestWebRPCEndPoint(nil) // initialize the server and obtain the credentials and root. // credentials are necessary to sign the HTTP request. rootPath, err := newTestConfig(globalMinioDefaultRegion) if err != nil { t.Fatal("Init Test config failed", err) } // remove the root directory after the test ends. defer removeAll(rootPath) rec := httptest.NewRecorder() credentials := serverConfig.GetCredential() authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey) if err != nil { t.Fatal("Cannot authenticate", err) } // Check if web rpc calls return Server not initialized. ServerInfo, GenerateAuth, // SetAuth and GetAuth are not concerned webRPCs := []string{"StorageInfo", "MakeBucket", "ListBuckets", "ListObjects", "RemoveObject", "GetBucketPolicy", "SetBucketPolicy", "ListAllBucketPolicies"} for _, rpcCall := range webRPCs { args := &AuthRPCArgs{} reply := &WebGenericRep{} req, nerr := newTestWebRPCRequest("Web."+rpcCall, authorization, args) if nerr != nil { t.Fatalf("Test %s: Failed to create HTTP request: %v", rpcCall, nerr) } apiRouter.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("Test %s: Expected the response status to be 200, but instead found `%d`", rpcCall, rec.Code) } err = getTestWebRPCResponse(rec, &reply) if err == nil { t.Fatalf("Test %s: Should fail", rpcCall) } else { if !strings.EqualFold(err.Error(), errServerNotInitialized.Error()) { t.Fatalf("Test %s: should fail with %s Found error: %v", rpcCall, errServerNotInitialized, err) } } } rec = httptest.NewRecorder() // Test authorization of Web.Download req, err := http.NewRequest("GET", "/minio/download/bucket/object?token="+authorization, nil) if err != nil { t.Fatalf("Cannot create upload request, %v", err) } apiRouter.ServeHTTP(rec, req) if rec.Code != http.StatusServiceUnavailable { t.Fatalf("Expected the response status to be 503, but instead found `%d`", rec.Code) } resp := string(rec.Body.Bytes()) if !strings.EqualFold(resp, errServerNotInitialized.Error()) { t.Fatalf("Unexpected error message, expected: `%s`, found: `%s`", errServerNotInitialized, resp) } rec = httptest.NewRecorder() // Test authorization of Web.Upload content := []byte("temporary file's content") req, err = http.NewRequest("PUT", "/minio/upload/bucket/object", nil) req.Header.Set("Authorization", "Bearer "+authorization) req.Header.Set("Content-Length", strconv.Itoa(len(content))) req.Header.Set("x-amz-date", "20160814T114029Z") req.Header.Set("Accept", "*/*") req.Body = ioutil.NopCloser(bytes.NewReader(content)) if err != nil { t.Fatalf("Cannot create upload request, %v", err) } apiRouter.ServeHTTP(rec, req) if rec.Code != http.StatusServiceUnavailable { t.Fatalf("Expected the response status to be 503, but instead found `%d`", rec.Code) } resp = string(rec.Body.Bytes()) if !strings.EqualFold(resp, errServerNotInitialized.Error()) { t.Fatalf("Unexpected error message, expected: `%s`, found: `%s`", errServerNotInitialized, resp) } } // TestWebObjectLayerFaultyDisks - Test Web RPC responses with faulty disks func TestWebObjectLayerFaultyDisks(t *testing.T) { root, err := newTestConfig(globalMinioDefaultRegion) if err != nil { t.Fatal(err) } defer removeAll(root) // Prepare XL backend obj, fsDirs, err := prepareXL() if err != nil { t.Fatalf("Initialization of object layer failed for XL setup: %s", err) } // Executing the object layer tests for XL. defer removeRoots(fsDirs) // Set faulty disks to XL backend xl := obj.(*xlObjects) for i, d := range xl.storageDisks { xl.storageDisks[i] = newNaughtyDisk(d.(*retryStorage), nil, errFaultyDisk) } // Initialize web rpc endpoint. apiRouter := initTestWebRPCEndPoint(obj) // initialize the server and obtain the credentials and root. // credentials are necessary to sign the HTTP request. rootPath, err := newTestConfig(globalMinioDefaultRegion) if err != nil { t.Fatal("Init Test config failed", err) } // remove the root directory after the test ends. defer removeAll(rootPath) rec := httptest.NewRecorder() credentials := serverConfig.GetCredential() authorization, err := getWebRPCToken(apiRouter, credentials.AccessKey, credentials.SecretKey) if err != nil { t.Fatal("Cannot authenticate", err) } // Check if web rpc calls return errors with faulty disks. ServerInfo, GenerateAuth, SetAuth, GetAuth are not concerned webRPCs := []string{"MakeBucket", "ListBuckets", "ListObjects", "RemoveObject", "GetBucketPolicy", "SetBucketPolicy"} for _, rpcCall := range webRPCs { args := &AuthRPCArgs{} reply := &WebGenericRep{} req, nerr := newTestWebRPCRequest("Web."+rpcCall, authorization, args) if nerr != nil { t.Fatalf("Test %s: Failed to create HTTP request: %v", rpcCall, nerr) } apiRouter.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("Test %s: Expected the response status to be 200, but instead found `%d`", rpcCall, rec.Code) } err = getTestWebRPCResponse(rec, &reply) if err == nil { t.Errorf("Test %s: Should fail", rpcCall) } } // Test Web.StorageInfo storageInfoRequest := AuthRPCArgs{} storageInfoReply := &StorageInfoRep{} req, err := newTestWebRPCRequest("Web.StorageInfo", authorization, storageInfoRequest) if err != nil { t.Fatalf("Failed to create HTTP request: %v", err) } apiRouter.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("Expected the response status to be 200, but instead found `%d`", rec.Code) } err = getTestWebRPCResponse(rec, &storageInfoReply) if err != nil { t.Fatalf("Failed %v", err) } if storageInfoReply.StorageInfo.Total != -1 || storageInfoReply.StorageInfo.Free != -1 { t.Fatalf("Should get negative values of Total and Free since disks are faulty ") } // Test authorization of Web.Download req, err = http.NewRequest("GET", "/minio/download/bucket/object?token="+authorization, nil) if err != nil { t.Fatalf("Cannot create upload request, %v", err) } apiRouter.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("Expected the response status to be 200, but instead found `%d`", rec.Code) } // Test authorization of Web.Upload content := []byte("temporary file's content") req, err = http.NewRequest("PUT", "/minio/upload/bucket/object", nil) req.Header.Set("Authorization", "Bearer "+authorization) req.Header.Set("Content-Length", strconv.Itoa(len(content))) req.Header.Set("x-amz-date", "20160814T114029Z") req.Header.Set("Accept", "*/*") req.Body = ioutil.NopCloser(bytes.NewReader(content)) if err != nil { t.Fatalf("Cannot create upload request, %v", err) } apiRouter.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("Expected the response status to be 200, but instead found `%d`", rec.Code) } }