BackendTest.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-License-Identifier: AGPL-3.0-or-later
  5. */
  6. namespace OCA\DAV\Tests\unit\CardDAV\Activity;
  7. use OCA\DAV\CardDAV\Activity\Backend;
  8. use OCA\DAV\CardDAV\Activity\Provider\Addressbook;
  9. use OCA\DAV\CardDAV\Activity\Provider\Card;
  10. use OCP\Activity\IEvent;
  11. use OCP\Activity\IManager;
  12. use OCP\App\IAppManager;
  13. use OCP\IGroup;
  14. use OCP\IGroupManager;
  15. use OCP\IUser;
  16. use OCP\IUserManager;
  17. use OCP\IUserSession;
  18. use PHPUnit\Framework\MockObject\MockObject;
  19. use Test\TestCase;
  20. class BackendTest extends TestCase {
  21. /** @var IManager|MockObject */
  22. protected $activityManager;
  23. /** @var IGroupManager|MockObject */
  24. protected $groupManager;
  25. /** @var IUserSession|MockObject */
  26. protected $userSession;
  27. /** @var IAppManager|MockObject */
  28. protected $appManager;
  29. /** @var IUserManager|MockObject */
  30. protected $userManager;
  31. protected function setUp(): void {
  32. parent::setUp();
  33. $this->activityManager = $this->createMock(IManager::class);
  34. $this->groupManager = $this->createMock(IGroupManager::class);
  35. $this->userSession = $this->createMock(IUserSession::class);
  36. $this->appManager = $this->createMock(IAppManager::class);
  37. $this->userManager = $this->createMock(IUserManager::class);
  38. }
  39. /**
  40. * @param array $methods
  41. * @return Backend|MockObject
  42. */
  43. protected function getBackend(array $methods = []) {
  44. if (empty($methods)) {
  45. return new Backend(
  46. $this->activityManager,
  47. $this->groupManager,
  48. $this->userSession,
  49. $this->appManager,
  50. $this->userManager
  51. );
  52. } else {
  53. return $this->getMockBuilder(Backend::class)
  54. ->setConstructorArgs([
  55. $this->activityManager,
  56. $this->groupManager,
  57. $this->userSession,
  58. $this->appManager,
  59. $this->userManager
  60. ])
  61. ->onlyMethods($methods)
  62. ->getMock();
  63. }
  64. }
  65. public function dataCallTriggerAddressBookActivity(): array {
  66. return [
  67. ['onAddressbookCreate', [['data']], Addressbook::SUBJECT_ADD, [['data'], [], []]],
  68. ['onAddressbookUpdate', [['data'], ['shares'], ['changed-properties']], Addressbook::SUBJECT_UPDATE, [['data'], ['shares'], ['changed-properties']]],
  69. ['onAddressbookDelete', [['data'], ['shares']], Addressbook::SUBJECT_DELETE, [['data'], ['shares'], []]],
  70. ];
  71. }
  72. /**
  73. * @dataProvider dataCallTriggerAddressBookActivity
  74. */
  75. public function testCallTriggerAddressBookActivity(string $method, array $payload, string $expectedSubject, array $expectedPayload): void {
  76. $backend = $this->getBackend(['triggerAddressbookActivity']);
  77. $backend->expects($this->once())
  78. ->method('triggerAddressbookActivity')
  79. ->willReturnCallback(function () use ($expectedPayload, $expectedSubject): void {
  80. $arguments = func_get_args();
  81. $this->assertSame($expectedSubject, array_shift($arguments));
  82. $this->assertEquals($expectedPayload, $arguments);
  83. });
  84. call_user_func_array([$backend, $method], $payload);
  85. }
  86. public function dataTriggerAddressBookActivity(): array {
  87. return [
  88. // Add addressbook
  89. [Addressbook::SUBJECT_ADD, [], [], [], '', '', null, []],
  90. [Addressbook::SUBJECT_ADD, [
  91. 'principaluri' => 'principal/user/admin',
  92. 'id' => 42,
  93. 'uri' => 'this-uri',
  94. '{DAV:}displayname' => 'Name of addressbook',
  95. ], [], [], '', 'admin', null, ['admin']],
  96. [Addressbook::SUBJECT_ADD, [
  97. 'principaluri' => 'principal/user/admin',
  98. 'id' => 42,
  99. 'uri' => 'this-uri',
  100. '{DAV:}displayname' => 'Name of addressbook',
  101. ], [], [], 'test2', 'test2', null, ['admin']],
  102. // Update addressbook
  103. [Addressbook::SUBJECT_UPDATE, [], [], [], '', '', null, []],
  104. // No visible change - owner only
  105. [Addressbook::SUBJECT_UPDATE, [
  106. 'principaluri' => 'principal/user/admin',
  107. 'id' => 42,
  108. 'uri' => 'this-uri',
  109. '{DAV:}displayname' => 'Name of addressbook',
  110. ], ['shares'], [], '', 'admin', null, ['admin']],
  111. // Visible change
  112. [Addressbook::SUBJECT_UPDATE, [
  113. 'principaluri' => 'principal/user/admin',
  114. 'id' => 42,
  115. 'uri' => 'this-uri',
  116. '{DAV:}displayname' => 'Name of addressbook',
  117. ], ['shares'], ['{DAV:}displayname' => 'Name'], '', 'admin', ['user1'], ['user1', 'admin']],
  118. [Addressbook::SUBJECT_UPDATE, [
  119. 'principaluri' => 'principal/user/admin',
  120. 'id' => 42,
  121. 'uri' => 'this-uri',
  122. '{DAV:}displayname' => 'Name of addressbook',
  123. ], ['shares'], ['{DAV:}displayname' => 'Name'], 'test2', 'test2', ['user1'], ['user1', 'admin']],
  124. // Delete addressbook
  125. [Addressbook::SUBJECT_DELETE, [], [], [], '', '', null, []],
  126. [Addressbook::SUBJECT_DELETE, [
  127. 'principaluri' => 'principal/user/admin',
  128. 'id' => 42,
  129. 'uri' => 'this-uri',
  130. '{DAV:}displayname' => 'Name of addressbook',
  131. ], ['shares'], [], '', 'admin', [], ['admin']],
  132. [Addressbook::SUBJECT_DELETE, [
  133. 'principaluri' => 'principal/user/admin',
  134. 'id' => 42,
  135. 'uri' => 'this-uri',
  136. '{DAV:}displayname' => 'Name of addressbook',
  137. ], ['shares'], [], '', 'admin', ['user1'], ['user1', 'admin']],
  138. [Addressbook::SUBJECT_DELETE, [
  139. 'principaluri' => 'principal/user/admin',
  140. 'id' => 42,
  141. 'uri' => 'this-uri',
  142. '{DAV:}displayname' => 'Name of addressbook',
  143. ], ['shares'], [], 'test2', 'test2', ['user1'], ['user1', 'admin']],
  144. ];
  145. }
  146. /**
  147. * @dataProvider dataTriggerAddressBookActivity
  148. * @param string $action
  149. * @param array $data
  150. * @param array $shares
  151. * @param array $changedProperties
  152. * @param string $currentUser
  153. * @param string $author
  154. * @param string[]|null $shareUsers
  155. * @param string[] $users
  156. */
  157. public function testTriggerAddressBookActivity(string $action, array $data, array $shares, array $changedProperties, string $currentUser, string $author, ?array $shareUsers, array $users): void {
  158. $backend = $this->getBackend(['getUsersForShares']);
  159. if ($shareUsers === null) {
  160. $backend->expects($this->never())
  161. ->method('getUsersForShares');
  162. } else {
  163. $backend->expects($this->once())
  164. ->method('getUsersForShares')
  165. ->with($shares)
  166. ->willReturn($shareUsers);
  167. }
  168. if ($author !== '') {
  169. if ($currentUser !== '') {
  170. $this->userSession->expects($this->once())
  171. ->method('getUser')
  172. ->willReturn($this->getUserMock($currentUser));
  173. } else {
  174. $this->userSession->expects($this->once())
  175. ->method('getUser')
  176. ->willReturn(null);
  177. }
  178. $event = $this->createMock(IEvent::class);
  179. $this->activityManager->expects($this->once())
  180. ->method('generateEvent')
  181. ->willReturn($event);
  182. $event->expects($this->once())
  183. ->method('setApp')
  184. ->with('dav')
  185. ->willReturnSelf();
  186. $event->expects($this->once())
  187. ->method('setObject')
  188. ->with('addressbook', $data['id'])
  189. ->willReturnSelf();
  190. $event->expects($this->once())
  191. ->method('setType')
  192. ->with('contacts')
  193. ->willReturnSelf();
  194. $event->expects($this->once())
  195. ->method('setAuthor')
  196. ->with($author)
  197. ->willReturnSelf();
  198. $this->userManager->expects($action === Addressbook::SUBJECT_DELETE ? $this->exactly(sizeof($users)) : $this->never())
  199. ->method('userExists')
  200. ->willReturn(true);
  201. $event->expects($this->exactly(sizeof($users)))
  202. ->method('setAffectedUser')
  203. ->willReturnSelf();
  204. $event->expects($this->exactly(sizeof($users)))
  205. ->method('setSubject')
  206. ->willReturnSelf();
  207. $this->activityManager->expects($this->exactly(sizeof($users)))
  208. ->method('publish')
  209. ->with($event);
  210. } else {
  211. $this->activityManager->expects($this->never())
  212. ->method('generateEvent');
  213. }
  214. $this->invokePrivate($backend, 'triggerAddressbookActivity', [$action, $data, $shares, $changedProperties]);
  215. }
  216. public function testNoAddressbookActivityCreatedForSystemAddressbook(): void {
  217. $backend = $this->getBackend();
  218. $this->activityManager->expects($this->never())
  219. ->method('generateEvent');
  220. $this->assertEmpty($this->invokePrivate($backend, 'triggerAddressbookActivity', [Addressbook::SUBJECT_ADD, ['principaluri' => 'principals/system/system'], [], [], '', '', null, []]));
  221. }
  222. public function testUserDeletionDoesNotCreateActivity(): void {
  223. $backend = $this->getBackend();
  224. $this->userManager->expects($this->once())
  225. ->method('userExists')
  226. ->willReturn(false);
  227. $this->activityManager->expects($this->never())
  228. ->method('publish');
  229. $this->invokePrivate($backend, 'triggerAddressbookActivity', [Addressbook::SUBJECT_DELETE, [
  230. 'principaluri' => 'principal/user/admin',
  231. 'id' => 42,
  232. 'uri' => 'this-uri',
  233. '{DAV:}displayname' => 'Name of addressbook',
  234. ], [], []]);
  235. }
  236. public function dataTriggerCardActivity(): array {
  237. $cardData = "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 3.4.8//EN\r\nUID:test-user\r\nFN:test-user\r\nN:test-user;;;;\r\nEND:VCARD\r\n\r\n";
  238. return [
  239. // Add card
  240. [Card::SUBJECT_ADD, [], [], [], '', '', null, []],
  241. [Card::SUBJECT_ADD, [
  242. 'principaluri' => 'principal/user/admin',
  243. 'id' => 42,
  244. 'uri' => 'this-uri',
  245. '{DAV:}displayname' => 'Name of addressbook',
  246. ], [], [
  247. 'carddata' => $cardData
  248. ], '', 'admin', [], ['admin']],
  249. [Card::SUBJECT_ADD, [
  250. 'principaluri' => 'principal/user/admin',
  251. 'id' => 42,
  252. 'uri' => 'this-uri',
  253. '{DAV:}displayname' => 'Name of addressbook',
  254. ], [], ['carddata' => $cardData], 'test2', 'test2', [], ['admin']],
  255. // Update card
  256. [Card::SUBJECT_UPDATE, [], [], [], '', '', null, []],
  257. // No visible change - owner only
  258. [Card::SUBJECT_UPDATE, [
  259. 'principaluri' => 'principal/user/admin',
  260. 'id' => 42,
  261. 'uri' => 'this-uri',
  262. '{DAV:}displayname' => 'Name of addressbook',
  263. ], ['shares'], ['carddata' => $cardData], '', 'admin', [], ['admin']],
  264. // Visible change
  265. [Card::SUBJECT_UPDATE, [
  266. 'principaluri' => 'principal/user/admin',
  267. 'id' => 42,
  268. 'uri' => 'this-uri',
  269. '{DAV:}displayname' => 'Name of addressbook',
  270. ], ['shares'], ['carddata' => $cardData], '', 'admin', ['user1'], ['user1', 'admin']],
  271. [Card::SUBJECT_UPDATE, [
  272. 'principaluri' => 'principal/user/admin',
  273. 'id' => 42,
  274. 'uri' => 'this-uri',
  275. '{DAV:}displayname' => 'Name of addressbook',
  276. ], ['shares'], ['carddata' => $cardData], 'test2', 'test2', ['user1'], ['user1', 'admin']],
  277. // Delete card
  278. [Card::SUBJECT_DELETE, [], [], ['carddata' => $cardData], '', '', null, []],
  279. [Card::SUBJECT_DELETE, [
  280. 'principaluri' => 'principal/user/admin',
  281. 'id' => 42,
  282. 'uri' => 'this-uri',
  283. '{DAV:}displayname' => 'Name of addressbook',
  284. ], ['shares'], ['carddata' => $cardData], '', 'admin', [], ['admin']],
  285. [Card::SUBJECT_DELETE, [
  286. 'principaluri' => 'principal/user/admin',
  287. 'id' => 42,
  288. 'uri' => 'this-uri',
  289. '{DAV:}displayname' => 'Name of addressbook',
  290. ], ['shares'], ['carddata' => $cardData], '', 'admin', ['user1'], ['user1', 'admin']],
  291. [Card::SUBJECT_DELETE, [
  292. 'principaluri' => 'principal/user/admin',
  293. 'id' => 42,
  294. 'uri' => 'this-uri',
  295. '{DAV:}displayname' => 'Name of addressbook',
  296. ], ['shares'], ['carddata' => $cardData], 'test2', 'test2', ['user1'], ['user1', 'admin']],
  297. ];
  298. }
  299. /**
  300. * @dataProvider dataTriggerCardActivity
  301. * @param string $action
  302. * @param array $addressBookData
  303. * @param array $shares
  304. * @param array $cardData
  305. * @param string $currentUser
  306. * @param string $author
  307. * @param string[]|null $shareUsers
  308. * @param string[] $users
  309. */
  310. public function testTriggerCardActivity(string $action, array $addressBookData, array $shares, array $cardData, string $currentUser, string $author, ?array $shareUsers, array $users): void {
  311. $backend = $this->getBackend(['getUsersForShares']);
  312. if ($shareUsers === null) {
  313. $backend->expects($this->never())
  314. ->method('getUsersForShares');
  315. } else {
  316. $backend->expects($this->once())
  317. ->method('getUsersForShares')
  318. ->with($shares)
  319. ->willReturn($shareUsers);
  320. }
  321. if ($author !== '') {
  322. if ($currentUser !== '') {
  323. $this->userSession->expects($this->once())
  324. ->method('getUser')
  325. ->willReturn($this->getUserMock($currentUser));
  326. } else {
  327. $this->userSession->expects($this->once())
  328. ->method('getUser')
  329. ->willReturn(null);
  330. }
  331. $event = $this->createMock(IEvent::class);
  332. $this->activityManager->expects($this->once())
  333. ->method('generateEvent')
  334. ->willReturn($event);
  335. $event->expects($this->once())
  336. ->method('setApp')
  337. ->with('dav')
  338. ->willReturnSelf();
  339. $event->expects($this->once())
  340. ->method('setObject')
  341. ->with('addressbook', $addressBookData['id'])
  342. ->willReturnSelf();
  343. $event->expects($this->once())
  344. ->method('setType')
  345. ->with('contacts')
  346. ->willReturnSelf();
  347. $event->expects($this->once())
  348. ->method('setAuthor')
  349. ->with($author)
  350. ->willReturnSelf();
  351. $event->expects($this->exactly(sizeof($users)))
  352. ->method('setAffectedUser')
  353. ->willReturnSelf();
  354. $event->expects($this->exactly(sizeof($users)))
  355. ->method('setSubject')
  356. ->willReturnSelf();
  357. $this->activityManager->expects($this->exactly(sizeof($users)))
  358. ->method('publish')
  359. ->with($event);
  360. } else {
  361. $this->activityManager->expects($this->never())
  362. ->method('generateEvent');
  363. }
  364. $this->invokePrivate($backend, 'triggerCardActivity', [$action, $addressBookData, $shares, $cardData]);
  365. }
  366. public function testNoCardActivityCreatedForSystemAddressbook(): void {
  367. $backend = $this->getBackend();
  368. $this->activityManager->expects($this->never())
  369. ->method('generateEvent');
  370. $this->assertEmpty($this->invokePrivate($backend, 'triggerCardActivity', [Card::SUBJECT_UPDATE, ['principaluri' => 'principals/system/system'], [], []]));
  371. }
  372. public function dataGetUsersForShares(): array {
  373. return [
  374. [
  375. [],
  376. [],
  377. [],
  378. ],
  379. [
  380. [
  381. ['{http://owncloud.org/ns}principal' => 'principal/users/user1'],
  382. ['{http://owncloud.org/ns}principal' => 'principal/users/user2'],
  383. ['{http://owncloud.org/ns}principal' => 'principal/users/user2'],
  384. ['{http://owncloud.org/ns}principal' => 'principal/users/user2'],
  385. ['{http://owncloud.org/ns}principal' => 'principal/users/user3'],
  386. ],
  387. [],
  388. ['user1', 'user2', 'user3'],
  389. ],
  390. [
  391. [
  392. ['{http://owncloud.org/ns}principal' => 'principal/users/user1'],
  393. ['{http://owncloud.org/ns}principal' => 'principal/users/user2'],
  394. ['{http://owncloud.org/ns}principal' => 'principal/users/user2'],
  395. ['{http://owncloud.org/ns}principal' => 'principal/groups/group2'],
  396. ['{http://owncloud.org/ns}principal' => 'principal/groups/group3'],
  397. ],
  398. ['group2' => null, 'group3' => null],
  399. ['user1', 'user2'],
  400. ],
  401. [
  402. [
  403. ['{http://owncloud.org/ns}principal' => 'principal/users/user1'],
  404. ['{http://owncloud.org/ns}principal' => 'principal/users/user2'],
  405. ['{http://owncloud.org/ns}principal' => 'principal/users/user2'],
  406. ['{http://owncloud.org/ns}principal' => 'principal/groups/group2'],
  407. ['{http://owncloud.org/ns}principal' => 'principal/groups/group3'],
  408. ],
  409. ['group2' => ['user1', 'user2', 'user3'], 'group3' => ['user2', 'user3', 'user4']],
  410. ['user1', 'user2', 'user3', 'user4'],
  411. ],
  412. ];
  413. }
  414. /**
  415. * @dataProvider dataGetUsersForShares
  416. * @param array $shares
  417. * @param array $groups
  418. * @param array $expected
  419. */
  420. public function testGetUsersForShares(array $shares, array $groups, array $expected): void {
  421. $backend = $this->getBackend();
  422. $getGroups = [];
  423. foreach ($groups as $gid => $members) {
  424. if ($members === null) {
  425. $getGroups[] = [$gid, null];
  426. continue;
  427. }
  428. $group = $this->createMock(IGroup::class);
  429. $group->expects($this->once())
  430. ->method('getUsers')
  431. ->willReturn($this->getUsers($members));
  432. $getGroups[] = [$gid, $group];
  433. }
  434. $this->groupManager->expects($this->exactly(sizeof($getGroups)))
  435. ->method('get')
  436. ->willReturnMap($getGroups);
  437. $users = $this->invokePrivate($backend, 'getUsersForShares', [$shares]);
  438. sort($users);
  439. $this->assertEquals($expected, $users);
  440. }
  441. /**
  442. * @param string[] $users
  443. * @return IUser[]|MockObject[]
  444. */
  445. protected function getUsers(array $users): array {
  446. $list = [];
  447. foreach ($users as $user) {
  448. $list[] = $this->getUserMock($user);
  449. }
  450. return $list;
  451. }
  452. /**
  453. * @param string $uid
  454. * @return IUser|MockObject
  455. */
  456. protected function getUserMock(string $uid) {
  457. $user = $this->createMock(IUser::class);
  458. $user->expects($this->once())
  459. ->method('getUID')
  460. ->willReturn($uid);
  461. return $user;
  462. }
  463. }