CardDavBackendTest.php 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Arne Hamann <kontakt+github@arne.email>
  6. * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
  7. * @author Bjoern Schiessle <bjoern@schiessle.org>
  8. * @author Björn Schießle <bjoern@schiessle.org>
  9. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  10. * @author Georg Ehrke <oc.list@georgehrke.com>
  11. * @author Joas Schilling <coding@schilljs.com>
  12. * @author John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
  13. * @author Morris Jobke <hey@morrisjobke.de>
  14. * @author Robin Appelman <robin@icewind.nl>
  15. * @author Roeland Jago Douma <roeland@famdouma.nl>
  16. * @author Thomas Müller <thomas.mueller@tmit.eu>
  17. *
  18. * @license AGPL-3.0
  19. *
  20. * This code is free software: you can redistribute it and/or modify
  21. * it under the terms of the GNU Affero General Public License, version 3,
  22. * as published by the Free Software Foundation.
  23. *
  24. * This program is distributed in the hope that it will be useful,
  25. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  26. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  27. * GNU Affero General Public License for more details.
  28. *
  29. * You should have received a copy of the GNU Affero General Public License, version 3,
  30. * along with this program. If not, see <http://www.gnu.org/licenses/>
  31. *
  32. */
  33. namespace OCA\DAV\Tests\unit\CardDAV;
  34. use OCA\DAV\CalDAV\Proxy\ProxyMapper;
  35. use OCA\DAV\CardDAV\AddressBook;
  36. use OCA\DAV\CardDAV\CardDavBackend;
  37. use OCA\DAV\Connector\Sabre\Principal;
  38. use OCP\App\IAppManager;
  39. use OCP\DB\QueryBuilder\IQueryBuilder;
  40. use OCP\EventDispatcher\IEventDispatcher;
  41. use OCP\IConfig;
  42. use OCP\IDBConnection;
  43. use OCP\IGroupManager;
  44. use OCP\IL10N;
  45. use OCP\IUserManager;
  46. use OCP\IUserSession;
  47. use OCP\Share\IManager as ShareManager;
  48. use Sabre\DAV\Exception\BadRequest;
  49. use Sabre\DAV\PropPatch;
  50. use Sabre\VObject\Component\VCard;
  51. use Sabre\VObject\Property\Text;
  52. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  53. use Symfony\Component\EventDispatcher\GenericEvent;
  54. use Test\TestCase;
  55. /**
  56. * Class CardDavBackendTest
  57. *
  58. * @group DB
  59. *
  60. * @package OCA\DAV\Tests\unit\CardDAV
  61. */
  62. class CardDavBackendTest extends TestCase {
  63. /** @var CardDavBackend */
  64. private $backend;
  65. /** @var Principal | \PHPUnit\Framework\MockObject\MockObject */
  66. private $principal;
  67. /** @var IUserManager|\PHPUnit\Framework\MockObject\MockObject */
  68. private $userManager;
  69. /** @var IGroupManager|\PHPUnit\Framework\MockObject\MockObject */
  70. private $groupManager;
  71. /** @var EventDispatcherInterface|\PHPUnit\Framework\MockObject\MockObject */
  72. private $legacyDispatcher;
  73. /** @var IEventDispatcher|\PHPUnit\Framework\MockObject\MockObject */
  74. private $dispatcher;
  75. /** @var IDBConnection */
  76. private $db;
  77. /** @var string */
  78. private $dbCardsTable = 'cards';
  79. /** @var string */
  80. private $dbCardsPropertiesTable = 'cards_properties';
  81. public const UNIT_TEST_USER = 'principals/users/carddav-unit-test';
  82. public const UNIT_TEST_USER1 = 'principals/users/carddav-unit-test1';
  83. public const UNIT_TEST_GROUP = 'principals/groups/carddav-unit-test-group';
  84. private $vcardTest0 = 'BEGIN:VCARD'.PHP_EOL.
  85. 'VERSION:3.0'.PHP_EOL.
  86. 'PRODID:-//Sabre//Sabre VObject 4.1.2//EN'.PHP_EOL.
  87. 'UID:Test'.PHP_EOL.
  88. 'FN:Test'.PHP_EOL.
  89. 'N:Test;;;;'.PHP_EOL.
  90. 'END:VCARD';
  91. private $vcardTest1 = 'BEGIN:VCARD'.PHP_EOL.
  92. 'VERSION:3.0'.PHP_EOL.
  93. 'PRODID:-//Sabre//Sabre VObject 4.1.2//EN'.PHP_EOL.
  94. 'UID:Test2'.PHP_EOL.
  95. 'FN:Test2'.PHP_EOL.
  96. 'N:Test2;;;;'.PHP_EOL.
  97. 'END:VCARD';
  98. private $vcardTest2 = 'BEGIN:VCARD'.PHP_EOL.
  99. 'VERSION:3.0'.PHP_EOL.
  100. 'PRODID:-//Sabre//Sabre VObject 4.1.2//EN'.PHP_EOL.
  101. 'UID:Test3'.PHP_EOL.
  102. 'FN:Test3'.PHP_EOL.
  103. 'N:Test3;;;;'.PHP_EOL.
  104. 'END:VCARD';
  105. private $vcardTestNoUID = 'BEGIN:VCARD'.PHP_EOL.
  106. 'VERSION:3.0'.PHP_EOL.
  107. 'PRODID:-//Sabre//Sabre VObject 4.1.2//EN'.PHP_EOL.
  108. 'FN:TestNoUID'.PHP_EOL.
  109. 'N:TestNoUID;;;;'.PHP_EOL.
  110. 'END:VCARD';
  111. protected function setUp(): void {
  112. parent::setUp();
  113. $this->userManager = $this->createMock(IUserManager::class);
  114. $this->groupManager = $this->createMock(IGroupManager::class);
  115. $this->principal = $this->getMockBuilder(Principal::class)
  116. ->setConstructorArgs([
  117. $this->userManager,
  118. $this->groupManager,
  119. $this->createMock(ShareManager::class),
  120. $this->createMock(IUserSession::class),
  121. $this->createMock(IAppManager::class),
  122. $this->createMock(ProxyMapper::class),
  123. $this->createMock(IConfig::class),
  124. ])
  125. ->setMethods(['getPrincipalByPath', 'getGroupMembership'])
  126. ->getMock();
  127. $this->principal->method('getPrincipalByPath')
  128. ->willReturn([
  129. 'uri' => 'principals/best-friend',
  130. '{DAV:}displayname' => 'User\'s displayname',
  131. ]);
  132. $this->principal->method('getGroupMembership')
  133. ->withAnyParameters()
  134. ->willReturn([self::UNIT_TEST_GROUP]);
  135. $this->dispatcher = $this->createMock(IEventDispatcher::class);
  136. $this->legacyDispatcher = $this->createMock(EventDispatcherInterface::class);
  137. $this->db = \OC::$server->getDatabaseConnection();
  138. $this->backend = new CardDavBackend($this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher, $this->legacyDispatcher);
  139. // start every test with a empty cards_properties and cards table
  140. $query = $this->db->getQueryBuilder();
  141. $query->delete('cards_properties')->execute();
  142. $query = $this->db->getQueryBuilder();
  143. $query->delete('cards')->execute();
  144. $this->tearDown();
  145. }
  146. protected function tearDown(): void {
  147. parent::tearDown();
  148. if (is_null($this->backend)) {
  149. return;
  150. }
  151. $this->principal->method('getGroupMembership')
  152. ->withAnyParameters()
  153. ->willReturn([self::UNIT_TEST_GROUP]);
  154. $books = $this->backend->getAddressBooksForUser(self::UNIT_TEST_USER);
  155. foreach ($books as $book) {
  156. $this->backend->deleteAddressBook($book['id']);
  157. }
  158. }
  159. public function testAddressBookOperations() {
  160. // create a new address book
  161. $this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
  162. $this->assertEquals(1, $this->backend->getAddressBooksForUserCount(self::UNIT_TEST_USER));
  163. $books = $this->backend->getAddressBooksForUser(self::UNIT_TEST_USER);
  164. $this->assertEquals(1, count($books));
  165. $this->assertEquals('Example', $books[0]['{DAV:}displayname']);
  166. $this->assertEquals('User\'s displayname', $books[0]['{http://nextcloud.com/ns}owner-displayname']);
  167. // update it's display name
  168. $patch = new PropPatch([
  169. '{DAV:}displayname' => 'Unit test',
  170. '{urn:ietf:params:xml:ns:carddav}addressbook-description' => 'Addressbook used for unit testing'
  171. ]);
  172. $this->backend->updateAddressBook($books[0]['id'], $patch);
  173. $patch->commit();
  174. $books = $this->backend->getAddressBooksForUser(self::UNIT_TEST_USER);
  175. $this->assertEquals(1, count($books));
  176. $this->assertEquals('Unit test', $books[0]['{DAV:}displayname']);
  177. $this->assertEquals('Addressbook used for unit testing', $books[0]['{urn:ietf:params:xml:ns:carddav}addressbook-description']);
  178. // delete the address book
  179. $this->backend->deleteAddressBook($books[0]['id']);
  180. $books = $this->backend->getAddressBooksForUser(self::UNIT_TEST_USER);
  181. $this->assertEquals(0, count($books));
  182. }
  183. public function testAddressBookSharing() {
  184. $this->userManager->expects($this->any())
  185. ->method('userExists')
  186. ->willReturn(true);
  187. $this->groupManager->expects($this->any())
  188. ->method('groupExists')
  189. ->willReturn(true);
  190. $this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
  191. $books = $this->backend->getAddressBooksForUser(self::UNIT_TEST_USER);
  192. $this->assertEquals(1, count($books));
  193. $l = $this->createMock(IL10N::class);
  194. $addressBook = new AddressBook($this->backend, $books[0], $l);
  195. $this->backend->updateShares($addressBook, [
  196. [
  197. 'href' => 'principal:' . self::UNIT_TEST_USER1,
  198. ],
  199. [
  200. 'href' => 'principal:' . self::UNIT_TEST_GROUP,
  201. ]
  202. ], []);
  203. $books = $this->backend->getAddressBooksForUser(self::UNIT_TEST_USER1);
  204. $this->assertEquals(1, count($books));
  205. // delete the address book
  206. $this->backend->deleteAddressBook($books[0]['id']);
  207. $books = $this->backend->getAddressBooksForUser(self::UNIT_TEST_USER);
  208. $this->assertEquals(0, count($books));
  209. }
  210. public function testCardOperations() {
  211. /** @var CardDavBackend | \PHPUnit\Framework\MockObject\MockObject $backend */
  212. $backend = $this->getMockBuilder(CardDavBackend::class)
  213. ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher, $this->legacyDispatcher])
  214. ->setMethods(['updateProperties', 'purgeProperties'])->getMock();
  215. // create a new address book
  216. $backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
  217. $books = $backend->getAddressBooksForUser(self::UNIT_TEST_USER);
  218. $this->assertEquals(1, count($books));
  219. $bookId = $books[0]['id'];
  220. $uri = $this->getUniqueID('card');
  221. // updateProperties is expected twice, once for createCard and once for updateCard
  222. $backend->expects($this->at(0))->method('updateProperties')->with($bookId, $uri, $this->vcardTest0);
  223. $backend->expects($this->at(1))->method('updateProperties')->with($bookId, $uri, $this->vcardTest1);
  224. // Expect event
  225. $this->legacyDispatcher->expects($this->at(0))
  226. ->method('dispatch')
  227. ->with('\OCA\DAV\CardDAV\CardDavBackend::createCard', $this->callback(function (GenericEvent $e) use ($bookId, $uri) {
  228. return $e->getArgument('addressBookId') === $bookId &&
  229. $e->getArgument('cardUri') === $uri &&
  230. $e->getArgument('cardData') === $this->vcardTest0;
  231. }));
  232. // create a card
  233. $backend->createCard($bookId, $uri, $this->vcardTest0);
  234. // get all the cards
  235. $cards = $backend->getCards($bookId);
  236. $this->assertEquals(1, count($cards));
  237. $this->assertEquals($this->vcardTest0, $cards[0]['carddata']);
  238. // get the cards
  239. $card = $backend->getCard($bookId, $uri);
  240. $this->assertNotNull($card);
  241. $this->assertArrayHasKey('id', $card);
  242. $this->assertArrayHasKey('uri', $card);
  243. $this->assertArrayHasKey('lastmodified', $card);
  244. $this->assertArrayHasKey('etag', $card);
  245. $this->assertArrayHasKey('size', $card);
  246. $this->assertEquals($this->vcardTest0, $card['carddata']);
  247. // Expect event
  248. $this->legacyDispatcher->expects($this->at(0))
  249. ->method('dispatch')
  250. ->with('\OCA\DAV\CardDAV\CardDavBackend::updateCard', $this->callback(function (GenericEvent $e) use ($bookId, $uri) {
  251. return $e->getArgument('addressBookId') === $bookId &&
  252. $e->getArgument('cardUri') === $uri &&
  253. $e->getArgument('cardData') === $this->vcardTest1;
  254. }));
  255. // update the card
  256. $backend->updateCard($bookId, $uri, $this->vcardTest1);
  257. $card = $backend->getCard($bookId, $uri);
  258. $this->assertEquals($this->vcardTest1, $card['carddata']);
  259. // Expect event
  260. $this->legacyDispatcher->expects($this->at(0))
  261. ->method('dispatch')
  262. ->with('\OCA\DAV\CardDAV\CardDavBackend::deleteCard', $this->callback(function (GenericEvent $e) use ($bookId, $uri) {
  263. return $e->getArgument('addressBookId') === $bookId &&
  264. $e->getArgument('cardUri') === $uri;
  265. }));
  266. // delete the card
  267. $backend->expects($this->once())->method('purgeProperties')->with($bookId, $card['id']);
  268. $backend->deleteCard($bookId, $uri);
  269. $cards = $backend->getCards($bookId);
  270. $this->assertEquals(0, count($cards));
  271. }
  272. public function testMultiCard() {
  273. $this->backend = $this->getMockBuilder(CardDavBackend::class)
  274. ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher, $this->legacyDispatcher])
  275. ->setMethods(['updateProperties'])->getMock();
  276. // create a new address book
  277. $this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
  278. $books = $this->backend->getAddressBooksForUser(self::UNIT_TEST_USER);
  279. $this->assertEquals(1, count($books));
  280. $bookId = $books[0]['id'];
  281. // create a card
  282. $uri0 = self::getUniqueID('card');
  283. $this->backend->createCard($bookId, $uri0, $this->vcardTest0);
  284. $uri1 = self::getUniqueID('card');
  285. $this->backend->createCard($bookId, $uri1, $this->vcardTest1);
  286. $uri2 = self::getUniqueID('card');
  287. $this->backend->createCard($bookId, $uri2, $this->vcardTest2);
  288. // get all the cards
  289. $cards = $this->backend->getCards($bookId);
  290. $this->assertEquals(3, count($cards));
  291. usort($cards, function ($a, $b) {
  292. return $a['id'] < $b['id'] ? -1 : 1;
  293. });
  294. $this->assertEquals($this->vcardTest0, $cards[0]['carddata']);
  295. $this->assertEquals($this->vcardTest1, $cards[1]['carddata']);
  296. $this->assertEquals($this->vcardTest2, $cards[2]['carddata']);
  297. // get the cards 1 & 2 (not 0)
  298. $cards = $this->backend->getMultipleCards($bookId, [$uri1, $uri2]);
  299. $this->assertEquals(2, count($cards));
  300. usort($cards, function ($a, $b) {
  301. return $a['id'] < $b['id'] ? -1 : 1;
  302. });
  303. foreach ($cards as $index => $card) {
  304. $this->assertArrayHasKey('id', $card);
  305. $this->assertArrayHasKey('uri', $card);
  306. $this->assertArrayHasKey('lastmodified', $card);
  307. $this->assertArrayHasKey('etag', $card);
  308. $this->assertArrayHasKey('size', $card);
  309. $this->assertEquals($this->{ 'vcardTest'.($index+1) }, $card['carddata']);
  310. }
  311. // delete the card
  312. $this->backend->deleteCard($bookId, $uri0);
  313. $this->backend->deleteCard($bookId, $uri1);
  314. $this->backend->deleteCard($bookId, $uri2);
  315. $cards = $this->backend->getCards($bookId);
  316. $this->assertEquals(0, count($cards));
  317. }
  318. public function testMultipleUIDOnDifferentAddressbooks() {
  319. $this->backend = $this->getMockBuilder(CardDavBackend::class)
  320. ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher, $this->legacyDispatcher])
  321. ->setMethods(['updateProperties'])->getMock();
  322. // create 2 new address books
  323. $this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
  324. $this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example2', []);
  325. $books = $this->backend->getAddressBooksForUser(self::UNIT_TEST_USER);
  326. $this->assertEquals(2, count($books));
  327. $bookId0 = $books[0]['id'];
  328. $bookId1 = $books[1]['id'];
  329. // create a card
  330. $uri0 = $this->getUniqueID('card');
  331. $this->backend->createCard($bookId0, $uri0, $this->vcardTest0);
  332. // create another card with same uid but in second address book
  333. $uri1 = $this->getUniqueID('card');
  334. $this->backend->createCard($bookId1, $uri1, $this->vcardTest0);
  335. }
  336. public function testMultipleUIDDenied() {
  337. $this->backend = $this->getMockBuilder(CardDavBackend::class)
  338. ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher, $this->legacyDispatcher])
  339. ->setMethods(['updateProperties'])->getMock();
  340. // create a new address book
  341. $this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
  342. $books = $this->backend->getAddressBooksForUser(self::UNIT_TEST_USER);
  343. $this->assertEquals(1, count($books));
  344. $bookId = $books[0]['id'];
  345. // create a card
  346. $uri0 = $this->getUniqueID('card');
  347. $this->backend->createCard($bookId, $uri0, $this->vcardTest0);
  348. // create another card with same uid
  349. $uri1 = $this->getUniqueID('card');
  350. $this->expectException(BadRequest::class);
  351. $test = $this->backend->createCard($bookId, $uri1, $this->vcardTest0);
  352. }
  353. public function testNoValidUID() {
  354. $this->backend = $this->getMockBuilder(CardDavBackend::class)
  355. ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher, $this->legacyDispatcher])
  356. ->setMethods(['updateProperties'])->getMock();
  357. // create a new address book
  358. $this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
  359. $books = $this->backend->getAddressBooksForUser(self::UNIT_TEST_USER);
  360. $this->assertEquals(1, count($books));
  361. $bookId = $books[0]['id'];
  362. // create a card without uid
  363. $uri1 = $this->getUniqueID('card');
  364. $this->expectException(BadRequest::class);
  365. $test = $this->backend->createCard($bookId, $uri1, $this->vcardTestNoUID);
  366. }
  367. public function testDeleteWithoutCard() {
  368. $this->backend = $this->getMockBuilder(CardDavBackend::class)
  369. ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher, $this->legacyDispatcher])
  370. ->setMethods([
  371. 'getCardId',
  372. 'addChange',
  373. 'purgeProperties',
  374. 'updateProperties',
  375. ])
  376. ->getMock();
  377. // create a new address book
  378. $this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
  379. $books = $this->backend->getUsersOwnAddressBooks(self::UNIT_TEST_USER);
  380. $this->assertEquals(1, count($books));
  381. $bookId = $books[0]['id'];
  382. $uri = $this->getUniqueID('card');
  383. // create a new address book
  384. $this->backend->expects($this->once())
  385. ->method('getCardId')
  386. ->with($bookId, $uri)
  387. ->willThrowException(new \InvalidArgumentException());
  388. $this->backend->expects($this->exactly(2))
  389. ->method('addChange')
  390. ->withConsecutive(
  391. [$bookId, $uri, 1],
  392. [$bookId, $uri, 3]
  393. );
  394. $this->backend->expects($this->never())
  395. ->method('purgeProperties');
  396. // create a card
  397. $this->backend->createCard($bookId, $uri, $this->vcardTest0);
  398. // delete the card
  399. $this->assertTrue($this->backend->deleteCard($bookId, $uri));
  400. }
  401. public function testSyncSupport() {
  402. $this->backend = $this->getMockBuilder(CardDavBackend::class)
  403. ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher, $this->legacyDispatcher])
  404. ->setMethods(['updateProperties'])->getMock();
  405. // create a new address book
  406. $this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
  407. $books = $this->backend->getAddressBooksForUser(self::UNIT_TEST_USER);
  408. $this->assertEquals(1, count($books));
  409. $bookId = $books[0]['id'];
  410. // fist call without synctoken
  411. $changes = $this->backend->getChangesForAddressBook($bookId, '', 1);
  412. $syncToken = $changes['syncToken'];
  413. // add a change
  414. $uri0 = $this->getUniqueID('card');
  415. $this->backend->createCard($bookId, $uri0, $this->vcardTest0);
  416. // look for changes
  417. $changes = $this->backend->getChangesForAddressBook($bookId, $syncToken, 1);
  418. $this->assertEquals($uri0, $changes['added'][0]);
  419. }
  420. public function testSharing() {
  421. $this->userManager->expects($this->any())
  422. ->method('userExists')
  423. ->willReturn(true);
  424. $this->groupManager->expects($this->any())
  425. ->method('groupExists')
  426. ->willReturn(true);
  427. $this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
  428. $books = $this->backend->getAddressBooksForUser(self::UNIT_TEST_USER);
  429. $this->assertEquals(1, count($books));
  430. $l = $this->createMock(IL10N::class);
  431. $exampleBook = new AddressBook($this->backend, $books[0], $l);
  432. $this->backend->updateShares($exampleBook, [['href' => 'principal:' . self::UNIT_TEST_USER1]], []);
  433. $shares = $this->backend->getShares($exampleBook->getResourceId());
  434. $this->assertEquals(1, count($shares));
  435. // adding the same sharee again has no effect
  436. $this->backend->updateShares($exampleBook, [['href' => 'principal:' . self::UNIT_TEST_USER1]], []);
  437. $shares = $this->backend->getShares($exampleBook->getResourceId());
  438. $this->assertEquals(1, count($shares));
  439. $books = $this->backend->getAddressBooksForUser(self::UNIT_TEST_USER1);
  440. $this->assertEquals(1, count($books));
  441. $this->backend->updateShares($exampleBook, [], ['principal:' . self::UNIT_TEST_USER1]);
  442. $shares = $this->backend->getShares($exampleBook->getResourceId());
  443. $this->assertEquals(0, count($shares));
  444. $books = $this->backend->getAddressBooksForUser(self::UNIT_TEST_USER1);
  445. $this->assertEquals(0, count($books));
  446. }
  447. public function testUpdateProperties() {
  448. $bookId = 42;
  449. $cardUri = 'card-uri';
  450. $cardId = 2;
  451. $backend = $this->getMockBuilder(CardDavBackend::class)
  452. ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher, $this->legacyDispatcher])
  453. ->setMethods(['getCardId'])->getMock();
  454. $backend->expects($this->any())->method('getCardId')->willReturn($cardId);
  455. // add properties for new vCard
  456. $vCard = new VCard();
  457. $vCard->UID = $cardUri;
  458. $vCard->FN = 'John Doe';
  459. $this->invokePrivate($backend, 'updateProperties', [$bookId, $cardUri, $vCard->serialize()]);
  460. $query = $this->db->getQueryBuilder();
  461. $query->select('*')
  462. ->from('cards_properties');
  463. $qResult = $query->execute();
  464. $result = $qResult->fetchAll();
  465. $qResult->closeCursor();
  466. $this->assertSame(2, count($result));
  467. $this->assertSame('UID', $result[0]['name']);
  468. $this->assertSame($cardUri, $result[0]['value']);
  469. $this->assertSame($bookId, (int)$result[0]['addressbookid']);
  470. $this->assertSame($cardId, (int)$result[0]['cardid']);
  471. $this->assertSame('FN', $result[1]['name']);
  472. $this->assertSame('John Doe', $result[1]['value']);
  473. $this->assertSame($bookId, (int)$result[1]['addressbookid']);
  474. $this->assertSame($cardId, (int)$result[1]['cardid']);
  475. // update properties for existing vCard
  476. $vCard = new VCard();
  477. $vCard->UID = $cardUri;
  478. $this->invokePrivate($backend, 'updateProperties', [$bookId, $cardUri, $vCard->serialize()]);
  479. $query = $this->db->getQueryBuilder();
  480. $query->select('*')
  481. ->from('cards_properties');
  482. $qResult = $query->execute();
  483. $result = $qResult->fetchAll();
  484. $qResult->closeCursor();
  485. $this->assertSame(1, count($result));
  486. $this->assertSame('UID', $result[0]['name']);
  487. $this->assertSame($cardUri, $result[0]['value']);
  488. $this->assertSame($bookId, (int)$result[0]['addressbookid']);
  489. $this->assertSame($cardId, (int)$result[0]['cardid']);
  490. }
  491. public function testPurgeProperties() {
  492. $query = $this->db->getQueryBuilder();
  493. $query->insert('cards_properties')
  494. ->values(
  495. [
  496. 'addressbookid' => $query->createNamedParameter(1),
  497. 'cardid' => $query->createNamedParameter(1),
  498. 'name' => $query->createNamedParameter('name1'),
  499. 'value' => $query->createNamedParameter('value1'),
  500. 'preferred' => $query->createNamedParameter(0)
  501. ]
  502. );
  503. $query->execute();
  504. $query = $this->db->getQueryBuilder();
  505. $query->insert('cards_properties')
  506. ->values(
  507. [
  508. 'addressbookid' => $query->createNamedParameter(1),
  509. 'cardid' => $query->createNamedParameter(2),
  510. 'name' => $query->createNamedParameter('name2'),
  511. 'value' => $query->createNamedParameter('value2'),
  512. 'preferred' => $query->createNamedParameter(0)
  513. ]
  514. );
  515. $query->execute();
  516. $this->invokePrivate($this->backend, 'purgeProperties', [1, 1]);
  517. $query = $this->db->getQueryBuilder();
  518. $query->select('*')
  519. ->from('cards_properties');
  520. $qResult = $query->execute();
  521. $result = $qResult->fetchAll();
  522. $qResult->closeCursor();
  523. $this->assertSame(1, count($result));
  524. $this->assertSame(1 ,(int)$result[0]['addressbookid']);
  525. $this->assertSame(2 ,(int)$result[0]['cardid']);
  526. }
  527. public function testGetCardId() {
  528. $query = $this->db->getQueryBuilder();
  529. $query->insert('cards')
  530. ->values(
  531. [
  532. 'addressbookid' => $query->createNamedParameter(1),
  533. 'carddata' => $query->createNamedParameter(''),
  534. 'uri' => $query->createNamedParameter('uri'),
  535. 'lastmodified' => $query->createNamedParameter(4738743),
  536. 'etag' => $query->createNamedParameter('etag'),
  537. 'size' => $query->createNamedParameter(120)
  538. ]
  539. );
  540. $query->execute();
  541. $id = $query->getLastInsertId();
  542. $this->assertSame($id,
  543. $this->invokePrivate($this->backend, 'getCardId', [1, 'uri']));
  544. }
  545. public function testGetCardIdFailed() {
  546. $this->expectException(\InvalidArgumentException::class);
  547. $this->invokePrivate($this->backend, 'getCardId', [1, 'uri']);
  548. }
  549. /**
  550. * @dataProvider dataTestSearch
  551. *
  552. * @param string $pattern
  553. * @param array $properties
  554. * @param array $options
  555. * @param array $expected
  556. */
  557. public function testSearch($pattern, $properties, $options, $expected) {
  558. /** @var VCard $vCards */
  559. $vCards = [];
  560. $vCards[0] = new VCard();
  561. $vCards[0]->add(new Text($vCards[0], 'UID', 'uid'));
  562. $vCards[0]->add(new Text($vCards[0], 'FN', 'John Doe'));
  563. $vCards[0]->add(new Text($vCards[0], 'CLOUD', 'john@nextcloud.com'));
  564. $vCards[1] = new VCard();
  565. $vCards[1]->add(new Text($vCards[1], 'UID', 'uid'));
  566. $vCards[1]->add(new Text($vCards[1], 'FN', 'John M. Doe'));
  567. $vCards[2] = new VCard();
  568. $vCards[2]->add(new Text($vCards[2], 'UID', 'uid'));
  569. $vCards[2]->add(new Text($vCards[2], 'FN', 'find without options'));
  570. $vCards[2]->add(new Text($vCards[2], 'CLOUD', 'peter_pan@nextcloud.com'));
  571. $vCardIds = [];
  572. $query = $this->db->getQueryBuilder();
  573. for ($i=0; $i < 3; $i++) {
  574. $query->insert($this->dbCardsTable)
  575. ->values(
  576. [
  577. 'addressbookid' => $query->createNamedParameter(0),
  578. 'carddata' => $query->createNamedParameter($vCards[$i]->serialize(), IQueryBuilder::PARAM_LOB),
  579. 'uri' => $query->createNamedParameter('uri' . $i),
  580. 'lastmodified' => $query->createNamedParameter(time()),
  581. 'etag' => $query->createNamedParameter('etag' . $i),
  582. 'size' => $query->createNamedParameter(120),
  583. ]
  584. );
  585. $query->execute();
  586. $vCardIds[] = $query->getLastInsertId();
  587. }
  588. $query->insert($this->dbCardsPropertiesTable)
  589. ->values(
  590. [
  591. 'addressbookid' => $query->createNamedParameter(0),
  592. 'cardid' => $query->createNamedParameter($vCardIds[0]),
  593. 'name' => $query->createNamedParameter('FN'),
  594. 'value' => $query->createNamedParameter('John Doe'),
  595. 'preferred' => $query->createNamedParameter(0)
  596. ]
  597. );
  598. $query->execute();
  599. $query->insert($this->dbCardsPropertiesTable)
  600. ->values(
  601. [
  602. 'addressbookid' => $query->createNamedParameter(0),
  603. 'cardid' => $query->createNamedParameter($vCardIds[0]),
  604. 'name' => $query->createNamedParameter('CLOUD'),
  605. 'value' => $query->createNamedParameter('John@nextcloud.com'),
  606. 'preferred' => $query->createNamedParameter(0)
  607. ]
  608. );
  609. $query->execute();
  610. $query->insert($this->dbCardsPropertiesTable)
  611. ->values(
  612. [
  613. 'addressbookid' => $query->createNamedParameter(0),
  614. 'cardid' => $query->createNamedParameter($vCardIds[1]),
  615. 'name' => $query->createNamedParameter('FN'),
  616. 'value' => $query->createNamedParameter('John M. Doe'),
  617. 'preferred' => $query->createNamedParameter(0)
  618. ]
  619. );
  620. $query->execute();
  621. $query->insert($this->dbCardsPropertiesTable)
  622. ->values(
  623. [
  624. 'addressbookid' => $query->createNamedParameter(0),
  625. 'cardid' => $query->createNamedParameter($vCardIds[2]),
  626. 'name' => $query->createNamedParameter('FN'),
  627. 'value' => $query->createNamedParameter('find without options'),
  628. 'preferred' => $query->createNamedParameter(0)
  629. ]
  630. );
  631. $query->execute();
  632. $query->insert($this->dbCardsPropertiesTable)
  633. ->values(
  634. [
  635. 'addressbookid' => $query->createNamedParameter(0),
  636. 'cardid' => $query->createNamedParameter($vCardIds[2]),
  637. 'name' => $query->createNamedParameter('CLOUD'),
  638. 'value' => $query->createNamedParameter('peter_pan@nextcloud.com'),
  639. 'preferred' => $query->createNamedParameter(0)
  640. ]
  641. );
  642. $query->execute();
  643. $result = $this->backend->search(0, $pattern, $properties, $options);
  644. // check result
  645. $this->assertSame(count($expected), count($result));
  646. $found = [];
  647. foreach ($result as $r) {
  648. foreach ($expected as $exp) {
  649. if ($r['uri'] === $exp[0] && strpos($r['carddata'], $exp[1]) > 0) {
  650. $found[$exp[1]] = true;
  651. break;
  652. }
  653. }
  654. }
  655. $this->assertSame(count($expected), count($found));
  656. }
  657. public function dataTestSearch() {
  658. return [
  659. ['John', ['FN'], [], [['uri0', 'John Doe'], ['uri1', 'John M. Doe']]],
  660. ['M. Doe', ['FN'], [], [['uri1', 'John M. Doe']]],
  661. ['Do', ['FN'], [], [['uri0', 'John Doe'], ['uri1', 'John M. Doe']]],
  662. 'check if duplicates are handled correctly' => ['John', ['FN', 'CLOUD'], [], [['uri0', 'John Doe'], ['uri1', 'John M. Doe']]],
  663. 'case insensitive' => ['john', ['FN'], [], [['uri0', 'John Doe'], ['uri1', 'John M. Doe']]],
  664. 'limit' => ['john', ['FN'], ['limit' => 1], [['uri0', 'John Doe']]],
  665. 'limit and offset' => ['john', ['FN'], ['limit' => 1, 'offset' => 1], [['uri1', 'John M. Doe']]],
  666. 'find "_" escaped' => ['_', ['CLOUD'], [], [['uri2', 'find without options']]],
  667. 'find not empty CLOUD' => ['%_%', ['CLOUD'], ['escape_like_param'=>false], [['uri0', 'John Doe'], ['uri2', 'find without options']]],
  668. ];
  669. }
  670. public function testGetCardUri() {
  671. $query = $this->db->getQueryBuilder();
  672. $query->insert($this->dbCardsTable)
  673. ->values(
  674. [
  675. 'addressbookid' => $query->createNamedParameter(1),
  676. 'carddata' => $query->createNamedParameter('carddata', IQueryBuilder::PARAM_LOB),
  677. 'uri' => $query->createNamedParameter('uri'),
  678. 'lastmodified' => $query->createNamedParameter(5489543),
  679. 'etag' => $query->createNamedParameter('etag'),
  680. 'size' => $query->createNamedParameter(120),
  681. ]
  682. );
  683. $query->execute();
  684. $id = $query->getLastInsertId();
  685. $this->assertSame('uri', $this->backend->getCardUri($id));
  686. }
  687. public function testGetCardUriFailed() {
  688. $this->expectException(\InvalidArgumentException::class);
  689. $this->backend->getCardUri(1);
  690. }
  691. public function testGetContact() {
  692. $query = $this->db->getQueryBuilder();
  693. for ($i=0; $i<2; $i++) {
  694. $query->insert($this->dbCardsTable)
  695. ->values(
  696. [
  697. 'addressbookid' => $query->createNamedParameter($i),
  698. 'carddata' => $query->createNamedParameter('carddata' . $i, IQueryBuilder::PARAM_LOB),
  699. 'uri' => $query->createNamedParameter('uri' . $i),
  700. 'lastmodified' => $query->createNamedParameter(5489543),
  701. 'etag' => $query->createNamedParameter('etag' . $i),
  702. 'size' => $query->createNamedParameter(120),
  703. ]
  704. );
  705. $query->execute();
  706. }
  707. $result = $this->backend->getContact(0, 'uri0');
  708. $this->assertSame(8, count($result));
  709. $this->assertSame(0, (int)$result['addressbookid']);
  710. $this->assertSame('uri0', $result['uri']);
  711. $this->assertSame(5489543, (int)$result['lastmodified']);
  712. $this->assertSame('"etag0"', $result['etag']);
  713. $this->assertSame(120, (int)$result['size']);
  714. // this shouldn't return any result because 'uri1' is in address book 1
  715. // see https://github.com/nextcloud/server/issues/229
  716. $result = $this->backend->getContact(0, 'uri1');
  717. $this->assertEmpty($result);
  718. }
  719. public function testGetContactFail() {
  720. $this->assertEmpty($this->backend->getContact(0, 'uri'));
  721. }
  722. public function testCollectCardProperties() {
  723. $query = $this->db->getQueryBuilder();
  724. $query->insert($this->dbCardsPropertiesTable)
  725. ->values(
  726. [
  727. 'addressbookid' => $query->createNamedParameter(666),
  728. 'cardid' => $query->createNamedParameter(777),
  729. 'name' => $query->createNamedParameter('FN'),
  730. 'value' => $query->createNamedParameter('John Doe'),
  731. 'preferred' => $query->createNamedParameter(0)
  732. ]
  733. )
  734. ->execute();
  735. $result = $this->backend->collectCardProperties(666, 'FN');
  736. $this->assertEquals(['John Doe'], $result);
  737. }
  738. }