ManagerTest.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. <?php
  2. /**
  3. * @copyright 2017, Georg Ehrke <oc.list@georgehrke.com>
  4. *
  5. * @author Georg Ehrke <oc.list@georgehrke.com>
  6. *
  7. * @license GNU AGPL version 3 or any later version
  8. *
  9. * This program is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU Affero General Public License as
  11. * published by the Free Software Foundation, either version 3 of the
  12. * License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU Affero General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Affero General Public License
  20. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. *
  22. */
  23. namespace Test\Calendar;
  24. use OC\AppFramework\Bootstrap\Coordinator;
  25. use OC\Calendar\Manager;
  26. use OCP\AppFramework\Utility\ITimeFactory;
  27. use OCP\Calendar\ICalendar;
  28. use OCP\Calendar\ICreateFromString;
  29. use OCP\Calendar\IHandleImipMessage;
  30. use PHPUnit\Framework\MockObject\MockObject;
  31. use Psr\Container\ContainerInterface;
  32. use Psr\Log\LoggerInterface;
  33. use Sabre\VObject\Document;
  34. use Sabre\VObject\Reader;
  35. use Test\TestCase;
  36. /*
  37. * This allows us to create Mock object supporting both interfaces
  38. */
  39. interface ICreateFromStringAndHandleImipMessage extends ICreateFromString, IHandleImipMessage {
  40. }
  41. class ManagerTest extends TestCase {
  42. /** @var Coordinator|MockObject */
  43. private $coordinator;
  44. /** @var MockObject|ContainerInterface */
  45. private $container;
  46. /** @var MockObject|LoggerInterface */
  47. private $logger;
  48. /** @var Manager */
  49. private $manager;
  50. /** @var ITimeFactory|ITimeFactory&MockObject|MockObject */
  51. private $time;
  52. protected function setUp(): void {
  53. parent::setUp();
  54. $this->coordinator = $this->createMock(Coordinator::class);
  55. $this->container = $this->createMock(ContainerInterface::class);
  56. $this->logger = $this->createMock(LoggerInterface::class);
  57. $this->time = $this->createMock(ITimeFactory::class);
  58. $this->manager = new Manager(
  59. $this->coordinator,
  60. $this->container,
  61. $this->logger,
  62. $this->time,
  63. );
  64. }
  65. /**
  66. * @dataProvider searchProvider
  67. */
  68. public function testSearch($search1, $search2, $expected) {
  69. /** @var ICalendar | MockObject $calendar1 */
  70. $calendar1 = $this->createMock(ICalendar::class);
  71. $calendar1->method('getKey')->willReturn('simple:1');
  72. $calendar1->expects($this->once())
  73. ->method('search')
  74. ->with('', [], [], null, null)
  75. ->willReturn($search1);
  76. /** @var ICalendar | MockObject $calendar2 */
  77. $calendar2 = $this->createMock(ICalendar::class);
  78. $calendar2->method('getKey')->willReturn('simple:2');
  79. $calendar2->expects($this->once())
  80. ->method('search')
  81. ->with('', [], [], null, null)
  82. ->willReturn($search2);
  83. $this->manager->registerCalendar($calendar1);
  84. $this->manager->registerCalendar($calendar2);
  85. $result = $this->manager->search('');
  86. $this->assertEquals($expected, $result);
  87. }
  88. /**
  89. * @dataProvider searchProvider
  90. */
  91. public function testSearchOptions($search1, $search2, $expected) {
  92. /** @var ICalendar | MockObject $calendar1 */
  93. $calendar1 = $this->createMock(ICalendar::class);
  94. $calendar1->method('getKey')->willReturn('simple:1');
  95. $calendar1->expects($this->once())
  96. ->method('search')
  97. ->with('searchTerm', ['SUMMARY', 'DESCRIPTION'],
  98. ['timerange' => ['start' => null, 'end' => null]], 5, 20)
  99. ->willReturn($search1);
  100. /** @var ICalendar | MockObject $calendar2 */
  101. $calendar2 = $this->createMock(ICalendar::class);
  102. $calendar2->method('getKey')->willReturn('simple:2');
  103. $calendar2->expects($this->once())
  104. ->method('search')
  105. ->with('searchTerm', ['SUMMARY', 'DESCRIPTION'],
  106. ['timerange' => ['start' => null, 'end' => null]], 5, 20)
  107. ->willReturn($search2);
  108. $this->manager->registerCalendar($calendar1);
  109. $this->manager->registerCalendar($calendar2);
  110. $result = $this->manager->search('searchTerm', ['SUMMARY', 'DESCRIPTION'],
  111. ['timerange' => ['start' => null, 'end' => null]], 5, 20);
  112. $this->assertEquals($expected, $result);
  113. }
  114. public function searchProvider() {
  115. $search1 = [
  116. [
  117. 'id' => 1,
  118. 'data' => 'foobar',
  119. ],
  120. [
  121. 'id' => 2,
  122. 'data' => 'barfoo',
  123. ]
  124. ];
  125. $search2 = [
  126. [
  127. 'id' => 3,
  128. 'data' => 'blablub',
  129. ],
  130. [
  131. 'id' => 4,
  132. 'data' => 'blubbla',
  133. ]
  134. ];
  135. $expected = [
  136. [
  137. 'id' => 1,
  138. 'data' => 'foobar',
  139. 'calendar-key' => 'simple:1',
  140. ],
  141. [
  142. 'id' => 2,
  143. 'data' => 'barfoo',
  144. 'calendar-key' => 'simple:1',
  145. ],
  146. [
  147. 'id' => 3,
  148. 'data' => 'blablub',
  149. 'calendar-key' => 'simple:2',
  150. ],
  151. [
  152. 'id' => 4,
  153. 'data' => 'blubbla',
  154. 'calendar-key' => 'simple:2',
  155. ]
  156. ];
  157. return [
  158. [
  159. $search1,
  160. $search2,
  161. $expected
  162. ]
  163. ];
  164. }
  165. public function testRegisterUnregister() {
  166. /** @var ICalendar | MockObject $calendar1 */
  167. $calendar1 = $this->createMock(ICalendar::class);
  168. $calendar1->method('getKey')->willReturn('key1');
  169. /** @var ICalendar | MockObject $calendar2 */
  170. $calendar2 = $this->createMock(ICalendar::class);
  171. $calendar2->method('getKey')->willReturn('key2');
  172. $this->manager->registerCalendar($calendar1);
  173. $this->manager->registerCalendar($calendar2);
  174. $result = $this->manager->getCalendars();
  175. $this->assertCount(2, $result);
  176. $this->assertContains($calendar1, $result);
  177. $this->assertContains($calendar2, $result);
  178. $this->manager->unregisterCalendar($calendar1);
  179. $result = $this->manager->getCalendars();
  180. $this->assertCount(1, $result);
  181. $this->assertContains($calendar2, $result);
  182. }
  183. public function testGetCalendars() {
  184. /** @var ICalendar | MockObject $calendar1 */
  185. $calendar1 = $this->createMock(ICalendar::class);
  186. $calendar1->method('getKey')->willReturn('key1');
  187. /** @var ICalendar | MockObject $calendar2 */
  188. $calendar2 = $this->createMock(ICalendar::class);
  189. $calendar2->method('getKey')->willReturn('key2');
  190. $this->manager->registerCalendar($calendar1);
  191. $this->manager->registerCalendar($calendar2);
  192. $result = $this->manager->getCalendars();
  193. $this->assertCount(2, $result);
  194. $this->assertContains($calendar1, $result);
  195. $this->assertContains($calendar2, $result);
  196. $this->manager->clear();
  197. $result = $this->manager->getCalendars();
  198. $this->assertCount(0, $result);
  199. }
  200. public function testEnabledIfNot() {
  201. $isEnabled = $this->manager->isEnabled();
  202. $this->assertFalse($isEnabled);
  203. }
  204. public function testIfEnabledIfSo() {
  205. /** @var ICalendar | MockObject $calendar */
  206. $calendar = $this->createMock(ICalendar::class);
  207. $this->manager->registerCalendar($calendar);
  208. $isEnabled = $this->manager->isEnabled();
  209. $this->assertTrue($isEnabled);
  210. }
  211. public function testHandleImipReplyWrongMethod(): void {
  212. $principalUri = 'principals/user/linus';
  213. $sender = 'pierre@general-store.com';
  214. $recipient = 'linus@stardew-tent-living.com';
  215. $calendarData = $this->getVCalendarReply();
  216. $calendarData->METHOD = 'REQUEST';
  217. $this->logger->expects(self::once())
  218. ->method('warning');
  219. $this->time->expects(self::never())
  220. ->method('getTime');
  221. $result = $this->manager->handleIMipReply($principalUri, $sender, $recipient, $calendarData->serialize());
  222. $this->assertFalse($result);
  223. }
  224. public function testHandleImipReplyOrganizerNotRecipient(): void {
  225. $principalUri = 'principals/user/linus';
  226. $recipient = 'pierre@general-store.com';
  227. $sender = 'linus@stardew-tent-living.com';
  228. $calendarData = $this->getVCalendarReply();
  229. $this->logger->expects(self::once())
  230. ->method('warning');
  231. $this->time->expects(self::never())
  232. ->method('getTime');
  233. $result = $this->manager->handleIMipReply($principalUri, $sender, $recipient, $calendarData->serialize());
  234. $this->assertFalse($result);
  235. }
  236. public function testHandleImipReplyDateInThePast(): void {
  237. $principalUri = 'principals/user/linus';
  238. $sender = 'pierre@general-store.com';
  239. $recipient = 'linus@stardew-tent-living.com';
  240. $calendarData = $this->getVCalendarReply();
  241. $calendarData->VEVENT->DTSTART = new \DateTime('2013-04-07'); // set to in the past
  242. $this->time->expects(self::once())
  243. ->method('getTime')
  244. ->willReturn(time());
  245. $this->logger->expects(self::once())
  246. ->method('warning');
  247. $result = $this->manager->handleIMipReply($principalUri, $sender, $recipient, $calendarData->serialize());
  248. $this->assertFalse($result);
  249. }
  250. public function testHandleImipReplyNoCalendars(): void {
  251. /** @var Manager | \PHPUnit\Framework\MockObject\MockObject $manager */
  252. $manager = $this->getMockBuilder(Manager::class)
  253. ->setConstructorArgs([
  254. $this->coordinator,
  255. $this->container,
  256. $this->logger,
  257. $this->time
  258. ])
  259. ->setMethods([
  260. 'getCalendarsForPrincipal'
  261. ])
  262. ->getMock();
  263. $principalUri = 'principals/user/linus';
  264. $sender = 'pierre@general-store.com';
  265. $recipient = 'linus@stardew-tent-living.com';
  266. $calendarData = $this->getVCalendarReply();
  267. $this->time->expects(self::once())
  268. ->method('getTime')
  269. ->willReturn(1628374233);
  270. $manager->expects(self::once())
  271. ->method('getCalendarsForPrincipal')
  272. ->willReturn([]);
  273. $this->logger->expects(self::once())
  274. ->method('warning');
  275. $result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendarData->serialize());
  276. $this->assertFalse($result);
  277. }
  278. public function testHandleImipReplyEventNotFound(): void {
  279. /** @var Manager | \PHPUnit\Framework\MockObject\MockObject $manager */
  280. $manager = $this->getMockBuilder(Manager::class)
  281. ->setConstructorArgs([
  282. $this->coordinator,
  283. $this->container,
  284. $this->logger,
  285. $this->time
  286. ])
  287. ->setMethods([
  288. 'getCalendarsForPrincipal'
  289. ])
  290. ->getMock();
  291. $calendar = $this->createMock(ICreateFromStringAndHandleImipMessage::class);
  292. $principalUri = 'principals/user/linus';
  293. $sender = 'pierre@general-store.com';
  294. $recipient = 'linus@stardew-tent-living.com';
  295. $calendarData = $this->getVCalendarReply();
  296. $this->time->expects(self::once())
  297. ->method('getTime')
  298. ->willReturn(1628374233);
  299. $manager->expects(self::once())
  300. ->method('getCalendarsForPrincipal')
  301. ->willReturn([$calendar]);
  302. $calendar->expects(self::once())
  303. ->method('search')
  304. ->willReturn([]);
  305. $this->logger->expects(self::once())
  306. ->method('info');
  307. $calendar->expects(self::never())
  308. ->method('handleIMipMessage');
  309. $result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendarData->serialize());
  310. $this->assertFalse($result);
  311. }
  312. public function testHandleImipReply(): void {
  313. /** @var Manager | \PHPUnit\Framework\MockObject\MockObject $manager */
  314. $manager = $this->getMockBuilder(Manager::class)
  315. ->setConstructorArgs([
  316. $this->coordinator,
  317. $this->container,
  318. $this->logger,
  319. $this->time
  320. ])
  321. ->setMethods([
  322. 'getCalendarsForPrincipal'
  323. ])
  324. ->getMock();
  325. $calendar = $this->createMock(ICreateFromStringAndHandleImipMessage::class);
  326. $principalUri = 'principals/user/linus';
  327. $sender = 'pierre@general-store.com';
  328. $recipient = 'linus@stardew-tent-living.com';
  329. $calendarData = $this->getVCalendarReply();
  330. $this->time->expects(self::once())
  331. ->method('getTime')
  332. ->willReturn(1628374233);
  333. $manager->expects(self::once())
  334. ->method('getCalendarsForPrincipal')
  335. ->willReturn([$calendar]);
  336. $calendar->expects(self::once())
  337. ->method('search')
  338. ->willReturn([['uri' => 'testname.ics']]);
  339. $calendar->expects(self::once())
  340. ->method('handleIMipMessage')
  341. ->with('testname.ics', $calendarData->serialize());
  342. $result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendarData->serialize());
  343. $this->assertTrue($result);
  344. }
  345. public function testHandleImipCancelWrongMethod(): void {
  346. $principalUri = 'principals/user/pierre';
  347. $sender = 'linus@stardew-tent-living.com';
  348. $recipient = 'pierre@general-store.com';
  349. $replyTo = null;
  350. $calendarData = $this->getVCalendarCancel();
  351. $calendarData->METHOD = 'REQUEST';
  352. $this->logger->expects(self::once())
  353. ->method('warning');
  354. $this->time->expects(self::never())
  355. ->method('getTime');
  356. $result = $this->manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendarData->serialize());
  357. $this->assertFalse($result);
  358. }
  359. public function testHandleImipCancelAttendeeNotRecipient(): void {
  360. $principalUri = '/user/admin';
  361. $sender = 'linus@stardew-tent-living.com';
  362. $recipient = 'leah@general-store.com';
  363. $replyTo = null;
  364. $calendarData = $this->getVCalendarCancel();
  365. $this->logger->expects(self::once())
  366. ->method('warning');
  367. $this->time->expects(self::never())
  368. ->method('getTime');
  369. $result = $this->manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendarData->serialize());
  370. $this->assertFalse($result);
  371. }
  372. public function testHandleImipCancelDateInThePast(): void {
  373. $principalUri = 'principals/user/pierre';
  374. $sender = 'linus@stardew-tent-living.com';
  375. $recipient = 'pierre@general-store.com';
  376. $replyTo = null;
  377. $calendarData = $this->getVCalendarCancel();
  378. $calendarData->VEVENT->DTSTART = new \DateTime('2013-04-07'); // set to in the past
  379. $this->time->expects(self::once())
  380. ->method('getTime')
  381. ->willReturn(time());
  382. $this->logger->expects(self::once())
  383. ->method('warning');
  384. $result = $this->manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendarData->serialize());
  385. $this->assertFalse($result);
  386. }
  387. public function testHandleImipCancelNoCalendars(): void {
  388. /** @var Manager | \PHPUnit\Framework\MockObject\MockObject $manager */
  389. $manager = $this->getMockBuilder(Manager::class)
  390. ->setConstructorArgs([
  391. $this->coordinator,
  392. $this->container,
  393. $this->logger,
  394. $this->time
  395. ])
  396. ->setMethods([
  397. 'getCalendarsForPrincipal'
  398. ])
  399. ->getMock();
  400. $principalUri = 'principals/user/pierre';
  401. $sender = 'linus@stardew-tent-living.com';
  402. $recipient = 'pierre@general-store.com';
  403. $replyTo = null;
  404. $calendarData = $this->getVCalendarCancel();
  405. $this->time->expects(self::once())
  406. ->method('getTime')
  407. ->willReturn(1628374233);
  408. $manager->expects(self::once())
  409. ->method('getCalendarsForPrincipal')
  410. ->with($principalUri)
  411. ->willReturn([]);
  412. $this->logger->expects(self::once())
  413. ->method('warning');
  414. $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendarData->serialize());
  415. $this->assertFalse($result);
  416. }
  417. public function testHandleImipCancelOrganiserInReplyTo(): void {
  418. /** @var Manager | \PHPUnit\Framework\MockObject\MockObject $manager */
  419. $manager = $this->getMockBuilder(Manager::class)
  420. ->setConstructorArgs([
  421. $this->coordinator,
  422. $this->container,
  423. $this->logger,
  424. $this->time
  425. ])
  426. ->setMethods([
  427. 'getCalendarsForPrincipal'
  428. ])
  429. ->getMock();
  430. $principalUri = 'principals/user/pierre';
  431. $sender = 'clint@stardew-blacksmiths.com';
  432. $recipient = 'pierre@general-store.com';
  433. $replyTo = 'linus@stardew-tent-living.com';
  434. $calendar = $this->createMock(ICreateFromStringAndHandleImipMessage::class);
  435. $calendarData = $this->getVCalendarCancel();
  436. $this->time->expects(self::once())
  437. ->method('getTime')
  438. ->willReturn(1628374233);
  439. $manager->expects(self::once())
  440. ->method('getCalendarsForPrincipal')
  441. ->with($principalUri)
  442. ->willReturn([$calendar]);
  443. $calendar->expects(self::once())
  444. ->method('search')
  445. ->willReturn([['uri' => 'testname.ics']]);
  446. $calendar->expects(self::once())
  447. ->method('handleIMipMessage')
  448. ->with('testname.ics', $calendarData->serialize());
  449. $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendarData->serialize());
  450. $this->assertTrue($result);
  451. }
  452. public function testHandleImipCancel(): void {
  453. /** @var Manager | \PHPUnit\Framework\MockObject\MockObject $manager */
  454. $manager = $this->getMockBuilder(Manager::class)
  455. ->setConstructorArgs([
  456. $this->coordinator,
  457. $this->container,
  458. $this->logger,
  459. $this->time
  460. ])
  461. ->setMethods([
  462. 'getCalendarsForPrincipal'
  463. ])
  464. ->getMock();
  465. $principalUri = 'principals/user/pierre';
  466. $sender = 'linus@stardew-tent-living.com';
  467. $recipient = 'pierre@general-store.com';
  468. $replyTo = null;
  469. $calendar = $this->createMock(ICreateFromStringAndHandleImipMessage::class);
  470. $calendarData = $this->getVCalendarCancel();
  471. $this->time->expects(self::once())
  472. ->method('getTime')
  473. ->willReturn(1628374233);
  474. $manager->expects(self::once())
  475. ->method('getCalendarsForPrincipal')
  476. ->with($principalUri)
  477. ->willReturn([$calendar]);
  478. $calendar->expects(self::once())
  479. ->method('search')
  480. ->willReturn([['uri' => 'testname.ics']]);
  481. $calendar->expects(self::once())
  482. ->method('handleIMipMessage')
  483. ->with('testname.ics', $calendarData->serialize());
  484. $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendarData->serialize());
  485. $this->assertTrue($result);
  486. }
  487. private function getVCalendarReply(): Document {
  488. $data = <<<EOF
  489. BEGIN:VCALENDAR
  490. PRODID:-//Nextcloud/Nextcloud CalDAV Server//EN
  491. VERSION:2.0
  492. CALSCALE:GREGORIAN
  493. METHOD:REPLY
  494. BEGIN:VEVENT
  495. DTSTART;VALUE=DATE:20210820
  496. DTEND;VALUE=DATE:20220821
  497. DTSTAMP:20210812T100040Z
  498. ORGANIZER;CN=admin:mailto:linus@stardew-tent-living.com
  499. UID:dcc733bf-b2b2-41f2-a8cf-550ae4b67aff
  500. ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;CN=pierr
  501. e@general-store.com;X-NUM-GUESTS=0:mailto:pierre@general-store.com
  502. CREATED:20220812T100021Z
  503. DESCRIPTION:
  504. LAST-MODIFIED:20220812T100040Z
  505. LOCATION:
  506. SEQUENCE:3
  507. STATUS:CONFIRMED
  508. SUMMARY:berry basket
  509. TRANSP:OPAQUE
  510. END:VEVENT
  511. END:VCALENDAR
  512. EOF;
  513. return Reader::read($data);
  514. }
  515. private function getVCalendarCancel(): Document {
  516. $data = <<<EOF
  517. BEGIN:VCALENDAR
  518. PRODID:-//Nextcloud/Nextcloud CalDAV Server//EN
  519. VERSION:2.0
  520. CALSCALE:GREGORIAN
  521. METHOD:CANCEL
  522. BEGIN:VEVENT
  523. DTSTART;VALUE=DATE:20210820
  524. DTEND;VALUE=DATE:20220821
  525. DTSTAMP:20210812T100040Z
  526. ORGANIZER;CN=admin:mailto:linus@stardew-tent-living.com
  527. UID:dcc733bf-b2b2-41f2-a8cf-550ae4b67aff
  528. ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;CN=pierr
  529. e@general-store.com;X-NUM-GUESTS=0:mailto:pierre@general-store.com
  530. CREATED:20220812T100021Z
  531. DESCRIPTION:
  532. LAST-MODIFIED:20220812T100040Z
  533. LOCATION:
  534. SEQUENCE:3
  535. STATUS:CANCELLED
  536. SUMMARY:berry basket
  537. TRANSP:OPAQUE
  538. END:VEVENT
  539. END:VCALENDAR
  540. EOF;
  541. return Reader::read($data);
  542. }
  543. }