EmailProviderTest.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace OCA\DAV\Tests\unit\CalDAV\Reminder\NotificationProvider;
  8. use OCA\DAV\CalDAV\Reminder\NotificationProvider\EmailProvider;
  9. use OCP\IL10N;
  10. use OCP\IUser;
  11. use OCP\Mail\IEMailTemplate;
  12. use OCP\Mail\IMailer;
  13. use OCP\Mail\IMessage;
  14. use PHPUnit\Framework\MockObject\MockObject;
  15. use Sabre\VObject\Component\VCalendar;
  16. class EmailProviderTest extends AbstractNotificationProviderTest {
  17. public const USER_EMAIL = 'frodo@hobb.it';
  18. /** @var IMailer|MockObject */
  19. private $mailer;
  20. protected function setUp(): void {
  21. parent::setUp();
  22. $this->mailer = $this->createMock(IMailer::class);
  23. $this->provider = new EmailProvider(
  24. $this->config,
  25. $this->mailer,
  26. $this->logger,
  27. $this->l10nFactory,
  28. $this->urlGenerator
  29. );
  30. }
  31. public function testSendWithoutAttendees():void {
  32. [$user1, $user2, $user3, , $user5] = $users = $this->getUsers();
  33. $principalEmailAddresses = [$user1->getEmailAddress()];
  34. $enL10N = $this->createMock(IL10N::class);
  35. $enL10N->method('t')
  36. ->willReturnArgument(0);
  37. $enL10N->method('l')
  38. ->willReturnArgument(0);
  39. $deL10N = $this->createMock(IL10N::class);
  40. $deL10N->method('t')
  41. ->willReturnArgument(0);
  42. $deL10N->method('l')
  43. ->willReturnArgument(0);
  44. $this->l10nFactory
  45. ->method('getUserLanguage')
  46. ->willReturnMap([
  47. [$user1, 'en'],
  48. [$user2, 'de'],
  49. [$user3, 'de'],
  50. [$user5, 'de'],
  51. ]);
  52. $this->l10nFactory
  53. ->method('findGenericLanguage')
  54. ->willReturn('en');
  55. $this->l10nFactory
  56. ->method('languageExists')
  57. ->willReturnMap([
  58. ['dav', 'en', true],
  59. ['dav', 'de', true],
  60. ]);
  61. $this->l10nFactory
  62. ->method('get')
  63. ->willReturnMap([
  64. ['dav', 'en', null, $enL10N],
  65. ['dav', 'de', null, $deL10N],
  66. ]);
  67. $template1 = $this->getTemplateMock();
  68. $message11 = $this->getMessageMock('uid1@example.com', $template1);
  69. $template2 = $this->getTemplateMock();
  70. $message21 = $this->getMessageMock('uid2@example.com', $template2);
  71. $message22 = $this->getMessageMock('uid3@example.com', $template2);
  72. $this->mailer->expects($this->exactly(2))
  73. ->method('createEMailTemplate')
  74. ->with('dav.calendarReminder')
  75. ->willReturnOnConsecutiveCalls(
  76. $template1,
  77. $template2
  78. );
  79. $this->mailer->expects($this->exactly(4))
  80. ->method('validateMailAddress')
  81. ->withConsecutive(
  82. ['uid1@example.com'],
  83. ['uid2@example.com'],
  84. ['uid3@example.com'],
  85. ['invalid'],
  86. )
  87. ->willReturnOnConsecutiveCalls(
  88. true,
  89. true,
  90. true,
  91. false,
  92. );
  93. $this->mailer->expects($this->exactly(3))
  94. ->method('createMessage')
  95. ->with()
  96. ->willReturnOnConsecutiveCalls(
  97. $message11,
  98. $message21,
  99. $message22
  100. );
  101. $this->mailer->expects($this->exactly(3))
  102. ->method('send')
  103. ->withConsecutive(
  104. [$message11],
  105. [$message21],
  106. [$message22],
  107. )
  108. ->willReturn([]);
  109. $this->setupURLGeneratorMock(2);
  110. $vcalendar = $this->getNoAttendeeVCalendar();
  111. $this->provider->send($vcalendar->VEVENT, $this->calendarDisplayName, $principalEmailAddresses, $users);
  112. }
  113. public function testSendWithAttendeesWhenOwnerIsOrganizer(): void {
  114. [$user1, $user2, $user3, , $user5] = $users = $this->getUsers();
  115. $principalEmailAddresses = [$user1->getEmailAddress()];
  116. $enL10N = $this->createMock(IL10N::class);
  117. $enL10N->method('t')
  118. ->willReturnArgument(0);
  119. $enL10N->method('l')
  120. ->willReturnArgument(0);
  121. $deL10N = $this->createMock(IL10N::class);
  122. $deL10N->method('t')
  123. ->willReturnArgument(0);
  124. $deL10N->method('l')
  125. ->willReturnArgument(0);
  126. $this->l10nFactory
  127. ->method('getUserLanguage')
  128. ->willReturnMap([
  129. [$user1, 'en'],
  130. [$user2, 'de'],
  131. [$user3, 'de'],
  132. [$user5, 'de'],
  133. ]);
  134. $this->l10nFactory
  135. ->method('findGenericLanguage')
  136. ->willReturn('en');
  137. $this->l10nFactory
  138. ->method('languageExists')
  139. ->willReturnMap([
  140. ['dav', 'en', true],
  141. ['dav', 'de', true],
  142. ]);
  143. $this->l10nFactory
  144. ->method('get')
  145. ->willReturnMap([
  146. ['dav', 'en', null, $enL10N],
  147. ['dav', 'de', null, $deL10N],
  148. ]);
  149. $template1 = $this->getTemplateMock();
  150. $message11 = $this->getMessageMock('foo1@example.org', $template1);
  151. $message12 = $this->getMessageMock('uid2@example.com', $template1);
  152. $message13 = $this->getMessageMock('uid3@example.com', $template1);
  153. $template2 = $this->getTemplateMock();
  154. $message21 = $this->getMessageMock('foo3@example.org', $template2);
  155. $message22 = $this->getMessageMock('foo4@example.org', $template2);
  156. $message23 = $this->getMessageMock('uid1@example.com', $template2);
  157. $this->mailer->expects(self::exactly(2))
  158. ->method('createEMailTemplate')
  159. ->with('dav.calendarReminder')
  160. ->willReturnOnConsecutiveCalls(
  161. $template1,
  162. $template2,
  163. );
  164. $this->mailer->expects($this->atLeastOnce())
  165. ->method('validateMailAddress')
  166. ->willReturnMap([
  167. ['foo1@example.org', true],
  168. ['foo3@example.org', true],
  169. ['foo4@example.org', true],
  170. ['uid1@example.com', true],
  171. ['uid2@example.com', true],
  172. ['uid3@example.com', true],
  173. ['invalid', false],
  174. ]);
  175. $this->mailer->expects($this->exactly(6))
  176. ->method('createMessage')
  177. ->with()
  178. ->willReturnOnConsecutiveCalls(
  179. $message11,
  180. $message12,
  181. $message13,
  182. $message21,
  183. $message22,
  184. $message23,
  185. );
  186. $this->mailer->expects($this->exactly(6))
  187. ->method('send')
  188. ->withConsecutive(
  189. [$message11],
  190. [$message12],
  191. [$message13],
  192. [$message21],
  193. [$message22],
  194. [$message23],
  195. )->willReturn([]);
  196. $this->setupURLGeneratorMock(2);
  197. $vcalendar = $this->getAttendeeVCalendar();
  198. $this->provider->send($vcalendar->VEVENT, $this->calendarDisplayName, $principalEmailAddresses, $users);
  199. }
  200. public function testSendWithAttendeesWhenOwnerIsAttendee(): void {
  201. [$user1, $user2, $user3] = $this->getUsers();
  202. $users = [$user2, $user3];
  203. $principalEmailAddresses = [$user2->getEmailAddress()];
  204. $deL10N = $this->createMock(IL10N::class);
  205. $deL10N->method('t')
  206. ->willReturnArgument(0);
  207. $deL10N->method('l')
  208. ->willReturnArgument(0);
  209. $this->l10nFactory
  210. ->method('getUserLanguage')
  211. ->willReturnMap([
  212. [$user2, 'de'],
  213. [$user3, 'de'],
  214. ]);
  215. $this->l10nFactory
  216. ->method('findGenericLanguage')
  217. ->willReturn('en');
  218. $this->l10nFactory
  219. ->method('languageExists')
  220. ->willReturnMap([
  221. ['dav', 'de', true],
  222. ]);
  223. $this->l10nFactory
  224. ->method('get')
  225. ->willReturnMap([
  226. ['dav', 'de', null, $deL10N],
  227. ]);
  228. $template1 = $this->getTemplateMock();
  229. $message12 = $this->getMessageMock('uid2@example.com', $template1);
  230. $message13 = $this->getMessageMock('uid3@example.com', $template1);
  231. $this->mailer->expects(self::once())
  232. ->method('createEMailTemplate')
  233. ->with('dav.calendarReminder')
  234. ->willReturnOnConsecutiveCalls(
  235. $template1,
  236. );
  237. $this->mailer->expects($this->atLeastOnce())
  238. ->method('validateMailAddress')
  239. ->willReturnMap([
  240. ['foo1@example.org', true],
  241. ['foo3@example.org', true],
  242. ['foo4@example.org', true],
  243. ['uid1@example.com', true],
  244. ['uid2@example.com', true],
  245. ['uid3@example.com', true],
  246. ['invalid', false],
  247. ]);
  248. $this->mailer->expects($this->exactly(2))
  249. ->method('createMessage')
  250. ->with()
  251. ->willReturnOnConsecutiveCalls(
  252. $message12,
  253. $message13,
  254. );
  255. $this->mailer->expects($this->exactly(2))
  256. ->method('send')
  257. ->withConsecutive(
  258. [$message12],
  259. [$message13],
  260. )->willReturn([]);
  261. $this->setupURLGeneratorMock(1);
  262. $vcalendar = $this->getAttendeeVCalendar();
  263. $this->provider->send($vcalendar->VEVENT, $this->calendarDisplayName, $principalEmailAddresses, $users);
  264. }
  265. /**
  266. * @return IEMailTemplate
  267. */
  268. private function getTemplateMock():IEMailTemplate {
  269. $template = $this->createMock(IEMailTemplate::class);
  270. $template->expects($this->once())
  271. ->method('addHeader')
  272. ->with()
  273. ->willReturn($template);
  274. $template->expects($this->once())
  275. ->method('setSubject')
  276. ->with()
  277. ->willReturn($template);
  278. $template->expects($this->once())
  279. ->method('addHeading')
  280. ->with()
  281. ->willReturn($template);
  282. $template->expects($this->exactly(4))
  283. ->method('addBodyListItem')
  284. ->with()
  285. ->willReturn($template);
  286. $template->expects($this->once())
  287. ->method('addFooter')
  288. ->with()
  289. ->willReturn($template);
  290. return $template;
  291. }
  292. /**
  293. * @param string $toMail
  294. * @param IEMailTemplate $templateMock
  295. * @param array|null $replyTo
  296. * @return IMessage
  297. */
  298. private function getMessageMock(string $toMail, IEMailTemplate $templateMock, ?array $replyTo = null):IMessage {
  299. $message = $this->createMock(IMessage::class);
  300. $i = 0;
  301. $message->expects($this->once())
  302. ->method('setFrom')
  303. ->with([\OCP\Util::getDefaultEmailAddress('reminders-noreply')])
  304. ->willReturn($message);
  305. if ($replyTo) {
  306. $message->expects($this->once())
  307. ->method('setReplyTo')
  308. ->with($replyTo)
  309. ->willReturn($message);
  310. } else {
  311. $message->expects($this->never())
  312. ->method('setReplyTo');
  313. }
  314. $message->expects($this->once())
  315. ->method('setTo')
  316. ->with([$toMail])
  317. ->willReturn($message);
  318. $message->expects($this->once())
  319. ->method('useTemplate')
  320. ->with($templateMock)
  321. ->willReturn($message);
  322. return $message;
  323. }
  324. private function getNoAttendeeVCalendar():VCalendar {
  325. $vcalendar = new VCalendar();
  326. $vcalendar->add('VEVENT', [
  327. 'SUMMARY' => 'Fellowship meeting',
  328. 'DTSTART' => new \DateTime('2017-01-01 00:00:00+00:00'), // 1483228800,
  329. 'UID' => 'uid1234',
  330. 'LOCATION' => 'Location 123',
  331. 'DESCRIPTION' => 'DESCRIPTION 456',
  332. ]);
  333. return $vcalendar;
  334. }
  335. private function getAttendeeVCalendar():VCalendar {
  336. $vcalendar = new VCalendar();
  337. $vcalendar->add('VEVENT', [
  338. 'SUMMARY' => 'Fellowship meeting',
  339. 'DTSTART' => new \DateTime('2017-01-01 00:00:00+00:00'), // 1483228800,
  340. 'UID' => 'uid1234',
  341. 'LOCATION' => 'Location 123',
  342. 'DESCRIPTION' => 'DESCRIPTION 456',
  343. ]);
  344. $vcalendar->VEVENT->add(
  345. 'ORGANIZER',
  346. 'mailto:uid1@example.com',
  347. [
  348. 'LANG' => 'en'
  349. ]
  350. );
  351. $vcalendar->VEVENT->add(
  352. 'ATTENDEE',
  353. 'mailto:foo1@example.org',
  354. [
  355. 'LANG' => 'de',
  356. 'PARTSTAT' => 'NEEDS-ACTION',
  357. ]
  358. );
  359. $vcalendar->VEVENT->add(
  360. 'ATTENDEE',
  361. 'mailto:foo2@example.org',
  362. [
  363. 'LANG' => 'de',
  364. 'PARTSTAT' => 'DECLINED',
  365. ]
  366. );
  367. $vcalendar->VEVENT->add(
  368. 'ATTENDEE',
  369. 'mailto:foo3@example.org',
  370. [
  371. 'LANG' => 'en',
  372. 'PARTSTAT' => 'CONFIRMED',
  373. ]
  374. );
  375. $vcalendar->VEVENT->add(
  376. 'ATTENDEE',
  377. 'mailto:foo4@example.org'
  378. );
  379. $vcalendar->VEVENT->add(
  380. 'ATTENDEE',
  381. 'tomail:foo5@example.org'
  382. );
  383. return $vcalendar;
  384. }
  385. private function setupURLGeneratorMock(int $times = 1): void {
  386. $this->urlGenerator
  387. ->expects($this->exactly($times * 4))
  388. ->method('imagePath')
  389. ->willReturnMap([
  390. ['core', 'actions/info.png', 'imagePath1'],
  391. ['core', 'places/calendar.png', 'imagePath2'],
  392. ['core', 'actions/address.png', 'imagePath3'],
  393. ['core', 'actions/more.png', 'imagePath4'],
  394. ]);
  395. $this->urlGenerator
  396. ->expects($this->exactly($times * 4))
  397. ->method('getAbsoluteURL')
  398. ->willReturnMap([
  399. ['imagePath1', 'AbsURL1'],
  400. ['imagePath2', 'AbsURL2'],
  401. ['imagePath3', 'AbsURL3'],
  402. ['imagePath4', 'AbsURL4'],
  403. ]);
  404. }
  405. private function getUsers(): array {
  406. $user1 = $this->createMock(IUser::class);
  407. $user1->method('getUID')
  408. ->willReturn('uid1');
  409. $user1->method('getEMailAddress')
  410. ->willReturn('uid1@example.com');
  411. $user2 = $this->createMock(IUser::class);
  412. $user2->method('getUID')
  413. ->willReturn('uid2');
  414. $user2->method('getEMailAddress')
  415. ->willReturn('uid2@example.com');
  416. $user3 = $this->createMock(IUser::class);
  417. $user3->method('getUID')
  418. ->willReturn('uid3');
  419. $user3->method('getEMailAddress')
  420. ->willReturn('uid3@example.com');
  421. $user4 = $this->createMock(IUser::class);
  422. $user4->method('getUID')
  423. ->willReturn('uid4');
  424. $user4->method('getEMailAddress')
  425. ->willReturn(null);
  426. $user5 = $this->createMock(IUser::class);
  427. $user5->method('getUID')
  428. ->willReturn('uid5');
  429. $user5->method('getEMailAddress')
  430. ->willReturn('invalid');
  431. return [$user1, $user2, $user3, $user4, $user5];
  432. }
  433. }