github.com/justinjmoses/evergreen@v0.0.0-20170530173719-1d50e381ff0d/rest/route/paginator_test.go (about)

     1  package route
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"net/http/httptest"
     7  	"net/url"
     8  	"testing"
     9  
    10  	"github.com/evergreen-ci/evergreen/rest"
    11  	"github.com/evergreen-ci/evergreen/rest/data"
    12  	"github.com/evergreen-ci/evergreen/rest/model"
    13  	"github.com/gorilla/context"
    14  	. "github.com/smartystreets/goconvey/convey"
    15  )
    16  
    17  func TestMakePaginationHeader(t *testing.T) {
    18  	testURL := "http://evergreenurl.com"
    19  	testRoute := "test/route"
    20  
    21  	testPrevKey := "prevkey"
    22  	testNextKey := "nextkey"
    23  	testNextLimit := 100
    24  	testPrevLimit := 105
    25  
    26  	testLimitQueryParam := "limit"
    27  	testKeyQueryParam := "key"
    28  
    29  	Convey("When there is an http response writer", t, func() {
    30  		w := httptest.NewRecorder()
    31  		Convey("creating a PaginationMetadata with both a NextKey and PrevKey"+
    32  			" should create 2 links in the header", func() {
    33  			testPages := &PageResult{
    34  				Next: &Page{
    35  					Key:      testNextKey,
    36  					Relation: "next",
    37  					Limit:    testNextLimit,
    38  				},
    39  				Prev: &Page{
    40  					Key:      testPrevKey,
    41  					Relation: "prev",
    42  					Limit:    testPrevLimit,
    43  				},
    44  			}
    45  
    46  			pm := &PaginationMetadata{
    47  				Pages:           testPages,
    48  				KeyQueryParam:   testKeyQueryParam,
    49  				LimitQueryParam: testLimitQueryParam,
    50  			}
    51  
    52  			err := pm.MakeHeader(w, testURL, testRoute)
    53  			So(err, ShouldBeNil)
    54  			linksHeader, ok := w.Header()["Link"]
    55  			So(ok, ShouldBeTrue)
    56  			So(len(linksHeader), ShouldEqual, 1)
    57  			pmRes, err := ParsePaginationHeader(linksHeader[0], testKeyQueryParam, testLimitQueryParam)
    58  			So(err, ShouldBeNil)
    59  
    60  			So(pmRes.Pages, ShouldNotBeNil)
    61  			So(pmRes.Pages.Next, ShouldResemble, testPages.Next)
    62  			So(pmRes.Pages.Prev, ShouldResemble, testPages.Prev)
    63  
    64  		})
    65  		Convey("creating a PaginationMetadata with just a NextKey"+
    66  			" should creating 1 links in the header", func() {
    67  			testPages := &PageResult{
    68  				Next: &Page{
    69  					Key:      testNextKey,
    70  					Relation: "next",
    71  					Limit:    testNextLimit,
    72  				},
    73  			}
    74  
    75  			pm := &PaginationMetadata{
    76  				Pages: testPages,
    77  
    78  				KeyQueryParam:   testKeyQueryParam,
    79  				LimitQueryParam: testLimitQueryParam,
    80  			}
    81  
    82  			err := pm.MakeHeader(w, testURL, testRoute)
    83  			So(err, ShouldBeNil)
    84  			linksHeader, ok := w.Header()["Link"]
    85  			So(ok, ShouldBeTrue)
    86  			So(len(linksHeader), ShouldEqual, 1)
    87  			pmRes, err := ParsePaginationHeader(linksHeader[0], testKeyQueryParam, testLimitQueryParam)
    88  			So(err, ShouldBeNil)
    89  
    90  			So(pmRes.Pages, ShouldNotBeNil)
    91  			So(pmRes.Pages.Next, ShouldResemble, testPages.Next)
    92  
    93  		})
    94  		Convey("creating a page with a limit of 0 should parse as 0", func() {
    95  			testPages := &PageResult{
    96  				Next: &Page{
    97  					Key:      testNextKey,
    98  					Relation: "next",
    99  				},
   100  			}
   101  
   102  			pm := &PaginationMetadata{
   103  				Pages: testPages,
   104  
   105  				KeyQueryParam:   testKeyQueryParam,
   106  				LimitQueryParam: testLimitQueryParam,
   107  			}
   108  
   109  			err := pm.MakeHeader(w, testURL, testRoute)
   110  			So(err, ShouldBeNil)
   111  			linksHeader, ok := w.Header()["Link"]
   112  			So(ok, ShouldBeTrue)
   113  			So(len(linksHeader), ShouldEqual, 1)
   114  			pmRes, err := ParsePaginationHeader(linksHeader[0], testKeyQueryParam, testLimitQueryParam)
   115  			So(err, ShouldBeNil)
   116  
   117  			So(pmRes.Pages, ShouldNotBeNil)
   118  			So(pmRes.Pages.Next, ShouldResemble, testPages.Next)
   119  		})
   120  	})
   121  }
   122  
   123  type testPaginationRequestHandler struct {
   124  	*PaginationExecutor
   125  }
   126  
   127  func (prh *testPaginationRequestHandler) Handler() RequestHandler {
   128  	return prh
   129  }
   130  
   131  func TestPaginationExecutor(t *testing.T) {
   132  	curPage := Page{
   133  		Limit:    10,
   134  		Key:      "cur_key",
   135  		Relation: "current",
   136  	}
   137  	nextPage := Page{
   138  		Limit:    10,
   139  		Key:      "next_key",
   140  		Relation: "next",
   141  	}
   142  	prevPage := Page{
   143  		Limit:    7,
   144  		Key:      "prev_key",
   145  		Relation: "prev",
   146  	}
   147  	numModels := 10
   148  	testKeyQueryParam := "key"
   149  	testLimitQueryParam := "limit"
   150  	testModels := []model.Model{}
   151  	for i := 0; i < numModels; i++ {
   152  		nextModel := &model.MockModel{
   153  			FieldId:   fmt.Sprintf("model_%d", i),
   154  			FieldInt1: i,
   155  		}
   156  		testModels = append(testModels, nextModel)
   157  	}
   158  	Convey("When paginating with a paginator func and executor", t, func() {
   159  		testPaginatorFunc := mockPaginatorFuncGenerator(testModels, nextPage.Key, prevPage.Key,
   160  			nextPage.Limit, prevPage.Limit, nil)
   161  
   162  		executor := &PaginationExecutor{
   163  			KeyQueryParam:   testKeyQueryParam,
   164  			LimitQueryParam: testLimitQueryParam,
   165  
   166  			Paginator: testPaginatorFunc,
   167  		}
   168  
   169  		prh := &testPaginationRequestHandler{
   170  			executor,
   171  		}
   172  
   173  		mh := MethodHandler{
   174  			MethodType:     "GET",
   175  			Authenticator:  &NoAuthAuthenticator{},
   176  			RequestHandler: prh,
   177  		}
   178  		Convey("A request with key and limit should return a correctly formed result",
   179  			func() {
   180  
   181  				queryParams := fmt.Sprintf("%s=%s&%s=%d", testKeyQueryParam, curPage.Key,
   182  					testLimitQueryParam, curPage.Limit)
   183  
   184  				expectedPages := PageResult{&nextPage, &prevPage}
   185  
   186  				hcf := func(header map[string][]string, t *testing.T) {
   187  					linkHeader := header["Link"]
   188  					So(len(linkHeader), ShouldEqual, 1)
   189  					pmRes, err := ParsePaginationHeader(linkHeader[0], testKeyQueryParam, testLimitQueryParam)
   190  					So(err, ShouldBeNil)
   191  
   192  					So(pmRes.Pages, ShouldNotBeNil)
   193  					So(pmRes.Pages.Next, ShouldResemble, expectedPages.Next)
   194  					So(pmRes.Pages.Prev, ShouldResemble, expectedPages.Prev)
   195  				}
   196  
   197  				checkResultMatches(mh, nil,
   198  					testModels, hcf, queryParams, http.StatusOK, t)
   199  			})
   200  		Convey("A request without a non number limit should return an error",
   201  			func() {
   202  
   203  				queryParams := fmt.Sprintf("%s=%s", testLimitQueryParam, "garbage")
   204  
   205  				expectedError := rest.APIError{
   206  					StatusCode: http.StatusBadRequest,
   207  					Message: fmt.Sprintf("Value '%v' provided for '%v' must be integer",
   208  						"garbage", testLimitQueryParam),
   209  				}
   210  
   211  				checkResultMatches(mh, expectedError,
   212  					nil, nil, queryParams, http.StatusBadRequest, t)
   213  			})
   214  		Convey("an errored executor should return an error",
   215  			func() {
   216  				errToReturn := fmt.Errorf("not found in DB")
   217  
   218  				executor.Paginator = mockPaginatorFuncGenerator(nil, "", "", 0, 0, errToReturn)
   219  				checkResultMatches(mh, errToReturn,
   220  					nil, nil, "", http.StatusInternalServerError, t)
   221  			})
   222  	})
   223  }
   224  
   225  type testAdditionalArgsPaginator struct {
   226  	*PaginationExecutor
   227  }
   228  
   229  func (tap *testAdditionalArgsPaginator) Handler() RequestHandler {
   230  	return tap
   231  }
   232  
   233  type ArgHolder struct {
   234  	Arg1 int
   235  	Arg2 string
   236  }
   237  
   238  const (
   239  	testArg1Holder = "arg1"
   240  	testArg2Holder = "arg2"
   241  )
   242  
   243  func (tap *testAdditionalArgsPaginator) ParseAndValidate(r *http.Request) error {
   244  	arg1 := context.Get(r, testArg1Holder)
   245  	castArg1, ok := arg1.(int)
   246  	if !ok {
   247  		return fmt.Errorf("incorrect type for arg1. found %v", arg1)
   248  	}
   249  	arg2 := context.Get(r, testArg2Holder)
   250  	castArg2, ok := arg2.(string)
   251  	if !ok {
   252  		return fmt.Errorf("incorrect type for arg2. found %v", arg2)
   253  	}
   254  
   255  	ah := ArgHolder{
   256  		Arg1: castArg1,
   257  		Arg2: castArg2,
   258  	}
   259  	tap.Args = &ah
   260  	return tap.PaginationExecutor.ParseAndValidate(r)
   261  }
   262  
   263  func TestPaginatorAdditionalArgs(t *testing.T) {
   264  	Convey("When paginating with a additional args paginator func and args", t, func() {
   265  		arg1 := 7
   266  		arg2 := "testArg1"
   267  
   268  		sc := data.MockConnector{}
   269  
   270  		r := &http.Request{
   271  			URL: &url.URL{
   272  				RawQuery: "key=key&limit=100",
   273  			},
   274  		}
   275  
   276  		context.Set(r, testArg1Holder, arg1)
   277  		context.Set(r, testArg2Holder, arg2)
   278  
   279  		pf := func(key string, limit int, args interface{},
   280  			sc data.Connector) ([]model.Model, *PageResult, error) {
   281  			castArgs, ok := args.(*ArgHolder)
   282  			So(ok, ShouldBeTrue)
   283  			So(castArgs.Arg1, ShouldEqual, arg1)
   284  			So(castArgs.Arg2, ShouldEqual, arg2)
   285  			So(key, ShouldEqual, "key")
   286  			So(limit, ShouldEqual, 100)
   287  			return []model.Model{}, nil, nil
   288  		}
   289  
   290  		executor := &PaginationExecutor{
   291  			KeyQueryParam:   "key",
   292  			LimitQueryParam: "limit",
   293  
   294  			Paginator: pf,
   295  		}
   296  		tap := testAdditionalArgsPaginator{executor}
   297  
   298  		Convey("parsing should succeed and args should be set", func() {
   299  			err := tap.ParseAndValidate(r)
   300  			So(err, ShouldBeNil)
   301  			_, err = tap.Execute(&sc)
   302  			So(err, ShouldBeNil)
   303  		})
   304  	})
   305  }