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.
 
 
 

711 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. // Need better messages. :)
  108. if matched {
  109. t.Errorf("Should match.")
  110. } else {
  111. t.Errorf("Should not match.")
  112. }
  113. }
  114. if matched {
  115. currentRoute := routeMatch.Route
  116. if currentRoute == nil {
  117. t.Errorf("Expected a current route.")
  118. }
  119. vars := routeMatch.Vars
  120. expectedVars := resultVars[shouldMatch]
  121. if len(vars) != len(expectedVars) {
  122. t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars)
  123. }
  124. for name, value := range vars {
  125. if expectedVars[name] != value {
  126. t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars)
  127. }
  128. }
  129. }
  130. }
  131. // 1st route --------------------------------------------------------------
  132. // Everything match.
  133. reset()
  134. match(true)
  135. // Scheme doesn't match.
  136. reset()
  137. scheme = "http"
  138. match(false)
  139. // Host doesn't match.
  140. reset()
  141. host = "www.mygoogle.com"
  142. match(false)
  143. // Path doesn't match.
  144. reset()
  145. path = "/product/notdigits"
  146. match(false)
  147. // Query doesn't match.
  148. reset()
  149. query = "?foo=baz"
  150. match(false)
  151. // Method doesn't match.
  152. reset()
  153. method = "POST"
  154. match(false)
  155. // Header doesn't match.
  156. reset()
  157. headers = map[string]string{}
  158. match(false)
  159. // Everything match, again.
  160. reset()
  161. match(true)
  162. // 2nd route --------------------------------------------------------------
  163. // Everything match.
  164. reset2()
  165. match(true)
  166. // Scheme doesn't match.
  167. reset2()
  168. scheme = "https"
  169. match(false)
  170. // Host doesn't match.
  171. reset2()
  172. host = "sub.google.com"
  173. match(false)
  174. // Path doesn't match.
  175. reset2()
  176. path = "/bar/product/42"
  177. match(false)
  178. // Query doesn't match.
  179. reset2()
  180. query = "?foo=baz"
  181. match(false)
  182. // Method doesn't match.
  183. reset2()
  184. method = "GET"
  185. match(false)
  186. // Header doesn't match.
  187. reset2()
  188. headers = map[string]string{}
  189. match(false)
  190. // Everything match, again.
  191. reset2()
  192. match(true)
  193. }
  194. type headerMatcherTest struct {
  195. matcher headerMatcher
  196. headers map[string]string
  197. result bool
  198. }
  199. var headerMatcherTests = []headerMatcherTest{
  200. {
  201. matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}),
  202. headers: map[string]string{"X-Requested-With": "XMLHttpRequest"},
  203. result: true,
  204. },
  205. {
  206. matcher: headerMatcher(map[string]string{"x-requested-with": ""}),
  207. headers: map[string]string{"X-Requested-With": "anything"},
  208. result: true,
  209. },
  210. {
  211. matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}),
  212. headers: map[string]string{},
  213. result: false,
  214. },
  215. }
  216. type hostMatcherTest struct {
  217. matcher *Route
  218. url string
  219. vars map[string]string
  220. result bool
  221. }
  222. var hostMatcherTests = []hostMatcherTest{
  223. {
  224. 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]}"),
  225. url: "http://abc.def.ghi/",
  226. vars: map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"},
  227. result: true,
  228. },
  229. {
  230. 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]}"),
  231. url: "http://a.b.c/",
  232. vars: map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"},
  233. result: false,
  234. },
  235. }
  236. type methodMatcherTest struct {
  237. matcher methodMatcher
  238. method string
  239. result bool
  240. }
  241. var methodMatcherTests = []methodMatcherTest{
  242. {
  243. matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
  244. method: "GET",
  245. result: true,
  246. },
  247. {
  248. matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
  249. method: "POST",
  250. result: true,
  251. },
  252. {
  253. matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
  254. method: "PUT",
  255. result: true,
  256. },
  257. {
  258. matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
  259. method: "DELETE",
  260. result: false,
  261. },
  262. }
  263. type pathMatcherTest struct {
  264. matcher *Route
  265. url string
  266. vars map[string]string
  267. result bool
  268. }
  269. var pathMatcherTests = []pathMatcherTest{
  270. {
  271. 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]}"),
  272. url: "http://localhost:8080/123/456/789",
  273. vars: map[string]string{"foo": "123", "bar": "456", "baz": "789"},
  274. result: true,
  275. },
  276. {
  277. 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]}"),
  278. url: "http://localhost:8080/1/2/3",
  279. vars: map[string]string{"foo": "123", "bar": "456", "baz": "789"},
  280. result: false,
  281. },
  282. }
  283. type schemeMatcherTest struct {
  284. matcher schemeMatcher
  285. url string
  286. result bool
  287. }
  288. var schemeMatcherTests = []schemeMatcherTest{
  289. {
  290. matcher: schemeMatcher([]string{"http", "https"}),
  291. url: "http://localhost:8080/",
  292. result: true,
  293. },
  294. {
  295. matcher: schemeMatcher([]string{"http", "https"}),
  296. url: "https://localhost:8080/",
  297. result: true,
  298. },
  299. {
  300. matcher: schemeMatcher([]string{"https"}),
  301. url: "http://localhost:8080/",
  302. result: false,
  303. },
  304. {
  305. matcher: schemeMatcher([]string{"http"}),
  306. url: "https://localhost:8080/",
  307. result: false,
  308. },
  309. }
  310. type urlBuildingTest struct {
  311. route *Route
  312. vars []string
  313. url string
  314. }
  315. var urlBuildingTests = []urlBuildingTest{
  316. {
  317. route: new(Route).Host("foo.domain.com"),
  318. vars: []string{},
  319. url: "http://foo.domain.com",
  320. },
  321. {
  322. route: new(Route).Host("{subdomain}.domain.com"),
  323. vars: []string{"subdomain", "bar"},
  324. url: "http://bar.domain.com",
  325. },
  326. {
  327. route: new(Route).Host("foo.domain.com").Path("/articles"),
  328. vars: []string{},
  329. url: "http://foo.domain.com/articles",
  330. },
  331. {
  332. route: new(Route).Path("/articles"),
  333. vars: []string{},
  334. url: "/articles",
  335. },
  336. {
  337. route: new(Route).Path("/articles/{category}/{id:[0-9]+}"),
  338. vars: []string{"category", "technology", "id", "42"},
  339. url: "/articles/technology/42",
  340. },
  341. {
  342. route: new(Route).Host("{subdomain}.domain.com").Path("/articles/{category}/{id:[0-9]+}"),
  343. vars: []string{"subdomain", "foo", "category", "technology", "id", "42"},
  344. url: "http://foo.domain.com/articles/technology/42",
  345. },
  346. }
  347. func TestHeaderMatcher(t *testing.T) {
  348. for _, v := range headerMatcherTests {
  349. request, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
  350. for key, value := range v.headers {
  351. request.Header.Add(key, value)
  352. }
  353. var routeMatch RouteMatch
  354. result := v.matcher.Match(request, &routeMatch)
  355. if result != v.result {
  356. if v.result {
  357. t.Errorf("%#v: should match %v.", v.matcher, request.Header)
  358. } else {
  359. t.Errorf("%#v: should not match %v.", v.matcher, request.Header)
  360. }
  361. }
  362. }
  363. }
  364. func TestHostMatcher(t *testing.T) {
  365. for _, v := range hostMatcherTests {
  366. request, _ := http.NewRequest("GET", v.url, nil)
  367. var routeMatch RouteMatch
  368. result := v.matcher.Match(request, &routeMatch)
  369. vars := routeMatch.Vars
  370. if result != v.result {
  371. if v.result {
  372. t.Errorf("%#v: should match %v.", v.matcher, v.url)
  373. } else {
  374. t.Errorf("%#v: should not match %v.", v.matcher, v.url)
  375. }
  376. }
  377. if result {
  378. if len(vars) != len(v.vars) {
  379. t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars))
  380. }
  381. for name, value := range vars {
  382. if v.vars[name] != value {
  383. t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value)
  384. }
  385. }
  386. } else {
  387. if len(vars) != 0 {
  388. t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars))
  389. }
  390. }
  391. }
  392. }
  393. func TestMethodMatcher(t *testing.T) {
  394. for _, v := range methodMatcherTests {
  395. request, _ := http.NewRequest(v.method, "http://localhost:8080/", nil)
  396. var routeMatch RouteMatch
  397. result := v.matcher.Match(request, &routeMatch)
  398. if result != v.result {
  399. if v.result {
  400. t.Errorf("%#v: should match %v.", v.matcher, v.method)
  401. } else {
  402. t.Errorf("%#v: should not match %v.", v.matcher, v.method)
  403. }
  404. }
  405. }
  406. }
  407. func TestPathMatcher(t *testing.T) {
  408. for _, v := range pathMatcherTests {
  409. request, _ := http.NewRequest("GET", v.url, nil)
  410. var routeMatch RouteMatch
  411. result := v.matcher.Match(request, &routeMatch)
  412. vars := routeMatch.Vars
  413. if result != v.result {
  414. if v.result {
  415. t.Errorf("%#v: should match %v.", v.matcher, v.url)
  416. } else {
  417. t.Errorf("%#v: should not match %v.", v.matcher, v.url)
  418. }
  419. }
  420. if result {
  421. if len(vars) != len(v.vars) {
  422. t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars))
  423. }
  424. for name, value := range vars {
  425. if v.vars[name] != value {
  426. t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value)
  427. }
  428. }
  429. } else {
  430. if len(vars) != 0 {
  431. t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars))
  432. }
  433. }
  434. }
  435. }
  436. func TestSchemeMatcher(t *testing.T) {
  437. for _, v := range schemeMatcherTests {
  438. request, _ := http.NewRequest("GET", v.url, nil)
  439. var routeMatch RouteMatch
  440. result := v.matcher.Match(request, &routeMatch)
  441. if result != v.result {
  442. if v.result {
  443. t.Errorf("%#v: should match %v.", v.matcher, v.url)
  444. } else {
  445. t.Errorf("%#v: should not match %v.", v.matcher, v.url)
  446. }
  447. }
  448. }
  449. }
  450. func TestUrlBuilding(t *testing.T) {
  451. for _, v := range urlBuildingTests {
  452. u, _ := v.route.URL(v.vars...)
  453. url := u.String()
  454. if url != v.url {
  455. t.Errorf("expected %v, got %v", v.url, url)
  456. /*
  457. reversePath := ""
  458. reverseHost := ""
  459. if v.route.pathTemplate != nil {
  460. reversePath = v.route.pathTemplate.Reverse
  461. }
  462. if v.route.hostTemplate != nil {
  463. reverseHost = v.route.hostTemplate.Reverse
  464. }
  465. t.Errorf("%#v:\nexpected: %q\ngot: %q\nreverse path: %q\nreverse host: %q", v.route, v.url, url, reversePath, reverseHost)
  466. */
  467. }
  468. }
  469. ArticleHandler := func(w http.ResponseWriter, r *http.Request) {
  470. }
  471. router := NewRouter()
  472. router.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).Name("article")
  473. url, _ := router.Get("article").URL("category", "technology", "id", "42")
  474. expected := "/articles/technology/42"
  475. if url.String() != expected {
  476. t.Errorf("Expected %v, got %v", expected, url.String())
  477. }
  478. }
  479. func TestMatchedRouteName(t *testing.T) {
  480. routeName := "stock"
  481. router := NewRouter()
  482. route := router.NewRoute().Path("/products/").Name(routeName)
  483. url := "http://www.example.com/products/"
  484. request, _ := http.NewRequest("GET", url, nil)
  485. var rv RouteMatch
  486. ok := router.Match(request, &rv)
  487. if !ok || rv.Route != route {
  488. t.Errorf("Expected same route, got %+v.", rv.Route)
  489. }
  490. retName := rv.Route.GetName()
  491. if retName != routeName {
  492. t.Errorf("Expected %q, got %q.", routeName, retName)
  493. }
  494. }
  495. func TestSubRouting(t *testing.T) {
  496. // Example from docs.
  497. router := NewRouter()
  498. subrouter := router.NewRoute().Host("www.example.com").Subrouter()
  499. route := subrouter.NewRoute().Path("/products/").Name("products")
  500. url := "http://www.example.com/products/"
  501. request, _ := http.NewRequest("GET", url, nil)
  502. var rv RouteMatch
  503. ok := router.Match(request, &rv)
  504. if !ok || rv.Route != route {
  505. t.Errorf("Expected same route, got %+v.", rv.Route)
  506. }
  507. u, _ := router.Get("products").URL()
  508. builtURL := u.String()
  509. // Yay, subroute aware of the domain when building!
  510. if builtURL != url {
  511. t.Errorf("Expected %q, got %q.", url, builtURL)
  512. }
  513. }
  514. func TestVariableNames(t *testing.T) {
  515. route := new(Route).Host("{arg1}.domain.com").Path("/{arg1}/{arg2:[0-9]+}")
  516. if route.err == nil {
  517. t.Errorf("Expected error for duplicated variable names")
  518. }
  519. }
  520. func TestRedirectSlash(t *testing.T) {
  521. var route *Route
  522. var routeMatch RouteMatch
  523. r := NewRouter()
  524. r.StrictSlash(false)
  525. route = r.NewRoute()
  526. if route.strictSlash != false {
  527. t.Errorf("Expected false redirectSlash.")
  528. }
  529. r.StrictSlash(true)
  530. route = r.NewRoute()
  531. if route.strictSlash != true {
  532. t.Errorf("Expected true redirectSlash.")
  533. }
  534. route = new(Route)
  535. route.strictSlash = true
  536. route.Path("/{arg1}/{arg2:[0-9]+}/")
  537. request, _ := http.NewRequest("GET", "http://localhost/foo/123", nil)
  538. routeMatch = RouteMatch{}
  539. _ = route.Match(request, &routeMatch)
  540. vars := routeMatch.Vars
  541. if vars["arg1"] != "foo" {
  542. t.Errorf("Expected foo.")
  543. }
  544. if vars["arg2"] != "123" {
  545. t.Errorf("Expected 123.")
  546. }
  547. rsp := NewRecorder()
  548. routeMatch.Handler.ServeHTTP(rsp, request)
  549. if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123/" {
  550. t.Errorf("Expected redirect header.")
  551. }
  552. route = new(Route)
  553. route.strictSlash = true
  554. route.Path("/{arg1}/{arg2:[0-9]+}")
  555. request, _ = http.NewRequest("GET", "http://localhost/foo/123/", nil)
  556. routeMatch = RouteMatch{}
  557. _ = route.Match(request, &routeMatch)
  558. vars = routeMatch.Vars
  559. if vars["arg1"] != "foo" {
  560. t.Errorf("Expected foo.")
  561. }
  562. if vars["arg2"] != "123" {
  563. t.Errorf("Expected 123.")
  564. }
  565. rsp = NewRecorder()
  566. routeMatch.Handler.ServeHTTP(rsp, request)
  567. if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123" {
  568. t.Errorf("Expected redirect header.")
  569. }
  570. }
  571. // Test for the new regexp library, still not available in stable Go.
  572. func TestNewRegexp(t *testing.T) {
  573. var p *routeRegexp
  574. var matches []string
  575. tests := map[string]map[string][]string{
  576. "/{foo:a{2}}": {
  577. "/a": nil,
  578. "/aa": {"aa"},
  579. "/aaa": nil,
  580. "/aaaa": nil,
  581. },
  582. "/{foo:a{2,}}": {
  583. "/a": nil,
  584. "/aa": {"aa"},
  585. "/aaa": {"aaa"},
  586. "/aaaa": {"aaaa"},
  587. },
  588. "/{foo:a{2,3}}": {
  589. "/a": nil,
  590. "/aa": {"aa"},
  591. "/aaa": {"aaa"},
  592. "/aaaa": nil,
  593. },
  594. "/{foo:[a-z]{3}}/{bar:[a-z]{2}}": {
  595. "/a": nil,
  596. "/ab": nil,
  597. "/abc": nil,
  598. "/abcd": nil,
  599. "/abc/ab": {"abc", "ab"},
  600. "/abc/abc": nil,
  601. "/abcd/ab": nil,
  602. },
  603. `/{foo:\w{3,}}/{bar:\d{2,}}`: {
  604. "/a": nil,
  605. "/ab": nil,
  606. "/abc": nil,
  607. "/abc/1": nil,
  608. "/abc/12": {"abc", "12"},
  609. "/abcd/12": {"abcd", "12"},
  610. "/abcd/123": {"abcd", "123"},
  611. },
  612. }
  613. for pattern, paths := range tests {
  614. p, _ = newRouteRegexp(pattern, false, false, false, false, false)
  615. for path, result := range paths {
  616. matches = p.regexp.FindStringSubmatch(path)
  617. if result == nil {
  618. if matches != nil {
  619. t.Errorf("%v should not match %v.", pattern, path)
  620. }
  621. } else {
  622. if len(matches) != len(result)+1 {
  623. t.Errorf("Expected %v matches, got %v.", len(result)+1, len(matches))
  624. } else {
  625. for k, v := range result {
  626. if matches[k+1] != v {
  627. t.Errorf("Expected %v, got %v.", v, matches[k+1])
  628. }
  629. }
  630. }
  631. }
  632. }
  633. }
  634. }