github.com/aavshr/aws-sdk-go@v1.41.3/private/protocol/restjson/unmarshal_error_test.go (about)

     1  //go:build go1.8
     2  // +build go1.8
     3  
     4  package restjson
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/hex"
     9  	"io/ioutil"
    10  	"net/http"
    11  	"reflect"
    12  	"strings"
    13  	"testing"
    14  
    15  	"github.com/aavshr/aws-sdk-go/aws"
    16  	"github.com/aavshr/aws-sdk-go/aws/awserr"
    17  	"github.com/aavshr/aws-sdk-go/aws/request"
    18  	"github.com/aavshr/aws-sdk-go/private/protocol"
    19  )
    20  
    21  const unknownErrJSON = `{"code":"UnknownError", "message":"error message", "something":123}`
    22  const simpleErrJSON = `{"code":"SimpleError", "message":"some message", "foo":123}`
    23  const simpleNoCodeJSON = `{"message":"some message", "foo":123}`
    24  
    25  type SimpleError struct {
    26  	_ struct{} `type:"structure"`
    27  	error
    28  
    29  	Message2 *string `type:"string" locationName:"message"`
    30  	Foo      *int64  `type:"integer" locationName:"foo"`
    31  }
    32  
    33  const otherErrJSON = `{"code":"OtherError", "message":"some message"}`
    34  const complexCodeErrJSON = `{"code":"OtherError:foo:bar", "message":"some message"}`
    35  
    36  type OtherError struct {
    37  	_ struct{} `type:"structure"`
    38  	error
    39  
    40  	Message2 *string `type:"string" locationName:"message"`
    41  }
    42  
    43  const complexErrJSON = `{"code":"ComplexError", "message":"some message", "foo": {"bar":"abc123", "baz":123}}`
    44  
    45  type ComplexError struct {
    46  	_ struct{} `type:"structure"`
    47  	error
    48  
    49  	Message2  *string      `type:"string" locationName:"message"`
    50  	Foo       *ErrorNested `type:"structure" locationName:"foo"`
    51  	HeaderVal *string      `type:"string" location:"header" locationName:"some-header"`
    52  	Status    *int64       `type:"integer" location:"statusCode"`
    53  }
    54  type ErrorNested struct {
    55  	_ struct{} `type:"structure"`
    56  
    57  	Bar *string `type:"string" locationName:"bar"`
    58  	Baz *int64  `type:"integer" locationName:"baz"`
    59  }
    60  
    61  func TestUnmarshalTypedError(t *testing.T) {
    62  
    63  	respMeta := protocol.ResponseMetadata{
    64  		StatusCode: 400,
    65  		RequestID:  "abc123",
    66  	}
    67  
    68  	exceptions := map[string]func(protocol.ResponseMetadata) error{
    69  		"SimpleError": func(meta protocol.ResponseMetadata) error {
    70  			return &SimpleError{}
    71  		},
    72  		"OtherError": func(meta protocol.ResponseMetadata) error {
    73  			return &OtherError{}
    74  		},
    75  		"ComplexError": func(meta protocol.ResponseMetadata) error {
    76  			return &ComplexError{}
    77  		},
    78  	}
    79  
    80  	cases := map[string]struct {
    81  		Response *http.Response
    82  		Expect   error
    83  		Err      string
    84  	}{
    85  		"simple error": {
    86  			Response: &http.Response{
    87  				Header: http.Header{},
    88  				Body:   ioutil.NopCloser(strings.NewReader(simpleErrJSON)),
    89  			},
    90  			Expect: &SimpleError{
    91  				Message2: aws.String("some message"),
    92  				Foo:      aws.Int64(123),
    93  			},
    94  		},
    95  		"other error": {
    96  			Response: &http.Response{
    97  				Header: http.Header{},
    98  				Body:   ioutil.NopCloser(strings.NewReader(otherErrJSON)),
    99  			},
   100  			Expect: &OtherError{
   101  				Message2: aws.String("some message"),
   102  			},
   103  		},
   104  		"other complex Code error": {
   105  			Response: &http.Response{
   106  				Header: http.Header{},
   107  				Body:   ioutil.NopCloser(strings.NewReader(complexCodeErrJSON)),
   108  			},
   109  			Expect: &OtherError{
   110  				Message2: aws.String("some message"),
   111  			},
   112  		},
   113  		"complex error": {
   114  			Response: &http.Response{
   115  				StatusCode: 400,
   116  				Header: http.Header{
   117  					"Some-Header": []string{"headval"},
   118  				},
   119  				Body: ioutil.NopCloser(strings.NewReader(complexErrJSON)),
   120  			},
   121  			Expect: &ComplexError{
   122  				Message2:  aws.String("some message"),
   123  				HeaderVal: aws.String("headval"),
   124  				Status:    aws.Int64(400),
   125  				Foo: &ErrorNested{
   126  					Bar: aws.String("abc123"),
   127  					Baz: aws.Int64(123),
   128  				},
   129  			},
   130  		},
   131  		"unknown error": {
   132  			Response: &http.Response{
   133  				Header: http.Header{},
   134  				Body:   ioutil.NopCloser(strings.NewReader(unknownErrJSON)),
   135  			},
   136  			Expect: awserr.NewRequestFailure(
   137  				awserr.New("UnknownError", "error message", nil),
   138  				respMeta.StatusCode,
   139  				respMeta.RequestID,
   140  			),
   141  		},
   142  		"invalid error": {
   143  			Response: &http.Response{
   144  				StatusCode: 400,
   145  				Header:     http.Header{},
   146  				Body:       ioutil.NopCloser(strings.NewReader(`{`)),
   147  			},
   148  			Err: "failed decoding",
   149  		},
   150  		"unknown from header": {
   151  			Response: &http.Response{
   152  				Header: http.Header{
   153  					errorTypeHeader:    []string{"UnknownError"},
   154  					errorMessageHeader: []string{"error message"},
   155  				},
   156  				Body: ioutil.NopCloser(nil),
   157  			},
   158  			Expect: awserr.NewRequestFailure(
   159  				awserr.New("UnknownError", "error message", nil),
   160  				respMeta.StatusCode,
   161  				respMeta.RequestID,
   162  			),
   163  		},
   164  		"code from header": {
   165  			Response: &http.Response{
   166  				Header: http.Header{
   167  					errorTypeHeader: []string{"SimpleError"},
   168  				},
   169  				Body: ioutil.NopCloser(strings.NewReader(simpleNoCodeJSON)),
   170  			},
   171  			Expect: &SimpleError{
   172  				Message2: aws.String("some message"),
   173  				Foo:      aws.Int64(123),
   174  			},
   175  		},
   176  		"ignore message header": {
   177  			Response: &http.Response{
   178  				Header: http.Header{
   179  					errorTypeHeader:    []string{"SimpleError"},
   180  					errorMessageHeader: []string{"error message"},
   181  				},
   182  				Body: ioutil.NopCloser(strings.NewReader(simpleNoCodeJSON)),
   183  			},
   184  			Expect: &SimpleError{
   185  				Message2: aws.String("some message"),
   186  				Foo:      aws.Int64(123),
   187  			},
   188  		},
   189  	}
   190  
   191  	for name, c := range cases {
   192  		t.Run(name, func(t *testing.T) {
   193  			u := NewUnmarshalTypedError(exceptions)
   194  			v, err := u.UnmarshalError(c.Response, respMeta)
   195  
   196  			if len(c.Err) != 0 {
   197  				if err == nil {
   198  					t.Fatalf("expect error, got none")
   199  				}
   200  				if e, a := c.Err, err.Error(); !strings.Contains(a, e) {
   201  					t.Fatalf("expect %v in error, got %v", e, a)
   202  				}
   203  			} else if err != nil {
   204  				t.Fatalf("expect no error, got %v", err)
   205  			}
   206  
   207  			if e, a := c.Expect, v; !reflect.DeepEqual(e, a) {
   208  				t.Errorf("expect %+#v, got %#+v", e, a)
   209  			}
   210  		})
   211  	}
   212  }
   213  
   214  func TestUnmarshalError_SerializationError(t *testing.T) {
   215  	cases := map[string]struct {
   216  		Request     *request.Request
   217  		ExpectMsg   string
   218  		ExpectBytes []byte
   219  	}{
   220  		"empty body": {
   221  			Request: &request.Request{
   222  				Data: &struct{}{},
   223  				HTTPResponse: &http.Response{
   224  					StatusCode: 400,
   225  					Header: http.Header{
   226  						"X-Amzn-Requestid": []string{"abc123"},
   227  					},
   228  					Body: ioutil.NopCloser(
   229  						bytes.NewReader([]byte{}),
   230  					),
   231  				},
   232  			},
   233  			ExpectMsg: "error message missing",
   234  		},
   235  		"HTML body": {
   236  			Request: &request.Request{
   237  				Data: &struct{}{},
   238  				HTTPResponse: &http.Response{
   239  					StatusCode: 400,
   240  					Header: http.Header{
   241  						"X-Amzn-Requestid": []string{"abc123"},
   242  					},
   243  					Body: ioutil.NopCloser(
   244  						bytes.NewReader([]byte(`<html></html>`)),
   245  					),
   246  				},
   247  			},
   248  			ExpectBytes: []byte(`<html></html>`),
   249  			ExpectMsg:   "failed decoding",
   250  		},
   251  	}
   252  
   253  	for name, c := range cases {
   254  		t.Run(name, func(t *testing.T) {
   255  			req := c.Request
   256  
   257  			UnmarshalError(req)
   258  			if req.Error == nil {
   259  				t.Fatal("expect error, got none")
   260  			}
   261  
   262  			aerr := req.Error.(awserr.RequestFailure)
   263  			if e, a := request.ErrCodeSerialization, aerr.Code(); e != a {
   264  				t.Errorf("expect %v, got %v", e, a)
   265  			}
   266  
   267  			uerr := aerr.OrigErr().(awserr.UnmarshalError)
   268  			if e, a := c.ExpectMsg, uerr.Message(); !strings.Contains(a, e) {
   269  				t.Errorf("Expect %q, in %q", e, a)
   270  			}
   271  			if e, a := c.ExpectBytes, uerr.Bytes(); !bytes.Equal(e, a) {
   272  				t.Errorf("expect:\n%v\nactual:\n%v", hex.Dump(e), hex.Dump(a))
   273  			}
   274  		})
   275  	}
   276  }