diff options
Diffstat (limited to 'tests/lib/Contacts/ContactsMenu')
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); + } +} |