aboutsummaryrefslogtreecommitdiffstats
path: root/apps/dav/tests/unit/CardDAV
diff options
context:
space:
mode:
Diffstat (limited to 'apps/dav/tests/unit/CardDAV')
-rw-r--r--apps/dav/tests/unit/CardDAV/Activity/BackendTest.php483
-rw-r--r--apps/dav/tests/unit/CardDAV/AddressBookImplTest.php208
-rw-r--r--apps/dav/tests/unit/CardDAV/AddressBookTest.php150
-rw-r--r--apps/dav/tests/unit/CardDAV/BirthdayServiceTest.php254
-rw-r--r--apps/dav/tests/unit/CardDAV/CardDavBackendTest.php491
-rw-r--r--apps/dav/tests/unit/CardDAV/ContactsManagerTest.php45
-rw-r--r--apps/dav/tests/unit/CardDAV/ConverterTest.php195
-rw-r--r--apps/dav/tests/unit/CardDAV/ImageExportPluginTest.php114
-rw-r--r--apps/dav/tests/unit/CardDAV/Security/CardDavRateLimitingPluginTest.php146
-rw-r--r--apps/dav/tests/unit/CardDAV/Sharing/PluginTest.php58
-rw-r--r--apps/dav/tests/unit/CardDAV/SyncServiceTest.php571
-rw-r--r--apps/dav/tests/unit/CardDAV/SystemAddressBookTest.php428
-rw-r--r--apps/dav/tests/unit/CardDAV/Validation/CardDavValidatePluginTest.php73
13 files changed, 2305 insertions, 911 deletions
diff --git a/apps/dav/tests/unit/CardDAV/Activity/BackendTest.php b/apps/dav/tests/unit/CardDAV/Activity/BackendTest.php
new file mode 100644
index 00000000000..a070a3d7131
--- /dev/null
+++ b/apps/dav/tests/unit/CardDAV/Activity/BackendTest.php
@@ -0,0 +1,483 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Tests\unit\CardDAV\Activity;
+
+use OCA\DAV\CardDAV\Activity\Backend;
+use OCA\DAV\CardDAV\Activity\Provider\Addressbook;
+use OCA\DAV\CardDAV\Activity\Provider\Card;
+use OCP\Activity\IEvent;
+use OCP\Activity\IManager;
+use OCP\App\IAppManager;
+use OCP\IGroup;
+use OCP\IGroupManager;
+use OCP\IUser;
+use OCP\IUserManager;
+use OCP\IUserSession;
+use PHPUnit\Framework\MockObject\MockObject;
+use Test\TestCase;
+
+class BackendTest extends TestCase {
+ protected IManager&MockObject $activityManager;
+ protected IGroupManager&MockObject $groupManager;
+ protected IUserSession&MockObject $userSession;
+ protected IAppManager&MockObject $appManager;
+ protected IUserManager&MockObject $userManager;
+
+ protected function setUp(): void {
+ parent::setUp();
+ $this->activityManager = $this->createMock(IManager::class);
+ $this->groupManager = $this->createMock(IGroupManager::class);
+ $this->userSession = $this->createMock(IUserSession::class);
+ $this->appManager = $this->createMock(IAppManager::class);
+ $this->userManager = $this->createMock(IUserManager::class);
+ }
+
+ /**
+ * @return Backend|MockObject
+ */
+ protected function getBackend(array $methods = []): Backend {
+ if (empty($methods)) {
+ return new Backend(
+ $this->activityManager,
+ $this->groupManager,
+ $this->userSession,
+ $this->appManager,
+ $this->userManager
+ );
+ } else {
+ return $this->getMockBuilder(Backend::class)
+ ->setConstructorArgs([
+ $this->activityManager,
+ $this->groupManager,
+ $this->userSession,
+ $this->appManager,
+ $this->userManager
+ ])
+ ->onlyMethods($methods)
+ ->getMock();
+ }
+ }
+
+ public static function dataCallTriggerAddressBookActivity(): array {
+ return [
+ ['onAddressbookCreate', [['data']], Addressbook::SUBJECT_ADD, [['data'], [], []]],
+ ['onAddressbookUpdate', [['data'], ['shares'], ['changed-properties']], Addressbook::SUBJECT_UPDATE, [['data'], ['shares'], ['changed-properties']]],
+ ['onAddressbookDelete', [['data'], ['shares']], Addressbook::SUBJECT_DELETE, [['data'], ['shares'], []]],
+ ];
+ }
+
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataCallTriggerAddressBookActivity')]
+ public function testCallTriggerAddressBookActivity(string $method, array $payload, string $expectedSubject, array $expectedPayload): void {
+ $backend = $this->getBackend(['triggerAddressbookActivity']);
+ $backend->expects($this->once())
+ ->method('triggerAddressbookActivity')
+ ->willReturnCallback(function () use ($expectedPayload, $expectedSubject): void {
+ $arguments = func_get_args();
+ $this->assertSame($expectedSubject, array_shift($arguments));
+ $this->assertEquals($expectedPayload, $arguments);
+ });
+
+ call_user_func_array([$backend, $method], $payload);
+ }
+
+ public static function dataTriggerAddressBookActivity(): array {
+ return [
+ // Add addressbook
+ [Addressbook::SUBJECT_ADD, [], [], [], '', '', null, []],
+ [Addressbook::SUBJECT_ADD, [
+ 'principaluri' => 'principal/user/admin',
+ 'id' => 42,
+ 'uri' => 'this-uri',
+ '{DAV:}displayname' => 'Name of addressbook',
+ ], [], [], '', 'admin', null, ['admin']],
+ [Addressbook::SUBJECT_ADD, [
+ 'principaluri' => 'principal/user/admin',
+ 'id' => 42,
+ 'uri' => 'this-uri',
+ '{DAV:}displayname' => 'Name of addressbook',
+ ], [], [], 'test2', 'test2', null, ['admin']],
+
+ // Update addressbook
+ [Addressbook::SUBJECT_UPDATE, [], [], [], '', '', null, []],
+ // No visible change - owner only
+ [Addressbook::SUBJECT_UPDATE, [
+ 'principaluri' => 'principal/user/admin',
+ 'id' => 42,
+ 'uri' => 'this-uri',
+ '{DAV:}displayname' => 'Name of addressbook',
+ ], ['shares'], [], '', 'admin', null, ['admin']],
+ // Visible change
+ [Addressbook::SUBJECT_UPDATE, [
+ 'principaluri' => 'principal/user/admin',
+ 'id' => 42,
+ 'uri' => 'this-uri',
+ '{DAV:}displayname' => 'Name of addressbook',
+ ], ['shares'], ['{DAV:}displayname' => 'Name'], '', 'admin', ['user1'], ['user1', 'admin']],
+ [Addressbook::SUBJECT_UPDATE, [
+ 'principaluri' => 'principal/user/admin',
+ 'id' => 42,
+ 'uri' => 'this-uri',
+ '{DAV:}displayname' => 'Name of addressbook',
+ ], ['shares'], ['{DAV:}displayname' => 'Name'], 'test2', 'test2', ['user1'], ['user1', 'admin']],
+
+ // Delete addressbook
+ [Addressbook::SUBJECT_DELETE, [], [], [], '', '', null, []],
+ [Addressbook::SUBJECT_DELETE, [
+ 'principaluri' => 'principal/user/admin',
+ 'id' => 42,
+ 'uri' => 'this-uri',
+ '{DAV:}displayname' => 'Name of addressbook',
+ ], ['shares'], [], '', 'admin', [], ['admin']],
+ [Addressbook::SUBJECT_DELETE, [
+ 'principaluri' => 'principal/user/admin',
+ 'id' => 42,
+ 'uri' => 'this-uri',
+ '{DAV:}displayname' => 'Name of addressbook',
+ ], ['shares'], [], '', 'admin', ['user1'], ['user1', 'admin']],
+ [Addressbook::SUBJECT_DELETE, [
+ 'principaluri' => 'principal/user/admin',
+ 'id' => 42,
+ 'uri' => 'this-uri',
+ '{DAV:}displayname' => 'Name of addressbook',
+ ], ['shares'], [], 'test2', 'test2', ['user1'], ['user1', 'admin']],
+ ];
+ }
+
+ /**
+ * @param string[]|null $shareUsers
+ * @param string[] $users
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTriggerAddressBookActivity')]
+ public function testTriggerAddressBookActivity(string $action, array $data, array $shares, array $changedProperties, string $currentUser, string $author, ?array $shareUsers, array $users): void {
+ $backend = $this->getBackend(['getUsersForShares']);
+
+ if ($shareUsers === null) {
+ $backend->expects($this->never())
+ ->method('getUsersForShares');
+ } else {
+ $backend->expects($this->once())
+ ->method('getUsersForShares')
+ ->with($shares)
+ ->willReturn($shareUsers);
+ }
+
+ if ($author !== '') {
+ if ($currentUser !== '') {
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->willReturn($this->getUserMock($currentUser));
+ } else {
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->willReturn(null);
+ }
+
+ $event = $this->createMock(IEvent::class);
+ $this->activityManager->expects($this->once())
+ ->method('generateEvent')
+ ->willReturn($event);
+
+ $event->expects($this->once())
+ ->method('setApp')
+ ->with('dav')
+ ->willReturnSelf();
+ $event->expects($this->once())
+ ->method('setObject')
+ ->with('addressbook', $data['id'])
+ ->willReturnSelf();
+ $event->expects($this->once())
+ ->method('setType')
+ ->with('contacts')
+ ->willReturnSelf();
+ $event->expects($this->once())
+ ->method('setAuthor')
+ ->with($author)
+ ->willReturnSelf();
+
+ $this->userManager->expects($action === Addressbook::SUBJECT_DELETE ? $this->exactly(sizeof($users)) : $this->never())
+ ->method('userExists')
+ ->willReturn(true);
+
+ $event->expects($this->exactly(count($users)))
+ ->method('setAffectedUser')
+ ->willReturnSelf();
+ $event->expects($this->exactly(count($users)))
+ ->method('setSubject')
+ ->willReturnSelf();
+ $this->activityManager->expects($this->exactly(count($users)))
+ ->method('publish')
+ ->with($event);
+ } else {
+ $this->activityManager->expects($this->never())
+ ->method('generateEvent');
+ }
+
+ $this->invokePrivate($backend, 'triggerAddressbookActivity', [$action, $data, $shares, $changedProperties]);
+ }
+
+ public function testNoAddressbookActivityCreatedForSystemAddressbook(): void {
+ $backend = $this->getBackend();
+ $this->activityManager->expects($this->never())
+ ->method('generateEvent');
+ $this->assertEmpty($this->invokePrivate($backend, 'triggerAddressbookActivity', [Addressbook::SUBJECT_ADD, ['principaluri' => 'principals/system/system'], [], [], '', '', null, []]));
+ }
+
+ public function testUserDeletionDoesNotCreateActivity(): void {
+ $backend = $this->getBackend();
+
+ $this->userManager->expects($this->once())
+ ->method('userExists')
+ ->willReturn(false);
+
+ $this->activityManager->expects($this->never())
+ ->method('publish');
+
+ $this->invokePrivate($backend, 'triggerAddressbookActivity', [Addressbook::SUBJECT_DELETE, [
+ 'principaluri' => 'principal/user/admin',
+ 'id' => 42,
+ 'uri' => 'this-uri',
+ '{DAV:}displayname' => 'Name of addressbook',
+ ], [], []]);
+ }
+
+ public static function dataTriggerCardActivity(): array {
+ $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";
+
+ return [
+ // Add card
+ [Card::SUBJECT_ADD, [], [], [], '', '', null, []],
+ [Card::SUBJECT_ADD, [
+ 'principaluri' => 'principal/user/admin',
+ 'id' => 42,
+ 'uri' => 'this-uri',
+ '{DAV:}displayname' => 'Name of addressbook',
+ ], [], [
+ 'carddata' => $cardData
+ ], '', 'admin', [], ['admin']],
+ [Card::SUBJECT_ADD, [
+ 'principaluri' => 'principal/user/admin',
+ 'id' => 42,
+ 'uri' => 'this-uri',
+ '{DAV:}displayname' => 'Name of addressbook',
+ ], [], ['carddata' => $cardData], 'test2', 'test2', [], ['admin']],
+
+ // Update card
+ [Card::SUBJECT_UPDATE, [], [], [], '', '', null, []],
+ // No visible change - owner only
+ [Card::SUBJECT_UPDATE, [
+ 'principaluri' => 'principal/user/admin',
+ 'id' => 42,
+ 'uri' => 'this-uri',
+ '{DAV:}displayname' => 'Name of addressbook',
+ ], ['shares'], ['carddata' => $cardData], '', 'admin', [], ['admin']],
+ // Visible change
+ [Card::SUBJECT_UPDATE, [
+ 'principaluri' => 'principal/user/admin',
+ 'id' => 42,
+ 'uri' => 'this-uri',
+ '{DAV:}displayname' => 'Name of addressbook',
+ ], ['shares'], ['carddata' => $cardData], '', 'admin', ['user1'], ['user1', 'admin']],
+ [Card::SUBJECT_UPDATE, [
+ 'principaluri' => 'principal/user/admin',
+ 'id' => 42,
+ 'uri' => 'this-uri',
+ '{DAV:}displayname' => 'Name of addressbook',
+ ], ['shares'], ['carddata' => $cardData], 'test2', 'test2', ['user1'], ['user1', 'admin']],
+
+ // Delete card
+ [Card::SUBJECT_DELETE, [], [], ['carddata' => $cardData], '', '', null, []],
+ [Card::SUBJECT_DELETE, [
+ 'principaluri' => 'principal/user/admin',
+ 'id' => 42,
+ 'uri' => 'this-uri',
+ '{DAV:}displayname' => 'Name of addressbook',
+ ], ['shares'], ['carddata' => $cardData], '', 'admin', [], ['admin']],
+ [Card::SUBJECT_DELETE, [
+ 'principaluri' => 'principal/user/admin',
+ 'id' => 42,
+ 'uri' => 'this-uri',
+ '{DAV:}displayname' => 'Name of addressbook',
+ ], ['shares'], ['carddata' => $cardData], '', 'admin', ['user1'], ['user1', 'admin']],
+ [Card::SUBJECT_DELETE, [
+ 'principaluri' => 'principal/user/admin',
+ 'id' => 42,
+ 'uri' => 'this-uri',
+ '{DAV:}displayname' => 'Name of addressbook',
+ ], ['shares'], ['carddata' => $cardData], 'test2', 'test2', ['user1'], ['user1', 'admin']],
+ ];
+ }
+
+ /**
+ * @param string[]|null $shareUsers
+ * @param string[] $users
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTriggerCardActivity')]
+ public function testTriggerCardActivity(string $action, array $addressBookData, array $shares, array $cardData, string $currentUser, string $author, ?array $shareUsers, array $users): void {
+ $backend = $this->getBackend(['getUsersForShares']);
+
+ if ($shareUsers === null) {
+ $backend->expects($this->never())
+ ->method('getUsersForShares');
+ } else {
+ $backend->expects($this->once())
+ ->method('getUsersForShares')
+ ->with($shares)
+ ->willReturn($shareUsers);
+ }
+
+ if ($author !== '') {
+ if ($currentUser !== '') {
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->willReturn($this->getUserMock($currentUser));
+ } else {
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->willReturn(null);
+ }
+
+ $event = $this->createMock(IEvent::class);
+ $this->activityManager->expects($this->once())
+ ->method('generateEvent')
+ ->willReturn($event);
+
+ $event->expects($this->once())
+ ->method('setApp')
+ ->with('dav')
+ ->willReturnSelf();
+ $event->expects($this->once())
+ ->method('setObject')
+ ->with('addressbook', $addressBookData['id'])
+ ->willReturnSelf();
+ $event->expects($this->once())
+ ->method('setType')
+ ->with('contacts')
+ ->willReturnSelf();
+ $event->expects($this->once())
+ ->method('setAuthor')
+ ->with($author)
+ ->willReturnSelf();
+
+ $event->expects($this->exactly(count($users)))
+ ->method('setAffectedUser')
+ ->willReturnSelf();
+ $event->expects($this->exactly(count($users)))
+ ->method('setSubject')
+ ->willReturnSelf();
+ $this->activityManager->expects($this->exactly(count($users)))
+ ->method('publish')
+ ->with($event);
+ } else {
+ $this->activityManager->expects($this->never())
+ ->method('generateEvent');
+ }
+
+ $this->invokePrivate($backend, 'triggerCardActivity', [$action, $addressBookData, $shares, $cardData]);
+ }
+
+ public function testNoCardActivityCreatedForSystemAddressbook(): void {
+ $backend = $this->getBackend();
+ $this->activityManager->expects($this->never())
+ ->method('generateEvent');
+ $this->assertEmpty($this->invokePrivate($backend, 'triggerCardActivity', [Card::SUBJECT_UPDATE, ['principaluri' => 'principals/system/system'], [], []]));
+ }
+
+ public static function dataGetUsersForShares(): array {
+ return [
+ [
+ [],
+ [],
+ [],
+ ],
+ [
+ [
+ ['{http://owncloud.org/ns}principal' => 'principal/users/user1'],
+ ['{http://owncloud.org/ns}principal' => 'principal/users/user2'],
+ ['{http://owncloud.org/ns}principal' => 'principal/users/user2'],
+ ['{http://owncloud.org/ns}principal' => 'principal/users/user2'],
+ ['{http://owncloud.org/ns}principal' => 'principal/users/user3'],
+ ],
+ [],
+ ['user1', 'user2', 'user3'],
+ ],
+ [
+ [
+ ['{http://owncloud.org/ns}principal' => 'principal/users/user1'],
+ ['{http://owncloud.org/ns}principal' => 'principal/users/user2'],
+ ['{http://owncloud.org/ns}principal' => 'principal/users/user2'],
+ ['{http://owncloud.org/ns}principal' => 'principal/groups/group2'],
+ ['{http://owncloud.org/ns}principal' => 'principal/groups/group3'],
+ ],
+ ['group2' => null, 'group3' => null],
+ ['user1', 'user2'],
+ ],
+ [
+ [
+ ['{http://owncloud.org/ns}principal' => 'principal/users/user1'],
+ ['{http://owncloud.org/ns}principal' => 'principal/users/user2'],
+ ['{http://owncloud.org/ns}principal' => 'principal/users/user2'],
+ ['{http://owncloud.org/ns}principal' => 'principal/groups/group2'],
+ ['{http://owncloud.org/ns}principal' => 'principal/groups/group3'],
+ ],
+ ['group2' => ['user1', 'user2', 'user3'], 'group3' => ['user2', 'user3', 'user4']],
+ ['user1', 'user2', 'user3', 'user4'],
+ ],
+ ];
+ }
+
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataGetUsersForShares')]
+ public function testGetUsersForShares(array $shares, array $groups, array $expected): void {
+ $backend = $this->getBackend();
+
+ $getGroups = [];
+ foreach ($groups as $gid => $members) {
+ if ($members === null) {
+ $getGroups[] = [$gid, null];
+ continue;
+ }
+
+ $group = $this->createMock(IGroup::class);
+ $group->expects($this->once())
+ ->method('getUsers')
+ ->willReturn($this->getUsers($members));
+
+ $getGroups[] = [$gid, $group];
+ }
+
+ $this->groupManager->expects($this->exactly(sizeof($getGroups)))
+ ->method('get')
+ ->willReturnMap($getGroups);
+
+ $users = $this->invokePrivate($backend, 'getUsersForShares', [$shares]);
+ sort($users);
+ $this->assertEquals($expected, $users);
+ }
+
+ /**
+ * @param string[] $users
+ * @return IUser[]|MockObject[]
+ */
+ protected function getUsers(array $users): array {
+ $list = [];
+ foreach ($users as $user) {
+ $list[] = $this->getUserMock($user);
+ }
+ return $list;
+ }
+
+ /**
+ * @return IUser|MockObject
+ */
+ protected function getUserMock(string $uid): IUser {
+ $user = $this->createMock(IUser::class);
+ $user->expects($this->once())
+ ->method('getUID')
+ ->willReturn($uid);
+ return $user;
+ }
+}
diff --git a/apps/dav/tests/unit/CardDAV/AddressBookImplTest.php b/apps/dav/tests/unit/CardDAV/AddressBookImplTest.php
index 5b28e6b025b..74699cf3925 100644
--- a/apps/dav/tests/unit/CardDAV/AddressBookImplTest.php
+++ b/apps/dav/tests/unit/CardDAV/AddressBookImplTest.php
@@ -1,63 +1,31 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Björn Schießle <bjoern@schiessle.org>
- * @author call-me-matt <nextcloud@matthiasheinisch.de>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Georg Ehrke <oc.list@georgehrke.com>
- * @author Joas Schilling <coding@schilljs.com>
- * @author John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
-
namespace OCA\DAV\Tests\unit\CardDAV;
use OCA\DAV\CardDAV\AddressBook;
use OCA\DAV\CardDAV\AddressBookImpl;
use OCA\DAV\CardDAV\CardDavBackend;
+use OCA\DAV\Db\PropertyMapper;
use OCP\IURLGenerator;
+use PHPUnit\Framework\MockObject\MockObject;
use Sabre\VObject\Component\VCard;
use Sabre\VObject\Property\Text;
//use Sabre\VObject\Property\;
use Test\TestCase;
class AddressBookImplTest extends TestCase {
-
- /** @var AddressBookImpl */
- private $addressBookImpl;
-
- /** @var array */
- private $addressBookInfo;
-
- /** @var AddressBook | \PHPUnit\Framework\MockObject\MockObject */
- private $addressBook;
-
- /** @var IURLGenerator | \PHPUnit\Framework\MockObject\MockObject */
- private $urlGenerator;
-
- /** @var CardDavBackend | \PHPUnit\Framework\MockObject\MockObject */
- private $backend;
-
- /** @var VCard | \PHPUnit\Framework\MockObject\MockObject */
- private $vCard;
+ private array $addressBookInfo;
+ private AddressBook&MockObject $addressBook;
+ private IURLGenerator&MockObject $urlGenerator;
+ private CardDavBackend&MockObject $backend;
+ private PropertyMapper&MockObject $propertyMapper;
+ private VCard&MockObject $vCard;
+ private AddressBookImpl $addressBookImpl;
protected function setUp(): void {
parent::setUp();
@@ -68,34 +36,34 @@ class AddressBookImplTest extends TestCase {
'principaluri' => 'principals/system/system',
'{DAV:}displayname' => 'display name',
];
- $this->addressBook = $this->getMockBuilder(AddressBook::class)
- ->disableOriginalConstructor()->getMock();
- $this->backend = $this->getMockBuilder(CardDavBackend::class)
- ->disableOriginalConstructor()->getMock();
+ $this->addressBook = $this->createMock(AddressBook::class);
+ $this->backend = $this->createMock(CardDavBackend::class);
$this->vCard = $this->createMock(VCard::class);
$this->urlGenerator = $this->createMock(IURLGenerator::class);
+ $this->propertyMapper = $this->createMock(PropertyMapper::class);
$this->addressBookImpl = new AddressBookImpl(
$this->addressBook,
$this->addressBookInfo,
$this->backend,
- $this->urlGenerator
+ $this->urlGenerator,
+ $this->propertyMapper,
+ null
);
}
- public function testGetKey() {
+ public function testGetKey(): void {
$this->assertSame($this->addressBookInfo['id'],
$this->addressBookImpl->getKey());
}
- public function testGetDisplayName() {
+ public function testGetDisplayName(): void {
$this->assertSame($this->addressBookInfo['{DAV:}displayname'],
$this->addressBookImpl->getDisplayName());
}
- public function testSearch() {
-
- /** @var \PHPUnit\Framework\MockObject\MockObject | AddressBookImpl $addressBookImpl */
+ public function testSearch(): void {
+ /** @var MockObject&AddressBookImpl $addressBookImpl */
$addressBookImpl = $this->getMockBuilder(AddressBookImpl::class)
->setConstructorArgs(
[
@@ -103,9 +71,11 @@ class AddressBookImplTest extends TestCase {
$this->addressBookInfo,
$this->backend,
$this->urlGenerator,
+ $this->propertyMapper,
+ null
]
)
- ->setMethods(['vCard2Array', 'readCard'])
+ ->onlyMethods(['vCard2Array', 'readCard'])
->getMock();
$pattern = 'pattern';
@@ -123,25 +93,21 @@ class AddressBookImplTest extends TestCase {
$addressBookImpl->expects($this->exactly(2))->method('readCard')
->willReturn($this->vCard);
$addressBookImpl->expects($this->exactly(2))->method('vCard2Array')
- ->withConsecutive(
- ['foo.vcf', $this->vCard],
- ['bar.vcf', $this->vCard]
- )->willReturn('vCard');
+ ->willReturnMap([
+ ['foo.vcf', $this->vCard, 'vCard'],
+ ['bar.vcf', $this->vCard, 'vCard'],
+ ]);
$result = $addressBookImpl->search($pattern, $searchProperties, []);
$this->assertTrue((is_array($result)));
$this->assertSame(2, count($result));
}
- /**
- * @dataProvider dataTestCreate
- *
- * @param array $properties
- */
- public function testCreate($properties) {
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTestCreate')]
+ public function testCreate(array $properties): void {
$uid = 'uid';
- /** @var \PHPUnit\Framework\MockObject\MockObject | AddressBookImpl $addressBookImpl */
+ /** @var MockObject&AddressBookImpl $addressBookImpl */
$addressBookImpl = $this->getMockBuilder(AddressBookImpl::class)
->setConstructorArgs(
[
@@ -149,16 +115,27 @@ class AddressBookImplTest extends TestCase {
$this->addressBookInfo,
$this->backend,
$this->urlGenerator,
+ $this->propertyMapper,
+ null
]
)
- ->setMethods(['vCard2Array', 'createUid', 'createEmptyVCard'])
+ ->onlyMethods(['vCard2Array', 'createUid', 'createEmptyVCard'])
->getMock();
+ $expectedProperties = 0;
+ foreach ($properties as $data) {
+ if (is_string($data)) {
+ $expectedProperties++;
+ } else {
+ $expectedProperties += count($data);
+ }
+ }
+
$addressBookImpl->expects($this->once())->method('createUid')
->willReturn($uid);
$addressBookImpl->expects($this->once())->method('createEmptyVCard')
->with($uid)->willReturn($this->vCard);
- $this->vCard->expects($this->exactly(count($properties)))
+ $this->vCard->expects($this->exactly($expectedProperties))
->method('createProperty');
$this->backend->expects($this->once())->method('createCard');
$this->backend->expects($this->never())->method('updateCard');
@@ -169,19 +146,20 @@ class AddressBookImplTest extends TestCase {
$this->assertTrue($addressBookImpl->createOrUpdate($properties));
}
- public function dataTestCreate() {
+ public static function dataTestCreate(): array {
return [
[[]],
- [['FN' => 'John Doe']]
+ [['FN' => 'John Doe']],
+ [['FN' => 'John Doe', 'EMAIL' => ['john@doe.cloud', 'john.doe@example.org']]],
];
}
- public function testUpdate() {
+ public function testUpdate(): void {
$uid = 'uid';
$uri = 'bla.vcf';
$properties = ['URI' => $uri, 'UID' => $uid, 'FN' => 'John Doe'];
- /** @var \PHPUnit\Framework\MockObject\MockObject | AddressBookImpl $addressBookImpl */
+ /** @var MockObject&AddressBookImpl $addressBookImpl */
$addressBookImpl = $this->getMockBuilder(AddressBookImpl::class)
->setConstructorArgs(
[
@@ -189,9 +167,11 @@ class AddressBookImplTest extends TestCase {
$this->addressBookInfo,
$this->backend,
$this->urlGenerator,
+ $this->propertyMapper,
+ null
]
)
- ->setMethods(['vCard2Array', 'createUid', 'createEmptyVCard', 'readCard'])
+ ->onlyMethods(['vCard2Array', 'createUid', 'createEmptyVCard', 'readCard'])
->getMock();
$addressBookImpl->expects($this->never())->method('createUid');
@@ -201,7 +181,7 @@ class AddressBookImplTest extends TestCase {
->willReturn(['carddata' => 'data']);
$addressBookImpl->expects($this->once())->method('readCard')
->with('data')->willReturn($this->vCard);
- $this->vCard->expects($this->exactly(count($properties)-1))
+ $this->vCard->expects($this->exactly(count($properties) - 1))
->method('createProperty');
$this->backend->expects($this->never())->method('createCard');
$this->backend->expects($this->once())->method('updateCard');
@@ -211,14 +191,14 @@ class AddressBookImplTest extends TestCase {
$this->assertTrue($addressBookImpl->createOrUpdate($properties));
}
- public function testUpdateWithTypes() {
+ public function testUpdateWithTypes(): void {
$uid = 'uid';
$uri = 'bla.vcf';
$properties = ['URI' => $uri, 'UID' => $uid, 'FN' => 'John Doe', 'ADR' => [['type' => 'HOME', 'value' => ';;street;city;;;country']]];
$vCard = new vCard;
- $textProperty = $vCard->createProperty('KEY','value');
+ $textProperty = $vCard->createProperty('KEY', 'value');
- /** @var \PHPUnit\Framework\MockObject\MockObject | AddressBookImpl $addressBookImpl */
+ /** @var MockObject&AddressBookImpl $addressBookImpl */
$addressBookImpl = $this->getMockBuilder(AddressBookImpl::class)
->setConstructorArgs(
[
@@ -226,9 +206,11 @@ class AddressBookImplTest extends TestCase {
$this->addressBookInfo,
$this->backend,
$this->urlGenerator,
+ $this->propertyMapper,
+ null
]
)
- ->setMethods(['vCard2Array', 'createUid', 'createEmptyVCard', 'readCard'])
+ ->onlyMethods(['vCard2Array', 'createUid', 'createEmptyVCard', 'readCard'])
->getMock();
$this->backend->expects($this->once())->method('getCard')
@@ -237,7 +219,7 @@ class AddressBookImplTest extends TestCase {
$addressBookImpl->expects($this->once())->method('readCard')
->with('data')->willReturn($this->vCard);
$this->vCard->method('createProperty')->willReturn($textProperty);
- $this->vCard->expects($this->exactly(count($properties)-1))
+ $this->vCard->expects($this->exactly(count($properties) - 1))
->method('createProperty');
$this->vCard->expects($this->once())->method('remove')
->with('ADR');
@@ -246,13 +228,8 @@ class AddressBookImplTest extends TestCase {
$addressBookImpl->createOrUpdate($properties);
}
- /**
- * @dataProvider dataTestGetPermissions
- *
- * @param array $permissions
- * @param int $expected
- */
- public function testGetPermissions($permissions, $expected) {
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGetPermissions')]
+ public function testGetPermissions(array $permissions, int $expected): void {
$this->addressBook->expects($this->once())->method('getACL')
->willReturn($permissions);
@@ -261,21 +238,22 @@ class AddressBookImplTest extends TestCase {
);
}
- public function dataTestGetPermissions() {
+ public static function dataTestGetPermissions(): array {
return [
[[], 0],
- [[['privilege' => '{DAV:}read']], 1],
- [[['privilege' => '{DAV:}write']], 6],
- [[['privilege' => '{DAV:}all']], 31],
- [[['privilege' => '{DAV:}read'],['privilege' => '{DAV:}write']], 7],
- [[['privilege' => '{DAV:}read'],['privilege' => '{DAV:}all']], 31],
- [[['privilege' => '{DAV:}all'],['privilege' => '{DAV:}write']], 31],
- [[['privilege' => '{DAV:}read'],['privilege' => '{DAV:}write'],['privilege' => '{DAV:}all']], 31],
- [[['privilege' => '{DAV:}all'],['privilege' => '{DAV:}read'],['privilege' => '{DAV:}write']], 31],
+ [[['privilege' => '{DAV:}read', 'principal' => 'principals/system/system']], 1],
+ [[['privilege' => '{DAV:}read', 'principal' => 'principals/system/system'], ['privilege' => '{DAV:}write', 'principal' => 'principals/someone/else']], 1],
+ [[['privilege' => '{DAV:}write', 'principal' => 'principals/system/system']], 6],
+ [[['privilege' => '{DAV:}all', 'principal' => 'principals/system/system']], 31],
+ [[['privilege' => '{DAV:}read', 'principal' => 'principals/system/system'],['privilege' => '{DAV:}write', 'principal' => 'principals/system/system']], 7],
+ [[['privilege' => '{DAV:}read', 'principal' => 'principals/system/system'],['privilege' => '{DAV:}all', 'principal' => 'principals/system/system']], 31],
+ [[['privilege' => '{DAV:}all', 'principal' => 'principals/system/system'],['privilege' => '{DAV:}write', 'principal' => 'principals/system/system']], 31],
+ [[['privilege' => '{DAV:}read', 'principal' => 'principals/system/system'],['privilege' => '{DAV:}write', 'principal' => 'principals/system/system'],['privilege' => '{DAV:}all', 'principal' => 'principals/system/system']], 31],
+ [[['privilege' => '{DAV:}all', 'principal' => 'principals/system/system'],['privilege' => '{DAV:}read', 'principal' => 'principals/system/system'],['privilege' => '{DAV:}write', 'principal' => 'principals/system/system']], 31],
];
}
- public function testDelete() {
+ public function testDelete(): void {
$cardId = 1;
$cardUri = 'cardUri';
$this->backend->expects($this->once())->method('getCardUri')
@@ -287,7 +265,7 @@ class AddressBookImplTest extends TestCase {
$this->assertTrue($this->addressBookImpl->delete($cardId));
}
- public function testReadCard() {
+ public function testReadCard(): void {
$vCard = new VCard();
$vCard->add(new Text($vCard, 'UID', 'uid'));
$vCardSerialized = $vCard->serialize();
@@ -298,8 +276,8 @@ class AddressBookImplTest extends TestCase {
$this->assertSame($vCardSerialized, $resultSerialized);
}
- public function testCreateUid() {
- /** @var \PHPUnit\Framework\MockObject\MockObject | AddressBookImpl $addressBookImpl */
+ public function testCreateUid(): void {
+ /** @var MockObject&AddressBookImpl $addressBookImpl */
$addressBookImpl = $this->getMockBuilder(AddressBookImpl::class)
->setConstructorArgs(
[
@@ -307,13 +285,19 @@ class AddressBookImplTest extends TestCase {
$this->addressBookInfo,
$this->backend,
$this->urlGenerator,
+ $this->propertyMapper,
+ null
]
)
- ->setMethods(['getUid'])
+ ->onlyMethods(['getUid'])
->getMock();
- $addressBookImpl->expects($this->at(0))->method('getUid')->willReturn('uid0');
- $addressBookImpl->expects($this->at(1))->method('getUid')->willReturn('uid1');
+ $addressBookImpl->expects($this->exactly(2))
+ ->method('getUid')
+ ->willReturnOnConsecutiveCalls(
+ 'uid0',
+ 'uid1',
+ );
// simulate that 'uid0' already exists, so the second uid will be returned
$this->backend->expects($this->exactly(2))->method('getContact')
@@ -328,7 +312,7 @@ class AddressBookImplTest extends TestCase {
);
}
- public function testCreateEmptyVCard() {
+ public function testCreateEmptyVCard(): void {
$uid = 'uid';
$expectedVCard = new VCard();
$expectedVCard->UID = $uid;
@@ -340,7 +324,7 @@ class AddressBookImplTest extends TestCase {
$this->assertSame($expectedVCardSerialized, $resultSerialized);
}
- public function testVCard2Array() {
+ public function testVCard2Array(): void {
$vCard = new VCard();
$vCard->add($vCard->createProperty('FN', 'Full Name'));
@@ -407,7 +391,7 @@ class AddressBookImplTest extends TestCase {
], $array);
}
- public function testVCard2ArrayWithTypes() {
+ public function testVCard2ArrayWithTypes(): void {
$vCard = new VCard();
$vCard->add($vCard->createProperty('FN', 'Full Name'));
@@ -499,7 +483,9 @@ class AddressBookImplTest extends TestCase {
$this->addressBook,
$addressBookInfo,
$this->backend,
- $this->urlGenerator
+ $this->urlGenerator,
+ $this->propertyMapper,
+ null
);
$this->assertTrue($addressBookImpl->isSystemAddressBook());
@@ -518,7 +504,9 @@ class AddressBookImplTest extends TestCase {
$this->addressBook,
$addressBookInfo,
$this->backend,
- $this->urlGenerator
+ $this->urlGenerator,
+ $this->propertyMapper,
+ 'user2'
);
$this->assertFalse($addressBookImpl->isSystemAddressBook());
@@ -538,7 +526,9 @@ class AddressBookImplTest extends TestCase {
$this->addressBook,
$addressBookInfo,
$this->backend,
- $this->urlGenerator
+ $this->urlGenerator,
+ $this->propertyMapper,
+ 'user2'
);
$this->assertFalse($addressBookImpl->isSystemAddressBook());
diff --git a/apps/dav/tests/unit/CardDAV/AddressBookTest.php b/apps/dav/tests/unit/CardDAV/AddressBookTest.php
index cddbd5164dc..cf28b7b8a8e 100644
--- a/apps/dav/tests/unit/CardDAV/AddressBookTest.php
+++ b/apps/dav/tests/unit/CardDAV/AddressBookTest.php
@@ -1,120 +1,144 @@
<?php
+
+declare(strict_types=1);
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
-
namespace OCA\DAV\Tests\unit\CardDAV;
use OCA\DAV\CardDAV\AddressBook;
+use OCA\DAV\CardDAV\Card;
use OCA\DAV\CardDAV\CardDavBackend;
use OCP\IL10N;
+use PHPUnit\Framework\MockObject\MockObject;
+use Psr\Log\LoggerInterface;
+use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\PropPatch;
use Test\TestCase;
class AddressBookTest extends TestCase {
- public function testDelete() {
- /** @var \PHPUnit\Framework\MockObject\MockObject | CardDavBackend $backend */
- $backend = $this->getMockBuilder(CardDavBackend::class)->disableOriginalConstructor()->getMock();
+ public function testMove(): void {
+ $backend = $this->createMock(CardDavBackend::class);
+ $addressBookInfo = [
+ '{http://owncloud.org/ns}owner-principal' => 'user1',
+ '{DAV:}displayname' => 'Test address book',
+ 'principaluri' => 'user2',
+ 'id' => 666,
+ 'uri' => 'default',
+ ];
+ $l10n = $this->createMock(IL10N::class);
+ $addressBook = new AddressBook($backend, $addressBookInfo, $l10n);
+
+ $card = new Card($backend, $addressBookInfo, ['id' => 5, 'carddata' => 'RANDOM VCF DATA', 'uri' => 'something', 'addressbookid' => 23]);
+
+ $backend->expects($this->once())->method('moveCard')
+ ->with(23, 'something', 666, 'new')
+ ->willReturn(true);
+
+ $addressBook->moveInto('new', 'old', $card);
+ }
+
+ public function testDelete(): void {
+ /** @var MockObject | CardDavBackend $backend */
+ $backend = $this->createMock(CardDavBackend::class);
$backend->expects($this->once())->method('updateShares');
$backend->expects($this->any())->method('getShares')->willReturn([
['href' => 'principal:user2']
]);
- $calendarInfo = [
+ $addressBookInfo = [
'{http://owncloud.org/ns}owner-principal' => 'user1',
'{DAV:}displayname' => 'Test address book',
'principaluri' => 'user2',
'id' => 666,
'uri' => 'default',
];
- $l = $this->createMock(IL10N::class);
- $c = new AddressBook($backend, $calendarInfo, $l);
- $c->delete();
+ $l10n = $this->createMock(IL10N::class);
+ $logger = $this->createMock(LoggerInterface::class);
+ $addressBook = new AddressBook($backend, $addressBookInfo, $l10n);
+ $addressBook->delete();
}
- public function testDeleteFromGroup() {
- $this->expectException(\Sabre\DAV\Exception\Forbidden::class);
+ public function testDeleteFromGroup(): void {
+ $this->expectException(Forbidden::class);
- /** @var \PHPUnit\Framework\MockObject\MockObject | CardDavBackend $backend */
- $backend = $this->getMockBuilder(CardDavBackend::class)->disableOriginalConstructor()->getMock();
+ /** @var MockObject | CardDavBackend $backend */
+ $backend = $this->createMock(CardDavBackend::class);
$backend->expects($this->never())->method('updateShares');
$backend->expects($this->any())->method('getShares')->willReturn([
['href' => 'principal:group2']
]);
- $calendarInfo = [
+ $addressBookInfo = [
'{http://owncloud.org/ns}owner-principal' => 'user1',
'{DAV:}displayname' => 'Test address book',
'principaluri' => 'user2',
'id' => 666,
'uri' => 'default',
];
- $l = $this->createMock(IL10N::class);
- $c = new AddressBook($backend, $calendarInfo, $l);
- $c->delete();
+ $l10n = $this->createMock(IL10N::class);
+ $logger = $this->createMock(LoggerInterface::class);
+ $addressBook = new AddressBook($backend, $addressBookInfo, $l10n);
+ $addressBook->delete();
}
- public function testPropPatch() {
- $this->expectException(\Sabre\DAV\Exception\Forbidden::class);
-
- /** @var \PHPUnit\Framework\MockObject\MockObject | CardDavBackend $backend */
- $backend = $this->getMockBuilder(CardDavBackend::class)->disableOriginalConstructor()->getMock();
- $calendarInfo = [
+ public function testPropPatchShared(): void {
+ /** @var MockObject | CardDavBackend $backend */
+ $backend = $this->createMock(CardDavBackend::class);
+ $backend->expects($this->never())->method('updateAddressBook');
+ $addressBookInfo = [
'{http://owncloud.org/ns}owner-principal' => 'user1',
'{DAV:}displayname' => 'Test address book',
'principaluri' => 'user2',
'id' => 666,
'uri' => 'default',
];
- $l = $this->createMock(IL10N::class);
- $c = new AddressBook($backend, $calendarInfo, $l);
- $c->propPatch(new PropPatch([]));
+ $l10n = $this->createMock(IL10N::class);
+ $logger = $this->createMock(LoggerInterface::class);
+ $addressBook = new AddressBook($backend, $addressBookInfo, $l10n);
+ $addressBook->propPatch(new PropPatch(['{DAV:}displayname' => 'Test address book']));
}
- /**
- * @dataProvider providesReadOnlyInfo
- */
- public function testAcl($expectsWrite, $readOnlyValue, $hasOwnerSet) {
- /** @var \PHPUnit\Framework\MockObject\MockObject | CardDavBackend $backend */
- $backend = $this->getMockBuilder(CardDavBackend::class)->disableOriginalConstructor()->getMock();
+ public function testPropPatchNotShared(): void {
+ /** @var MockObject | CardDavBackend $backend */
+ $backend = $this->createMock(CardDavBackend::class);
+ $backend->expects($this->atLeast(1))->method('updateAddressBook');
+ $addressBookInfo = [
+ '{DAV:}displayname' => 'Test address book',
+ 'principaluri' => 'user1',
+ 'id' => 666,
+ 'uri' => 'default',
+ ];
+ $l10n = $this->createMock(IL10N::class);
+ $logger = $this->createMock(LoggerInterface::class);
+ $addressBook = new AddressBook($backend, $addressBookInfo, $l10n);
+ $addressBook->propPatch(new PropPatch(['{DAV:}displayname' => 'Test address book']));
+ }
+
+ #[\PHPUnit\Framework\Attributes\DataProvider('providesReadOnlyInfo')]
+ public function testAcl(bool $expectsWrite, ?bool $readOnlyValue, bool $hasOwnerSet): void {
+ /** @var MockObject | CardDavBackend $backend */
+ $backend = $this->createMock(CardDavBackend::class);
$backend->expects($this->any())->method('applyShareAcl')->willReturnArgument(1);
- $calendarInfo = [
+ $addressBookInfo = [
'{DAV:}displayname' => 'Test address book',
'principaluri' => 'user2',
'id' => 666,
'uri' => 'default'
];
if (!is_null($readOnlyValue)) {
- $calendarInfo['{http://owncloud.org/ns}read-only'] = $readOnlyValue;
+ $addressBookInfo['{http://owncloud.org/ns}read-only'] = $readOnlyValue;
}
if ($hasOwnerSet) {
- $calendarInfo['{http://owncloud.org/ns}owner-principal'] = 'user1';
+ $addressBookInfo['{http://owncloud.org/ns}owner-principal'] = 'user1';
}
- $l = $this->createMock(IL10N::class);
- $c = new AddressBook($backend, $calendarInfo, $l);
- $acl = $c->getACL();
- $childAcl = $c->getChildACL();
+ $l10n = $this->createMock(IL10N::class);
+ $logger = $this->createMock(LoggerInterface::class);
+ $addressBook = new AddressBook($backend, $addressBookInfo, $l10n);
+ $acl = $addressBook->getACL();
+ $childAcl = $addressBook->getChildACL();
$expectedAcl = [[
'privilege' => '{DAV:}read',
@@ -124,6 +148,10 @@ class AddressBookTest extends TestCase {
'privilege' => '{DAV:}write',
'principal' => $hasOwnerSet ? 'user1' : 'user2',
'protected' => true
+ ], [
+ 'privilege' => '{DAV:}write-properties',
+ 'principal' => $hasOwnerSet ? 'user1' : 'user2',
+ 'protected' => true
]];
if ($hasOwnerSet) {
$expectedAcl[] = [
@@ -143,7 +171,7 @@ class AddressBookTest extends TestCase {
$this->assertEquals($expectedAcl, $childAcl);
}
- public function providesReadOnlyInfo() {
+ public static function providesReadOnlyInfo(): array {
return [
'read-only property not set' => [true, null, true],
'read-only property is false' => [true, false, true],
diff --git a/apps/dav/tests/unit/CardDAV/BirthdayServiceTest.php b/apps/dav/tests/unit/CardDAV/BirthdayServiceTest.php
index 5d1323ba8b9..6908dfd17bc 100644
--- a/apps/dav/tests/unit/CardDAV/BirthdayServiceTest.php
+++ b/apps/dav/tests/unit/CardDAV/BirthdayServiceTest.php
@@ -1,30 +1,11 @@
<?php
+
+declare(strict_types=1);
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Georg Ehrke <oc.list@georgehrke.com>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
-
namespace OCA\DAV\Tests\unit\CardDAV;
use OCA\DAV\CalDAV\BirthdayService;
@@ -34,26 +15,19 @@ use OCA\DAV\DAV\GroupPrincipalBackend;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IL10N;
+use PHPUnit\Framework\MockObject\MockObject;
use Sabre\VObject\Component\VCalendar;
use Sabre\VObject\Reader;
use Test\TestCase;
class BirthdayServiceTest extends TestCase {
-
- /** @var BirthdayService */
- private $service;
- /** @var CalDavBackend | \PHPUnit\Framework\MockObject\MockObject */
- private $calDav;
- /** @var CardDavBackend | \PHPUnit\Framework\MockObject\MockObject */
- private $cardDav;
- /** @var GroupPrincipalBackend | \PHPUnit\Framework\MockObject\MockObject */
- private $groupPrincipalBackend;
- /** @var IConfig | \PHPUnit\Framework\MockObject\MockObject */
- private $config;
- /** @var IDBConnection | \PHPUnit\Framework\MockObject\MockObject */
- private $dbConnection;
- /** @var IL10N | \PHPUnit\Framework\MockObject\MockObject */
- private $l10n;
+ private CalDavBackend&MockObject $calDav;
+ private CardDavBackend&MockObject $cardDav;
+ private GroupPrincipalBackend&MockObject $groupPrincipalBackend;
+ private IConfig&MockObject $config;
+ private IDBConnection&MockObject $dbConnection;
+ private IL10N&MockObject $l10n;
+ private BirthdayService $service;
protected function setUp(): void {
parent::setUp();
@@ -71,23 +45,15 @@ class BirthdayServiceTest extends TestCase {
return vsprintf($string, $args);
});
- $this->service = new BirthdayService($this->calDav,$this->cardDav,
+ $this->service = new BirthdayService($this->calDav, $this->cardDav,
$this->groupPrincipalBackend, $this->config,
$this->dbConnection, $this->l10n);
}
- /**
- * @dataProvider providesVCards
- * @param string $expectedSummary
- * @param string $expectedDTStart
- * @param string $expectedFieldType
- * @param string $expectedUnknownYear
- * @param string $expectedOriginalYear
- * @param string | null $data
- */
- public function testBuildBirthdayFromContact($expectedSummary, $expectedDTStart, $expectedFieldType, $expectedUnknownYear, $expectedOriginalYear, $data, $fieldType, $prefix, $supports4Bytes) {
+ #[\PHPUnit\Framework\Attributes\DataProvider('providesVCards')]
+ public function testBuildBirthdayFromContact(?string $expectedSummary, ?string $expectedDTStart, ?string $expectedRrule, ?string $expectedFieldType, ?string $expectedUnknownYear, ?string $expectedOriginalYear, ?string $expectedReminder, ?string $data, string $fieldType, string $prefix, bool $supports4Bytes, ?string $configuredReminder): void {
$this->dbConnection->method('supports4ByteText')->willReturn($supports4Bytes);
- $cal = $this->service->buildDateFromContact($data, $fieldType, $prefix);
+ $cal = $this->service->buildDateFromContact($data, $fieldType, $prefix, $configuredReminder);
if ($expectedSummary === null) {
$this->assertNull($cal);
@@ -95,7 +61,7 @@ class BirthdayServiceTest extends TestCase {
$this->assertInstanceOf('Sabre\VObject\Component\VCalendar', $cal);
$this->assertEquals('-//IDN nextcloud.com//Birthday calendar//EN', $cal->PRODID->getValue());
$this->assertTrue(isset($cal->VEVENT));
- $this->assertEquals('FREQ=YEARLY', $cal->VEVENT->RRULE->getValue());
+ $this->assertEquals($expectedRrule, $cal->VEVENT->RRULE->getValue());
$this->assertEquals($expectedSummary, $cal->VEVENT->SUMMARY->getValue());
$this->assertEquals($expectedDTStart, $cal->VEVENT->DTSTART->getValue());
$this->assertEquals($expectedFieldType, $cal->VEVENT->{'X-NEXTCLOUD-BC-FIELD-TYPE'}->getValue());
@@ -105,11 +71,16 @@ class BirthdayServiceTest extends TestCase {
$this->assertEquals($expectedOriginalYear, $cal->VEVENT->{'X-NEXTCLOUD-BC-YEAR'}->getValue());
}
+ if ($expectedReminder) {
+ $this->assertEquals($expectedReminder, $cal->VEVENT->VALARM->TRIGGER->getValue());
+ $this->assertEquals('DURATION', $cal->VEVENT->VALARM->TRIGGER->getValueType());
+ }
+
$this->assertEquals('TRANSPARENT', $cal->VEVENT->TRANSP->getValue());
}
}
- public function testOnCardDeleteGloballyDisabled() {
+ public function testOnCardDeleteGloballyDisabled(): void {
$this->config->expects($this->once())
->method('getAppValue')
->with('dav', 'generateBirthdayCalendar', 'yes')
@@ -120,7 +91,7 @@ class BirthdayServiceTest extends TestCase {
$this->service->onCardDeleted(666, 'gump.vcf');
}
- public function testOnCardDeleteUserDisabled() {
+ public function testOnCardDeleteUserDisabled(): void {
$this->config->expects($this->once())
->method('getAppValue')
->with('dav', 'generateBirthdayCalendar', 'yes')
@@ -144,7 +115,7 @@ class BirthdayServiceTest extends TestCase {
$this->service->onCardDeleted(666, 'gump.vcf');
}
- public function testOnCardDeleted() {
+ public function testOnCardDeleted(): void {
$this->config->expects($this->once())
->method('getAppValue')
->with('dav', 'generateBirthdayCalendar', 'yes')
@@ -166,15 +137,23 @@ class BirthdayServiceTest extends TestCase {
->willReturn([
'id' => 1234
]);
- $this->calDav->expects($this->at(1))->method('deleteCalendarObject')->with(1234, 'default-gump.vcf.ics');
- $this->calDav->expects($this->at(2))->method('deleteCalendarObject')->with(1234, 'default-gump.vcf-death.ics');
- $this->calDav->expects($this->at(3))->method('deleteCalendarObject')->with(1234, 'default-gump.vcf-anniversary.ics');
+ $calls = [
+ [1234, 'default-gump.vcf.ics'],
+ [1234, 'default-gump.vcf-death.ics'],
+ [1234, 'default-gump.vcf-anniversary.ics'],
+ ];
+ $this->calDav->expects($this->exactly(count($calls)))
+ ->method('deleteCalendarObject')
+ ->willReturnCallback(function ($calendarId, $objectUri) use (&$calls): void {
+ $expected = array_shift($calls);
+ $this->assertEquals($expected, [$calendarId, $objectUri]);
+ });
$this->cardDav->expects($this->once())->method('getShares')->willReturn([]);
$this->service->onCardDeleted(666, 'gump.vcf');
}
- public function testOnCardChangedGloballyDisabled() {
+ public function testOnCardChangedGloballyDisabled(): void {
$this->config->expects($this->once())
->method('getAppValue')
->with('dav', 'generateBirthdayCalendar', 'yes')
@@ -183,14 +162,14 @@ class BirthdayServiceTest extends TestCase {
$this->cardDav->expects($this->never())->method('getAddressBookById');
$service = $this->getMockBuilder(BirthdayService::class)
- ->setMethods(['buildDateFromContact', 'birthdayEvenChanged'])
+ ->onlyMethods(['buildDateFromContact', 'birthdayEvenChanged'])
->setConstructorArgs([$this->calDav, $this->cardDav, $this->groupPrincipalBackend, $this->config, $this->dbConnection, $this->l10n])
->getMock();
$service->onCardChanged(666, 'gump.vcf', '');
}
- public function testOnCardChangedUserDisabled() {
+ public function testOnCardChangedUserDisabled(): void {
$this->config->expects($this->once())
->method('getAppValue')
->with('dav', 'generateBirthdayCalendar', 'yes')
@@ -210,28 +189,28 @@ class BirthdayServiceTest extends TestCase {
$this->cardDav->expects($this->once())->method('getShares')->willReturn([]);
$this->calDav->expects($this->never())->method('getCalendarByUri');
- /** @var BirthdayService | \PHPUnit\Framework\MockObject\MockObject $service */
+ /** @var BirthdayService&MockObject $service */
$service = $this->getMockBuilder(BirthdayService::class)
- ->setMethods(['buildDateFromContact', 'birthdayEvenChanged'])
+ ->onlyMethods(['buildDateFromContact', 'birthdayEvenChanged'])
->setConstructorArgs([$this->calDav, $this->cardDav, $this->groupPrincipalBackend, $this->config, $this->dbConnection, $this->l10n])
->getMock();
$service->onCardChanged(666, 'gump.vcf', '');
}
- /**
- * @dataProvider providesCardChanges
- */
- public function testOnCardChanged($expectedOp) {
+ #[\PHPUnit\Framework\Attributes\DataProvider('providesCardChanges')]
+ public function testOnCardChanged(string $expectedOp): void {
$this->config->expects($this->once())
->method('getAppValue')
->with('dav', 'generateBirthdayCalendar', 'yes')
->willReturn('yes');
- $this->config->expects($this->once())
+ $this->config->expects($this->exactly(2))
->method('getUserValue')
- ->with('user01', 'dav', 'generateBirthdayCalendar', 'yes')
- ->willReturn('yes');
+ ->willReturnMap([
+ ['user01', 'dav', 'generateBirthdayCalendar', 'yes', 'yes'],
+ ['user01', 'dav', 'birthdayCalendarReminderOffset', 'PT9H', 'PT9H'],
+ ]);
$this->cardDav->expects($this->once())->method('getAddressBookById')
->with(666)
@@ -246,31 +225,45 @@ class BirthdayServiceTest extends TestCase {
]);
$this->cardDav->expects($this->once())->method('getShares')->willReturn([]);
- /** @var BirthdayService | \PHPUnit\Framework\MockObject\MockObject $service */
+ /** @var BirthdayService&MockObject $service */
$service = $this->getMockBuilder(BirthdayService::class)
- ->setMethods(['buildDateFromContact', 'birthdayEvenChanged'])
+ ->onlyMethods(['buildDateFromContact', 'birthdayEvenChanged'])
->setConstructorArgs([$this->calDav, $this->cardDav, $this->groupPrincipalBackend, $this->config, $this->dbConnection, $this->l10n])
->getMock();
if ($expectedOp === 'delete') {
$this->calDav->expects($this->exactly(3))->method('getCalendarObject')->willReturn('');
$service->expects($this->exactly(3))->method('buildDateFromContact')->willReturn(null);
- $this->calDav->expects($this->exactly(3))->method('deleteCalendarObject')->withConsecutive(
+
+ $calls = [
[1234, 'default-gump.vcf.ics'],
[1234, 'default-gump.vcf-death.ics'],
[1234, 'default-gump.vcf-anniversary.ics']
- );
+ ];
+ $this->calDav->expects($this->exactly(count($calls)))
+ ->method('deleteCalendarObject')
+ ->willReturnCallback(function ($calendarId, $objectUri) use (&$calls): void {
+ $expected = array_shift($calls);
+ $this->assertEquals($expected, [$calendarId, $objectUri]);
+ });
}
if ($expectedOp === 'create') {
$vCal = new VCalendar();
$vCal->PRODID = '-//Nextcloud testing//mocked object//';
$service->expects($this->exactly(3))->method('buildDateFromContact')->willReturn($vCal);
- $this->calDav->expects($this->exactly(3))->method('createCalendarObject')->withConsecutive(
+
+ $createCalendarObjectCalls = [
[1234, 'default-gump.vcf.ics', "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nCALSCALE:GREGORIAN\r\nPRODID:-//Nextcloud testing//mocked object//\r\nEND:VCALENDAR\r\n"],
[1234, 'default-gump.vcf-death.ics', "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nCALSCALE:GREGORIAN\r\nPRODID:-//Nextcloud testing//mocked object//\r\nEND:VCALENDAR\r\n"],
[1234, 'default-gump.vcf-anniversary.ics', "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nCALSCALE:GREGORIAN\r\nPRODID:-//Nextcloud testing//mocked object//\r\nEND:VCALENDAR\r\n"]
- );
+ ];
+ $this->calDav->expects($this->exactly(count($createCalendarObjectCalls)))
+ ->method('createCalendarObject')
+ ->willReturnCallback(function ($calendarId, $objectUri, $calendarData) use (&$createCalendarObjectCalls): void {
+ $expected = array_shift($createCalendarObjectCalls);
+ $this->assertEquals($expected, [$calendarId, $objectUri, $calendarData]);
+ });
}
if ($expectedOp === 'update') {
$vCal = new VCalendar();
@@ -279,28 +272,30 @@ class BirthdayServiceTest extends TestCase {
$service->expects($this->exactly(3))->method('buildDateFromContact')->willReturn($vCal);
$service->expects($this->exactly(3))->method('birthdayEvenChanged')->willReturn(true);
$this->calDav->expects($this->exactly(3))->method('getCalendarObject')->willReturn(['calendardata' => '']);
- $this->calDav->expects($this->exactly(3))->method('updateCalendarObject')->withConsecutive(
+
+ $updateCalendarObjectCalls = [
[1234, 'default-gump.vcf.ics', "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nCALSCALE:GREGORIAN\r\nPRODID:-//Nextcloud testing//mocked object//\r\nEND:VCALENDAR\r\n"],
[1234, 'default-gump.vcf-death.ics', "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nCALSCALE:GREGORIAN\r\nPRODID:-//Nextcloud testing//mocked object//\r\nEND:VCALENDAR\r\n"],
[1234, 'default-gump.vcf-anniversary.ics', "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nCALSCALE:GREGORIAN\r\nPRODID:-//Nextcloud testing//mocked object//\r\nEND:VCALENDAR\r\n"]
- );
+ ];
+ $this->calDav->expects($this->exactly(count($updateCalendarObjectCalls)))
+ ->method('updateCalendarObject')
+ ->willReturnCallback(function ($calendarId, $objectUri, $calendarData) use (&$updateCalendarObjectCalls): void {
+ $expected = array_shift($updateCalendarObjectCalls);
+ $this->assertEquals($expected, [$calendarId, $objectUri, $calendarData]);
+ });
}
$service->onCardChanged(666, 'gump.vcf', '');
}
- /**
- * @dataProvider providesBirthday
- * @param $expected
- * @param $old
- * @param $new
- */
- public function testBirthdayEvenChanged($expected, $old, $new) {
+ #[\PHPUnit\Framework\Attributes\DataProvider('providesBirthday')]
+ public function testBirthdayEvenChanged(bool $expected, string $old, string $new): void {
$new = Reader::read($new);
$this->assertEquals($expected, $this->service->birthdayEvenChanged($old, $new));
}
- public function testGetAllAffectedPrincipals() {
+ public function testGetAllAffectedPrincipals(): void {
$this->cardDav->expects($this->once())->method('getShares')->willReturn([
[
'{http://owncloud.org/ns}group-share' => false,
@@ -339,7 +334,7 @@ class BirthdayServiceTest extends TestCase {
], $users);
}
- public function testBirthdayCalendarHasComponentEvent() {
+ public function testBirthdayCalendarHasComponentEvent(): void {
$this->calDav->expects($this->once())
->method('createCalendar')
->with('principal001', 'contact_birthdays', [
@@ -350,33 +345,33 @@ class BirthdayServiceTest extends TestCase {
$this->service->ensureCalendarExists('principal001');
}
- public function testResetForUser() {
- $this->calDav->expects($this->at(0))
+ public function testResetForUser(): void {
+ $this->calDav->expects($this->once())
->method('getCalendarByUri')
->with('principals/users/user123', 'contact_birthdays')
->willReturn(['id' => 42]);
- $this->calDav->expects($this->at(1))
+ $this->calDav->expects($this->once())
->method('getCalendarObjects')
->with(42, 0)
->willReturn([['uri' => '1.ics'], ['uri' => '2.ics'], ['uri' => '3.ics']]);
- $this->calDav->expects($this->at(2))
- ->method('deleteCalendarObject')
- ->with(42, '1.ics', 0);
-
- $this->calDav->expects($this->at(3))
- ->method('deleteCalendarObject')
- ->with(42, '2.ics', 0);
-
- $this->calDav->expects($this->at(4))
+ $calls = [
+ [42, '1.ics', 0],
+ [42, '2.ics', 0],
+ [42, '3.ics', 0],
+ ];
+ $this->calDav->expects($this->exactly(count($calls)))
->method('deleteCalendarObject')
- ->with(42, '3.ics', 0);
+ ->willReturnCallback(function ($calendarId, $objectUri, $calendarType) use (&$calls): void {
+ $expected = array_shift($calls);
+ $this->assertEquals($expected, [$calendarId, $objectUri, $calendarType]);
+ });
$this->service->resetForUser('user123');
}
- public function providesBirthday() {
+ public static function providesBirthday(): array {
return [
[true,
'',
@@ -393,7 +388,7 @@ class BirthdayServiceTest extends TestCase {
];
}
- public function providesCardChanges() {
+ public static function providesCardChanges(): array {
return[
['delete'],
['create'],
@@ -401,31 +396,38 @@ class BirthdayServiceTest extends TestCase {
];
}
- public function providesVCards() {
+ public static function providesVCards(): array {
return [
- // $expectedSummary, $expectedDTStart, $expectedFieldType, $expectedUnknownYear, $expectedOriginalYear, $data, $fieldType, $prefix, $supports4Byte
- [null, null, null, null, null, 'yasfewf', '', '', true],
- [null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nEND:VCARD\r\n", 'BDAY', '', true],
- [null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:\r\nEND:VCARD\r\n", 'BDAY', '', true],
- [null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:someday\r\nEND:VCARD\r\n", 'BDAY', '', true],
- ['🎂 12345 (1900)', '19700101', 'BDAY', '0', '1900', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19000101\r\nEND:VCARD\r\n", 'BDAY', '', true],
- ['🎂 12345 (1900)', '19701231', 'BDAY', '0', '1900', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19001231\r\nEND:VCARD\r\n", 'BDAY', '', true],
- ['Death of 12345 (1900)', '19701231', 'DEATHDATE', '0', '1900', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nDEATHDATE:19001231\r\nEND:VCARD\r\n", 'DEATHDATE', '-death', true],
- ['Death of 12345 (1900)', '19701231', 'DEATHDATE', '0', '1900', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nDEATHDATE:19001231\r\nEND:VCARD\r\n", 'DEATHDATE', '-death', false],
- ['💍 12345 (1900)', '19701231', 'ANNIVERSARY', '0', '1900', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nANNIVERSARY:19001231\r\nEND:VCARD\r\n", 'ANNIVERSARY', '-anniversary', true],
- ['12345 (⚭1900)', '19701231', 'ANNIVERSARY', '0', '1900', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nANNIVERSARY:19001231\r\nEND:VCARD\r\n", 'ANNIVERSARY', '-anniversary', false],
- ['🎂 12345', '19701231', 'BDAY', '1', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:--1231\r\nEND:VCARD\r\n", 'BDAY', '', true],
- ['🎂 12345', '19701231', 'BDAY', '1', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY;X-APPLE-OMIT-YEAR=1604:16041231\r\nEND:VCARD\r\n", 'BDAY', '', true],
- [null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:;VALUE=text:circa 1800\r\nEND:VCARD\r\n", 'BDAY', '', true],
- [null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nN:12345;;;;\r\nBDAY:20031231\r\nEND:VCARD\r\n", 'BDAY', '', true],
- ['🎂 12345 (900)', '19701231', 'BDAY', '0', '900', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:09001231\r\nEND:VCARD\r\n", 'BDAY', '', true],
- ['12345 (*1900)', '19700101', 'BDAY', '0', '1900', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19000101\r\nEND:VCARD\r\n", 'BDAY', '', false],
- ['12345 (*1900)', '19701231', 'BDAY', '0', '1900', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19001231\r\nEND:VCARD\r\n", 'BDAY', '', false],
- ['12345 *', '19701231', 'BDAY', '1', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:--1231\r\nEND:VCARD\r\n", 'BDAY', '', false],
- ['12345 *', '19701231', 'BDAY', '1', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY;X-APPLE-OMIT-YEAR=1604:16041231\r\nEND:VCARD\r\n", 'BDAY', '', false],
- [null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:;VALUE=text:circa 1800\r\nEND:VCARD\r\n", 'BDAY', '', false],
- [null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nN:12345;;;;\r\nBDAY:20031231\r\nEND:VCARD\r\n", 'BDAY', '', false],
- ['12345 (*900)', '19701231', 'BDAY', '0', '900', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:09001231\r\nEND:VCARD\r\n", 'BDAY', '', false],
+ // $expectedSummary, $expectedDTStart, $expectedRrule, $expectedFieldType, $expectedUnknownYear, $expectedOriginalYear, $expectedReminder, $data, $fieldType, $prefix, $supports4Byte, $configuredReminder
+ [null, null, null, null, null, null, null, 'yasfewf', '', '', true, null],
+ [null, null, null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
+ [null, null, null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
+ [null, null, null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:someday\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
+ ['🎂 12345 (1900)', '19700101', 'FREQ=YEARLY', 'BDAY', '0', '1900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19000101\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
+ ['🎂 12345 (1900)', '19701231', 'FREQ=YEARLY', 'BDAY', '0', '1900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19001231\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
+ ['Death of 12345 (1900)', '19701231', 'FREQ=YEARLY', 'DEATHDATE', '0', '1900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nDEATHDATE:19001231\r\nEND:VCARD\r\n", 'DEATHDATE', '-death', true, null],
+ ['Death of 12345 (1900)', '19701231', 'FREQ=YEARLY', 'DEATHDATE', '0', '1900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nDEATHDATE:19001231\r\nEND:VCARD\r\n", 'DEATHDATE', '-death', false, null],
+ ['💍 12345 (1900)', '19701231', 'FREQ=YEARLY', 'ANNIVERSARY', '0', '1900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nANNIVERSARY:19001231\r\nEND:VCARD\r\n", 'ANNIVERSARY', '-anniversary', true, null],
+ ['12345 (⚭1900)', '19701231', 'FREQ=YEARLY', 'ANNIVERSARY', '0', '1900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nANNIVERSARY:19001231\r\nEND:VCARD\r\n", 'ANNIVERSARY', '-anniversary', false, null],
+ ['🎂 12345', '19701231', 'FREQ=YEARLY', 'BDAY', '1', null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:--1231\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
+ ['🎂 12345', '19701231', 'FREQ=YEARLY', 'BDAY', '1', null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY;X-APPLE-OMIT-YEAR=1604:16041231\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
+ [null, null, null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:;VALUE=text:circa 1800\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
+ [null, null, null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nN:12345;;;;\r\nBDAY:20031231\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
+ ['🎂 12345 (900)', '19701231', 'FREQ=YEARLY', 'BDAY', '0', '900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:09001231\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
+ ['12345 (*1900)', '19700101', 'FREQ=YEARLY', 'BDAY', '0', '1900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19000101\r\nEND:VCARD\r\n", 'BDAY', '', false, null],
+ ['12345 (*1900)', '19701231', 'FREQ=YEARLY', 'BDAY', '0', '1900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19001231\r\nEND:VCARD\r\n", 'BDAY', '', false, null],
+ ['12345 *', '19701231', 'FREQ=YEARLY', 'BDAY', '1', null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:--1231\r\nEND:VCARD\r\n", 'BDAY', '', false, null],
+ ['12345 *', '19701231', 'FREQ=YEARLY', 'BDAY', '1', null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY;X-APPLE-OMIT-YEAR=1604:16041231\r\nEND:VCARD\r\n", 'BDAY', '', false, null],
+ [null, null, null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:;VALUE=text:circa 1800\r\nEND:VCARD\r\n", 'BDAY', '', false, null],
+ [null, null, null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nN:12345;;;;\r\nBDAY:20031231\r\nEND:VCARD\r\n", 'BDAY', '', false, null],
+ ['12345 (*900)', '19701231', 'FREQ=YEARLY', 'BDAY', '0', '900', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:09001231\r\nEND:VCARD\r\n", 'BDAY', '', false, null],
+ ['12345 (*1900)', '19701231', 'FREQ=YEARLY', 'BDAY', '0', '1900', 'PT9H', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19001231\r\nEND:VCARD\r\n", 'BDAY', '', false, 'PT9H'],
+ ['12345 (*1900)', '19701231', 'FREQ=YEARLY', 'BDAY', '0', '1900', '-PT15H', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19001231\r\nEND:VCARD\r\n", 'BDAY', '', false, '-PT15H'],
+ ['12345 (*1900)', '19701231', 'FREQ=YEARLY', 'BDAY', '0', '1900', '-P6DT15H', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19001231\r\nEND:VCARD\r\n", 'BDAY', '', false, '-P6DT15H'],
+ [null, null, null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19000101\r\nX-NC-EXCLUDE-FROM-BIRTHDAY-CALENDAR;TYPE=boolean:true\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
+ [null, null, null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nX-NC-EXCLUDE-FROM-BIRTHDAY-CALENDAR;TYPE=boolean:true\r\nDEATHDATE:19001231\r\nEND:VCARD\r\n", 'DEATHDATE', '-death', true, null],
+ [null, null, null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nANNIVERSARY:19001231\r\nX-NC-EXCLUDE-FROM-BIRTHDAY-CALENDAR;TYPE=boolean:true\r\nEND:VCARD\r\n", 'ANNIVERSARY', '-anniversary', true, null],
+ ['🎂 12345 (1902)', '19720229', 'FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=-1', 'BDAY', '0', null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19020229\r\nEND:VCARD\r\n", 'BDAY', '', true, null],
];
}
}
diff --git a/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php b/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php
index a8c7a781724..c5eafa0764a 100644
--- a/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php
+++ b/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php
@@ -1,59 +1,42 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Arne Hamann <kontakt+github@arne.email>
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Bjoern Schiessle <bjoern@schiessle.org>
- * @author Björn Schießle <bjoern@schiessle.org>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Georg Ehrke <oc.list@georgehrke.com>
- * @author Joas Schilling <coding@schilljs.com>
- * @author John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Robin Appelman <robin@icewind.nl>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
-
namespace OCA\DAV\Tests\unit\CardDAV;
+use OC\KnownUser\KnownUserService;
use OCA\DAV\CalDAV\Proxy\ProxyMapper;
use OCA\DAV\CardDAV\AddressBook;
use OCA\DAV\CardDAV\CardDavBackend;
+use OCA\DAV\CardDAV\Sharing\Backend;
+use OCA\DAV\CardDAV\Sharing\Service;
use OCA\DAV\Connector\Sabre\Principal;
+use OCA\DAV\DAV\Sharing\SharingMapper;
+use OCP\Accounts\IAccountManager;
use OCP\App\IAppManager;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\EventDispatcher\IEventDispatcher;
+use OCP\ICacheFactory;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IGroupManager;
use OCP\IL10N;
use OCP\IUserManager;
use OCP\IUserSession;
+use OCP\L10N\IFactory;
+use OCP\Server;
use OCP\Share\IManager as ShareManager;
+use PHPUnit\Framework\MockObject\MockObject;
+use Psr\Log\LoggerInterface;
use Sabre\DAV\Exception\BadRequest;
use Sabre\DAV\PropPatch;
use Sabre\VObject\Component\VCard;
use Sabre\VObject\Property\Text;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
-use Symfony\Component\EventDispatcher\GenericEvent;
use Test\TestCase;
+use function time;
/**
* Class CardDavBackendTest
@@ -63,85 +46,72 @@ use Test\TestCase;
* @package OCA\DAV\Tests\unit\CardDAV
*/
class CardDavBackendTest extends TestCase {
-
- /** @var CardDavBackend */
- private $backend;
-
- /** @var Principal | \PHPUnit\Framework\MockObject\MockObject */
- private $principal;
-
- /** @var IUserManager|\PHPUnit\Framework\MockObject\MockObject */
- private $userManager;
-
- /** @var IGroupManager|\PHPUnit\Framework\MockObject\MockObject */
- private $groupManager;
-
- /** @var EventDispatcherInterface|\PHPUnit\Framework\MockObject\MockObject */
- private $legacyDispatcher;
-
- /** @var IEventDispatcher|\PHPUnit\Framework\MockObject\MockObject */
- private $dispatcher;
-
- /** @var IDBConnection */
- private $db;
-
- /** @var string */
- private $dbCardsTable = 'cards';
-
- /** @var string */
- private $dbCardsPropertiesTable = 'cards_properties';
+ private Principal&MockObject $principal;
+ private IUserManager&MockObject $userManager;
+ private IGroupManager&MockObject $groupManager;
+ private IEventDispatcher&MockObject $dispatcher;
+ private IConfig&MockObject $config;
+ private Backend $sharingBackend;
+ private IDBConnection $db;
+ private CardDavBackend $backend;
+ private string $dbCardsTable = 'cards';
+ private string $dbCardsPropertiesTable = 'cards_properties';
public const UNIT_TEST_USER = 'principals/users/carddav-unit-test';
public const UNIT_TEST_USER1 = 'principals/users/carddav-unit-test1';
public const UNIT_TEST_GROUP = 'principals/groups/carddav-unit-test-group';
- private $vcardTest0 = 'BEGIN:VCARD'.PHP_EOL.
- 'VERSION:3.0'.PHP_EOL.
- 'PRODID:-//Sabre//Sabre VObject 4.1.2//EN'.PHP_EOL.
- 'UID:Test'.PHP_EOL.
- 'FN:Test'.PHP_EOL.
- 'N:Test;;;;'.PHP_EOL.
- 'END:VCARD';
-
- private $vcardTest1 = 'BEGIN:VCARD'.PHP_EOL.
- 'VERSION:3.0'.PHP_EOL.
- 'PRODID:-//Sabre//Sabre VObject 4.1.2//EN'.PHP_EOL.
- 'UID:Test2'.PHP_EOL.
- 'FN:Test2'.PHP_EOL.
- 'N:Test2;;;;'.PHP_EOL.
- 'END:VCARD';
-
- private $vcardTest2 = 'BEGIN:VCARD'.PHP_EOL.
- 'VERSION:3.0'.PHP_EOL.
- 'PRODID:-//Sabre//Sabre VObject 4.1.2//EN'.PHP_EOL.
- 'UID:Test3'.PHP_EOL.
- 'FN:Test3'.PHP_EOL.
- 'N:Test3;;;;'.PHP_EOL.
- 'END:VCARD';
-
- private $vcardTestNoUID = 'BEGIN:VCARD'.PHP_EOL.
- 'VERSION:3.0'.PHP_EOL.
- 'PRODID:-//Sabre//Sabre VObject 4.1.2//EN'.PHP_EOL.
- 'FN:TestNoUID'.PHP_EOL.
- 'N:TestNoUID;;;;'.PHP_EOL.
- 'END:VCARD';
+ private $vcardTest0 = 'BEGIN:VCARD' . PHP_EOL
+ . 'VERSION:3.0' . PHP_EOL
+ . 'PRODID:-//Sabre//Sabre VObject 4.1.2//EN' . PHP_EOL
+ . 'UID:Test' . PHP_EOL
+ . 'FN:Test' . PHP_EOL
+ . 'N:Test;;;;' . PHP_EOL
+ . 'END:VCARD';
+
+ private $vcardTest1 = 'BEGIN:VCARD' . PHP_EOL
+ . 'VERSION:3.0' . PHP_EOL
+ . 'PRODID:-//Sabre//Sabre VObject 4.1.2//EN' . PHP_EOL
+ . 'UID:Test2' . PHP_EOL
+ . 'FN:Test2' . PHP_EOL
+ . 'N:Test2;;;;' . PHP_EOL
+ . 'END:VCARD';
+
+ private $vcardTest2 = 'BEGIN:VCARD' . PHP_EOL
+ . 'VERSION:3.0' . PHP_EOL
+ . 'PRODID:-//Sabre//Sabre VObject 4.1.2//EN' . PHP_EOL
+ . 'UID:Test3' . PHP_EOL
+ . 'FN:Test3' . PHP_EOL
+ . 'N:Test3;;;;' . PHP_EOL
+ . 'END:VCARD';
+
+ private $vcardTestNoUID = 'BEGIN:VCARD' . PHP_EOL
+ . 'VERSION:3.0' . PHP_EOL
+ . 'PRODID:-//Sabre//Sabre VObject 4.1.2//EN' . PHP_EOL
+ . 'FN:TestNoUID' . PHP_EOL
+ . 'N:TestNoUID;;;;' . PHP_EOL
+ . 'END:VCARD';
protected function setUp(): void {
parent::setUp();
$this->userManager = $this->createMock(IUserManager::class);
$this->groupManager = $this->createMock(IGroupManager::class);
+ $this->config = $this->createMock(IConfig::class);
$this->principal = $this->getMockBuilder(Principal::class)
->setConstructorArgs([
$this->userManager,
$this->groupManager,
+ $this->createMock(IAccountManager::class),
$this->createMock(ShareManager::class),
$this->createMock(IUserSession::class),
$this->createMock(IAppManager::class),
$this->createMock(ProxyMapper::class),
- $this->createMock(IConfig::class),
+ $this->createMock(KnownUserService::class),
+ $this->config,
+ $this->createMock(IFactory::class)
])
- ->setMethods(['getPrincipalByPath', 'getGroupMembership'])
+ ->onlyMethods(['getPrincipalByPath', 'getGroupMembership', 'findByUri'])
->getMock();
$this->principal->method('getPrincipalByPath')
->willReturn([
@@ -152,23 +122,39 @@ class CardDavBackendTest extends TestCase {
->withAnyParameters()
->willReturn([self::UNIT_TEST_GROUP]);
$this->dispatcher = $this->createMock(IEventDispatcher::class);
- $this->legacyDispatcher = $this->createMock(EventDispatcherInterface::class);
-
- $this->db = \OC::$server->getDatabaseConnection();
- $this->backend = new CardDavBackend($this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher, $this->legacyDispatcher);
+ $this->db = Server::get(IDBConnection::class);
+ $this->sharingBackend = new Backend($this->userManager,
+ $this->groupManager,
+ $this->principal,
+ $this->createMock(ICacheFactory::class),
+ new Service(new SharingMapper($this->db)),
+ $this->createMock(LoggerInterface::class)
+ );
+
+ $this->backend = new CardDavBackend($this->db,
+ $this->principal,
+ $this->userManager,
+ $this->dispatcher,
+ $this->sharingBackend,
+ $this->config,
+ );
// start every test with a empty cards_properties and cards table
$query = $this->db->getQueryBuilder();
- $query->delete('cards_properties')->execute();
+ $query->delete('cards_properties')->executeStatement();
$query = $this->db->getQueryBuilder();
- $query->delete('cards')->execute();
+ $query->delete('cards')->executeStatement();
- $this->tearDown();
+ $this->principal->method('getGroupMembership')
+ ->withAnyParameters()
+ ->willReturn([self::UNIT_TEST_GROUP]);
+ $books = $this->backend->getAddressBooksForUser(self::UNIT_TEST_USER);
+ foreach ($books as $book) {
+ $this->backend->deleteAddressBook($book['id']);
+ }
}
protected function tearDown(): void {
- parent::tearDown();
-
if (is_null($this->backend)) {
return;
}
@@ -180,10 +166,11 @@ class CardDavBackendTest extends TestCase {
foreach ($books as $book) {
$this->backend->deleteAddressBook($book['id']);
}
- }
- public function testAddressBookOperations() {
+ parent::tearDown();
+ }
+ public function testAddressBookOperations(): void {
// create a new address book
$this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
@@ -193,7 +180,7 @@ class CardDavBackendTest extends TestCase {
$this->assertEquals('Example', $books[0]['{DAV:}displayname']);
$this->assertEquals('User\'s displayname', $books[0]['{http://nextcloud.com/ns}owner-displayname']);
- // update it's display name
+ // update its display name
$patch = new PropPatch([
'{DAV:}displayname' => 'Unit test',
'{urn:ietf:params:xml:ns:carddav}addressbook-description' => 'Addressbook used for unit testing'
@@ -211,14 +198,16 @@ class CardDavBackendTest extends TestCase {
$this->assertEquals(0, count($books));
}
- public function testAddressBookSharing() {
+ public function testAddressBookSharing(): void {
$this->userManager->expects($this->any())
->method('userExists')
->willReturn(true);
-
$this->groupManager->expects($this->any())
->method('groupExists')
->willReturn(true);
+ $this->principal->expects(self::atLeastOnce())
+ ->method('findByUri')
+ ->willReturnOnConsecutiveCalls(self::UNIT_TEST_USER1, self::UNIT_TEST_GROUP);
$this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
$books = $this->backend->getAddressBooksForUser(self::UNIT_TEST_USER);
@@ -242,12 +231,12 @@ class CardDavBackendTest extends TestCase {
$this->assertEquals(0, count($books));
}
- public function testCardOperations() {
-
- /** @var CardDavBackend | \PHPUnit\Framework\MockObject\MockObject $backend */
+ public function testCardOperations(): void {
+ /** @var CardDavBackend&MockObject $backend */
$backend = $this->getMockBuilder(CardDavBackend::class)
- ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher, $this->legacyDispatcher])
- ->setMethods(['updateProperties', 'purgeProperties'])->getMock();
+ ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend,$this->config])
+ ->onlyMethods(['updateProperties', 'purgeProperties'])
+ ->getMock();
// create a new address book
$backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
@@ -257,17 +246,21 @@ class CardDavBackendTest extends TestCase {
$uri = $this->getUniqueID('card');
// updateProperties is expected twice, once for createCard and once for updateCard
- $backend->expects($this->at(0))->method('updateProperties')->with($bookId, $uri, $this->vcardTest0);
- $backend->expects($this->at(1))->method('updateProperties')->with($bookId, $uri, $this->vcardTest1);
+ $calls = [
+ [$bookId, $uri, $this->vcardTest0],
+ [$bookId, $uri, $this->vcardTest1],
+ ];
+ $backend->expects($this->exactly(count($calls)))
+ ->method('updateProperties')
+ ->willReturnCallback(function () use (&$calls): void {
+ $expected = array_shift($calls);
+ $this->assertEquals($expected, func_get_args());
+ });
// Expect event
- $this->legacyDispatcher->expects($this->at(0))
- ->method('dispatch')
- ->with('\OCA\DAV\CardDAV\CardDavBackend::createCard', $this->callback(function (GenericEvent $e) use ($bookId, $uri) {
- return $e->getArgument('addressBookId') === $bookId &&
- $e->getArgument('cardUri') === $uri &&
- $e->getArgument('cardData') === $this->vcardTest0;
- }));
+ $this->dispatcher
+ ->expects($this->exactly(3))
+ ->method('dispatchTyped');
// create a card
$backend->createCard($bookId, $uri, $this->vcardTest0);
@@ -287,28 +280,11 @@ class CardDavBackendTest extends TestCase {
$this->assertArrayHasKey('size', $card);
$this->assertEquals($this->vcardTest0, $card['carddata']);
- // Expect event
- $this->legacyDispatcher->expects($this->at(0))
- ->method('dispatch')
- ->with('\OCA\DAV\CardDAV\CardDavBackend::updateCard', $this->callback(function (GenericEvent $e) use ($bookId, $uri) {
- return $e->getArgument('addressBookId') === $bookId &&
- $e->getArgument('cardUri') === $uri &&
- $e->getArgument('cardData') === $this->vcardTest1;
- }));
-
// update the card
$backend->updateCard($bookId, $uri, $this->vcardTest1);
$card = $backend->getCard($bookId, $uri);
$this->assertEquals($this->vcardTest1, $card['carddata']);
- // Expect event
- $this->legacyDispatcher->expects($this->at(0))
- ->method('dispatch')
- ->with('\OCA\DAV\CardDAV\CardDavBackend::deleteCard', $this->callback(function (GenericEvent $e) use ($bookId, $uri) {
- return $e->getArgument('addressBookId') === $bookId &&
- $e->getArgument('cardUri') === $uri;
- }));
-
// delete the card
$backend->expects($this->once())->method('purgeProperties')->with($bookId, $card['id']);
$backend->deleteCard($bookId, $uri);
@@ -316,10 +292,11 @@ class CardDavBackendTest extends TestCase {
$this->assertEquals(0, count($cards));
}
- public function testMultiCard() {
+ public function testMultiCard(): void {
$this->backend = $this->getMockBuilder(CardDavBackend::class)
- ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher, $this->legacyDispatcher])
- ->setMethods(['updateProperties'])->getMock();
+ ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend,$this->config])
+ ->onlyMethods(['updateProperties'])
+ ->getMock();
// create a new address book
$this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
@@ -358,7 +335,7 @@ class CardDavBackendTest extends TestCase {
$this->assertArrayHasKey('lastmodified', $card);
$this->assertArrayHasKey('etag', $card);
$this->assertArrayHasKey('size', $card);
- $this->assertEquals($this->{ 'vcardTest'.($index + 1) }, $card['carddata']);
+ $this->assertEquals($this->{ 'vcardTest' . ($index + 1) }, $card['carddata']);
}
// delete the card
@@ -369,10 +346,11 @@ class CardDavBackendTest extends TestCase {
$this->assertEquals(0, count($cards));
}
- public function testMultipleUIDOnDifferentAddressbooks() {
+ public function testMultipleUIDOnDifferentAddressbooks(): void {
$this->backend = $this->getMockBuilder(CardDavBackend::class)
- ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher, $this->legacyDispatcher])
- ->setMethods(['updateProperties'])->getMock();
+ ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend,$this->config])
+ ->onlyMethods(['updateProperties'])
+ ->getMock();
// create 2 new address books
$this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
@@ -391,10 +369,11 @@ class CardDavBackendTest extends TestCase {
$this->backend->createCard($bookId1, $uri1, $this->vcardTest0);
}
- public function testMultipleUIDDenied() {
+ public function testMultipleUIDDenied(): void {
$this->backend = $this->getMockBuilder(CardDavBackend::class)
- ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher, $this->legacyDispatcher])
- ->setMethods(['updateProperties'])->getMock();
+ ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend, $this->config])
+ ->onlyMethods(['updateProperties'])
+ ->getMock();
// create a new address book
$this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
@@ -412,10 +391,11 @@ class CardDavBackendTest extends TestCase {
$test = $this->backend->createCard($bookId, $uri1, $this->vcardTest0);
}
- public function testNoValidUID() {
+ public function testNoValidUID(): void {
$this->backend = $this->getMockBuilder(CardDavBackend::class)
- ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher, $this->legacyDispatcher])
- ->setMethods(['updateProperties'])->getMock();
+ ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend, $this->config])
+ ->onlyMethods(['updateProperties'])
+ ->getMock();
// create a new address book
$this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
@@ -429,10 +409,10 @@ class CardDavBackendTest extends TestCase {
$test = $this->backend->createCard($bookId, $uri1, $this->vcardTestNoUID);
}
- public function testDeleteWithoutCard() {
+ public function testDeleteWithoutCard(): void {
$this->backend = $this->getMockBuilder(CardDavBackend::class)
- ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher, $this->legacyDispatcher])
- ->setMethods([
+ ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend, $this->config])
+ ->onlyMethods([
'getCardId',
'addChange',
'purgeProperties',
@@ -453,12 +433,17 @@ class CardDavBackendTest extends TestCase {
->method('getCardId')
->with($bookId, $uri)
->willThrowException(new \InvalidArgumentException());
- $this->backend->expects($this->exactly(2))
+
+ $calls = [
+ [$bookId, $uri, 1],
+ [$bookId, $uri, 3],
+ ];
+ $this->backend->expects($this->exactly(count($calls)))
->method('addChange')
- ->withConsecutive(
- [$bookId, $uri, 1],
- [$bookId, $uri, 3]
- );
+ ->willReturnCallback(function () use (&$calls): void {
+ $expected = array_shift($calls);
+ $this->assertEquals($expected, func_get_args());
+ });
$this->backend->expects($this->never())
->method('purgeProperties');
@@ -469,10 +454,11 @@ class CardDavBackendTest extends TestCase {
$this->assertTrue($this->backend->deleteCard($bookId, $uri));
}
- public function testSyncSupport() {
+ public function testSyncSupport(): void {
$this->backend = $this->getMockBuilder(CardDavBackend::class)
- ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher, $this->legacyDispatcher])
- ->setMethods(['updateProperties'])->getMock();
+ ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend, $this->config])
+ ->onlyMethods(['updateProperties'])
+ ->getMock();
// create a new address book
$this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
@@ -493,14 +479,16 @@ class CardDavBackendTest extends TestCase {
$this->assertEquals($uri0, $changes['added'][0]);
}
- public function testSharing() {
+ public function testSharing(): void {
$this->userManager->expects($this->any())
->method('userExists')
->willReturn(true);
-
$this->groupManager->expects($this->any())
->method('groupExists')
->willReturn(true);
+ $this->principal->expects(self::any())
+ ->method('findByUri')
+ ->willReturn(self::UNIT_TEST_USER1);
$this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
$books = $this->backend->getAddressBooksForUser(self::UNIT_TEST_USER);
@@ -531,14 +519,14 @@ class CardDavBackendTest extends TestCase {
$this->assertEquals(0, count($books));
}
- public function testUpdateProperties() {
+ public function testUpdateProperties(): void {
$bookId = 42;
$cardUri = 'card-uri';
$cardId = 2;
$backend = $this->getMockBuilder(CardDavBackend::class)
- ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher, $this->legacyDispatcher])
- ->setMethods(['getCardId'])->getMock();
+ ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend, $this->config])
+ ->onlyMethods(['getCardId'])->getMock();
$backend->expects($this->any())->method('getCardId')->willReturn($cardId);
@@ -550,7 +538,8 @@ class CardDavBackendTest extends TestCase {
$query = $this->db->getQueryBuilder();
$query->select('*')
- ->from('cards_properties');
+ ->from('cards_properties')
+ ->orderBy('name');
$qResult = $query->execute();
$result = $qResult->fetchAll();
@@ -558,13 +547,13 @@ class CardDavBackendTest extends TestCase {
$this->assertSame(2, count($result));
- $this->assertSame('UID', $result[0]['name']);
- $this->assertSame($cardUri, $result[0]['value']);
+ $this->assertSame('FN', $result[0]['name']);
+ $this->assertSame('John Doe', $result[0]['value']);
$this->assertSame($bookId, (int)$result[0]['addressbookid']);
$this->assertSame($cardId, (int)$result[0]['cardid']);
- $this->assertSame('FN', $result[1]['name']);
- $this->assertSame('John Doe', $result[1]['value']);
+ $this->assertSame('UID', $result[1]['name']);
+ $this->assertSame($cardUri, $result[1]['value']);
$this->assertSame($bookId, (int)$result[1]['addressbookid']);
$this->assertSame($cardId, (int)$result[1]['cardid']);
@@ -589,7 +578,7 @@ class CardDavBackendTest extends TestCase {
$this->assertSame($cardId, (int)$result[0]['cardid']);
}
- public function testPurgeProperties() {
+ public function testPurgeProperties(): void {
$query = $this->db->getQueryBuilder();
$query->insert('cards_properties')
->values(
@@ -627,11 +616,11 @@ class CardDavBackendTest extends TestCase {
$qResult->closeCursor();
$this->assertSame(1, count($result));
- $this->assertSame(1 ,(int)$result[0]['addressbookid']);
- $this->assertSame(2 ,(int)$result[0]['cardid']);
+ $this->assertSame(1, (int)$result[0]['addressbookid']);
+ $this->assertSame(2, (int)$result[0]['cardid']);
}
- public function testGetCardId() {
+ public function testGetCardId(): void {
$query = $this->db->getQueryBuilder();
$query->insert('cards')
@@ -653,21 +642,14 @@ class CardDavBackendTest extends TestCase {
}
- public function testGetCardIdFailed() {
+ public function testGetCardIdFailed(): void {
$this->expectException(\InvalidArgumentException::class);
$this->invokePrivate($this->backend, 'getCardId', [1, 'uri']);
}
- /**
- * @dataProvider dataTestSearch
- *
- * @param string $pattern
- * @param array $properties
- * @param array $options
- * @param array $expected
- */
- public function testSearch($pattern, $properties, $options, $expected) {
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTestSearch')]
+ public function testSearch(string $pattern, array $properties, array $options, array $expected): void {
/** @var VCard $vCards */
$vCards = [];
$vCards[0] = new VCard();
@@ -686,20 +668,21 @@ class CardDavBackendTest extends TestCase {
$query = $this->db->getQueryBuilder();
for ($i = 0; $i < 3; $i++) {
$query->insert($this->dbCardsTable)
- ->values(
- [
- 'addressbookid' => $query->createNamedParameter(0),
- 'carddata' => $query->createNamedParameter($vCards[$i]->serialize(), IQueryBuilder::PARAM_LOB),
- 'uri' => $query->createNamedParameter('uri' . $i),
- 'lastmodified' => $query->createNamedParameter(time()),
- 'etag' => $query->createNamedParameter('etag' . $i),
- 'size' => $query->createNamedParameter(120),
- ]
- );
+ ->values(
+ [
+ 'addressbookid' => $query->createNamedParameter(0),
+ 'carddata' => $query->createNamedParameter($vCards[$i]->serialize(), IQueryBuilder::PARAM_LOB),
+ 'uri' => $query->createNamedParameter('uri' . $i),
+ 'lastmodified' => $query->createNamedParameter(time()),
+ 'etag' => $query->createNamedParameter('etag' . $i),
+ 'size' => $query->createNamedParameter(120),
+ ]
+ );
$query->execute();
$vCardIds[] = $query->getLastInsertId();
}
+ $query = $this->db->getQueryBuilder();
$query->insert($this->dbCardsPropertiesTable)
->values(
[
@@ -711,6 +694,7 @@ class CardDavBackendTest extends TestCase {
]
);
$query->execute();
+ $query = $this->db->getQueryBuilder();
$query->insert($this->dbCardsPropertiesTable)
->values(
[
@@ -722,6 +706,7 @@ class CardDavBackendTest extends TestCase {
]
);
$query->execute();
+ $query = $this->db->getQueryBuilder();
$query->insert($this->dbCardsPropertiesTable)
->values(
[
@@ -733,6 +718,7 @@ class CardDavBackendTest extends TestCase {
]
);
$query->execute();
+ $query = $this->db->getQueryBuilder();
$query->insert($this->dbCardsPropertiesTable)
->values(
[
@@ -744,6 +730,7 @@ class CardDavBackendTest extends TestCase {
]
);
$query->execute();
+ $query = $this->db->getQueryBuilder();
$query->insert($this->dbCardsPropertiesTable)
->values(
[
@@ -773,7 +760,7 @@ class CardDavBackendTest extends TestCase {
$this->assertSame(count($expected), count($found));
}
- public function dataTestSearch() {
+ public static function dataTestSearch(): array {
return [
['John', ['FN'], [], [['uri0', 'John Doe'], ['uri1', 'John M. Doe']]],
['M. Doe', ['FN'], [], [['uri1', 'John M. Doe']]],
@@ -787,19 +774,19 @@ class CardDavBackendTest extends TestCase {
];
}
- public function testGetCardUri() {
+ public function testGetCardUri(): void {
$query = $this->db->getQueryBuilder();
$query->insert($this->dbCardsTable)
- ->values(
- [
- 'addressbookid' => $query->createNamedParameter(1),
- 'carddata' => $query->createNamedParameter('carddata', IQueryBuilder::PARAM_LOB),
- 'uri' => $query->createNamedParameter('uri'),
- 'lastmodified' => $query->createNamedParameter(5489543),
- 'etag' => $query->createNamedParameter('etag'),
- 'size' => $query->createNamedParameter(120),
- ]
- );
+ ->values(
+ [
+ 'addressbookid' => $query->createNamedParameter(1),
+ 'carddata' => $query->createNamedParameter('carddata', IQueryBuilder::PARAM_LOB),
+ 'uri' => $query->createNamedParameter('uri'),
+ 'lastmodified' => $query->createNamedParameter(5489543),
+ 'etag' => $query->createNamedParameter('etag'),
+ 'size' => $query->createNamedParameter(120),
+ ]
+ );
$query->execute();
$id = $query->getLastInsertId();
@@ -808,26 +795,26 @@ class CardDavBackendTest extends TestCase {
}
- public function testGetCardUriFailed() {
+ public function testGetCardUriFailed(): void {
$this->expectException(\InvalidArgumentException::class);
$this->backend->getCardUri(1);
}
- public function testGetContact() {
+ public function testGetContact(): void {
$query = $this->db->getQueryBuilder();
for ($i = 0; $i < 2; $i++) {
$query->insert($this->dbCardsTable)
- ->values(
- [
- 'addressbookid' => $query->createNamedParameter($i),
- 'carddata' => $query->createNamedParameter('carddata' . $i, IQueryBuilder::PARAM_LOB),
- 'uri' => $query->createNamedParameter('uri' . $i),
- 'lastmodified' => $query->createNamedParameter(5489543),
- 'etag' => $query->createNamedParameter('etag' . $i),
- 'size' => $query->createNamedParameter(120),
- ]
- );
+ ->values(
+ [
+ 'addressbookid' => $query->createNamedParameter($i),
+ 'carddata' => $query->createNamedParameter('carddata' . $i, IQueryBuilder::PARAM_LOB),
+ 'uri' => $query->createNamedParameter('uri' . $i),
+ 'lastmodified' => $query->createNamedParameter(5489543),
+ 'etag' => $query->createNamedParameter('etag' . $i),
+ 'size' => $query->createNamedParameter(120),
+ ]
+ );
$query->execute();
}
@@ -845,11 +832,11 @@ class CardDavBackendTest extends TestCase {
$this->assertEmpty($result);
}
- public function testGetContactFail() {
+ public function testGetContactFail(): void {
$this->assertEmpty($this->backend->getContact(0, 'uri'));
}
- public function testCollectCardProperties() {
+ public function testCollectCardProperties(): void {
$query = $this->db->getQueryBuilder();
$query->insert($this->dbCardsPropertiesTable)
->values(
@@ -861,9 +848,71 @@ class CardDavBackendTest extends TestCase {
'preferred' => $query->createNamedParameter(0)
]
)
- ->execute();
+ ->execute();
$result = $this->backend->collectCardProperties(666, 'FN');
$this->assertEquals(['John Doe'], $result);
}
+
+ /**
+ * @throws \OCP\DB\Exception
+ * @throws \Sabre\DAV\Exception\BadRequest
+ */
+ public function testPruneOutdatedSyncTokens(): void {
+ $addressBookId = $this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
+ $changes = $this->backend->getChangesForAddressBook($addressBookId, '', 1);
+ $syncToken = $changes['syncToken'];
+
+ $uri = $this->getUniqueID('card');
+ $this->backend->createCard($addressBookId, $uri, $this->vcardTest0);
+ $this->backend->updateCard($addressBookId, $uri, $this->vcardTest1);
+
+ // Do not delete anything if week data as old as ts=0
+ $deleted = $this->backend->pruneOutdatedSyncTokens(0, 0);
+ self::assertSame(0, $deleted);
+
+ $deleted = $this->backend->pruneOutdatedSyncTokens(0, time());
+ // At least one from the object creation and one from the object update
+ $this->assertGreaterThanOrEqual(2, $deleted);
+ $changes = $this->backend->getChangesForAddressBook($addressBookId, $syncToken, 1);
+ $this->assertEmpty($changes['added']);
+ $this->assertEmpty($changes['modified']);
+ $this->assertEmpty($changes['deleted']);
+
+ // Test that objects remain
+
+ // Currently changes are empty
+ $changes = $this->backend->getChangesForAddressBook($addressBookId, $syncToken, 100);
+ $this->assertEquals(0, count($changes['added'] + $changes['modified'] + $changes['deleted']));
+
+ // Create card
+ $uri = $this->getUniqueID('card');
+ $this->backend->createCard($addressBookId, $uri, $this->vcardTest0);
+ // We now have one add
+ $changes = $this->backend->getChangesForAddressBook($addressBookId, $syncToken, 100);
+ $this->assertEquals(1, count($changes['added']));
+ $this->assertEmpty($changes['modified']);
+ $this->assertEmpty($changes['deleted']);
+
+ // Update card
+ $this->backend->updateCard($addressBookId, $uri, $this->vcardTest1);
+ // One add, one modify, but shortened to modify
+ $changes = $this->backend->getChangesForAddressBook($addressBookId, $syncToken, 100);
+ $this->assertEmpty($changes['added']);
+ $this->assertEquals(1, count($changes['modified']));
+ $this->assertEmpty($changes['deleted']);
+
+ // Delete all but last change
+ $deleted = $this->backend->pruneOutdatedSyncTokens(1, time());
+ $this->assertEquals(1, $deleted); // We had two changes before, now one
+
+ // Only update should remain
+ $changes = $this->backend->getChangesForAddressBook($addressBookId, $syncToken, 100);
+ $this->assertEmpty($changes['added']);
+ $this->assertEquals(1, count($changes['modified']));
+ $this->assertEmpty($changes['deleted']);
+
+ // Check that no crash occurs when prune is called without current changes
+ $deleted = $this->backend->pruneOutdatedSyncTokens(1, time());
+ }
}
diff --git a/apps/dav/tests/unit/CardDAV/ContactsManagerTest.php b/apps/dav/tests/unit/CardDAV/ContactsManagerTest.php
index 63c1413eec0..bdd826f671b 100644
--- a/apps/dav/tests/unit/CardDAV/ContactsManagerTest.php
+++ b/apps/dav/tests/unit/CardDAV/ContactsManagerTest.php
@@ -1,52 +1,37 @@
<?php
+
+declare(strict_types=1);
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Georg Ehrke <oc.list@georgehrke.com>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
-
namespace OCA\DAV\Tests\unit\CardDAV;
use OCA\DAV\CardDAV\CardDavBackend;
use OCA\DAV\CardDAV\ContactsManager;
+use OCA\DAV\Db\PropertyMapper;
use OCP\Contacts\IManager;
use OCP\IL10N;
use OCP\IURLGenerator;
+use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;
class ContactsManagerTest extends TestCase {
- public function test() {
- /** @var IManager | \PHPUnit\Framework\MockObject\MockObject $cm */
- $cm = $this->getMockBuilder(IManager::class)->disableOriginalConstructor()->getMock();
+ public function test(): void {
+ /** @var IManager&MockObject $cm */
+ $cm = $this->createMock(IManager::class);
$cm->expects($this->exactly(2))->method('registerAddressBook');
- $urlGenerator = $this->getMockBuilder(IURLGenerator::class)->disableOriginalConstructor()->getMock();
- /** @var CardDavBackend | \PHPUnit\Framework\MockObject\MockObject $backEnd */
- $backEnd = $this->getMockBuilder(CardDavBackend::class)->disableOriginalConstructor()->getMock();
+ $urlGenerator = $this->createMock(IURLGenerator::class);
+ /** @var CardDavBackend&MockObject $backEnd */
+ $backEnd = $this->createMock(CardDavBackend::class);
$backEnd->method('getAddressBooksForUser')->willReturn([
['{DAV:}displayname' => 'Test address book', 'uri' => 'default'],
]);
+ $propertyMapper = $this->createMock(PropertyMapper::class);
$l = $this->createMock(IL10N::class);
- $app = new ContactsManager($backEnd, $l);
+ $app = new ContactsManager($backEnd, $l, $propertyMapper);
$app->setupContactsProvider($cm, 'user01', $urlGenerator);
}
}
diff --git a/apps/dav/tests/unit/CardDAV/ConverterTest.php b/apps/dav/tests/unit/CardDAV/ConverterTest.php
index aef5cf8ef1c..00519b82766 100644
--- a/apps/dav/tests/unit/CardDAV/ConverterTest.php
+++ b/apps/dav/tests/unit/CardDAV/ConverterTest.php
@@ -1,103 +1,90 @@
<?php
+
+declare(strict_types=1);
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Bjoern Schiessle <bjoern@schiessle.org>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
-
namespace OCA\DAV\Tests\unit\CardDAV;
-use OC\Accounts\AccountManager;
use OCA\DAV\CardDAV\Converter;
+use OCP\Accounts\IAccount;
use OCP\Accounts\IAccountManager;
+use OCP\Accounts\IAccountProperty;
use OCP\IImage;
+use OCP\IURLGenerator;
use OCP\IUser;
+use OCP\IUserManager;
+use PHPUnit\Framework\MockObject\MockObject;
+use Psr\Log\LoggerInterface;
use Test\TestCase;
class ConverterTest extends TestCase {
-
- /** @var AccountManager|\PHPUnit\Framework\MockObject\MockObject */
- private $accountManager;
+ private IAccountManager&MockObject $accountManager;
+ private IUserManager&MockObject $userManager;
+ private IURLGenerator&MockObject $urlGenerator;
+ private LoggerInterface&MockObject $logger;
protected function setUp(): void {
parent::setUp();
- $this->accountManager = $this->createMock(AccountManager::class);
+ $this->accountManager = $this->createMock(IAccountManager::class);
+ $this->userManager = $this->createMock(IUserManager::class);
+ $this->urlGenerator = $this->createMock(IURLGenerator::class);
+ $this->logger = $this->createMock(LoggerInterface::class);
+ }
+
+ /**
+ * @return IAccountProperty&MockObject
+ */
+ protected function getAccountPropertyMock(string $name, ?string $value, string $scope) {
+ $property = $this->createMock(IAccountProperty::class);
+ $property->expects($this->any())
+ ->method('getName')
+ ->willReturn($name);
+ $property->expects($this->any())
+ ->method('getValue')
+ ->willReturn((string)$value);
+ $property->expects($this->any())
+ ->method('getScope')
+ ->willReturn($scope);
+ $property->expects($this->any())
+ ->method('getVerified')
+ ->willReturn(IAccountManager::NOT_VERIFIED);
+ return $property;
}
public function getAccountManager(IUser $user) {
- $accountManager = $this->getMockBuilder(AccountManager::class)
- ->disableOriginalConstructor()->getMock();
- $accountManager->expects($this->any())->method('getUser')->willReturn(
- [
- IAccountManager::PROPERTY_DISPLAYNAME =>
- [
- 'value' => $user->getDisplayName(),
- 'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY,
- ],
- IAccountManager::PROPERTY_ADDRESS =>
- [
- 'value' => '',
- 'scope' => AccountManager::VISIBILITY_PRIVATE,
- ],
- IAccountManager::PROPERTY_WEBSITE =>
- [
- 'value' => '',
- 'scope' => AccountManager::VISIBILITY_PRIVATE,
- ],
- IAccountManager::PROPERTY_EMAIL =>
- [
- 'value' => $user->getEMailAddress(),
- 'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY,
- ],
- IAccountManager::PROPERTY_AVATAR =>
- [
- 'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY
- ],
- IAccountManager::PROPERTY_PHONE =>
- [
- 'value' => '',
- 'scope' => AccountManager::VISIBILITY_PRIVATE,
- ],
- IAccountManager::PROPERTY_TWITTER =>
- [
- 'value' => '',
- 'scope' => AccountManager::VISIBILITY_PRIVATE,
- ],
- ]
- );
+ $account = $this->createMock(IAccount::class);
+ $account->expects($this->any())
+ ->method('getAllProperties')
+ ->willReturnCallback(function () use ($user) {
+ yield $this->getAccountPropertyMock(IAccountManager::PROPERTY_DISPLAYNAME, $user->getDisplayName(), IAccountManager::SCOPE_FEDERATED);
+ yield $this->getAccountPropertyMock(IAccountManager::PROPERTY_ADDRESS, '', IAccountManager::SCOPE_LOCAL);
+ yield $this->getAccountPropertyMock(IAccountManager::PROPERTY_WEBSITE, '', IAccountManager::SCOPE_LOCAL);
+ yield $this->getAccountPropertyMock(IAccountManager::PROPERTY_EMAIL, $user->getEMailAddress(), IAccountManager::SCOPE_FEDERATED);
+ yield $this->getAccountPropertyMock(IAccountManager::PROPERTY_AVATAR, $user->getAvatarImage(-1)->data(), IAccountManager::SCOPE_FEDERATED);
+ yield $this->getAccountPropertyMock(IAccountManager::PROPERTY_PHONE, '', IAccountManager::SCOPE_LOCAL);
+ yield $this->getAccountPropertyMock(IAccountManager::PROPERTY_TWITTER, '', IAccountManager::SCOPE_LOCAL);
+ });
+
+ $accountManager = $this->createMock(IAccountManager::class);
+
+ $accountManager->expects($this->any())
+ ->method('getAccount')
+ ->willReturn($account);
return $accountManager;
}
- /**
- * @dataProvider providesNewUsers
- */
- public function testCreation($expectedVCard, $displayName = null, $eMailAddress = null, $cloudId = null) {
- $user = $this->getUserMock($displayName, $eMailAddress, $cloudId);
+ #[\PHPUnit\Framework\Attributes\DataProvider('providesNewUsers')]
+ public function testCreation($expectedVCard, $displayName = null, $eMailAddress = null, $cloudId = null): void {
+ $user = $this->getUserMock((string)$displayName, $eMailAddress, $cloudId);
$accountManager = $this->getAccountManager($user);
- $converter = new Converter($accountManager);
+ $converter = new Converter($accountManager, $this->userManager, $this->urlGenerator, $this->logger);
$vCard = $converter->createCardFromUser($user);
if ($expectedVCard !== null) {
$this->assertInstanceOf('Sabre\VObject\Component\VCard', $vCard);
@@ -108,7 +95,30 @@ class ConverterTest extends TestCase {
}
}
- protected function compareData($expected, $data) {
+ public function testManagerProp(): void {
+ $user = $this->getUserMock('user', 'user@domain.tld', 'user@cloud.domain.tld');
+ $user->method('getManagerUids')
+ ->willReturn(['mgr']);
+ $this->userManager->expects(self::once())
+ ->method('getDisplayName')
+ ->with('mgr')
+ ->willReturn('Manager');
+ $accountManager = $this->getAccountManager($user);
+
+ $converter = new Converter($accountManager, $this->userManager, $this->urlGenerator, $this->logger);
+ $vCard = $converter->createCardFromUser($user);
+
+ $this->compareData(
+ [
+ 'cloud' => 'user@cloud.domain.tld',
+ 'email' => 'user@domain.tld',
+ 'x-managersname' => 'Manager',
+ ],
+ $vCard->jsonSerialize()
+ );
+ }
+
+ protected function compareData(array $expected, array $data): void {
foreach ($expected as $key => $value) {
$found = false;
foreach ($data[1] as $d) {
@@ -123,7 +133,7 @@ class ConverterTest extends TestCase {
}
}
- public function providesNewUsers() {
+ public static function providesNewUsers(): array {
return [
[
null
@@ -150,8 +160,8 @@ class ConverterTest extends TestCase {
'fn' => 'Dr. Foo Bar',
'photo' => 'MTIzNDU2Nzg5',
],
- "Dr. Foo Bar",
- "foo@bar.net",
+ 'Dr. Foo Bar',
+ 'foo@bar.net',
'foo@cloud.net'
],
[
@@ -160,9 +170,9 @@ class ConverterTest extends TestCase {
'fn' => 'Dr. Foo Bar',
'photo' => 'MTIzNDU2Nzg5',
],
- "Dr. Foo Bar",
+ 'Dr. Foo Bar',
null,
- "foo@cloud.net"
+ 'foo@cloud.net'
],
[
[
@@ -177,19 +187,15 @@ class ConverterTest extends TestCase {
];
}
- /**
- * @dataProvider providesNames
- * @param $expected
- * @param $fullName
- */
- public function testNameSplitter($expected, $fullName) {
- $converter = new Converter($this->accountManager);
+ #[\PHPUnit\Framework\Attributes\DataProvider('providesNames')]
+ public function testNameSplitter(string $expected, string $fullName): void {
+ $converter = new Converter($this->accountManager, $this->userManager, $this->urlGenerator, $this->logger);
$r = $converter->splitFullName($fullName);
$r = implode(';', $r);
$this->assertEquals($expected, $r);
}
- public function providesNames() {
+ public static function providesNames(): array {
return [
['Sauron;;;;', 'Sauron'],
['Baggins;Bilbo;;;', 'Bilbo Baggins'],
@@ -198,16 +204,13 @@ class ConverterTest extends TestCase {
}
/**
- * @param $displayName
- * @param $eMailAddress
- * @param $cloudId
- * @return IUser | \PHPUnit\Framework\MockObject\MockObject
+ * @return IUser&MockObject
*/
- protected function getUserMock($displayName, $eMailAddress, $cloudId) {
- $image0 = $this->getMockBuilder(IImage::class)->disableOriginalConstructor()->getMock();
+ protected function getUserMock(string $displayName, ?string $eMailAddress, ?string $cloudId) {
+ $image0 = $this->createMock(IImage::class);
$image0->method('mimeType')->willReturn('image/jpeg');
$image0->method('data')->willReturn('123456789');
- $user = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
+ $user = $this->createMock(IUser::class);
$user->method('getUID')->willReturn('12345');
$user->method('getDisplayName')->willReturn($displayName);
$user->method('getEMailAddress')->willReturn($eMailAddress);
diff --git a/apps/dav/tests/unit/CardDAV/ImageExportPluginTest.php b/apps/dav/tests/unit/CardDAV/ImageExportPluginTest.php
index 47606a45241..d47f53bddcd 100644
--- a/apps/dav/tests/unit/CardDAV/ImageExportPluginTest.php
+++ b/apps/dav/tests/unit/CardDAV/ImageExportPluginTest.php
@@ -1,36 +1,20 @@
<?php
+
+declare(strict_types=1);
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Georg Ehrke <oc.list@georgehrke.com>
- * @author Jacob Neplokh <me@jacobneplokh.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
-
namespace OCA\DAV\Tests\unit\CardDAV;
use OCA\DAV\CardDAV\AddressBook;
use OCA\DAV\CardDAV\ImageExportPlugin;
use OCA\DAV\CardDAV\PhotoCache;
+use OCP\AppFramework\Http;
use OCP\Files\NotFoundException;
use OCP\Files\SimpleFS\ISimpleFile;
+use PHPUnit\Framework\MockObject\MockObject;
use Sabre\CardDAV\Card;
use Sabre\DAV\Node;
use Sabre\DAV\Server;
@@ -40,19 +24,12 @@ use Sabre\HTTP\ResponseInterface;
use Test\TestCase;
class ImageExportPluginTest extends TestCase {
-
- /** @var ResponseInterface|\PHPUnit\Framework\MockObject\MockObject */
- private $response;
- /** @var RequestInterface|\PHPUnit\Framework\MockObject\MockObject */
- private $request;
- /** @var ImageExportPlugin|\PHPUnit\Framework\MockObject\MockObject */
- private $plugin;
- /** @var Server */
- private $server;
- /** @var Tree|\PHPUnit\Framework\MockObject\MockObject */
- private $tree;
- /** @var PhotoCache|\PHPUnit\Framework\MockObject\MockObject */
- private $cache;
+ private ResponseInterface&MockObject $response;
+ private RequestInterface&MockObject $request;
+ private Server&MockObject $server;
+ private Tree&MockObject $tree;
+ private PhotoCache&MockObject $cache;
+ private ImageExportPlugin $plugin;
protected function setUp(): void {
parent::setUp();
@@ -64,24 +41,18 @@ class ImageExportPluginTest extends TestCase {
$this->server->tree = $this->tree;
$this->cache = $this->createMock(PhotoCache::class);
- $this->plugin = $this->getMockBuilder(ImageExportPlugin::class)
- ->setMethods(['getPhoto'])
- ->setConstructorArgs([$this->cache])
- ->getMock();
+ $this->plugin = new ImageExportPlugin($this->cache);
$this->plugin->initialize($this->server);
}
- /**
- * @dataProvider providesQueryParams
- * @param $param
- */
- public function testQueryParams($param) {
+ #[\PHPUnit\Framework\Attributes\DataProvider('providesQueryParams')]
+ public function testQueryParams(array $param): void {
$this->request->expects($this->once())->method('getQueryParameters')->willReturn($param);
$result = $this->plugin->httpGet($this->request, $this->response);
$this->assertTrue($result);
}
- public function providesQueryParams() {
+ public static function providesQueryParams(): array {
return [
[[]],
[['1']],
@@ -89,7 +60,7 @@ class ImageExportPluginTest extends TestCase {
];
}
- public function testNoCard() {
+ public function testNoCard(): void {
$this->request->method('getQueryParameters')
->willReturn([
'photo'
@@ -106,7 +77,7 @@ class ImageExportPluginTest extends TestCase {
$this->assertTrue($result);
}
- public function dataTestCard() {
+ public static function dataTestCard(): array {
return [
[null, false],
[null, true],
@@ -115,13 +86,8 @@ class ImageExportPluginTest extends TestCase {
];
}
- /**
- * @dataProvider dataTestCard
- *
- * @param $size
- * @param bool $photo
- */
- public function testCard($size, $photo) {
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTestCard')]
+ public function testCard(?int $size, bool $photo): void {
$query = ['photo' => null];
if ($size !== null) {
$query['size'] = $size;
@@ -151,16 +117,6 @@ class ImageExportPluginTest extends TestCase {
$this->fail();
});
- $this->response->expects($this->at(0))
- ->method('setHeader')
- ->with('Cache-Control', 'private, max-age=3600, must-revalidate');
- $this->response->expects($this->at(1))
- ->method('setHeader')
- ->with('Etag', '"myEtag"');
- $this->response->expects($this->at(2))
- ->method('setHeader')
- ->with('Pragma', 'public');
-
$size = $size === null ? -1 : $size;
if ($photo) {
@@ -174,12 +130,18 @@ class ImageExportPluginTest extends TestCase {
->with(1, 'card', $size, $card)
->willReturn($file);
- $this->response->expects($this->at(3))
- ->method('setHeader')
- ->with('Content-Type', 'image/jpeg');
- $this->response->expects($this->at(4))
+ $setHeaderCalls = [
+ ['Cache-Control', 'private, max-age=3600, must-revalidate'],
+ ['Etag', '"myEtag"'],
+ ['Content-Type', 'image/jpeg'],
+ ['Content-Disposition', 'attachment; filename=card.jpg'],
+ ];
+ $this->response->expects($this->exactly(count($setHeaderCalls)))
->method('setHeader')
- ->with('Content-Disposition', 'attachment; filename=card.jpg');
+ ->willReturnCallback(function () use (&$setHeaderCalls): void {
+ $expected = array_shift($setHeaderCalls);
+ $this->assertEquals($expected, func_get_args());
+ });
$this->response->expects($this->once())
->method('setStatus')
@@ -188,12 +150,22 @@ class ImageExportPluginTest extends TestCase {
->method('setBody')
->with('imgdata');
} else {
+ $setHeaderCalls = [
+ ['Cache-Control', 'private, max-age=3600, must-revalidate'],
+ ['Etag', '"myEtag"'],
+ ];
+ $this->response->expects($this->exactly(count($setHeaderCalls)))
+ ->method('setHeader')
+ ->willReturnCallback(function () use (&$setHeaderCalls): void {
+ $expected = array_shift($setHeaderCalls);
+ $this->assertEquals($expected, func_get_args());
+ });
$this->cache->method('get')
->with(1, 'card', $size, $card)
->willThrowException(new NotFoundException());
$this->response->expects($this->once())
->method('setStatus')
- ->with(404);
+ ->with(Http::STATUS_NO_CONTENT);
}
$result = $this->plugin->httpGet($this->request, $this->response);
diff --git a/apps/dav/tests/unit/CardDAV/Security/CardDavRateLimitingPluginTest.php b/apps/dav/tests/unit/CardDAV/Security/CardDavRateLimitingPluginTest.php
new file mode 100644
index 00000000000..ee599d5a76c
--- /dev/null
+++ b/apps/dav/tests/unit/CardDAV/Security/CardDavRateLimitingPluginTest.php
@@ -0,0 +1,146 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\DAV\Tests\unit\CardDAV\Security;
+
+use OC\Security\RateLimiting\Exception\RateLimitExceededException;
+use OC\Security\RateLimiting\Limiter;
+use OCA\DAV\CardDAV\CardDavBackend;
+use OCA\DAV\CardDAV\Security\CardDavRateLimitingPlugin;
+use OCA\DAV\Connector\Sabre\Exception\TooManyRequests;
+use OCP\IAppConfig;
+use OCP\IUser;
+use OCP\IUserManager;
+use PHPUnit\Framework\MockObject\MockObject;
+use Psr\Log\LoggerInterface;
+use Sabre\DAV\Exception\Forbidden;
+use Test\TestCase;
+
+class CardDavRateLimitingPluginTest extends TestCase {
+
+ private Limiter&MockObject $limiter;
+ private CardDavBackend&MockObject $cardDavBackend;
+ private IUserManager&MockObject $userManager;
+ private LoggerInterface&MockObject $logger;
+ private IAppConfig&MockObject $config;
+ private string $userId = 'user123';
+ private CardDavRateLimitingPlugin $plugin;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->limiter = $this->createMock(Limiter::class);
+ $this->userManager = $this->createMock(IUserManager::class);
+ $this->cardDavBackend = $this->createMock(CardDavBackend::class);
+ $this->logger = $this->createMock(LoggerInterface::class);
+ $this->config = $this->createMock(IAppConfig::class);
+ $this->plugin = new CardDavRateLimitingPlugin(
+ $this->limiter,
+ $this->userManager,
+ $this->cardDavBackend,
+ $this->logger,
+ $this->config,
+ $this->userId,
+ );
+ }
+
+ public function testNoUserObject(): void {
+ $this->limiter->expects(self::never())
+ ->method('registerUserRequest');
+
+ $this->plugin->beforeBind('addressbooks/users/foo/addressbookname');
+ }
+
+ public function testUnrelated(): void {
+ $user = $this->createMock(IUser::class);
+ $this->userManager->expects(self::once())
+ ->method('get')
+ ->with($this->userId)
+ ->willReturn($user);
+ $this->limiter->expects(self::never())
+ ->method('registerUserRequest');
+
+ $this->plugin->beforeBind('foo/bar');
+ }
+
+ public function testRegisterAddressBookrCreation(): void {
+ $user = $this->createMock(IUser::class);
+ $this->userManager->expects(self::once())
+ ->method('get')
+ ->with($this->userId)
+ ->willReturn($user);
+ $this->config
+ ->method('getValueInt')
+ ->with('dav')
+ ->willReturnArgument(2);
+ $this->limiter->expects(self::once())
+ ->method('registerUserRequest')
+ ->with(
+ 'carddav-create-address-book',
+ 10,
+ 3600,
+ $user,
+ );
+
+ $this->plugin->beforeBind('addressbooks/users/foo/addressbookname');
+ }
+
+ public function testAddressBookCreationRateLimitExceeded(): void {
+ $user = $this->createMock(IUser::class);
+ $this->userManager->expects(self::once())
+ ->method('get')
+ ->with($this->userId)
+ ->willReturn($user);
+ $this->config
+ ->method('getValueInt')
+ ->with('dav')
+ ->willReturnArgument(2);
+ $this->limiter->expects(self::once())
+ ->method('registerUserRequest')
+ ->with(
+ 'carddav-create-address-book',
+ 10,
+ 3600,
+ $user,
+ )
+ ->willThrowException(new RateLimitExceededException());
+ $this->expectException(TooManyRequests::class);
+
+ $this->plugin->beforeBind('addressbooks/users/foo/addressbookname');
+ }
+
+ public function testAddressBookLimitReached(): void {
+ $user = $this->createMock(IUser::class);
+ $this->userManager->expects(self::once())
+ ->method('get')
+ ->with($this->userId)
+ ->willReturn($user);
+ $user->method('getUID')->willReturn('user123');
+ $this->config
+ ->method('getValueInt')
+ ->with('dav')
+ ->willReturnArgument(2);
+ $this->limiter->expects(self::once())
+ ->method('registerUserRequest')
+ ->with(
+ 'carddav-create-address-book',
+ 10,
+ 3600,
+ $user,
+ );
+ $this->cardDavBackend->expects(self::once())
+ ->method('getAddressBooksForUserCount')
+ ->with('principals/users/user123')
+ ->willReturn(11);
+ $this->expectException(Forbidden::class);
+
+ $this->plugin->beforeBind('addressbooks/users/foo/addressbookname');
+ }
+
+}
diff --git a/apps/dav/tests/unit/CardDAV/Sharing/PluginTest.php b/apps/dav/tests/unit/CardDAV/Sharing/PluginTest.php
index 472826ac2f7..1e934a69a53 100644
--- a/apps/dav/tests/unit/CardDAV/Sharing/PluginTest.php
+++ b/apps/dav/tests/unit/CardDAV/Sharing/PluginTest.php
@@ -1,30 +1,11 @@
<?php
+
+declare(strict_types=1);
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Georg Ehrke <oc.list@georgehrke.com>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
-
namespace OCA\DAV\Tests\unit\CardDAV\Sharing;
use OCA\DAV\Connector\Sabre\Auth;
@@ -32,6 +13,7 @@ use OCA\DAV\DAV\Sharing\IShareable;
use OCA\DAV\DAV\Sharing\Plugin;
use OCP\IConfig;
use OCP\IRequest;
+use PHPUnit\Framework\MockObject\MockObject;
use Sabre\DAV\Server;
use Sabre\DAV\SimpleCollection;
use Sabre\HTTP\Request;
@@ -39,36 +21,30 @@ use Sabre\HTTP\Response;
use Test\TestCase;
class PluginTest extends TestCase {
-
- /** @var Plugin */
- private $plugin;
- /** @var Server */
- private $server;
- /** @var IShareable | \PHPUnit\Framework\MockObject\MockObject */
- private $book;
+ private Plugin $plugin;
+ private Server $server;
+ private IShareable&MockObject $book;
protected function setUp(): void {
parent::setUp();
- /** @var Auth | \PHPUnit\Framework\MockObject\MockObject $authBackend */
- $authBackend = $this->getMockBuilder(Auth::class)->disableOriginalConstructor()->getMock();
- $authBackend->method('isDavAuthenticated')->willReturn(true);
-
- /** @var IRequest $request */
- $request = $this->getMockBuilder(IRequest::class)->disableOriginalConstructor()->getMock();
+ $authBackend = $this->createMock(Auth::class);
+ $authBackend->method('isDavAuthenticated')
+ ->willReturn(true);
+ $request = $this->createMock(IRequest::class);
$config = $this->createMock(IConfig::class);
$this->plugin = new Plugin($authBackend, $request, $config);
$root = new SimpleCollection('root');
$this->server = new \Sabre\DAV\Server($root);
- /** @var SimpleCollection $node */
- $this->book = $this->getMockBuilder(IShareable::class)->disableOriginalConstructor()->getMock();
- $this->book->method('getName')->willReturn('addressbook1.vcf');
+ $this->book = $this->createMock(IShareable::class);
+ $this->book->method('getName')
+ ->willReturn('addressbook1.vcf');
$root->addChild($this->book);
$this->plugin->initialize($this->server);
}
- public function testSharing() {
+ public function testSharing(): void {
$this->book->expects($this->once())->method('updateShares')->with([[
'href' => 'principal:principals/admin',
'commonName' => null,
diff --git a/apps/dav/tests/unit/CardDAV/SyncServiceTest.php b/apps/dav/tests/unit/CardDAV/SyncServiceTest.php
index eb8186807c6..77caed336f4 100644
--- a/apps/dav/tests/unit/CardDAV/SyncServiceTest.php
+++ b/apps/dav/tests/unit/CardDAV/SyncServiceTest.php
@@ -1,112 +1,335 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Bjoern Schiessle <bjoern@schiessle.org>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Thomas Citharel <nextcloud@tcit.fr>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
-
namespace OCA\DAV\Tests\unit\CardDAV;
-use OC\Accounts\AccountManager;
+use GuzzleHttp\Exception\ClientException;
+use GuzzleHttp\Psr7\Request as PsrRequest;
+use GuzzleHttp\Psr7\Response as PsrResponse;
+use OC\Http\Client\Response;
use OCA\DAV\CardDAV\CardDavBackend;
+use OCA\DAV\CardDAV\Converter;
use OCA\DAV\CardDAV\SyncService;
-use OCP\Accounts\IAccountManager;
-use OCP\ILogger;
+use OCP\Http\Client\IClient;
+use OCP\Http\Client\IClientService;
+use OCP\IConfig;
+use OCP\IDBConnection;
use OCP\IUser;
use OCP\IUserManager;
+use PHPUnit\Framework\MockObject\MockObject;
+use Psr\Http\Client\ClientExceptionInterface;
+use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
+use Sabre\VObject\Component\VCard;
use Test\TestCase;
class SyncServiceTest extends TestCase {
- public function testEmptySync() {
- $backend = $this->getBackendMock(0, 0, 0);
- $ss = $this->getSyncServiceMock($backend, []);
- $return = $ss->syncRemoteAddressBook('', 'system', 'system', '1234567890', null, '1', 'principals/system/system', []);
- $this->assertEquals('sync-token-1', $return);
+ protected CardDavBackend&MockObject $backend;
+ protected IUserManager&MockObject $userManager;
+ protected IDBConnection&MockObject $dbConnection;
+ protected LoggerInterface $logger;
+ protected Converter&MockObject $converter;
+ protected IClient&MockObject $client;
+ protected IConfig&MockObject $config;
+ protected SyncService $service;
+
+ public function setUp(): void {
+ parent::setUp();
+
+ $addressBook = [
+ 'id' => 1,
+ 'uri' => 'system',
+ 'principaluri' => 'principals/system/system',
+ '{DAV:}displayname' => 'system',
+ // watch out, incomplete address book mock.
+ ];
+
+ $this->backend = $this->createMock(CardDavBackend::class);
+ $this->backend->method('getAddressBooksByUri')
+ ->with('principals/system/system', 1)
+ ->willReturn($addressBook);
+
+ $this->userManager = $this->createMock(IUserManager::class);
+ $this->dbConnection = $this->createMock(IDBConnection::class);
+ $this->logger = new NullLogger();
+ $this->converter = $this->createMock(Converter::class);
+ $this->client = $this->createMock(IClient::class);
+ $this->config = $this->createMock(IConfig::class);
+
+ $clientService = $this->createMock(IClientService::class);
+ $clientService->method('newClient')
+ ->willReturn($this->client);
+
+ $this->service = new SyncService(
+ $this->backend,
+ $this->userManager,
+ $this->dbConnection,
+ $this->logger,
+ $this->converter,
+ $clientService,
+ $this->config
+ );
}
- public function testSyncWithNewElement() {
- $backend = $this->getBackendMock(1, 0, 0);
- $backend->method('getCard')->willReturn(false);
+ public function testEmptySync(): void {
+ $this->backend->expects($this->exactly(0))
+ ->method('createCard');
+ $this->backend->expects($this->exactly(0))
+ ->method('updateCard');
+ $this->backend->expects($this->exactly(0))
+ ->method('deleteCard');
+
+ $body = '<?xml version="1.0"?>
+<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:card="urn:ietf:params:xml:ns:carddav" xmlns:oc="http://owncloud.org/ns">
+ <d:sync-token>http://sabre.io/ns/sync/1</d:sync-token>
+</d:multistatus>';
+
+ $requestResponse = new Response(new PsrResponse(
+ 207,
+ ['Content-Type' => 'application/xml; charset=utf-8', 'Content-Length' => strlen($body)],
+ $body
+ ));
+
+ $this->client
+ ->method('request')
+ ->willReturn($requestResponse);
- $ss = $this->getSyncServiceMock($backend, ['0' => [200 => '']]);
- $return = $ss->syncRemoteAddressBook('', 'system', 'system', '1234567890', null, '1', 'principals/system/system', []);
- $this->assertEquals('sync-token-1', $return);
+ $token = $this->service->syncRemoteAddressBook(
+ '',
+ 'system',
+ 'system',
+ '1234567890',
+ null,
+ '1',
+ 'principals/system/system',
+ []
+ )[0];
+
+ $this->assertEquals('http://sabre.io/ns/sync/1', $token);
}
- public function testSyncWithUpdatedElement() {
- $backend = $this->getBackendMock(0, 1, 0);
- $backend->method('getCard')->willReturn(true);
+ public function testSyncWithNewElement(): void {
+ $this->backend->expects($this->exactly(1))
+ ->method('createCard');
+ $this->backend->expects($this->exactly(0))
+ ->method('updateCard');
+ $this->backend->expects($this->exactly(0))
+ ->method('deleteCard');
+
+ $this->backend->method('getCard')
+ ->willReturn(false);
+
+
+ $body = '<?xml version="1.0"?>
+<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:card="urn:ietf:params:xml:ns:carddav" xmlns:oc="http://owncloud.org/ns">
+ <d:response>
+ <d:href>/remote.php/dav/addressbooks/system/system/system/Database:alice.vcf</d:href>
+ <d:propstat>
+ <d:prop>
+ <d:getcontenttype>text/vcard; charset=utf-8</d:getcontenttype>
+ <d:getetag>&quot;2df155fa5c2a24cd7f750353fc63f037&quot;</d:getetag>
+ </d:prop>
+ <d:status>HTTP/1.1 200 OK</d:status>
+ </d:propstat>
+ </d:response>
+ <d:sync-token>http://sabre.io/ns/sync/2</d:sync-token>
+</d:multistatus>';
- $ss = $this->getSyncServiceMock($backend, ['0' => [200 => '']]);
- $return = $ss->syncRemoteAddressBook('', 'system', 'system', '1234567890', null, '1', 'principals/system/system', []);
- $this->assertEquals('sync-token-1', $return);
+ $reportResponse = new Response(new PsrResponse(
+ 207,
+ ['Content-Type' => 'application/xml; charset=utf-8', 'Content-Length' => strlen($body)],
+ $body
+ ));
+
+ $this->client
+ ->method('request')
+ ->willReturn($reportResponse);
+
+ $vCard = 'BEGIN:VCARD
+VERSION:3.0
+PRODID:-//Sabre//Sabre VObject 4.5.4//EN
+UID:alice
+FN;X-NC-SCOPE=v2-federated:alice
+N;X-NC-SCOPE=v2-federated:alice;;;;
+X-SOCIALPROFILE;TYPE=NEXTCLOUD;X-NC-SCOPE=v2-published:https://server2.internal/index.php/u/alice
+CLOUD:alice@server2.internal
+END:VCARD';
+
+ $getResponse = new Response(new PsrResponse(
+ 200,
+ ['Content-Type' => 'text/vcard; charset=utf-8', 'Content-Length' => strlen($vCard)],
+ $vCard,
+ ));
+
+ $this->client
+ ->method('get')
+ ->willReturn($getResponse);
+
+ $token = $this->service->syncRemoteAddressBook(
+ '',
+ 'system',
+ 'system',
+ '1234567890',
+ null,
+ '1',
+ 'principals/system/system',
+ []
+ )[0];
+
+ $this->assertEquals('http://sabre.io/ns/sync/2', $token);
}
- public function testSyncWithDeletedElement() {
- $backend = $this->getBackendMock(0, 0, 1);
+ public function testSyncWithUpdatedElement(): void {
+ $this->backend->expects($this->exactly(0))
+ ->method('createCard');
+ $this->backend->expects($this->exactly(1))
+ ->method('updateCard');
+ $this->backend->expects($this->exactly(0))
+ ->method('deleteCard');
+
+ $this->backend->method('getCard')
+ ->willReturn(true);
+
+
+ $body = '<?xml version="1.0"?>
+<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:card="urn:ietf:params:xml:ns:carddav" xmlns:oc="http://owncloud.org/ns">
+ <d:response>
+ <d:href>/remote.php/dav/addressbooks/system/system/system/Database:alice.vcf</d:href>
+ <d:propstat>
+ <d:prop>
+ <d:getcontenttype>text/vcard; charset=utf-8</d:getcontenttype>
+ <d:getetag>&quot;2df155fa5c2a24cd7f750353fc63f037&quot;</d:getetag>
+ </d:prop>
+ <d:status>HTTP/1.1 200 OK</d:status>
+ </d:propstat>
+ </d:response>
+ <d:sync-token>http://sabre.io/ns/sync/3</d:sync-token>
+</d:multistatus>';
+
+ $reportResponse = new Response(new PsrResponse(
+ 207,
+ ['Content-Type' => 'application/xml; charset=utf-8', 'Content-Length' => strlen($body)],
+ $body
+ ));
+
+ $this->client
+ ->method('request')
+ ->willReturn($reportResponse);
+
+ $vCard = 'BEGIN:VCARD
+VERSION:3.0
+PRODID:-//Sabre//Sabre VObject 4.5.4//EN
+UID:alice
+FN;X-NC-SCOPE=v2-federated:alice
+N;X-NC-SCOPE=v2-federated:alice;;;;
+X-SOCIALPROFILE;TYPE=NEXTCLOUD;X-NC-SCOPE=v2-published:https://server2.internal/index.php/u/alice
+CLOUD:alice@server2.internal
+END:VCARD';
+
+ $getResponse = new Response(new PsrResponse(
+ 200,
+ ['Content-Type' => 'text/vcard; charset=utf-8', 'Content-Length' => strlen($vCard)],
+ $vCard,
+ ));
+
+ $this->client
+ ->method('get')
+ ->willReturn($getResponse);
+
+ $token = $this->service->syncRemoteAddressBook(
+ '',
+ 'system',
+ 'system',
+ '1234567890',
+ null,
+ '1',
+ 'principals/system/system',
+ []
+ )[0];
- $ss = $this->getSyncServiceMock($backend, ['0' => [404 => '']]);
- $return = $ss->syncRemoteAddressBook('', 'system', 'system', '1234567890', null, '1', 'principals/system/system', []);
- $this->assertEquals('sync-token-1', $return);
+ $this->assertEquals('http://sabre.io/ns/sync/3', $token);
}
- public function testEnsureSystemAddressBookExists() {
- /** @var CardDavBackend | \PHPUnit\Framework\MockObject\MockObject $backend */
- $backend = $this->getMockBuilder(CardDavBackend::class)->disableOriginalConstructor()->getMock();
+ public function testSyncWithDeletedElement(): void {
+ $this->backend->expects($this->exactly(0))
+ ->method('createCard');
+ $this->backend->expects($this->exactly(0))
+ ->method('updateCard');
+ $this->backend->expects($this->exactly(1))
+ ->method('deleteCard');
+
+ $body = '<?xml version="1.0"?>
+<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:card="urn:ietf:params:xml:ns:carddav" xmlns:oc="http://owncloud.org/ns">
+<d:response>
+ <d:href>/remote.php/dav/addressbooks/system/system/system/Database:alice.vcf</d:href>
+ <d:status>HTTP/1.1 404 Not Found</d:status>
+</d:response>
+<d:sync-token>http://sabre.io/ns/sync/4</d:sync-token>
+</d:multistatus>';
+
+ $reportResponse = new Response(new PsrResponse(
+ 207,
+ ['Content-Type' => 'application/xml; charset=utf-8', 'Content-Length' => strlen($body)],
+ $body
+ ));
+
+ $this->client
+ ->method('request')
+ ->willReturn($reportResponse);
+
+ $token = $this->service->syncRemoteAddressBook(
+ '',
+ 'system',
+ 'system',
+ '1234567890',
+ null,
+ '1',
+ 'principals/system/system',
+ []
+ )[0];
+
+ $this->assertEquals('http://sabre.io/ns/sync/4', $token);
+ }
+
+ public function testEnsureSystemAddressBookExists(): void {
+ /** @var CardDavBackend&MockObject $backend */
+ $backend = $this->createMock(CardDavBackend::class);
$backend->expects($this->exactly(1))->method('createAddressBook');
- $backend->expects($this->at(0))->method('getAddressBooksByUri')->willReturn(null);
- $backend->expects($this->at(1))->method('getAddressBooksByUri')->willReturn([]);
-
- /** @var IUserManager $userManager */
- $userManager = $this->getMockBuilder(IUserManager::class)->disableOriginalConstructor()->getMock();
- $logger = $this->getMockBuilder(ILogger::class)->disableOriginalConstructor()->getMock();
- $accountManager = $this->getMockBuilder(AccountManager::class)->disableOriginalConstructor()->getMock();
- $ss = new SyncService($backend, $userManager, $logger, $accountManager);
+ $backend->expects($this->exactly(2))
+ ->method('getAddressBooksByUri')
+ ->willReturnOnConsecutiveCalls(
+ null,
+ [],
+ );
+
+ $userManager = $this->createMock(IUserManager::class);
+ $dbConnection = $this->createMock(IDBConnection::class);
+ $logger = $this->createMock(LoggerInterface::class);
+ $converter = $this->createMock(Converter::class);
+ $clientService = $this->createMock(IClientService::class);
+ $config = $this->createMock(IConfig::class);
+
+ $ss = new SyncService($backend, $userManager, $dbConnection, $logger, $converter, $clientService, $config);
$ss->ensureSystemAddressBookExists('principals/users/adam', 'contacts', []);
}
- public function dataActivatedUsers() {
+ public static function dataActivatedUsers(): array {
return [
[true, 1, 1, 1],
[false, 0, 0, 3],
];
}
- /**
- * @dataProvider dataActivatedUsers
- *
- * @param boolean $activated
- * @param integer $createCalls
- * @param integer $updateCalls
- * @param integer $deleteCalls
- * @return void
- */
- public function testUpdateAndDeleteUser($activated, $createCalls, $updateCalls, $deleteCalls) {
- /** @var CardDavBackend | \PHPUnit\Framework\MockObject\MockObject $backend */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataActivatedUsers')]
+ public function testUpdateAndDeleteUser(bool $activated, int $createCalls, int $updateCalls, int $deleteCalls): void {
+ /** @var CardDavBackend | MockObject $backend */
$backend = $this->getMockBuilder(CardDavBackend::class)->disableOriginalConstructor()->getMock();
- $logger = $this->getMockBuilder(ILogger::class)->disableOriginalConstructor()->getMock();
+ $logger = $this->getMockBuilder(LoggerInterface::class)->disableOriginalConstructor()->getMock();
$backend->expects($this->exactly($createCalls))->method('createCard');
$backend->expects($this->exactly($updateCalls))->method('updateCard');
@@ -120,57 +343,23 @@ class SyncServiceTest extends TestCase {
->with('principals/system/system', 'system')
->willReturn(['id' => -1]);
- /** @var IUserManager | \PHPUnit\Framework\MockObject\MockObject $userManager */
- $userManager = $this->getMockBuilder(IUserManager::class)->disableOriginalConstructor()->getMock();
-
- /** @var IUser | \PHPUnit\Framework\MockObject\MockObject $user */
- $user = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
+ $userManager = $this->createMock(IUserManager::class);
+ $dbConnection = $this->createMock(IDBConnection::class);
+ $user = $this->createMock(IUser::class);
$user->method('getBackendClassName')->willReturn('unittest');
$user->method('getUID')->willReturn('test-user');
$user->method('getCloudId')->willReturn('cloudId');
$user->method('getDisplayName')->willReturn('test-user');
$user->method('isEnabled')->willReturn($activated);
- $accountManager = $this->getMockBuilder(AccountManager::class)->disableOriginalConstructor()->getMock();
- $accountManager->expects($this->any())->method('getUser')
- ->willReturn([
- IAccountManager::PROPERTY_DISPLAYNAME =>
- [
- 'value' => $user->getDisplayName(),
- 'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY,
- ],
- IAccountManager::PROPERTY_ADDRESS =>
- [
- 'value' => '',
- 'scope' => AccountManager::VISIBILITY_PRIVATE,
- ],
- IAccountManager::PROPERTY_WEBSITE =>
- [
- 'value' => '',
- 'scope' => AccountManager::VISIBILITY_PRIVATE,
- ],
- IAccountManager::PROPERTY_EMAIL =>
- [
- 'value' => $user->getEMailAddress(),
- 'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY,
- ],
- IAccountManager::PROPERTY_AVATAR =>
- [
- 'scope' => AccountManager::VISIBILITY_CONTACTS_ONLY
- ],
- IAccountManager::PROPERTY_PHONE =>
- [
- 'value' => '',
- 'scope' => AccountManager::VISIBILITY_PRIVATE,
- ],
- IAccountManager::PROPERTY_TWITTER =>
- [
- 'value' => '',
- 'scope' => AccountManager::VISIBILITY_PRIVATE,
- ],
- ]
- );
+ $converter = $this->createMock(Converter::class);
+ $converter->expects($this->any())
+ ->method('createCardFromUser')
+ ->willReturn($this->createMock(VCard::class));
+
+ $clientService = $this->createMock(IClientService::class);
+ $config = $this->createMock(IConfig::class);
- $ss = new SyncService($backend, $userManager, $logger, $accountManager);
+ $ss = new SyncService($backend, $userManager, $dbConnection, $logger, $converter, $clientService, $config);
$ss->updateUser($user);
$ss->updateUser($user);
@@ -178,44 +367,114 @@ class SyncServiceTest extends TestCase {
$ss->deleteUser($user);
}
- /**
- * @param int $createCount
- * @param int $updateCount
- * @param int $deleteCount
- * @return \PHPUnit\Framework\MockObject\MockObject
- */
- private function getBackendMock($createCount, $updateCount, $deleteCount) {
- $backend = $this->getMockBuilder(CardDavBackend::class)
- ->disableOriginalConstructor()
- ->getMock();
- $backend->expects($this->exactly($createCount))->method('createCard');
- $backend->expects($this->exactly($updateCount))->method('updateCard');
- $backend->expects($this->exactly($deleteCount))->method('deleteCard');
- return $backend;
+ public function testDeleteAddressbookWhenAccessRevoked(): void {
+ $this->expectException(ClientExceptionInterface::class);
+
+ $this->backend->expects($this->exactly(0))
+ ->method('createCard');
+ $this->backend->expects($this->exactly(0))
+ ->method('updateCard');
+ $this->backend->expects($this->exactly(0))
+ ->method('deleteCard');
+ $this->backend->expects($this->exactly(1))
+ ->method('deleteAddressBook');
+
+ $request = new PsrRequest(
+ 'REPORT',
+ 'https://server2.internal/remote.php/dav/addressbooks/system/system/system',
+ ['Content-Type' => 'application/xml'],
+ );
+
+ $body = '<?xml version="1.0" encoding="utf-8"?>
+<d:error xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns">
+ <s:exception>Sabre\DAV\Exception\NotAuthenticated</s:exception>
+ <s:message>No public access to this resource., Username or password was incorrect, No \'Authorization: Bearer\' header found. Either the client didn\'t send one, or the server is mis-configured, Username or password was incorrect</s:message>
+</d:error>';
+
+ $response = new PsrResponse(
+ 401,
+ ['Content-Type' => 'application/xml; charset=utf-8', 'Content-Length' => strlen($body)],
+ $body
+ );
+
+ $message = 'Client error: `REPORT https://server2.internal/cloud/remote.php/dav/addressbooks/system/system/system` resulted in a `401 Unauthorized` response:
+<?xml version="1.0" encoding="utf-8"?>
+<d:error xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns">
+ <s:exception>Sabre\DA (truncated...)
+';
+
+ $reportException = new ClientException(
+ $message,
+ $request,
+ $response
+ );
+
+ $this->client
+ ->method('request')
+ ->willThrowException($reportException);
+
+ $this->service->syncRemoteAddressBook(
+ '',
+ 'system',
+ 'system',
+ '1234567890',
+ null,
+ '1',
+ 'principals/system/system',
+ []
+ );
+ }
+
+ #[\PHPUnit\Framework\Attributes\DataProvider('providerUseAbsoluteUriReport')]
+ public function testUseAbsoluteUriReport(string $host, string $expected): void {
+ $body = '<?xml version="1.0"?>
+<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:card="urn:ietf:params:xml:ns:carddav" xmlns:oc="http://owncloud.org/ns">
+ <d:sync-token>http://sabre.io/ns/sync/1</d:sync-token>
+</d:multistatus>';
+
+ $requestResponse = new Response(new PsrResponse(
+ 207,
+ ['Content-Type' => 'application/xml; charset=utf-8', 'Content-Length' => strlen($body)],
+ $body
+ ));
+
+ $this->client
+ ->method('request')
+ ->with(
+ 'REPORT',
+ $this->callback(function ($uri) use ($expected) {
+ $this->assertEquals($expected, $uri);
+ return true;
+ }),
+ $this->callback(function ($options) {
+ $this->assertIsArray($options);
+ return true;
+ }),
+ )
+ ->willReturn($requestResponse);
+
+ $this->service->syncRemoteAddressBook(
+ $host,
+ 'system',
+ 'remote.php/dav/addressbooks/system/system/system',
+ '1234567890',
+ null,
+ '1',
+ 'principals/system/system',
+ []
+ );
}
- /**
- * @param $backend
- * @param $response
- * @return SyncService|\PHPUnit\Framework\MockObject\MockObject
- */
- private function getSyncServiceMock($backend, $response) {
- $userManager = $this->getMockBuilder(IUserManager::class)->disableOriginalConstructor()->getMock();
- $logger = $this->getMockBuilder(ILogger::class)->disableOriginalConstructor()->getMock();
- $accountManager = $this->getMockBuilder(AccountManager::class)->disableOriginalConstructor()->getMock();
- /** @var SyncService | \PHPUnit\Framework\MockObject\MockObject $ss */
- $ss = $this->getMockBuilder(SyncService::class)
- ->setMethods(['ensureSystemAddressBookExists', 'requestSyncReport', 'download', 'getCertPath'])
- ->setConstructorArgs([$backend, $userManager, $logger, $accountManager])
- ->getMock();
- $ss->method('requestSyncReport')->withAnyParameters()->willReturn(['response' => $response, 'token' => 'sync-token-1']);
- $ss->method('ensureSystemAddressBookExists')->willReturn(['id' => 1]);
- $ss->method('download')->willReturn([
- 'body' => '',
- 'statusCode' => 200,
- 'headers' => []
- ]);
- $ss->method('getCertPath')->willReturn('');
- return $ss;
+ public static function providerUseAbsoluteUriReport(): array {
+ return [
+ ['https://server.internal', 'https://server.internal/remote.php/dav/addressbooks/system/system/system'],
+ ['https://server.internal/', 'https://server.internal/remote.php/dav/addressbooks/system/system/system'],
+ ['https://server.internal/nextcloud', 'https://server.internal/nextcloud/remote.php/dav/addressbooks/system/system/system'],
+ ['https://server.internal/nextcloud/', 'https://server.internal/nextcloud/remote.php/dav/addressbooks/system/system/system'],
+ ['https://server.internal:8080', 'https://server.internal:8080/remote.php/dav/addressbooks/system/system/system'],
+ ['https://server.internal:8080/', 'https://server.internal:8080/remote.php/dav/addressbooks/system/system/system'],
+ ['https://server.internal:8080/nextcloud', 'https://server.internal:8080/nextcloud/remote.php/dav/addressbooks/system/system/system'],
+ ['https://server.internal:8080/nextcloud/', 'https://server.internal:8080/nextcloud/remote.php/dav/addressbooks/system/system/system'],
+ ];
}
}
diff --git a/apps/dav/tests/unit/CardDAV/SystemAddressBookTest.php b/apps/dav/tests/unit/CardDAV/SystemAddressBookTest.php
new file mode 100644
index 00000000000..4a218fa4616
--- /dev/null
+++ b/apps/dav/tests/unit/CardDAV/SystemAddressBookTest.php
@@ -0,0 +1,428 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\DAV\Tests\unit\CardDAV;
+
+use OC\AppFramework\Http\Request;
+use OCA\DAV\CardDAV\SyncService;
+use OCA\DAV\CardDAV\SystemAddressbook;
+use OCA\Federation\TrustedServers;
+use OCP\Accounts\IAccountManager;
+use OCP\IConfig;
+use OCP\IGroup;
+use OCP\IGroupManager;
+use OCP\IL10N;
+use OCP\IRequest;
+use OCP\IUser;
+use OCP\IUserSession;
+use PHPUnit\Framework\MockObject\MockObject;
+use Sabre\CardDAV\Backend\BackendInterface;
+use Sabre\DAV\Exception\Forbidden;
+use Sabre\DAV\Exception\NotFound;
+use Sabre\VObject\Component\VCard;
+use Sabre\VObject\Reader;
+use Test\TestCase;
+
+class SystemAddressBookTest extends TestCase {
+ private BackendInterface&MockObject $cardDavBackend;
+ private array $addressBookInfo;
+ private IL10N&MockObject $l10n;
+ private IConfig&MockObject $config;
+ private IUserSession $userSession;
+ private IRequest&MockObject $request;
+ private array $server;
+ private TrustedServers&MockObject $trustedServers;
+ private IGroupManager&MockObject $groupManager;
+ private SystemAddressbook $addressBook;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->cardDavBackend = $this->createMock(BackendInterface::class);
+ $this->addressBookInfo = [
+ 'id' => 123,
+ '{DAV:}displayname' => 'Accounts',
+ 'principaluri' => 'principals/system/system',
+ ];
+ $this->l10n = $this->createMock(IL10N::class);
+ $this->config = $this->createMock(IConfig::class);
+ $this->userSession = $this->createMock(IUserSession::class);
+ $this->request = $this->createMock(Request::class);
+ $this->server = [
+ 'PHP_AUTH_USER' => 'system',
+ 'PHP_AUTH_PW' => 'shared123',
+ ];
+ $this->request->method('__get')->with('server')->willReturn($this->server);
+ $this->trustedServers = $this->createMock(TrustedServers::class);
+ $this->groupManager = $this->createMock(IGroupManager::class);
+
+ $this->addressBook = new SystemAddressbook(
+ $this->cardDavBackend,
+ $this->addressBookInfo,
+ $this->l10n,
+ $this->config,
+ $this->userSession,
+ $this->request,
+ $this->trustedServers,
+ $this->groupManager,
+ );
+ }
+
+ public function testGetChildrenAsGuest(): void {
+ $this->config->expects(self::exactly(3))
+ ->method('getAppValue')
+ ->willReturnMap([
+ ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
+ ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
+ ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
+ ]);
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('user');
+ $user->method('getBackendClassName')->willReturn('Guests');
+ $this->userSession->expects(self::once())
+ ->method('getUser')
+ ->willReturn($user);
+ $vcfWithScopes = <<<VCF
+BEGIN:VCARD
+VERSION:3.0
+PRODID:-//Sabre//Sabre VObject 4.4.2//EN
+UID:admin
+FN;X-NC-SCOPE=v2-federated:admin
+N;X-NC-SCOPE=v2-federated:admin;;;;
+ADR;TYPE=OTHER;X-NC-SCOPE=v2-local:Testing test test test;;;;;;
+EMAIL;TYPE=OTHER;X-NC-SCOPE=v2-federated:miau_lalala@gmx.net
+TEL;TYPE=OTHER;X-NC-SCOPE=v2-local:+435454454544
+CLOUD:admin@http://localhost
+END:VCARD
+VCF;
+ $originalCard = [
+ 'carddata' => $vcfWithScopes,
+ ];
+ $this->cardDavBackend->expects(self::once())
+ ->method('getCard')
+ ->with(123, 'Guests:user.vcf')
+ ->willReturn($originalCard);
+
+ $children = $this->addressBook->getChildren();
+
+ self::assertCount(1, $children);
+ }
+
+ public function testGetFilteredChildForFederation(): void {
+ $this->config->expects(self::exactly(3))
+ ->method('getAppValue')
+ ->willReturnMap([
+ ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
+ ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
+ ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
+ ]);
+ $this->trustedServers->expects(self::once())
+ ->method('getServers')
+ ->willReturn([
+ [
+ 'shared_secret' => 'shared123',
+ ],
+ ]);
+ $vcfWithScopes = <<<VCF
+BEGIN:VCARD
+VERSION:3.0
+PRODID:-//Sabre//Sabre VObject 4.4.2//EN
+UID:admin
+FN;X-NC-SCOPE=v2-federated:admin
+N;X-NC-SCOPE=v2-federated:admin;;;;
+ADR;TYPE=OTHER;X-NC-SCOPE=v2-local:Testing test test test;;;;;;
+EMAIL;TYPE=OTHER;X-NC-SCOPE=v2-federated:miau_lalala@gmx.net
+TEL;TYPE=OTHER;X-NC-SCOPE=v2-local:+435454454544
+CLOUD:admin@http://localhost
+END:VCARD
+VCF;
+ $originalCard = [
+ 'carddata' => $vcfWithScopes,
+ ];
+ $this->cardDavBackend->expects(self::once())
+ ->method('getCard')
+ ->with(123, 'user.vcf')
+ ->willReturn($originalCard);
+
+ $card = $this->addressBook->getChild('user.vcf');
+
+ /** @var VCard $vCard */
+ $vCard = Reader::read($card->get());
+ foreach ($vCard->children() as $child) {
+ $scope = $child->offsetGet('X-NC-SCOPE');
+ if ($scope !== null) {
+ self::assertNotEquals(IAccountManager::SCOPE_PRIVATE, $scope->getValue());
+ self::assertNotEquals(IAccountManager::SCOPE_LOCAL, $scope->getValue());
+ }
+ }
+ }
+
+ public function testGetChildNotFound(): void {
+ $this->config->expects(self::exactly(3))
+ ->method('getAppValue')
+ ->willReturnMap([
+ ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
+ ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
+ ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
+ ]);
+ $this->trustedServers->expects(self::once())
+ ->method('getServers')
+ ->willReturn([
+ [
+ 'shared_secret' => 'shared123',
+ ],
+ ]);
+ $this->expectException(NotFound::class);
+
+ $this->addressBook->getChild('LDAP:user.vcf');
+ }
+
+ public function testGetChildWithoutEnumeration(): void {
+ $this->config->expects(self::exactly(3))
+ ->method('getAppValue')
+ ->willReturnMap([
+ ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'no'],
+ ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
+ ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
+ ]);
+ $this->expectException(Forbidden::class);
+
+ $this->addressBook->getChild('LDAP:user.vcf');
+ }
+
+ public function testGetChildAsGuest(): void {
+ $this->config->expects(self::exactly(3))
+ ->method('getAppValue')
+ ->willReturnMap([
+ ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
+ ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
+ ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
+ ]);
+ $user = $this->createMock(IUser::class);
+ $user->method('getBackendClassName')->willReturn('Guests');
+ $this->userSession->expects(self::once())
+ ->method('getUser')
+ ->willReturn($user);
+ $this->expectException(Forbidden::class);
+
+ $this->addressBook->getChild('LDAP:user.vcf');
+ }
+
+ public function testGetChildWithGroupEnumerationRestriction(): void {
+ $this->config->expects(self::exactly(3))
+ ->method('getAppValue')
+ ->willReturnMap([
+ ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
+ ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'yes'],
+ ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
+ ]);
+ $user = $this->createMock(IUser::class);
+ $user->method('getBackendClassName')->willReturn('LDAP');
+ $this->userSession->expects(self::once())
+ ->method('getUser')
+ ->willReturn($user);
+ $otherUser = $this->createMock(IUser::class);
+ $user->method('getBackendClassName')->willReturn('LDAP');
+ $otherUser->method('getUID')->willReturn('other');
+ $group = $this->createMock(IGroup::class);
+ $group->expects(self::once())
+ ->method('getUsers')
+ ->willReturn([$otherUser]);
+ $this->groupManager->expects(self::once())
+ ->method('getUserGroups')
+ ->with($user)
+ ->willReturn([$group]);
+ $cardData = <<<VCF
+BEGIN:VCARD
+VERSION:3.0
+PRODID:-//Sabre//Sabre VObject 4.4.2//EN
+UID:admin
+FN;X-NC-SCOPE=v2-federated:other
+END:VCARD
+VCF;
+ $this->cardDavBackend->expects(self::once())
+ ->method('getCard')
+ ->with($this->addressBookInfo['id'], "{$otherUser->getBackendClassName()}:{$otherUser->getUID()}.vcf")
+ ->willReturn([
+ 'id' => 123,
+ 'carddata' => $cardData,
+ ]);
+
+ $this->addressBook->getChild("{$otherUser->getBackendClassName()}:{$otherUser->getUID()}.vcf");
+ }
+
+ public function testGetChildWithPhoneNumberEnumerationRestriction(): void {
+ $this->config->expects(self::exactly(3))
+ ->method('getAppValue')
+ ->willReturnMap([
+ ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
+ ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
+ ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'yes'],
+ ]);
+ $user = $this->createMock(IUser::class);
+ $user->method('getBackendClassName')->willReturn('LDAP');
+ $this->userSession->expects(self::once())
+ ->method('getUser')
+ ->willReturn($user);
+ $this->expectException(Forbidden::class);
+
+ $this->addressBook->getChild('LDAP:user.vcf');
+ }
+
+ public function testGetOwnChildWithPhoneNumberEnumerationRestriction(): void {
+ $this->config->expects(self::exactly(3))
+ ->method('getAppValue')
+ ->willReturnMap([
+ ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
+ ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
+ ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'yes'],
+ ]);
+ $user = $this->createMock(IUser::class);
+ $user->method('getBackendClassName')->willReturn('LDAP');
+ $user->method('getUID')->willReturn('user');
+ $this->userSession->expects(self::once())
+ ->method('getUser')
+ ->willReturn($user);
+ $cardData = <<<VCF
+BEGIN:VCARD
+VERSION:3.0
+PRODID:-//Sabre//Sabre VObject 4.4.2//EN
+UID:admin
+FN;X-NC-SCOPE=v2-federated:user
+END:VCARD
+VCF;
+ $this->cardDavBackend->expects(self::once())
+ ->method('getCard')
+ ->with($this->addressBookInfo['id'], 'LDAP:user.vcf')
+ ->willReturn([
+ 'id' => 123,
+ 'carddata' => $cardData,
+ ]);
+
+ $this->addressBook->getChild('LDAP:user.vcf');
+ }
+
+ public function testGetMultipleChildrenWithGroupEnumerationRestriction(): void {
+ $this->config
+ ->method('getAppValue')
+ ->willReturnMap([
+ ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
+ ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'yes'],
+ ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
+ ]);
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('user');
+ $user->method('getBackendClassName')->willReturn('LDAP');
+ $other1 = $this->createMock(IUser::class);
+ $other1->method('getUID')->willReturn('other1');
+ $other1->method('getBackendClassName')->willReturn('LDAP');
+ $other2 = $this->createMock(IUser::class);
+ $other2->method('getUID')->willReturn('other2');
+ $other2->method('getBackendClassName')->willReturn('LDAP');
+ $other3 = $this->createMock(IUser::class);
+ $other3->method('getUID')->willReturn('other3');
+ $other3->method('getBackendClassName')->willReturn('LDAP');
+ $this->userSession
+ ->method('getUser')
+ ->willReturn($user);
+ $group1 = $this->createMock(IGroup::class);
+ $group1
+ ->method('getUsers')
+ ->willReturn([$user, $other1]);
+ $group2 = $this->createMock(IGroup::class);
+ $group2
+ ->method('getUsers')
+ ->willReturn([$other1, $other2, $user]);
+ $this->groupManager
+ ->method('getUserGroups')
+ ->with($user)
+ ->willReturn([$group1]);
+ $this->cardDavBackend->expects(self::once())
+ ->method('getMultipleCards')
+ ->with($this->addressBookInfo['id'], [
+ SyncService::getCardUri($user),
+ SyncService::getCardUri($other1),
+ ])
+ ->willReturn([
+ [],
+ [],
+ ]);
+
+ $cards = $this->addressBook->getMultipleChildren([
+ SyncService::getCardUri($user),
+ SyncService::getCardUri($other1),
+ // SyncService::getCardUri($other2), // Omitted to test that it's not returned as stray
+ SyncService::getCardUri($other3), // No overlapping group with this one
+ ]);
+
+ self::assertCount(2, $cards);
+ }
+
+ public function testGetMultipleChildrenAsGuest(): void {
+ $this->config
+ ->method('getAppValue')
+ ->willReturnMap([
+ ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
+ ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
+ ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
+ ]);
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('user');
+ $user->method('getBackendClassName')->willReturn('Guests');
+ $this->userSession->expects(self::once())
+ ->method('getUser')
+ ->willReturn($user);
+
+ $cards = $this->addressBook->getMultipleChildren(['Database:user1.vcf', 'LDAP:user2.vcf']);
+
+ self::assertEmpty($cards);
+ }
+
+ public function testGetMultipleChildren(): void {
+ $this->config
+ ->method('getAppValue')
+ ->willReturnMap([
+ ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
+ ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
+ ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
+ ]);
+ $this->trustedServers
+ ->method('getServers')
+ ->willReturn([
+ [
+ 'shared_secret' => 'shared123',
+ ],
+ ]);
+ $cardData = <<<VCF
+BEGIN:VCARD
+VERSION:3.0
+PRODID:-//Sabre//Sabre VObject 4.4.2//EN
+UID:admin
+FN;X-NC-SCOPE=v2-federated:user
+END:VCARD
+VCF;
+ $this->cardDavBackend->expects(self::once())
+ ->method('getMultipleCards')
+ ->with($this->addressBookInfo['id'], ['Database:user1.vcf', 'LDAP:user2.vcf'])
+ ->willReturn([
+ [
+ 'id' => 123,
+ 'carddata' => $cardData,
+ ],
+ [
+ 'id' => 321,
+ 'carddata' => $cardData,
+ ],
+ ]);
+
+ $cards = $this->addressBook->getMultipleChildren(['Database:user1.vcf', 'LDAP:user2.vcf']);
+
+ self::assertCount(2, $cards);
+ }
+}
diff --git a/apps/dav/tests/unit/CardDAV/Validation/CardDavValidatePluginTest.php b/apps/dav/tests/unit/CardDAV/Validation/CardDavValidatePluginTest.php
new file mode 100644
index 00000000000..058735ba32a
--- /dev/null
+++ b/apps/dav/tests/unit/CardDAV/Validation/CardDavValidatePluginTest.php
@@ -0,0 +1,73 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\DAV\Tests\unit\CardDAV\Validation;
+
+use OCA\DAV\CardDAV\Validation\CardDavValidatePlugin;
+use OCP\IAppConfig;
+use PHPUnit\Framework\MockObject\MockObject;
+use Sabre\DAV\Exception\Forbidden;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+use Test\TestCase;
+
+class CardDavValidatePluginTest extends TestCase {
+
+ private CardDavValidatePlugin $plugin;
+ private IAppConfig&MockObject $config;
+ private RequestInterface&MockObject $request;
+ private ResponseInterface&MockObject $response;
+
+ protected function setUp(): void {
+ parent::setUp();
+ // construct mock objects
+ $this->config = $this->createMock(IAppConfig::class);
+ $this->request = $this->createMock(RequestInterface::class);
+ $this->response = $this->createMock(ResponseInterface::class);
+ $this->plugin = new CardDavValidatePlugin(
+ $this->config,
+ );
+ }
+
+ public function testPutSizeLessThenLimit(): void {
+
+ // construct method responses
+ $this->config
+ ->method('getValueInt')
+ ->with('dav', 'card_size_limit', 5242880)
+ ->willReturn(5242880);
+ $this->request
+ ->method('getRawServerValue')
+ ->with('CONTENT_LENGTH')
+ ->willReturn('1024');
+ // test condition
+ $this->assertTrue(
+ $this->plugin->beforePut($this->request, $this->response)
+ );
+
+ }
+
+ public function testPutSizeMoreThenLimit(): void {
+
+ // construct method responses
+ $this->config
+ ->method('getValueInt')
+ ->with('dav', 'card_size_limit', 5242880)
+ ->willReturn(5242880);
+ $this->request
+ ->method('getRawServerValue')
+ ->with('CONTENT_LENGTH')
+ ->willReturn('6242880');
+ $this->expectException(Forbidden::class);
+ // test condition
+ $this->plugin->beforePut($this->request, $this->response);
+
+ }
+
+}