github.com/sotirispl/buffalo@v0.11.1/router_test.go (about) 1 package buffalo 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "net/http" 7 "net/http/httptest" 8 "path" 9 "path/filepath" 10 "testing" 11 12 "github.com/gobuffalo/buffalo/render" 13 "github.com/gobuffalo/packr" 14 "github.com/gorilla/mux" 15 "github.com/markbates/willie" 16 "github.com/stretchr/testify/require" 17 ) 18 19 func testApp() *App { 20 a := New(Options{}) 21 a.Redirect(301, "/foo", "/bar") 22 a.GET("/bar", func(c Context) error { 23 return c.Render(200, render.String("bar")) 24 }) 25 26 rt := a.Group("/router/tests") 27 28 h := func(c Context) error { 29 return c.Render(200, render.String(c.Request().Method+"|"+c.Value("current_path").(string))) 30 } 31 32 rt.GET("/", h) 33 rt.POST("/", h) 34 rt.PUT("/", h) 35 rt.DELETE("/", h) 36 rt.OPTIONS("/", h) 37 rt.PATCH("/", h) 38 return a 39 } 40 41 func otherTestApp() *App { 42 a := New(Options{}) 43 f := func(c Context) error { 44 req := c.Request() 45 return c.Render(200, render.String(req.Method+" - "+req.URL.String())) 46 } 47 a.GET("/foo", f) 48 a.POST("/bar", f) 49 a.DELETE("/baz/baz", f) 50 return a 51 } 52 53 func Test_Mount_Buffalo(t *testing.T) { 54 r := require.New(t) 55 a := testApp() 56 a.Mount("/admin", otherTestApp()) 57 58 table := map[string]string{ 59 "/foo": "GET", 60 "/bar": "POST", 61 "/baz/baz": "DELETE", 62 } 63 ts := httptest.NewServer(a) 64 defer ts.Close() 65 66 for u, m := range table { 67 p := fmt.Sprintf("%s/%s", ts.URL, path.Join("admin", u)) 68 req, err := http.NewRequest(m, p, nil) 69 r.NoError(err) 70 res, err := http.DefaultClient.Do(req) 71 r.NoError(err) 72 b, _ := ioutil.ReadAll(res.Body) 73 r.Equal(fmt.Sprintf("%s - %s", m, u), string(b)) 74 } 75 } 76 77 func Test_Mount_Buffalo_on_Group(t *testing.T) { 78 r := require.New(t) 79 a := testApp() 80 g := a.Group("/users") 81 g.Mount("/admin", otherTestApp()) 82 83 table := map[string]string{ 84 "/foo": "GET", 85 "/bar": "POST", 86 "/baz/baz": "DELETE", 87 } 88 ts := httptest.NewServer(a) 89 defer ts.Close() 90 91 for u, m := range table { 92 p := fmt.Sprintf("%s/%s", ts.URL, path.Join("users", "admin", u)) 93 req, err := http.NewRequest(m, p, nil) 94 r.NoError(err) 95 res, err := http.DefaultClient.Do(req) 96 r.NoError(err) 97 b, _ := ioutil.ReadAll(res.Body) 98 r.Equal(fmt.Sprintf("%s - %s", m, u), string(b)) 99 } 100 } 101 102 func muxer() http.Handler { 103 f := func(res http.ResponseWriter, req *http.Request) { 104 fmt.Fprintf(res, "%s - %s", req.Method, req.URL.String()) 105 } 106 mux := mux.NewRouter() 107 mux.HandleFunc("/foo", f).Methods("GET") 108 mux.HandleFunc("/bar", f).Methods("POST") 109 mux.HandleFunc("/baz/baz", f).Methods("DELETE") 110 return mux 111 } 112 113 func Test_Mount_Handler(t *testing.T) { 114 r := require.New(t) 115 a := testApp() 116 a.Mount("/admin", muxer()) 117 118 table := map[string]string{ 119 "/foo": "GET", 120 "/bar": "POST", 121 "/baz/baz": "DELETE", 122 } 123 ts := httptest.NewServer(a) 124 defer ts.Close() 125 126 for u, m := range table { 127 p := fmt.Sprintf("%s/%s", ts.URL, path.Join("admin", u)) 128 req, err := http.NewRequest(m, p, nil) 129 r.NoError(err) 130 res, err := http.DefaultClient.Do(req) 131 r.NoError(err) 132 b, _ := ioutil.ReadAll(res.Body) 133 r.Equal(fmt.Sprintf("%s - %s", m, u), string(b)) 134 } 135 } 136 137 func Test_PreHandlers(t *testing.T) { 138 r := require.New(t) 139 a := testApp() 140 bh := func(c Context) error { 141 req := c.Request() 142 return c.Render(200, render.String(req.Method+"-"+req.URL.String())) 143 } 144 a.GET("/ph", bh) 145 a.POST("/ph", bh) 146 mh := func(res http.ResponseWriter, req *http.Request) { 147 if req.Method == "GET" { 148 res.WriteHeader(418) 149 res.Write([]byte("boo")) 150 } 151 } 152 a.PreHandlers = append(a.PreHandlers, http.HandlerFunc(mh)) 153 154 ts := httptest.NewServer(a) 155 defer ts.Close() 156 157 table := []struct { 158 Code int 159 Method string 160 Result string 161 }{ 162 {Code: 418, Method: "GET", Result: "boo"}, 163 {Code: 200, Method: "POST", Result: "POST-/ph"}, 164 } 165 166 for _, v := range table { 167 req, err := http.NewRequest(v.Method, ts.URL+"/ph", nil) 168 r.NoError(err) 169 res, err := http.DefaultClient.Do(req) 170 r.NoError(err) 171 b, err := ioutil.ReadAll(res.Body) 172 r.NoError(err) 173 r.Equal(v.Code, res.StatusCode) 174 r.Equal(v.Result, string(b)) 175 } 176 } 177 178 func Test_PreWares(t *testing.T) { 179 r := require.New(t) 180 a := testApp() 181 bh := func(c Context) error { 182 req := c.Request() 183 return c.Render(200, render.String(req.Method+"-"+req.URL.String())) 184 } 185 a.GET("/ph", bh) 186 a.POST("/ph", bh) 187 188 mh := func(h http.Handler) http.Handler { 189 return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { 190 if req.Method == "GET" { 191 res.WriteHeader(418) 192 res.Write([]byte("boo")) 193 } 194 }) 195 } 196 197 a.PreWares = append(a.PreWares, mh) 198 199 ts := httptest.NewServer(a) 200 defer ts.Close() 201 202 table := []struct { 203 Code int 204 Method string 205 Result string 206 }{ 207 {Code: 418, Method: "GET", Result: "boo"}, 208 {Code: 200, Method: "POST", Result: "POST-/ph"}, 209 } 210 211 for _, v := range table { 212 req, err := http.NewRequest(v.Method, ts.URL+"/ph", nil) 213 r.NoError(err) 214 res, err := http.DefaultClient.Do(req) 215 r.NoError(err) 216 b, err := ioutil.ReadAll(res.Body) 217 r.NoError(err) 218 r.Equal(v.Code, res.StatusCode) 219 r.Equal(v.Result, string(b)) 220 } 221 } 222 223 func Test_Router(t *testing.T) { 224 r := require.New(t) 225 226 table := []string{ 227 "GET", 228 "POST", 229 "PUT", 230 "DELETE", 231 "OPTIONS", 232 "PATCH", 233 } 234 235 ts := httptest.NewServer(testApp()) 236 defer ts.Close() 237 238 for _, v := range table { 239 req, err := http.NewRequest(v, fmt.Sprintf("%s/router/tests", ts.URL), nil) 240 r.NoError(err) 241 res, err := http.DefaultClient.Do(req) 242 r.NoError(err) 243 b, _ := ioutil.ReadAll(res.Body) 244 r.Equal(fmt.Sprintf("%s|/router/tests", v), string(b)) 245 } 246 } 247 248 func Test_Router_Group(t *testing.T) { 249 r := require.New(t) 250 251 a := testApp() 252 g := a.Group("/api/v1") 253 g.GET("/users", func(c Context) error { 254 return c.Render(201, nil) 255 }) 256 257 w := willie.New(a) 258 res := w.Request("/api/v1/users").Get() 259 r.Equal(201, res.Code) 260 } 261 262 func Test_Router_Group_on_Group(t *testing.T) { 263 r := require.New(t) 264 265 a := testApp() 266 g := a.Group("/api/v1") 267 g.GET("/users", func(c Context) error { 268 return c.Render(201, nil) 269 }) 270 f := g.Group("/foo") 271 f.GET("/bar", func(c Context) error { 272 return c.Render(420, nil) 273 }) 274 275 w := willie.New(a) 276 res := w.Request("/api/v1/foo/bar").Get() 277 r.Equal(420, res.Code) 278 } 279 280 func Test_Router_Group_Middleware(t *testing.T) { 281 r := require.New(t) 282 283 a := testApp() 284 a.Use(func(h Handler) Handler { return h }) 285 r.Len(a.Middleware.stack, 4) 286 287 g := a.Group("/api/v1") 288 r.Len(a.Middleware.stack, 4) 289 r.Len(g.Middleware.stack, 4) 290 291 g.Use(func(h Handler) Handler { return h }) 292 r.Len(a.Middleware.stack, 4) 293 r.Len(g.Middleware.stack, 5) 294 } 295 296 func Test_Router_Redirect(t *testing.T) { 297 r := require.New(t) 298 w := willie.New(testApp()) 299 res := w.Request("/foo").Get() 300 r.Equal(301, res.Code) 301 r.Equal("/bar", res.Location()) 302 } 303 304 func Test_Router_ServeFiles(t *testing.T) { 305 r := require.New(t) 306 307 tmpFile, err := ioutil.TempFile("", "assets") 308 r.NoError(err) 309 310 af := []byte("hi") 311 _, err = tmpFile.Write(af) 312 r.NoError(err) 313 314 a := New(Options{}) 315 a.ServeFiles("/assets", http.Dir(filepath.Dir(tmpFile.Name()))) 316 317 w := willie.New(a) 318 res := w.Request("/assets/%s", filepath.Base(tmpFile.Name())).Get() 319 320 r.Equal(200, res.Code) 321 r.Equal(af, res.Body.Bytes()) 322 } 323 324 func Test_App_NamedRoutes(t *testing.T) { 325 326 type CarsResource struct { 327 *BaseResource 328 } 329 330 r := require.New(t) 331 a := New(Options{}) 332 333 var carsResource Resource 334 carsResource = CarsResource{&BaseResource{}} 335 336 rr := render.New(render.Options{ 337 HTMLLayout: "application.html", 338 TemplatesBox: packr.NewBox("../templates"), 339 Helpers: map[string]interface{}{}, 340 }) 341 342 sampleHandler := func(c Context) error { 343 c.Set("opts", map[string]interface{}{}) 344 return c.Render(200, rr.String(` 345 1. <%= rootPath() %> 346 2. <%= usersPath() %> 347 3. <%= userPath({user_id: 1}) %> 348 4. <%= myPeepsPath() %> 349 5. <%= userPath(opts) %> 350 6. <%= carPath({car_id: 1}) %> 351 7. <%= newCarPath() %> 352 8. <%= editCarPath({car_id: 1}) %> 353 9. <%= editCarPath({car_id: 1, other: 12}) %> 354 10. <%= rootPath({"some":"variable","other": 12}) %> 355 11. <%= rootPath() %> 356 12. <%= rootPath({"special/":"12=ss"}) %> 357 `)) 358 } 359 360 a.GET("/", sampleHandler) 361 a.GET("/users", sampleHandler) 362 a.GET("/users/{user_id}", sampleHandler) 363 a.GET("/peeps", sampleHandler).Name("myPeeps") 364 a.Resource("/car", carsResource) 365 366 w := willie.New(a) 367 res := w.Request("/").Get() 368 369 r.Equal(200, res.Code) 370 r.Contains(res.Body.String(), "1. /") 371 r.Contains(res.Body.String(), "2. /users") 372 r.Contains(res.Body.String(), "3. /users/1") 373 r.Contains(res.Body.String(), "4. /peeps") 374 r.Contains(res.Body.String(), "5. /users/{user_id}") 375 r.Contains(res.Body.String(), "6. /car/1") 376 r.Contains(res.Body.String(), "7. /car/new") 377 r.Contains(res.Body.String(), "8. /car/1/edit") 378 r.Contains(res.Body.String(), "9. /car/1/edit?other=12") 379 r.Contains(res.Body.String(), "10. /?other=12&some=variable") 380 r.Contains(res.Body.String(), "11. /") 381 r.Contains(res.Body.String(), "12. /?special%2F=12%3Dss") 382 } 383 384 func Test_Resource(t *testing.T) { 385 r := require.New(t) 386 387 type trs struct { 388 Method string 389 Path string 390 Result string 391 } 392 393 tests := []trs{ 394 { 395 Method: "GET", 396 Path: "", 397 Result: "list", 398 }, 399 { 400 Method: "GET", 401 Path: "/new", 402 Result: "new", 403 }, 404 { 405 Method: "GET", 406 Path: "/1", 407 Result: "show 1", 408 }, 409 { 410 Method: "GET", 411 Path: "/1/edit", 412 Result: "edit 1", 413 }, 414 { 415 Method: "POST", 416 Path: "", 417 Result: "create", 418 }, 419 { 420 Method: "PUT", 421 Path: "/1", 422 Result: "update 1", 423 }, 424 { 425 Method: "DELETE", 426 Path: "/1", 427 Result: "destroy 1", 428 }, 429 } 430 431 a := New(Options{}) 432 a.Resource("/users", &userResource{}) 433 a.Resource("/api/v1/users", &userResource{}) 434 435 ts := httptest.NewServer(a) 436 defer ts.Close() 437 438 c := http.Client{} 439 for _, path := range []string{"/users", "/api/v1/users"} { 440 for _, test := range tests { 441 u := ts.URL + path + test.Path 442 req, err := http.NewRequest(test.Method, u, nil) 443 r.NoError(err) 444 res, err := c.Do(req) 445 r.NoError(err) 446 b, err := ioutil.ReadAll(res.Body) 447 r.NoError(err) 448 r.Equal(test.Result, string(b)) 449 } 450 } 451 452 } 453 454 type userResource struct{} 455 456 func (u *userResource) List(c Context) error { 457 return c.Render(200, render.String("list")) 458 } 459 460 func (u *userResource) Show(c Context) error { 461 return c.Render(200, render.String(`show <%=params["user_id"] %>`)) 462 } 463 464 func (u *userResource) New(c Context) error { 465 return c.Render(200, render.String("new")) 466 } 467 468 func (u *userResource) Create(c Context) error { 469 return c.Render(200, render.String("create")) 470 } 471 472 func (u *userResource) Edit(c Context) error { 473 return c.Render(200, render.String(`edit <%=params["user_id"] %>`)) 474 } 475 476 func (u *userResource) Update(c Context) error { 477 return c.Render(200, render.String(`update <%=params["user_id"] %>`)) 478 } 479 480 func (u *userResource) Destroy(c Context) error { 481 return c.Render(200, render.String(`destroy <%=params["user_id"] %>`)) 482 } 483 484 func Test_ResourceOnResource(t *testing.T) { 485 r := require.New(t) 486 487 a := New(Options{}) 488 ur := a.Resource("/users", &userResource{}) 489 ur.Resource("/people", &userResource{}) 490 491 ts := httptest.NewServer(a) 492 defer ts.Close() 493 494 type trs struct { 495 Method string 496 Path string 497 Result string 498 } 499 tests := []trs{ 500 { 501 Method: "GET", 502 Path: "/people", 503 Result: "list", 504 }, 505 { 506 Method: "GET", 507 Path: "/people/new", 508 Result: "new", 509 }, 510 { 511 Method: "GET", 512 Path: "/people/1", 513 Result: "show 1", 514 }, 515 { 516 Method: "GET", 517 Path: "/people/1/edit", 518 Result: "edit 1", 519 }, 520 { 521 Method: "POST", 522 Path: "/people", 523 Result: "create", 524 }, 525 { 526 Method: "PUT", 527 Path: "/people/1", 528 Result: "update 1", 529 }, 530 { 531 Method: "DELETE", 532 Path: "/people/1", 533 Result: "destroy 1", 534 }, 535 } 536 c := http.Client{} 537 for _, test := range tests { 538 u := ts.URL + path.Join("/users/42", test.Path) 539 req, err := http.NewRequest(test.Method, u, nil) 540 r.NoError(err) 541 res, err := c.Do(req) 542 r.NoError(err) 543 b, err := ioutil.ReadAll(res.Body) 544 r.NoError(err) 545 r.Equal(test.Result, string(b)) 546 } 547 548 } 549 550 func Test_buildRouteName(t *testing.T) { 551 r := require.New(t) 552 cases := map[string]string{ 553 "/": "root", 554 "/users": "users", 555 "/users/new": "newUsers", 556 "/users/{user_id}": "user", 557 "/users/{user_id}/children": "userChildren", 558 "/users/{user_id}/children/{child_id}": "userChild", 559 "/users/{user_id}/children/new": "newUserChildren", 560 "/users/{user_id}/children/{child_id}/build": "userChildBuild", 561 "/admin/planes": "adminPlanes", 562 "/admin/planes/{plane_id}": "adminPlane", 563 "/admin/planes/{plane_id}/edit": "editAdminPlane", 564 } 565 566 a := New(Options{}) 567 568 for input, result := range cases { 569 fResult := a.buildRouteName(input) 570 r.Equal(result, fResult, input) 571 } 572 573 a = New(Options{Prefix: "/test"}) 574 cases = map[string]string{ 575 "/test": "test", 576 "/test/users": "testUsers", 577 } 578 579 for input, result := range cases { 580 fResult := a.buildRouteName(input) 581 r.Equal(result, fResult, input) 582 } 583 } 584 585 func Test_CatchAll_Route(t *testing.T) { 586 r := require.New(t) 587 rr := render.New(render.Options{}) 588 589 a := New(Options{}) 590 a.GET("/{name:.+}", func(c Context) error { 591 name := c.Param("name") 592 return c.Render(200, rr.String(name)) 593 }) 594 595 w := willie.New(a) 596 res := w.Request("/john").Get() 597 598 r.Contains(res.Body.String(), "john") 599 } 600 601 func Test_Router_Matches_Trailing_Slash(t *testing.T) { 602 r := require.New(t) 603 604 table := []string{ 605 "/bar", 606 "/bar/", 607 } 608 609 ts := httptest.NewServer(testApp()) 610 defer ts.Close() 611 612 for _, v := range table { 613 req, err := http.NewRequest("GET", fmt.Sprintf("%s/%s", ts.URL, v), nil) 614 r.NoError(err) 615 res, err := http.DefaultClient.Do(req) 616 r.NoError(err) 617 b, _ := ioutil.ReadAll(res.Body) 618 r.Equal("bar", string(b)) 619 } 620 }