/backend/vendor/src/github.com/zenazn/goji/web/router_test.go

https://github.com/coreroller/coreroller · Go · 326 lines · 275 code · 42 blank · 9 comment · 27 complexity · 024a3a36983cc3df4c6ffcf34d7041df MD5 · raw file

  1. package web
  2. import (
  3. "net/http"
  4. "net/http/httptest"
  5. "reflect"
  6. "regexp"
  7. "testing"
  8. "time"
  9. )
  10. // These tests can probably be DRY'd up a bunch
  11. func chHandler(ch chan string, s string) http.Handler {
  12. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  13. ch <- s
  14. })
  15. }
  16. var methods = []string{"CONNECT", "DELETE", "GET", "HEAD", "OPTIONS", "PATCH",
  17. "POST", "PUT", "TRACE", "OTHER"}
  18. func TestMethods(t *testing.T) {
  19. t.Parallel()
  20. m := New()
  21. ch := make(chan string, 1)
  22. m.Connect("/", chHandler(ch, "CONNECT"))
  23. m.Delete("/", chHandler(ch, "DELETE"))
  24. m.Head("/", chHandler(ch, "HEAD"))
  25. m.Get("/", chHandler(ch, "GET"))
  26. m.Options("/", chHandler(ch, "OPTIONS"))
  27. m.Patch("/", chHandler(ch, "PATCH"))
  28. m.Post("/", chHandler(ch, "POST"))
  29. m.Put("/", chHandler(ch, "PUT"))
  30. m.Trace("/", chHandler(ch, "TRACE"))
  31. m.Handle("/", chHandler(ch, "OTHER"))
  32. for _, method := range methods {
  33. r, _ := http.NewRequest(method, "/", nil)
  34. w := httptest.NewRecorder()
  35. m.ServeHTTP(w, r)
  36. select {
  37. case val := <-ch:
  38. if val != method {
  39. t.Errorf("Got %q, expected %q", val, method)
  40. }
  41. case <-time.After(5 * time.Millisecond):
  42. t.Errorf("Timeout waiting for method %q", method)
  43. }
  44. }
  45. }
  46. type testPattern struct{}
  47. func (t testPattern) Prefix() string {
  48. return ""
  49. }
  50. func (t testPattern) Match(r *http.Request, c *C) bool {
  51. return true
  52. }
  53. func (t testPattern) Run(r *http.Request, c *C) {
  54. }
  55. var _ Pattern = testPattern{}
  56. func TestPatternTypes(t *testing.T) {
  57. t.Parallel()
  58. m := New()
  59. m.Get("/hello/carl", http.NotFound)
  60. m.Get("/hello/:name", http.NotFound)
  61. m.Get(regexp.MustCompile(`^/hello/(?P<name>.+)$`), http.NotFound)
  62. m.Get(testPattern{}, http.NotFound)
  63. }
  64. type testHandler chan string
  65. func (t testHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  66. t <- "http"
  67. }
  68. func (t testHandler) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) {
  69. t <- "httpc"
  70. }
  71. var testHandlerTable = map[string]string{
  72. "/a": "http fn",
  73. "/b": "http handler",
  74. "/c": "web fn",
  75. "/d": "web handler",
  76. "/e": "httpc",
  77. }
  78. func TestHandlerTypes(t *testing.T) {
  79. t.Parallel()
  80. m := New()
  81. ch := make(chan string, 1)
  82. m.Get("/a", func(w http.ResponseWriter, r *http.Request) {
  83. ch <- "http fn"
  84. })
  85. m.Get("/b", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  86. ch <- "http handler"
  87. }))
  88. m.Get("/c", func(c C, w http.ResponseWriter, r *http.Request) {
  89. ch <- "web fn"
  90. })
  91. m.Get("/d", HandlerFunc(func(c C, w http.ResponseWriter, r *http.Request) {
  92. ch <- "web handler"
  93. }))
  94. m.Get("/e", testHandler(ch))
  95. for route, response := range testHandlerTable {
  96. r, _ := http.NewRequest("GET", route, nil)
  97. w := httptest.NewRecorder()
  98. m.ServeHTTP(w, r)
  99. select {
  100. case resp := <-ch:
  101. if resp != response {
  102. t.Errorf("Got %q, expected %q", resp, response)
  103. }
  104. case <-time.After(5 * time.Millisecond):
  105. t.Errorf("Timeout waiting for path %q", route)
  106. }
  107. }
  108. }
  109. // The idea behind this test is to comprehensively test if routes are being
  110. // applied in the right order. We define a special pattern type that always
  111. // matches so long as it's greater than or equal to the global test index. By
  112. // incrementing this index, we can invalidate all routes up to some point, and
  113. // therefore test the routing guarantee that Goji provides: for any path P, if
  114. // both A and B match P, and if A was inserted before B, then Goji will route to
  115. // A before it routes to B.
  116. var rsRoutes = []string{
  117. "/",
  118. "/a",
  119. "/a",
  120. "/b",
  121. "/ab",
  122. "/",
  123. "/ba",
  124. "/b",
  125. "/a",
  126. }
  127. var rsTests = []struct {
  128. key string
  129. results []int
  130. }{
  131. {"/", []int{0, 5, 5, 5, 5, 5, -1, -1, -1, -1}},
  132. {"/a", []int{0, 1, 2, 5, 5, 5, 8, 8, 8, -1}},
  133. {"/b", []int{0, 3, 3, 3, 5, 5, 7, 7, -1, -1}},
  134. {"/ab", []int{0, 1, 2, 4, 4, 5, 8, 8, 8, -1}},
  135. {"/ba", []int{0, 3, 3, 3, 5, 5, 6, 7, -1, -1}},
  136. {"/c", []int{0, 5, 5, 5, 5, 5, -1, -1, -1, -1}},
  137. {"nope", []int{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}},
  138. }
  139. type rsPattern struct {
  140. i int
  141. counter *int
  142. prefix string
  143. ichan chan int
  144. }
  145. func (rs rsPattern) Prefix() string {
  146. return rs.prefix
  147. }
  148. func (rs rsPattern) Match(_ *http.Request, _ *C) bool {
  149. return rs.i >= *rs.counter
  150. }
  151. func (rs rsPattern) Run(_ *http.Request, _ *C) {
  152. }
  153. func (rs rsPattern) ServeHTTP(_ http.ResponseWriter, _ *http.Request) {
  154. rs.ichan <- rs.i
  155. }
  156. var _ Pattern = rsPattern{}
  157. var _ http.Handler = rsPattern{}
  158. func TestRouteSelection(t *testing.T) {
  159. t.Parallel()
  160. m := New()
  161. counter := 0
  162. ichan := make(chan int, 1)
  163. m.NotFound(func(w http.ResponseWriter, r *http.Request) {
  164. ichan <- -1
  165. })
  166. for i, s := range rsRoutes {
  167. pat := rsPattern{
  168. i: i,
  169. counter: &counter,
  170. prefix: s,
  171. ichan: ichan,
  172. }
  173. m.Get(pat, pat)
  174. }
  175. for _, test := range rsTests {
  176. var n int
  177. for counter, n = range test.results {
  178. r, _ := http.NewRequest("GET", test.key, nil)
  179. w := httptest.NewRecorder()
  180. m.ServeHTTP(w, r)
  181. actual := <-ichan
  182. if n != actual {
  183. t.Errorf("Expected %q @ %d to be %d, got %d",
  184. test.key, counter, n, actual)
  185. }
  186. }
  187. }
  188. }
  189. func TestNotFound(t *testing.T) {
  190. t.Parallel()
  191. m := New()
  192. r, _ := http.NewRequest("post", "/", nil)
  193. w := httptest.NewRecorder()
  194. m.ServeHTTP(w, r)
  195. if w.Code != 404 {
  196. t.Errorf("Expected 404, got %d", w.Code)
  197. }
  198. m.NotFound(func(w http.ResponseWriter, r *http.Request) {
  199. http.Error(w, "I'm a teapot!", http.StatusTeapot)
  200. })
  201. r, _ = http.NewRequest("POST", "/", nil)
  202. w = httptest.NewRecorder()
  203. m.ServeHTTP(w, r)
  204. if w.Code != http.StatusTeapot {
  205. t.Errorf("Expected a teapot, got %d", w.Code)
  206. }
  207. }
  208. func TestPrefix(t *testing.T) {
  209. t.Parallel()
  210. m := New()
  211. ch := make(chan string, 1)
  212. m.Handle("/hello/*", func(w http.ResponseWriter, r *http.Request) {
  213. ch <- r.URL.Path
  214. })
  215. r, _ := http.NewRequest("GET", "/hello/world", nil)
  216. w := httptest.NewRecorder()
  217. m.ServeHTTP(w, r)
  218. select {
  219. case val := <-ch:
  220. if val != "/hello/world" {
  221. t.Errorf("Got %q, expected /hello/world", val)
  222. }
  223. case <-time.After(5 * time.Millisecond):
  224. t.Errorf("Timeout waiting for hello")
  225. }
  226. }
  227. var validMethodsTable = map[string][]string{
  228. "/hello/carl": {"DELETE", "GET", "HEAD", "PATCH", "POST", "PUT"},
  229. "/hello/bob": {"DELETE", "GET", "HEAD", "PATCH", "PUT"},
  230. "/hola/carl": {"DELETE", "GET", "HEAD", "PUT"},
  231. "/hola/bob": {"DELETE"},
  232. "/does/not/compute": {},
  233. }
  234. func TestValidMethods(t *testing.T) {
  235. t.Parallel()
  236. m := New()
  237. ch := make(chan []string, 1)
  238. m.NotFound(func(c C, w http.ResponseWriter, r *http.Request) {
  239. if c.Env == nil {
  240. ch <- []string{}
  241. return
  242. }
  243. methods, ok := c.Env[ValidMethodsKey]
  244. if !ok {
  245. ch <- []string{}
  246. return
  247. }
  248. ch <- methods.([]string)
  249. })
  250. m.Get("/hello/carl", http.NotFound)
  251. m.Post("/hello/carl", http.NotFound)
  252. m.Head("/hello/bob", http.NotFound)
  253. m.Get("/hello/:name", http.NotFound)
  254. m.Put("/hello/:name", http.NotFound)
  255. m.Patch("/hello/:name", http.NotFound)
  256. m.Get("/:greet/carl", http.NotFound)
  257. m.Put("/:greet/carl", http.NotFound)
  258. m.Delete("/:greet/:anyone", http.NotFound)
  259. for path, eMethods := range validMethodsTable {
  260. r, _ := http.NewRequest("BOGUS", path, nil)
  261. m.ServeHTTP(httptest.NewRecorder(), r)
  262. aMethods := <-ch
  263. if !reflect.DeepEqual(eMethods, aMethods) {
  264. t.Errorf("For %q, expected %v, got %v", path, eMethods,
  265. aMethods)
  266. }
  267. }
  268. // This should also work when c.Env has already been initalized
  269. m.Use(func(c *C, h http.Handler) http.Handler {
  270. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  271. c.Env = make(map[interface{}]interface{})
  272. h.ServeHTTP(w, r)
  273. })
  274. })
  275. for path, eMethods := range validMethodsTable {
  276. r, _ := http.NewRequest("BOGUS", path, nil)
  277. m.ServeHTTP(httptest.NewRecorder(), r)
  278. aMethods := <-ch
  279. if !reflect.DeepEqual(eMethods, aMethods) {
  280. t.Errorf("For %q, expected %v, got %v", path, eMethods,
  281. aMethods)
  282. }
  283. }
  284. }