tree_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. // Copyright 2014 beego Author. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package beego
  15. import (
  16. "strings"
  17. "testing"
  18. "github.com/astaxie/beego/context"
  19. )
  20. type testinfo struct {
  21. url string
  22. requesturl string
  23. params map[string]string
  24. }
  25. var routers []testinfo
  26. func init() {
  27. routers = make([]testinfo, 0)
  28. routers = append(routers, testinfo{"/topic/?:auth:int", "/topic", nil})
  29. routers = append(routers, testinfo{"/topic/?:auth:int", "/topic/123", map[string]string{":auth": "123"}})
  30. routers = append(routers, testinfo{"/topic/:id/?:auth", "/topic/1", map[string]string{":id": "1"}})
  31. routers = append(routers, testinfo{"/topic/:id/?:auth", "/topic/1/2", map[string]string{":id": "1", ":auth": "2"}})
  32. routers = append(routers, testinfo{"/topic/:id/?:auth:int", "/topic/1", map[string]string{":id": "1"}})
  33. routers = append(routers, testinfo{"/topic/:id/?:auth:int", "/topic/1/123", map[string]string{":id": "1", ":auth": "123"}})
  34. routers = append(routers, testinfo{"/:id", "/123", map[string]string{":id": "123"}})
  35. routers = append(routers, testinfo{"/hello/?:id", "/hello", map[string]string{":id": ""}})
  36. routers = append(routers, testinfo{"/", "/", nil})
  37. routers = append(routers, testinfo{"/customer/login", "/customer/login", nil})
  38. routers = append(routers, testinfo{"/customer/login", "/customer/login.json", map[string]string{":ext": "json"}})
  39. routers = append(routers, testinfo{"/*", "/customer/123", map[string]string{":splat": "customer/123"}})
  40. routers = append(routers, testinfo{"/*", "/customer/2009/12/11", map[string]string{":splat": "customer/2009/12/11"}})
  41. routers = append(routers, testinfo{"/aa/*/bb", "/aa/2009/bb", map[string]string{":splat": "2009"}})
  42. routers = append(routers, testinfo{"/cc/*/dd", "/cc/2009/11/dd", map[string]string{":splat": "2009/11"}})
  43. routers = append(routers, testinfo{"/cc/:id/*", "/cc/2009/11/dd", map[string]string{":id": "2009", ":splat": "11/dd"}})
  44. routers = append(routers, testinfo{"/ee/:year/*/ff", "/ee/2009/11/ff", map[string]string{":year": "2009", ":splat": "11"}})
  45. routers = append(routers, testinfo{"/thumbnail/:size/uploads/*",
  46. "/thumbnail/100x100/uploads/items/2014/04/20/dPRCdChkUd651t1Hvs18.jpg",
  47. map[string]string{":size": "100x100", ":splat": "items/2014/04/20/dPRCdChkUd651t1Hvs18.jpg"}})
  48. routers = append(routers, testinfo{"/*.*", "/nice/api.json", map[string]string{":path": "nice/api", ":ext": "json"}})
  49. routers = append(routers, testinfo{"/:name/*.*", "/nice/api.json", map[string]string{":name": "nice", ":path": "api", ":ext": "json"}})
  50. routers = append(routers, testinfo{"/:name/test/*.*", "/nice/test/api.json", map[string]string{":name": "nice", ":path": "api", ":ext": "json"}})
  51. routers = append(routers, testinfo{"/dl/:width:int/:height:int/*.*",
  52. "/dl/48/48/05ac66d9bda00a3acf948c43e306fc9a.jpg",
  53. map[string]string{":width": "48", ":height": "48", ":ext": "jpg", ":path": "05ac66d9bda00a3acf948c43e306fc9a"}})
  54. routers = append(routers, testinfo{"/v1/shop/:id:int", "/v1/shop/123", map[string]string{":id": "123"}})
  55. routers = append(routers, testinfo{"/v1/shop/:id\\((a|b|c)\\)", "/v1/shop/123(a)", map[string]string{":id": "123"}})
  56. routers = append(routers, testinfo{"/v1/shop/:id\\((a|b|c)\\)", "/v1/shop/123(b)", map[string]string{":id": "123"}})
  57. routers = append(routers, testinfo{"/v1/shop/:id\\((a|b|c)\\)", "/v1/shop/123(c)", map[string]string{":id": "123"}})
  58. routers = append(routers, testinfo{"/:year:int/:month:int/:id/:endid", "/1111/111/aaa/aaa", map[string]string{":year": "1111", ":month": "111", ":id": "aaa", ":endid": "aaa"}})
  59. routers = append(routers, testinfo{"/v1/shop/:id/:name", "/v1/shop/123/nike", map[string]string{":id": "123", ":name": "nike"}})
  60. routers = append(routers, testinfo{"/v1/shop/:id/account", "/v1/shop/123/account", map[string]string{":id": "123"}})
  61. routers = append(routers, testinfo{"/v1/shop/:name:string", "/v1/shop/nike", map[string]string{":name": "nike"}})
  62. routers = append(routers, testinfo{"/v1/shop/:id([0-9]+)", "/v1/shop//123", map[string]string{":id": "123"}})
  63. routers = append(routers, testinfo{"/v1/shop/:id([0-9]+)_:name", "/v1/shop/123_nike", map[string]string{":id": "123", ":name": "nike"}})
  64. routers = append(routers, testinfo{"/v1/shop/:id(.+)_cms.html", "/v1/shop/123_cms.html", map[string]string{":id": "123"}})
  65. routers = append(routers, testinfo{"/v1/shop/cms_:id(.+)_:page(.+).html", "/v1/shop/cms_123_1.html", map[string]string{":id": "123", ":page": "1"}})
  66. routers = append(routers, testinfo{"/v1/:v/cms/aaa_:id(.+)_:page(.+).html", "/v1/2/cms/aaa_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"}})
  67. routers = append(routers, testinfo{"/v1/:v/cms_:id(.+)_:page(.+).html", "/v1/2/cms_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"}})
  68. routers = append(routers, testinfo{"/v1/:v(.+)_cms/ttt_:id(.+)_:page(.+).html", "/v1/2_cms/ttt_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"}})
  69. routers = append(routers, testinfo{"/api/projects/:pid/members/?:mid", "/api/projects/1/members", map[string]string{":pid": "1"}})
  70. routers = append(routers, testinfo{"/api/projects/:pid/members/?:mid", "/api/projects/1/members/2", map[string]string{":pid": "1", ":mid": "2"}})
  71. }
  72. func TestTreeRouters(t *testing.T) {
  73. for _, r := range routers {
  74. tr := NewTree()
  75. tr.AddRouter(r.url, "astaxie")
  76. ctx := context.NewContext()
  77. obj := tr.Match(r.requesturl, ctx)
  78. if obj == nil || obj.(string) != "astaxie" {
  79. t.Fatal(r.url+" can't get obj, Expect ", r.requesturl)
  80. }
  81. if r.params != nil {
  82. for k, v := range r.params {
  83. if vv := ctx.Input.Param(k); vv != v {
  84. t.Fatal("The Rule: " + r.url + "\nThe RequestURL:" + r.requesturl + "\nThe Key is " + k + ", The Value should be: " + v + ", but get: " + vv)
  85. } else if vv == "" && v != "" {
  86. t.Fatal(r.url + " " + r.requesturl + " get param empty:" + k)
  87. }
  88. }
  89. }
  90. }
  91. }
  92. func TestStaticPath(t *testing.T) {
  93. tr := NewTree()
  94. tr.AddRouter("/topic/:id", "wildcard")
  95. tr.AddRouter("/topic", "static")
  96. ctx := context.NewContext()
  97. obj := tr.Match("/topic", ctx)
  98. if obj == nil || obj.(string) != "static" {
  99. t.Fatal("/topic is a static route")
  100. }
  101. obj = tr.Match("/topic/1", ctx)
  102. if obj == nil || obj.(string) != "wildcard" {
  103. t.Fatal("/topic/1 is a wildcard route")
  104. }
  105. }
  106. func TestAddTree(t *testing.T) {
  107. tr := NewTree()
  108. tr.AddRouter("/shop/:id/account", "astaxie")
  109. tr.AddRouter("/shop/:sd/ttt_:id(.+)_:page(.+).html", "astaxie")
  110. t1 := NewTree()
  111. t1.AddTree("/v1/zl", tr)
  112. ctx := context.NewContext()
  113. obj := t1.Match("/v1/zl/shop/123/account", ctx)
  114. if obj == nil || obj.(string) != "astaxie" {
  115. t.Fatal("/v1/zl/shop/:id/account can't get obj ")
  116. }
  117. if ctx.Input.ParamsLen() == 0 {
  118. t.Fatal("get param error")
  119. }
  120. if ctx.Input.Param(":id") != "123" {
  121. t.Fatal("get :id param error")
  122. }
  123. ctx.Input.Reset(ctx)
  124. obj = t1.Match("/v1/zl/shop/123/ttt_1_12.html", ctx)
  125. if obj == nil || obj.(string) != "astaxie" {
  126. t.Fatal("/v1/zl//shop/:sd/ttt_:id(.+)_:page(.+).html can't get obj ")
  127. }
  128. if ctx.Input.ParamsLen() == 0 {
  129. t.Fatal("get param error")
  130. }
  131. if ctx.Input.Param(":sd") != "123" || ctx.Input.Param(":id") != "1" || ctx.Input.Param(":page") != "12" {
  132. t.Fatal("get :sd :id :page param error")
  133. }
  134. t2 := NewTree()
  135. t2.AddTree("/v1/:shopid", tr)
  136. ctx.Input.Reset(ctx)
  137. obj = t2.Match("/v1/zl/shop/123/account", ctx)
  138. if obj == nil || obj.(string) != "astaxie" {
  139. t.Fatal("/v1/:shopid/shop/:id/account can't get obj ")
  140. }
  141. if ctx.Input.ParamsLen() == 0 {
  142. t.Fatal("get param error")
  143. }
  144. if ctx.Input.Param(":id") != "123" || ctx.Input.Param(":shopid") != "zl" {
  145. t.Fatal("get :id :shopid param error")
  146. }
  147. ctx.Input.Reset(ctx)
  148. obj = t2.Match("/v1/zl/shop/123/ttt_1_12.html", ctx)
  149. if obj == nil || obj.(string) != "astaxie" {
  150. t.Fatal("/v1/:shopid/shop/:sd/ttt_:id(.+)_:page(.+).html can't get obj ")
  151. }
  152. if ctx.Input.ParamsLen() == 0 {
  153. t.Fatal("get :shopid param error")
  154. }
  155. if ctx.Input.Param(":sd") != "123" || ctx.Input.Param(":id") != "1" || ctx.Input.Param(":page") != "12" || ctx.Input.Param(":shopid") != "zl" {
  156. t.Fatal("get :sd :id :page :shopid param error")
  157. }
  158. }
  159. func TestAddTree2(t *testing.T) {
  160. tr := NewTree()
  161. tr.AddRouter("/shop/:id/account", "astaxie")
  162. tr.AddRouter("/shop/:sd/ttt_:id(.+)_:page(.+).html", "astaxie")
  163. t3 := NewTree()
  164. t3.AddTree("/:version(v1|v2)/:prefix", tr)
  165. ctx := context.NewContext()
  166. obj := t3.Match("/v1/zl/shop/123/account", ctx)
  167. if obj == nil || obj.(string) != "astaxie" {
  168. t.Fatal("/:version(v1|v2)/:prefix/shop/:id/account can't get obj ")
  169. }
  170. if ctx.Input.ParamsLen() == 0 {
  171. t.Fatal("get param error")
  172. }
  173. if ctx.Input.Param(":id") != "123" || ctx.Input.Param(":prefix") != "zl" || ctx.Input.Param(":version") != "v1" {
  174. t.Fatal("get :id :prefix :version param error")
  175. }
  176. }
  177. func TestAddTree3(t *testing.T) {
  178. tr := NewTree()
  179. tr.AddRouter("/create", "astaxie")
  180. tr.AddRouter("/shop/:sd/account", "astaxie")
  181. t3 := NewTree()
  182. t3.AddTree("/table/:num", tr)
  183. ctx := context.NewContext()
  184. obj := t3.Match("/table/123/shop/123/account", ctx)
  185. if obj == nil || obj.(string) != "astaxie" {
  186. t.Fatal("/table/:num/shop/:sd/account can't get obj ")
  187. }
  188. if ctx.Input.ParamsLen() == 0 {
  189. t.Fatal("get param error")
  190. }
  191. if ctx.Input.Param(":num") != "123" || ctx.Input.Param(":sd") != "123" {
  192. t.Fatal("get :num :sd param error")
  193. }
  194. ctx.Input.Reset(ctx)
  195. obj = t3.Match("/table/123/create", ctx)
  196. if obj == nil || obj.(string) != "astaxie" {
  197. t.Fatal("/table/:num/create can't get obj ")
  198. }
  199. }
  200. func TestAddTree4(t *testing.T) {
  201. tr := NewTree()
  202. tr.AddRouter("/create", "astaxie")
  203. tr.AddRouter("/shop/:sd/:account", "astaxie")
  204. t4 := NewTree()
  205. t4.AddTree("/:info:int/:num/:id", tr)
  206. ctx := context.NewContext()
  207. obj := t4.Match("/12/123/456/shop/123/account", ctx)
  208. if obj == nil || obj.(string) != "astaxie" {
  209. t.Fatal("/:info:int/:num/:id/shop/:sd/:account can't get obj ")
  210. }
  211. if ctx.Input.ParamsLen() == 0 {
  212. t.Fatal("get param error")
  213. }
  214. if ctx.Input.Param(":info") != "12" || ctx.Input.Param(":num") != "123" ||
  215. ctx.Input.Param(":id") != "456" || ctx.Input.Param(":sd") != "123" ||
  216. ctx.Input.Param(":account") != "account" {
  217. t.Fatal("get :info :num :id :sd :account param error")
  218. }
  219. ctx.Input.Reset(ctx)
  220. obj = t4.Match("/12/123/456/create", ctx)
  221. if obj == nil || obj.(string) != "astaxie" {
  222. t.Fatal("/:info:int/:num/:id/create can't get obj ")
  223. }
  224. }
  225. // Test for issue #1595
  226. func TestAddTree5(t *testing.T) {
  227. tr := NewTree()
  228. tr.AddRouter("/v1/shop/:id", "shopdetail")
  229. tr.AddRouter("/v1/shop/", "shophome")
  230. ctx := context.NewContext()
  231. obj := tr.Match("/v1/shop/", ctx)
  232. if obj == nil || obj.(string) != "shophome" {
  233. t.Fatal("url /v1/shop/ need match router /v1/shop/ ")
  234. }
  235. }
  236. func TestSplitPath(t *testing.T) {
  237. a := splitPath("")
  238. if len(a) != 0 {
  239. t.Fatal("/ should retrun []")
  240. }
  241. a = splitPath("/")
  242. if len(a) != 0 {
  243. t.Fatal("/ should retrun []")
  244. }
  245. a = splitPath("/admin")
  246. if len(a) != 1 || a[0] != "admin" {
  247. t.Fatal("/admin should retrun [admin]")
  248. }
  249. a = splitPath("/admin/")
  250. if len(a) != 1 || a[0] != "admin" {
  251. t.Fatal("/admin/ should retrun [admin]")
  252. }
  253. a = splitPath("/admin/users")
  254. if len(a) != 2 || a[0] != "admin" || a[1] != "users" {
  255. t.Fatal("/admin should retrun [admin users]")
  256. }
  257. a = splitPath("/admin/:id:int")
  258. if len(a) != 2 || a[0] != "admin" || a[1] != ":id:int" {
  259. t.Fatal("/admin should retrun [admin :id:int]")
  260. }
  261. }
  262. func TestSplitSegment(t *testing.T) {
  263. items := map[string]struct {
  264. isReg bool
  265. params []string
  266. regStr string
  267. }{
  268. "admin": {false, nil, ""},
  269. "*": {true, []string{":splat"}, ""},
  270. "*.*": {true, []string{".", ":path", ":ext"}, ""},
  271. ":id": {true, []string{":id"}, ""},
  272. "?:id": {true, []string{":", ":id"}, ""},
  273. ":id:int": {true, []string{":id"}, "([0-9]+)"},
  274. ":name:string": {true, []string{":name"}, `([\w]+)`},
  275. ":id([0-9]+)": {true, []string{":id"}, `([0-9]+)`},
  276. ":id([0-9]+)_:name": {true, []string{":id", ":name"}, `([0-9]+)_(.+)`},
  277. ":id(.+)_cms.html": {true, []string{":id"}, `(.+)_cms.html`},
  278. "cms_:id(.+)_:page(.+).html": {true, []string{":id", ":page"}, `cms_(.+)_(.+).html`},
  279. `:app(a|b|c)`: {true, []string{":app"}, `(a|b|c)`},
  280. `:app\((a|b|c)\)`: {true, []string{":app"}, `(.+)\((a|b|c)\)`},
  281. }
  282. for pattern, v := range items {
  283. b, w, r := splitSegment(pattern)
  284. if b != v.isReg || r != v.regStr || strings.Join(w, ",") != strings.Join(v.params, ",") {
  285. t.Fatalf("%s should return %t,%s,%q, got %t,%s,%q", pattern, v.isReg, v.params, v.regStr, b, w, r)
  286. }
  287. }
  288. }