You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

705 lines
17 KiB

  1. // Old tests ported to Go1. This is a mess. Want to drop it one day.
  2. // Copyright 2011 Gorilla Authors. All rights reserved.
  3. // Use of this source code is governed by a BSD-style
  4. // license that can be found in the LICENSE file.
  5. package mux
  6. import (
  7. "bytes"
  8. "net/http"
  9. "testing"
  10. )
  11. // ----------------------------------------------------------------------------
  12. // ResponseRecorder
  13. // ----------------------------------------------------------------------------
  14. // Copyright 2009 The Go Authors. All rights reserved.
  15. // Use of this source code is governed by a BSD-style
  16. // license that can be found in the LICENSE file.
  17. // ResponseRecorder is an implementation of http.ResponseWriter that
  18. // records its mutations for later inspection in tests.
  19. type ResponseRecorder struct {
  20. Code int // the HTTP response code from WriteHeader
  21. HeaderMap http.Header // the HTTP response headers
  22. Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to
  23. Flushed bool
  24. }
  25. // NewRecorder returns an initialized ResponseRecorder.
  26. func NewRecorder() *ResponseRecorder {
  27. return &ResponseRecorder{
  28. HeaderMap: make(http.Header),
  29. Body: new(bytes.Buffer),
  30. }
  31. }
  32. // Header returns the response headers.
  33. func (rw *ResponseRecorder) Header() http.Header {
  34. return rw.HeaderMap
  35. }
  36. // Write always succeeds and writes to rw.Body, if not nil.
  37. func (rw *ResponseRecorder) Write(buf []byte) (int, error) {
  38. if rw.Body != nil {
  39. rw.Body.Write(buf)
  40. }
  41. if rw.Code == 0 {
  42. rw.Code = http.StatusOK
  43. }
  44. return len(buf), nil
  45. }
  46. // WriteHeader sets rw.Code.
  47. func (rw *ResponseRecorder) WriteHeader(code int) {
  48. rw.Code = code
  49. }
  50. // Flush sets rw.Flushed to true.
  51. func (rw *ResponseRecorder) Flush() {
  52. rw.Flushed = true
  53. }
  54. // ----------------------------------------------------------------------------
  55. func TestRouteMatchers(t *testing.T) {
  56. var scheme, host, path, query, method string
  57. var headers map[string]string
  58. var resultVars map[bool]map[string]string
  59. router := NewRouter()
  60. router.NewRoute().Host("{var1}.google.com").
  61. Path("/{var2:[a-z]+}/{var3:[0-9]+}").
  62. Queries("foo", "bar").
  63. Methods("GET").
  64. Schemes("https").
  65. Headers("x-requested-with", "XMLHttpRequest")
  66. router.NewRoute().Host("www.{var4}.com").
  67. PathPrefix("/foo/{var5:[a-z]+}/{var6:[0-9]+}").
  68. Queries("baz", "ding").
  69. Methods("POST").
  70. Schemes("http").
  71. Headers("Content-Type", "application/json")
  72. reset := func() {
  73. // Everything match.
  74. scheme = "https"
  75. host = "www.google.com"
  76. path = "/product/42"
  77. query = "?foo=bar"
  78. method = "GET"
  79. headers = map[string]string{"X-Requested-With": "XMLHttpRequest"}
  80. resultVars = map[bool]map[string]string{
  81. true: {"var1": "www", "var2": "product", "var3": "42"},
  82. false: {},
  83. }
  84. }
  85. reset2 := func() {
  86. // Everything match.
  87. scheme = "http"
  88. host = "www.google.com"
  89. path = "/foo/product/42/path/that/is/ignored"
  90. query = "?baz=ding"
  91. method = "POST"
  92. headers = map[string]string{"Content-Type": "application/json"}
  93. resultVars = map[bool]map[string]string{
  94. true: {"var4": "google", "var5": "product", "var6": "42"},
  95. false: {},
  96. }
  97. }
  98. match := func(shouldMatch bool) {
  99. url := scheme + "://" + host + path + query
  100. request, _ := http.NewRequest(method, url, nil)
  101. for key, value := range headers {
  102. request.Header.Add(key, value)
  103. }
  104. var routeMatch RouteMatch
  105. matched := router.Match(request, &routeMatch)
  106. if matched != shouldMatch {
  107. t.Errorf("Expected: %v\nGot: %v\nRequest: %v %v", shouldMatch, matched, request.Method, url)
  108. }
  109. if matched {
  110. currentRoute := routeMatch.Route
  111. if currentRoute == nil {
  112. t.Errorf("Expected a current route.")
  113. }
  114. vars := routeMatch.Vars
  115. expectedVars := resultVars[shouldMatch]
  116. if len(vars) != len(expectedVars) {
  117. t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars)
  118. }
  119. for name, value := range vars {
  120. if expectedVars[name] != value {
  121. t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars)
  122. }
  123. }
  124. }
  125. }
  126. // 1st route --------------------------------------------------------------
  127. // Everything match.
  128. reset()
  129. match(true)
  130. // Scheme doesn't match.
  131. reset()
  132. scheme = "http"
  133. match(false)
  134. // Host doesn't match.
  135. reset()
  136. host = "www.mygoogle.com"
  137. match(false)
  138. // Path doesn't match.
  139. reset()
  140. path = "/product/notdigits"
  141. match(false)
  142. // Query doesn't match.
  143. reset()
  144. query = "?foo=baz"
  145. match(false)
  146. // Method doesn't match.
  147. reset()
  148. method = "POST"
  149. match(false)
  150. // Header doesn't match.
  151. reset()
  152. headers = map[string]string{}
  153. match(false)
  154. // Everything match, again.
  155. reset()
  156. match(true)
  157. // 2nd route --------------------------------------------------------------
  158. // Everything match.
  159. reset2()
  160. match(true)
  161. // Scheme doesn't match.
  162. reset2()
  163. scheme = "https"
  164. match(false)
  165. // Host doesn't match.
  166. reset2()
  167. host = "sub.google.com"
  168. match(false)
  169. // Path doesn't match.
  170. reset2()
  171. path = "/bar/product/42"
  172. match(false)
  173. // Query doesn't match.
  174. reset2()
  175. query = "?foo=baz"
  176. match(false)
  177. // Method doesn't match.
  178. reset2()
  179. method = "GET"
  180. match(false)
  181. // Header doesn't match.
  182. reset2()
  183. headers = map[string]string{}
  184. match(false)
  185. // Everything match, again.
  186. reset2()
  187. match(true)
  188. }
  189. type headerMatcherTest struct {
  190. matcher headerMatcher
  191. headers map[string]string
  192. result bool
  193. }
  194. var headerMatcherTests = []headerMatcherTest{
  195. {
  196. matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}),
  197. headers: map[string]string{"X-Requested-With": "XMLHttpRequest"},
  198. result: true,
  199. },
  200. {
  201. matcher: headerMatcher(map[string]string{"x-requested-with": ""}),
  202. headers: map[string]string{"X-Requested-With": "anything"},
  203. result: true,
  204. },
  205. {
  206. matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}),
  207. headers: map[string]string{},
  208. result: false,
  209. },
  210. }
  211. type hostMatcherTest struct {
  212. matcher *Route
  213. url string
  214. vars map[string]string
  215. result bool
  216. }
  217. var hostMatcherTests = []hostMatcherTest{
  218. {
  219. matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}"),
  220. url: "http://abc.def.ghi/",
  221. vars: map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"},
  222. result: true,
  223. },
  224. {
  225. matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}"),
  226. url: "http://a.b.c/",
  227. vars: map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"},
  228. result: false,
  229. },
  230. }
  231. type methodMatcherTest struct {
  232. matcher methodMatcher
  233. method string
  234. result bool
  235. }
  236. var methodMatcherTests = []methodMatcherTest{
  237. {
  238. matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
  239. method: "GET",
  240. result: true,
  241. },
  242. {
  243. matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
  244. method: "POST",
  245. result: true,
  246. },
  247. {
  248. matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
  249. method: "PUT",
  250. result: true,
  251. },
  252. {
  253. matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
  254. method: "DELETE",
  255. result: false,
  256. },
  257. }
  258. type pathMatcherTest struct {
  259. matcher *Route
  260. url string
  261. vars map[string]string
  262. result bool
  263. }
  264. var pathMatcherTests = []pathMatcherTest{
  265. {
  266. matcher: NewRouter().NewRoute().Path("/{foo:[0-9][0-9][0-9]}/{bar:[0-9][0-9][0-9]}/{baz:[0-9][0-9][0-9]}"),
  267. url: "http://localhost:8080/123/456/789",
  268. vars: map[string]string{"foo": "123", "bar": "456", "baz": "789"},
  269. result: true,
  270. },
  271. {
  272. matcher: NewRouter().NewRoute().Path("/{foo:[0-9][0-9][0-9]}/{bar:[0-9][0-9][0-9]}/{baz:[0-9][0-9][0-9]}"),
  273. url: "http://localhost:8080/1/2/3",
  274. vars: map[string]string{"foo": "123", "bar": "456", "baz": "789"},
  275. result: false,
  276. },
  277. }
  278. type schemeMatcherTest struct {
  279. matcher schemeMatcher
  280. url string
  281. result bool
  282. }
  283. var schemeMatcherTests = []schemeMatcherTest{
  284. {
  285. matcher: schemeMatcher([]string{"http", "https"}),
  286. url: "http://localhost:8080/",
  287. result: true,
  288. },
  289. {
  290. matcher: schemeMatcher([]string{"http", "https"}),
  291. url: "https://localhost:8080/",
  292. result: true,
  293. },
  294. {
  295. matcher: schemeMatcher([]string{"https"}),
  296. url: "http://localhost:8080/",
  297. result: false,
  298. },
  299. {
  300. matcher: schemeMatcher([]string{"http"}),
  301. url: "https://localhost:8080/",
  302. result: false,
  303. },
  304. }
  305. type urlBuildingTest struct {
  306. route *Route
  307. vars []string
  308. url string
  309. }
  310. var urlBuildingTests = []urlBuildingTest{
  311. {
  312. route: new(Route).Host("foo.domain.com"),
  313. vars: []string{},
  314. url: "http://foo.domain.com",
  315. },
  316. {
  317. route: new(Route).Host("{subdomain}.domain.com"),
  318. vars: []string{"subdomain", "bar"},
  319. url: "http://bar.domain.com",
  320. },
  321. {
  322. route: new(Route).Host("foo.domain.com").Path("/articles"),
  323. vars: []string{},
  324. url: "http://foo.domain.com/articles",
  325. },
  326. {
  327. route: new(Route).Path("/articles"),
  328. vars: []string{},
  329. url: "/articles",
  330. },
  331. {
  332. route: new(Route).Path("/articles/{category}/{id:[0-9]+}"),
  333. vars: []string{"category", "technology", "id", "42"},
  334. url: "/articles/technology/42",
  335. },
  336. {
  337. route: new(Route).Host("{subdomain}.domain.com").Path("/articles/{category}/{id:[0-9]+}"),
  338. vars: []string{"subdomain", "foo", "category", "technology", "id", "42"},
  339. url: "http://foo.domain.com/articles/technology/42",
  340. },
  341. }
  342. func TestHeaderMatcher(t *testing.T) {
  343. for _, v := range headerMatcherTests {
  344. request, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
  345. for key, value := range v.headers {
  346. request.Header.Add(key, value)
  347. }
  348. var routeMatch RouteMatch
  349. result := v.matcher.Match(request, &routeMatch)
  350. if result != v.result {
  351. if v.result {
  352. t.Errorf("%#v: should match %v.", v.matcher, request.Header)
  353. } else {
  354. t.Errorf("%#v: should not match %v.", v.matcher, request.Header)
  355. }
  356. }
  357. }
  358. }
  359. func TestHostMatcher(t *testing.T) {
  360. for _, v := range hostMatcherTests {
  361. request, _ := http.NewRequest("GET", v.url, nil)
  362. var routeMatch RouteMatch
  363. result := v.matcher.Match(request, &routeMatch)
  364. vars := routeMatch.Vars
  365. if result != v.result {
  366. if v.result {
  367. t.Errorf("%#v: should match %v.", v.matcher, v.url)
  368. } else {
  369. t.Errorf("%#v: should not match %v.", v.matcher, v.url)
  370. }
  371. }
  372. if result {
  373. if len(vars) != len(v.vars) {
  374. t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars))
  375. }
  376. for name, value := range vars {
  377. if v.vars[name] != value {
  378. t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value)
  379. }
  380. }
  381. } else {
  382. if len(vars) != 0 {
  383. t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars))
  384. }
  385. }
  386. }
  387. }
  388. func TestMethodMatcher(t *testing.T) {
  389. for _, v := range methodMatcherTests {
  390. request, _ := http.NewRequest(v.method, "http://localhost:8080/", nil)
  391. var routeMatch RouteMatch
  392. result := v.matcher.Match(request, &routeMatch)
  393. if result != v.result {
  394. if v.result {
  395. t.Errorf("%#v: should match %v.", v.matcher, v.method)
  396. } else {
  397. t.Errorf("%#v: should not match %v.", v.matcher, v.method)
  398. }
  399. }
  400. }
  401. }
  402. func TestPathMatcher(t *testing.T) {
  403. for _, v := range pathMatcherTests {
  404. request, _ := http.NewRequest("GET", v.url, nil)
  405. var routeMatch RouteMatch
  406. result := v.matcher.Match(request, &routeMatch)
  407. vars := routeMatch.Vars
  408. if result != v.result {
  409. if v.result {
  410. t.Errorf("%#v: should match %v.", v.matcher, v.url)
  411. } else {
  412. t.Errorf("%#v: should not match %v.", v.matcher, v.url)
  413. }
  414. }
  415. if result {
  416. if len(vars) != len(v.vars) {
  417. t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars))
  418. }
  419. for name, value := range vars {
  420. if v.vars[name] != value {
  421. t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value)
  422. }
  423. }
  424. } else {
  425. if len(vars) != 0 {
  426. t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars))
  427. }
  428. }
  429. }
  430. }
  431. func TestSchemeMatcher(t *testing.T) {
  432. for _, v := range schemeMatcherTests {
  433. request, _ := http.NewRequest("GET", v.url, nil)
  434. var routeMatch RouteMatch
  435. result := v.matcher.Match(request, &routeMatch)
  436. if result != v.result {
  437. if v.result {
  438. t.Errorf("%#v: should match %v.", v.matcher, v.url)
  439. } else {
  440. t.Errorf("%#v: should not match %v.", v.matcher, v.url)
  441. }
  442. }
  443. }
  444. }
  445. func TestUrlBuilding(t *testing.T) {
  446. for _, v := range urlBuildingTests {
  447. u, _ := v.route.URL(v.vars...)
  448. url := u.String()
  449. if url != v.url {
  450. t.Errorf("expected %v, got %v", v.url, url)
  451. /*
  452. reversePath := ""
  453. reverseHost := ""
  454. if v.route.pathTemplate != nil {
  455. reversePath = v.route.pathTemplate.Reverse
  456. }
  457. if v.route.hostTemplate != nil {
  458. reverseHost = v.route.hostTemplate.Reverse
  459. }
  460. t.Errorf("%#v:\nexpected: %q\ngot: %q\nreverse path: %q\nreverse host: %q", v.route, v.url, url, reversePath, reverseHost)
  461. */
  462. }
  463. }
  464. ArticleHandler := func(w http.ResponseWriter, r *http.Request) {
  465. }
  466. router := NewRouter()
  467. router.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).Name("article")
  468. url, _ := router.Get("article").URL("category", "technology", "id", "42")
  469. expected := "/articles/technology/42"
  470. if url.String() != expected {
  471. t.Errorf("Expected %v, got %v", expected, url.String())
  472. }
  473. }
  474. func TestMatchedRouteName(t *testing.T) {
  475. routeName := "stock"
  476. router := NewRouter()
  477. route := router.NewRoute().Path("/products/").Name(routeName)
  478. url := "http://www.example.com/products/"
  479. request, _ := http.NewRequest("GET", url, nil)
  480. var rv RouteMatch
  481. ok := router.Match(request, &rv)
  482. if !ok || rv.Route != route {
  483. t.Errorf("Expected same route, got %+v.", rv.Route)
  484. }
  485. retName := rv.Route.GetName()
  486. if retName != routeName {
  487. t.Errorf("Expected %q, got %q.", routeName, retName)
  488. }
  489. }
  490. func TestSubRouting(t *testing.T) {
  491. // Example from docs.
  492. router := NewRouter()
  493. subrouter := router.NewRoute().Host("www.example.com").Subrouter()
  494. route := subrouter.NewRoute().Path("/products/").Name("products")
  495. url := "http://www.example.com/products/"
  496. request, _ := http.NewRequest("GET", url, nil)
  497. var rv RouteMatch
  498. ok := router.Match(request, &rv)
  499. if !ok || rv.Route != route {
  500. t.Errorf("Expected same route, got %+v.", rv.Route)
  501. }
  502. u, _ := router.Get("products").URL()
  503. builtURL := u.String()
  504. // Yay, subroute aware of the domain when building!
  505. if builtURL != url {
  506. t.Errorf("Expected %q, got %q.", url, builtURL)
  507. }
  508. }
  509. func TestVariableNames(t *testing.T) {
  510. route := new(Route).Host("{arg1}.domain.com").Path("/{arg1}/{arg2:[0-9]+}")
  511. if route.err == nil {
  512. t.Errorf("Expected error for duplicated variable names")
  513. }
  514. }
  515. func TestRedirectSlash(t *testing.T) {
  516. var route *Route
  517. var routeMatch RouteMatch
  518. r := NewRouter()
  519. r.StrictSlash(false)
  520. route = r.NewRoute()
  521. if route.strictSlash != false {
  522. t.Errorf("Expected false redirectSlash.")
  523. }
  524. r.StrictSlash(true)
  525. route = r.NewRoute()
  526. if route.strictSlash != true {
  527. t.Errorf("Expected true redirectSlash.")
  528. }
  529. route = new(Route)
  530. route.strictSlash = true
  531. route.Path("/{arg1}/{arg2:[0-9]+}/")
  532. request, _ := http.NewRequest("GET", "http://localhost/foo/123", nil)
  533. routeMatch = RouteMatch{}
  534. _ = route.Match(request, &routeMatch)
  535. vars := routeMatch.Vars
  536. if vars["arg1"] != "foo" {
  537. t.Errorf("Expected foo.")
  538. }
  539. if vars["arg2"] != "123" {
  540. t.Errorf("Expected 123.")
  541. }
  542. rsp := NewRecorder()
  543. routeMatch.Handler.ServeHTTP(rsp, request)
  544. if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123/" {
  545. t.Errorf("Expected redirect header.")
  546. }
  547. route = new(Route)
  548. route.strictSlash = true
  549. route.Path("/{arg1}/{arg2:[0-9]+}")
  550. request, _ = http.NewRequest("GET", "http://localhost/foo/123/", nil)
  551. routeMatch = RouteMatch{}
  552. _ = route.Match(request, &routeMatch)
  553. vars = routeMatch.Vars
  554. if vars["arg1"] != "foo" {
  555. t.Errorf("Expected foo.")
  556. }
  557. if vars["arg2"] != "123" {
  558. t.Errorf("Expected 123.")
  559. }
  560. rsp = NewRecorder()
  561. routeMatch.Handler.ServeHTTP(rsp, request)
  562. if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123" {
  563. t.Errorf("Expected redirect header.")
  564. }
  565. }
  566. // Test for the new regexp library, still not available in stable Go.
  567. func TestNewRegexp(t *testing.T) {
  568. var p *routeRegexp
  569. var matches []string
  570. tests := map[string]map[string][]string{
  571. "/{foo:a{2}}": {
  572. "/a": nil,
  573. "/aa": {"aa"},
  574. "/aaa": nil,
  575. "/aaaa": nil,
  576. },
  577. "/{foo:a{2,}}": {
  578. "/a": nil,
  579. "/aa": {"aa"},
  580. "/aaa": {"aaa"},
  581. "/aaaa": {"aaaa"},
  582. },
  583. "/{foo:a{2,3}}": {
  584. "/a": nil,
  585. "/aa": {"aa"},
  586. "/aaa": {"aaa"},
  587. "/aaaa": nil,
  588. },
  589. "/{foo:[a-z]{3}}/{bar:[a-z]{2}}": {
  590. "/a": nil,
  591. "/ab": nil,
  592. "/abc": nil,
  593. "/abcd": nil,
  594. "/abc/ab": {"abc", "ab"},
  595. "/abc/abc": nil,
  596. "/abcd/ab": nil,
  597. },
  598. `/{foo:\w{3,}}/{bar:\d{2,}}`: {
  599. "/a": nil,
  600. "/ab": nil,
  601. "/abc": nil,
  602. "/abc/1": nil,
  603. "/abc/12": {"abc", "12"},
  604. "/abcd/12": {"abcd", "12"},
  605. "/abcd/123": {"abcd", "123"},
  606. },
  607. }
  608. for pattern, paths := range tests {
  609. p, _ = newRouteRegexp(pattern, regexpTypePath, routeRegexpOptions{})
  610. for path, result := range paths {
  611. matches = p.regexp.FindStringSubmatch(path)
  612. if result == nil {
  613. if matches != nil {
  614. t.Errorf("%v should not match %v.", pattern, path)
  615. }
  616. } else {
  617. if len(matches) != len(result)+1 {
  618. t.Errorf("Expected %v matches, got %v.", len(result)+1, len(matches))
  619. } else {
  620. for k, v := range result {
  621. if matches[k+1] != v {
  622. t.Errorf("Expected %v, got %v.", v, matches[k+1])
  623. }
  624. }
  625. }
  626. }
  627. }
  628. }
  629. }