/Godeps/_workspace/src/github.com/fsouza/go-dockerclient/external/github.com/gorilla/mux/mux_test.go

https://gitlab.com/cowboygneox/gitlab-ci-multi-runner · Go · 1358 lines · 1263 code · 79 blank · 16 comment · 97 complexity · 52570a62c05bf7d76d0b0d4d6109ba28 MD5 · raw file

  1. // Copyright 2012 The Gorilla Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package mux
  5. import (
  6. "fmt"
  7. "net/http"
  8. "strings"
  9. "testing"
  10. "github.com/fsouza/go-dockerclient/external/github.com/gorilla/context"
  11. )
  12. func (r *Route) GoString() string {
  13. matchers := make([]string, len(r.matchers))
  14. for i, m := range r.matchers {
  15. matchers[i] = fmt.Sprintf("%#v", m)
  16. }
  17. return fmt.Sprintf("&Route{matchers:[]matcher{%s}}", strings.Join(matchers, ", "))
  18. }
  19. func (r *routeRegexp) GoString() string {
  20. return fmt.Sprintf("&routeRegexp{template: %q, matchHost: %t, matchQuery: %t, strictSlash: %t, regexp: regexp.MustCompile(%q), reverse: %q, varsN: %v, varsR: %v", r.template, r.matchHost, r.matchQuery, r.strictSlash, r.regexp.String(), r.reverse, r.varsN, r.varsR)
  21. }
  22. type routeTest struct {
  23. title string // title of the test
  24. route *Route // the route being tested
  25. request *http.Request // a request to test the route
  26. vars map[string]string // the expected vars of the match
  27. host string // the expected host of the match
  28. path string // the expected path of the match
  29. shouldMatch bool // whether the request is expected to match the route at all
  30. shouldRedirect bool // whether the request should result in a redirect
  31. }
  32. func TestHost(t *testing.T) {
  33. // newRequestHost a new request with a method, url, and host header
  34. newRequestHost := func(method, url, host string) *http.Request {
  35. req, err := http.NewRequest(method, url, nil)
  36. if err != nil {
  37. panic(err)
  38. }
  39. req.Host = host
  40. return req
  41. }
  42. tests := []routeTest{
  43. {
  44. title: "Host route match",
  45. route: new(Route).Host("aaa.bbb.ccc"),
  46. request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
  47. vars: map[string]string{},
  48. host: "aaa.bbb.ccc",
  49. path: "",
  50. shouldMatch: true,
  51. },
  52. {
  53. title: "Host route, wrong host in request URL",
  54. route: new(Route).Host("aaa.bbb.ccc"),
  55. request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
  56. vars: map[string]string{},
  57. host: "aaa.bbb.ccc",
  58. path: "",
  59. shouldMatch: false,
  60. },
  61. {
  62. title: "Host route with port, match",
  63. route: new(Route).Host("aaa.bbb.ccc:1234"),
  64. request: newRequest("GET", "http://aaa.bbb.ccc:1234/111/222/333"),
  65. vars: map[string]string{},
  66. host: "aaa.bbb.ccc:1234",
  67. path: "",
  68. shouldMatch: true,
  69. },
  70. {
  71. title: "Host route with port, wrong port in request URL",
  72. route: new(Route).Host("aaa.bbb.ccc:1234"),
  73. request: newRequest("GET", "http://aaa.bbb.ccc:9999/111/222/333"),
  74. vars: map[string]string{},
  75. host: "aaa.bbb.ccc:1234",
  76. path: "",
  77. shouldMatch: false,
  78. },
  79. {
  80. title: "Host route, match with host in request header",
  81. route: new(Route).Host("aaa.bbb.ccc"),
  82. request: newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc"),
  83. vars: map[string]string{},
  84. host: "aaa.bbb.ccc",
  85. path: "",
  86. shouldMatch: true,
  87. },
  88. {
  89. title: "Host route, wrong host in request header",
  90. route: new(Route).Host("aaa.bbb.ccc"),
  91. request: newRequestHost("GET", "/111/222/333", "aaa.222.ccc"),
  92. vars: map[string]string{},
  93. host: "aaa.bbb.ccc",
  94. path: "",
  95. shouldMatch: false,
  96. },
  97. // BUG {new(Route).Host("aaa.bbb.ccc:1234"), newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc:1234"), map[string]string{}, "aaa.bbb.ccc:1234", "", true},
  98. {
  99. title: "Host route with port, wrong host in request header",
  100. route: new(Route).Host("aaa.bbb.ccc:1234"),
  101. request: newRequestHost("GET", "/111/222/333", "aaa.bbb.ccc:9999"),
  102. vars: map[string]string{},
  103. host: "aaa.bbb.ccc:1234",
  104. path: "",
  105. shouldMatch: false,
  106. },
  107. {
  108. title: "Host route with pattern, match",
  109. route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc"),
  110. request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
  111. vars: map[string]string{"v1": "bbb"},
  112. host: "aaa.bbb.ccc",
  113. path: "",
  114. shouldMatch: true,
  115. },
  116. {
  117. title: "Host route with pattern, additional capturing group, match",
  118. route: new(Route).Host("aaa.{v1:[a-z]{2}(b|c)}.ccc"),
  119. request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
  120. vars: map[string]string{"v1": "bbb"},
  121. host: "aaa.bbb.ccc",
  122. path: "",
  123. shouldMatch: true,
  124. },
  125. {
  126. title: "Host route with pattern, wrong host in request URL",
  127. route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc"),
  128. request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
  129. vars: map[string]string{"v1": "bbb"},
  130. host: "aaa.bbb.ccc",
  131. path: "",
  132. shouldMatch: false,
  133. },
  134. {
  135. title: "Host route with multiple patterns, match",
  136. route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}"),
  137. request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
  138. vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc"},
  139. host: "aaa.bbb.ccc",
  140. path: "",
  141. shouldMatch: true,
  142. },
  143. {
  144. title: "Host route with multiple patterns, wrong host in request URL",
  145. route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}"),
  146. request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
  147. vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc"},
  148. host: "aaa.bbb.ccc",
  149. path: "",
  150. shouldMatch: false,
  151. },
  152. {
  153. title: "Host route with hyphenated name and pattern, match",
  154. route: new(Route).Host("aaa.{v-1:[a-z]{3}}.ccc"),
  155. request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
  156. vars: map[string]string{"v-1": "bbb"},
  157. host: "aaa.bbb.ccc",
  158. path: "",
  159. shouldMatch: true,
  160. },
  161. {
  162. title: "Host route with hyphenated name and pattern, additional capturing group, match",
  163. route: new(Route).Host("aaa.{v-1:[a-z]{2}(b|c)}.ccc"),
  164. request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
  165. vars: map[string]string{"v-1": "bbb"},
  166. host: "aaa.bbb.ccc",
  167. path: "",
  168. shouldMatch: true,
  169. },
  170. {
  171. title: "Host route with multiple hyphenated names and patterns, match",
  172. route: new(Route).Host("{v-1:[a-z]{3}}.{v-2:[a-z]{3}}.{v-3:[a-z]{3}}"),
  173. request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
  174. vars: map[string]string{"v-1": "aaa", "v-2": "bbb", "v-3": "ccc"},
  175. host: "aaa.bbb.ccc",
  176. path: "",
  177. shouldMatch: true,
  178. },
  179. {
  180. title: "Path route with single pattern with pipe, match",
  181. route: new(Route).Path("/{category:a|b/c}"),
  182. request: newRequest("GET", "http://localhost/a"),
  183. vars: map[string]string{"category": "a"},
  184. host: "",
  185. path: "/a",
  186. shouldMatch: true,
  187. },
  188. {
  189. title: "Path route with single pattern with pipe, match",
  190. route: new(Route).Path("/{category:a|b/c}"),
  191. request: newRequest("GET", "http://localhost/b/c"),
  192. vars: map[string]string{"category": "b/c"},
  193. host: "",
  194. path: "/b/c",
  195. shouldMatch: true,
  196. },
  197. {
  198. title: "Path route with multiple patterns with pipe, match",
  199. route: new(Route).Path("/{category:a|b/c}/{product}/{id:[0-9]+}"),
  200. request: newRequest("GET", "http://localhost/a/product_name/1"),
  201. vars: map[string]string{"category": "a", "product": "product_name", "id": "1"},
  202. host: "",
  203. path: "/a/product_name/1",
  204. shouldMatch: true,
  205. },
  206. {
  207. title: "Path route with multiple patterns with pipe, match",
  208. route: new(Route).Path("/{category:a|b/c}/{product}/{id:[0-9]+}"),
  209. request: newRequest("GET", "http://localhost/b/c/product_name/1"),
  210. vars: map[string]string{"category": "b/c", "product": "product_name", "id": "1"},
  211. host: "",
  212. path: "/b/c/product_name/1",
  213. shouldMatch: true,
  214. },
  215. }
  216. for _, test := range tests {
  217. testRoute(t, test)
  218. }
  219. }
  220. func TestPath(t *testing.T) {
  221. tests := []routeTest{
  222. {
  223. title: "Path route, match",
  224. route: new(Route).Path("/111/222/333"),
  225. request: newRequest("GET", "http://localhost/111/222/333"),
  226. vars: map[string]string{},
  227. host: "",
  228. path: "/111/222/333",
  229. shouldMatch: true,
  230. },
  231. {
  232. title: "Path route, match with trailing slash in request and path",
  233. route: new(Route).Path("/111/"),
  234. request: newRequest("GET", "http://localhost/111/"),
  235. vars: map[string]string{},
  236. host: "",
  237. path: "/111/",
  238. shouldMatch: true,
  239. },
  240. {
  241. title: "Path route, do not match with trailing slash in path",
  242. route: new(Route).Path("/111/"),
  243. request: newRequest("GET", "http://localhost/111"),
  244. vars: map[string]string{},
  245. host: "",
  246. path: "/111",
  247. shouldMatch: false,
  248. },
  249. {
  250. title: "Path route, do not match with trailing slash in request",
  251. route: new(Route).Path("/111"),
  252. request: newRequest("GET", "http://localhost/111/"),
  253. vars: map[string]string{},
  254. host: "",
  255. path: "/111/",
  256. shouldMatch: false,
  257. },
  258. {
  259. title: "Path route, wrong path in request in request URL",
  260. route: new(Route).Path("/111/222/333"),
  261. request: newRequest("GET", "http://localhost/1/2/3"),
  262. vars: map[string]string{},
  263. host: "",
  264. path: "/111/222/333",
  265. shouldMatch: false,
  266. },
  267. {
  268. title: "Path route with pattern, match",
  269. route: new(Route).Path("/111/{v1:[0-9]{3}}/333"),
  270. request: newRequest("GET", "http://localhost/111/222/333"),
  271. vars: map[string]string{"v1": "222"},
  272. host: "",
  273. path: "/111/222/333",
  274. shouldMatch: true,
  275. },
  276. {
  277. title: "Path route with pattern, URL in request does not match",
  278. route: new(Route).Path("/111/{v1:[0-9]{3}}/333"),
  279. request: newRequest("GET", "http://localhost/111/aaa/333"),
  280. vars: map[string]string{"v1": "222"},
  281. host: "",
  282. path: "/111/222/333",
  283. shouldMatch: false,
  284. },
  285. {
  286. title: "Path route with multiple patterns, match",
  287. route: new(Route).Path("/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}"),
  288. request: newRequest("GET", "http://localhost/111/222/333"),
  289. vars: map[string]string{"v1": "111", "v2": "222", "v3": "333"},
  290. host: "",
  291. path: "/111/222/333",
  292. shouldMatch: true,
  293. },
  294. {
  295. title: "Path route with multiple patterns, URL in request does not match",
  296. route: new(Route).Path("/{v1:[0-9]{3}}/{v2:[0-9]{3}}/{v3:[0-9]{3}}"),
  297. request: newRequest("GET", "http://localhost/111/aaa/333"),
  298. vars: map[string]string{"v1": "111", "v2": "222", "v3": "333"},
  299. host: "",
  300. path: "/111/222/333",
  301. shouldMatch: false,
  302. },
  303. {
  304. title: "Path route with multiple patterns with pipe, match",
  305. route: new(Route).Path("/{category:a|(b/c)}/{product}/{id:[0-9]+}"),
  306. request: newRequest("GET", "http://localhost/a/product_name/1"),
  307. vars: map[string]string{"category": "a", "product": "product_name", "id": "1"},
  308. host: "",
  309. path: "/a/product_name/1",
  310. shouldMatch: true,
  311. },
  312. {
  313. title: "Path route with hyphenated name and pattern, match",
  314. route: new(Route).Path("/111/{v-1:[0-9]{3}}/333"),
  315. request: newRequest("GET", "http://localhost/111/222/333"),
  316. vars: map[string]string{"v-1": "222"},
  317. host: "",
  318. path: "/111/222/333",
  319. shouldMatch: true,
  320. },
  321. {
  322. title: "Path route with multiple hyphenated names and patterns, match",
  323. route: new(Route).Path("/{v-1:[0-9]{3}}/{v-2:[0-9]{3}}/{v-3:[0-9]{3}}"),
  324. request: newRequest("GET", "http://localhost/111/222/333"),
  325. vars: map[string]string{"v-1": "111", "v-2": "222", "v-3": "333"},
  326. host: "",
  327. path: "/111/222/333",
  328. shouldMatch: true,
  329. },
  330. {
  331. title: "Path route with multiple hyphenated names and patterns with pipe, match",
  332. route: new(Route).Path("/{product-category:a|(b/c)}/{product-name}/{product-id:[0-9]+}"),
  333. request: newRequest("GET", "http://localhost/a/product_name/1"),
  334. vars: map[string]string{"product-category": "a", "product-name": "product_name", "product-id": "1"},
  335. host: "",
  336. path: "/a/product_name/1",
  337. shouldMatch: true,
  338. },
  339. }
  340. for _, test := range tests {
  341. testRoute(t, test)
  342. }
  343. }
  344. func TestPathPrefix(t *testing.T) {
  345. tests := []routeTest{
  346. {
  347. title: "PathPrefix route, match",
  348. route: new(Route).PathPrefix("/111"),
  349. request: newRequest("GET", "http://localhost/111/222/333"),
  350. vars: map[string]string{},
  351. host: "",
  352. path: "/111",
  353. shouldMatch: true,
  354. },
  355. {
  356. title: "PathPrefix route, match substring",
  357. route: new(Route).PathPrefix("/1"),
  358. request: newRequest("GET", "http://localhost/111/222/333"),
  359. vars: map[string]string{},
  360. host: "",
  361. path: "/1",
  362. shouldMatch: true,
  363. },
  364. {
  365. title: "PathPrefix route, URL prefix in request does not match",
  366. route: new(Route).PathPrefix("/111"),
  367. request: newRequest("GET", "http://localhost/1/2/3"),
  368. vars: map[string]string{},
  369. host: "",
  370. path: "/111",
  371. shouldMatch: false,
  372. },
  373. {
  374. title: "PathPrefix route with pattern, match",
  375. route: new(Route).PathPrefix("/111/{v1:[0-9]{3}}"),
  376. request: newRequest("GET", "http://localhost/111/222/333"),
  377. vars: map[string]string{"v1": "222"},
  378. host: "",
  379. path: "/111/222",
  380. shouldMatch: true,
  381. },
  382. {
  383. title: "PathPrefix route with pattern, URL prefix in request does not match",
  384. route: new(Route).PathPrefix("/111/{v1:[0-9]{3}}"),
  385. request: newRequest("GET", "http://localhost/111/aaa/333"),
  386. vars: map[string]string{"v1": "222"},
  387. host: "",
  388. path: "/111/222",
  389. shouldMatch: false,
  390. },
  391. {
  392. title: "PathPrefix route with multiple patterns, match",
  393. route: new(Route).PathPrefix("/{v1:[0-9]{3}}/{v2:[0-9]{3}}"),
  394. request: newRequest("GET", "http://localhost/111/222/333"),
  395. vars: map[string]string{"v1": "111", "v2": "222"},
  396. host: "",
  397. path: "/111/222",
  398. shouldMatch: true,
  399. },
  400. {
  401. title: "PathPrefix route with multiple patterns, URL prefix in request does not match",
  402. route: new(Route).PathPrefix("/{v1:[0-9]{3}}/{v2:[0-9]{3}}"),
  403. request: newRequest("GET", "http://localhost/111/aaa/333"),
  404. vars: map[string]string{"v1": "111", "v2": "222"},
  405. host: "",
  406. path: "/111/222",
  407. shouldMatch: false,
  408. },
  409. }
  410. for _, test := range tests {
  411. testRoute(t, test)
  412. }
  413. }
  414. func TestHostPath(t *testing.T) {
  415. tests := []routeTest{
  416. {
  417. title: "Host and Path route, match",
  418. route: new(Route).Host("aaa.bbb.ccc").Path("/111/222/333"),
  419. request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
  420. vars: map[string]string{},
  421. host: "",
  422. path: "",
  423. shouldMatch: true,
  424. },
  425. {
  426. title: "Host and Path route, wrong host in request URL",
  427. route: new(Route).Host("aaa.bbb.ccc").Path("/111/222/333"),
  428. request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
  429. vars: map[string]string{},
  430. host: "",
  431. path: "",
  432. shouldMatch: false,
  433. },
  434. {
  435. title: "Host and Path route with pattern, match",
  436. route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc").Path("/111/{v2:[0-9]{3}}/333"),
  437. request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
  438. vars: map[string]string{"v1": "bbb", "v2": "222"},
  439. host: "aaa.bbb.ccc",
  440. path: "/111/222/333",
  441. shouldMatch: true,
  442. },
  443. {
  444. title: "Host and Path route with pattern, URL in request does not match",
  445. route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc").Path("/111/{v2:[0-9]{3}}/333"),
  446. request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
  447. vars: map[string]string{"v1": "bbb", "v2": "222"},
  448. host: "aaa.bbb.ccc",
  449. path: "/111/222/333",
  450. shouldMatch: false,
  451. },
  452. {
  453. title: "Host and Path route with multiple patterns, match",
  454. route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}").Path("/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}"),
  455. request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
  456. vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc", "v4": "111", "v5": "222", "v6": "333"},
  457. host: "aaa.bbb.ccc",
  458. path: "/111/222/333",
  459. shouldMatch: true,
  460. },
  461. {
  462. title: "Host and Path route with multiple patterns, URL in request does not match",
  463. route: new(Route).Host("{v1:[a-z]{3}}.{v2:[a-z]{3}}.{v3:[a-z]{3}}").Path("/{v4:[0-9]{3}}/{v5:[0-9]{3}}/{v6:[0-9]{3}}"),
  464. request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
  465. vars: map[string]string{"v1": "aaa", "v2": "bbb", "v3": "ccc", "v4": "111", "v5": "222", "v6": "333"},
  466. host: "aaa.bbb.ccc",
  467. path: "/111/222/333",
  468. shouldMatch: false,
  469. },
  470. }
  471. for _, test := range tests {
  472. testRoute(t, test)
  473. }
  474. }
  475. func TestHeaders(t *testing.T) {
  476. // newRequestHeaders creates a new request with a method, url, and headers
  477. newRequestHeaders := func(method, url string, headers map[string]string) *http.Request {
  478. req, err := http.NewRequest(method, url, nil)
  479. if err != nil {
  480. panic(err)
  481. }
  482. for k, v := range headers {
  483. req.Header.Add(k, v)
  484. }
  485. return req
  486. }
  487. tests := []routeTest{
  488. {
  489. title: "Headers route, match",
  490. route: new(Route).Headers("foo", "bar", "baz", "ding"),
  491. request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar", "baz": "ding"}),
  492. vars: map[string]string{},
  493. host: "",
  494. path: "",
  495. shouldMatch: true,
  496. },
  497. {
  498. title: "Headers route, bad header values",
  499. route: new(Route).Headers("foo", "bar", "baz", "ding"),
  500. request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar", "baz": "dong"}),
  501. vars: map[string]string{},
  502. host: "",
  503. path: "",
  504. shouldMatch: false,
  505. },
  506. {
  507. title: "Headers route, regex header values to match",
  508. route: new(Route).Headers("foo", "ba[zr]"),
  509. request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar"}),
  510. vars: map[string]string{},
  511. host: "",
  512. path: "",
  513. shouldMatch: false,
  514. },
  515. {
  516. title: "Headers route, regex header values to match",
  517. route: new(Route).HeadersRegexp("foo", "ba[zr]"),
  518. request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "baz"}),
  519. vars: map[string]string{},
  520. host: "",
  521. path: "",
  522. shouldMatch: true,
  523. },
  524. }
  525. for _, test := range tests {
  526. testRoute(t, test)
  527. }
  528. }
  529. func TestMethods(t *testing.T) {
  530. tests := []routeTest{
  531. {
  532. title: "Methods route, match GET",
  533. route: new(Route).Methods("GET", "POST"),
  534. request: newRequest("GET", "http://localhost"),
  535. vars: map[string]string{},
  536. host: "",
  537. path: "",
  538. shouldMatch: true,
  539. },
  540. {
  541. title: "Methods route, match POST",
  542. route: new(Route).Methods("GET", "POST"),
  543. request: newRequest("POST", "http://localhost"),
  544. vars: map[string]string{},
  545. host: "",
  546. path: "",
  547. shouldMatch: true,
  548. },
  549. {
  550. title: "Methods route, bad method",
  551. route: new(Route).Methods("GET", "POST"),
  552. request: newRequest("PUT", "http://localhost"),
  553. vars: map[string]string{},
  554. host: "",
  555. path: "",
  556. shouldMatch: false,
  557. },
  558. }
  559. for _, test := range tests {
  560. testRoute(t, test)
  561. }
  562. }
  563. func TestQueries(t *testing.T) {
  564. tests := []routeTest{
  565. {
  566. title: "Queries route, match",
  567. route: new(Route).Queries("foo", "bar", "baz", "ding"),
  568. request: newRequest("GET", "http://localhost?foo=bar&baz=ding"),
  569. vars: map[string]string{},
  570. host: "",
  571. path: "",
  572. shouldMatch: true,
  573. },
  574. {
  575. title: "Queries route, match with a query string",
  576. route: new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"),
  577. request: newRequest("GET", "http://www.example.com/api?foo=bar&baz=ding"),
  578. vars: map[string]string{},
  579. host: "",
  580. path: "",
  581. shouldMatch: true,
  582. },
  583. {
  584. title: "Queries route, match with a query string out of order",
  585. route: new(Route).Host("www.example.com").Path("/api").Queries("foo", "bar", "baz", "ding"),
  586. request: newRequest("GET", "http://www.example.com/api?baz=ding&foo=bar"),
  587. vars: map[string]string{},
  588. host: "",
  589. path: "",
  590. shouldMatch: true,
  591. },
  592. {
  593. title: "Queries route, bad query",
  594. route: new(Route).Queries("foo", "bar", "baz", "ding"),
  595. request: newRequest("GET", "http://localhost?foo=bar&baz=dong"),
  596. vars: map[string]string{},
  597. host: "",
  598. path: "",
  599. shouldMatch: false,
  600. },
  601. {
  602. title: "Queries route with pattern, match",
  603. route: new(Route).Queries("foo", "{v1}"),
  604. request: newRequest("GET", "http://localhost?foo=bar"),
  605. vars: map[string]string{"v1": "bar"},
  606. host: "",
  607. path: "",
  608. shouldMatch: true,
  609. },
  610. {
  611. title: "Queries route with multiple patterns, match",
  612. route: new(Route).Queries("foo", "{v1}", "baz", "{v2}"),
  613. request: newRequest("GET", "http://localhost?foo=bar&baz=ding"),
  614. vars: map[string]string{"v1": "bar", "v2": "ding"},
  615. host: "",
  616. path: "",
  617. shouldMatch: true,
  618. },
  619. {
  620. title: "Queries route with regexp pattern, match",
  621. route: new(Route).Queries("foo", "{v1:[0-9]+}"),
  622. request: newRequest("GET", "http://localhost?foo=10"),
  623. vars: map[string]string{"v1": "10"},
  624. host: "",
  625. path: "",
  626. shouldMatch: true,
  627. },
  628. {
  629. title: "Queries route with regexp pattern, regexp does not match",
  630. route: new(Route).Queries("foo", "{v1:[0-9]+}"),
  631. request: newRequest("GET", "http://localhost?foo=a"),
  632. vars: map[string]string{},
  633. host: "",
  634. path: "",
  635. shouldMatch: false,
  636. },
  637. {
  638. title: "Queries route with regexp pattern with quantifier, match",
  639. route: new(Route).Queries("foo", "{v1:[0-9]{1}}"),
  640. request: newRequest("GET", "http://localhost?foo=1"),
  641. vars: map[string]string{"v1": "1"},
  642. host: "",
  643. path: "",
  644. shouldMatch: true,
  645. },
  646. {
  647. title: "Queries route with regexp pattern with quantifier, additional variable in query string, match",
  648. route: new(Route).Queries("foo", "{v1:[0-9]{1}}"),
  649. request: newRequest("GET", "http://localhost?bar=2&foo=1"),
  650. vars: map[string]string{"v1": "1"},
  651. host: "",
  652. path: "",
  653. shouldMatch: true,
  654. },
  655. {
  656. title: "Queries route with regexp pattern with quantifier, regexp does not match",
  657. route: new(Route).Queries("foo", "{v1:[0-9]{1}}"),
  658. request: newRequest("GET", "http://localhost?foo=12"),
  659. vars: map[string]string{},
  660. host: "",
  661. path: "",
  662. shouldMatch: false,
  663. },
  664. {
  665. title: "Queries route with regexp pattern with quantifier, additional capturing group",
  666. route: new(Route).Queries("foo", "{v1:[0-9]{1}(a|b)}"),
  667. request: newRequest("GET", "http://localhost?foo=1a"),
  668. vars: map[string]string{"v1": "1a"},
  669. host: "",
  670. path: "",
  671. shouldMatch: true,
  672. },
  673. {
  674. title: "Queries route with regexp pattern with quantifier, additional variable in query string, regexp does not match",
  675. route: new(Route).Queries("foo", "{v1:[0-9]{1}}"),
  676. request: newRequest("GET", "http://localhost?foo=12"),
  677. vars: map[string]string{},
  678. host: "",
  679. path: "",
  680. shouldMatch: false,
  681. },
  682. {
  683. title: "Queries route with hyphenated name, match",
  684. route: new(Route).Queries("foo", "{v-1}"),
  685. request: newRequest("GET", "http://localhost?foo=bar"),
  686. vars: map[string]string{"v-1": "bar"},
  687. host: "",
  688. path: "",
  689. shouldMatch: true,
  690. },
  691. {
  692. title: "Queries route with multiple hyphenated names, match",
  693. route: new(Route).Queries("foo", "{v-1}", "baz", "{v-2}"),
  694. request: newRequest("GET", "http://localhost?foo=bar&baz=ding"),
  695. vars: map[string]string{"v-1": "bar", "v-2": "ding"},
  696. host: "",
  697. path: "",
  698. shouldMatch: true,
  699. },
  700. {
  701. title: "Queries route with hyphenate name and pattern, match",
  702. route: new(Route).Queries("foo", "{v-1:[0-9]+}"),
  703. request: newRequest("GET", "http://localhost?foo=10"),
  704. vars: map[string]string{"v-1": "10"},
  705. host: "",
  706. path: "",
  707. shouldMatch: true,
  708. },
  709. {
  710. title: "Queries route with hyphenated name and pattern with quantifier, additional capturing group",
  711. route: new(Route).Queries("foo", "{v-1:[0-9]{1}(a|b)}"),
  712. request: newRequest("GET", "http://localhost?foo=1a"),
  713. vars: map[string]string{"v-1": "1a"},
  714. host: "",
  715. path: "",
  716. shouldMatch: true,
  717. },
  718. {
  719. title: "Queries route with empty value, should match",
  720. route: new(Route).Queries("foo", ""),
  721. request: newRequest("GET", "http://localhost?foo=bar"),
  722. vars: map[string]string{},
  723. host: "",
  724. path: "",
  725. shouldMatch: true,
  726. },
  727. {
  728. title: "Queries route with empty value and no parameter in request, should not match",
  729. route: new(Route).Queries("foo", ""),
  730. request: newRequest("GET", "http://localhost"),
  731. vars: map[string]string{},
  732. host: "",
  733. path: "",
  734. shouldMatch: false,
  735. },
  736. {
  737. title: "Queries route with empty value and empty parameter in request, should match",
  738. route: new(Route).Queries("foo", ""),
  739. request: newRequest("GET", "http://localhost?foo="),
  740. vars: map[string]string{},
  741. host: "",
  742. path: "",
  743. shouldMatch: true,
  744. },
  745. {
  746. title: "Queries route with overlapping value, should not match",
  747. route: new(Route).Queries("foo", "bar"),
  748. request: newRequest("GET", "http://localhost?foo=barfoo"),
  749. vars: map[string]string{},
  750. host: "",
  751. path: "",
  752. shouldMatch: false,
  753. },
  754. {
  755. title: "Queries route with no parameter in request, should not match",
  756. route: new(Route).Queries("foo", "{bar}"),
  757. request: newRequest("GET", "http://localhost"),
  758. vars: map[string]string{},
  759. host: "",
  760. path: "",
  761. shouldMatch: false,
  762. },
  763. {
  764. title: "Queries route with empty parameter in request, should match",
  765. route: new(Route).Queries("foo", "{bar}"),
  766. request: newRequest("GET", "http://localhost?foo="),
  767. vars: map[string]string{"foo": ""},
  768. host: "",
  769. path: "",
  770. shouldMatch: true,
  771. },
  772. {
  773. title: "Queries route, bad submatch",
  774. route: new(Route).Queries("foo", "bar", "baz", "ding"),
  775. request: newRequest("GET", "http://localhost?fffoo=bar&baz=dingggg"),
  776. vars: map[string]string{},
  777. host: "",
  778. path: "",
  779. shouldMatch: false,
  780. },
  781. }
  782. for _, test := range tests {
  783. testRoute(t, test)
  784. }
  785. }
  786. func TestSchemes(t *testing.T) {
  787. tests := []routeTest{
  788. // Schemes
  789. {
  790. title: "Schemes route, match https",
  791. route: new(Route).Schemes("https", "ftp"),
  792. request: newRequest("GET", "https://localhost"),
  793. vars: map[string]string{},
  794. host: "",
  795. path: "",
  796. shouldMatch: true,
  797. },
  798. {
  799. title: "Schemes route, match ftp",
  800. route: new(Route).Schemes("https", "ftp"),
  801. request: newRequest("GET", "ftp://localhost"),
  802. vars: map[string]string{},
  803. host: "",
  804. path: "",
  805. shouldMatch: true,
  806. },
  807. {
  808. title: "Schemes route, bad scheme",
  809. route: new(Route).Schemes("https", "ftp"),
  810. request: newRequest("GET", "http://localhost"),
  811. vars: map[string]string{},
  812. host: "",
  813. path: "",
  814. shouldMatch: false,
  815. },
  816. }
  817. for _, test := range tests {
  818. testRoute(t, test)
  819. }
  820. }
  821. func TestMatcherFunc(t *testing.T) {
  822. m := func(r *http.Request, m *RouteMatch) bool {
  823. if r.URL.Host == "aaa.bbb.ccc" {
  824. return true
  825. }
  826. return false
  827. }
  828. tests := []routeTest{
  829. {
  830. title: "MatchFunc route, match",
  831. route: new(Route).MatcherFunc(m),
  832. request: newRequest("GET", "http://aaa.bbb.ccc"),
  833. vars: map[string]string{},
  834. host: "",
  835. path: "",
  836. shouldMatch: true,
  837. },
  838. {
  839. title: "MatchFunc route, non-match",
  840. route: new(Route).MatcherFunc(m),
  841. request: newRequest("GET", "http://aaa.222.ccc"),
  842. vars: map[string]string{},
  843. host: "",
  844. path: "",
  845. shouldMatch: false,
  846. },
  847. }
  848. for _, test := range tests {
  849. testRoute(t, test)
  850. }
  851. }
  852. func TestBuildVarsFunc(t *testing.T) {
  853. tests := []routeTest{
  854. {
  855. title: "BuildVarsFunc set on route",
  856. route: new(Route).Path(`/111/{v1:\d}{v2:.*}`).BuildVarsFunc(func(vars map[string]string) map[string]string {
  857. vars["v1"] = "3"
  858. vars["v2"] = "a"
  859. return vars
  860. }),
  861. request: newRequest("GET", "http://localhost/111/2"),
  862. path: "/111/3a",
  863. shouldMatch: true,
  864. },
  865. {
  866. title: "BuildVarsFunc set on route and parent route",
  867. route: new(Route).PathPrefix(`/{v1:\d}`).BuildVarsFunc(func(vars map[string]string) map[string]string {
  868. vars["v1"] = "2"
  869. return vars
  870. }).Subrouter().Path(`/{v2:\w}`).BuildVarsFunc(func(vars map[string]string) map[string]string {
  871. vars["v2"] = "b"
  872. return vars
  873. }),
  874. request: newRequest("GET", "http://localhost/1/a"),
  875. path: "/2/b",
  876. shouldMatch: true,
  877. },
  878. }
  879. for _, test := range tests {
  880. testRoute(t, test)
  881. }
  882. }
  883. func TestSubRouter(t *testing.T) {
  884. subrouter1 := new(Route).Host("{v1:[a-z]+}.google.com").Subrouter()
  885. subrouter2 := new(Route).PathPrefix("/foo/{v1}").Subrouter()
  886. tests := []routeTest{
  887. {
  888. route: subrouter1.Path("/{v2:[a-z]+}"),
  889. request: newRequest("GET", "http://aaa.google.com/bbb"),
  890. vars: map[string]string{"v1": "aaa", "v2": "bbb"},
  891. host: "aaa.google.com",
  892. path: "/bbb",
  893. shouldMatch: true,
  894. },
  895. {
  896. route: subrouter1.Path("/{v2:[a-z]+}"),
  897. request: newRequest("GET", "http://111.google.com/111"),
  898. vars: map[string]string{"v1": "aaa", "v2": "bbb"},
  899. host: "aaa.google.com",
  900. path: "/bbb",
  901. shouldMatch: false,
  902. },
  903. {
  904. route: subrouter2.Path("/baz/{v2}"),
  905. request: newRequest("GET", "http://localhost/foo/bar/baz/ding"),
  906. vars: map[string]string{"v1": "bar", "v2": "ding"},
  907. host: "",
  908. path: "/foo/bar/baz/ding",
  909. shouldMatch: true,
  910. },
  911. {
  912. route: subrouter2.Path("/baz/{v2}"),
  913. request: newRequest("GET", "http://localhost/foo/bar"),
  914. vars: map[string]string{"v1": "bar", "v2": "ding"},
  915. host: "",
  916. path: "/foo/bar/baz/ding",
  917. shouldMatch: false,
  918. },
  919. }
  920. for _, test := range tests {
  921. testRoute(t, test)
  922. }
  923. }
  924. func TestNamedRoutes(t *testing.T) {
  925. r1 := NewRouter()
  926. r1.NewRoute().Name("a")
  927. r1.NewRoute().Name("b")
  928. r1.NewRoute().Name("c")
  929. r2 := r1.NewRoute().Subrouter()
  930. r2.NewRoute().Name("d")
  931. r2.NewRoute().Name("e")
  932. r2.NewRoute().Name("f")
  933. r3 := r2.NewRoute().Subrouter()
  934. r3.NewRoute().Name("g")
  935. r3.NewRoute().Name("h")
  936. r3.NewRoute().Name("i")
  937. if r1.namedRoutes == nil || len(r1.namedRoutes) != 9 {
  938. t.Errorf("Expected 9 named routes, got %v", r1.namedRoutes)
  939. } else if r1.Get("i") == nil {
  940. t.Errorf("Subroute name not registered")
  941. }
  942. }
  943. func TestStrictSlash(t *testing.T) {
  944. r := NewRouter()
  945. r.StrictSlash(true)
  946. tests := []routeTest{
  947. {
  948. title: "Redirect path without slash",
  949. route: r.NewRoute().Path("/111/"),
  950. request: newRequest("GET", "http://localhost/111"),
  951. vars: map[string]string{},
  952. host: "",
  953. path: "/111/",
  954. shouldMatch: true,
  955. shouldRedirect: true,
  956. },
  957. {
  958. title: "Do not redirect path with slash",
  959. route: r.NewRoute().Path("/111/"),
  960. request: newRequest("GET", "http://localhost/111/"),
  961. vars: map[string]string{},
  962. host: "",
  963. path: "/111/",
  964. shouldMatch: true,
  965. shouldRedirect: false,
  966. },
  967. {
  968. title: "Redirect path with slash",
  969. route: r.NewRoute().Path("/111"),
  970. request: newRequest("GET", "http://localhost/111/"),
  971. vars: map[string]string{},
  972. host: "",
  973. path: "/111",
  974. shouldMatch: true,
  975. shouldRedirect: true,
  976. },
  977. {
  978. title: "Do not redirect path without slash",
  979. route: r.NewRoute().Path("/111"),
  980. request: newRequest("GET", "http://localhost/111"),
  981. vars: map[string]string{},
  982. host: "",
  983. path: "/111",
  984. shouldMatch: true,
  985. shouldRedirect: false,
  986. },
  987. {
  988. title: "Propagate StrictSlash to subrouters",
  989. route: r.NewRoute().PathPrefix("/static/").Subrouter().Path("/images/"),
  990. request: newRequest("GET", "http://localhost/static/images"),
  991. vars: map[string]string{},
  992. host: "",
  993. path: "/static/images/",
  994. shouldMatch: true,
  995. shouldRedirect: true,
  996. },
  997. {
  998. title: "Ignore StrictSlash for path prefix",
  999. route: r.NewRoute().PathPrefix("/static/"),
  1000. request: newRequest("GET", "http://localhost/static/logo.png"),
  1001. vars: map[string]string{},
  1002. host: "",
  1003. path: "/static/",
  1004. shouldMatch: true,
  1005. shouldRedirect: false,
  1006. },
  1007. }
  1008. for _, test := range tests {
  1009. testRoute(t, test)
  1010. }
  1011. }
  1012. func TestWalkSingleDepth(t *testing.T) {
  1013. r0 := NewRouter()
  1014. r1 := NewRouter()
  1015. r2 := NewRouter()
  1016. r0.Path("/g")
  1017. r0.Path("/o")
  1018. r0.Path("/d").Handler(r1)
  1019. r0.Path("/r").Handler(r2)
  1020. r0.Path("/a")
  1021. r1.Path("/z")
  1022. r1.Path("/i")
  1023. r1.Path("/l")
  1024. r1.Path("/l")
  1025. r2.Path("/i")
  1026. r2.Path("/l")
  1027. r2.Path("/l")
  1028. paths := []string{"g", "o", "r", "i", "l", "l", "a"}
  1029. depths := []int{0, 0, 0, 1, 1, 1, 0}
  1030. i := 0
  1031. err := r0.Walk(func(route *Route, router *Router, ancestors []*Route) error {
  1032. matcher := route.matchers[0].(*routeRegexp)
  1033. if matcher.template == "/d" {
  1034. return SkipRouter
  1035. }
  1036. if len(ancestors) != depths[i] {
  1037. t.Errorf(`Expected depth of %d at i = %d; got "%d"`, depths[i], i, len(ancestors))
  1038. }
  1039. if matcher.template != "/"+paths[i] {
  1040. t.Errorf(`Expected "/%s" at i = %d; got "%s"`, paths[i], i, matcher.template)
  1041. }
  1042. i++
  1043. return nil
  1044. })
  1045. if err != nil {
  1046. panic(err)
  1047. }
  1048. if i != len(paths) {
  1049. t.Errorf("Expected %d routes, found %d", len(paths), i)
  1050. }
  1051. }
  1052. func TestWalkNested(t *testing.T) {
  1053. router := NewRouter()
  1054. g := router.Path("/g").Subrouter()
  1055. o := g.PathPrefix("/o").Subrouter()
  1056. r := o.PathPrefix("/r").Subrouter()
  1057. i := r.PathPrefix("/i").Subrouter()
  1058. l1 := i.PathPrefix("/l").Subrouter()
  1059. l2 := l1.PathPrefix("/l").Subrouter()
  1060. l2.Path("/a")
  1061. paths := []string{"/g", "/g/o", "/g/o/r", "/g/o/r/i", "/g/o/r/i/l", "/g/o/r/i/l/l", "/g/o/r/i/l/l/a"}
  1062. idx := 0
  1063. err := router.Walk(func(route *Route, router *Router, ancestors []*Route) error {
  1064. path := paths[idx]
  1065. tpl := route.regexp.path.template
  1066. if tpl != path {
  1067. t.Errorf(`Expected %s got %s`, path, tpl)
  1068. }
  1069. idx++
  1070. return nil
  1071. })
  1072. if err != nil {
  1073. panic(err)
  1074. }
  1075. if idx != len(paths) {
  1076. t.Errorf("Expected %d routes, found %d", len(paths), idx)
  1077. }
  1078. }
  1079. func TestSubrouterErrorHandling(t *testing.T) {
  1080. superRouterCalled := false
  1081. subRouterCalled := false
  1082. router := NewRouter()
  1083. router.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1084. superRouterCalled = true
  1085. })
  1086. subRouter := router.PathPrefix("/bign8").Subrouter()
  1087. subRouter.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1088. subRouterCalled = true
  1089. })
  1090. req, _ := http.NewRequest("GET", "http://localhost/bign8/was/here", nil)
  1091. router.ServeHTTP(NewRecorder(), req)
  1092. if superRouterCalled {
  1093. t.Error("Super router 404 handler called when sub-router 404 handler is available.")
  1094. }
  1095. if !subRouterCalled {
  1096. t.Error("Sub-router 404 handler was not called.")
  1097. }
  1098. }
  1099. // ----------------------------------------------------------------------------
  1100. // Helpers
  1101. // ----------------------------------------------------------------------------
  1102. func getRouteTemplate(route *Route) string {
  1103. host, path := "none", "none"
  1104. if route.regexp != nil {
  1105. if route.regexp.host != nil {
  1106. host = route.regexp.host.template
  1107. }
  1108. if route.regexp.path != nil {
  1109. path = route.regexp.path.template
  1110. }
  1111. }
  1112. return fmt.Sprintf("Host: %v, Path: %v", host, path)
  1113. }
  1114. func testRoute(t *testing.T, test routeTest) {
  1115. request := test.request
  1116. route := test.route
  1117. vars := test.vars
  1118. shouldMatch := test.shouldMatch
  1119. host := test.host
  1120. path := test.path
  1121. url := test.host + test.path
  1122. shouldRedirect := test.shouldRedirect
  1123. var match RouteMatch
  1124. ok := route.Match(request, &match)
  1125. if ok != shouldMatch {
  1126. msg := "Should match"
  1127. if !shouldMatch {
  1128. msg = "Should not match"
  1129. }
  1130. t.Errorf("(%v) %v:\nRoute: %#v\nRequest: %#v\nVars: %v\n", test.title, msg, route, request, vars)
  1131. return
  1132. }
  1133. if shouldMatch {
  1134. if test.vars != nil && !stringMapEqual(test.vars, match.Vars) {
  1135. t.Errorf("(%v) Vars not equal: expected %v, got %v", test.title, vars, match.Vars)
  1136. return
  1137. }
  1138. if host != "" {
  1139. u, _ := test.route.URLHost(mapToPairs(match.Vars)...)
  1140. if host != u.Host {
  1141. t.Errorf("(%v) URLHost not equal: expected %v, got %v -- %v", test.title, host, u.Host, getRouteTemplate(route))
  1142. return
  1143. }
  1144. }
  1145. if path != "" {
  1146. u, _ := route.URLPath(mapToPairs(match.Vars)...)
  1147. if path != u.Path {
  1148. t.Errorf("(%v) URLPath not equal: expected %v, got %v -- %v", test.title, path, u.Path, getRouteTemplate(route))
  1149. return
  1150. }
  1151. }
  1152. if url != "" {
  1153. u, _ := route.URL(mapToPairs(match.Vars)...)
  1154. if url != u.Host+u.Path {
  1155. t.Errorf("(%v) URL not equal: expected %v, got %v -- %v", test.title, url, u.Host+u.Path, getRouteTemplate(route))
  1156. return
  1157. }
  1158. }
  1159. if shouldRedirect && match.Handler == nil {
  1160. t.Errorf("(%v) Did not redirect", test.title)
  1161. return
  1162. }
  1163. if !shouldRedirect && match.Handler != nil {
  1164. t.Errorf("(%v) Unexpected redirect", test.title)
  1165. return
  1166. }
  1167. }
  1168. }
  1169. // Tests that the context is cleared or not cleared properly depending on
  1170. // the configuration of the router
  1171. func TestKeepContext(t *testing.T) {
  1172. func1 := func(w http.ResponseWriter, r *http.Request) {}
  1173. r := NewRouter()
  1174. r.HandleFunc("/", func1).Name("func1")
  1175. req, _ := http.NewRequest("GET", "http://localhost/", nil)
  1176. context.Set(req, "t", 1)
  1177. res := new(http.ResponseWriter)
  1178. r.ServeHTTP(*res, req)
  1179. if _, ok := context.GetOk(req, "t"); ok {
  1180. t.Error("Context should have been cleared at end of request")
  1181. }
  1182. r.KeepContext = true
  1183. req, _ = http.NewRequest("GET", "http://localhost/", nil)
  1184. context.Set(req, "t", 1)
  1185. r.ServeHTTP(*res, req)
  1186. if _, ok := context.GetOk(req, "t"); !ok {
  1187. t.Error("Context should NOT have been cleared at end of request")
  1188. }
  1189. }
  1190. type TestA301ResponseWriter struct {
  1191. hh http.Header
  1192. status int
  1193. }
  1194. func (ho TestA301ResponseWriter) Header() http.Header {
  1195. return http.Header(ho.hh)
  1196. }
  1197. func (ho TestA301ResponseWriter) Write(b []byte) (int, error) {
  1198. return 0, nil
  1199. }
  1200. func (ho TestA301ResponseWriter) WriteHeader(code int) {
  1201. ho.status = code
  1202. }
  1203. func Test301Redirect(t *testing.T) {
  1204. m := make(http.Header)
  1205. func1 := func(w http.ResponseWriter, r *http.Request) {}
  1206. func2 := func(w http.ResponseWriter, r *http.Request) {}
  1207. r := NewRouter()
  1208. r.HandleFunc("/api/", func2).Name("func2")
  1209. r.HandleFunc("/", func1).Name("func1")
  1210. req, _ := http.NewRequest("GET", "http://localhost//api/?abc=def", nil)
  1211. res := TestA301ResponseWriter{
  1212. hh: m,
  1213. status: 0,
  1214. }
  1215. r.ServeHTTP(&res, req)
  1216. if "http://localhost/api/?abc=def" != res.hh["Location"][0] {
  1217. t.Errorf("Should have complete URL with query string")
  1218. }
  1219. }
  1220. // https://plus.google.com/101022900381697718949/posts/eWy6DjFJ6uW
  1221. func TestSubrouterHeader(t *testing.T) {
  1222. expected := "func1 response"
  1223. func1 := func(w http.ResponseWriter, r *http.Request) {
  1224. fmt.Fprint(w, expected)
  1225. }
  1226. func2 := func(http.ResponseWriter, *http.Request) {}
  1227. r := NewRouter()
  1228. s := r.Headers("SomeSpecialHeader", "").Subrouter()
  1229. s.HandleFunc("/", func1).Name("func1")
  1230. r.HandleFunc("/", func2).Name("func2")
  1231. req, _ := http.NewRequest("GET", "http://localhost/", nil)
  1232. req.Header.Add("SomeSpecialHeader", "foo")
  1233. match := new(RouteMatch)
  1234. matched := r.Match(req, match)
  1235. if !matched {
  1236. t.Errorf("Should match request")
  1237. }
  1238. if match.Route.GetName() != "func1" {
  1239. t.Errorf("Expecting func1 handler, got %s", match.Route.GetName())
  1240. }
  1241. resp := NewRecorder()
  1242. match.Handler.ServeHTTP(resp, req)
  1243. if resp.Body.String() != expected {
  1244. t.Errorf("Expecting %q", expected)
  1245. }
  1246. }
  1247. // mapToPairs converts a string map to a slice of string pairs
  1248. func mapToPairs(m map[string]string) []string {
  1249. var i int
  1250. p := make([]string, len(m)*2)
  1251. for k, v := range m {
  1252. p[i] = k
  1253. p[i+1] = v
  1254. i += 2
  1255. }
  1256. return p
  1257. }
  1258. // stringMapEqual checks the equality of two string maps
  1259. func stringMapEqual(m1, m2 map[string]string) bool {
  1260. nil1 := m1 == nil
  1261. nil2 := m2 == nil
  1262. if nil1 != nil2 || len(m1) != len(m2) {
  1263. return false
  1264. }
  1265. for k, v := range m1 {
  1266. if v != m2[k] {
  1267. return false
  1268. }
  1269. }
  1270. return true
  1271. }
  1272. // newRequest is a helper function to create a new request with a method and url
  1273. func newRequest(method, url string) *http.Request {
  1274. req, err := http.NewRequest(method, url, nil)
  1275. if err != nil {
  1276. panic(err)
  1277. }
  1278. return req
  1279. }