TasksSearchProviderTest.php 11 KB

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