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