2017-07-23 12:24:45 +00:00
/ *
2017-12-08 19:45:59 +00:00
* Minio Go Library for Amazon S3 Compatible Cloud Storage
* Copyright 2015 - 2017 Minio , Inc .
2017-07-23 12:24:45 +00:00
*
* 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
*
2017-12-08 19:45:59 +00:00
* Unless required by applicable law or agreed to in writing , software
2017-07-23 12:24:45 +00:00
* 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 minio
import (
"bytes"
"encoding/xml"
"fmt"
"io/ioutil"
"net/http"
"reflect"
"strconv"
"testing"
)
// Tests validate the Error generator function for http response with error.
func TestHttpRespToErrorResponse ( t * testing . T ) {
// 'genAPIErrorResponse' generates ErrorResponse for given APIError.
// provides a encodable populated response values.
genAPIErrorResponse := func ( err APIError , bucketName string ) ErrorResponse {
2017-12-08 19:45:59 +00:00
return ErrorResponse {
Code : err . Code ,
Message : err . Description ,
BucketName : bucketName ,
}
2017-07-23 12:24:45 +00:00
}
// Encodes the response headers into XML format.
2017-12-08 19:45:59 +00:00
encodeErr := func ( response ErrorResponse ) [ ] byte {
buf := & bytes . Buffer { }
buf . WriteString ( xml . Header )
encoder := xml . NewEncoder ( buf )
err := encoder . Encode ( response )
if err != nil {
t . Fatalf ( "error encoding response: %v" , err )
}
return buf . Bytes ( )
2017-07-23 12:24:45 +00:00
}
// `createAPIErrorResponse` Mocks XML error response from the server.
createAPIErrorResponse := func ( APIErr APIError , bucketName string ) * http . Response {
// generate error response.
// response body contains the XML error message.
resp := & http . Response { }
errorResponse := genAPIErrorResponse ( APIErr , bucketName )
encodedErrorResponse := encodeErr ( errorResponse )
// write Header.
resp . StatusCode = APIErr . HTTPStatusCode
resp . Body = ioutil . NopCloser ( bytes . NewBuffer ( encodedErrorResponse ) )
return resp
}
// 'genErrResponse' contructs error response based http Status Code
genErrResponse := func ( resp * http . Response , code , message , bucketName , objectName string ) ErrorResponse {
errResp := ErrorResponse {
2017-12-08 19:45:59 +00:00
StatusCode : resp . StatusCode ,
2017-07-23 12:24:45 +00:00
Code : code ,
Message : message ,
BucketName : bucketName ,
Key : objectName ,
RequestID : resp . Header . Get ( "x-amz-request-id" ) ,
HostID : resp . Header . Get ( "x-amz-id-2" ) ,
Region : resp . Header . Get ( "x-amz-bucket-region" ) ,
Headers : resp . Header ,
}
return errResp
}
// Generate invalid argument error.
genInvalidError := func ( message string ) error {
errResp := ErrorResponse {
2017-12-08 19:45:59 +00:00
StatusCode : http . StatusBadRequest ,
Code : "InvalidArgument" ,
Message : message ,
RequestID : "minio" ,
2017-07-23 12:24:45 +00:00
}
return errResp
}
// Set common http response headers.
setCommonHeaders := func ( resp * http . Response ) * http . Response {
// set headers.
resp . Header = make ( http . Header )
resp . Header . Set ( "x-amz-request-id" , "xyz" )
resp . Header . Set ( "x-amz-id-2" , "abc" )
resp . Header . Set ( "x-amz-bucket-region" , "us-east-1" )
return resp
}
// Generate http response with empty body.
// Set the StatusCode to the argument supplied.
// Sets common headers.
genEmptyBodyResponse := func ( statusCode int ) * http . Response {
2017-12-08 19:45:59 +00:00
resp := & http . Response {
StatusCode : statusCode ,
Body : ioutil . NopCloser ( bytes . NewReader ( nil ) ) ,
}
2017-07-23 12:24:45 +00:00
setCommonHeaders ( resp )
return resp
}
// Decode XML error message from the http response body.
2017-12-08 19:45:59 +00:00
decodeXMLError := func ( resp * http . Response ) error {
errResp := ErrorResponse {
StatusCode : resp . StatusCode ,
}
2017-07-23 12:24:45 +00:00
err := xmlDecoder ( resp . Body , & errResp )
if err != nil {
2017-12-08 19:45:59 +00:00
t . Fatalf ( "XML decoding of response body failed: %v" , err )
2017-07-23 12:24:45 +00:00
}
return errResp
}
// List of APIErrors used to generate/mock server side XML error response.
APIErrors := [ ] APIError {
{
Code : "NoSuchBucketPolicy" ,
Description : "The specified bucket does not have a bucket policy." ,
HTTPStatusCode : http . StatusNotFound ,
} ,
}
// List of expected response.
// Used for asserting the actual response.
expectedErrResponse := [ ] error {
genInvalidError ( "Response is empty. " + "Please report this issue at https://github.com/minio/minio-go/issues." ) ,
2017-12-08 19:45:59 +00:00
decodeXMLError ( createAPIErrorResponse ( APIErrors [ 0 ] , "minio-bucket" ) ) ,
genErrResponse ( setCommonHeaders ( & http . Response { StatusCode : http . StatusNotFound } ) , "NoSuchBucket" , "The specified bucket does not exist." , "minio-bucket" , "" ) ,
genErrResponse ( setCommonHeaders ( & http . Response { StatusCode : http . StatusNotFound } ) , "NoSuchKey" , "The specified key does not exist." , "minio-bucket" , "Asia/" ) ,
genErrResponse ( setCommonHeaders ( & http . Response { StatusCode : http . StatusForbidden } ) , "AccessDenied" , "Access Denied." , "minio-bucket" , "" ) ,
genErrResponse ( setCommonHeaders ( & http . Response { StatusCode : http . StatusConflict } ) , "Conflict" , "Bucket not empty." , "minio-bucket" , "" ) ,
genErrResponse ( setCommonHeaders ( & http . Response { StatusCode : http . StatusBadRequest } ) , "Bad Request" , "Bad Request" , "minio-bucket" , "" ) ,
2017-07-23 12:24:45 +00:00
}
// List of http response to be used as input.
inputResponses := [ ] * http . Response {
nil ,
createAPIErrorResponse ( APIErrors [ 0 ] , "minio-bucket" ) ,
genEmptyBodyResponse ( http . StatusNotFound ) ,
genEmptyBodyResponse ( http . StatusNotFound ) ,
genEmptyBodyResponse ( http . StatusForbidden ) ,
genEmptyBodyResponse ( http . StatusConflict ) ,
genEmptyBodyResponse ( http . StatusBadRequest ) ,
}
testCases := [ ] struct {
bucketName string
objectName string
inputHTTPResp * http . Response
// expected results.
expectedResult error
// flag indicating whether tests should pass.
} {
{ "minio-bucket" , "" , inputResponses [ 0 ] , expectedErrResponse [ 0 ] } ,
{ "minio-bucket" , "" , inputResponses [ 1 ] , expectedErrResponse [ 1 ] } ,
{ "minio-bucket" , "" , inputResponses [ 2 ] , expectedErrResponse [ 2 ] } ,
{ "minio-bucket" , "Asia/" , inputResponses [ 3 ] , expectedErrResponse [ 3 ] } ,
{ "minio-bucket" , "" , inputResponses [ 4 ] , expectedErrResponse [ 4 ] } ,
{ "minio-bucket" , "" , inputResponses [ 5 ] , expectedErrResponse [ 5 ] } ,
}
for i , testCase := range testCases {
actualResult := httpRespToErrorResponse ( testCase . inputHTTPResp , testCase . bucketName , testCase . objectName )
if ! reflect . DeepEqual ( testCase . expectedResult , actualResult ) {
t . Errorf ( "Test %d: Expected result to be '%#v', but instead got '%#v'" , i + 1 , testCase . expectedResult , actualResult )
}
}
}
// Test validates 'ErrEntityTooLarge' error response.
func TestErrEntityTooLarge ( t * testing . T ) {
msg := fmt . Sprintf ( "Your proposed upload size ‘ %d’ exceeds the maximum allowed object size ‘ %d’ for single PUT operation." , 1000000 , 99999 )
expectedResult := ErrorResponse {
2017-12-08 19:45:59 +00:00
StatusCode : http . StatusBadRequest ,
2017-07-23 12:24:45 +00:00
Code : "EntityTooLarge" ,
Message : msg ,
BucketName : "minio-bucket" ,
Key : "Asia/" ,
}
actualResult := ErrEntityTooLarge ( 1000000 , 99999 , "minio-bucket" , "Asia/" )
if ! reflect . DeepEqual ( expectedResult , actualResult ) {
2017-12-08 19:45:59 +00:00
t . Errorf ( "Expected result to be '%#v', but instead got '%#v'" , expectedResult , actualResult )
2017-07-23 12:24:45 +00:00
}
}
// Test validates 'ErrEntityTooSmall' error response.
func TestErrEntityTooSmall ( t * testing . T ) {
2017-12-08 19:45:59 +00:00
msg := fmt . Sprintf ( "Your proposed upload size ‘ %d’ is below the minimum allowed object size ‘ 0B’ for single PUT operation." , - 1 )
2017-07-23 12:24:45 +00:00
expectedResult := ErrorResponse {
2017-12-08 19:45:59 +00:00
StatusCode : http . StatusBadRequest ,
Code : "EntityTooSmall" ,
2017-07-23 12:24:45 +00:00
Message : msg ,
BucketName : "minio-bucket" ,
Key : "Asia/" ,
}
actualResult := ErrEntityTooSmall ( - 1 , "minio-bucket" , "Asia/" )
if ! reflect . DeepEqual ( expectedResult , actualResult ) {
2017-12-08 19:45:59 +00:00
t . Errorf ( "Expected result to be '%#v', but instead got '%#v'" , expectedResult , actualResult )
2017-07-23 12:24:45 +00:00
}
}
// Test validates 'ErrUnexpectedEOF' error response.
func TestErrUnexpectedEOF ( t * testing . T ) {
msg := fmt . Sprintf ( "Data read ‘ %s’ is not equal to the size ‘ %s’ of the input Reader." ,
strconv . FormatInt ( 100 , 10 ) , strconv . FormatInt ( 101 , 10 ) )
expectedResult := ErrorResponse {
2017-12-08 19:45:59 +00:00
StatusCode : http . StatusBadRequest ,
2017-07-23 12:24:45 +00:00
Code : "UnexpectedEOF" ,
Message : msg ,
BucketName : "minio-bucket" ,
Key : "Asia/" ,
}
actualResult := ErrUnexpectedEOF ( 100 , 101 , "minio-bucket" , "Asia/" )
if ! reflect . DeepEqual ( expectedResult , actualResult ) {
2017-12-08 19:45:59 +00:00
t . Errorf ( "Expected result to be '%#v', but instead got '%#v'" , expectedResult , actualResult )
2017-07-23 12:24:45 +00:00
}
}
// Test validates 'ErrInvalidBucketName' error response.
func TestErrInvalidBucketName ( t * testing . T ) {
expectedResult := ErrorResponse {
2017-12-08 19:45:59 +00:00
StatusCode : http . StatusBadRequest ,
Code : "InvalidBucketName" ,
Message : "Invalid Bucket name" ,
RequestID : "minio" ,
2017-07-23 12:24:45 +00:00
}
actualResult := ErrInvalidBucketName ( "Invalid Bucket name" )
if ! reflect . DeepEqual ( expectedResult , actualResult ) {
2017-12-08 19:45:59 +00:00
t . Errorf ( "Expected result to be '%#v', but instead got '%#v'" , expectedResult , actualResult )
2017-07-23 12:24:45 +00:00
}
}
// Test validates 'ErrInvalidObjectName' error response.
func TestErrInvalidObjectName ( t * testing . T ) {
expectedResult := ErrorResponse {
2017-12-08 19:45:59 +00:00
StatusCode : http . StatusNotFound ,
Code : "NoSuchKey" ,
Message : "Invalid Object Key" ,
RequestID : "minio" ,
2017-07-23 12:24:45 +00:00
}
actualResult := ErrInvalidObjectName ( "Invalid Object Key" )
if ! reflect . DeepEqual ( expectedResult , actualResult ) {
2017-12-08 19:45:59 +00:00
t . Errorf ( "Expected result to be '%#v', but instead got '%#v'" , expectedResult , actualResult )
2017-07-23 12:24:45 +00:00
}
}
// Test validates 'ErrInvalidArgument' response.
func TestErrInvalidArgument ( t * testing . T ) {
expectedResult := ErrorResponse {
2017-12-08 19:45:59 +00:00
StatusCode : http . StatusBadRequest ,
Code : "InvalidArgument" ,
Message : "Invalid Argument" ,
RequestID : "minio" ,
2017-07-23 12:24:45 +00:00
}
actualResult := ErrInvalidArgument ( "Invalid Argument" )
if ! reflect . DeepEqual ( expectedResult , actualResult ) {
2017-12-08 19:45:59 +00:00
t . Errorf ( "Expected result to be '%#v', but instead got '%#v'" , expectedResult , actualResult )
2017-07-23 12:24:45 +00:00
}
}
// Tests if the Message field is missing.
func TestErrWithoutMessage ( t * testing . T ) {
errResp := ErrorResponse {
Code : "AccessDenied" ,
RequestID : "minio" ,
}
if errResp . Error ( ) != "Access Denied." {
t . Errorf ( "Expected \"Access Denied.\", got %s" , errResp )
}
errResp = ErrorResponse {
Code : "InvalidArgument" ,
RequestID : "minio" ,
}
if errResp . Error ( ) != "Error response code InvalidArgument." {
t . Errorf ( "Expected \"Error response code InvalidArgument.\", got %s" , errResp )
}
}