github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/apiv3/route/handler_test.go (about)

     1  package route
     2  
     3  import (
     4  	"io/ioutil"
     5  	"net/http"
     6  	"net/http/httptest"
     7  	"net/url"
     8  	"testing"
     9  
    10  	"github.com/evergreen-ci/evergreen"
    11  	"github.com/evergreen-ci/evergreen/apiv3"
    12  	"github.com/evergreen-ci/evergreen/apiv3/model"
    13  	"github.com/evergreen-ci/evergreen/apiv3/servicecontext"
    14  	"github.com/evergreen-ci/evergreen/util"
    15  	"github.com/gorilla/mux"
    16  	"github.com/pkg/errors"
    17  	. "github.com/smartystreets/goconvey/convey"
    18  )
    19  
    20  func TestMakeHandler(t *testing.T) {
    21  
    22  	Convey("When creating an http.HandlerFunc", t, func() {
    23  		auth := &mockAuthenticator{}
    24  		requestHandler := &mockRequestHandler{}
    25  		mockMethod := MethodHandler{
    26  			Authenticator:  auth,
    27  			RequestHandler: requestHandler,
    28  		}
    29  
    30  		Convey("And authenticator errors should cause response to "+
    31  			"contain error information", func() {
    32  			auth.err = apiv3.APIError{
    33  				StatusCode: http.StatusBadRequest,
    34  				Message:    "Not Authenticated",
    35  			}
    36  			checkResultMatchesBasic(mockMethod, auth.err, nil, http.StatusBadRequest, t)
    37  		})
    38  		Convey("And parseValidate errors should cause response to "+
    39  			"contain error information", func() {
    40  			requestHandler.parseValidateErr = apiv3.APIError{
    41  				StatusCode: http.StatusRequestEntityTooLarge,
    42  				Message:    "Request too large to parse",
    43  			}
    44  			checkResultMatchesBasic(mockMethod, requestHandler.parseValidateErr,
    45  				nil, http.StatusRequestEntityTooLarge, t)
    46  		})
    47  		Convey("And execute errors should cause response to "+
    48  			"contain error information", func() {
    49  			requestHandler.executeErr = apiv3.APIError{
    50  				StatusCode: http.StatusNotFound,
    51  				Message:    "Not found in DB",
    52  			}
    53  			checkResultMatchesBasic(mockMethod, requestHandler.executeErr,
    54  				nil, http.StatusNotFound, t)
    55  		})
    56  		Convey("And parseValidate and execute errors should cause response to "+
    57  			"contain parseValidate error information", func() {
    58  			requestHandler.executeErr = apiv3.APIError{
    59  				StatusCode: http.StatusNotFound,
    60  				Message:    "Not found in DB",
    61  			}
    62  			requestHandler.parseValidateErr = apiv3.APIError{
    63  				StatusCode: http.StatusBadRequest,
    64  				Message:    "Request did not contain all fields",
    65  			}
    66  			checkResultMatchesBasic(mockMethod, requestHandler.parseValidateErr,
    67  				nil, http.StatusBadRequest, t)
    68  		})
    69  		Convey("And authenticate and execute errors should cause response to "+
    70  			"contain authenticate error information", func() {
    71  			auth.err = apiv3.APIError{
    72  				StatusCode: http.StatusBadRequest,
    73  				Message:    "Not Authenticated",
    74  			}
    75  			requestHandler.executeErr = apiv3.APIError{
    76  				StatusCode: http.StatusNotFound,
    77  				Message:    "Not found in DB",
    78  			}
    79  			checkResultMatchesBasic(mockMethod, auth.err,
    80  				nil, http.StatusBadRequest, t)
    81  		})
    82  		Convey("And non-API errors should be surfaced as an internal "+
    83  			"server error", func() {
    84  			requestHandler.executeErr = errors.New("The DB is broken")
    85  			checkResultMatchesBasic(mockMethod, requestHandler.executeErr,
    86  				nil, http.StatusInternalServerError, t)
    87  		})
    88  		Convey("And api model should be returned successfully ", func() {
    89  			requestHandler.storedModels = []model.Model{
    90  				&model.MockModel{
    91  					FieldId:   "model_id",
    92  					FieldInt1: 1234,
    93  					FieldInt2: 4567,
    94  					FieldMap: map[string]string{
    95  						"mapkey1": "mapval1",
    96  						"mapkey2": "mapval2",
    97  					},
    98  					FieldStruct: &model.MockSubStruct{1},
    99  				},
   100  			}
   101  			checkResultMatchesBasic(mockMethod, nil,
   102  				requestHandler.storedModels, http.StatusOK, t)
   103  		})
   104  	})
   105  }
   106  
   107  type headerCheckFunc func(map[string][]string, *testing.T)
   108  
   109  func checkResultMatchesBasic(m MethodHandler, expectedErr error,
   110  	expectedModels []model.Model, expectedStatusCode int, t *testing.T) {
   111  	checkResultMatches(m, expectedErr, expectedModels, nil, "", expectedStatusCode, t)
   112  }
   113  
   114  func checkResultMatches(m MethodHandler, expectedErr error,
   115  	expectedModels []model.Model, headerCheck headerCheckFunc, queryParams string,
   116  	expectedStatusCode int, t *testing.T) {
   117  
   118  	testRoute := "/test/route"
   119  
   120  	handler := makeHandler(m, &servicecontext.MockServiceContext{})
   121  
   122  	u := &url.URL{
   123  		RawQuery: queryParams,
   124  		Path:     testRoute,
   125  	}
   126  
   127  	req, err := http.NewRequest(evergreen.MethodGet, u.String(), nil)
   128  	So(err, ShouldBeNil)
   129  
   130  	resp := httptest.NewRecorder()
   131  	r := mux.NewRouter()
   132  	r.HandleFunc(testRoute, handler)
   133  
   134  	r.ServeHTTP(resp, req)
   135  
   136  	if expectedErr != nil {
   137  		errResult := apiv3.APIError{}
   138  		wrappedBody := ioutil.NopCloser(resp.Body)
   139  
   140  		err := util.ReadJSONInto(wrappedBody, &errResult)
   141  		So(err, ShouldBeNil)
   142  
   143  		So(errResult.StatusCode, ShouldEqual, expectedStatusCode)
   144  		if apiErr, ok := expectedErr.(apiv3.APIError); ok {
   145  			So(apiErr.Message, ShouldEqual, errResult.Message)
   146  		} else {
   147  			So(expectedErr.Error(), ShouldEqual, errResult.Message)
   148  		}
   149  		So(resp.Code, ShouldEqual, expectedStatusCode)
   150  	} else if len(expectedModels) > 0 {
   151  		wrappedBody := ioutil.NopCloser(resp.Body)
   152  
   153  		var respResult []*model.MockModel
   154  		if len(expectedModels) == 1 {
   155  			singleResult := &model.MockModel{}
   156  			err := util.ReadJSONInto(wrappedBody, singleResult)
   157  			So(err, ShouldBeNil)
   158  			respResult = append(respResult, singleResult)
   159  		} else {
   160  			err := util.ReadJSONInto(wrappedBody, &respResult)
   161  			So(err, ShouldBeNil)
   162  		}
   163  
   164  		for ix, res := range respResult {
   165  			var resModel model.Model = res
   166  			So(resModel, ShouldResemble, expectedModels[ix])
   167  		}
   168  
   169  	} else {
   170  		t.Errorf("Expected-error and expected-model cannot both be nil")
   171  	}
   172  
   173  	if headerCheck != nil {
   174  		headerCheck(resp.Header(), t)
   175  	}
   176  }