xml_test.go 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. // Copyright 2014 The Go 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 webdav
  5. import (
  6. "encoding/xml"
  7. "net/http"
  8. "reflect"
  9. "strings"
  10. "testing"
  11. )
  12. func TestReadLockInfo(t *testing.T) {
  13. // The "section x.y.z" test cases come from section x.y.z of the spec at
  14. // http://www.webdav.org/specs/rfc4918.html
  15. testCases := []struct {
  16. desc string
  17. input string
  18. wantLI lockInfo
  19. wantStatus int
  20. }{{
  21. "bad: junk",
  22. "xxx",
  23. lockInfo{},
  24. http.StatusBadRequest,
  25. }, {
  26. "bad: invalid owner XML",
  27. "" +
  28. "<D:lockinfo xmlns:D='DAV:'>\n" +
  29. " <D:lockscope><D:exclusive/></D:lockscope>\n" +
  30. " <D:locktype><D:write/></D:locktype>\n" +
  31. " <D:owner>\n" +
  32. " <D:href> no end tag \n" +
  33. " </D:owner>\n" +
  34. "</D:lockinfo>",
  35. lockInfo{},
  36. http.StatusBadRequest,
  37. }, {
  38. "bad: invalid UTF-8",
  39. "" +
  40. "<D:lockinfo xmlns:D='DAV:'>\n" +
  41. " <D:lockscope><D:exclusive/></D:lockscope>\n" +
  42. " <D:locktype><D:write/></D:locktype>\n" +
  43. " <D:owner>\n" +
  44. " <D:href> \xff </D:href>\n" +
  45. " </D:owner>\n" +
  46. "</D:lockinfo>",
  47. lockInfo{},
  48. http.StatusBadRequest,
  49. }, {
  50. "bad: unfinished XML #1",
  51. "" +
  52. "<D:lockinfo xmlns:D='DAV:'>\n" +
  53. " <D:lockscope><D:exclusive/></D:lockscope>\n" +
  54. " <D:locktype><D:write/></D:locktype>\n",
  55. lockInfo{},
  56. http.StatusBadRequest,
  57. }, {
  58. "bad: unfinished XML #2",
  59. "" +
  60. "<D:lockinfo xmlns:D='DAV:'>\n" +
  61. " <D:lockscope><D:exclusive/></D:lockscope>\n" +
  62. " <D:locktype><D:write/></D:locktype>\n" +
  63. " <D:owner>\n",
  64. lockInfo{},
  65. http.StatusBadRequest,
  66. }, {
  67. "good: empty",
  68. "",
  69. lockInfo{},
  70. 0,
  71. }, {
  72. "good: plain-text owner",
  73. "" +
  74. "<D:lockinfo xmlns:D='DAV:'>\n" +
  75. " <D:lockscope><D:exclusive/></D:lockscope>\n" +
  76. " <D:locktype><D:write/></D:locktype>\n" +
  77. " <D:owner>gopher</D:owner>\n" +
  78. "</D:lockinfo>",
  79. lockInfo{
  80. XMLName: xml.Name{Space: "DAV:", Local: "lockinfo"},
  81. Exclusive: new(struct{}),
  82. Write: new(struct{}),
  83. Owner: owner{
  84. InnerXML: "gopher",
  85. },
  86. },
  87. 0,
  88. }, {
  89. "section 9.10.7",
  90. "" +
  91. "<D:lockinfo xmlns:D='DAV:'>\n" +
  92. " <D:lockscope><D:exclusive/></D:lockscope>\n" +
  93. " <D:locktype><D:write/></D:locktype>\n" +
  94. " <D:owner>\n" +
  95. " <D:href>http://example.org/~ejw/contact.html</D:href>\n" +
  96. " </D:owner>\n" +
  97. "</D:lockinfo>",
  98. lockInfo{
  99. XMLName: xml.Name{Space: "DAV:", Local: "lockinfo"},
  100. Exclusive: new(struct{}),
  101. Write: new(struct{}),
  102. Owner: owner{
  103. InnerXML: "\n <D:href>http://example.org/~ejw/contact.html</D:href>\n ",
  104. },
  105. },
  106. 0,
  107. }}
  108. for _, tc := range testCases {
  109. li, status, err := readLockInfo(strings.NewReader(tc.input))
  110. if tc.wantStatus != 0 {
  111. if err == nil {
  112. t.Errorf("%s: got nil error, want non-nil", tc.desc)
  113. continue
  114. }
  115. } else if err != nil {
  116. t.Errorf("%s: %v", tc.desc, err)
  117. continue
  118. }
  119. if !reflect.DeepEqual(li, tc.wantLI) || status != tc.wantStatus {
  120. t.Errorf("%s:\ngot lockInfo=%v, status=%v\nwant lockInfo=%v, status=%v",
  121. tc.desc, li, status, tc.wantLI, tc.wantStatus)
  122. continue
  123. }
  124. }
  125. }
  126. func TestReadPropfind(t *testing.T) {
  127. testCases := []struct {
  128. desc string
  129. input string
  130. wantPF propfind
  131. wantStatus int
  132. }{{
  133. desc: "propfind: propname",
  134. input: "" +
  135. "<A:propfind xmlns:A='DAV:'>\n" +
  136. " <A:propname/>\n" +
  137. "</A:propfind>",
  138. wantPF: propfind{
  139. XMLName: xml.Name{"DAV:", "propfind"},
  140. Propname: new(struct{}),
  141. },
  142. }, {
  143. desc: "propfind: empty body means allprop",
  144. input: "",
  145. wantPF: propfind{
  146. Allprop: new(struct{}),
  147. },
  148. }, {
  149. desc: "propfind: allprop",
  150. input: "" +
  151. "<A:propfind xmlns:A='DAV:'>\n" +
  152. " <A:allprop/>\n" +
  153. "</A:propfind>",
  154. wantPF: propfind{
  155. XMLName: xml.Name{"DAV:", "propfind"},
  156. Allprop: new(struct{}),
  157. },
  158. }, {
  159. desc: "propfind: allprop followed by include",
  160. input: "" +
  161. "<A:propfind xmlns:A='DAV:'>\n" +
  162. " <A:allprop/>\n" +
  163. " <A:include><A:displayname/></A:include>\n" +
  164. "</A:propfind>",
  165. wantPF: propfind{
  166. XMLName: xml.Name{"DAV:", "propfind"},
  167. Allprop: new(struct{}),
  168. Include: propnames{xml.Name{"DAV:", "displayname"}},
  169. },
  170. }, {
  171. desc: "propfind: include followed by allprop",
  172. input: "" +
  173. "<A:propfind xmlns:A='DAV:'>\n" +
  174. " <A:include><A:displayname/></A:include>\n" +
  175. " <A:allprop/>\n" +
  176. "</A:propfind>",
  177. wantPF: propfind{
  178. XMLName: xml.Name{"DAV:", "propfind"},
  179. Allprop: new(struct{}),
  180. Include: propnames{xml.Name{"DAV:", "displayname"}},
  181. },
  182. }, {
  183. desc: "propfind: propfind",
  184. input: "" +
  185. "<A:propfind xmlns:A='DAV:'>\n" +
  186. " <A:prop><A:displayname/></A:prop>\n" +
  187. "</A:propfind>",
  188. wantPF: propfind{
  189. XMLName: xml.Name{"DAV:", "propfind"},
  190. Prop: propnames{xml.Name{"DAV:", "displayname"}},
  191. },
  192. }, {
  193. desc: "propfind: prop with ignored comments",
  194. input: "" +
  195. "<A:propfind xmlns:A='DAV:'>\n" +
  196. " <A:prop>\n" +
  197. " <!-- ignore -->\n" +
  198. " <A:displayname><!-- ignore --></A:displayname>\n" +
  199. " </A:prop>\n" +
  200. "</A:propfind>",
  201. wantPF: propfind{
  202. XMLName: xml.Name{"DAV:", "propfind"},
  203. Prop: propnames{xml.Name{"DAV:", "displayname"}},
  204. },
  205. }, {
  206. desc: "propfind: propfind with ignored whitespace",
  207. input: "" +
  208. "<A:propfind xmlns:A='DAV:'>\n" +
  209. " <A:prop> <A:displayname/></A:prop>\n" +
  210. "</A:propfind>",
  211. wantPF: propfind{
  212. XMLName: xml.Name{"DAV:", "propfind"},
  213. Prop: propnames{xml.Name{"DAV:", "displayname"}},
  214. },
  215. }, {
  216. desc: "propfind: propfind with ignored mixed-content",
  217. input: "" +
  218. "<A:propfind xmlns:A='DAV:'>\n" +
  219. " <A:prop>foo<A:displayname/>bar</A:prop>\n" +
  220. "</A:propfind>",
  221. wantPF: propfind{
  222. XMLName: xml.Name{"DAV:", "propfind"},
  223. Prop: propnames{xml.Name{"DAV:", "displayname"}},
  224. },
  225. }, {
  226. desc: "propfind: propname with ignored element (section A.4)",
  227. input: "" +
  228. "<A:propfind xmlns:A='DAV:'>\n" +
  229. " <A:propname/>\n" +
  230. " <E:leave-out xmlns:E='E:'>*boss*</E:leave-out>\n" +
  231. "</A:propfind>",
  232. wantPF: propfind{
  233. XMLName: xml.Name{"DAV:", "propfind"},
  234. Propname: new(struct{}),
  235. },
  236. }, {
  237. desc: "propfind: bad: junk",
  238. input: "xxx",
  239. wantStatus: http.StatusBadRequest,
  240. }, {
  241. desc: "propfind: bad: propname and allprop (section A.3)",
  242. input: "" +
  243. "<A:propfind xmlns:A='DAV:'>\n" +
  244. " <A:propname/>" +
  245. " <A:allprop/>" +
  246. "</A:propfind>",
  247. wantStatus: http.StatusBadRequest,
  248. }, {
  249. desc: "propfind: bad: propname and prop",
  250. input: "" +
  251. "<A:propfind xmlns:A='DAV:'>\n" +
  252. " <A:prop><A:displayname/></A:prop>\n" +
  253. " <A:propname/>\n" +
  254. "</A:propfind>",
  255. wantStatus: http.StatusBadRequest,
  256. }, {
  257. desc: "propfind: bad: allprop and prop",
  258. input: "" +
  259. "<A:propfind xmlns:A='DAV:'>\n" +
  260. " <A:allprop/>\n" +
  261. " <A:prop><A:foo/><A:/prop>\n" +
  262. "</A:propfind>",
  263. wantStatus: http.StatusBadRequest,
  264. }, {
  265. desc: "propfind: bad: empty propfind with ignored element (section A.4)",
  266. input: "" +
  267. "<A:propfind xmlns:A='DAV:'>\n" +
  268. " <E:expired-props/>\n" +
  269. "</A:propfind>",
  270. wantStatus: http.StatusBadRequest,
  271. }, {
  272. desc: "propfind: bad: empty prop",
  273. input: "" +
  274. "<A:propfind xmlns:A='DAV:'>\n" +
  275. " <A:prop/>\n" +
  276. "</A:propfind>",
  277. wantStatus: http.StatusBadRequest,
  278. }, {
  279. desc: "propfind: bad: prop with just chardata",
  280. input: "" +
  281. "<A:propfind xmlns:A='DAV:'>\n" +
  282. " <A:prop>foo</A:prop>\n" +
  283. "</A:propfind>",
  284. wantStatus: http.StatusBadRequest,
  285. }, {
  286. desc: "bad: interrupted prop",
  287. input: "" +
  288. "<A:propfind xmlns:A='DAV:'>\n" +
  289. " <A:prop><A:foo></A:prop>\n",
  290. wantStatus: http.StatusBadRequest,
  291. }, {
  292. desc: "bad: malformed end element prop",
  293. input: "" +
  294. "<A:propfind xmlns:A='DAV:'>\n" +
  295. " <A:prop><A:foo/></A:bar></A:prop>\n",
  296. wantStatus: http.StatusBadRequest,
  297. }, {
  298. desc: "propfind: bad: property with chardata value",
  299. input: "" +
  300. "<A:propfind xmlns:A='DAV:'>\n" +
  301. " <A:prop><A:foo>bar</A:foo></A:prop>\n" +
  302. "</A:propfind>",
  303. wantStatus: http.StatusBadRequest,
  304. }, {
  305. desc: "propfind: bad: property with whitespace value",
  306. input: "" +
  307. "<A:propfind xmlns:A='DAV:'>\n" +
  308. " <A:prop><A:foo> </A:foo></A:prop>\n" +
  309. "</A:propfind>",
  310. wantStatus: http.StatusBadRequest,
  311. }, {
  312. desc: "propfind: bad: include without allprop",
  313. input: "" +
  314. "<A:propfind xmlns:A='DAV:'>\n" +
  315. " <A:include><A:foo/></A:include>\n" +
  316. "</A:propfind>",
  317. wantStatus: http.StatusBadRequest,
  318. }}
  319. for _, tc := range testCases {
  320. pf, status, err := readPropfind(strings.NewReader(tc.input))
  321. if tc.wantStatus != 0 {
  322. if err == nil {
  323. t.Errorf("%s: got nil error, want non-nil", tc.desc)
  324. continue
  325. }
  326. } else if err != nil {
  327. t.Errorf("%s: %v", tc.desc, err)
  328. continue
  329. }
  330. if !reflect.DeepEqual(pf, tc.wantPF) || status != tc.wantStatus {
  331. t.Errorf("%s:\ngot propfind=%v, status=%v\nwant propfind=%v, status=%v",
  332. tc.desc, pf, status, tc.wantPF, tc.wantStatus)
  333. continue
  334. }
  335. }
  336. }