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

     1  //go:build go1.7
     2  // +build go1.7
     3  
     4  package jsonrpc
     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 = `{"__type":"UnknownError", "message":"error message", "something":123}`
    22  const simpleErrJSON = `{"__type":"SimpleError", "message":"some message", "foo":123}`
    23  const simpleCasedErrJSON = `{"__type":"SimpleError", "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 = `{"__type":"OtherError", "message":"some message"}`
    34  const complexCodeErrJSON = `{"__type":"foo.bar#OtherError", "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 = `{"__type":"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  }
    52  type ErrorNested struct {
    53  	_ struct{} `type:"structure"`
    54  
    55  	Bar *string `type:"string" locationName:"bar"`
    56  	Baz *int64  `type:"integer" locationName:"baz"`
    57  }
    58  
    59  func TestUnmarshalTypedError(t *testing.T) {
    60  
    61  	respMeta := protocol.ResponseMetadata{
    62  		StatusCode: 400,
    63  		RequestID:  "abc123",
    64  	}
    65  
    66  	exceptions := map[string]func(protocol.ResponseMetadata) error{
    67  		"SimpleError": func(meta protocol.ResponseMetadata) error {
    68  			return &SimpleError{}
    69  		},
    70  		"OtherError": func(meta protocol.ResponseMetadata) error {
    71  			return &OtherError{}
    72  		},
    73  		"ComplexError": func(meta protocol.ResponseMetadata) error {
    74  			return &ComplexError{}
    75  		},
    76  	}
    77  
    78  	cases := map[string]struct {
    79  		Response *http.Response
    80  		Expect   error
    81  		Err      string
    82  	}{
    83  		"simple error": {
    84  			Response: &http.Response{
    85  				Header: http.Header{},
    86  				Body:   ioutil.NopCloser(strings.NewReader(simpleErrJSON)),
    87  			},
    88  			Expect: &SimpleError{
    89  				Message2: aws.String("some message"),
    90  				Foo:      aws.Int64(123),
    91  			},
    92  		},
    93  		"other error": {
    94  			Response: &http.Response{
    95  				Header: http.Header{},
    96  				Body:   ioutil.NopCloser(strings.NewReader(otherErrJSON)),
    97  			},
    98  			Expect: &OtherError{
    99  				Message2: aws.String("some message"),
   100  			},
   101  		},
   102  		"other complex Code error": {
   103  			Response: &http.Response{
   104  				Header: http.Header{},
   105  				Body:   ioutil.NopCloser(strings.NewReader(complexCodeErrJSON)),
   106  			},
   107  			Expect: &OtherError{
   108  				Message2: aws.String("some message"),
   109  			},
   110  		},
   111  		"complex error": {
   112  			Response: &http.Response{
   113  				Header: http.Header{},
   114  				Body:   ioutil.NopCloser(strings.NewReader(complexErrJSON)),
   115  			},
   116  			Expect: &ComplexError{
   117  				Message2: aws.String("some message"),
   118  				Foo: &ErrorNested{
   119  					Bar: aws.String("abc123"),
   120  					Baz: aws.Int64(123),
   121  				},
   122  			},
   123  		},
   124  		"unknown error": {
   125  			Response: &http.Response{
   126  				Header: http.Header{},
   127  				Body:   ioutil.NopCloser(strings.NewReader(unknownErrJSON)),
   128  			},
   129  			Expect: awserr.NewRequestFailure(
   130  				awserr.New("UnknownError", "error message", nil),
   131  				respMeta.StatusCode,
   132  				respMeta.RequestID,
   133  			),
   134  		},
   135  		"invalid error": {
   136  			Response: &http.Response{
   137  				StatusCode: 400,
   138  				Header:     http.Header{},
   139  				Body:       ioutil.NopCloser(strings.NewReader(`{`)),
   140  			},
   141  			Err: "failed decoding",
   142  		},
   143  		"mixed case fields": {
   144  			Response: &http.Response{
   145  				Header: http.Header{},
   146  				Body:   ioutil.NopCloser(strings.NewReader(simpleCasedErrJSON)),
   147  			},
   148  			Expect: &SimpleError{
   149  				Message2: aws.String("some message"),
   150  				Foo:      aws.Int64(123),
   151  			},
   152  		},
   153  	}
   154  
   155  	for name, c := range cases {
   156  		t.Run(name, func(t *testing.T) {
   157  			u := NewUnmarshalTypedError(exceptions)
   158  			v, err := u.UnmarshalError(c.Response, respMeta)
   159  
   160  			if len(c.Err) != 0 {
   161  				if err == nil {
   162  					t.Fatalf("expect error, got none")
   163  				}
   164  				if e, a := c.Err, err.Error(); !strings.Contains(a, e) {
   165  					t.Fatalf("expect %v in error, got %v", e, a)
   166  				}
   167  			} else if err != nil {
   168  				t.Fatalf("expect no error, got %v", err)
   169  			}
   170  
   171  			if e, a := c.Expect, v; !reflect.DeepEqual(e, a) {
   172  				t.Errorf("expect %+#v, got %#+v", e, a)
   173  			}
   174  		})
   175  	}
   176  }
   177  
   178  func TestUnmarshalError_SerializationError(t *testing.T) {
   179  	cases := map[string]struct {
   180  		Request     *request.Request
   181  		ExpectMsg   string
   182  		ExpectBytes []byte
   183  	}{
   184  		"empty body": {
   185  			Request: &request.Request{
   186  				Data: &struct{}{},
   187  				HTTPResponse: &http.Response{
   188  					StatusCode: 400,
   189  					Header: http.Header{
   190  						"X-Amzn-Requestid": []string{"abc123"},
   191  					},
   192  					Body: ioutil.NopCloser(
   193  						bytes.NewReader([]byte{}),
   194  					),
   195  				},
   196  			},
   197  			ExpectMsg: "error message missing",
   198  		},
   199  		"HTML body": {
   200  			Request: &request.Request{
   201  				Data: &struct{}{},
   202  				HTTPResponse: &http.Response{
   203  					StatusCode: 400,
   204  					Header: http.Header{
   205  						"X-Amzn-Requestid": []string{"abc123"},
   206  					},
   207  					Body: ioutil.NopCloser(
   208  						bytes.NewReader([]byte(`<html></html>`)),
   209  					),
   210  				},
   211  			},
   212  			ExpectBytes: []byte(`<html></html>`),
   213  			ExpectMsg:   "failed decoding",
   214  		},
   215  	}
   216  
   217  	for name, c := range cases {
   218  		t.Run(name, func(t *testing.T) {
   219  			req := c.Request
   220  
   221  			UnmarshalError(req)
   222  			if req.Error == nil {
   223  				t.Fatal("expect error, got none")
   224  			}
   225  
   226  			aerr := req.Error.(awserr.RequestFailure)
   227  			if e, a := request.ErrCodeSerialization, aerr.Code(); e != a {
   228  				t.Errorf("expect %v, got %v", e, a)
   229  			}
   230  
   231  			uerr := aerr.OrigErr().(awserr.UnmarshalError)
   232  			if e, a := c.ExpectMsg, uerr.Message(); !strings.Contains(a, e) {
   233  				t.Errorf("Expect %q, in %q", e, a)
   234  			}
   235  			if e, a := c.ExpectBytes, uerr.Bytes(); !bytes.Equal(e, a) {
   236  				t.Errorf("expect:\n%v\nactual:\n%v", hex.Dump(e), hex.Dump(a))
   237  			}
   238  		})
   239  	}
   240  }