You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

CardDavBackendTest.php 30KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853
  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. $result = $query->select('*')->from('cards_properties')->execute()->fetchAll();
  462. $this->assertSame(2, count($result));
  463. $this->assertSame('UID', $result[0]['name']);
  464. $this->assertSame($cardUri, $result[0]['value']);
  465. $this->assertSame($bookId, (int)$result[0]['addressbookid']);
  466. $this->assertSame($cardId, (int)$result[0]['cardid']);
  467. $this->assertSame('FN', $result[1]['name']);
  468. $this->assertSame('John Doe', $result[1]['value']);
  469. $this->assertSame($bookId, (int)$result[1]['addressbookid']);
  470. $this->assertSame($cardId, (int)$result[1]['cardid']);
  471. // update properties for existing vCard
  472. $vCard = new VCard();
  473. $vCard->UID = $cardUri;
  474. $this->invokePrivate($backend, 'updateProperties', [$bookId, $cardUri, $vCard->serialize()]);
  475. $query = $this->db->getQueryBuilder();
  476. $result = $query->select('*')->from('cards_properties')->execute()->fetchAll();
  477. $this->assertSame(1, count($result));
  478. $this->assertSame('UID', $result[0]['name']);
  479. $this->assertSame($cardUri, $result[0]['value']);
  480. $this->assertSame($bookId, (int)$result[0]['addressbookid']);
  481. $this->assertSame($cardId, (int)$result[0]['cardid']);
  482. }
  483. public function testPurgeProperties() {
  484. $query = $this->db->getQueryBuilder();
  485. $query->insert('cards_properties')
  486. ->values(
  487. [
  488. 'addressbookid' => $query->createNamedParameter(1),
  489. 'cardid' => $query->createNamedParameter(1),
  490. 'name' => $query->createNamedParameter('name1'),
  491. 'value' => $query->createNamedParameter('value1'),
  492. 'preferred' => $query->createNamedParameter(0)
  493. ]
  494. );
  495. $query->execute();
  496. $query = $this->db->getQueryBuilder();
  497. $query->insert('cards_properties')
  498. ->values(
  499. [
  500. 'addressbookid' => $query->createNamedParameter(1),
  501. 'cardid' => $query->createNamedParameter(2),
  502. 'name' => $query->createNamedParameter('name2'),
  503. 'value' => $query->createNamedParameter('value2'),
  504. 'preferred' => $query->createNamedParameter(0)
  505. ]
  506. );
  507. $query->execute();
  508. $this->invokePrivate($this->backend, 'purgeProperties', [1, 1]);
  509. $query = $this->db->getQueryBuilder();
  510. $result = $query->select('*')->from('cards_properties')->execute()->fetchAll();
  511. $this->assertSame(1, count($result));
  512. $this->assertSame(1 ,(int)$result[0]['addressbookid']);
  513. $this->assertSame(2 ,(int)$result[0]['cardid']);
  514. }
  515. public function testGetCardId() {
  516. $query = $this->db->getQueryBuilder();
  517. $query->insert('cards')
  518. ->values(
  519. [
  520. 'addressbookid' => $query->createNamedParameter(1),
  521. 'carddata' => $query->createNamedParameter(''),
  522. 'uri' => $query->createNamedParameter('uri'),
  523. 'lastmodified' => $query->createNamedParameter(4738743),
  524. 'etag' => $query->createNamedParameter('etag'),
  525. 'size' => $query->createNamedParameter(120)
  526. ]
  527. );
  528. $query->execute();
  529. $id = $query->getLastInsertId();
  530. $this->assertSame($id,
  531. $this->invokePrivate($this->backend, 'getCardId', [1, 'uri']));
  532. }
  533. public function testGetCardIdFailed() {
  534. $this->expectException(\InvalidArgumentException::class);
  535. $this->invokePrivate($this->backend, 'getCardId', [1, 'uri']);
  536. }
  537. /**
  538. * @dataProvider dataTestSearch
  539. *
  540. * @param string $pattern
  541. * @param array $properties
  542. * @param array $options
  543. * @param array $expected
  544. */
  545. public function testSearch($pattern, $properties, $options, $expected) {
  546. /** @var VCard $vCards */
  547. $vCards = [];
  548. $vCards[0] = new VCard();
  549. $vCards[0]->add(new Text($vCards[0], 'UID', 'uid'));
  550. $vCards[0]->add(new Text($vCards[0], 'FN', 'John Doe'));
  551. $vCards[0]->add(new Text($vCards[0], 'CLOUD', 'john@nextcloud.com'));
  552. $vCards[1] = new VCard();
  553. $vCards[1]->add(new Text($vCards[1], 'UID', 'uid'));
  554. $vCards[1]->add(new Text($vCards[1], 'FN', 'John M. Doe'));
  555. $vCards[2] = new VCard();
  556. $vCards[2]->add(new Text($vCards[2], 'UID', 'uid'));
  557. $vCards[2]->add(new Text($vCards[2], 'FN', 'find without options'));
  558. $vCards[2]->add(new Text($vCards[2], 'CLOUD', 'peter_pan@nextcloud.com'));
  559. $vCardIds = [];
  560. $query = $this->db->getQueryBuilder();
  561. for ($i = 0; $i < 3; $i++) {
  562. $query->insert($this->dbCardsTable)
  563. ->values(
  564. [
  565. 'addressbookid' => $query->createNamedParameter(0),
  566. 'carddata' => $query->createNamedParameter($vCards[$i]->serialize(), IQueryBuilder::PARAM_LOB),
  567. 'uri' => $query->createNamedParameter('uri' . $i),
  568. 'lastmodified' => $query->createNamedParameter(time()),
  569. 'etag' => $query->createNamedParameter('etag' . $i),
  570. 'size' => $query->createNamedParameter(120),
  571. ]
  572. );
  573. $query->execute();
  574. $vCardIds[] = $query->getLastInsertId();
  575. }
  576. $query->insert($this->dbCardsPropertiesTable)
  577. ->values(
  578. [
  579. 'addressbookid' => $query->createNamedParameter(0),
  580. 'cardid' => $query->createNamedParameter($vCardIds[0]),
  581. 'name' => $query->createNamedParameter('FN'),
  582. 'value' => $query->createNamedParameter('John Doe'),
  583. 'preferred' => $query->createNamedParameter(0)
  584. ]
  585. );
  586. $query->execute();
  587. $query->insert($this->dbCardsPropertiesTable)
  588. ->values(
  589. [
  590. 'addressbookid' => $query->createNamedParameter(0),
  591. 'cardid' => $query->createNamedParameter($vCardIds[0]),
  592. 'name' => $query->createNamedParameter('CLOUD'),
  593. 'value' => $query->createNamedParameter('John@nextcloud.com'),
  594. 'preferred' => $query->createNamedParameter(0)
  595. ]
  596. );
  597. $query->execute();
  598. $query->insert($this->dbCardsPropertiesTable)
  599. ->values(
  600. [
  601. 'addressbookid' => $query->createNamedParameter(0),
  602. 'cardid' => $query->createNamedParameter($vCardIds[1]),
  603. 'name' => $query->createNamedParameter('FN'),
  604. 'value' => $query->createNamedParameter('John M. Doe'),
  605. 'preferred' => $query->createNamedParameter(0)
  606. ]
  607. );
  608. $query->execute();
  609. $query->insert($this->dbCardsPropertiesTable)
  610. ->values(
  611. [
  612. 'addressbookid' => $query->createNamedParameter(0),
  613. 'cardid' => $query->createNamedParameter($vCardIds[2]),
  614. 'name' => $query->createNamedParameter('FN'),
  615. 'value' => $query->createNamedParameter('find without options'),
  616. 'preferred' => $query->createNamedParameter(0)
  617. ]
  618. );
  619. $query->execute();
  620. $query->insert($this->dbCardsPropertiesTable)
  621. ->values(
  622. [
  623. 'addressbookid' => $query->createNamedParameter(0),
  624. 'cardid' => $query->createNamedParameter($vCardIds[2]),
  625. 'name' => $query->createNamedParameter('CLOUD'),
  626. 'value' => $query->createNamedParameter('peter_pan@nextcloud.com'),
  627. 'preferred' => $query->createNamedParameter(0)
  628. ]
  629. );
  630. $query->execute();
  631. $result = $this->backend->search(0, $pattern, $properties, $options);
  632. // check result
  633. $this->assertSame(count($expected), count($result));
  634. $found = [];
  635. foreach ($result as $r) {
  636. foreach ($expected as $exp) {
  637. if ($r['uri'] === $exp[0] && strpos($r['carddata'], $exp[1]) > 0) {
  638. $found[$exp[1]] = true;
  639. break;
  640. }
  641. }
  642. }
  643. $this->assertSame(count($expected), count($found));
  644. }
  645. public function dataTestSearch() {
  646. return [
  647. ['John', ['FN'], [], [['uri0', 'John Doe'], ['uri1', 'John M. Doe']]],
  648. ['M. Doe', ['FN'], [], [['uri1', 'John M. Doe']]],
  649. ['Do', ['FN'], [], [['uri0', 'John Doe'], ['uri1', 'John M. Doe']]],
  650. 'check if duplicates are handled correctly' => ['John', ['FN', 'CLOUD'], [], [['uri0', 'John Doe'], ['uri1', 'John M. Doe']]],
  651. 'case insensitive' => ['john', ['FN'], [], [['uri0', 'John Doe'], ['uri1', 'John M. Doe']]],
  652. 'limit' => ['john', ['FN'], ['limit' => 1], [['uri0', 'John Doe']]],
  653. 'limit and offset' => ['john', ['FN'], ['limit' => 1, 'offset' => 1], [['uri1', 'John M. Doe']]],
  654. 'find "_" escaped' => ['_', ['CLOUD'], [], [['uri2', 'find without options']]],
  655. 'find not empty CLOUD' => ['%_%', ['CLOUD'], ['escape_like_param' => false], [['uri0', 'John Doe'], ['uri2', 'find without options']]],
  656. ];
  657. }
  658. public function testGetCardUri() {
  659. $query = $this->db->getQueryBuilder();
  660. $query->insert($this->dbCardsTable)
  661. ->values(
  662. [
  663. 'addressbookid' => $query->createNamedParameter(1),
  664. 'carddata' => $query->createNamedParameter('carddata', IQueryBuilder::PARAM_LOB),
  665. 'uri' => $query->createNamedParameter('uri'),
  666. 'lastmodified' => $query->createNamedParameter(5489543),
  667. 'etag' => $query->createNamedParameter('etag'),
  668. 'size' => $query->createNamedParameter(120),
  669. ]
  670. );
  671. $query->execute();
  672. $id = $query->getLastInsertId();
  673. $this->assertSame('uri', $this->backend->getCardUri($id));
  674. }
  675. public function testGetCardUriFailed() {
  676. $this->expectException(\InvalidArgumentException::class);
  677. $this->backend->getCardUri(1);
  678. }
  679. public function testGetContact() {
  680. $query = $this->db->getQueryBuilder();
  681. for ($i = 0; $i < 2; $i++) {
  682. $query->insert($this->dbCardsTable)
  683. ->values(
  684. [
  685. 'addressbookid' => $query->createNamedParameter($i),
  686. 'carddata' => $query->createNamedParameter('carddata' . $i, IQueryBuilder::PARAM_LOB),
  687. 'uri' => $query->createNamedParameter('uri' . $i),
  688. 'lastmodified' => $query->createNamedParameter(5489543),
  689. 'etag' => $query->createNamedParameter('etag' . $i),
  690. 'size' => $query->createNamedParameter(120),
  691. ]
  692. );
  693. $query->execute();
  694. }
  695. $result = $this->backend->getContact(0, 'uri0');
  696. $this->assertSame(8, count($result));
  697. $this->assertSame(0, (int)$result['addressbookid']);
  698. $this->assertSame('uri0', $result['uri']);
  699. $this->assertSame(5489543, (int)$result['lastmodified']);
  700. $this->assertSame('"etag0"', $result['etag']);
  701. $this->assertSame(120, (int)$result['size']);
  702. // this shouldn't return any result because 'uri1' is in address book 1
  703. // see https://github.com/nextcloud/server/issues/229
  704. $result = $this->backend->getContact(0, 'uri1');
  705. $this->assertEmpty($result);
  706. }
  707. public function testGetContactFail() {
  708. $this->assertEmpty($this->backend->getContact(0, 'uri'));
  709. }
  710. public function testCollectCardProperties() {
  711. $query = $this->db->getQueryBuilder();
  712. $query->insert($this->dbCardsPropertiesTable)
  713. ->values(
  714. [
  715. 'addressbookid' => $query->createNamedParameter(666),
  716. 'cardid' => $query->createNamedParameter(777),
  717. 'name' => $query->createNamedParameter('FN'),
  718. 'value' => $query->createNamedParameter('John Doe'),
  719. 'preferred' => $query->createNamedParameter(0)
  720. ]
  721. )
  722. ->execute();
  723. $result = $this->backend->collectCardProperties(666, 'FN');
  724. $this->assertEquals(['John Doe'], $result);
  725. }
  726. }