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