test_expand.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. # This file is part of Radicale - CalDAV and CardDAV server
  2. # Copyright © 2012-2017 Guillaume Ayoub
  3. # Copyright © 2017-2019 Unrud <unrud@outlook.com>
  4. #
  5. # This library is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, either version 3 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This library is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with Radicale. If not, see <http://www.gnu.org/licenses/>.
  17. """
  18. Radicale tests with expand requests.
  19. """
  20. import os
  21. import posixpath
  22. from typing import Any, Callable, ClassVar, Iterable, List, Optional, Tuple
  23. import defusedxml.ElementTree as DefusedET
  24. import vobject
  25. from radicale import storage, xmlutils
  26. from radicale.tests import RESPONSES, BaseTest
  27. from radicale.tests.helpers import get_file_content
  28. class TestExpandRequests(BaseTest):
  29. """Tests with expand requests."""
  30. # Allow skipping sync-token tests, when not fully supported by the backend
  31. full_sync_token_support: ClassVar[bool] = True
  32. def setup_method(self) -> None:
  33. BaseTest.setup_method(self)
  34. rights_file_path = os.path.join(self.colpath, "rights")
  35. with open(rights_file_path, "w") as f:
  36. f.write("""\
  37. [permit delete collection]
  38. user: .*
  39. collection: test-permit-delete
  40. permissions: RrWwD
  41. [forbid delete collection]
  42. user: .*
  43. collection: test-forbid-delete
  44. permissions: RrWwd
  45. [permit overwrite collection]
  46. user: .*
  47. collection: test-permit-overwrite
  48. permissions: RrWwO
  49. [forbid overwrite collection]
  50. user: .*
  51. collection: test-forbid-overwrite
  52. permissions: RrWwo
  53. [allow all]
  54. user: .*
  55. collection: .*
  56. permissions: RrWw""")
  57. self.configure({"rights": {"file": rights_file_path,
  58. "type": "from_file"}})
  59. def test_report_with_expand_property(self) -> None:
  60. """Test report with expand property"""
  61. self.put("/calendar.ics/", get_file_content("event_daily_rrule.ics"))
  62. req_body_without_expand = \
  63. """<?xml version="1.0" encoding="utf-8" ?>
  64. <C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
  65. <D:prop>
  66. <C:calendar-data>
  67. </C:calendar-data>
  68. </D:prop>
  69. <C:filter>
  70. <C:comp-filter name="VCALENDAR">
  71. <C:comp-filter name="VEVENT">
  72. <C:time-range start="20060103T000000Z" end="20060105T000000Z"/>
  73. </C:comp-filter>
  74. </C:comp-filter>
  75. </C:filter>
  76. </C:calendar-query>
  77. """
  78. _, responses = self.report("/calendar.ics/", req_body_without_expand)
  79. assert len(responses) == 1
  80. response_without_expand = responses['/calendar.ics/event_daily_rrule.ics']
  81. assert not isinstance(response_without_expand, int)
  82. status, element = response_without_expand["C:calendar-data"]
  83. assert status == 200 and element.text
  84. assert "RRULE" in element.text
  85. assert "BEGIN:VTIMEZONE" in element.text
  86. assert "RECURRENCE-ID" not in element.text
  87. uids: List[str] = []
  88. for line in element.text.split("\n"):
  89. if line.startswith("UID:"):
  90. uid = line[len("UID:"):]
  91. assert uid == "event_daily_rrule"
  92. uids.append(uid)
  93. assert len(uids) == 1
  94. req_body_with_expand = \
  95. """<?xml version="1.0" encoding="utf-8" ?>
  96. <C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
  97. <D:prop>
  98. <C:calendar-data>
  99. <C:expand start="20060103T000000Z" end="20060105T000000Z"/>
  100. </C:calendar-data>
  101. </D:prop>
  102. <C:filter>
  103. <C:comp-filter name="VCALENDAR">
  104. <C:comp-filter name="VEVENT">
  105. <C:time-range start="20060103T000000Z" end="20060105T000000Z"/>
  106. </C:comp-filter>
  107. </C:comp-filter>
  108. </C:filter>
  109. </C:calendar-query>
  110. """
  111. _, responses = self.report("/calendar.ics/", req_body_with_expand)
  112. assert len(responses) == 1
  113. response_with_expand = responses['/calendar.ics/event_daily_rrule.ics']
  114. assert not isinstance(response_with_expand, int)
  115. status, element = response_with_expand["C:calendar-data"]
  116. assert status == 200 and element.text
  117. assert "RRULE" not in element.text
  118. assert "BEGIN:VTIMEZONE" not in element.text
  119. uids = []
  120. recurrence_ids = []
  121. for line in element.text.split("\n"):
  122. if line.startswith("UID:"):
  123. assert line == "UID:event_daily_rrule"
  124. uids.append(line)
  125. if line.startswith("RECURRENCE-ID:"):
  126. assert line in ["RECURRENCE-ID:20060103T170000Z", "RECURRENCE-ID:20060104T170000Z"]
  127. recurrence_ids.append(line)
  128. if line.startswith("DTSTART:"):
  129. assert line in ["DTSTART:20060103T170000Z", "DTSTART:20060104T170000Z"]
  130. assert len(uids) == 2
  131. assert len(set(recurrence_ids)) == 2
  132. def test_report_with_expand_property_all_day_event(self) -> None:
  133. """Test report with expand property"""
  134. self.put("/calendar.ics/", get_file_content("event_full_day_rrule.ics"))
  135. req_body_without_expand = \
  136. """<?xml version="1.0" encoding="utf-8" ?>
  137. <C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
  138. <D:prop>
  139. <C:calendar-data>
  140. </C:calendar-data>
  141. </D:prop>
  142. <C:filter>
  143. <C:comp-filter name="VCALENDAR">
  144. <C:comp-filter name="VEVENT">
  145. <C:time-range start="20060103T000000Z" end="20060105T000000Z"/>
  146. </C:comp-filter>
  147. </C:comp-filter>
  148. </C:filter>
  149. </C:calendar-query>
  150. """
  151. _, responses = self.report("/calendar.ics/", req_body_without_expand)
  152. assert len(responses) == 1
  153. response_without_expand = responses['/calendar.ics/event_full_day_rrule.ics']
  154. assert not isinstance(response_without_expand, int)
  155. status, element = response_without_expand["C:calendar-data"]
  156. assert status == 200 and element.text
  157. assert "RRULE" in element.text
  158. assert "RECURRENCE-ID" not in element.text
  159. uids: List[str] = []
  160. for line in element.text.split("\n"):
  161. if line.startswith("UID:"):
  162. uid = line[len("UID:"):]
  163. assert uid == "event_full_day_rrule"
  164. uids.append(uid)
  165. assert len(uids) == 1
  166. req_body_with_expand = \
  167. """<?xml version="1.0" encoding="utf-8" ?>
  168. <C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
  169. <D:prop>
  170. <C:calendar-data>
  171. <C:expand start="20060103T000000Z" end="20060105T000000Z"/>
  172. </C:calendar-data>
  173. </D:prop>
  174. <C:filter>
  175. <C:comp-filter name="VCALENDAR">
  176. <C:comp-filter name="VEVENT">
  177. <C:time-range start="20060103T000000Z" end="20060105T000000Z"/>
  178. </C:comp-filter>
  179. </C:comp-filter>
  180. </C:filter>
  181. </C:calendar-query>
  182. """
  183. _, responses = self.report("/calendar.ics/", req_body_with_expand)
  184. assert len(responses) == 1
  185. response_with_expand = responses['/calendar.ics/event_full_day_rrule.ics']
  186. assert not isinstance(response_with_expand, int)
  187. status, element = response_with_expand["C:calendar-data"]
  188. assert status == 200 and element.text
  189. assert "RRULE" not in element.text
  190. assert "BEGIN:VTIMEZONE" not in element.text
  191. uids = []
  192. recurrence_ids = []
  193. for line in element.text.split("\n"):
  194. if line.startswith("UID:"):
  195. assert line == "UID:event_full_day_rrule"
  196. uids.append(line)
  197. if line.startswith("RECURRENCE-ID:"):
  198. assert line in ["RECURRENCE-ID:20060103", "RECURRENCE-ID:20060104", "RECURRENCE-ID:20060105"]
  199. recurrence_ids.append(line)
  200. if line.startswith("DTSTART:"):
  201. assert line in ["DTSTART:20060103", "DTSTART:20060104", "DTSTART:20060105"]
  202. if line.startswith("DTEND:"):
  203. assert line in ["DTEND:20060104", "DTEND:20060105", "DTEND:20060106"]
  204. assert len(uids) == 3
  205. assert len(set(recurrence_ids)) == 3
  206. def test_report_with_expand_property_overridden(self) -> None:
  207. """Test report with expand property"""
  208. self.put("/calendar.ics/", get_file_content("event_daily_rrule_overridden.ics"))
  209. req_body_without_expand = \
  210. """<?xml version="1.0" encoding="utf-8" ?>
  211. <C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
  212. <D:prop>
  213. <C:calendar-data>
  214. </C:calendar-data>
  215. </D:prop>
  216. <C:filter>
  217. <C:comp-filter name="VCALENDAR">
  218. <C:comp-filter name="VEVENT">
  219. <C:time-range start="20060103T000000Z" end="20060105T000000Z"/>
  220. </C:comp-filter>
  221. </C:comp-filter>
  222. </C:filter>
  223. </C:calendar-query>
  224. """
  225. _, responses = self.report("/calendar.ics/", req_body_without_expand)
  226. assert len(responses) == 1
  227. response_without_expand = responses['/calendar.ics/event_daily_rrule_overridden.ics']
  228. assert not isinstance(response_without_expand, int)
  229. status, element = response_without_expand["C:calendar-data"]
  230. assert status == 200 and element.text
  231. assert "RRULE" in element.text
  232. assert "BEGIN:VTIMEZONE" in element.text
  233. uids: List[str] = []
  234. for line in element.text.split("\n"):
  235. if line.startswith("UID:"):
  236. uid = line[len("UID:"):]
  237. assert uid == "event_daily_rrule_overridden"
  238. uids.append(uid)
  239. assert len(uids) == 2
  240. req_body_with_expand = \
  241. """<?xml version="1.0" encoding="utf-8" ?>
  242. <C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
  243. <D:prop>
  244. <C:calendar-data>
  245. <C:expand start="20060103T000000Z" end="20060105T000000Z"/>
  246. </C:calendar-data>
  247. </D:prop>
  248. <C:filter>
  249. <C:comp-filter name="VCALENDAR">
  250. <C:comp-filter name="VEVENT">
  251. <C:time-range start="20060103T000000Z" end="20060105T000000Z"/>
  252. </C:comp-filter>
  253. </C:comp-filter>
  254. </C:filter>
  255. </C:calendar-query>
  256. """
  257. _, responses = self.report("/calendar.ics/", req_body_with_expand)
  258. assert len(responses) == 1
  259. response_with_expand = responses['/calendar.ics/event_daily_rrule_overridden.ics']
  260. assert not isinstance(response_with_expand, int)
  261. status, element = response_with_expand["C:calendar-data"]
  262. assert status == 200 and element.text
  263. assert "RRULE" not in element.text
  264. assert "BEGIN:VTIMEZONE" not in element.text
  265. uids = []
  266. recurrence_ids = []
  267. for line in element.text.split("\n"):
  268. if line.startswith("UID:"):
  269. assert line == "UID:event_daily_rrule_overridden"
  270. uids.append(line)
  271. if line.startswith("RECURRENCE-ID:"):
  272. assert line in ["RECURRENCE-ID:20060103T170000Z", "RECURRENCE-ID:20060104T170000Z"]
  273. recurrence_ids.append(line)
  274. if line.startswith("DTSTART:"):
  275. assert line in ["DTSTART:20060103T170000Z", "DTSTART:20060104T190000Z"]
  276. assert len(uids) == 2
  277. assert len(set(recurrence_ids)) == 2