func TestGet(t *testing.T) { assert := assert.New(t) svc := awstesting.NewClient(&aws.Config{ Credentials: credentials.NewStaticCredentials("AKID", "SECRET", "SESSION"), Region: aws.String("ap-southeast-2"), }) r := svc.NewRequest( &request.Operation{ Name: "OpName", HTTPMethod: "GET", HTTPPath: "/", }, nil, nil, ) r.Build() assert.Equal("GET", r.HTTPRequest.Method) assert.Equal("", r.HTTPRequest.URL.Query().Get("Signature")) Sign(r) assert.NoError(r.Error) t.Logf("Signature: %s", r.HTTPRequest.URL.Query().Get("Signature")) assert.NotEqual("", r.HTTPRequest.URL.Query().Get("Signature")) }
func TestPreResignRequestExpiredCreds(t *testing.T) { provider := &credentials.StaticProvider{Value: credentials.Value{ AccessKeyID: "AKID", SecretAccessKey: "SECRET", SessionToken: "SESSION", }} creds := credentials.NewCredentials(provider) svc := awstesting.NewClient(&aws.Config{Credentials: creds}) r := svc.NewRequest( &request.Operation{ Name: "BatchGetItem", HTTPMethod: "POST", HTTPPath: "/", }, nil, nil, ) r.ExpireTime = time.Minute * 10 Sign(r) querySig := r.HTTPRequest.URL.Query().Get("X-Amz-Signature") creds.Expire() r.Time = time.Now().Add(time.Hour * 48) Sign(r) assert.NotEqual(t, querySig, r.HTTPRequest.URL.Query().Get("X-Amz-Signature")) }
func TestAfterRetryRefreshCreds(t *testing.T) { os.Clearenv() credProvider := &mockCredsProvider{} svc := awstesting.NewClient(&aws.Config{ Credentials: credentials.NewCredentials(credProvider), MaxRetries: aws.Int(1), }) svc.Handlers.Clear() svc.Handlers.ValidateResponse.PushBack(func(r *request.Request) { r.Error = awserr.New("UnknownError", "", nil) r.HTTPResponse = &http.Response{StatusCode: 400} }) svc.Handlers.UnmarshalError.PushBack(func(r *request.Request) { r.Error = awserr.New("ExpiredTokenException", "", nil) }) svc.Handlers.AfterRetry.PushBackNamed(corehandlers.AfterRetryHandler) assert.True(t, svc.Config.Credentials.IsExpired(), "Expect to start out expired") assert.False(t, credProvider.retrieveCalled) req := svc.NewRequest(&request.Operation{Name: "Operation"}, nil, nil) req.Send() assert.True(t, svc.Config.Credentials.IsExpired()) assert.False(t, credProvider.retrieveCalled) _, err := svc.Config.Credentials.Get() assert.NoError(t, err) assert.True(t, credProvider.retrieveCalled) }
func TestRequestUserAgent(t *testing.T) { s := awstesting.NewClient(&aws.Config{Region: aws.String("us-east-1")}) // s.Handlers.Validate.Clear() req := s.NewRequest(&request.Operation{Name: "Operation"}, nil, &testData{}) req.HTTPRequest.Header.Set("User-Agent", "foo/bar") assert.NoError(t, req.Build()) expectUA := fmt.Sprintf("foo/bar %s/%s (%s; %s; %s)", aws.SDKName, aws.SDKVersion, runtime.Version(), runtime.GOOS, runtime.GOARCH) assert.Equal(t, expectUA, req.HTTPRequest.Header.Get("User-Agent")) }
func TestValidateEndpointHandler(t *testing.T) { os.Clearenv() svc := awstesting.NewClient(aws.NewConfig().WithRegion("us-west-2")) svc.Handlers.Clear() svc.Handlers.Validate.PushBackNamed(corehandlers.ValidateEndpointHandler) req := svc.NewRequest(&request.Operation{Name: "Operation"}, nil, nil) err := req.Build() assert.NoError(t, err) }
func benchRESTXMLBuild(b *testing.B, op *request.Operation, params interface{}) { svc := awstesting.NewClient() svc.ServiceName = "cloudfront" svc.APIVersion = "2015-04-17" for i := 0; i < b.N; i++ { r := svc.NewRequest(op, params, nil) restxml.Build(r) if r.Error != nil { b.Fatal("Unexpected error", r.Error) } } }
func BenchmarkRESTBuild_Complex_elastictranscoderCreateJobInput(b *testing.B) { svc := awstesting.NewClient() svc.ServiceName = "elastictranscoder" svc.APIVersion = "2012-09-25" for i := 0; i < b.N; i++ { r := svc.NewRequest(&request.Operation{Name: "CreateJobInput"}, restjsonBuildParms, nil) rest.Build(r) if r.Error != nil { b.Fatal("Unexpected error", r.Error) } } }
func BenchmarkJSONRPCBuild_Simple_dynamodbPutItem(b *testing.B) { svc := awstesting.NewClient() params := getDynamodbPutItemParams(b) for i := 0; i < b.N; i++ { r := svc.NewRequest(&request.Operation{Name: "Operation"}, params, nil) jsonrpc.Build(r) if r.Error != nil { b.Fatal("Unexpected error", r.Error) } } }
func TestValidateEndpointHandlerErrorRegion(t *testing.T) { os.Clearenv() svc := awstesting.NewClient() svc.Handlers.Clear() svc.Handlers.Validate.PushBackNamed(corehandlers.ValidateEndpointHandler) req := svc.NewRequest(&request.Operation{Name: "Operation"}, nil, nil) err := req.Build() assert.Error(t, err) assert.Equal(t, aws.ErrMissingRegion, err) }
func BenchmarkJSONUtilBuild_Simple_dynamodbPutItem(b *testing.B) { svc := awstesting.NewClient() params := getDynamodbPutItemParams(b) for i := 0; i < b.N; i++ { r := svc.NewRequest(&request.Operation{Name: "Operation"}, params, nil) _, err := jsonutil.BuildJSON(r.Params) if err != nil { b.Fatal("Unexpected error", err) } } }
func TestWaiterStatus(t *testing.T) { svc := &mockClient{Client: awstesting.NewClient(&aws.Config{ Region: aws.String("mock-region"), })} svc.Handlers.Send.Clear() // mock sending svc.Handlers.Unmarshal.Clear() svc.Handlers.UnmarshalMeta.Clear() svc.Handlers.ValidateResponse.Clear() reqNum := 0 svc.Handlers.Build.PushBack(func(r *request.Request) { reqNum++ }) svc.Handlers.Send.PushBack(func(r *request.Request) { code := 200 if reqNum == 3 { code = 404 r.Error = awserr.New("NotFound", "Not Found", nil) } r.HTTPResponse = &http.Response{ StatusCode: code, Status: http.StatusText(code), Body: ioutil.NopCloser(bytes.NewReader([]byte{})), } }) waiterCfg := waiter.Config{ Operation: "Mock", Delay: 0, MaxAttempts: 10, Acceptors: []waiter.WaitAcceptor{ { State: "success", Matcher: "status", Argument: "", Expected: 404, }, }, } w := waiter.Waiter{ Client: svc, Input: &MockInput{}, Config: waiterCfg, } err := w.Wait() assert.NoError(t, err) assert.Equal(t, 3, reqNum) }
func TestSendHandlerError(t *testing.T) { svc := awstesting.NewClient(&aws.Config{ HTTPClient: &http.Client{ Transport: &testSendHandlerTransport{}, }, }) svc.Handlers.Clear() svc.Handlers.Send.PushBackNamed(corehandlers.SendHandler) r := svc.NewRequest(&request.Operation{Name: "Operation"}, nil, nil) r.Send() assert.Error(t, r.Error) assert.NotNil(t, r.HTTPResponse) }
func benchEC2QueryBuild(b *testing.B, opName string, params interface{}) { svc := awstesting.NewClient() svc.ServiceName = "ec2" svc.APIVersion = "2015-04-15" for i := 0; i < b.N; i++ { r := svc.NewRequest(&request.Operation{ Name: opName, HTTPMethod: "POST", HTTPPath: "/", }, params, nil) ec2query.Build(r) if r.Error != nil { b.Fatal("Unexpected error", r.Error) } } }
func TestRequestExhaustRetries(t *testing.T) { delays := []time.Duration{} sleepDelay := func(delay time.Duration) { delays = append(delays, delay) } reqNum := 0 reqs := []http.Response{ {StatusCode: 500, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)}, {StatusCode: 500, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)}, {StatusCode: 500, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)}, {StatusCode: 500, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)}, } s := awstesting.NewClient(aws.NewConfig().WithSleepDelay(sleepDelay)) s.Handlers.Validate.Clear() s.Handlers.Unmarshal.PushBack(unmarshal) s.Handlers.UnmarshalError.PushBack(unmarshalError) s.Handlers.Send.Clear() // mock sending s.Handlers.Send.PushBack(func(r *request.Request) { r.HTTPResponse = &reqs[reqNum] reqNum++ }) r := s.NewRequest(&request.Operation{Name: "Operation"}, nil, nil) err := r.Send() assert.NotNil(t, err) if e, ok := err.(awserr.RequestFailure); ok { assert.Equal(t, 500, e.StatusCode()) } else { assert.Fail(t, "Expected error to be a service failure") } assert.Equal(t, "UnknownError", err.(awserr.Error).Code()) assert.Equal(t, "An error occurred.", err.(awserr.Error).Message()) assert.Equal(t, 3, int(r.RetryCount)) expectDelays := []struct{ min, max time.Duration }{{30, 59}, {60, 118}, {120, 236}} for i, v := range delays { min := expectDelays[i].min * time.Millisecond max := expectDelays[i].max * time.Millisecond assert.True(t, min <= v && v <= max, "Expect delay to be within range, i:%d, v:%s, min:%s, max:%s", i, v, min, max) } }
func BenchmarkRESTBuild_Simple_elastictranscoderListJobsByPipeline(b *testing.B) { svc := awstesting.NewClient() svc.ServiceName = "elastictranscoder" svc.APIVersion = "2012-09-25" params := &elastictranscoder.ListJobsByPipelineInput{ PipelineId: aws.String("Id"), // Required Ascending: aws.String("Ascending"), PageToken: aws.String("Id"), } for i := 0; i < b.N; i++ { r := svc.NewRequest(&request.Operation{Name: "ListJobsByPipeline"}, params, nil) rest.Build(r) if r.Error != nil { b.Fatal("Unexpected error", r.Error) } } }
func TestResignRequestExpiredCreds(t *testing.T) { creds := credentials.NewStaticCredentials("AKID", "SECRET", "SESSION") svc := awstesting.NewClient(&aws.Config{Credentials: creds}) r := svc.NewRequest( &request.Operation{ Name: "BatchGetItem", HTTPMethod: "POST", HTTPPath: "/", }, nil, nil, ) Sign(r) querySig := r.HTTPRequest.Header.Get("Authorization") creds.Expire() Sign(r) assert.NotEqual(t, querySig, r.HTTPRequest.Header.Get("Authorization")) }
func TestIgnoreResignRequestWithValidCreds(t *testing.T) { svc := awstesting.NewClient(&aws.Config{ Credentials: credentials.NewStaticCredentials("AKID", "SECRET", "SESSION"), Region: aws.String("us-west-2"), }) r := svc.NewRequest( &request.Operation{ Name: "BatchGetItem", HTTPMethod: "POST", HTTPPath: "/", }, nil, nil, ) Sign(r) sig := r.HTTPRequest.Header.Get("Authorization") Sign(r) assert.Equal(t, sig, r.HTTPRequest.Header.Get("Authorization")) }
// test that the request is retried after the credentials are expired. func TestRequestRecoverExpiredCreds(t *testing.T) { reqNum := 0 reqs := []http.Response{ {StatusCode: 400, Body: body(`{"__type":"ExpiredTokenException","message":"expired token"}`)}, {StatusCode: 200, Body: body(`{"data":"valid"}`)}, } s := awstesting.NewClient(&aws.Config{MaxRetries: aws.Int(10), Credentials: credentials.NewStaticCredentials("AKID", "SECRET", "")}) s.Handlers.Validate.Clear() s.Handlers.Unmarshal.PushBack(unmarshal) s.Handlers.UnmarshalError.PushBack(unmarshalError) credExpiredBeforeRetry := false credExpiredAfterRetry := false s.Handlers.AfterRetry.PushBack(func(r *request.Request) { credExpiredAfterRetry = r.Config.Credentials.IsExpired() }) s.Handlers.Sign.Clear() s.Handlers.Sign.PushBack(func(r *request.Request) { r.Config.Credentials.Get() }) s.Handlers.Send.Clear() // mock sending s.Handlers.Send.PushBack(func(r *request.Request) { r.HTTPResponse = &reqs[reqNum] reqNum++ }) out := &testData{} r := s.NewRequest(&request.Operation{Name: "Operation"}, nil, out) err := r.Send() assert.Nil(t, err) assert.False(t, credExpiredBeforeRetry, "Expect valid creds before retry check") assert.True(t, credExpiredAfterRetry, "Expect expired creds after retry check") assert.False(t, s.Config.Credentials.IsExpired(), "Expect valid creds after cred expired recovery") assert.Equal(t, 1, int(r.RetryCount)) assert.Equal(t, "valid", out.Data) }
// test that retries don't occur for 4xx status codes with a response type that can't be retried func TestRequest4xxUnretryable(t *testing.T) { s := awstesting.NewClient(aws.NewConfig().WithMaxRetries(10)) s.Handlers.Validate.Clear() s.Handlers.Unmarshal.PushBack(unmarshal) s.Handlers.UnmarshalError.PushBack(unmarshalError) s.Handlers.Send.Clear() // mock sending s.Handlers.Send.PushBack(func(r *request.Request) { r.HTTPResponse = &http.Response{StatusCode: 401, Body: body(`{"__type":"SignatureDoesNotMatch","message":"Signature does not match."}`)} }) out := &testData{} r := s.NewRequest(&request.Operation{Name: "Operation"}, nil, out) err := r.Send() assert.NotNil(t, err) if e, ok := err.(awserr.RequestFailure); ok { assert.Equal(t, 401, e.StatusCode()) } else { assert.Fail(t, "Expected error to be a service failure") } assert.Equal(t, "SignatureDoesNotMatch", err.(awserr.Error).Code()) assert.Equal(t, "Signature does not match.", err.(awserr.Error).Message()) assert.Equal(t, 0, int(r.RetryCount)) }
func TestAnonymousCredentials(t *testing.T) { svc := awstesting.NewClient(&aws.Config{Credentials: credentials.AnonymousCredentials}) r := svc.NewRequest( &request.Operation{ Name: "BatchGetItem", HTTPMethod: "POST", HTTPPath: "/", }, nil, nil, ) Sign(r) urlQ := r.HTTPRequest.URL.Query() assert.Empty(t, urlQ.Get("X-Amz-Signature")) assert.Empty(t, urlQ.Get("X-Amz-Credential")) assert.Empty(t, urlQ.Get("X-Amz-SignedHeaders")) assert.Empty(t, urlQ.Get("X-Amz-Date")) hQ := r.HTTPRequest.Header assert.Empty(t, hQ.Get("Authorization")) assert.Empty(t, hQ.Get("X-Amz-Date")) }
func TestAnonymousCredentials(t *testing.T) { assert := assert.New(t) svc := awstesting.NewClient(&aws.Config{ Credentials: credentials.AnonymousCredentials, Region: aws.String("ap-southeast-2"), }) r := svc.NewRequest( &request.Operation{ Name: "PutAttributes", HTTPMethod: "POST", HTTPPath: "/", }, nil, nil, ) r.Build() Sign(r) req := r.HTTPRequest req.ParseForm() assert.Empty(req.PostForm.Get("Signature")) }
// test that retries occur for 5xx status codes func TestRequestRecoverRetry5xx(t *testing.T) { reqNum := 0 reqs := []http.Response{ {StatusCode: 500, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)}, {StatusCode: 501, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)}, {StatusCode: 200, Body: body(`{"data":"valid"}`)}, } s := awstesting.NewClient(aws.NewConfig().WithMaxRetries(10)) s.Handlers.Validate.Clear() s.Handlers.Unmarshal.PushBack(unmarshal) s.Handlers.UnmarshalError.PushBack(unmarshalError) s.Handlers.Send.Clear() // mock sending s.Handlers.Send.PushBack(func(r *request.Request) { r.HTTPResponse = &reqs[reqNum] reqNum++ }) out := &testData{} r := s.NewRequest(&request.Operation{Name: "Operation"}, nil, out) err := r.Send() assert.Nil(t, err) assert.Equal(t, 2, int(r.RetryCount)) assert.Equal(t, "valid", out.Data) }
// test that retries occur for 4xx status codes with a response type that can be retried - see `shouldRetry` func TestRequestRecoverRetry4xxRetryable(t *testing.T) { reqNum := 0 reqs := []http.Response{ {StatusCode: 400, Body: body(`{"__type":"Throttling","message":"Rate exceeded."}`)}, {StatusCode: 429, Body: body(`{"__type":"ProvisionedThroughputExceededException","message":"Rate exceeded."}`)}, {StatusCode: 200, Body: body(`{"data":"valid"}`)}, } s := awstesting.NewClient(aws.NewConfig().WithMaxRetries(10)) s.Handlers.Validate.Clear() s.Handlers.Unmarshal.PushBack(unmarshal) s.Handlers.UnmarshalError.PushBack(unmarshalError) s.Handlers.Send.Clear() // mock sending s.Handlers.Send.PushBack(func(r *request.Request) { r.HTTPResponse = &reqs[reqNum] reqNum++ }) out := &testData{} r := s.NewRequest(&request.Operation{Name: "Operation"}, nil, out) err := r.Send() assert.Nil(t, err) assert.Equal(t, 2, int(r.RetryCount)) assert.Equal(t, "valid", out.Data) }
func TestWaiterError(t *testing.T) { svc := &mockClient{Client: awstesting.NewClient(&aws.Config{ Region: aws.String("mock-region"), })} svc.Handlers.Send.Clear() // mock sending svc.Handlers.Unmarshal.Clear() svc.Handlers.UnmarshalMeta.Clear() svc.Handlers.UnmarshalError.Clear() svc.Handlers.ValidateResponse.Clear() reqNum := 0 resps := []*MockOutput{ { // Request 1 States: []*MockState{ {State: aws.String("pending")}, {State: aws.String("pending")}, }, }, { // Request 2, error case }, { // Request 3 States: []*MockState{ {State: aws.String("running")}, {State: aws.String("running")}, }, }, } numBuiltReq := 0 svc.Handlers.Build.PushBack(func(r *request.Request) { numBuiltReq++ }) svc.Handlers.Send.PushBack(func(r *request.Request) { code := 200 if reqNum == 1 { code = 400 } r.HTTPResponse = &http.Response{ StatusCode: code, Status: http.StatusText(code), Body: ioutil.NopCloser(bytes.NewReader([]byte{})), } }) svc.Handlers.Unmarshal.PushBack(func(r *request.Request) { if reqNum >= len(resps) { assert.Fail(t, "too many polling requests made") return } r.Data = resps[reqNum] reqNum++ }) svc.Handlers.UnmarshalMeta.PushBack(func(r *request.Request) { if reqNum == 1 { r.Error = awserr.New("MockException", "mock exception message", nil) // If there was an error unmarshal error will be called instead of unmarshal // need to increment count here also reqNum++ } }) waiterCfg := waiter.Config{ Operation: "Mock", Delay: 0, MaxAttempts: 10, Acceptors: []waiter.WaitAcceptor{ { State: "success", Matcher: "pathAll", Argument: "States[].State", Expected: "running", }, { State: "retry", Matcher: "error", Argument: "", Expected: "MockException", }, }, } w := waiter.Waiter{ Client: svc, Input: &MockInput{}, Config: waiterCfg, } err := w.Wait() assert.NoError(t, err) assert.Equal(t, 3, numBuiltReq) assert.Equal(t, 3, reqNum) }
func TestWaiterPathAll(t *testing.T) { svc := &mockClient{Client: awstesting.NewClient(&aws.Config{ Region: aws.String("mock-region"), })} svc.Handlers.Send.Clear() // mock sending svc.Handlers.Unmarshal.Clear() svc.Handlers.UnmarshalMeta.Clear() svc.Handlers.ValidateResponse.Clear() reqNum := 0 resps := []*MockOutput{ { // Request 1 States: []*MockState{ {State: aws.String("pending")}, {State: aws.String("pending")}, }, }, { // Request 2 States: []*MockState{ {State: aws.String("running")}, {State: aws.String("pending")}, }, }, { // Request 3 States: []*MockState{ {State: aws.String("running")}, {State: aws.String("running")}, }, }, } numBuiltReq := 0 svc.Handlers.Build.PushBack(func(r *request.Request) { numBuiltReq++ }) svc.Handlers.Unmarshal.PushBack(func(r *request.Request) { if reqNum >= len(resps) { assert.Fail(t, "too many polling requests made") return } r.Data = resps[reqNum] reqNum++ }) waiterCfg := waiter.Config{ Operation: "Mock", Delay: 0, MaxAttempts: 10, Acceptors: []waiter.WaitAcceptor{ { State: "success", Matcher: "pathAll", Argument: "States[].State", Expected: "running", }, }, } w := waiter.Waiter{ Client: svc, Input: &MockInput{}, Config: waiterCfg, } err := w.Wait() assert.NoError(t, err) assert.Equal(t, 3, numBuiltReq) assert.Equal(t, 3, reqNum) }