test_base.py 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777
  1. # This file is part of Radicale Server - Calendar Server
  2. # Copyright © 2012-2016 Guillaume Ayoub
  3. #
  4. # This library is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This library is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with Radicale. If not, see <http://www.gnu.org/licenses/>.
  16. """
  17. Radicale tests with simple requests.
  18. """
  19. import logging
  20. import posixpath
  21. import shutil
  22. import tempfile
  23. from radicale import Application, config
  24. from . import BaseTest
  25. from .helpers import get_file_content
  26. class BaseRequestsMixIn:
  27. """Tests with simple requests."""
  28. def test_root(self):
  29. """GET request at "/"."""
  30. status, headers, answer = self.request("GET", "/")
  31. assert status == 200
  32. assert "Radicale works!" in answer
  33. # Test the creation of the collection
  34. self.request("MKCOL", "/calendar.ics/")
  35. self.request(
  36. "PUT", "/calendar.ics/", "BEGIN:VCALENDAR\r\nEND:VCALENDAR")
  37. status, headers, answer = self.request("GET", "/calendar.ics/")
  38. assert "BEGIN:VCALENDAR" in answer
  39. assert "END:VCALENDAR" in answer
  40. def test_add_event(self):
  41. """Add an event."""
  42. self.request("MKCOL", "/calendar.ics/")
  43. self.request(
  44. "PUT", "/calendar.ics/", "BEGIN:VCALENDAR\r\nEND:VCALENDAR")
  45. event = get_file_content("event1.ics")
  46. path = "/calendar.ics/event1.ics"
  47. status, headers, answer = self.request("PUT", path, event)
  48. assert status == 201
  49. status, headers, answer = self.request("GET", path)
  50. assert "ETag" in headers.keys()
  51. assert status == 200
  52. assert "VEVENT" in answer
  53. assert "Event" in answer
  54. assert "UID:event" in answer
  55. def test_add_todo(self):
  56. """Add a todo."""
  57. self.request("MKCOL", "/calendar.ics/")
  58. self.request(
  59. "PUT", "/calendar.ics/", "BEGIN:VCALENDAR\r\nEND:VCALENDAR")
  60. todo = get_file_content("todo1.ics")
  61. path = "/calendar.ics/todo1.ics"
  62. status, headers, answer = self.request("PUT", path, todo)
  63. assert status == 201
  64. status, headers, answer = self.request("GET", path)
  65. assert "ETag" in headers.keys()
  66. assert "VTODO" in answer
  67. assert "Todo" in answer
  68. assert "UID:todo" in answer
  69. def test_update(self):
  70. """Update an event."""
  71. self.request("MKCOL", "/calendar.ics/")
  72. self.request(
  73. "PUT", "/calendar.ics/", "BEGIN:VCALENDAR\r\nEND:VCALENDAR")
  74. event = get_file_content("event1.ics")
  75. path = "/calendar.ics/event1.ics"
  76. status, headers, answer = self.request("PUT", path, event)
  77. assert status == 201
  78. status, headers, answer = self.request("GET", path)
  79. assert "ETag" in headers.keys()
  80. assert status == 200
  81. assert "VEVENT" in answer
  82. assert "Event" in answer
  83. assert "UID:event" in answer
  84. assert "DTSTART;TZID=Europe/Paris:20130901T180000" in answer
  85. assert "DTEND;TZID=Europe/Paris:20130901T190000" in answer
  86. # Then we send another PUT request
  87. event = get_file_content("event1-prime.ics")
  88. status, headers, answer = self.request("PUT", path, event)
  89. assert status == 201
  90. status, headers, answer = self.request("GET", "/calendar.ics/")
  91. assert answer.count("BEGIN:VEVENT") == 1
  92. status, headers, answer = self.request("GET", path)
  93. assert "ETag" in headers.keys()
  94. assert status == 200
  95. assert "VEVENT" in answer
  96. assert "Event" in answer
  97. assert "UID:event" in answer
  98. assert "DTSTART;TZID=Europe/Paris:20130901T180000" not in answer
  99. assert "DTEND;TZID=Europe/Paris:20130901T190000" not in answer
  100. assert "DTSTART;TZID=Europe/Paris:20140901T180000" in answer
  101. assert "DTEND;TZID=Europe/Paris:20140901T210000" in answer
  102. def test_put_whole_collection(self):
  103. """Create and overwrite a whole collection."""
  104. event = get_file_content("event1.ics")
  105. status, headers, answer = self.request("PUT", "/calendar.ics/", event)
  106. assert status == 201
  107. status, headers, answer = self.request(
  108. "PUT", "/calendar.ics/event1.ics", event)
  109. assert status == 201
  110. # Overwrite
  111. status, headers, answer = self.request("PUT", "/calendar.ics/", event)
  112. assert status == 201
  113. status, headers, answer = self.request(
  114. "GET", "/calendar.ics/event1.ics")
  115. assert status == 404
  116. def test_delete(self):
  117. """Delete an event."""
  118. self.request("MKCOL", "/calendar.ics/")
  119. self.request(
  120. "PUT", "/calendar.ics/", "BEGIN:VCALENDAR\r\nEND:VCALENDAR")
  121. event = get_file_content("event1.ics")
  122. path = "/calendar.ics/event1.ics"
  123. status, headers, answer = self.request("PUT", path, event)
  124. # Then we send a DELETE request
  125. status, headers, answer = self.request("DELETE", path)
  126. assert status == 200
  127. assert "href>%s</" % path in answer
  128. status, headers, answer = self.request("GET", "/calendar.ics/")
  129. assert "VEVENT" not in answer
  130. def test_mkcalendar(self):
  131. """Make a calendar."""
  132. self.request("MKCALENDAR", "/calendar.ics/")
  133. status, headers, answer = self.request("GET", "/calendar.ics/")
  134. assert status == 200
  135. def test_move(self):
  136. """Move a item."""
  137. self.request("MKCALENDAR", "/calendar.ics/")
  138. event = get_file_content("event1.ics")
  139. path1 = "/calendar.ics/event1.ics"
  140. path2 = "/calendar.ics/event2.ics"
  141. status, headers, answer = self.request("PUT", path1, event)
  142. status, headers, answer = self.request(
  143. "MOVE", path1, HTTP_DESTINATION=path2, HTTP_HOST="")
  144. assert status == 201
  145. status, headers, answer = self.request("GET", path1)
  146. assert status == 404
  147. status, headers, answer = self.request("GET", path2)
  148. assert status == 200
  149. def test_head(self):
  150. status, headers, answer = self.request("HEAD", "/")
  151. assert status == 200
  152. def test_options(self):
  153. status, headers, answer = self.request("OPTIONS", "/")
  154. assert status == 200
  155. assert "DAV" in headers
  156. def test_delete_collection(self):
  157. """Delete a collection."""
  158. self.request("MKCOL", "/calendar.ics/")
  159. event = get_file_content("event1.ics")
  160. self.request("PUT", "/calendar.ics/event1.ics", event)
  161. status, headers, answer = self.request("DELETE", "/calendar.ics/")
  162. assert status == 200
  163. assert "href>/calendar.ics/</" in answer
  164. status, headers, answer = self.request("GET", "/calendar.ics/")
  165. assert status == 404
  166. def test_delete_root_collection(self):
  167. """Delete the root collection."""
  168. self.request("MKCOL", "/calendar.ics/")
  169. event = get_file_content("event1.ics")
  170. self.request("PUT", "/event1.ics", event)
  171. self.request("PUT", "/calendar.ics/event1.ics", event)
  172. status, headers, answer = self.request("DELETE", "/")
  173. assert status == 200
  174. assert "href>/</" in answer
  175. status, headers, answer = self.request("GET", "/calendar.ics/")
  176. assert status == 404
  177. status, headers, answer = self.request("GET", "/event1.ics")
  178. assert status == 404
  179. def test_propfind(self):
  180. calendar_path = "/calendar.ics/"
  181. self.request("MKCALENDAR", calendar_path)
  182. event = get_file_content("event1.ics")
  183. event_path = posixpath.join(calendar_path, "event.ics")
  184. self.request("PUT", event_path, event)
  185. status, headers, answer = self.request("PROPFIND", "/", HTTP_DEPTH="1")
  186. assert status == 207
  187. assert "href>/</" in answer
  188. assert "href>%s</" % calendar_path in answer
  189. status, headers, answer = self.request("PROPFIND", calendar_path, HTTP_DEPTH="1")
  190. assert status == 207
  191. assert "href>%s</" % calendar_path in answer
  192. assert "href>%s</" % event_path in answer
  193. def test_proppatch(self):
  194. """Write a property and read it back."""
  195. self.request("MKCALENDAR", "/calendar.ics/")
  196. proppatch = get_file_content("proppatch1.xml")
  197. status, headers, answer = self.request(
  198. "PROPPATCH", "/calendar.ics/", proppatch)
  199. assert status == 207
  200. assert "calendar-color" in answer
  201. assert "200 OK</status" in answer
  202. # Read property back
  203. propfind = get_file_content("propfind1.xml")
  204. status, headers, answer = self.request(
  205. "PROPFIND", "/calendar.ics/", propfind)
  206. assert status == 207
  207. assert ":calendar-color>#BADA55</" in answer
  208. assert "200 OK</status" in answer
  209. def test_multiple_events_with_same_uid(self):
  210. """Add two events with the same UID."""
  211. self.request("MKCOL", "/calendar.ics/")
  212. self.request("PUT", "/calendar.ics/", get_file_content("event2.ics"))
  213. status, headers, answer = self.request(
  214. "REPORT", "/calendar.ics/",
  215. """<?xml version="1.0" encoding="utf-8" ?>
  216. <C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav">
  217. <D:prop xmlns:D="DAV:"><D:getetag/></D:prop>
  218. </C:calendar-query>""")
  219. assert answer.count("<getetag>") == 1
  220. status, headers, answer = self.request("GET", "/calendar.ics/")
  221. assert answer.count("BEGIN:VEVENT") == 2
  222. def _test_filter(self, filters, kind="event", items=1):
  223. filters_text = "".join(
  224. "<C:filter>%s</C:filter>" % filter_ for filter_ in filters)
  225. self.request("MKCOL", "/calendar.ics/")
  226. self.request(
  227. "PUT", "/calendar.ics/", "BEGIN:VCALENDAR\r\nEND:VCALENDAR")
  228. for i in range(items):
  229. filename = "{}{}.ics".format(kind, i + 1)
  230. event = get_file_content(filename)
  231. self.request("PUT", "/calendar.ics/{}".format(filename), event)
  232. status, headers, answer = self.request(
  233. "REPORT", "/calendar.ics",
  234. """<?xml version="1.0" encoding="utf-8" ?>
  235. <C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav">
  236. <D:prop xmlns:D="DAV:">
  237. <D:getetag/>
  238. </D:prop>
  239. %s
  240. </C:calendar-query>""" % filters_text)
  241. return answer
  242. def test_calendar_tag_filter(self):
  243. """Report request with tag-based filter on calendar."""
  244. assert "href>/calendar.ics/event1.ics</" in self._test_filter(["""
  245. <C:comp-filter name="VCALENDAR"></C:comp-filter>"""])
  246. def test_item_tag_filter(self):
  247. """Report request with tag-based filter on an item."""
  248. assert "href>/calendar.ics/event1.ics</" in self._test_filter(["""
  249. <C:comp-filter name="VCALENDAR">
  250. <C:comp-filter name="VEVENT"></C:comp-filter>
  251. </C:comp-filter>"""])
  252. assert "href>/calendar.ics/event1.ics</" not in self._test_filter(["""
  253. <C:comp-filter name="VCALENDAR">
  254. <C:comp-filter name="VTODO"></C:comp-filter>
  255. </C:comp-filter>"""])
  256. def test_item_not_tag_filter(self):
  257. """Report request with tag-based is-not filter on an item."""
  258. assert "href>/calendar.ics/event1.ics</" not in self._test_filter(["""
  259. <C:comp-filter name="VCALENDAR">
  260. <C:comp-filter name="VEVENT">
  261. <C:is-not-defined />
  262. </C:comp-filter>
  263. </C:comp-filter>"""])
  264. assert "href>/calendar.ics/event1.ics</" in self._test_filter(["""
  265. <C:comp-filter name="VCALENDAR">
  266. <C:comp-filter name="VTODO">
  267. <C:is-not-defined />
  268. </C:comp-filter>
  269. </C:comp-filter>"""])
  270. def test_item_prop_filter(self):
  271. """Report request with prop-based filter on an item."""
  272. assert "href>/calendar.ics/event1.ics</" in self._test_filter(["""
  273. <C:comp-filter name="VCALENDAR">
  274. <C:comp-filter name="VEVENT">
  275. <C:prop-filter name="SUMMARY"></C:prop-filter>
  276. </C:comp-filter>
  277. </C:comp-filter>"""])
  278. assert "href>/calendar.ics/event1.ics</" not in self._test_filter(["""
  279. <C:comp-filter name="VCALENDAR">
  280. <C:comp-filter name="VEVENT">
  281. <C:prop-filter name="UNKNOWN"></C:prop-filter>
  282. </C:comp-filter>
  283. </C:comp-filter>"""])
  284. def test_item_not_prop_filter(self):
  285. """Report request with prop-based is-not filter on an item."""
  286. assert "href>/calendar.ics/event1.ics</" not in self._test_filter(["""
  287. <C:comp-filter name="VCALENDAR">
  288. <C:comp-filter name="VEVENT">
  289. <C:prop-filter name="SUMMARY">
  290. <C:is-not-defined />
  291. </C:prop-filter>
  292. </C:comp-filter>
  293. </C:comp-filter>"""])
  294. assert "href>/calendar.ics/event1.ics</" in self._test_filter(["""
  295. <C:comp-filter name="VCALENDAR">
  296. <C:comp-filter name="VEVENT">
  297. <C:prop-filter name="UNKNOWN">
  298. <C:is-not-defined />
  299. </C:prop-filter>
  300. </C:comp-filter>
  301. </C:comp-filter>"""])
  302. def test_mutiple_filters(self):
  303. """Report request with multiple filters on an item."""
  304. assert "href>/calendar.ics/event1.ics</" not in self._test_filter(["""
  305. <C:comp-filter name="VCALENDAR">
  306. <C:comp-filter name="VEVENT">
  307. <C:prop-filter name="SUMMARY">
  308. <C:is-not-defined />
  309. </C:prop-filter>
  310. </C:comp-filter>
  311. </C:comp-filter>""", """
  312. <C:comp-filter name="VCALENDAR">
  313. <C:comp-filter name="VEVENT">
  314. <C:prop-filter name="UNKNOWN">
  315. <C:is-not-defined />
  316. </C:prop-filter>
  317. </C:comp-filter>
  318. </C:comp-filter>"""])
  319. assert "href>/calendar.ics/event1.ics</" in self._test_filter(["""
  320. <C:comp-filter name="VCALENDAR">
  321. <C:comp-filter name="VEVENT">
  322. <C:prop-filter name="SUMMARY"></C:prop-filter>
  323. </C:comp-filter>
  324. </C:comp-filter>""", """
  325. <C:comp-filter name="VCALENDAR">
  326. <C:comp-filter name="VEVENT">
  327. <C:prop-filter name="UNKNOWN">
  328. <C:is-not-defined />
  329. </C:prop-filter>
  330. </C:comp-filter>
  331. </C:comp-filter>"""])
  332. assert "href>/calendar.ics/event1.ics</" in self._test_filter(["""
  333. <C:comp-filter name="VCALENDAR">
  334. <C:comp-filter name="VEVENT">
  335. <C:prop-filter name="SUMMARY"></C:prop-filter>
  336. <C:prop-filter name="UNKNOWN">
  337. <C:is-not-defined />
  338. </C:prop-filter>
  339. </C:comp-filter>
  340. </C:comp-filter>"""])
  341. def test_text_match_filter(self):
  342. """Report request with text-match filter on calendar."""
  343. assert "href>/calendar.ics/event1.ics</" in self._test_filter(["""
  344. <C:comp-filter name="VCALENDAR">
  345. <C:comp-filter name="VEVENT">
  346. <C:prop-filter name="SUMMARY">
  347. <C:text-match>event</C:text-match>
  348. </C:prop-filter>
  349. </C:comp-filter>
  350. </C:comp-filter>"""])
  351. assert "href>/calendar.ics/event1.ics</" not in self._test_filter(["""
  352. <C:comp-filter name="VCALENDAR">
  353. <C:comp-filter name="VEVENT">
  354. <C:prop-filter name="UNKNOWN">
  355. <C:text-match>event</C:text-match>
  356. </C:prop-filter>
  357. </C:comp-filter>
  358. </C:comp-filter>"""])
  359. assert "href>/calendar.ics/event1.ics</" not in self._test_filter(["""
  360. <C:comp-filter name="VCALENDAR">
  361. <C:comp-filter name="VEVENT">
  362. <C:prop-filter name="SUMMARY">
  363. <C:text-match>unknown</C:text-match>
  364. </C:prop-filter>
  365. </C:comp-filter>
  366. </C:comp-filter>"""])
  367. assert "href>/calendar.ics/event1.ics</" not in self._test_filter(["""
  368. <C:comp-filter name="VCALENDAR">
  369. <C:comp-filter name="VEVENT">
  370. <C:prop-filter name="SUMMARY">
  371. <C:text-match negate-condition="yes">event</C:text-match>
  372. </C:prop-filter>
  373. </C:comp-filter>
  374. </C:comp-filter>"""])
  375. def test_param_filter(self):
  376. """Report request with param-filter on calendar."""
  377. assert "href>/calendar.ics/event1.ics</" in self._test_filter(["""
  378. <C:comp-filter name="VCALENDAR">
  379. <C:comp-filter name="VEVENT">
  380. <C:prop-filter name="ATTENDEE">
  381. <C:param-filter name="PARTSTAT">
  382. <C:text-match collation="i;ascii-casemap"
  383. >ACCEPTED</C:text-match>
  384. </C:param-filter>
  385. </C:prop-filter>
  386. </C:comp-filter>
  387. </C:comp-filter>"""])
  388. assert "href>/calendar.ics/event1.ics</" not in self._test_filter(["""
  389. <C:comp-filter name="VCALENDAR">
  390. <C:comp-filter name="VEVENT">
  391. <C:prop-filter name="ATTENDEE">
  392. <C:param-filter name="PARTSTAT">
  393. <C:text-match collation="i;ascii-casemap"
  394. >UNKNOWN</C:text-match>
  395. </C:param-filter>
  396. </C:prop-filter>
  397. </C:comp-filter>
  398. </C:comp-filter>"""])
  399. assert "href>/calendar.ics/event1.ics</" not in self._test_filter(["""
  400. <C:comp-filter name="VCALENDAR">
  401. <C:comp-filter name="VEVENT">
  402. <C:prop-filter name="ATTENDEE">
  403. <C:param-filter name="PARTSTAT">
  404. <C:is-not-defined />
  405. </C:param-filter>
  406. </C:prop-filter>
  407. </C:comp-filter>
  408. </C:comp-filter>"""])
  409. assert "href>/calendar.ics/event1.ics</" in self._test_filter(["""
  410. <C:comp-filter name="VCALENDAR">
  411. <C:comp-filter name="VEVENT">
  412. <C:prop-filter name="ATTENDEE">
  413. <C:param-filter name="UNKNOWN">
  414. <C:is-not-defined />
  415. </C:param-filter>
  416. </C:prop-filter>
  417. </C:comp-filter>
  418. </C:comp-filter>"""])
  419. def test_time_range_filter_events(self):
  420. """Report request with time-range filter on events."""
  421. answer = self._test_filter(["""
  422. <C:comp-filter name="VCALENDAR">
  423. <C:comp-filter name="VEVENT">
  424. <C:time-range start="20130801T000000Z" end="20131001T000000Z"/>
  425. </C:comp-filter>
  426. </C:comp-filter>"""], "event", items=5)
  427. assert "href>/calendar.ics/event1.ics</" in answer
  428. assert "href>/calendar.ics/event2.ics</" in answer
  429. assert "href>/calendar.ics/event3.ics</" in answer
  430. assert "href>/calendar.ics/event4.ics</" in answer
  431. assert "href>/calendar.ics/event5.ics</" in answer
  432. answer = self._test_filter(["""
  433. <C:comp-filter name="VCALENDAR">
  434. <C:comp-filter name="VEVENT">
  435. <C:prop-filter name="ATTENDEE">
  436. <C:param-filter name="PARTSTAT">
  437. <C:is-not-defined />
  438. </C:param-filter>
  439. </C:prop-filter>
  440. <C:time-range start="20130801T000000Z" end="20131001T000000Z"/>
  441. </C:comp-filter>
  442. </C:comp-filter>"""], items=5)
  443. assert "href>/calendar.ics/event1.ics</" not in answer
  444. assert "href>/calendar.ics/event2.ics</" not in answer
  445. assert "href>/calendar.ics/event3.ics</" not in answer
  446. assert "href>/calendar.ics/event4.ics</" not in answer
  447. assert "href>/calendar.ics/event5.ics</" not in answer
  448. answer = self._test_filter(["""
  449. <C:comp-filter name="VCALENDAR">
  450. <C:comp-filter name="VEVENT">
  451. <C:time-range start="20130902T000000Z" end="20131001T000000Z"/>
  452. </C:comp-filter>
  453. </C:comp-filter>"""], items=5)
  454. assert "href>/calendar.ics/event1.ics</" not in answer
  455. assert "href>/calendar.ics/event2.ics</" in answer
  456. assert "href>/calendar.ics/event3.ics</" in answer
  457. assert "href>/calendar.ics/event4.ics</" in answer
  458. assert "href>/calendar.ics/event5.ics</" in answer
  459. answer = self._test_filter(["""
  460. <C:comp-filter name="VCALENDAR">
  461. <C:comp-filter name="VEVENT">
  462. <C:time-range start="20130903T000000Z" end="20130908T000000Z"/>
  463. </C:comp-filter>
  464. </C:comp-filter>"""], items=5)
  465. assert "href>/calendar.ics/event1.ics</" not in answer
  466. assert "href>/calendar.ics/event2.ics</" not in answer
  467. assert "href>/calendar.ics/event3.ics</" in answer
  468. assert "href>/calendar.ics/event4.ics</" in answer
  469. assert "href>/calendar.ics/event5.ics</" in answer
  470. answer = self._test_filter(["""
  471. <C:comp-filter name="VCALENDAR">
  472. <C:comp-filter name="VEVENT">
  473. <C:time-range start="20130903T000000Z" end="20130904T000000Z"/>
  474. </C:comp-filter>
  475. </C:comp-filter>"""], items=5)
  476. assert "href>/calendar.ics/event1.ics</" not in answer
  477. assert "href>/calendar.ics/event2.ics</" not in answer
  478. assert "href>/calendar.ics/event3.ics</" in answer
  479. assert "href>/calendar.ics/event4.ics</" not in answer
  480. assert "href>/calendar.ics/event5.ics</" not in answer
  481. answer = self._test_filter(["""
  482. <C:comp-filter name="VCALENDAR">
  483. <C:comp-filter name="VEVENT">
  484. <C:time-range start="20130805T000000Z" end="20130810T000000Z"/>
  485. </C:comp-filter>
  486. </C:comp-filter>"""], items=5)
  487. assert "href>/calendar.ics/event1.ics</" not in answer
  488. assert "href>/calendar.ics/event2.ics</" not in answer
  489. assert "href>/calendar.ics/event3.ics</" not in answer
  490. assert "href>/calendar.ics/event4.ics</" not in answer
  491. assert "href>/calendar.ics/event5.ics</" not in answer
  492. def test_time_range_filter_events_rrule(self):
  493. """Report request with time-range filter on events with rrules."""
  494. answer = self._test_filter(["""
  495. <C:comp-filter name="VCALENDAR">
  496. <C:comp-filter name="VEVENT">
  497. <C:time-range start="20130801T000000Z" end="20131001T000000Z"/>
  498. </C:comp-filter>
  499. </C:comp-filter>"""], "event", items=2)
  500. assert "href>/calendar.ics/event1.ics</" in answer
  501. assert "href>/calendar.ics/event2.ics</" in answer
  502. answer = self._test_filter(["""
  503. <C:comp-filter name="VCALENDAR">
  504. <C:comp-filter name="VEVENT">
  505. <C:time-range start="20140801T000000Z" end="20141001T000000Z"/>
  506. </C:comp-filter>
  507. </C:comp-filter>"""], "event", items=2)
  508. assert "href>/calendar.ics/event1.ics</" not in answer
  509. assert "href>/calendar.ics/event2.ics</" in answer
  510. answer = self._test_filter(["""
  511. <C:comp-filter name="VCALENDAR">
  512. <C:comp-filter name="VEVENT">
  513. <C:time-range start="20120801T000000Z" end="20121001T000000Z"/>
  514. </C:comp-filter>
  515. </C:comp-filter>"""], "event", items=2)
  516. assert "href>/calendar.ics/event1.ics</" not in answer
  517. assert "href>/calendar.ics/event2.ics</" not in answer
  518. answer = self._test_filter(["""
  519. <C:comp-filter name="VCALENDAR">
  520. <C:comp-filter name="VEVENT">
  521. <C:time-range start="20130903T000000Z" end="20130907T000000Z"/>
  522. </C:comp-filter>
  523. </C:comp-filter>"""], "event", items=2)
  524. assert "href>/calendar.ics/event1.ics</" not in answer
  525. assert "href>/calendar.ics/event2.ics</" not in answer
  526. def test_time_range_filter_todos(self):
  527. """Report request with time-range filter on todos."""
  528. answer = self._test_filter(["""
  529. <C:comp-filter name="VCALENDAR">
  530. <C:comp-filter name="VTODO">
  531. <C:time-range start="20130801T000000Z" end="20131001T000000Z"/>
  532. </C:comp-filter>
  533. </C:comp-filter>"""], "todo", items=8)
  534. assert "href>/calendar.ics/todo1.ics</" in answer
  535. assert "href>/calendar.ics/todo2.ics</" in answer
  536. assert "href>/calendar.ics/todo3.ics</" in answer
  537. assert "href>/calendar.ics/todo4.ics</" in answer
  538. assert "href>/calendar.ics/todo5.ics</" in answer
  539. assert "href>/calendar.ics/todo6.ics</" in answer
  540. assert "href>/calendar.ics/todo7.ics</" in answer
  541. assert "href>/calendar.ics/todo8.ics</" in answer
  542. answer = self._test_filter(["""
  543. <C:comp-filter name="VCALENDAR">
  544. <C:comp-filter name="VTODO">
  545. <C:time-range start="20130901T160000Z" end="20130901T183000Z"/>
  546. </C:comp-filter>
  547. </C:comp-filter>"""], "todo", items=8)
  548. assert "href>/calendar.ics/todo1.ics</" not in answer
  549. assert "href>/calendar.ics/todo2.ics</" in answer
  550. assert "href>/calendar.ics/todo3.ics</" in answer
  551. assert "href>/calendar.ics/todo4.ics</" not in answer
  552. assert "href>/calendar.ics/todo5.ics</" not in answer
  553. assert "href>/calendar.ics/todo6.ics</" not in answer
  554. assert "href>/calendar.ics/todo7.ics</" in answer
  555. assert "href>/calendar.ics/todo8.ics</" in answer
  556. answer = self._test_filter(["""
  557. <C:comp-filter name="VCALENDAR">
  558. <C:comp-filter name="VTODO">
  559. <C:time-range start="20130903T160000Z" end="20130901T183000Z"/>
  560. </C:comp-filter>
  561. </C:comp-filter>"""], "todo", items=8)
  562. assert "href>/calendar.ics/todo2.ics</" not in answer
  563. answer = self._test_filter(["""
  564. <C:comp-filter name="VCALENDAR">
  565. <C:comp-filter name="VTODO">
  566. <C:time-range start="20130903T160000Z" end="20130901T173000Z"/>
  567. </C:comp-filter>
  568. </C:comp-filter>"""], "todo", items=8)
  569. assert "href>/calendar.ics/todo2.ics</" not in answer
  570. answer = self._test_filter(["""
  571. <C:comp-filter name="VCALENDAR">
  572. <C:comp-filter name="VTODO">
  573. <C:time-range start="20130903T160000Z" end="20130903T173000Z"/>
  574. </C:comp-filter>
  575. </C:comp-filter>"""], "todo", items=8)
  576. assert "href>/calendar.ics/todo3.ics</" not in answer
  577. answer = self._test_filter(["""
  578. <C:comp-filter name="VCALENDAR">
  579. <C:comp-filter name="VTODO">
  580. <C:time-range start="20130903T160000Z" end="20130803T203000Z"/>
  581. </C:comp-filter>
  582. </C:comp-filter>"""], "todo", items=8)
  583. assert "href>/calendar.ics/todo7.ics</" in answer
  584. def test_time_range_filter_todos_rrule(self):
  585. """Report request with time-range filter on todos with rrules."""
  586. answer = self._test_filter(["""
  587. <C:comp-filter name="VCALENDAR">
  588. <C:comp-filter name="VTODO">
  589. <C:time-range start="20130801T000000Z" end="20131001T000000Z"/>
  590. </C:comp-filter>
  591. </C:comp-filter>"""], "todo", items=2)
  592. assert "href>/calendar.ics/todo1.ics</" in answer
  593. assert "href>/calendar.ics/todo2.ics</" in answer
  594. answer = self._test_filter(["""
  595. <C:comp-filter name="VCALENDAR">
  596. <C:comp-filter name="VTODO">
  597. <C:time-range start="20140801T000000Z" end="20141001T000000Z"/>
  598. </C:comp-filter>
  599. </C:comp-filter>"""], "todo", items=2)
  600. assert "href>/calendar.ics/todo1.ics</" not in answer
  601. assert "href>/calendar.ics/todo2.ics</" in answer
  602. answer = self._test_filter(["""
  603. <C:comp-filter name="VCALENDAR">
  604. <C:comp-filter name="VTODO">
  605. <C:time-range start="20140902T000000Z" end="20140903T000000Z"/>
  606. </C:comp-filter>
  607. </C:comp-filter>"""], "todo", items=2)
  608. assert "href>/calendar.ics/todo1.ics</" not in answer
  609. assert "href>/calendar.ics/todo2.ics</" in answer
  610. answer = self._test_filter(["""
  611. <C:comp-filter name="VCALENDAR">
  612. <C:comp-filter name="VTODO">
  613. <C:time-range start="20140904T000000Z" end="20140914T000000Z"/>
  614. </C:comp-filter>
  615. </C:comp-filter>"""], "todo", items=2)
  616. assert "href>/calendar.ics/todo1.ics</" not in answer
  617. assert "href>/calendar.ics/todo2.ics</" not in answer
  618. def test_time_range_filter_journals(self):
  619. """Report request with time-range filter on journals."""
  620. answer = self._test_filter(["""
  621. <C:comp-filter name="VCALENDAR">
  622. <C:comp-filter name="VJOURNAL">
  623. <C:time-range start="19991229T000000Z" end="20000202T000000Z"/>
  624. </C:comp-filter>
  625. </C:comp-filter>"""], "journal", items=3)
  626. assert "href>/calendar.ics/journal1.ics</" not in answer
  627. assert "href>/calendar.ics/journal2.ics</" in answer
  628. assert "href>/calendar.ics/journal3.ics</" in answer
  629. answer = self._test_filter(["""
  630. <C:comp-filter name="VCALENDAR">
  631. <C:comp-filter name="VJOURNAL">
  632. <C:time-range start="19991229T000000Z" end="20000202T000000Z"/>
  633. </C:comp-filter>
  634. </C:comp-filter>"""], "journal", items=3)
  635. assert "href>/calendar.ics/journal1.ics</" not in answer
  636. assert "href>/calendar.ics/journal2.ics</" in answer
  637. assert "href>/calendar.ics/journal3.ics</" in answer
  638. answer = self._test_filter(["""
  639. <C:comp-filter name="VCALENDAR">
  640. <C:comp-filter name="VJOURNAL">
  641. <C:time-range start="19981229T000000Z" end="19991012T000000Z"/>
  642. </C:comp-filter>
  643. </C:comp-filter>"""], "journal", items=3)
  644. assert "href>/calendar.ics/journal1.ics</" not in answer
  645. assert "href>/calendar.ics/journal2.ics</" not in answer
  646. assert "href>/calendar.ics/journal3.ics</" not in answer
  647. answer = self._test_filter(["""
  648. <C:comp-filter name="VCALENDAR">
  649. <C:comp-filter name="VJOURNAL">
  650. <C:time-range start="20131229T000000Z" end="21520202T000000Z"/>
  651. </C:comp-filter>
  652. </C:comp-filter>"""], "journal", items=3)
  653. assert "href>/calendar.ics/journal1.ics</" not in answer
  654. assert "href>/calendar.ics/journal2.ics</" in answer
  655. assert "href>/calendar.ics/journal3.ics</" not in answer
  656. answer = self._test_filter(["""
  657. <C:comp-filter name="VCALENDAR">
  658. <C:comp-filter name="VJOURNAL">
  659. <C:time-range start="20000101T000000Z" end="20000202T000000Z"/>
  660. </C:comp-filter>
  661. </C:comp-filter>"""], "journal", items=3)
  662. assert "href>/calendar.ics/journal1.ics</" not in answer
  663. assert "href>/calendar.ics/journal2.ics</" in answer
  664. assert "href>/calendar.ics/journal3.ics</" in answer
  665. def test_time_range_filter_journals_rrule(self):
  666. """Report request with time-range filter on journals with rrules."""
  667. answer = self._test_filter(["""
  668. <C:comp-filter name="VCALENDAR">
  669. <C:comp-filter name="VJOURNAL">
  670. <C:time-range start="19991229T000000Z" end="20000202T000000Z"/>
  671. </C:comp-filter>
  672. </C:comp-filter>"""], "journal", items=2)
  673. assert "href>/calendar.ics/journal1.ics</" not in answer
  674. assert "href>/calendar.ics/journal2.ics</" in answer
  675. answer = self._test_filter(["""
  676. <C:comp-filter name="VCALENDAR">
  677. <C:comp-filter name="VJOURNAL">
  678. <C:time-range start="20051229T000000Z" end="20060202T000000Z"/>
  679. </C:comp-filter>
  680. </C:comp-filter>"""], "journal", items=2)
  681. assert "href>/calendar.ics/journal1.ics</" not in answer
  682. assert "href>/calendar.ics/journal2.ics</" in answer
  683. answer = self._test_filter(["""
  684. <C:comp-filter name="VCALENDAR">
  685. <C:comp-filter name="VJOURNAL">
  686. <C:time-range start="20060102T000000Z" end="20060202T000000Z"/>
  687. </C:comp-filter>
  688. </C:comp-filter>"""], "journal", items=2)
  689. assert "href>/calendar.ics/journal1.ics</" not in answer
  690. assert "href>/calendar.ics/journal2.ics</" not in answer
  691. def test_principal_collection_creation(self):
  692. """Verify existence of the principal collection."""
  693. status, headers, answer = self.request(
  694. "GET", "/user/", REMOTE_USER="user")
  695. assert status == 200
  696. def test_existence_of_root_collections(self):
  697. """Verify that the root collection always exists."""
  698. # Use PROPFIND because GET returns message
  699. status, headers, answer = self.request("PROPFIND", "/")
  700. assert status == 207
  701. # it should still exist after deletion
  702. self.request("DELETE", "/")
  703. status, headers, answer = self.request("PROPFIND", "/")
  704. assert status == 207
  705. class BaseFileSystemTest(BaseTest):
  706. """Base class for filesystem backend tests."""
  707. storage_type = None
  708. def setup(self):
  709. self.configuration = config.load()
  710. self.configuration.set("storage", "type", self.storage_type)
  711. self.logger = logging.getLogger("radicale_test")
  712. self.colpath = tempfile.mkdtemp()
  713. self.configuration.set("storage", "filesystem_folder", self.colpath)
  714. # Disable syncing to disk for better performance
  715. self.configuration.set("storage", "fsync", "False")
  716. self.application = Application(self.configuration, self.logger)
  717. def teardown(self):
  718. shutil.rmtree(self.colpath)
  719. class TestMultiFileSystem(BaseFileSystemTest, BaseRequestsMixIn):
  720. """Test BaseRequests on multifilesystem."""
  721. storage_type = "multifilesystem"
  722. class TestCustomStorageSystem(BaseFileSystemTest):
  723. """Test custom backend loading."""
  724. storage_type = "tests.custom.storage"
  725. def test_root(self):
  726. """A simple test to verify that the custom backend works."""
  727. BaseRequestsMixIn.test_root(self)