test_hook_email.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. # This file is part of Radicale - CalDAV and CardDAV server
  2. # Copyright © 2025-2025 Peter Bieringer <pb@bieringer.de>
  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 related to hook 'email'
  18. """
  19. import logging
  20. import os
  21. import re
  22. from datetime import datetime, timedelta
  23. from radicale.tests import BaseTest
  24. from radicale.tests.helpers import get_file_content
  25. class TestHooks(BaseTest):
  26. """Tests with hooks."""
  27. def setup_method(self) -> None:
  28. BaseTest.setup_method(self)
  29. rights_file_path = os.path.join(self.colpath, "rights")
  30. with open(rights_file_path, "w") as f:
  31. f.write("""\
  32. [permit delete collection]
  33. user: .*
  34. collection: test-permit-delete
  35. permissions: RrWwD
  36. [forbid delete collection]
  37. user: .*
  38. collection: test-forbid-delete
  39. permissions: RrWwd
  40. [permit overwrite collection]
  41. user: .*
  42. collection: test-permit-overwrite
  43. permissions: RrWwO
  44. [forbid overwrite collection]
  45. user: .*
  46. collection: test-forbid-overwrite
  47. permissions: RrWwo
  48. [allow all]
  49. user: .*
  50. collection: .*
  51. permissions: RrWw""")
  52. self.configure({"rights": {"file": rights_file_path,
  53. "type": "from_file"}})
  54. self.configure({"hook": {"type": "email",
  55. "dryrun": "True"}})
  56. def _future_date_timestamp(self) -> str:
  57. """Return a date timestamp for a future date."""
  58. future_date = datetime.now() + timedelta(days=1)
  59. return future_date.strftime("%Y%m%dT%H%M%S")
  60. def _past_date_timestamp(self) -> str:
  61. past_date = datetime.now() - timedelta(days=1)
  62. return past_date.strftime("%Y%m%dT%H%M%S")
  63. def _replace_end_date_in_event(self, event: str, new_date: str) -> str:
  64. """Replace the end date in an event string."""
  65. return re.sub(r"DTEND;TZID=Europe/Paris:\d{8}T\d{6}",
  66. f"DTEND;TZID=Europe/Paris:{new_date}", event)
  67. def test_add_event_with_future_end_date(self, caplog) -> None:
  68. caplog.set_level(logging.WARNING)
  69. """Add an event."""
  70. self.mkcalendar("/calendar.ics/")
  71. event = get_file_content("event1.ics")
  72. event = self._replace_end_date_in_event(event, self._future_date_timestamp())
  73. path = "/calendar.ics/event1.ics"
  74. self.put(path, event)
  75. _, headers, answer = self.request("GET", path, check=200)
  76. assert "ETag" in headers
  77. assert headers["Content-Type"] == "text/calendar; charset=utf-8"
  78. assert "VEVENT" in answer
  79. assert "Event" in answer
  80. assert "UID:event" in answer
  81. found = 0
  82. for line in caplog.messages:
  83. if line.find("notification_item: {'type': 'upsert'") != -1:
  84. found = found | 1
  85. if line.find("to_addresses=['janedoe@example.com']") != -1:
  86. found = found | 2
  87. if line.find("to_addresses=['johndoe@example.com']") != -1:
  88. found = found | 4
  89. if (found != 7):
  90. raise ValueError("Logging misses expected log lines, found=%d", found)
  91. def test_add_event_with_past_end_date(self, caplog) -> None:
  92. caplog.set_level(logging.WARNING)
  93. """Add an event."""
  94. self.mkcalendar("/calendar.ics/")
  95. event = get_file_content("event1.ics")
  96. event = self._replace_end_date_in_event(event, self._past_date_timestamp())
  97. path = "/calendar.ics/event1.ics"
  98. self.put(path, event)
  99. _, headers, answer = self.request("GET", path, check=200)
  100. assert "ETag" in headers
  101. assert headers["Content-Type"] == "text/calendar; charset=utf-8"
  102. assert "VEVENT" in answer
  103. assert "Event" in answer
  104. assert "UID:event" in answer
  105. # Should not trigger an email
  106. assert len(caplog.messages) == 0
  107. def test_delete_event_with_future_end_date(self, caplog) -> None:
  108. caplog.set_level(logging.WARNING)
  109. """Delete an event."""
  110. self.mkcalendar("/calendar.ics/")
  111. event = get_file_content("event1.ics")
  112. event = self._replace_end_date_in_event(event, self._future_date_timestamp())
  113. path = "/calendar.ics/event1.ics"
  114. self.put(path, event)
  115. _, responses = self.delete(path)
  116. assert responses[path] == 200
  117. _, answer = self.get("/calendar.ics/")
  118. assert "VEVENT" not in answer
  119. found = 0
  120. for line in caplog.messages:
  121. if line.find("notification_item: {'type': 'delete'") != -1:
  122. found = found | 1
  123. if line.find("to_addresses=['janedoe@example.com']") != -1:
  124. found = found | 2
  125. if line.find("to_addresses=['johndoe@example.com']") != -1:
  126. found = found | 4
  127. if (found != 7):
  128. raise ValueError("Logging misses expected log lines, found=%d", found)
  129. def test_delete_event_with_past_end_date(self, caplog) -> None:
  130. caplog.set_level(logging.WARNING)
  131. """Delete an event."""
  132. self.mkcalendar("/calendar.ics/")
  133. event = get_file_content("event1.ics")
  134. event = self._replace_end_date_in_event(event, self._past_date_timestamp())
  135. path = "/calendar.ics/event1.ics"
  136. self.put(path, event)
  137. _, responses = self.delete(path)
  138. assert responses[path] == 200
  139. _, answer = self.get("/calendar.ics/")
  140. assert "VEVENT" not in answer
  141. # Should not trigger an email
  142. assert len(caplog.messages) == 0