1
0

AddressBookImplTest.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-only
  6. */
  7. namespace OCA\DAV\Tests\unit\CardDAV;
  8. use OCA\DAV\CardDAV\AddressBook;
  9. use OCA\DAV\CardDAV\AddressBookImpl;
  10. use OCA\DAV\CardDAV\CardDavBackend;
  11. use OCP\IURLGenerator;
  12. use Sabre\VObject\Component\VCard;
  13. use Sabre\VObject\Property\Text;
  14. //use Sabre\VObject\Property\;
  15. use Test\TestCase;
  16. class AddressBookImplTest extends TestCase {
  17. /** @var AddressBookImpl */
  18. private $addressBookImpl;
  19. /** @var array */
  20. private $addressBookInfo;
  21. /** @var AddressBook | \PHPUnit\Framework\MockObject\MockObject */
  22. private $addressBook;
  23. /** @var IURLGenerator | \PHPUnit\Framework\MockObject\MockObject */
  24. private $urlGenerator;
  25. /** @var CardDavBackend | \PHPUnit\Framework\MockObject\MockObject */
  26. private $backend;
  27. /** @var VCard | \PHPUnit\Framework\MockObject\MockObject */
  28. private $vCard;
  29. protected function setUp(): void {
  30. parent::setUp();
  31. $this->addressBookInfo = [
  32. 'id' => 42,
  33. 'uri' => 'system',
  34. 'principaluri' => 'principals/system/system',
  35. '{DAV:}displayname' => 'display name',
  36. ];
  37. $this->addressBook = $this->getMockBuilder(AddressBook::class)
  38. ->disableOriginalConstructor()->getMock();
  39. $this->backend = $this->getMockBuilder(CardDavBackend::class)
  40. ->disableOriginalConstructor()->getMock();
  41. $this->vCard = $this->createMock(VCard::class);
  42. $this->urlGenerator = $this->createMock(IURLGenerator::class);
  43. $this->addressBookImpl = new AddressBookImpl(
  44. $this->addressBook,
  45. $this->addressBookInfo,
  46. $this->backend,
  47. $this->urlGenerator
  48. );
  49. }
  50. public function testGetKey(): void {
  51. $this->assertSame($this->addressBookInfo['id'],
  52. $this->addressBookImpl->getKey());
  53. }
  54. public function testGetDisplayName(): void {
  55. $this->assertSame($this->addressBookInfo['{DAV:}displayname'],
  56. $this->addressBookImpl->getDisplayName());
  57. }
  58. public function testSearch(): void {
  59. /** @var \PHPUnit\Framework\MockObject\MockObject | AddressBookImpl $addressBookImpl */
  60. $addressBookImpl = $this->getMockBuilder(AddressBookImpl::class)
  61. ->setConstructorArgs(
  62. [
  63. $this->addressBook,
  64. $this->addressBookInfo,
  65. $this->backend,
  66. $this->urlGenerator,
  67. ]
  68. )
  69. ->setMethods(['vCard2Array', 'readCard'])
  70. ->getMock();
  71. $pattern = 'pattern';
  72. $searchProperties = 'properties';
  73. $this->backend->expects($this->once())->method('search')
  74. ->with($this->addressBookInfo['id'], $pattern, $searchProperties)
  75. ->willReturn(
  76. [
  77. ['uri' => 'foo.vcf', 'carddata' => 'cardData1'],
  78. ['uri' => 'bar.vcf', 'carddata' => 'cardData2']
  79. ]
  80. );
  81. $addressBookImpl->expects($this->exactly(2))->method('readCard')
  82. ->willReturn($this->vCard);
  83. $addressBookImpl->expects($this->exactly(2))->method('vCard2Array')
  84. ->withConsecutive(
  85. ['foo.vcf', $this->vCard],
  86. ['bar.vcf', $this->vCard]
  87. )->willReturn('vCard');
  88. $result = $addressBookImpl->search($pattern, $searchProperties, []);
  89. $this->assertTrue((is_array($result)));
  90. $this->assertSame(2, count($result));
  91. }
  92. /**
  93. * @dataProvider dataTestCreate
  94. *
  95. * @param array $properties
  96. */
  97. public function testCreate($properties): void {
  98. $uid = 'uid';
  99. /** @var \PHPUnit\Framework\MockObject\MockObject | AddressBookImpl $addressBookImpl */
  100. $addressBookImpl = $this->getMockBuilder(AddressBookImpl::class)
  101. ->setConstructorArgs(
  102. [
  103. $this->addressBook,
  104. $this->addressBookInfo,
  105. $this->backend,
  106. $this->urlGenerator,
  107. ]
  108. )
  109. ->setMethods(['vCard2Array', 'createUid', 'createEmptyVCard'])
  110. ->getMock();
  111. $expectedProperties = 0;
  112. foreach ($properties as $data) {
  113. if (is_string($data)) {
  114. $expectedProperties++;
  115. } else {
  116. $expectedProperties += count($data);
  117. }
  118. }
  119. $addressBookImpl->expects($this->once())->method('createUid')
  120. ->willReturn($uid);
  121. $addressBookImpl->expects($this->once())->method('createEmptyVCard')
  122. ->with($uid)->willReturn($this->vCard);
  123. $this->vCard->expects($this->exactly($expectedProperties))
  124. ->method('createProperty');
  125. $this->backend->expects($this->once())->method('createCard');
  126. $this->backend->expects($this->never())->method('updateCard');
  127. $this->backend->expects($this->never())->method('getCard');
  128. $addressBookImpl->expects($this->once())->method('vCard2Array')
  129. ->with('uid.vcf', $this->vCard)->willReturn(true);
  130. $this->assertTrue($addressBookImpl->createOrUpdate($properties));
  131. }
  132. public function dataTestCreate() {
  133. return [
  134. [[]],
  135. [['FN' => 'John Doe']],
  136. [['FN' => 'John Doe', 'EMAIL' => ['john@doe.cloud', 'john.doe@example.org']]],
  137. ];
  138. }
  139. public function testUpdate(): void {
  140. $uid = 'uid';
  141. $uri = 'bla.vcf';
  142. $properties = ['URI' => $uri, 'UID' => $uid, 'FN' => 'John Doe'];
  143. /** @var \PHPUnit\Framework\MockObject\MockObject | AddressBookImpl $addressBookImpl */
  144. $addressBookImpl = $this->getMockBuilder(AddressBookImpl::class)
  145. ->setConstructorArgs(
  146. [
  147. $this->addressBook,
  148. $this->addressBookInfo,
  149. $this->backend,
  150. $this->urlGenerator,
  151. ]
  152. )
  153. ->setMethods(['vCard2Array', 'createUid', 'createEmptyVCard', 'readCard'])
  154. ->getMock();
  155. $addressBookImpl->expects($this->never())->method('createUid');
  156. $addressBookImpl->expects($this->never())->method('createEmptyVCard');
  157. $this->backend->expects($this->once())->method('getCard')
  158. ->with($this->addressBookInfo['id'], $uri)
  159. ->willReturn(['carddata' => 'data']);
  160. $addressBookImpl->expects($this->once())->method('readCard')
  161. ->with('data')->willReturn($this->vCard);
  162. $this->vCard->expects($this->exactly(count($properties) - 1))
  163. ->method('createProperty');
  164. $this->backend->expects($this->never())->method('createCard');
  165. $this->backend->expects($this->once())->method('updateCard');
  166. $addressBookImpl->expects($this->once())->method('vCard2Array')
  167. ->with($uri, $this->vCard)->willReturn(true);
  168. $this->assertTrue($addressBookImpl->createOrUpdate($properties));
  169. }
  170. public function testUpdateWithTypes(): void {
  171. $uid = 'uid';
  172. $uri = 'bla.vcf';
  173. $properties = ['URI' => $uri, 'UID' => $uid, 'FN' => 'John Doe', 'ADR' => [['type' => 'HOME', 'value' => ';;street;city;;;country']]];
  174. $vCard = new vCard;
  175. $textProperty = $vCard->createProperty('KEY', 'value');
  176. /** @var \PHPUnit\Framework\MockObject\MockObject | AddressBookImpl $addressBookImpl */
  177. $addressBookImpl = $this->getMockBuilder(AddressBookImpl::class)
  178. ->setConstructorArgs(
  179. [
  180. $this->addressBook,
  181. $this->addressBookInfo,
  182. $this->backend,
  183. $this->urlGenerator,
  184. ]
  185. )
  186. ->setMethods(['vCard2Array', 'createUid', 'createEmptyVCard', 'readCard'])
  187. ->getMock();
  188. $this->backend->expects($this->once())->method('getCard')
  189. ->with($this->addressBookInfo['id'], $uri)
  190. ->willReturn(['carddata' => 'data']);
  191. $addressBookImpl->expects($this->once())->method('readCard')
  192. ->with('data')->willReturn($this->vCard);
  193. $this->vCard->method('createProperty')->willReturn($textProperty);
  194. $this->vCard->expects($this->exactly(count($properties) - 1))
  195. ->method('createProperty');
  196. $this->vCard->expects($this->once())->method('remove')
  197. ->with('ADR');
  198. $this->vCard->expects($this->once())->method('add');
  199. $addressBookImpl->createOrUpdate($properties);
  200. }
  201. /**
  202. * @dataProvider dataTestGetPermissions
  203. *
  204. * @param array $permissions
  205. * @param int $expected
  206. */
  207. public function testGetPermissions($permissions, $expected): void {
  208. $this->addressBook->expects($this->once())->method('getACL')
  209. ->willReturn($permissions);
  210. $this->assertSame($expected,
  211. $this->addressBookImpl->getPermissions()
  212. );
  213. }
  214. public function dataTestGetPermissions() {
  215. return [
  216. [[], 0],
  217. [[['privilege' => '{DAV:}read']], 1],
  218. [[['privilege' => '{DAV:}write']], 6],
  219. [[['privilege' => '{DAV:}all']], 31],
  220. [[['privilege' => '{DAV:}read'],['privilege' => '{DAV:}write']], 7],
  221. [[['privilege' => '{DAV:}read'],['privilege' => '{DAV:}all']], 31],
  222. [[['privilege' => '{DAV:}all'],['privilege' => '{DAV:}write']], 31],
  223. [[['privilege' => '{DAV:}read'],['privilege' => '{DAV:}write'],['privilege' => '{DAV:}all']], 31],
  224. [[['privilege' => '{DAV:}all'],['privilege' => '{DAV:}read'],['privilege' => '{DAV:}write']], 31],
  225. ];
  226. }
  227. public function testDelete(): void {
  228. $cardId = 1;
  229. $cardUri = 'cardUri';
  230. $this->backend->expects($this->once())->method('getCardUri')
  231. ->with($cardId)->willReturn($cardUri);
  232. $this->backend->expects($this->once())->method('deleteCard')
  233. ->with($this->addressBookInfo['id'], $cardUri)
  234. ->willReturn(true);
  235. $this->assertTrue($this->addressBookImpl->delete($cardId));
  236. }
  237. public function testReadCard(): void {
  238. $vCard = new VCard();
  239. $vCard->add(new Text($vCard, 'UID', 'uid'));
  240. $vCardSerialized = $vCard->serialize();
  241. $result = $this->invokePrivate($this->addressBookImpl, 'readCard', [$vCardSerialized]);
  242. $resultSerialized = $result->serialize();
  243. $this->assertSame($vCardSerialized, $resultSerialized);
  244. }
  245. public function testCreateUid(): void {
  246. /** @var \PHPUnit\Framework\MockObject\MockObject | AddressBookImpl $addressBookImpl */
  247. $addressBookImpl = $this->getMockBuilder(AddressBookImpl::class)
  248. ->setConstructorArgs(
  249. [
  250. $this->addressBook,
  251. $this->addressBookInfo,
  252. $this->backend,
  253. $this->urlGenerator,
  254. ]
  255. )
  256. ->setMethods(['getUid'])
  257. ->getMock();
  258. $addressBookImpl->expects($this->exactly(2))
  259. ->method('getUid')
  260. ->willReturnOnConsecutiveCalls(
  261. 'uid0',
  262. 'uid1',
  263. );
  264. // simulate that 'uid0' already exists, so the second uid will be returned
  265. $this->backend->expects($this->exactly(2))->method('getContact')
  266. ->willReturnCallback(
  267. function ($id, $uid) {
  268. return ($uid === 'uid0.vcf');
  269. }
  270. );
  271. $this->assertSame('uid1',
  272. $this->invokePrivate($addressBookImpl, 'createUid', [])
  273. );
  274. }
  275. public function testCreateEmptyVCard(): void {
  276. $uid = 'uid';
  277. $expectedVCard = new VCard();
  278. $expectedVCard->UID = $uid;
  279. $expectedVCardSerialized = $expectedVCard->serialize();
  280. $result = $this->invokePrivate($this->addressBookImpl, 'createEmptyVCard', [$uid]);
  281. $resultSerialized = $result->serialize();
  282. $this->assertSame($expectedVCardSerialized, $resultSerialized);
  283. }
  284. public function testVCard2Array(): void {
  285. $vCard = new VCard();
  286. $vCard->add($vCard->createProperty('FN', 'Full Name'));
  287. // Multi-value properties
  288. $vCard->add($vCard->createProperty('CLOUD', 'cloud-user1@localhost'));
  289. $vCard->add($vCard->createProperty('CLOUD', 'cloud-user2@example.tld'));
  290. $vCard->add($vCard->createProperty('EMAIL', 'email-user1@localhost'));
  291. $vCard->add($vCard->createProperty('EMAIL', 'email-user2@example.tld'));
  292. $vCard->add($vCard->createProperty('IMPP', 'impp-user1@localhost'));
  293. $vCard->add($vCard->createProperty('IMPP', 'impp-user2@example.tld'));
  294. $vCard->add($vCard->createProperty('TEL', '+49 123456789'));
  295. $vCard->add($vCard->createProperty('TEL', '+1 555 123456789'));
  296. $vCard->add($vCard->createProperty('URL', 'https://localhost'));
  297. $vCard->add($vCard->createProperty('URL', 'https://example.tld'));
  298. // Type depending properties
  299. $property = $vCard->createProperty('X-SOCIALPROFILE', 'tw-example');
  300. $property->add('TYPE', 'twitter');
  301. $vCard->add($property);
  302. $property = $vCard->createProperty('X-SOCIALPROFILE', 'tw-example-2');
  303. $property->add('TYPE', 'twitter');
  304. $vCard->add($property);
  305. $property = $vCard->createProperty('X-SOCIALPROFILE', 'fb-example');
  306. $property->add('TYPE', 'facebook');
  307. $vCard->add($property);
  308. $array = $this->invokePrivate($this->addressBookImpl, 'vCard2Array', ['uri', $vCard]);
  309. unset($array['PRODID']);
  310. unset($array['UID']);
  311. $this->assertEquals([
  312. 'URI' => 'uri',
  313. 'VERSION' => '4.0',
  314. 'FN' => 'Full Name',
  315. 'CLOUD' => [
  316. 'cloud-user1@localhost',
  317. 'cloud-user2@example.tld',
  318. ],
  319. 'EMAIL' => [
  320. 'email-user1@localhost',
  321. 'email-user2@example.tld',
  322. ],
  323. 'IMPP' => [
  324. 'impp-user1@localhost',
  325. 'impp-user2@example.tld',
  326. ],
  327. 'TEL' => [
  328. '+49 123456789',
  329. '+1 555 123456789',
  330. ],
  331. 'URL' => [
  332. 'https://localhost',
  333. 'https://example.tld',
  334. ],
  335. 'X-SOCIALPROFILE' => [
  336. 'tw-example',
  337. 'tw-example-2',
  338. 'fb-example',
  339. ],
  340. 'isLocalSystemBook' => true,
  341. ], $array);
  342. }
  343. public function testVCard2ArrayWithTypes(): void {
  344. $vCard = new VCard();
  345. $vCard->add($vCard->createProperty('FN', 'Full Name'));
  346. // Multi-value properties
  347. $vCard->add($vCard->createProperty('CLOUD', 'cloud-user1@localhost'));
  348. $vCard->add($vCard->createProperty('CLOUD', 'cloud-user2@example.tld'));
  349. $property = $vCard->createProperty('EMAIL', 'email-user1@localhost');
  350. $property->add('TYPE', 'HOME');
  351. $vCard->add($property);
  352. $property = $vCard->createProperty('EMAIL', 'email-user2@example.tld');
  353. $property->add('TYPE', 'WORK');
  354. $vCard->add($property);
  355. $vCard->add($vCard->createProperty('IMPP', 'impp-user1@localhost'));
  356. $vCard->add($vCard->createProperty('IMPP', 'impp-user2@example.tld'));
  357. $property = $vCard->createProperty('TEL', '+49 123456789');
  358. $property->add('TYPE', 'HOME,VOICE');
  359. $vCard->add($property);
  360. $property = $vCard->createProperty('TEL', '+1 555 123456789');
  361. $property->add('TYPE', 'WORK');
  362. $vCard->add($property);
  363. $vCard->add($vCard->createProperty('URL', 'https://localhost'));
  364. $vCard->add($vCard->createProperty('URL', 'https://example.tld'));
  365. // Type depending properties
  366. $property = $vCard->createProperty('X-SOCIALPROFILE', 'tw-example');
  367. $property->add('TYPE', 'twitter');
  368. $vCard->add($property);
  369. $property = $vCard->createProperty('X-SOCIALPROFILE', 'tw-example-2');
  370. $property->add('TYPE', 'twitter');
  371. $vCard->add($property);
  372. $property = $vCard->createProperty('X-SOCIALPROFILE', 'fb-example');
  373. $property->add('TYPE', 'facebook');
  374. $vCard->add($property);
  375. $array = $this->invokePrivate($this->addressBookImpl, 'vCard2Array', ['uri', $vCard, true]);
  376. unset($array['PRODID']);
  377. unset($array['UID']);
  378. $this->assertEquals([
  379. 'URI' => 'uri',
  380. 'VERSION' => '4.0',
  381. 'FN' => 'Full Name',
  382. 'CLOUD' => [
  383. ['type' => '', 'value' => 'cloud-user1@localhost'],
  384. ['type' => '', 'value' => 'cloud-user2@example.tld'],
  385. ],
  386. 'EMAIL' => [
  387. ['type' => 'HOME', 'value' => 'email-user1@localhost'],
  388. ['type' => 'WORK', 'value' => 'email-user2@example.tld'],
  389. ],
  390. 'IMPP' => [
  391. ['type' => '', 'value' => 'impp-user1@localhost'],
  392. ['type' => '', 'value' => 'impp-user2@example.tld'],
  393. ],
  394. 'TEL' => [
  395. ['type' => 'HOME,VOICE', 'value' => '+49 123456789'],
  396. ['type' => 'WORK', 'value' => '+1 555 123456789'],
  397. ],
  398. 'URL' => [
  399. ['type' => '', 'value' => 'https://localhost'],
  400. ['type' => '', 'value' => 'https://example.tld'],
  401. ],
  402. 'X-SOCIALPROFILE' => [
  403. ['type' => 'twitter', 'value' => 'tw-example'],
  404. ['type' => 'twitter', 'value' => 'tw-example-2'],
  405. ['type' => 'facebook', 'value' => 'fb-example'],
  406. ],
  407. 'isLocalSystemBook' => true,
  408. ], $array);
  409. }
  410. public function testIsSystemAddressBook(): void {
  411. $addressBookInfo = [
  412. '{http://owncloud.org/ns}owner-principal' => 'principals/system/system',
  413. 'principaluri' => 'principals/system/system',
  414. '{DAV:}displayname' => 'display name',
  415. 'id' => 666,
  416. 'uri' => 'system',
  417. ];
  418. $addressBookImpl = new AddressBookImpl(
  419. $this->addressBook,
  420. $addressBookInfo,
  421. $this->backend,
  422. $this->urlGenerator
  423. );
  424. $this->assertTrue($addressBookImpl->isSystemAddressBook());
  425. }
  426. public function testIsShared(): void {
  427. $addressBookInfo = [
  428. '{http://owncloud.org/ns}owner-principal' => 'user1',
  429. '{DAV:}displayname' => 'Test address book',
  430. 'principaluri' => 'user2',
  431. 'id' => 666,
  432. 'uri' => 'default',
  433. ];
  434. $addressBookImpl = new AddressBookImpl(
  435. $this->addressBook,
  436. $addressBookInfo,
  437. $this->backend,
  438. $this->urlGenerator
  439. );
  440. $this->assertFalse($addressBookImpl->isSystemAddressBook());
  441. $this->assertTrue($addressBookImpl->isShared());
  442. }
  443. public function testIsNotShared(): void {
  444. $addressBookInfo = [
  445. '{http://owncloud.org/ns}owner-principal' => 'user1',
  446. '{DAV:}displayname' => 'Test address book',
  447. 'principaluri' => 'user1',
  448. 'id' => 666,
  449. 'uri' => 'default',
  450. ];
  451. $addressBookImpl = new AddressBookImpl(
  452. $this->addressBook,
  453. $addressBookInfo,
  454. $this->backend,
  455. $this->urlGenerator
  456. );
  457. $this->assertFalse($addressBookImpl->isSystemAddressBook());
  458. $this->assertFalse($addressBookImpl->isShared());
  459. }
  460. }