aboutsummaryrefslogtreecommitdiffstats
path: root/tests/lib/Contacts/ContactsMenu
diff options
context:
space:
mode:
Diffstat (limited to 'tests/lib/Contacts/ContactsMenu')
-rw-r--r--tests/lib/Contacts/ContactsMenu/ActionFactoryTest.php47
-rw-r--r--tests/lib/Contacts/ContactsMenu/ActionProviderStoreTest.php124
-rw-r--r--tests/lib/Contacts/ContactsMenu/Actions/LinkActionTest.php90
-rw-r--r--tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php1068
-rw-r--r--tests/lib/Contacts/ContactsMenu/EntryTest.php104
-rw-r--r--tests/lib/Contacts/ContactsMenu/ManagerTest.php201
-rw-r--r--tests/lib/Contacts/ContactsMenu/Providers/EMailproviderTest.php85
-rw-r--r--tests/lib/Contacts/ContactsMenu/Providers/LocalTimeProviderTest.php197
8 files changed, 1916 insertions, 0 deletions
diff --git a/tests/lib/Contacts/ContactsMenu/ActionFactoryTest.php b/tests/lib/Contacts/ContactsMenu/ActionFactoryTest.php
new file mode 100644
index 00000000000..09e0e11bd5e
--- /dev/null
+++ b/tests/lib/Contacts/ContactsMenu/ActionFactoryTest.php
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Tests\Contacts\ContactsMenu;
+
+use OC\Contacts\ContactsMenu\ActionFactory;
+use OCP\Contacts\ContactsMenu\IAction;
+use Test\TestCase;
+
+class ActionFactoryTest extends TestCase {
+ private ActionFactory $actionFactory;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->actionFactory = new ActionFactory();
+ }
+
+ public function testNewLinkAction(): void {
+ $icon = 'icon-test';
+ $name = 'Test';
+ $href = 'some/url';
+
+ $action = $this->actionFactory->newLinkAction($icon, $name, $href);
+
+ $this->assertInstanceOf(IAction::class, $action);
+ $this->assertEquals($name, $action->getName());
+ $this->assertEquals(10, $action->getPriority());
+ }
+
+ public function testNewEMailAction(): void {
+ $icon = 'icon-test';
+ $name = 'Test';
+ $href = 'user@example.com';
+
+ $action = $this->actionFactory->newEMailAction($icon, $name, $href);
+
+ $this->assertInstanceOf(IAction::class, $action);
+ $this->assertEquals($name, $action->getName());
+ $this->assertEquals(10, $action->getPriority());
+ $this->assertEquals('mailto:user@example.com', $action->getHref());
+ }
+}
diff --git a/tests/lib/Contacts/ContactsMenu/ActionProviderStoreTest.php b/tests/lib/Contacts/ContactsMenu/ActionProviderStoreTest.php
new file mode 100644
index 00000000000..84de6ec88e2
--- /dev/null
+++ b/tests/lib/Contacts/ContactsMenu/ActionProviderStoreTest.php
@@ -0,0 +1,124 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Tests\Contacts\ContactsMenu;
+
+use OC\App\AppManager;
+use OC\Contacts\ContactsMenu\ActionProviderStore;
+use OC\Contacts\ContactsMenu\Providers\EMailProvider;
+use OC\Contacts\ContactsMenu\Providers\LocalTimeProvider;
+use OC\Contacts\ContactsMenu\Providers\ProfileProvider;
+use OCP\App\IAppManager;
+use OCP\AppFramework\QueryException;
+use OCP\Contacts\ContactsMenu\IProvider;
+use OCP\IServerContainer;
+use OCP\IUser;
+use PHPUnit\Framework\MockObject\MockObject;
+use Psr\Log\LoggerInterface;
+use Test\TestCase;
+
+class ActionProviderStoreTest extends TestCase {
+ /** @var IServerContainer|MockObject */
+ private $serverContainer;
+
+ /** @var IAppManager|MockObject */
+ private $appManager;
+
+ private ActionProviderStore $actionProviderStore;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->serverContainer = $this->createMock(IServerContainer::class);
+ $this->appManager = $this->createMock(AppManager::class);
+ $logger = $this->createMock(LoggerInterface::class);
+
+ $this->actionProviderStore = new ActionProviderStore($this->serverContainer, $this->appManager, $logger);
+ }
+
+ public function testGetProviders(): void {
+ $user = $this->createMock(IUser::class);
+ $provider1 = $this->createMock(ProfileProvider::class);
+ $provider2 = $this->createMock(LocalTimeProvider::class);
+ $provider3 = $this->createMock(EMailProvider::class);
+ $provider4 = $this->createMock(IProvider::class);
+
+ $this->appManager->expects($this->once())
+ ->method('getEnabledAppsForUser')
+ ->with($user)
+ ->willReturn(['contacts']);
+ $this->appManager->expects($this->once())
+ ->method('getAppInfo')
+ ->with('contacts')
+ ->willReturn([
+ 'contactsmenu' => [
+ 'OCA\Contacts\Provider1',
+ ],
+ ]);
+ $this->serverContainer->expects($this->exactly(4))
+ ->method('get')
+ ->willReturnMap([
+ [ProfileProvider::class, $provider1],
+ [LocalTimeProvider::class, $provider2],
+ [EMailProvider::class, $provider3],
+ ['OCA\Contacts\Provider1', $provider4]
+ ]);
+
+ $providers = $this->actionProviderStore->getProviders($user);
+
+ $this->assertCount(4, $providers);
+ $this->assertInstanceOf(ProfileProvider::class, $providers[0]);
+ $this->assertInstanceOf(LocalTimeProvider::class, $providers[1]);
+ $this->assertInstanceOf(EMailProvider::class, $providers[2]);
+ }
+
+ public function testGetProvidersOfAppWithIncompleInfo(): void {
+ $user = $this->createMock(IUser::class);
+ $provider1 = $this->createMock(ProfileProvider::class);
+ $provider2 = $this->createMock(LocalTimeProvider::class);
+ $provider3 = $this->createMock(EMailProvider::class);
+
+ $this->appManager->expects($this->once())
+ ->method('getEnabledAppsForUser')
+ ->with($user)
+ ->willReturn(['contacts']);
+ $this->appManager->expects($this->once())
+ ->method('getAppInfo')
+ ->with('contacts')
+ ->willReturn([/* Empty info.xml */]);
+ $this->serverContainer->expects($this->exactly(3))
+ ->method('get')
+ ->willReturnMap([
+ [ProfileProvider::class, $provider1],
+ [LocalTimeProvider::class, $provider2],
+ [EMailProvider::class, $provider3],
+ ]);
+
+ $providers = $this->actionProviderStore->getProviders($user);
+
+ $this->assertCount(3, $providers);
+ $this->assertInstanceOf(ProfileProvider::class, $providers[0]);
+ $this->assertInstanceOf(LocalTimeProvider::class, $providers[1]);
+ $this->assertInstanceOf(EMailProvider::class, $providers[2]);
+ }
+
+
+ public function testGetProvidersWithQueryException(): void {
+ $this->expectException(\Exception::class);
+
+ $user = $this->createMock(IUser::class);
+ $this->appManager->expects($this->once())
+ ->method('getEnabledAppsForUser')
+ ->with($user)
+ ->willReturn([]);
+ $this->serverContainer->expects($this->once())
+ ->method('get')
+ ->willThrowException(new QueryException());
+
+ $this->actionProviderStore->getProviders($user);
+ }
+}
diff --git a/tests/lib/Contacts/ContactsMenu/Actions/LinkActionTest.php b/tests/lib/Contacts/ContactsMenu/Actions/LinkActionTest.php
new file mode 100644
index 00000000000..5e2b416a66b
--- /dev/null
+++ b/tests/lib/Contacts/ContactsMenu/Actions/LinkActionTest.php
@@ -0,0 +1,90 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Tests\Contacts\ContactsMenu\Actions;
+
+use OC\Contacts\ContactsMenu\Actions\LinkAction;
+use Test\TestCase;
+
+class LinkActionTest extends TestCase {
+ private LinkAction $action;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->action = new LinkAction();
+ }
+
+ public function testSetIcon(): void {
+ $icon = 'icon-test';
+
+ $this->action->setIcon($icon);
+ $json = $this->action->jsonSerialize();
+
+ $this->assertArrayHasKey('icon', $json);
+ $this->assertEquals($json['icon'], $icon);
+ }
+
+ public function testGetSetName(): void {
+ $name = 'Jane Doe';
+
+ $this->assertEmpty($this->action->getName());
+ $this->action->setName($name);
+ $this->assertEquals($name, $this->action->getName());
+ }
+
+ public function testGetSetPriority(): void {
+ $prio = 50;
+
+ $this->assertEquals(10, $this->action->getPriority());
+ $this->action->setPriority($prio);
+ $this->assertEquals($prio, $this->action->getPriority());
+ }
+
+ public function testSetHref(): void {
+ $this->action->setHref('/some/url');
+
+ $json = $this->action->jsonSerialize();
+ $this->assertArrayHasKey('hyperlink', $json);
+ $this->assertEquals('/some/url', $json['hyperlink']);
+ }
+
+ public function testJsonSerialize(): void {
+ $this->action->setIcon('icon-contacts');
+ $this->action->setName('Nickie Works');
+ $this->action->setPriority(33);
+ $this->action->setHref('example.com');
+ $this->action->setAppId('contacts');
+ $expected = [
+ 'title' => 'Nickie Works',
+ 'icon' => 'icon-contacts',
+ 'hyperlink' => 'example.com',
+ 'appId' => 'contacts',
+ ];
+
+ $json = $this->action->jsonSerialize();
+
+ $this->assertEquals($expected, $json);
+ }
+
+ public function testJsonSerializeNoAppName(): void {
+ $this->action->setIcon('icon-contacts');
+ $this->action->setName('Nickie Works');
+ $this->action->setPriority(33);
+ $this->action->setHref('example.com');
+ $expected = [
+ 'title' => 'Nickie Works',
+ 'icon' => 'icon-contacts',
+ 'hyperlink' => 'example.com',
+ 'appId' => '',
+ ];
+
+ $json = $this->action->jsonSerialize();
+
+ $this->assertEquals($expected, $json);
+ }
+}
diff --git a/tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php b/tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php
new file mode 100644
index 00000000000..9097ee779d2
--- /dev/null
+++ b/tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php
@@ -0,0 +1,1068 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Tests\Contacts\ContactsMenu;
+
+use OC\Contacts\ContactsMenu\ContactsStore;
+use OC\KnownUser\KnownUserService;
+use OC\Profile\ProfileManager;
+use OCA\UserStatus\Db\UserStatus;
+use OCA\UserStatus\Service\StatusService;
+use OCP\Contacts\IManager;
+use OCP\IConfig;
+use OCP\IGroupManager;
+use OCP\IURLGenerator;
+use OCP\IUser;
+use OCP\IUserManager;
+use OCP\L10N\IFactory as IL10NFactory;
+use PHPUnit\Framework\MockObject\MockObject;
+use Test\TestCase;
+
+class ContactsStoreTest extends TestCase {
+ private ContactsStore $contactsStore;
+ private StatusService|MockObject $statusService;
+ /** @var IManager|MockObject */
+ private $contactsManager;
+ /** @var ProfileManager */
+ private $profileManager;
+ /** @var IUserManager|MockObject */
+ private $userManager;
+ /** @var IURLGenerator */
+ private $urlGenerator;
+ /** @var IGroupManager|MockObject */
+ private $groupManager;
+ /** @var IConfig|MockObject */
+ private $config;
+ /** @var KnownUserService|MockObject */
+ private $knownUserService;
+ /** @var IL10NFactory */
+ private $l10nFactory;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->contactsManager = $this->createMock(IManager::class);
+ $this->statusService = $this->createMock(StatusService::class);
+ $this->userManager = $this->createMock(IUserManager::class);
+ $this->profileManager = $this->createMock(ProfileManager::class);
+ $this->urlGenerator = $this->createMock(IURLGenerator::class);
+ $this->groupManager = $this->createMock(IGroupManager::class);
+ $this->config = $this->createMock(IConfig::class);
+ $this->knownUserService = $this->createMock(KnownUserService::class);
+ $this->l10nFactory = $this->createMock(IL10NFactory::class);
+ $this->contactsStore = new ContactsStore(
+ $this->contactsManager,
+ $this->statusService,
+ $this->config,
+ $this->profileManager,
+ $this->userManager,
+ $this->urlGenerator,
+ $this->groupManager,
+ $this->knownUserService,
+ $this->l10nFactory,
+ );
+ }
+
+ public function testGetContactsWithoutFilter(): void {
+ /** @var IUser|MockObject $user */
+ $user = $this->createMock(IUser::class);
+ $this->contactsManager->expects($this->once())
+ ->method('search')
+ ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
+ ->willReturn([
+ [
+ 'UID' => 123,
+ ],
+ [
+ 'UID' => 567,
+ 'FN' => 'Darren Roner',
+ 'EMAIL' => [
+ 'darren@roner.au'
+ ],
+ ],
+ ]);
+ $user->expects($this->exactly(2))
+ ->method('getUID')
+ ->willReturn('user123');
+
+ $entries = $this->contactsStore->getContacts($user, '');
+
+ $this->assertCount(2, $entries);
+ $this->assertEquals([
+ 'darren@roner.au'
+ ], $entries[1]->getEMailAddresses());
+ }
+
+ public function testGetContactsHidesOwnEntry(): void {
+ /** @var IUser|MockObject $user */
+ $user = $this->createMock(IUser::class);
+ $this->contactsManager->expects($this->once())
+ ->method('search')
+ ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
+ ->willReturn([
+ [
+ 'UID' => 'user123',
+ ],
+ [
+ 'UID' => 567,
+ 'FN' => 'Darren Roner',
+ 'EMAIL' => [
+ 'darren@roner.au'
+ ],
+ ],
+ ]);
+ $user->expects($this->exactly(2))
+ ->method('getUID')
+ ->willReturn('user123');
+
+ $entries = $this->contactsStore->getContacts($user, '');
+
+ $this->assertCount(1, $entries);
+ }
+
+ public function testGetContactsWithoutBinaryImage(): void {
+ /** @var IUser|MockObject $user */
+ $user = $this->createMock(IUser::class);
+ $this->urlGenerator->expects($this->any())
+ ->method('linkToRouteAbsolute')
+ ->with('core.GuestAvatar.getAvatar', $this->anything())
+ ->willReturn('https://urlToNcAvatar.test');
+ $this->contactsManager->expects($this->once())
+ ->method('search')
+ ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
+ ->willReturn([
+ [
+ 'UID' => 123,
+ ],
+ [
+ 'UID' => 567,
+ 'FN' => 'Darren Roner',
+ 'EMAIL' => [
+ 'darren@roner.au'
+ ],
+ 'PHOTO' => base64_encode('photophotophoto'),
+ ],
+ ]);
+ $user->expects($this->exactly(2))
+ ->method('getUID')
+ ->willReturn('user123');
+
+ $entries = $this->contactsStore->getContacts($user, '');
+
+ $this->assertCount(2, $entries);
+ $this->assertSame('https://urlToNcAvatar.test', $entries[1]->getAvatar());
+ }
+
+ public function testGetContactsWithoutAvatarURI(): void {
+ /** @var IUser|MockObject $user */
+ $user = $this->createMock(IUser::class);
+ $this->contactsManager->expects($this->once())
+ ->method('search')
+ ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
+ ->willReturn([
+ [
+ 'UID' => 123,
+ ],
+ [
+ 'UID' => 567,
+ 'FN' => 'Darren Roner',
+ 'EMAIL' => [
+ 'darren@roner.au'
+ ],
+ 'PHOTO' => 'VALUE=uri:https://photo',
+ ],
+ ]);
+ $user->expects($this->exactly(2))
+ ->method('getUID')
+ ->willReturn('user123');
+
+ $entries = $this->contactsStore->getContacts($user, '');
+
+ $this->assertCount(2, $entries);
+ $this->assertEquals('https://photo', $entries[1]->getAvatar());
+ }
+
+ public function testGetContactsWhenUserIsInExcludeGroups(): 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'],
+ ['core', 'shareapi_exclude_groups', 'no', 'yes'],
+ ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
+ ['core', 'shareapi_exclude_groups_list', '', '["group1", "group5", "group6"]'],
+ ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
+ ]);
+
+ /** @var IUser|MockObject $currentUser */
+ $currentUser = $this->createMock(IUser::class);
+ $currentUser->expects($this->exactly(2))
+ ->method('getUID')
+ ->willReturn('user001');
+
+ $this->groupManager->expects($this->once())
+ ->method('getUserGroupIds')
+ ->with($this->equalTo($currentUser))
+ ->willReturn(['group1', 'group2', 'group3']);
+
+
+ $this->contactsManager->expects($this->once())
+ ->method('search')
+ ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
+ ->willReturn([
+ [
+ 'UID' => 'user123',
+ 'isLocalSystemBook' => true
+ ],
+ [
+ 'UID' => 'user12345',
+ 'isLocalSystemBook' => true
+ ],
+ ]);
+
+
+ $entries = $this->contactsStore->getContacts($currentUser, '');
+
+ $this->assertCount(0, $entries);
+ }
+
+ public function testGetContactsOnlyShareIfInTheSameGroup(): 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'],
+ ['core', 'shareapi_exclude_groups', 'no', 'no'],
+ ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
+ ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
+ ]);
+
+ /** @var IUser|MockObject $currentUser */
+ $currentUser = $this->createMock(IUser::class);
+ $currentUser->expects($this->exactly(2))
+ ->method('getUID')
+ ->willReturn('user001');
+
+ $user1 = $this->createMock(IUser::class);
+ $user2 = $this->createMock(IUser::class);
+ $user3 = $this->createMock(IUser::class);
+
+ $calls = [
+ [[$currentUser], ['group1', 'group2', 'group3']],
+ [[$user1], ['group1']],
+ [[$user2], ['group2', 'group3']],
+ [[$user3], ['group8', 'group9']],
+ ];
+ $this->groupManager->expects($this->exactly(4))
+ ->method('getUserGroupIds')
+ ->willReturnCallback(function () use (&$calls): array {
+ $expected = array_shift($calls);
+ $this->assertEquals($expected[0], func_get_args());
+ return $expected[1];
+ });
+
+ $this->userManager->expects($this->exactly(3))
+ ->method('get')
+ ->willReturnMap([
+ ['user1', $user1],
+ ['user2', $user2],
+ ['user3', $user3],
+ ]);
+
+ $this->contactsManager->expects($this->once())
+ ->method('search')
+ ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
+ ->willReturn([
+ [
+ 'UID' => 'user1',
+ 'isLocalSystemBook' => true
+ ],
+ [
+ 'UID' => 'user2',
+ 'isLocalSystemBook' => true
+ ],
+ [
+ 'UID' => 'user3',
+ 'isLocalSystemBook' => true
+ ],
+ [
+ 'UID' => 'contact',
+ ],
+ ]);
+
+ $entries = $this->contactsStore->getContacts($currentUser, '');
+
+ $this->assertCount(3, $entries);
+ $this->assertEquals('user1', $entries[0]->getProperty('UID'));
+ $this->assertEquals('user2', $entries[1]->getProperty('UID'));
+ $this->assertEquals('contact', $entries[2]->getProperty('UID'));
+ }
+
+ public function testGetContactsOnlyEnumerateIfInTheSameGroup(): 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'],
+ ['core', 'shareapi_exclude_groups', 'no', 'no'],
+ ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
+ ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
+ ]);
+
+ /** @var IUser|MockObject $currentUser */
+ $currentUser = $this->createMock(IUser::class);
+ $currentUser->expects($this->exactly(2))
+ ->method('getUID')
+ ->willReturn('user001');
+
+ $user1 = $this->createMock(IUser::class);
+ $user2 = $this->createMock(IUser::class);
+ $user3 = $this->createMock(IUser::class);
+
+ $calls = [
+ [[$currentUser], ['group1', 'group2', 'group3']],
+ [[$user1], ['group1']],
+ [[$user2], ['group2', 'group3']],
+ [[$user3], ['group8', 'group9']],
+ ];
+ $this->groupManager->expects($this->exactly(4))
+ ->method('getUserGroupIds')
+ ->willReturnCallback(function () use (&$calls): array {
+ $expected = array_shift($calls);
+ $this->assertEquals($expected[0], func_get_args());
+ return $expected[1];
+ });
+
+ $this->userManager->expects($this->exactly(3))
+ ->method('get')
+ ->willReturnMap([
+ ['user1', $user1],
+ ['user2', $user2],
+ ['user3', $user3],
+ ]);
+
+ $this->contactsManager->expects($this->once())
+ ->method('search')
+ ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
+ ->willReturn([
+ [
+ 'UID' => 'user1',
+ 'isLocalSystemBook' => true
+ ],
+ [
+ 'UID' => 'user2',
+ 'isLocalSystemBook' => true
+ ],
+ [
+ 'UID' => 'user3',
+ 'isLocalSystemBook' => true
+ ],
+ [
+ 'UID' => 'contact',
+ ],
+ ]);
+
+ $entries = $this->contactsStore->getContacts($currentUser, '');
+
+ $this->assertCount(3, $entries);
+ $this->assertEquals('user1', $entries[0]->getProperty('UID'));
+ $this->assertEquals('user2', $entries[1]->getProperty('UID'));
+ $this->assertEquals('contact', $entries[2]->getProperty('UID'));
+ }
+
+ public function testGetContactsOnlyEnumerateIfPhoneBookMatch(): 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', 'yes'],
+ ['core', 'shareapi_exclude_groups', 'no', 'no'],
+ ['core', 'shareapi_only_share_with_group_members', 'no', 'no'],
+ ]);
+
+ /** @var IUser|MockObject $currentUser */
+ $currentUser = $this->createMock(IUser::class);
+ $currentUser->expects($this->exactly(2))
+ ->method('getUID')
+ ->willReturn('user001');
+
+ $this->groupManager->expects($this->once())
+ ->method('getUserGroupIds')
+ ->with($this->equalTo($currentUser))
+ ->willReturn(['group1', 'group2', 'group3']);
+
+ $this->knownUserService->method('isKnownToUser')
+ ->willReturnMap([
+ ['user001', 'user1', true],
+ ['user001', 'user2', true],
+ ['user001', 'user3', false],
+ ]);
+
+ $this->contactsManager->expects($this->once())
+ ->method('search')
+ ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
+ ->willReturn([
+ [
+ 'UID' => 'user1',
+ 'isLocalSystemBook' => true
+ ],
+ [
+ 'UID' => 'user2',
+ 'isLocalSystemBook' => true
+ ],
+ [
+ 'UID' => 'user3',
+ 'isLocalSystemBook' => true
+ ],
+ [
+ 'UID' => 'contact',
+ ],
+ ]);
+
+ $entries = $this->contactsStore->getContacts($currentUser, '');
+
+ $this->assertCount(3, $entries);
+ $this->assertEquals('user1', $entries[0]->getProperty('UID'));
+ $this->assertEquals('user2', $entries[1]->getProperty('UID'));
+ $this->assertEquals('contact', $entries[2]->getProperty('UID'));
+ }
+
+ public function testGetContactsOnlyEnumerateIfPhoneBookMatchWithOwnGroupsOnly(): 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', 'yes'],
+ ['core', 'shareapi_exclude_groups', 'no', 'no'],
+ ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
+ ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
+ ]);
+
+ /** @var IUser|MockObject $currentUser */
+ $currentUser = $this->createMock(IUser::class);
+ $currentUser->expects($this->exactly(2))
+ ->method('getUID')
+ ->willReturn('user001');
+
+ $user1 = $this->createMock(IUser::class);
+ $user2 = $this->createMock(IUser::class);
+ $user3 = $this->createMock(IUser::class);
+
+ $calls = [
+ [[$currentUser], ['group1', 'group2', 'group3']],
+ [[$user1], ['group1']],
+ [[$user2], ['group2', 'group3']],
+ [[$user3], ['group8', 'group9']],
+ ];
+ $this->groupManager->expects($this->exactly(4))
+ ->method('getUserGroupIds')
+ ->willReturnCallback(function () use (&$calls): array {
+ $expected = array_shift($calls);
+ $this->assertEquals($expected[0], func_get_args());
+ return $expected[1];
+ });
+
+ $this->userManager->expects($this->exactly(3))
+ ->method('get')
+ ->willReturnMap([
+ ['user1', $user1],
+ ['user2', $user2],
+ ['user3', $user3],
+ ]);
+
+ $this->knownUserService->method('isKnownToUser')
+ ->willReturnMap([
+ ['user001', 'user1', true],
+ ['user001', 'user2', true],
+ ['user001', 'user3', true],
+ ]);
+
+ $this->contactsManager->expects($this->once())
+ ->method('search')
+ ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
+ ->willReturn([
+ [
+ 'UID' => 'user1',
+ 'isLocalSystemBook' => true
+ ],
+ [
+ 'UID' => 'user2',
+ 'isLocalSystemBook' => true
+ ],
+ [
+ 'UID' => 'user3',
+ 'isLocalSystemBook' => true
+ ],
+ [
+ 'UID' => 'contact',
+ ],
+ ]);
+
+ $entries = $this->contactsStore->getContacts($currentUser, '');
+
+ $this->assertCount(3, $entries);
+ $this->assertEquals('user1', $entries[0]->getProperty('UID'));
+ $this->assertEquals('user2', $entries[1]->getProperty('UID'));
+ $this->assertEquals('contact', $entries[2]->getProperty('UID'));
+ }
+
+ public function testGetContactsOnlyEnumerateIfPhoneBookOrSameGroup(): 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', 'yes'],
+ ['core', 'shareapi_exclude_groups', 'no', 'no'],
+ ['core', 'shareapi_only_share_with_group_members', 'no', 'no'],
+ ]);
+
+ /** @var IUser|MockObject $currentUser */
+ $currentUser = $this->createMock(IUser::class);
+ $currentUser->expects($this->exactly(2))
+ ->method('getUID')
+ ->willReturn('user001');
+
+ $user1 = $this->createMock(IUser::class);
+
+ $calls = [
+ [[$currentUser], ['group1', 'group2', 'group3']],
+ [[$user1], ['group1']],
+ ];
+ $this->groupManager->expects($this->exactly(2))
+ ->method('getUserGroupIds')
+ ->willReturnCallback(function () use (&$calls): array {
+ $expected = array_shift($calls);
+ $this->assertEquals($expected[0], func_get_args());
+ return $expected[1];
+ });
+
+ $this->userManager->expects($this->once())
+ ->method('get')
+ ->with('user1')
+ ->willReturn($user1);
+
+ $this->knownUserService->method('isKnownToUser')
+ ->willReturnMap([
+ ['user001', 'user1', false],
+ ['user001', 'user2', true],
+ ['user001', 'user3', true],
+ ]);
+
+ $this->contactsManager->expects($this->once())
+ ->method('search')
+ ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
+ ->willReturn([
+ [
+ 'UID' => 'user1',
+ 'isLocalSystemBook' => true
+ ],
+ [
+ 'UID' => 'user2',
+ 'isLocalSystemBook' => true
+ ],
+ [
+ 'UID' => 'user3',
+ 'isLocalSystemBook' => true
+ ],
+ [
+ 'UID' => 'contact',
+ ],
+ ]);
+
+ $entries = $this->contactsStore->getContacts($currentUser, '');
+
+ $this->assertCount(4, $entries);
+ $this->assertEquals('user1', $entries[0]->getProperty('UID'));
+ $this->assertEquals('user2', $entries[1]->getProperty('UID'));
+ $this->assertEquals('user3', $entries[2]->getProperty('UID'));
+ $this->assertEquals('contact', $entries[3]->getProperty('UID'));
+ }
+
+ public function testGetContactsOnlyEnumerateIfPhoneBookOrSameGroupInOwnGroupsOnly(): 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', 'yes'],
+ ['core', 'shareapi_exclude_groups', 'no', 'no'],
+ ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
+ ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
+ ]);
+
+ /** @var IUser|MockObject $currentUser */
+ $currentUser = $this->createMock(IUser::class);
+ $currentUser->expects($this->exactly(2))
+ ->method('getUID')
+ ->willReturn('user001');
+
+ $user1 = $this->createMock(IUser::class);
+ $user2 = $this->createMock(IUser::class);
+ $user3 = $this->createMock(IUser::class);
+
+ $calls = [
+ [[$currentUser], ['group1', 'group2', 'group3']],
+ [[$user1], ['group1']],
+ [[$user2], ['group2', 'group3']],
+ [[$user3], ['group8', 'group9']],
+ ];
+ $this->groupManager->expects($this->exactly(4))
+ ->method('getUserGroupIds')
+ ->willReturnCallback(function () use (&$calls): array {
+ $expected = array_shift($calls);
+ $this->assertEquals($expected[0], func_get_args());
+ return $expected[1];
+ });
+
+ $this->userManager->expects($this->exactly(3))
+ ->method('get')
+ ->willReturnMap([
+ ['user1', $user1],
+ ['user2', $user2],
+ ['user3', $user3],
+ ]);
+
+ $this->knownUserService->method('isKnownToUser')
+ ->willReturnMap([
+ ['user001', 'user1', false],
+ ['user001', 'user2', true],
+ ['user001', 'user3', true],
+ ]);
+
+ $this->contactsManager->expects($this->once())
+ ->method('search')
+ ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
+ ->willReturn([
+ [
+ 'UID' => 'user1',
+ 'isLocalSystemBook' => true
+ ],
+ [
+ 'UID' => 'user2',
+ 'isLocalSystemBook' => true
+ ],
+ [
+ 'UID' => 'user3',
+ 'isLocalSystemBook' => true
+ ],
+ [
+ 'UID' => 'contact',
+ ],
+ ]);
+
+ $entries = $this->contactsStore->getContacts($currentUser, '');
+
+ $this->assertCount(3, $entries);
+ $this->assertEquals('user1', $entries[0]->getProperty('UID'));
+ $this->assertEquals('user2', $entries[1]->getProperty('UID'));
+ $this->assertEquals('contact', $entries[2]->getProperty('UID'));
+ }
+
+ public function testGetContactsWithFilter(): void {
+ $this->config
+ ->method('getAppValue')
+ ->willReturnMap([
+ ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'no'],
+ ['core', 'shareapi_restrict_user_enumeration_full_match', 'yes', 'yes'],
+ ]);
+
+ /** @var IUser|MockObject $user */
+ $user = $this->createMock(IUser::class);
+ $this->contactsManager->expects($this->any())
+ ->method('search')
+ ->willReturn([
+ [
+ 'UID' => 'a567',
+ 'FN' => 'Darren Roner',
+ 'EMAIL' => [
+ 'darren@roner.au',
+ ],
+ 'isLocalSystemBook' => true,
+ ],
+ [
+ 'UID' => 'john',
+ 'FN' => 'John Doe',
+ 'EMAIL' => [
+ 'john@example.com',
+ ],
+ 'isLocalSystemBook' => true,
+ ],
+ [
+ 'FN' => 'Anne D',
+ 'EMAIL' => [
+ 'anne@example.com',
+ ],
+ 'isLocalSystemBook' => false,
+ ],
+ ]);
+ $user->expects($this->any())
+ ->method('getUID')
+ ->willReturn('user123');
+
+ // Complete match on UID should match
+ $entry = $this->contactsStore->getContacts($user, 'a567');
+ $this->assertSame(2, count($entry));
+ $this->assertEquals([
+ 'darren@roner.au'
+ ], $entry[0]->getEMailAddresses());
+
+ // Partial match on UID should not match
+ $entry = $this->contactsStore->getContacts($user, 'a56');
+ $this->assertSame(1, count($entry));
+ $this->assertEquals([
+ 'anne@example.com'
+ ], $entry[0]->getEMailAddresses());
+
+ // Complete match on email should match
+ $entry = $this->contactsStore->getContacts($user, 'john@example.com');
+ $this->assertSame(2, count($entry));
+ $this->assertEquals([
+ 'john@example.com'
+ ], $entry[0]->getEMailAddresses());
+ $this->assertEquals([
+ 'anne@example.com'
+ ], $entry[1]->getEMailAddresses());
+
+ // Partial match on email should not match
+ $entry = $this->contactsStore->getContacts($user, 'john@example.co');
+ $this->assertSame(1, count($entry));
+ $this->assertEquals([
+ 'anne@example.com'
+ ], $entry[0]->getEMailAddresses());
+
+ // Match on FN should not match
+ $entry = $this->contactsStore->getContacts($user, 'Darren Roner');
+ $this->assertSame(1, count($entry));
+ $this->assertEquals([
+ 'anne@example.com'
+ ], $entry[0]->getEMailAddresses());
+
+ // Don't filter users in local addressbook
+ $entry = $this->contactsStore->getContacts($user, 'Anne D');
+ $this->assertSame(1, count($entry));
+ $this->assertEquals([
+ 'anne@example.com'
+ ], $entry[0]->getEMailAddresses());
+ }
+
+ public function testGetContactsWithFilterWithoutFullMatch(): void {
+ $this->config
+ ->method('getAppValue')
+ ->willReturnMap([
+ ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'no'],
+ ['core', 'shareapi_restrict_user_enumeration_full_match', 'yes', 'no'],
+ ]);
+
+ /** @var IUser|MockObject $user */
+ $user = $this->createMock(IUser::class);
+ $this->contactsManager->expects($this->any())
+ ->method('search')
+ ->willReturn([
+ [
+ 'UID' => 'a567',
+ 'FN' => 'Darren Roner',
+ 'EMAIL' => [
+ 'darren@roner.au',
+ ],
+ 'isLocalSystemBook' => true,
+ ],
+ [
+ 'UID' => 'john',
+ 'FN' => 'John Doe',
+ 'EMAIL' => [
+ 'john@example.com',
+ ],
+ 'isLocalSystemBook' => true,
+ ],
+ [
+ 'FN' => 'Anne D',
+ 'EMAIL' => [
+ 'anne@example.com',
+ ],
+ 'isLocalSystemBook' => false,
+ ],
+ ]);
+ $user->expects($this->any())
+ ->method('getUID')
+ ->willReturn('user123');
+
+ // Complete match on UID should not match
+ $entry = $this->contactsStore->getContacts($user, 'a567');
+ $this->assertSame(1, count($entry));
+ $this->assertEquals([
+ 'anne@example.com'
+ ], $entry[0]->getEMailAddresses());
+
+ // Partial match on UID should not match
+ $entry = $this->contactsStore->getContacts($user, 'a56');
+ $this->assertSame(1, count($entry));
+ $this->assertEquals([
+ 'anne@example.com'
+ ], $entry[0]->getEMailAddresses());
+
+ // Complete match on email should not match
+ $entry = $this->contactsStore->getContacts($user, 'john@example.com');
+ $this->assertSame(1, count($entry));
+ $this->assertEquals([
+ 'anne@example.com'
+ ], $entry[0]->getEMailAddresses());
+
+ // Partial match on email should not match
+ $entry = $this->contactsStore->getContacts($user, 'john@example.co');
+ $this->assertSame(1, count($entry));
+ $this->assertEquals([
+ 'anne@example.com'
+ ], $entry[0]->getEMailAddresses());
+
+ // Match on FN should not match
+ $entry = $this->contactsStore->getContacts($user, 'Darren Roner');
+ $this->assertSame(1, count($entry));
+ $this->assertEquals([
+ 'anne@example.com'
+ ], $entry[0]->getEMailAddresses());
+
+ // Don't filter users in local addressbook
+ $entry = $this->contactsStore->getContacts($user, 'Anne D');
+ $this->assertSame(1, count($entry));
+ $this->assertEquals([
+ 'anne@example.com'
+ ], $entry[0]->getEMailAddresses());
+ }
+
+ public function testFindOneUser(): 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'],
+ ['core', 'shareapi_restrict_user_enumeration_full_match', 'yes', 'yes'],
+ ['core', 'shareapi_exclude_groups', 'no', 'yes'],
+ ['core', 'shareapi_exclude_groups_list', '', ''],
+ ['core', 'shareapi_only_share_with_group_members', 'no', 'no'],
+ ]);
+
+ /** @var IUser|MockObject $user */
+ $user = $this->createMock(IUser::class);
+ $this->contactsManager->expects($this->once())
+ ->method('search')
+ ->with($this->equalTo('a567'), $this->equalTo(['UID']))
+ ->willReturn([
+ [
+ 'UID' => 123,
+ 'isLocalSystemBook' => false
+ ],
+ [
+ 'UID' => 'a567',
+ 'FN' => 'Darren Roner',
+ 'EMAIL' => [
+ 'darren@roner.au'
+ ],
+ 'isLocalSystemBook' => true
+ ],
+ ]);
+ $user->expects($this->any())
+ ->method('getUID')
+ ->willReturn('user123');
+
+ $entry = $this->contactsStore->findOne($user, 0, 'a567');
+
+ $this->assertEquals([
+ 'darren@roner.au'
+ ], $entry->getEMailAddresses());
+ }
+
+ public function testFindOneEMail(): void {
+ /** @var IUser|MockObject $user */
+ $user = $this->createMock(IUser::class);
+ $this->contactsManager->expects($this->once())
+ ->method('search')
+ ->with($this->equalTo('darren@roner.au'), $this->equalTo(['EMAIL']))
+ ->willReturn([
+ [
+ 'UID' => 123,
+ 'isLocalSystemBook' => false
+ ],
+ [
+ 'UID' => 'a567',
+ 'FN' => 'Darren Roner',
+ 'EMAIL' => [
+ 'darren@roner.au'
+ ],
+ 'isLocalSystemBook' => false
+ ],
+ ]);
+ $user->expects($this->any())
+ ->method('getUID')
+ ->willReturn('user123');
+
+ $entry = $this->contactsStore->findOne($user, 4, 'darren@roner.au');
+
+ $this->assertEquals([
+ 'darren@roner.au'
+ ], $entry->getEMailAddresses());
+ }
+
+ public function testFindOneNotSupportedType(): void {
+ /** @var IUser|MockObject $user */
+ $user = $this->createMock(IUser::class);
+
+ $entry = $this->contactsStore->findOne($user, 42, 'darren@roner.au');
+
+ $this->assertEquals(null, $entry);
+ }
+
+ public function testFindOneNoMatches(): void {
+ /** @var IUser|MockObject $user */
+ $user = $this->createMock(IUser::class);
+ $this->contactsManager->expects($this->once())
+ ->method('search')
+ ->with($this->equalTo('a567'), $this->equalTo(['UID']))
+ ->willReturn([
+ [
+ 'UID' => 123,
+ 'isLocalSystemBook' => false
+ ],
+ [
+ 'UID' => 'a567',
+ 'FN' => 'Darren Roner',
+ 'EMAIL' => [
+ 'darren@roner.au123'
+ ],
+ 'isLocalSystemBook' => false
+ ],
+ ]);
+ $user->expects($this->never())
+ ->method('getUID');
+
+ $entry = $this->contactsStore->findOne($user, 0, 'a567');
+
+ $this->assertEquals(null, $entry);
+ }
+
+ public function testGetRecentStatusFirst(): void {
+ $user = $this->createMock(IUser::class);
+ $status1 = new UserStatus();
+ $status1->setUserId('user1');
+ $status2 = new UserStatus();
+ $status2->setUserId('user2');
+ $this->statusService->expects(self::once())
+ ->method('findAllRecentStatusChanges')
+ ->willReturn([
+ $status1,
+ $status2,
+ ]);
+ $user1 = $this->createMock(IUser::class);
+ $user1->method('getCloudId')->willReturn('user1@localcloud');
+ $user2 = $this->createMock(IUser::class);
+ $user2->method('getCloudId')->willReturn('user2@localcloud');
+ $this->userManager->expects(self::exactly(2))
+ ->method('get')
+ ->willReturnCallback(function ($uid) use ($user1, $user2) {
+ return match ($uid) {
+ 'user1' => $user1,
+ 'user2' => $user2,
+ };
+ });
+ $this->contactsManager
+ ->expects(self::exactly(3))
+ ->method('search')
+ ->willReturnCallback(function ($uid, $searchProps, $options) {
+ return match ([$uid, $options['limit'] ?? null]) {
+ ['user1@localcloud', 1] => [
+ [
+ 'UID' => 'user1',
+ 'URI' => 'user1.vcf',
+ ],
+ ],
+ ['user2@localcloud' => [], 1], // Simulate not found
+ ['', 4] => [
+ [
+ 'UID' => 'contact1',
+ 'URI' => 'contact1.vcf',
+ ],
+ [
+ 'UID' => 'contact2',
+ 'URI' => 'contact2.vcf',
+ ],
+ ],
+ default => [],
+ };
+ });
+
+ $contacts = $this->contactsStore->getContacts(
+ $user,
+ null,
+ 5,
+ );
+
+ self::assertCount(3, $contacts);
+ self::assertEquals('user1', $contacts[0]->getProperty('UID'));
+ self::assertEquals('contact1', $contacts[1]->getProperty('UID'));
+ self::assertEquals('contact2', $contacts[2]->getProperty('UID'));
+ }
+
+ public function testPaginateRecentStatus(): void {
+ $user = $this->createMock(IUser::class);
+ $status1 = new UserStatus();
+ $status1->setUserId('user1');
+ $status2 = new UserStatus();
+ $status2->setUserId('user2');
+ $status3 = new UserStatus();
+ $status3->setUserId('user3');
+ $this->statusService->expects(self::never())
+ ->method('findAllRecentStatusChanges');
+ $this->contactsManager
+ ->expects(self::exactly(2))
+ ->method('search')
+ ->willReturnCallback(function ($uid, $searchProps, $options) {
+ return match ([$uid, $options['limit'] ?? null, $options['offset'] ?? null]) {
+ ['', 2, 0] => [
+ [
+ 'UID' => 'contact1',
+ 'URI' => 'contact1.vcf',
+ ],
+ [
+ 'UID' => 'contact2',
+ 'URI' => 'contact2.vcf',
+ ],
+ ],
+ ['', 2, 3] => [
+ [
+ 'UID' => 'contact3',
+ 'URI' => 'contact3.vcf',
+ ],
+ ],
+ default => [],
+ };
+ });
+
+ $page1 = $this->contactsStore->getContacts(
+ $user,
+ null,
+ 2,
+ 0,
+ );
+ $page2 = $this->contactsStore->getContacts(
+ $user,
+ null,
+ 2,
+ 3,
+ );
+
+ self::assertCount(2, $page1);
+ self::assertCount(1, $page2);
+ }
+}
diff --git a/tests/lib/Contacts/ContactsMenu/EntryTest.php b/tests/lib/Contacts/ContactsMenu/EntryTest.php
new file mode 100644
index 00000000000..15f5b60b948
--- /dev/null
+++ b/tests/lib/Contacts/ContactsMenu/EntryTest.php
@@ -0,0 +1,104 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Tests\Contacts\ContactsMenu;
+
+use OC\Contacts\ContactsMenu\Actions\LinkAction;
+use OC\Contacts\ContactsMenu\Entry;
+use Test\TestCase;
+
+class EntryTest extends TestCase {
+ private Entry $entry;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->entry = new Entry();
+ }
+
+ public function testSetId(): void {
+ $this->entry->setId(123);
+ $this->addToAssertionCount(1);
+ }
+
+ public function testSetGetFullName(): void {
+ $fn = 'Danette Chaille';
+ $this->assertEquals('', $this->entry->getFullName());
+ $this->entry->setFullName($fn);
+ $this->assertEquals($fn, $this->entry->getFullName());
+ }
+
+ public function testAddGetEMailAddresses(): void {
+ $this->assertEmpty($this->entry->getEMailAddresses());
+ $this->entry->addEMailAddress('user@example.com');
+ $this->assertEquals(['user@example.com'], $this->entry->getEMailAddresses());
+ }
+
+ public function testAddAndSortAction(): void {
+ // Three actions, two with equal priority
+ $action1 = new LinkAction();
+ $action2 = new LinkAction();
+ $action3 = new LinkAction();
+ $action1->setPriority(10);
+ $action1->setName('Bravo');
+
+ $action2->setPriority(0);
+ $action2->setName('Batman');
+
+ $action3->setPriority(10);
+ $action3->setName('Alfa');
+
+ $this->entry->addAction($action1);
+ $this->entry->addAction($action2);
+ $this->entry->addAction($action3);
+ $sorted = $this->entry->getActions();
+
+ $this->assertSame($action3, $sorted[0]);
+ $this->assertSame($action1, $sorted[1]);
+ $this->assertSame($action2, $sorted[2]);
+ }
+
+ public function testSetGetProperties(): void {
+ $props = [
+ 'prop1' => 123,
+ 'prop2' => 'string',
+ ];
+
+ $this->entry->setProperties($props);
+
+ $this->assertNull($this->entry->getProperty('doesntexist'));
+ $this->assertEquals(123, $this->entry->getProperty('prop1'));
+ $this->assertEquals('string', $this->entry->getProperty('prop2'));
+ }
+
+ public function testJsonSerialize(): void {
+ $expectedJson = [
+ 'id' => '123',
+ 'fullName' => 'Guadalupe Frisbey',
+ 'topAction' => null,
+ 'actions' => [],
+ 'lastMessage' => '',
+ 'avatar' => null,
+ 'emailAddresses' => ['user@example.com'],
+ 'profileTitle' => null,
+ 'profileUrl' => null,
+ 'status' => null,
+ 'statusMessage' => null,
+ 'statusMessageTimestamp' => null,
+ 'statusIcon' => null,
+ 'isUser' => false,
+ 'uid' => null,
+ ];
+
+ $this->entry->setId(123);
+ $this->entry->setFullName('Guadalupe Frisbey');
+ $this->entry->addEMailAddress('user@example.com');
+ $json = $this->entry->jsonSerialize();
+
+ $this->assertEquals($expectedJson, $json);
+ }
+}
diff --git a/tests/lib/Contacts/ContactsMenu/ManagerTest.php b/tests/lib/Contacts/ContactsMenu/ManagerTest.php
new file mode 100644
index 00000000000..dd6c634c740
--- /dev/null
+++ b/tests/lib/Contacts/ContactsMenu/ManagerTest.php
@@ -0,0 +1,201 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Tests\Contacts\ContactsMenu;
+
+use OC\Contacts\ContactsMenu\ActionProviderStore;
+use OC\Contacts\ContactsMenu\ContactsStore;
+use OC\Contacts\ContactsMenu\Entry;
+use OC\Contacts\ContactsMenu\Manager;
+use OCP\App\IAppManager;
+use OCP\Constants;
+use OCP\Contacts\ContactsMenu\IProvider;
+use OCP\IConfig;
+use OCP\IUser;
+use PHPUnit\Framework\MockObject\MockObject;
+use Test\TestCase;
+
+class ManagerTest extends TestCase {
+ /** @var ContactsStore|MockObject */
+ private $contactsStore;
+
+ /** @var IAppManager|MockObject */
+ private $appManager;
+
+ /** @var IConfig|MockObject */
+ private $config;
+
+ /** @var ActionProviderStore|MockObject */
+ private $actionProviderStore;
+
+ private Manager $manager;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->contactsStore = $this->createMock(ContactsStore::class);
+ $this->actionProviderStore = $this->createMock(ActionProviderStore::class);
+ $this->appManager = $this->createMock(IAppManager::class);
+ $this->config = $this->createMock(IConfig::class);
+
+ $this->manager = new Manager($this->contactsStore, $this->actionProviderStore, $this->appManager, $this->config);
+ }
+
+ private function generateTestEntries(): array {
+ $entries = [];
+ foreach (range('Z', 'A') as $char) {
+ $entry = $this->createMock(Entry::class);
+ $entry->expects($this->any())
+ ->method('getFullName')
+ ->willReturn('Contact ' . $char);
+ $entries[] = $entry;
+ }
+ return $entries;
+ }
+
+ public function testGetFilteredEntries(): void {
+ $filter = 'con';
+ $user = $this->createMock(IUser::class);
+ $entries = $this->generateTestEntries();
+ $provider = $this->createMock(IProvider::class);
+
+ $this->config->expects($this->exactly(2))
+ ->method('getSystemValueInt')
+ ->willReturnMap([
+ ['sharing.maxAutocompleteResults', Constants::SHARING_MAX_AUTOCOMPLETE_RESULTS_DEFAULT, 25],
+ ['sharing.minSearchStringLength', 0, 0],
+ ]);
+ $this->contactsStore->expects($this->once())
+ ->method('getContacts')
+ ->with($user, $filter)
+ ->willReturn($entries);
+ $this->actionProviderStore->expects($this->once())
+ ->method('getProviders')
+ ->with($user)
+ ->willReturn([$provider]);
+ $provider->expects($this->exactly(25))
+ ->method('process');
+ $this->appManager->expects($this->once())
+ ->method('isEnabledForUser')
+ ->with($this->equalTo('contacts'), $user)
+ ->willReturn(false);
+ $expected = [
+ 'contacts' => array_slice($entries, 0, 25),
+ 'contactsAppEnabled' => false,
+ ];
+
+ $data = $this->manager->getEntries($user, $filter);
+
+ $this->assertEquals($expected, $data);
+ }
+
+ public function testGetFilteredEntriesLimit(): void {
+ $filter = 'con';
+ $user = $this->createMock(IUser::class);
+ $entries = $this->generateTestEntries();
+ $provider = $this->createMock(IProvider::class);
+
+ $this->config->expects($this->exactly(2))
+ ->method('getSystemValueInt')
+ ->willReturnMap([
+ ['sharing.maxAutocompleteResults', Constants::SHARING_MAX_AUTOCOMPLETE_RESULTS_DEFAULT, 3],
+ ['sharing.minSearchStringLength', 0, 0],
+ ]);
+ $this->contactsStore->expects($this->once())
+ ->method('getContacts')
+ ->with($user, $filter)
+ ->willReturn($entries);
+ $this->actionProviderStore->expects($this->once())
+ ->method('getProviders')
+ ->with($user)
+ ->willReturn([$provider]);
+ $provider->expects($this->exactly(3))
+ ->method('process');
+ $this->appManager->expects($this->once())
+ ->method('isEnabledForUser')
+ ->with($this->equalTo('contacts'), $user)
+ ->willReturn(false);
+ $expected = [
+ 'contacts' => array_slice($entries, 0, 3),
+ 'contactsAppEnabled' => false,
+ ];
+
+ $data = $this->manager->getEntries($user, $filter);
+
+ $this->assertEquals($expected, $data);
+ }
+
+ public function testGetFilteredEntriesMinSearchStringLength(): void {
+ $filter = 'con';
+ $user = $this->createMock(IUser::class);
+ $provider = $this->createMock(IProvider::class);
+
+ $this->config->expects($this->exactly(2))
+ ->method('getSystemValueInt')
+ ->willReturnMap([
+ ['sharing.maxAutocompleteResults', Constants::SHARING_MAX_AUTOCOMPLETE_RESULTS_DEFAULT, 3],
+ ['sharing.minSearchStringLength', 0, 4],
+ ]);
+ $this->appManager->expects($this->once())
+ ->method('isEnabledForUser')
+ ->with($this->equalTo('contacts'), $user)
+ ->willReturn(false);
+ $expected = [
+ 'contacts' => [],
+ 'contactsAppEnabled' => false,
+ ];
+
+ $data = $this->manager->getEntries($user, $filter);
+
+ $this->assertEquals($expected, $data);
+ }
+
+ public function testFindOne(): void {
+ $shareTypeFilter = 42;
+ $shareWithFilter = 'foobar';
+
+ $user = $this->createMock(IUser::class);
+ $entry = current($this->generateTestEntries());
+ $provider = $this->createMock(IProvider::class);
+ $this->contactsStore->expects($this->once())
+ ->method('findOne')
+ ->with($user, $shareTypeFilter, $shareWithFilter)
+ ->willReturn($entry);
+ $this->actionProviderStore->expects($this->once())
+ ->method('getProviders')
+ ->with($user)
+ ->willReturn([$provider]);
+ $provider->expects($this->once())
+ ->method('process');
+
+ $data = $this->manager->findOne($user, $shareTypeFilter, $shareWithFilter);
+
+ $this->assertEquals($entry, $data);
+ }
+
+ public function testFindOne404(): void {
+ $shareTypeFilter = 42;
+ $shareWithFilter = 'foobar';
+
+ $user = $this->createMock(IUser::class);
+ $provider = $this->createMock(IProvider::class);
+ $this->contactsStore->expects($this->once())
+ ->method('findOne')
+ ->with($user, $shareTypeFilter, $shareWithFilter)
+ ->willReturn(null);
+ $this->actionProviderStore->expects($this->never())
+ ->method('getProviders')
+ ->with($user)
+ ->willReturn([$provider]);
+ $provider->expects($this->never())
+ ->method('process');
+
+ $data = $this->manager->findOne($user, $shareTypeFilter, $shareWithFilter);
+
+ $this->assertEquals(null, $data);
+ }
+}
diff --git a/tests/lib/Contacts/ContactsMenu/Providers/EMailproviderTest.php b/tests/lib/Contacts/ContactsMenu/Providers/EMailproviderTest.php
new file mode 100644
index 00000000000..648351c2ca9
--- /dev/null
+++ b/tests/lib/Contacts/ContactsMenu/Providers/EMailproviderTest.php
@@ -0,0 +1,85 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Tests\Contacts\ContactsMenu\Providers;
+
+use OC\Contacts\ContactsMenu\Providers\EMailProvider;
+use OCP\Contacts\ContactsMenu\IActionFactory;
+use OCP\Contacts\ContactsMenu\IEntry;
+use OCP\Contacts\ContactsMenu\ILinkAction;
+use OCP\IURLGenerator;
+use PHPUnit\Framework\MockObject\MockObject;
+use Test\TestCase;
+
+class EMailproviderTest extends TestCase {
+ /** @var IActionFactory|MockObject */
+ private $actionFactory;
+
+ /** @var IURLGenerator|MockObject */
+ private $urlGenerator;
+
+ private EMailProvider $provider;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->actionFactory = $this->createMock(IActionFactory::class);
+ $this->urlGenerator = $this->createMock(IURLGenerator::class);
+
+ $this->provider = new EMailProvider($this->actionFactory, $this->urlGenerator);
+ }
+
+ public function testProcess(): void {
+ $entry = $this->createMock(IEntry::class);
+ $action = $this->createMock(ILinkAction::class);
+ $iconUrl = 'https://example.com/img/actions/icon.svg';
+ $this->urlGenerator->expects($this->once())
+ ->method('imagePath')
+ ->willReturn('img/actions/icon.svg');
+ $this->urlGenerator->expects($this->once())
+ ->method('getAbsoluteURL')
+ ->with('img/actions/icon.svg')
+ ->willReturn($iconUrl);
+ $entry->expects($this->once())
+ ->method('getEMailAddresses')
+ ->willReturn([
+ 'user@example.com',
+ ]);
+ $this->actionFactory->expects($this->once())
+ ->method('newEMailAction')
+ ->with($this->equalTo($iconUrl), $this->equalTo('user@example.com'), $this->equalTo('user@example.com'))
+ ->willReturn($action);
+ $entry->expects($this->once())
+ ->method('addAction')
+ ->with($action);
+
+ $this->provider->process($entry);
+ }
+
+ public function testProcessEmptyAddress(): void {
+ $entry = $this->createMock(IEntry::class);
+ $iconUrl = 'https://example.com/img/actions/icon.svg';
+ $this->urlGenerator->expects($this->once())
+ ->method('imagePath')
+ ->willReturn('img/actions/icon.svg');
+ $this->urlGenerator->expects($this->once())
+ ->method('getAbsoluteURL')
+ ->with('img/actions/icon.svg')
+ ->willReturn($iconUrl);
+ $entry->expects($this->once())
+ ->method('getEMailAddresses')
+ ->willReturn([
+ '',
+ ]);
+ $this->actionFactory->expects($this->never())
+ ->method('newEMailAction');
+ $entry->expects($this->never())
+ ->method('addAction');
+
+ $this->provider->process($entry);
+ }
+}
diff --git a/tests/lib/Contacts/ContactsMenu/Providers/LocalTimeProviderTest.php b/tests/lib/Contacts/ContactsMenu/Providers/LocalTimeProviderTest.php
new file mode 100644
index 00000000000..cc53c0bcfcf
--- /dev/null
+++ b/tests/lib/Contacts/ContactsMenu/Providers/LocalTimeProviderTest.php
@@ -0,0 +1,197 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace lib\Contacts\ContactsMenu\Providers;
+
+use OC\Contacts\ContactsMenu\Providers\LocalTimeProvider;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\Contacts\ContactsMenu\IActionFactory;
+use OCP\Contacts\ContactsMenu\IEntry;
+use OCP\Contacts\ContactsMenu\ILinkAction;
+use OCP\IConfig;
+use OCP\IDateTimeFormatter;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\IUser;
+use OCP\IUserManager;
+use OCP\IUserSession;
+use OCP\L10N\IFactory as IL10NFactory;
+use PHPUnit\Framework\MockObject\MockObject;
+use Test\TestCase;
+
+class LocalTimeProviderTest extends TestCase {
+
+ private IActionFactory&MockObject $actionFactory;
+ private IL10N&MockObject $l;
+ private IL10NFactory&MockObject $l10nFactory;
+ private IURLGenerator&MockObject $urlGenerator;
+ private IUserManager&MockObject $userManager;
+ private ITimeFactory&MockObject $timeFactory;
+ private IUserSession&MockObject $userSession;
+ private IDateTimeFormatter&MockObject $dateTimeFormatter;
+ private IConfig&MockObject $config;
+
+ private LocalTimeProvider $provider;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->actionFactory = $this->createMock(IActionFactory::class);
+ $this->l10nFactory = $this->createMock(IL10NFactory::class);
+ $this->l = $this->createMock(IL10N::class);
+ $this->l->expects($this->any())
+ ->method('t')
+ ->willReturnCallback(function ($text, $parameters = []) {
+ return vsprintf($text, $parameters);
+ });
+ $this->l->expects($this->any())
+ ->method('n')
+ ->willReturnCallback(function ($text, $textPlural, $n, $parameters = []) {
+ $formatted = str_replace('%n', (string)$n, $n === 1 ? $text : $textPlural);
+ return vsprintf($formatted, $parameters);
+ });
+ $this->urlGenerator = $this->createMock(IURLGenerator::class);
+ $this->userManager = $this->createMock(IUserManager::class);
+ $this->timeFactory = $this->createMock(ITimeFactory::class);
+ $this->dateTimeFormatter = $this->createMock(IDateTimeFormatter::class);
+ $this->config = $this->createMock(IConfig::class);
+ $this->userSession = $this->createMock(IUserSession::class);
+
+ $this->provider = new LocalTimeProvider(
+ $this->actionFactory,
+ $this->l10nFactory,
+ $this->urlGenerator,
+ $this->userManager,
+ $this->timeFactory,
+ $this->dateTimeFormatter,
+ $this->config,
+ $this->userSession,
+ );
+ }
+
+ public static function dataTestProcess(): array {
+ return [
+ 'no current user' => [
+ false,
+ null,
+ null,
+ 'Local time: 10:24',
+ ],
+ 'both UTC' => [
+ true,
+ null,
+ null,
+ '10:24 • same time',
+ ],
+ 'both same time zone' => [
+ true,
+ 'Europe/Berlin',
+ 'Europe/Berlin',
+ '11:24 • same time',
+ ],
+ '1h behind' => [
+ true,
+ 'Europe/Berlin',
+ 'Europe/London',
+ '10:24 • 1h behind',
+ ],
+ '4:45h ahead' => [
+ true,
+ 'Europe/Berlin',
+ 'Asia/Kathmandu',
+ '16:09 • 4h45m ahead',
+ ],
+ ];
+ }
+
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTestProcess')]
+ public function testProcess(bool $hasCurrentUser, ?string $currentUserTZ, ?string $targetUserTZ, string $expected): void {
+ $entry = $this->createMock(IEntry::class);
+ $entry->expects($this->once())
+ ->method('getProperty')
+ ->with('UID')
+ ->willReturn('user1');
+
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')
+ ->willReturn('user1');
+ $this->userManager->expects($this->once())
+ ->method('get')
+ ->with('user1')
+ ->willReturn($user);
+
+ $this->l10nFactory->method('get')
+ ->with('lib')
+ ->willReturn($this->l);
+
+ $this->config->method('getSystemValueString')
+ ->with('default_timezone', 'UTC')
+ ->willReturn('UTC');
+ $this->config
+ ->method('getUserValue')
+ ->willReturnMap([
+ ['user1', 'core', 'timezone', '', $targetUserTZ],
+ ['currentUser', 'core', 'timezone', '', $currentUserTZ],
+ ]);
+
+ if ($hasCurrentUser) {
+ $currentUser = $this->createMock(IUser::class);
+ $currentUser->method('getUID')
+ ->willReturn('currentUser');
+ $this->userSession->method('getUser')
+ ->willReturn($currentUser);
+ }
+
+ $this->timeFactory->method('getDateTime')
+ ->willReturnCallback(fn ($time, $tz) => (new \DateTime('2023-01-04 10:24:43', new \DateTimeZone('UTC')))->setTimezone($tz));
+
+ $this->dateTimeFormatter->method('formatTime')
+ ->willReturnCallback(fn (\DateTime $time) => $time->format('H:i'));
+
+ $this->urlGenerator->method('imagePath')
+ ->willReturn('actions/recent.svg');
+ $this->urlGenerator->method('getAbsoluteURL')
+ ->with('actions/recent.svg')
+ ->willReturn('https://localhost/actions/recent.svg');
+
+ $action = $this->createMock(ILinkAction::class);
+ $this->actionFactory->expects($this->once())
+ ->method('newLinkAction')
+ ->with(
+ 'https://localhost/actions/recent.svg',
+ $expected,
+ '#',
+ 'timezone'
+ )
+ ->willReturn($action);
+
+ $entry->expects($this->once())
+ ->method('addAction')
+ ->with($action);
+
+ $this->provider->process($entry);
+ }
+
+ public function testProcessNoUser(): void {
+ $entry = $this->createMock(IEntry::class);
+ $entry->expects($this->once())
+ ->method('getProperty')
+ ->with('UID')
+ ->willReturn('user1');
+
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')
+ ->willReturn(null);
+
+ $entry->expects($this->never())
+ ->method('addAction');
+
+ $this->provider->process($entry);
+ }
+}