...

Source file src/github.com/Azure/go-autorest/autorest/azure/async_test.go

Documentation: github.com/Azure/go-autorest/autorest/azure

     1  package azure
     2  
     3  // Copyright 2017 Microsoft Corporation
     4  //
     5  //  Licensed under the Apache License, Version 2.0 (the "License");
     6  //  you may not use this file except in compliance with the License.
     7  //  You may obtain a copy of the License at
     8  //
     9  //      http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  //  Unless required by applicable law or agreed to in writing, software
    12  //  distributed under the License is distributed on an "AS IS" BASIS,
    13  //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  //  See the License for the specific language governing permissions and
    15  //  limitations under the License.
    16  
    17  import (
    18  	"context"
    19  	"encoding/json"
    20  	"errors"
    21  	"fmt"
    22  	"net/http"
    23  	"reflect"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/Azure/go-autorest/autorest"
    28  	"github.com/Azure/go-autorest/autorest/mocks"
    29  )
    30  
    31  func TestCreateFromInvalidRequestVerb(t *testing.T) {
    32  	resp := mocks.NewResponseWithBodyAndStatus(nil, http.StatusOK, "some status")
    33  	resp.Request = mocks.NewRequestWithParams(http.MethodGet, mocks.TestURL, nil)
    34  	_, err := createPollingTracker(resp)
    35  	if err == nil {
    36  		t.Fatal("unexpected nil error")
    37  	}
    38  }
    39  
    40  // DELETE
    41  func TestCreateDeleteTracker201Success(t *testing.T) {
    42  	resp := newAsyncResp(newAsyncReq(http.MethodDelete, nil), http.StatusCreated, nil)
    43  	mocks.SetLocationHeader(resp, mocks.TestLocationURL)
    44  	pt, err := createPollingTracker(resp)
    45  	if err != nil {
    46  		t.Fatalf("failed to create tracker: %v", err)
    47  	}
    48  	if pt.pollingMethod() != PollingLocation {
    49  		t.Fatalf("wrong polling method: %s", pt.pollingMethod())
    50  	}
    51  	if pt.finalGetURL() != mocks.TestLocationURL {
    52  		t.Fatalf("wrong final GET URL: %s", pt.finalGetURL())
    53  	}
    54  }
    55  
    56  func TestCreateDeleteTracker201FailNoLocation(t *testing.T) {
    57  	resp := newAsyncResp(newAsyncReq(http.MethodDelete, nil), http.StatusCreated, nil)
    58  	_, err := createPollingTracker(resp)
    59  	if err == nil {
    60  		t.Fatal("unexpected nil error")
    61  	}
    62  }
    63  
    64  func TestCreateDeleteTracker201FailBadLocation(t *testing.T) {
    65  	resp := newAsyncResp(newAsyncReq(http.MethodDelete, nil), http.StatusCreated, nil)
    66  	mocks.SetLocationHeader(resp, mocks.TestBadURL)
    67  	_, err := createPollingTracker(resp)
    68  	if err == nil {
    69  		t.Fatal("unexpected nil error")
    70  	}
    71  }
    72  
    73  func TestCreateDeleteTracker202SuccessAsyncOp(t *testing.T) {
    74  	resp := newAsyncResp(newAsyncReq(http.MethodDelete, nil), http.StatusAccepted, nil)
    75  	setAsyncOpHeader(resp, mocks.TestAzureAsyncURL)
    76  	pt, err := createPollingTracker(resp)
    77  	if err != nil {
    78  		t.Fatalf("failed to create tracker: %v", err)
    79  	}
    80  	if pt.pollingMethod() != PollingAsyncOperation {
    81  		t.Fatalf("wrong polling method: %s", pt.pollingMethod())
    82  	}
    83  	if pt.finalGetURL() != "" {
    84  		t.Fatal("expected empty GET URL")
    85  	}
    86  }
    87  
    88  func TestCreateDeleteTracker202SuccessLocation(t *testing.T) {
    89  	resp := newAsyncResp(newAsyncReq(http.MethodDelete, nil), http.StatusAccepted, nil)
    90  	mocks.SetLocationHeader(resp, mocks.TestLocationURL)
    91  	pt, err := createPollingTracker(resp)
    92  	if err != nil {
    93  		t.Fatalf("failed to create tracker: %v", err)
    94  	}
    95  	if pt.pollingMethod() != PollingLocation {
    96  		t.Fatalf("wrong polling method: %s", pt.pollingMethod())
    97  	}
    98  	if pt.finalGetURL() != mocks.TestLocationURL {
    99  		t.Fatalf("wrong final GET URL: %s", pt.finalGetURL())
   100  	}
   101  }
   102  
   103  func TestCreateDeleteTracker202SuccessBoth(t *testing.T) {
   104  	resp := newAsyncResp(newAsyncReq(http.MethodDelete, nil), http.StatusAccepted, nil)
   105  	setAsyncOpHeader(resp, mocks.TestAzureAsyncURL)
   106  	mocks.SetLocationHeader(resp, mocks.TestLocationURL)
   107  	pt, err := createPollingTracker(resp)
   108  	if err != nil {
   109  		t.Fatalf("failed to create tracker: %v", err)
   110  	}
   111  	if pt.pollingMethod() != PollingAsyncOperation {
   112  		t.Fatalf("wrong polling method: %s", pt.pollingMethod())
   113  	}
   114  	if pt.finalGetURL() != mocks.TestLocationURL {
   115  		t.Fatalf("wrong final GET URL: %s", pt.finalGetURL())
   116  	}
   117  }
   118  
   119  func TestCreateDeleteTracker202SuccessBadLocation(t *testing.T) {
   120  	resp := newAsyncResp(newAsyncReq(http.MethodDelete, nil), http.StatusAccepted, nil)
   121  	setAsyncOpHeader(resp, mocks.TestAzureAsyncURL)
   122  	mocks.SetLocationHeader(resp, mocks.TestBadURL)
   123  	pt, err := createPollingTracker(resp)
   124  	if err != nil {
   125  		t.Fatalf("failed to create tracker: %v", err)
   126  	}
   127  	if pt.pollingMethod() != PollingAsyncOperation {
   128  		t.Fatalf("wrong polling method: %s", pt.pollingMethod())
   129  	}
   130  	if pt.finalGetURL() != "" {
   131  		t.Fatal("expected empty GET URL")
   132  	}
   133  }
   134  
   135  func TestCreateDeleteTracker202FailBadAsyncOp(t *testing.T) {
   136  	resp := newAsyncResp(newAsyncReq(http.MethodDelete, nil), http.StatusAccepted, nil)
   137  	setAsyncOpHeader(resp, mocks.TestBadURL)
   138  	_, err := createPollingTracker(resp)
   139  	if err == nil {
   140  		t.Fatal("unexpected nil error")
   141  	}
   142  }
   143  
   144  func TestCreateDeleteTracker202FailBadLocation(t *testing.T) {
   145  	resp := newAsyncResp(newAsyncReq(http.MethodDelete, nil), http.StatusAccepted, nil)
   146  	mocks.SetLocationHeader(resp, mocks.TestBadURL)
   147  	_, err := createPollingTracker(resp)
   148  	if err == nil {
   149  		t.Fatal("unexpected nil error")
   150  	}
   151  }
   152  
   153  // PATCH
   154  
   155  func TestCreatePatchTracker201Success(t *testing.T) {
   156  	resp := newAsyncResp(newAsyncReq(http.MethodPatch, nil), http.StatusCreated, nil)
   157  	setAsyncOpHeader(resp, mocks.TestAzureAsyncURL)
   158  	pt, err := createPollingTracker(resp)
   159  	if err != nil {
   160  		t.Fatalf("failed to create tracker: %v", err)
   161  	}
   162  	if pt.pollingMethod() != PollingAsyncOperation {
   163  		t.Fatalf("wrong polling method: %s", pt.pollingMethod())
   164  	}
   165  	if pt.finalGetURL() != mocks.TestURL {
   166  		t.Fatalf("wrong final GET URL: %s", pt.finalGetURL())
   167  	}
   168  }
   169  
   170  func TestCreatePatchTracker201SuccessNoHeaders(t *testing.T) {
   171  	resp := newAsyncResp(newAsyncReq(http.MethodPatch, nil), http.StatusCreated, nil)
   172  	pt, err := createPollingTracker(resp)
   173  	if err != nil {
   174  		t.Fatalf("failed to create tracker: %v", err)
   175  	}
   176  	if pt.pollingMethod() != PollingRequestURI {
   177  		t.Fatalf("wrong polling method: %s", pt.pollingMethod())
   178  	}
   179  	if pt.finalGetURL() != mocks.TestURL {
   180  		t.Fatalf("wrong final GET URL: %s", pt.finalGetURL())
   181  	}
   182  }
   183  
   184  func TestCreatePatchTracker201FailBadAsyncOp(t *testing.T) {
   185  	resp := newAsyncResp(newAsyncReq(http.MethodPatch, nil), http.StatusCreated, nil)
   186  	setAsyncOpHeader(resp, mocks.TestBadURL)
   187  	_, err := createPollingTracker(resp)
   188  	if err == nil {
   189  		t.Fatal("unexpected nil error")
   190  	}
   191  }
   192  
   193  func TestCreatePatchTracker202SuccessAsyncOp(t *testing.T) {
   194  	resp := newAsyncResp(newAsyncReq(http.MethodPatch, nil), http.StatusAccepted, nil)
   195  	setAsyncOpHeader(resp, mocks.TestAzureAsyncURL)
   196  	pt, err := createPollingTracker(resp)
   197  	if err != nil {
   198  		t.Fatalf("failed to create tracker: %v", err)
   199  	}
   200  	if pt.pollingMethod() != PollingAsyncOperation {
   201  		t.Fatalf("wrong polling method: %s", pt.pollingMethod())
   202  	}
   203  	if pt.finalGetURL() != mocks.TestURL {
   204  		t.Fatalf("wrong final GET URL: %s", pt.finalGetURL())
   205  	}
   206  }
   207  
   208  func TestCreatePatchTracker202SuccessLocation(t *testing.T) {
   209  	resp := newAsyncResp(newAsyncReq(http.MethodPatch, nil), http.StatusAccepted, nil)
   210  	mocks.SetLocationHeader(resp, mocks.TestLocationURL)
   211  	pt, err := createPollingTracker(resp)
   212  	if err != nil {
   213  		t.Fatalf("failed to create tracker: %v", err)
   214  	}
   215  	if pt.pollingMethod() != PollingLocation {
   216  		t.Fatalf("wrong polling method: %s", pt.pollingMethod())
   217  	}
   218  	if pt.finalGetURL() != mocks.TestURL {
   219  		t.Fatalf("wrong final GET URL: %s", pt.finalGetURL())
   220  	}
   221  }
   222  
   223  func TestCreatePatchTracker202SuccessBoth(t *testing.T) {
   224  	resp := newAsyncResp(newAsyncReq(http.MethodPatch, nil), http.StatusAccepted, nil)
   225  	setAsyncOpHeader(resp, mocks.TestAzureAsyncURL)
   226  	mocks.SetLocationHeader(resp, mocks.TestLocationURL)
   227  	pt, err := createPollingTracker(resp)
   228  	if err != nil {
   229  		t.Fatalf("failed to create tracker: %v", err)
   230  	}
   231  	if pt.pollingMethod() != PollingAsyncOperation {
   232  		t.Fatalf("wrong polling method: %s", pt.pollingMethod())
   233  	}
   234  	if pt.finalGetURL() != mocks.TestURL {
   235  		t.Fatalf("wrong final GET URL: %s", pt.finalGetURL())
   236  	}
   237  }
   238  
   239  func TestCreatePatchTracker202FailBadAsyncOp(t *testing.T) {
   240  	resp := newAsyncResp(newAsyncReq(http.MethodPatch, nil), http.StatusAccepted, nil)
   241  	setAsyncOpHeader(resp, mocks.TestBadURL)
   242  	_, err := createPollingTracker(resp)
   243  	if err == nil {
   244  		t.Fatal("unexpected nil error")
   245  	}
   246  }
   247  
   248  func TestCreatePatchTracker202FailBadLocation(t *testing.T) {
   249  	resp := newAsyncResp(newAsyncReq(http.MethodPatch, nil), http.StatusAccepted, nil)
   250  	mocks.SetLocationHeader(resp, mocks.TestBadURL)
   251  	_, err := createPollingTracker(resp)
   252  	if err == nil {
   253  		t.Fatal("unexpected nil error")
   254  	}
   255  }
   256  
   257  // POST
   258  
   259  func TestCreatePostTracker201Success(t *testing.T) {
   260  	resp := newAsyncResp(newAsyncReq(http.MethodPost, nil), http.StatusCreated, nil)
   261  	mocks.SetLocationHeader(resp, mocks.TestLocationURL)
   262  	pt, err := createPollingTracker(resp)
   263  	if err != nil {
   264  		t.Fatalf("failed to create tracker: %v", err)
   265  	}
   266  	if pt.pollingMethod() != PollingLocation {
   267  		t.Fatalf("wrong polling method: %s", pt.pollingMethod())
   268  	}
   269  	if pt.finalGetURL() != mocks.TestLocationURL {
   270  		t.Fatalf("wrong final GET URL: %s", pt.finalGetURL())
   271  	}
   272  }
   273  
   274  func TestCreatePostTracker201FailNoHeader(t *testing.T) {
   275  	resp := newAsyncResp(newAsyncReq(http.MethodPost, nil), http.StatusCreated, nil)
   276  	_, err := createPollingTracker(resp)
   277  	if err == nil {
   278  		t.Fatal("unexpected nil err")
   279  	}
   280  }
   281  
   282  func TestCreatePostTracker201FailBadHeader(t *testing.T) {
   283  	resp := newAsyncResp(newAsyncReq(http.MethodPost, nil), http.StatusCreated, nil)
   284  	mocks.SetLocationHeader(resp, mocks.TestBadURL)
   285  	_, err := createPollingTracker(resp)
   286  	if err == nil {
   287  		t.Fatal("unexpected nil err")
   288  	}
   289  }
   290  
   291  func TestCreatePostTracker202SuccessAsyncOp(t *testing.T) {
   292  	resp := newAsyncResp(newAsyncReq(http.MethodPost, nil), http.StatusAccepted, nil)
   293  	setAsyncOpHeader(resp, mocks.TestAzureAsyncURL)
   294  	pt, err := createPollingTracker(resp)
   295  	if err != nil {
   296  		t.Fatalf("failed to create tracker: %v", err)
   297  	}
   298  	if pt.pollingMethod() != PollingAsyncOperation {
   299  		t.Fatalf("wrong polling method: %s", pt.pollingMethod())
   300  	}
   301  	if pt.finalGetURL() != "" {
   302  		t.Fatal("expected empty final GET URL")
   303  	}
   304  }
   305  
   306  func TestCreatePostTracker202SuccessLocation(t *testing.T) {
   307  	resp := newAsyncResp(newAsyncReq(http.MethodPost, nil), http.StatusAccepted, nil)
   308  	mocks.SetLocationHeader(resp, mocks.TestLocationURL)
   309  	pt, err := createPollingTracker(resp)
   310  	if err != nil {
   311  		t.Fatalf("failed to create tracker: %v", err)
   312  	}
   313  	if pt.pollingMethod() != PollingLocation {
   314  		t.Fatalf("wrong polling method: %s", pt.pollingMethod())
   315  	}
   316  	if pt.finalGetURL() != mocks.TestLocationURL {
   317  		t.Fatalf("wrong final GET URI: %s", pt.finalGetURL())
   318  	}
   319  }
   320  
   321  func TestCreatePostTracker202SuccessBoth(t *testing.T) {
   322  	resp := newAsyncResp(newAsyncReq(http.MethodPost, nil), http.StatusAccepted, nil)
   323  	setAsyncOpHeader(resp, mocks.TestAzureAsyncURL)
   324  	mocks.SetLocationHeader(resp, mocks.TestLocationURL)
   325  	pt, err := createPollingTracker(resp)
   326  	if err != nil {
   327  		t.Fatalf("failed to create tracker: %v", err)
   328  	}
   329  	if pt.pollingMethod() != PollingAsyncOperation {
   330  		t.Fatalf("wrong polling method: %s", pt.pollingMethod())
   331  	}
   332  	if pt.finalGetURL() != mocks.TestLocationURL {
   333  		t.Fatalf("wrong final GET URL: %s", pt.finalGetURL())
   334  	}
   335  }
   336  
   337  func TestCreatePostTracker202SuccessBadLocation(t *testing.T) {
   338  	resp := newAsyncResp(newAsyncReq(http.MethodPost, nil), http.StatusAccepted, nil)
   339  	setAsyncOpHeader(resp, mocks.TestAzureAsyncURL)
   340  	mocks.SetLocationHeader(resp, mocks.TestBadURL)
   341  	pt, err := createPollingTracker(resp)
   342  	if err != nil {
   343  		t.Fatalf("failed to create tracker: %v", err)
   344  	}
   345  	if pt.pollingMethod() != PollingAsyncOperation {
   346  		t.Fatalf("wrong polling method: %s", pt.pollingMethod())
   347  	}
   348  	if pt.finalGetURL() != "" {
   349  		t.Fatal("expected empty final GET URL")
   350  	}
   351  }
   352  
   353  func TestCreatePostTracker202FailBadAsyncOp(t *testing.T) {
   354  	resp := newAsyncResp(newAsyncReq(http.MethodPost, nil), http.StatusAccepted, nil)
   355  	setAsyncOpHeader(resp, mocks.TestBadURL)
   356  	_, err := createPollingTracker(resp)
   357  	if err == nil {
   358  		t.Fatal("unexpected nil error")
   359  	}
   360  }
   361  
   362  func TestCreatePostTracker202FailBadLocation(t *testing.T) {
   363  	resp := newAsyncResp(newAsyncReq(http.MethodPost, nil), http.StatusAccepted, nil)
   364  	_, err := createPollingTracker(resp)
   365  	mocks.SetLocationHeader(resp, mocks.TestBadURL)
   366  	if err == nil {
   367  		t.Fatal("unexpected nil error")
   368  	}
   369  }
   370  
   371  // PUT
   372  
   373  func TestCreatePutTracker201SuccessAsyncOp(t *testing.T) {
   374  	resp := newAsyncResp(newAsyncReq(http.MethodPut, nil), http.StatusCreated, nil)
   375  	setAsyncOpHeader(resp, mocks.TestAzureAsyncURL)
   376  	pt, err := createPollingTracker(resp)
   377  	if err != nil {
   378  		t.Fatalf("failed to create tracker: %v", err)
   379  	}
   380  	if pt.pollingMethod() != PollingAsyncOperation {
   381  		t.Fatalf("wrong polling method: %s", pt.pollingMethod())
   382  	}
   383  	if pt.finalGetURL() != mocks.TestURL {
   384  		t.Fatalf("wrong final GET URL: %s", pt.finalGetURL())
   385  	}
   386  }
   387  
   388  func TestCreatePutTracker201SuccessNoHeaders(t *testing.T) {
   389  	resp := newAsyncResp(newAsyncReq(http.MethodPut, nil), http.StatusCreated, nil)
   390  	pt, err := createPollingTracker(resp)
   391  	if err != nil {
   392  		t.Fatalf("failed to create tracker: %v", err)
   393  	}
   394  	if pt.pollingMethod() != PollingRequestURI {
   395  		t.Fatalf("wrong polling method: %s", pt.pollingMethod())
   396  	}
   397  	if pt.finalGetURL() != mocks.TestURL {
   398  		t.Fatalf("wrong final GET URL: %s", pt.finalGetURL())
   399  	}
   400  }
   401  
   402  func TestCreatePutTracker201FailBadAsyncOp(t *testing.T) {
   403  	resp := newAsyncResp(newAsyncReq(http.MethodPut, nil), http.StatusCreated, nil)
   404  	setAsyncOpHeader(resp, mocks.TestBadURL)
   405  	_, err := createPollingTracker(resp)
   406  	if err == nil {
   407  		t.Fatal("unexpected nil error")
   408  	}
   409  }
   410  
   411  func TestCreatePutTracker202SuccessAsyncOp(t *testing.T) {
   412  	resp := newAsyncResp(newAsyncReq(http.MethodPut, nil), http.StatusAccepted, nil)
   413  	setAsyncOpHeader(resp, mocks.TestAzureAsyncURL)
   414  	pt, err := createPollingTracker(resp)
   415  	if err != nil {
   416  		t.Fatalf("failed to create tracker: %v", err)
   417  	}
   418  	if pt.pollingMethod() != PollingAsyncOperation {
   419  		t.Fatalf("wrong polling method: %s", pt.pollingMethod())
   420  	}
   421  	if pt.finalGetURL() != mocks.TestURL {
   422  		t.Fatalf("wrong final GET URL: %s", pt.finalGetURL())
   423  	}
   424  }
   425  
   426  func TestCreatePutTracker202SuccessLocation(t *testing.T) {
   427  	resp := newAsyncResp(newAsyncReq(http.MethodPut, nil), http.StatusAccepted, nil)
   428  	mocks.SetLocationHeader(resp, mocks.TestLocationURL)
   429  	pt, err := createPollingTracker(resp)
   430  	if err != nil {
   431  		t.Fatalf("failed to create tracker: %v", err)
   432  	}
   433  	if pt.pollingMethod() != PollingLocation {
   434  		t.Fatalf("wrong polling method: %s", pt.pollingMethod())
   435  	}
   436  	if pt.finalGetURL() != resp.Request.URL.String() {
   437  		t.Fatalf("wrong final GET URL: %s", pt.finalGetURL())
   438  	}
   439  }
   440  
   441  func TestCreatePutTracker202SuccessBoth(t *testing.T) {
   442  	resp := newAsyncResp(newAsyncReq(http.MethodPut, nil), http.StatusAccepted, nil)
   443  	setAsyncOpHeader(resp, mocks.TestAzureAsyncURL)
   444  	mocks.SetLocationHeader(resp, mocks.TestLocationURL)
   445  	pt, err := createPollingTracker(resp)
   446  	if err != nil {
   447  		t.Fatalf("failed to create tracker: %v", err)
   448  	}
   449  	if pt.pollingMethod() != PollingAsyncOperation {
   450  		t.Fatalf("wrong polling method: %s", pt.pollingMethod())
   451  	}
   452  	if pt.finalGetURL() != resp.Request.URL.String() {
   453  		t.Fatalf("wrong final GET URL: %s", pt.finalGetURL())
   454  	}
   455  }
   456  
   457  func TestCreatePutTracker202SuccessBadLocation(t *testing.T) {
   458  	resp := newAsyncResp(newAsyncReq(http.MethodPut, nil), http.StatusAccepted, nil)
   459  	setAsyncOpHeader(resp, mocks.TestAzureAsyncURL)
   460  	mocks.SetLocationHeader(resp, mocks.TestBadURL)
   461  	pt, err := createPollingTracker(resp)
   462  	if err != nil {
   463  		t.Fatalf("failed to create tracker: %v", err)
   464  	}
   465  	if pt.pollingMethod() != PollingAsyncOperation {
   466  		t.Fatalf("wrong polling method: %s", pt.pollingMethod())
   467  	}
   468  	if pt.finalGetURL() != mocks.TestURL {
   469  		t.Fatalf("wrong final GET URL: %s", pt.finalGetURL())
   470  	}
   471  }
   472  
   473  func TestCreatePutTracker202FailBadAsyncOp(t *testing.T) {
   474  	resp := newAsyncResp(newAsyncReq(http.MethodPut, nil), http.StatusAccepted, nil)
   475  	setAsyncOpHeader(resp, mocks.TestBadURL)
   476  	_, err := createPollingTracker(resp)
   477  	if err == nil {
   478  		t.Fatal("unexpected nil error")
   479  	}
   480  }
   481  
   482  func TestPollPutTrackerSuccessNoHeaders(t *testing.T) {
   483  	resp := newAsyncResp(newAsyncReq(http.MethodPut, nil), http.StatusAccepted, nil)
   484  	pt, err := createPollingTracker(resp)
   485  	if err != nil {
   486  		t.Fatalf("failed to create tracker: %v", err)
   487  	}
   488  	sender := mocks.NewSender()
   489  	sender.AppendResponse(newProvisioningStatusResponse("InProgress"))
   490  	err = pt.pollForStatus(context.Background(), sender)
   491  	if err != nil {
   492  		t.Fatalf("failed to poll for status: %v", err)
   493  	}
   494  	err = pt.checkForErrors()
   495  	if err != nil {
   496  		t.Fatalf("unexpected error: %v", err)
   497  	}
   498  }
   499  
   500  func TestPollPutTrackerFailNoHeadersEmptyBody(t *testing.T) {
   501  	resp := newAsyncResp(newAsyncReq(http.MethodPut, nil), http.StatusAccepted, nil)
   502  	pt, err := createPollingTracker(resp)
   503  	if err != nil {
   504  		t.Fatalf("failed to create tracker: %v", err)
   505  	}
   506  	sender := mocks.NewSender()
   507  	sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(&mocks.Body{}, http.StatusOK, "status ok"))
   508  	err = pt.pollForStatus(context.Background(), sender)
   509  	if err != nil {
   510  		t.Fatalf("failed to poll for status: %v", err)
   511  	}
   512  	err = pt.checkForErrors()
   513  	if err == nil {
   514  		t.Fatalf("unexpected nil error")
   515  	}
   516  }
   517  
   518  // errors
   519  
   520  func TestAsyncPollingReturnsWrappedError(t *testing.T) {
   521  	resp := newSimpleAsyncResp()
   522  	pt, err := createPollingTracker(resp)
   523  	if err != nil {
   524  		t.Fatalf("failed to create tracker: %v", err)
   525  	}
   526  	sender := mocks.NewSender()
   527  	sender.AppendResponse(newOperationResourceErrorResponse("Failed"))
   528  	err = pt.pollForStatus(context.Background(), sender)
   529  	if err == nil {
   530  		t.Fatal("unexpected nil polling error")
   531  	}
   532  	err = pt.pollingError()
   533  	if err == nil {
   534  		t.Fatal("unexpected nil polling error")
   535  	}
   536  	if se, ok := err.(*ServiceError); !ok {
   537  		t.Fatal("incorrect error type")
   538  	} else if se.Code == "" {
   539  		t.Fatal("empty service error code")
   540  	} else if se.Message == "" {
   541  		t.Fatal("empty service error message")
   542  	}
   543  }
   544  
   545  func TestLocationPollingReturnsWrappedError(t *testing.T) {
   546  	resp := newSimpleLocationResp()
   547  	pt, err := createPollingTracker(resp)
   548  	if err != nil {
   549  		t.Fatalf("failed to create tracker: %v", err)
   550  	}
   551  	sender := mocks.NewSender()
   552  	sender.AppendResponse(newProvisioningStatusErrorResponse("Failed"))
   553  	err = pt.pollForStatus(context.Background(), sender)
   554  	if err == nil {
   555  		t.Fatal("unexpected nil polling error")
   556  	}
   557  	err = pt.pollingError()
   558  	if err == nil {
   559  		t.Fatal("unexpected nil polling error")
   560  	}
   561  	if se, ok := err.(*ServiceError); !ok {
   562  		t.Fatal("incorrect error type")
   563  	} else if se.Code == "" {
   564  		t.Fatal("empty service error code")
   565  	} else if se.Message == "" {
   566  		t.Fatal("empty service error message")
   567  	}
   568  }
   569  
   570  func TestLocationPollingReturnsUnwrappedError(t *testing.T) {
   571  	resp := newSimpleLocationResp()
   572  	pt, err := createPollingTracker(resp)
   573  	if err != nil {
   574  		t.Fatalf("failed to create tracker: %v", err)
   575  	}
   576  	sender := mocks.NewSender()
   577  	sender.AppendResponse(newProvisioningStatusUnwrappedErrorResponse("Failed"))
   578  	err = pt.pollForStatus(context.Background(), sender)
   579  	if err == nil {
   580  		t.Fatal("unexpected nil polling error")
   581  	}
   582  	err = pt.pollingError()
   583  	if err == nil {
   584  		t.Fatal("unexpected nil polling error")
   585  	}
   586  	if se, ok := err.(*ServiceError); !ok {
   587  		t.Fatal("incorrect error type")
   588  	} else if se.Code == "" {
   589  		t.Fatal("empty service error code")
   590  	} else if se.Message == "" {
   591  		t.Fatal("empty service error message")
   592  	}
   593  }
   594  
   595  func TestFuture_PollsUntilProvisioningStatusSucceeds(t *testing.T) {
   596  	r2 := newOperationResourceResponse("busy")
   597  	r3 := newOperationResourceResponse(operationSucceeded)
   598  
   599  	sender := mocks.NewSender()
   600  	ctx := context.Background()
   601  	sender.AppendAndRepeatResponse(r2, 2)
   602  	sender.AppendResponse(r3)
   603  
   604  	future, err := NewFutureFromResponse(newSimpleAsyncResp())
   605  	if err != nil {
   606  		t.Fatalf("failed to create future: %v", err)
   607  	}
   608  
   609  	for done, err := future.DoneWithContext(ctx, sender); !done; done, err = future.DoneWithContext(ctx, sender) {
   610  		if future.PollingMethod() != PollingAsyncOperation {
   611  			t.Fatalf("wrong future polling method: %s", future.PollingMethod())
   612  		}
   613  		if err != nil {
   614  			t.Fatalf("polling Done failed: %v", err)
   615  		}
   616  		delay, ok := future.GetPollingDelay()
   617  		if !ok {
   618  			t.Fatalf("expected Retry-After value")
   619  		}
   620  		time.Sleep(delay)
   621  	}
   622  
   623  	if sender.Attempts() < sender.NumResponses() {
   624  		t.Fatalf("stopped polling before receiving a terminated OperationResource")
   625  	}
   626  
   627  	autorest.Respond(future.Response(),
   628  		autorest.ByClosing())
   629  }
   630  
   631  func TestFuture_MarshallingSuccess(t *testing.T) {
   632  	future, err := NewFutureFromResponse(newSimpleAsyncResp())
   633  	if err != nil {
   634  		t.Fatalf("failed to create future: %v", err)
   635  	}
   636  
   637  	data, err := json.Marshal(future)
   638  	if err != nil {
   639  		t.Fatalf("failed to marshal: %v", err)
   640  	}
   641  
   642  	var future2 Future
   643  	err = json.Unmarshal(data, &future2)
   644  	if err != nil {
   645  		t.Fatalf("failed to unmarshal: %v", err)
   646  	}
   647  
   648  	if reflect.DeepEqual(future.pt, future2.pt) {
   649  		t.Fatalf("marshalling unexpected match")
   650  	}
   651  
   652  	// these fields don't get marshalled so nil them before deep comparison
   653  	future.pt.(*pollingTrackerPut).resp = nil
   654  	future.pt.(*pollingTrackerPut).rawBody = nil
   655  	if !reflect.DeepEqual(future.pt, future2.pt) {
   656  		t.Fatalf("marshalling futures don't match")
   657  	}
   658  }
   659  
   660  func TestFuture_MarshallingWithError(t *testing.T) {
   661  	r2 := newOperationResourceResponse("busy")
   662  	r3 := newOperationResourceErrorResponse(operationFailed)
   663  
   664  	sender := mocks.NewSender()
   665  	sender.AppendAndRepeatResponse(r2, 2)
   666  	sender.AppendResponse(r3)
   667  	client := autorest.Client{
   668  		PollingDelay:    1 * time.Second,
   669  		PollingDuration: autorest.DefaultPollingDuration,
   670  		RetryAttempts:   autorest.DefaultRetryAttempts,
   671  		RetryDuration:   1 * time.Second,
   672  		Sender:          sender,
   673  	}
   674  
   675  	future, err := NewFutureFromResponse(newSimpleAsyncResp())
   676  	if err != nil {
   677  		t.Fatalf("failed to create future: %v", err)
   678  	}
   679  
   680  	err = future.WaitForCompletionRef(context.Background(), client)
   681  	if err == nil {
   682  		t.Fatal("expected non-nil error")
   683  	}
   684  
   685  	data, err := json.Marshal(future)
   686  	if err != nil {
   687  		t.Fatalf("failed to marshal: %v", err)
   688  	}
   689  
   690  	var future2 Future
   691  	err = json.Unmarshal(data, &future2)
   692  	if err != nil {
   693  		t.Fatalf("failed to unmarshal: %v", err)
   694  	}
   695  
   696  	if reflect.DeepEqual(future.pt, future2.pt) {
   697  		t.Fatalf("marshalling unexpected match")
   698  	}
   699  
   700  	// these fields don't get marshalled so nil them before deep comparison
   701  	future.pt.(*pollingTrackerPut).resp = nil
   702  	future.pt.(*pollingTrackerPut).rawBody = nil
   703  	if !reflect.DeepEqual(future.pt, future2.pt) {
   704  		t.Fatalf("marshalling futures don't match")
   705  	}
   706  }
   707  
   708  func TestFuture_CreateFromFailedOperation(t *testing.T) {
   709  	_, err := NewFutureFromResponse(newAsyncResponseWithError(http.MethodPut))
   710  	if err == nil {
   711  		t.Fatal("expected non-nil error")
   712  	}
   713  }
   714  
   715  func TestFuture_WaitForCompletionRef(t *testing.T) {
   716  	r2 := newOperationResourceResponse("busy")
   717  	r3 := newOperationResourceResponse(operationSucceeded)
   718  
   719  	sender := mocks.NewSender()
   720  	sender.AppendAndRepeatResponse(r2, 2)
   721  	sender.AppendResponse(r3)
   722  	client := autorest.Client{
   723  		PollingDelay:    1 * time.Second,
   724  		PollingDuration: autorest.DefaultPollingDuration,
   725  		RetryAttempts:   autorest.DefaultRetryAttempts,
   726  		RetryDuration:   1 * time.Second,
   727  		Sender:          sender,
   728  	}
   729  
   730  	future, err := NewFutureFromResponse(newSimpleAsyncResp())
   731  	if err != nil {
   732  		t.Fatalf("failed to create future: %v", err)
   733  	}
   734  
   735  	err = future.WaitForCompletionRef(context.Background(), client)
   736  	if err != nil {
   737  		t.Fatalf("WaitForCompletion returned non-nil error")
   738  	}
   739  
   740  	if sender.Attempts() < sender.NumResponses() {
   741  		t.Fatalf("stopped polling before receiving a terminated OperationResource")
   742  	}
   743  
   744  	autorest.Respond(future.Response(),
   745  		autorest.ByClosing())
   746  }
   747  
   748  func TestFuture_WaitForCompletionRefWithRetryAfter(t *testing.T) {
   749  	r2 := newOperationResourceResponse("busy")
   750  	r3 := newOperationResourceResponse(operationSucceeded)
   751  
   752  	sender := mocks.NewSender()
   753  	sender.AppendAndRepeatResponse(r2, 2)
   754  	sender.AppendResponse(r3)
   755  	client := autorest.Client{
   756  		PollingDelay:    1 * time.Second,
   757  		PollingDuration: autorest.DefaultPollingDuration,
   758  		RetryAttempts:   autorest.DefaultRetryAttempts,
   759  		RetryDuration:   1 * time.Second,
   760  		Sender:          sender,
   761  	}
   762  
   763  	future, err := NewFutureFromResponse(newSimpleAsyncRespWithRetryAfter())
   764  	if err != nil {
   765  		t.Fatalf("failed to create future: %v", err)
   766  	}
   767  
   768  	err = future.WaitForCompletionRef(context.Background(), client)
   769  	if err != nil {
   770  		t.Fatalf("WaitForCompletion returned non-nil error")
   771  	}
   772  
   773  	if sender.Attempts() < sender.NumResponses() {
   774  		t.Fatalf("stopped polling before receiving a terminated OperationResource")
   775  	}
   776  
   777  	autorest.Respond(future.Response(),
   778  		autorest.ByClosing())
   779  }
   780  
   781  func TestFuture_WaitForCompletionTimedOut(t *testing.T) {
   782  	r2 := newProvisioningStatusResponse("busy")
   783  
   784  	sender := mocks.NewSender()
   785  	sender.AppendAndRepeatResponseWithDelay(r2, 1*time.Second, 5)
   786  
   787  	future, err := NewFutureFromResponse(newSimpleAsyncResp())
   788  	if err != nil {
   789  		t.Fatalf("failed to create future: %v", err)
   790  	}
   791  
   792  	client := autorest.Client{
   793  		PollingDelay:    autorest.DefaultPollingDelay,
   794  		PollingDuration: 2 * time.Second,
   795  		RetryAttempts:   autorest.DefaultRetryAttempts,
   796  		RetryDuration:   1 * time.Second,
   797  		Sender:          sender,
   798  	}
   799  
   800  	err = future.WaitForCompletionRef(context.Background(), client)
   801  	if err == nil {
   802  		t.Fatalf("WaitForCompletion returned nil error, should have timed out")
   803  	}
   804  }
   805  
   806  func TestFuture_WaitForCompletionRetriesExceeded(t *testing.T) {
   807  	r1 := newProvisioningStatusResponse("InProgress")
   808  
   809  	sender := mocks.NewSender()
   810  	sender.AppendResponse(r1)
   811  	sender.AppendAndRepeatError(errors.New("transient network failure"), autorest.DefaultRetryAttempts+1)
   812  
   813  	future, err := NewFutureFromResponse(newSimpleAsyncResp())
   814  	if err != nil {
   815  		t.Fatalf("failed to create future: %v", err)
   816  	}
   817  
   818  	client := autorest.Client{
   819  		PollingDelay:    autorest.DefaultPollingDelay,
   820  		PollingDuration: autorest.DefaultPollingDuration,
   821  		RetryAttempts:   autorest.DefaultRetryAttempts,
   822  		RetryDuration:   100 * time.Millisecond,
   823  		Sender:          sender,
   824  	}
   825  
   826  	err = future.WaitForCompletionRef(context.Background(), client)
   827  	if err == nil {
   828  		t.Fatalf("WaitForCompletion returned nil error, should have errored out")
   829  	}
   830  }
   831  
   832  func TestFuture_WaitForCompletionCancelled(t *testing.T) {
   833  	r1 := newProvisioningStatusResponse("InProgress")
   834  
   835  	sender := mocks.NewSender()
   836  	sender.AppendAndRepeatResponseWithDelay(r1, 1*time.Second, 5)
   837  
   838  	future, err := NewFutureFromResponse(newSimpleAsyncResp())
   839  	if err != nil {
   840  		t.Fatalf("failed to create future: %v", err)
   841  	}
   842  
   843  	client := autorest.Client{
   844  		PollingDelay:    autorest.DefaultPollingDelay,
   845  		PollingDuration: autorest.DefaultPollingDuration,
   846  		RetryAttempts:   autorest.DefaultRetryAttempts,
   847  		RetryDuration:   autorest.DefaultRetryDuration,
   848  		Sender:          sender,
   849  	}
   850  
   851  	ctx, cancel := context.WithCancel(context.Background())
   852  	go func() {
   853  		time.Sleep(2 * time.Second)
   854  		cancel()
   855  	}()
   856  
   857  	err = future.WaitForCompletionRef(ctx, client)
   858  	if err == nil {
   859  		t.Fatalf("WaitForCompletion returned nil error, should have been cancelled")
   860  	}
   861  }
   862  
   863  func TestFuture_GetResultFromNonAsyncOperation(t *testing.T) {
   864  	resp := newAsyncResp(newAsyncReq(http.MethodPost, nil), http.StatusOK, mocks.NewBody(someResource))
   865  	future, err := NewFutureFromResponse(resp)
   866  	if err != nil {
   867  		t.Fatalf("failed to create tracker: %v", err)
   868  	}
   869  	if pm := future.PollingMethod(); pm != PollingUnknown {
   870  		t.Fatalf("wrong polling method: %s", pm)
   871  	}
   872  	done, err := future.DoneWithContext(context.Background(), nil)
   873  	if err != nil {
   874  		t.Fatalf("failed to check status: %v", err)
   875  	}
   876  	if !done {
   877  		t.Fatal("operation should be done")
   878  	}
   879  	res, err := future.GetResult(nil)
   880  	if err != nil {
   881  		t.Fatalf("failed to get result: %v", err)
   882  	}
   883  	if res != resp {
   884  		t.Fatal("result and response don't match")
   885  	}
   886  }
   887  
   888  func TestFuture_GetResultNonTerminal(t *testing.T) {
   889  	resp := newAsyncResp(newAsyncReq(http.MethodDelete, nil), http.StatusAccepted, mocks.NewBody(fmt.Sprintf(operationResourceFormat, operationInProgress)))
   890  	mocks.SetResponseHeader(resp, headerAsyncOperation, mocks.TestAzureAsyncURL)
   891  	future, err := NewFutureFromResponse(resp)
   892  	if err != nil {
   893  		t.Fatalf("failed to create future: %v", err)
   894  	}
   895  	res, err := future.GetResult(nil)
   896  	if err == nil {
   897  		t.Fatal("expected non-nil error")
   898  	}
   899  	if res != nil {
   900  		t.Fatal("expected nil result")
   901  	}
   902  }
   903  
   904  const (
   905  	operationResourceIllegal = `
   906  	This is not JSON and should fail...badly.
   907  	`
   908  
   909  	// returned from LROs that use Location header
   910  	pollingStateFormat = `
   911  	{
   912  		"unused" : {
   913  			"somefield" : 42
   914  		},
   915  		"properties" : {
   916  			"provisioningState": "%s"
   917  		}
   918  	}
   919  	`
   920  
   921  	// returned from LROs that use Location header
   922  	errorResponse = `
   923  	{
   924  		"error" : {
   925  			"code" : "InvalidParameter",
   926  			"message" : "tom-service-DISCOVERY-server-base-v1.core.local' is not a valid captured VHD blob name prefix."
   927  		}
   928  	}
   929  	`
   930  
   931  	// returned from LROs that use Location header
   932  	unwrappedErrorResponse = `
   933  	{
   934  		"code" : "InvalidParameter",
   935  		"message" : "tom-service-DISCOVERY-server-base-v1.core.local' is not a valid captured VHD blob name prefix."
   936  	}
   937  	`
   938  
   939  	// returned from LROs that use Location header
   940  	pollingStateEmpty = `
   941  	{
   942  		"unused" : {
   943  			"somefield" : 42
   944  		},
   945  		"properties" : {
   946  		}
   947  	}
   948  	`
   949  
   950  	// returned from LROs that use Azure-AsyncOperation header
   951  	operationResourceFormat = `
   952  	{
   953  		"id": "/subscriptions/id/locations/westus/operationsStatus/sameguid",
   954  		"name": "sameguid",
   955  		"status" : "%s",
   956  		"startTime" : "2006-01-02T15:04:05Z",
   957  		"endTime" : "2006-01-02T16:04:05Z",
   958  		"percentComplete" : 50.00,
   959  		"properties" : {
   960  			"foo": "bar"
   961  		}
   962  	}
   963  	`
   964  
   965  	// returned from LROs that use Azure-AsyncOperation header
   966  	operationResourceErrorFormat = `
   967  	{
   968  		"id": "/subscriptions/id/locations/westus/operationsStatus/sameguid",
   969  		"name": "sameguid",
   970  		"status" : "%s",
   971  		"startTime" : "2006-01-02T15:04:05Z",
   972  		"endTime" : "2006-01-02T16:04:05Z",
   973  		"percentComplete" : 50.00,
   974  		"properties" : {},
   975  		"error" : {
   976  			"code" : "BadArgument",
   977  			"message" : "The provided database 'foo' has an invalid username."
   978  		}
   979  	}
   980  	`
   981  
   982  	// returned from an operation marked as LRO but really isn't
   983  	someResource = `
   984  	{
   985  		"id": "/subscriptions/guid/resourceGroups/rg/providers/something/else/thing",
   986  		"name": "thing",
   987  		"type": "Imaginary.type",
   988  		"location": "Central US",
   989  		"properties": {}
   990  	}
   991  	`
   992  )
   993  
   994  // creates an async request with the specified body.
   995  func newAsyncReq(reqMethod string, body *mocks.Body) *http.Request {
   996  	return mocks.NewRequestWithParams(reqMethod, mocks.TestURL, body)
   997  }
   998  
   999  // creates an async response with the specified body.
  1000  // the req param is the originating LRO request.
  1001  func newAsyncResp(req *http.Request, statusCode int, body *mocks.Body) *http.Response {
  1002  	status := "Unknown"
  1003  	switch statusCode {
  1004  	case http.StatusOK, http.StatusNoContent:
  1005  		status = "Completed"
  1006  	case http.StatusCreated:
  1007  		status = "Creating"
  1008  	case http.StatusAccepted:
  1009  		status = "In progress"
  1010  	case http.StatusBadRequest:
  1011  		status = "Bad request"
  1012  	}
  1013  	r := mocks.NewResponseWithBodyAndStatus(body, statusCode, status)
  1014  	r.Request = req
  1015  	return r
  1016  }
  1017  
  1018  // creates a simple LRO response, PUT/201 with Azure-AsyncOperation header
  1019  func newSimpleAsyncResp() *http.Response {
  1020  	r := newAsyncResp(newAsyncReq(http.MethodPut, nil), http.StatusCreated, mocks.NewBody(fmt.Sprintf(operationResourceFormat, operationInProgress)))
  1021  	mocks.SetResponseHeader(r, headerAsyncOperation, mocks.TestAzureAsyncURL)
  1022  	return r
  1023  }
  1024  
  1025  func newSimpleAsyncRespWithRetryAfter() *http.Response {
  1026  	r := newAsyncResp(newAsyncReq(http.MethodPut, nil), http.StatusCreated, mocks.NewBody(fmt.Sprintf(operationResourceFormat, operationInProgress)))
  1027  	mocks.SetResponseHeader(r, headerAsyncOperation, mocks.TestAzureAsyncURL)
  1028  	mocks.SetResponseHeader(r, autorest.HeaderRetryAfter, "1")
  1029  	return r
  1030  }
  1031  
  1032  // creates a simple LRO response, POST/201 with Location header
  1033  func newSimpleLocationResp() *http.Response {
  1034  	r := newAsyncResp(newAsyncReq(http.MethodPost, nil), http.StatusCreated, mocks.NewBody(fmt.Sprintf(pollingStateFormat, operationInProgress)))
  1035  	mocks.SetResponseHeader(r, autorest.HeaderLocation, mocks.TestLocationURL)
  1036  	return r
  1037  }
  1038  
  1039  // creates an async response that contains an error (HTTP 400 + error response body)
  1040  func newAsyncResponseWithError(reqMethod string) *http.Response {
  1041  	return newAsyncResp(newAsyncReq(reqMethod, nil), http.StatusBadRequest, mocks.NewBody(errorResponse))
  1042  }
  1043  
  1044  // creates a LRO polling response using the operation resource format (Azure-AsyncOperation LROs)
  1045  func newOperationResourceResponse(status string) *http.Response {
  1046  	r := mocks.NewResponseWithBodyAndStatus(mocks.NewBody(fmt.Sprintf(operationResourceFormat, status)), http.StatusOK, status)
  1047  	mocks.SetRetryHeader(r, retryDelay)
  1048  	return r
  1049  }
  1050  
  1051  // creates a LRO polling error response using the operation resource format (Azure-AsyncOperation LROs)
  1052  func newOperationResourceErrorResponse(status string) *http.Response {
  1053  	return mocks.NewResponseWithBodyAndStatus(mocks.NewBody(fmt.Sprintf(operationResourceErrorFormat, status)), http.StatusBadRequest, status)
  1054  }
  1055  
  1056  // creates a LRO polling response using the provisioning state format (Location LROs)
  1057  func newProvisioningStatusResponse(status string) *http.Response {
  1058  	r := mocks.NewResponseWithBodyAndStatus(mocks.NewBody(fmt.Sprintf(pollingStateFormat, status)), http.StatusOK, status)
  1059  	mocks.SetRetryHeader(r, retryDelay)
  1060  	return r
  1061  }
  1062  
  1063  // creates a LRO polling error response using the provisioning state format (Location LROs)
  1064  func newProvisioningStatusErrorResponse(status string) *http.Response {
  1065  	return mocks.NewResponseWithBodyAndStatus(mocks.NewBody(errorResponse), http.StatusBadRequest, status)
  1066  }
  1067  
  1068  // creates a LRO polling unwrapped error response using the provisioning state format (Location LROs)
  1069  func newProvisioningStatusUnwrappedErrorResponse(status string) *http.Response {
  1070  	return mocks.NewResponseWithBodyAndStatus(mocks.NewBody(unwrappedErrorResponse), http.StatusBadRequest, status)
  1071  }
  1072  
  1073  // adds the Azure-AsyncOperation header with the specified location to the response
  1074  func setAsyncOpHeader(resp *http.Response, location string) {
  1075  	mocks.SetResponseHeader(resp, http.CanonicalHeaderKey(headerAsyncOperation), location)
  1076  }
  1077  

View as plain text