TasksSearchProviderTest.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace OCA\DAV\Tests\unit\Search;
  8. use OCA\DAV\CalDAV\CalDavBackend;
  9. use OCA\DAV\Search\TasksSearchProvider;
  10. use OCP\App\IAppManager;
  11. use OCP\IL10N;
  12. use OCP\IURLGenerator;
  13. use OCP\IUser;
  14. use OCP\Search\ISearchQuery;
  15. use OCP\Search\SearchResult;
  16. use OCP\Search\SearchResultEntry;
  17. use Sabre\VObject\Reader;
  18. use Test\TestCase;
  19. class TasksSearchProviderTest extends TestCase {
  20. /** @var IAppManager|\PHPUnit\Framework\MockObject\MockObject */
  21. private $appManager;
  22. /** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
  23. private $l10n;
  24. /** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */
  25. private $urlGenerator;
  26. /** @var CalDavBackend|\PHPUnit\Framework\MockObject\MockObject */
  27. private $backend;
  28. /** @var TasksSearchProvider */
  29. private $provider;
  30. // NO DUE NOR COMPLETED NOR SUMMARY
  31. private $vTodo0 = 'BEGIN:VCALENDAR' . PHP_EOL .
  32. 'PRODID:TEST' . PHP_EOL .
  33. 'VERSION:2.0' . PHP_EOL .
  34. 'BEGIN:VTODO' . PHP_EOL .
  35. 'UID:20070313T123432Z-456553@example.com' . PHP_EOL .
  36. 'DTSTAMP:20070313T123432Z' . PHP_EOL .
  37. 'STATUS:NEEDS-ACTION' . PHP_EOL .
  38. 'END:VTODO' . PHP_EOL .
  39. 'END:VCALENDAR';
  40. // DUE AND COMPLETED
  41. private $vTodo1 = 'BEGIN:VCALENDAR' . PHP_EOL .
  42. 'PRODID:TEST' . PHP_EOL .
  43. 'VERSION:2.0' . PHP_EOL .
  44. 'BEGIN:VTODO' . PHP_EOL .
  45. 'UID:20070313T123432Z-456553@example.com' . PHP_EOL .
  46. 'DTSTAMP:20070313T123432Z' . PHP_EOL .
  47. 'COMPLETED:20070707T100000Z' . PHP_EOL .
  48. 'DUE;VALUE=DATE:20070501' . PHP_EOL .
  49. 'SUMMARY:Task title' . PHP_EOL .
  50. 'STATUS:NEEDS-ACTION' . PHP_EOL .
  51. 'END:VTODO' . PHP_EOL .
  52. 'END:VCALENDAR';
  53. // COMPLETED ONLY
  54. private $vTodo2 = 'BEGIN:VCALENDAR' . PHP_EOL .
  55. 'PRODID:TEST' . PHP_EOL .
  56. 'VERSION:2.0' . PHP_EOL .
  57. 'BEGIN:VTODO' . PHP_EOL .
  58. 'UID:20070313T123432Z-456553@example.com' . PHP_EOL .
  59. 'DTSTAMP:20070313T123432Z' . PHP_EOL .
  60. 'COMPLETED:20070707T100000Z' . PHP_EOL .
  61. 'SUMMARY:Task title' . PHP_EOL .
  62. 'STATUS:NEEDS-ACTION' . PHP_EOL .
  63. 'END:VTODO' . PHP_EOL .
  64. 'END:VCALENDAR';
  65. // DUE DATE
  66. private $vTodo3 = 'BEGIN:VCALENDAR' . PHP_EOL .
  67. 'PRODID:TEST' . PHP_EOL .
  68. 'VERSION:2.0' . PHP_EOL .
  69. 'BEGIN:VTODO' . PHP_EOL .
  70. 'UID:20070313T123432Z-456553@example.com' . PHP_EOL .
  71. 'DTSTAMP:20070313T123432Z' . PHP_EOL .
  72. 'DUE;VALUE=DATE:20070501' . PHP_EOL .
  73. 'SUMMARY:Task title' . PHP_EOL .
  74. 'STATUS:NEEDS-ACTION' . PHP_EOL .
  75. 'END:VTODO' . PHP_EOL .
  76. 'END:VCALENDAR';
  77. // DUE DATETIME
  78. private $vTodo4 = 'BEGIN:VCALENDAR' . PHP_EOL .
  79. 'PRODID:TEST' . PHP_EOL .
  80. 'VERSION:2.0' . PHP_EOL .
  81. 'BEGIN:VTODO' . PHP_EOL .
  82. 'UID:20070313T123432Z-456553@example.com' . PHP_EOL .
  83. 'DTSTAMP:20070313T123432Z' . PHP_EOL .
  84. 'DUE:20070709T130000Z' . PHP_EOL .
  85. 'SUMMARY:Task title' . PHP_EOL .
  86. 'STATUS:NEEDS-ACTION' . PHP_EOL .
  87. 'END:VTODO' . PHP_EOL .
  88. 'END:VCALENDAR';
  89. protected function setUp(): void {
  90. parent::setUp();
  91. $this->appManager = $this->createMock(IAppManager::class);
  92. $this->l10n = $this->createMock(IL10N::class);
  93. $this->urlGenerator = $this->createMock(IURLGenerator::class);
  94. $this->backend = $this->createMock(CalDavBackend::class);
  95. $this->provider = new TasksSearchProvider(
  96. $this->appManager,
  97. $this->l10n,
  98. $this->urlGenerator,
  99. $this->backend
  100. );
  101. }
  102. public function testGetId(): void {
  103. $this->assertEquals('tasks', $this->provider->getId());
  104. }
  105. public function testGetName(): void {
  106. $this->l10n->expects($this->exactly(1))
  107. ->method('t')
  108. ->with('Tasks')
  109. ->willReturnArgument(0);
  110. $this->assertEquals('Tasks', $this->provider->getName());
  111. }
  112. public function testSearchAppDisabled(): void {
  113. $user = $this->createMock(IUser::class);
  114. $query = $this->createMock(ISearchQuery::class);
  115. $this->appManager->expects($this->once())
  116. ->method('isEnabledForUser')
  117. ->with('tasks', $user)
  118. ->willReturn(false);
  119. $this->l10n->expects($this->exactly(1))
  120. ->method('t')
  121. ->willReturnArgument(0);
  122. $this->backend->expects($this->never())
  123. ->method('getCalendarsForUser');
  124. $this->backend->expects($this->never())
  125. ->method('getSubscriptionsForUser');
  126. $this->backend->expects($this->never())
  127. ->method('searchPrincipalUri');
  128. $actual = $this->provider->search($user, $query);
  129. $data = $actual->jsonSerialize();
  130. $this->assertInstanceOf(SearchResult::class, $actual);
  131. $this->assertEquals('Tasks', $data['name']);
  132. $this->assertEmpty($data['entries']);
  133. $this->assertFalse($data['isPaginated']);
  134. $this->assertNull($data['cursor']);
  135. }
  136. public function testSearch(): void {
  137. $user = $this->createMock(IUser::class);
  138. $user->method('getUID')->willReturn('john.doe');
  139. $query = $this->createMock(ISearchQuery::class);
  140. $query->method('getTerm')->willReturn('search term');
  141. $query->method('getLimit')->willReturn(5);
  142. $query->method('getCursor')->willReturn(20);
  143. $this->appManager->expects($this->once())
  144. ->method('isEnabledForUser')
  145. ->with('tasks', $user)
  146. ->willReturn(true);
  147. $this->l10n->method('t')->willReturnArgument(0);
  148. $this->backend->expects($this->once())
  149. ->method('getCalendarsForUser')
  150. ->with('principals/users/john.doe')
  151. ->willReturn([
  152. [
  153. 'id' => 99,
  154. 'principaluri' => 'principals/users/john.doe',
  155. 'uri' => 'calendar-uri-99',
  156. ], [
  157. 'id' => 123,
  158. 'principaluri' => 'principals/users/john.doe',
  159. 'uri' => 'calendar-uri-123',
  160. ]
  161. ]);
  162. $this->backend->expects($this->once())
  163. ->method('getSubscriptionsForUser')
  164. ->with('principals/users/john.doe')
  165. ->willReturn([
  166. [
  167. 'id' => 1337,
  168. 'principaluri' => 'principals/users/john.doe',
  169. 'uri' => 'subscription-uri-1337',
  170. ]
  171. ]);
  172. $this->backend->expects($this->once())
  173. ->method('searchPrincipalUri')
  174. ->with('principals/users/john.doe', '', ['VTODO'],
  175. ['SUMMARY', 'DESCRIPTION', 'CATEGORIES'],
  176. [],
  177. ['limit' => 5, 'offset' => 20, 'since' => null, 'until' => null])
  178. ->willReturn([
  179. [
  180. 'calendarid' => 99,
  181. 'calendartype' => CalDavBackend::CALENDAR_TYPE_CALENDAR,
  182. 'uri' => 'todo0.ics',
  183. 'calendardata' => $this->vTodo0,
  184. ],
  185. [
  186. 'calendarid' => 123,
  187. 'calendartype' => CalDavBackend::CALENDAR_TYPE_CALENDAR,
  188. 'uri' => 'todo1.ics',
  189. 'calendardata' => $this->vTodo1,
  190. ],
  191. [
  192. 'calendarid' => 1337,
  193. 'calendartype' => CalDavBackend::CALENDAR_TYPE_SUBSCRIPTION,
  194. 'uri' => 'todo2.ics',
  195. 'calendardata' => $this->vTodo2,
  196. ]
  197. ]);
  198. $provider = $this->getMockBuilder(TasksSearchProvider::class)
  199. ->setConstructorArgs([
  200. $this->appManager,
  201. $this->l10n,
  202. $this->urlGenerator,
  203. $this->backend,
  204. ])
  205. ->setMethods([
  206. 'getDeepLinkToTasksApp',
  207. 'generateSubline',
  208. ])
  209. ->getMock();
  210. $provider->expects($this->exactly(3))
  211. ->method('generateSubline')
  212. ->willReturn('subline');
  213. $provider->expects($this->exactly(3))
  214. ->method('getDeepLinkToTasksApp')
  215. ->withConsecutive(
  216. ['calendar-uri-99', 'todo0.ics'],
  217. ['calendar-uri-123', 'todo1.ics'],
  218. ['subscription-uri-1337', 'todo2.ics']
  219. )
  220. ->willReturn('deep-link-to-tasks');
  221. $actual = $provider->search($user, $query);
  222. $data = $actual->jsonSerialize();
  223. $this->assertInstanceOf(SearchResult::class, $actual);
  224. $this->assertEquals('Tasks', $data['name']);
  225. $this->assertCount(3, $data['entries']);
  226. $this->assertTrue($data['isPaginated']);
  227. $this->assertEquals(23, $data['cursor']);
  228. $result0 = $data['entries'][0];
  229. $result0Data = $result0->jsonSerialize();
  230. $result1 = $data['entries'][1];
  231. $result1Data = $result1->jsonSerialize();
  232. $result2 = $data['entries'][2];
  233. $result2Data = $result2->jsonSerialize();
  234. $this->assertInstanceOf(SearchResultEntry::class, $result0);
  235. $this->assertEmpty($result0Data['thumbnailUrl']);
  236. $this->assertEquals('Untitled task', $result0Data['title']);
  237. $this->assertEquals('subline', $result0Data['subline']);
  238. $this->assertEquals('deep-link-to-tasks', $result0Data['resourceUrl']);
  239. $this->assertEquals('icon-checkmark', $result0Data['icon']);
  240. $this->assertFalse($result0Data['rounded']);
  241. $this->assertInstanceOf(SearchResultEntry::class, $result1);
  242. $this->assertEmpty($result1Data['thumbnailUrl']);
  243. $this->assertEquals('Task title', $result1Data['title']);
  244. $this->assertEquals('subline', $result1Data['subline']);
  245. $this->assertEquals('deep-link-to-tasks', $result1Data['resourceUrl']);
  246. $this->assertEquals('icon-checkmark', $result1Data['icon']);
  247. $this->assertFalse($result1Data['rounded']);
  248. $this->assertInstanceOf(SearchResultEntry::class, $result2);
  249. $this->assertEmpty($result2Data['thumbnailUrl']);
  250. $this->assertEquals('Task title', $result2Data['title']);
  251. $this->assertEquals('subline', $result2Data['subline']);
  252. $this->assertEquals('deep-link-to-tasks', $result2Data['resourceUrl']);
  253. $this->assertEquals('icon-checkmark', $result2Data['icon']);
  254. $this->assertFalse($result2Data['rounded']);
  255. }
  256. public function testGetDeepLinkToTasksApp(): void {
  257. $this->urlGenerator->expects($this->once())
  258. ->method('linkToRoute')
  259. ->with('tasks.page.index')
  260. ->willReturn('link-to-route-tasks.index');
  261. $this->urlGenerator->expects($this->once())
  262. ->method('getAbsoluteURL')
  263. ->with('link-to-route-tasks.index#/calendars/uri-john.doe/tasks/task-uri.ics')
  264. ->willReturn('absolute-url-link-to-route-tasks.index#/calendars/uri-john.doe/tasks/task-uri.ics');
  265. $actual = self::invokePrivate($this->provider, 'getDeepLinkToTasksApp', ['uri-john.doe', 'task-uri.ics']);
  266. $this->assertEquals('absolute-url-link-to-route-tasks.index#/calendars/uri-john.doe/tasks/task-uri.ics', $actual);
  267. }
  268. /**
  269. * @param string $ics
  270. * @param string $expectedSubline
  271. *
  272. * @dataProvider generateSublineDataProvider
  273. */
  274. public function testGenerateSubline(string $ics, string $expectedSubline): void {
  275. $vCalendar = Reader::read($ics, Reader::OPTION_FORGIVING);
  276. $taskComponent = $vCalendar->VTODO;
  277. $this->l10n->method('t')->willReturnArgument(0);
  278. $this->l10n->method('l')->willReturnArgument('');
  279. $actual = self::invokePrivate($this->provider, 'generateSubline', [$taskComponent]);
  280. $this->assertEquals($expectedSubline, $actual);
  281. }
  282. public function generateSublineDataProvider(): array {
  283. return [
  284. [$this->vTodo0, ''],
  285. [$this->vTodo1, 'Completed on %s'],
  286. [$this->vTodo2, 'Completed on %s'],
  287. [$this->vTodo3, 'Due on %s'],
  288. [$this->vTodo4, 'Due on %s by %s'],
  289. ];
  290. }
  291. }