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 }