aboutsummaryrefslogtreecommitdiffstats
path: root/tests/lib/Collaboration
diff options
context:
space:
mode:
Diffstat (limited to 'tests/lib/Collaboration')
-rw-r--r--tests/lib/Collaboration/Collaborators/GroupPluginTest.php503
-rw-r--r--tests/lib/Collaboration/Collaborators/LookupPluginTest.php498
-rw-r--r--tests/lib/Collaboration/Collaborators/MailPluginTest.php707
-rw-r--r--tests/lib/Collaboration/Collaborators/RemotePluginTest.php430
-rw-r--r--tests/lib/Collaboration/Collaborators/SearchResultTest.php85
-rw-r--r--tests/lib/Collaboration/Collaborators/SearchTest.php253
-rw-r--r--tests/lib/Collaboration/Collaborators/UserPluginTest.php810
-rw-r--r--tests/lib/Collaboration/Resources/ManagerTest.php46
-rw-r--r--tests/lib/Collaboration/Resources/ProviderManagerTest.php98
9 files changed, 3430 insertions, 0 deletions
diff --git a/tests/lib/Collaboration/Collaborators/GroupPluginTest.php b/tests/lib/Collaboration/Collaborators/GroupPluginTest.php
new file mode 100644
index 00000000000..a4ecd598562
--- /dev/null
+++ b/tests/lib/Collaboration/Collaborators/GroupPluginTest.php
@@ -0,0 +1,503 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Test\Collaboration\Collaborators;
+
+use OC\Collaboration\Collaborators\GroupPlugin;
+use OC\Collaboration\Collaborators\SearchResult;
+use OCP\Collaboration\Collaborators\ISearchResult;
+use OCP\IConfig;
+use OCP\IGroup;
+use OCP\IGroupManager;
+use OCP\IUser;
+use OCP\IUserSession;
+use OCP\Share\IShare;
+use Test\TestCase;
+
+class GroupPluginTest extends TestCase {
+ /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
+ protected $config;
+
+ /** @var IGroupManager|\PHPUnit\Framework\MockObject\MockObject */
+ protected $groupManager;
+
+ /** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */
+ protected $session;
+
+ /** @var ISearchResult */
+ protected $searchResult;
+
+ /** @var GroupPlugin */
+ protected $plugin;
+
+ /** @var int */
+ protected $limit = 2;
+
+ /** @var int */
+ protected $offset = 0;
+
+ /** @var IUser|\PHPUnit\Framework\MockObject\MockObject */
+ protected $user;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->config = $this->createMock(IConfig::class);
+
+ $this->groupManager = $this->createMock(IGroupManager::class);
+
+ $this->session = $this->createMock(IUserSession::class);
+
+ $this->searchResult = new SearchResult();
+
+ $this->user = $this->getUserMock('admin', 'Administrator');
+ }
+
+ public function instantiatePlugin() {
+ // cannot be done within setUp, because dependent mocks needs to be set
+ // up with configuration etc. first
+ $this->plugin = new GroupPlugin(
+ $this->config,
+ $this->groupManager,
+ $this->session
+ );
+ }
+
+ public function getUserMock($uid, $displayName) {
+ $user = $this->createMock(IUser::class);
+
+ $user->expects($this->any())
+ ->method('getUID')
+ ->willReturn($uid);
+
+ $user->expects($this->any())
+ ->method('getDisplayName')
+ ->willReturn($displayName);
+
+ return $user;
+ }
+
+ /**
+ * @param string $gid
+ * @param null $displayName
+ * @param bool $hide
+ * @return IGroup|\PHPUnit\Framework\MockObject\MockObject
+ */
+ protected function getGroupMock($gid, $displayName = null, $hide = false) {
+ $group = $this->createMock(IGroup::class);
+
+ $group->expects($this->any())
+ ->method('getGID')
+ ->willReturn($gid);
+
+ if (is_null($displayName)) {
+ // note: this is how the Group class behaves
+ $displayName = $gid;
+ }
+
+ $group->expects($this->any())
+ ->method('getDisplayName')
+ ->willReturn($displayName);
+
+ $group->method('hideFromCollaboration')
+ ->willReturn($hide);
+
+ return $group;
+ }
+
+ public function dataGetGroups(): array {
+ return [
+ ['test', false, true, false, [], [], [], [], true, false],
+ ['test', false, false, false, [], [], [], [], true, false],
+ // group sharing disabled
+ ['test', false, true, true, [], [], [], [], false, false],
+ // group without display name
+ [
+ 'test', false, true, false,
+ [$this->getGroupMock('test1')],
+ [],
+ [],
+ [['label' => 'test1', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test1']]],
+ true,
+ false,
+ ],
+ // group with display name, search by id
+ [
+ 'test', false, true, false,
+ [$this->getGroupMock('test1', 'Test One')],
+ [],
+ [],
+ [['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test1']]],
+ true,
+ false,
+ ],
+ // group with display name, search by display name
+ [
+ 'one', false, true, false,
+ [$this->getGroupMock('test1', 'Test One')],
+ [],
+ [],
+ [['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test1']]],
+ true,
+ false,
+ ],
+ // group with display name, search by display name, exact expected
+ [
+ 'Test One', false, true, false,
+ [$this->getGroupMock('test1', 'Test One')],
+ [],
+ [['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test1']]],
+ [],
+ true,
+ false,
+ ],
+ [
+ 'test', false, false, false,
+ [$this->getGroupMock('test1')],
+ [],
+ [],
+ [],
+ true,
+ false,
+ ],
+ [
+ 'test', false, true, false,
+ [
+ $this->getGroupMock('test'),
+ $this->getGroupMock('test1'),
+ ],
+ [],
+ [['label' => 'test', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test']]],
+ [['label' => 'test1', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test1']]],
+ false,
+ false,
+ ],
+ [
+ 'test', false, false, false,
+ [
+ $this->getGroupMock('test'),
+ $this->getGroupMock('test1'),
+ ],
+ [],
+ [['label' => 'test', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test']]],
+ [],
+ true,
+ false,
+ ],
+ [
+ 'test', false, true, false,
+ [
+ $this->getGroupMock('test0'),
+ $this->getGroupMock('test1'),
+ ],
+ [],
+ [],
+ [
+ ['label' => 'test0', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test0']],
+ ['label' => 'test1', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test1']],
+ ],
+ false,
+ null,
+ ],
+ [
+ 'test', false, false, false,
+ [
+ $this->getGroupMock('test0'),
+ $this->getGroupMock('test1'),
+ ],
+ [],
+ [],
+ [],
+ true,
+ null,
+ ],
+ [
+ 'test', false, true, false,
+ [
+ $this->getGroupMock('test0'),
+ $this->getGroupMock('test1'),
+ ],
+ [],
+ [
+ ['label' => 'test', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test']],
+ ],
+ [
+ ['label' => 'test0', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test0']],
+ ['label' => 'test1', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test1']],
+ ],
+ false,
+ $this->getGroupMock('test'),
+ ],
+ [
+ 'test', false, false, false,
+ [
+ $this->getGroupMock('test0'),
+ $this->getGroupMock('test1'),
+ ],
+ [],
+ [
+ ['label' => 'test', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test']],
+ ],
+ [],
+ true,
+ $this->getGroupMock('test'),
+ ],
+ ['test', true, true, false, [], [], [], [], true, false],
+ ['test', true, false, false, [], [], [], [], true, false],
+ [
+ 'test', true, true, false,
+ [
+ $this->getGroupMock('test1'),
+ $this->getGroupMock('test2'),
+ ],
+ [$this->getGroupMock('test1')],
+ [],
+ [['label' => 'test1', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test1']]],
+ false,
+ false,
+ ],
+ [
+ 'test', true, false, false,
+ [
+ $this->getGroupMock('test1'),
+ $this->getGroupMock('test2'),
+ ],
+ [$this->getGroupMock('test1')],
+ [],
+ [],
+ true,
+ false,
+ ],
+ [
+ 'test', true, true, false,
+ [
+ $this->getGroupMock('test'),
+ $this->getGroupMock('test1'),
+ ],
+ [$this->getGroupMock('test')],
+ [['label' => 'test', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test']]],
+ [],
+ false,
+ false,
+ ],
+ [
+ 'test', true, false, false,
+ [
+ $this->getGroupMock('test'),
+ $this->getGroupMock('test1'),
+ ],
+ [$this->getGroupMock('test')],
+ [['label' => 'test', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test']]],
+ [],
+ true,
+ false,
+ ],
+ [
+ 'test', true, true, false,
+ [
+ $this->getGroupMock('test'),
+ $this->getGroupMock('test1'),
+ ],
+ [$this->getGroupMock('test1')],
+ [],
+ [['label' => 'test1', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test1']]],
+ false,
+ false,
+ ],
+ [
+ 'test', true, false, false,
+ [
+ $this->getGroupMock('test'),
+ $this->getGroupMock('test1'),
+ ],
+ [$this->getGroupMock('test1')],
+ [],
+ [],
+ true,
+ false,
+ ],
+ [
+ 'test', true, true, false,
+ [
+ $this->getGroupMock('test'),
+ $this->getGroupMock('test1'),
+ ],
+ [$this->getGroupMock('test'), $this->getGroupMock('test0'), $this->getGroupMock('test1')],
+ [['label' => 'test', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test']]],
+ [['label' => 'test1', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test1']]],
+ false,
+ false,
+ ],
+ [
+ 'test', true, false, false,
+ [
+ $this->getGroupMock('test'),
+ $this->getGroupMock('test1'),
+ ],
+ [$this->getGroupMock('test'), $this->getGroupMock('test0'), $this->getGroupMock('test1')],
+ [['label' => 'test', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test']]],
+ [],
+ true,
+ false,
+ ],
+ [
+ 'test', true, true, false,
+ [
+ $this->getGroupMock('test0'),
+ $this->getGroupMock('test1'),
+ ],
+ [$this->getGroupMock('test'), $this->getGroupMock('test0'), $this->getGroupMock('test1')],
+ [],
+ [
+ ['label' => 'test0', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test0']],
+ ['label' => 'test1', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test1']],
+ ],
+ false,
+ null,
+ ],
+ [
+ 'test', true, false, false,
+ [
+ $this->getGroupMock('test0'),
+ $this->getGroupMock('test1'),
+ ],
+ [$this->getGroupMock('test'), $this->getGroupMock('test0'), $this->getGroupMock('test1')],
+ [],
+ [],
+ true,
+ null,
+ ],
+ [
+ 'test', true, true, false,
+ [
+ $this->getGroupMock('test0'),
+ $this->getGroupMock('test1'),
+ ],
+ [$this->getGroupMock('test'), $this->getGroupMock('test0'), $this->getGroupMock('test1')],
+ [
+ ['label' => 'test', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test']],
+ ],
+ [
+ ['label' => 'test0', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test0']],
+ ['label' => 'test1', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test1']],
+ ],
+ false,
+ $this->getGroupMock('test'),
+ ],
+ [
+ 'test', true, false, false,
+ [
+ $this->getGroupMock('test0'),
+ $this->getGroupMock('test1'),
+ ],
+ [$this->getGroupMock('test'), $this->getGroupMock('test0'), $this->getGroupMock('test1')],
+ [
+ ['label' => 'test', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test']],
+ ],
+ [],
+ true,
+ $this->getGroupMock('test'),
+ ],
+ [
+ 'test', false, false, false,
+ [
+ $this->getGroupMock('test', null, true),
+ $this->getGroupMock('test1'),
+ ],
+ [],
+ [],
+ [],
+ true,
+ false,
+ ],
+ ];
+ }
+
+ /**
+ *
+ * @param string $searchTerm
+ * @param bool $shareWithGroupOnly
+ * @param bool $shareeEnumeration
+ * @param bool $groupSharingDisabled
+ * @param array $groupResponse
+ * @param array $userGroupsResponse
+ * @param array $exactExpected
+ * @param array $expected
+ * @param bool $reachedEnd
+ * @param bool|IGroup $singleGroup
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataGetGroups')]
+ public function testSearch(
+ string $searchTerm,
+ bool $shareWithGroupOnly,
+ bool $shareeEnumeration,
+ bool $groupSharingDisabled,
+ array $groupResponse,
+ array $userGroupsResponse,
+ array $exactExpected,
+ array $expected,
+ bool $reachedEnd,
+ $singleGroup,
+ ): void {
+ $this->config->expects($this->any())
+ ->method('getAppValue')
+ ->willReturnCallback(
+ function ($appName, $key, $default) use ($shareWithGroupOnly, $shareeEnumeration, $groupSharingDisabled) {
+ if ($appName !== 'core') {
+ return $default;
+ }
+ switch ($key) {
+ case 'shareapi_only_share_with_group_members':
+ return $shareWithGroupOnly ? 'yes' : 'no';
+ case 'shareapi_allow_share_dialog_user_enumeration':
+ return $shareeEnumeration ? 'yes' : 'no';
+ case 'shareapi_allow_group_sharing':
+ return $groupSharingDisabled ? 'no' : 'yes';
+ default:
+ return $default;
+ }
+ }
+ );
+
+ $this->instantiatePlugin();
+
+ if (!$groupSharingDisabled) {
+ $this->groupManager->expects($this->once())
+ ->method('search')
+ ->with($searchTerm, $this->limit, $this->offset)
+ ->willReturn($groupResponse);
+ }
+
+ if ($singleGroup !== false) {
+ $this->groupManager->expects($this->once())
+ ->method('get')
+ ->with($searchTerm)
+ ->willReturn($singleGroup);
+ }
+
+ if ($shareWithGroupOnly) {
+ $this->session->expects($this->any())
+ ->method('getUser')
+ ->willReturn($this->user);
+
+ $numGetUserGroupsCalls = empty($groupResponse) ? 0 : 1;
+ $this->groupManager->expects($this->exactly($numGetUserGroupsCalls))
+ ->method('getUserGroups')
+ ->with($this->user)
+ ->willReturn($userGroupsResponse);
+ }
+
+ $moreResults = $this->plugin->search($searchTerm, $this->limit, $this->offset, $this->searchResult);
+ $result = $this->searchResult->asArray();
+
+ if (!$groupSharingDisabled) {
+ $this->assertEquals($exactExpected, $result['exact']['groups']);
+ $this->assertEquals($expected, $result['groups']);
+ }
+ $this->assertSame($reachedEnd, $moreResults);
+ }
+}
diff --git a/tests/lib/Collaboration/Collaborators/LookupPluginTest.php b/tests/lib/Collaboration/Collaborators/LookupPluginTest.php
new file mode 100644
index 00000000000..ac9b196ea1e
--- /dev/null
+++ b/tests/lib/Collaboration/Collaborators/LookupPluginTest.php
@@ -0,0 +1,498 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Test\Collaboration\Collaborators;
+
+use OC\Collaboration\Collaborators\LookupPlugin;
+use OC\Federation\CloudId;
+use OCP\Collaboration\Collaborators\ISearchResult;
+use OCP\Collaboration\Collaborators\SearchResultType;
+use OCP\Federation\ICloudId;
+use OCP\Federation\ICloudIdManager;
+use OCP\Http\Client\IClient;
+use OCP\Http\Client\IClientService;
+use OCP\Http\Client\IResponse;
+use OCP\IConfig;
+use OCP\IUser;
+use OCP\IUserSession;
+use OCP\Share\IShare;
+use PHPUnit\Framework\MockObject\MockObject;
+use Psr\Log\LoggerInterface;
+use Test\TestCase;
+
+class LookupPluginTest extends TestCase {
+ /** @var IConfig|MockObject */
+ protected $config;
+ /** @var IClientService|MockObject */
+ protected $clientService;
+ /** @var IUserSession|MockObject */
+ protected $userSession;
+ /** @var ICloudIdManager|MockObject */
+ protected $cloudIdManager;
+ /** @var LookupPlugin */
+ protected $plugin;
+ /** @var LoggerInterface|MockObject */
+ protected $logger;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->userSession = $this->createMock(IUserSession::class);
+ $this->cloudIdManager = $this->createMock(ICloudIdManager::class);
+ $this->config = $this->createMock(IConfig::class);
+ $this->logger = $this->createMock(LoggerInterface::class);
+ $this->clientService = $this->createMock(IClientService::class);
+ $cloudId = $this->createMock(ICloudId::class);
+ $cloudId->expects($this->any())->method('getRemote')->willReturn('myNextcloud.net');
+ $user = $this->createMock(IUser::class);
+ $user->expects($this->any())->method('getCloudId')->willReturn('user@myNextcloud.net');
+ $this->userSession->expects($this->any())->method('getUser')
+ ->willReturn($user);
+ $this->cloudIdManager->expects($this->any())->method('resolveCloudId')
+ ->willReturnCallback(function ($cloudId) {
+ if ($cloudId === 'user@myNextcloud.net') {
+ return new CloudId('user@myNextcloud.net', 'user', 'myNextcloud.net');
+ }
+ return new CloudId('user@someNextcloud.net', 'user', 'someNextcloud.net');
+ });
+
+
+ $this->plugin = new LookupPlugin(
+ $this->config,
+ $this->clientService,
+ $this->userSession,
+ $this->cloudIdManager,
+ $this->logger
+ );
+ }
+
+ public function testSearchNoLookupServerURI(): void {
+ $this->config->expects($this->once())
+ ->method('getAppValue')
+ ->with('files_sharing', 'lookupServerEnabled', 'no')
+ ->willReturn('yes');
+ $this->config->expects($this->exactly(2))
+ ->method('getSystemValueBool')
+ ->willReturnMap([
+ ['gs.enabled', false, true],
+ ['has_internet_connection', true, true],
+ ]);
+
+ $this->config->expects($this->once())
+ ->method('getSystemValueString')
+ ->with('lookup_server', 'https://lookup.nextcloud.com')
+ ->willReturn('');
+
+ $this->clientService->expects($this->never())
+ ->method('newClient');
+
+ /** @var ISearchResult|MockObject $searchResult */
+ $searchResult = $this->createMock(ISearchResult::class);
+
+ $this->plugin->search('foobar', 10, 0, $searchResult);
+ }
+
+ public function testSearchNoInternet(): void {
+ $this->config->expects($this->once())
+ ->method('getAppValue')
+ ->with('files_sharing', 'lookupServerEnabled', 'no')
+ ->willReturn('yes');
+ $this->config->expects($this->exactly(2))
+ ->method('getSystemValueBool')
+ ->willReturnMap([
+ ['gs.enabled', false, false],
+ ['has_internet_connection', true, false],
+ ]);
+
+ $this->clientService->expects($this->never())
+ ->method('newClient');
+
+ /** @var ISearchResult|MockObject $searchResult */
+ $searchResult = $this->createMock(ISearchResult::class);
+
+ $this->plugin->search('foobar', 10, 0, $searchResult);
+ }
+
+ /**
+ * @param array $searchParams
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('searchDataProvider')]
+ public function testSearch(array $searchParams): void {
+ $type = new SearchResultType('lookup');
+
+ /** @var ISearchResult|MockObject $searchResult */
+ $searchResult = $this->createMock(ISearchResult::class);
+ $searchResult->expects($this->once())
+ ->method('addResultSet')
+ ->with($type, $searchParams['expectedResult'], []);
+
+ $this->config->expects($this->once())
+ ->method('getAppValue')
+ ->with('files_sharing', 'lookupServerEnabled', 'no')
+ ->willReturn('yes');
+ $this->config->expects($this->exactly(2))
+ ->method('getSystemValueBool')
+ ->willReturnMap([
+ ['gs.enabled', false, true],
+ ['has_internet_connection', true, true],
+ ]);
+
+ $this->config->expects($this->once())
+ ->method('getSystemValueString')
+ ->with('lookup_server', 'https://lookup.nextcloud.com')
+ ->willReturn($searchParams['server']);
+
+ $response = $this->createMock(IResponse::class);
+ $response->expects($this->once())
+ ->method('getBody')
+ ->willReturn(json_encode($searchParams['resultBody']));
+
+ $client = $this->createMock(IClient::class);
+ $client->expects($this->once())
+ ->method('get')
+ ->willReturnCallback(function ($url) use ($searchParams, $response) {
+ $this->assertSame(strpos($url, $searchParams['server'] . '/users?search='), 0);
+ $this->assertNotFalse(strpos($url, urlencode($searchParams['search'])));
+ return $response;
+ });
+
+ $this->clientService->expects($this->once())
+ ->method('newClient')
+ ->willReturn($client);
+
+ $moreResults = $this->plugin->search(
+ $searchParams['search'],
+ $searchParams['limit'],
+ $searchParams['offset'],
+ $searchResult
+ );
+
+ $this->assertFalse($moreResults);
+ }
+
+
+ /**
+ * @param array $searchParams
+ * @param bool $GSEnabled
+ * @param bool $LookupEnabled
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataSearchEnableDisableLookupServer')]
+ public function testSearchEnableDisableLookupServer(array $searchParams, $GSEnabled, $LookupEnabled): void {
+ $type = new SearchResultType('lookup');
+
+ /** @var ISearchResult|MockObject $searchResult */
+ $searchResult = $this->createMock(ISearchResult::class);
+
+ $this->config->expects($this->once())
+ ->method('getAppValue')
+ ->with('files_sharing', 'lookupServerEnabled', 'no')
+ ->willReturn($LookupEnabled ? 'yes' : 'no');
+ if ($GSEnabled) {
+ $searchResult->expects($this->once())
+ ->method('addResultSet')
+ ->with($type, $searchParams['expectedResult'], []);
+
+ $this->config->expects($this->exactly(2))
+ ->method('getSystemValueBool')
+ ->willReturnMap([
+ ['gs.enabled', false, $GSEnabled],
+ ['has_internet_connection', true, true],
+ ]);
+ $this->config->expects($this->once())
+ ->method('getSystemValueString')
+ ->with('lookup_server', 'https://lookup.nextcloud.com')
+ ->willReturn($searchParams['server']);
+
+ $response = $this->createMock(IResponse::class);
+ $response->expects($this->once())
+ ->method('getBody')
+ ->willReturn(json_encode($searchParams['resultBody']));
+
+ $client = $this->createMock(IClient::class);
+ $client->expects($this->once())
+ ->method('get')
+ ->willReturnCallback(function ($url) use ($searchParams, $response) {
+ $this->assertSame(strpos($url, $searchParams['server'] . '/users?search='), 0);
+ $this->assertNotFalse(strpos($url, urlencode($searchParams['search'])));
+ return $response;
+ });
+
+ $this->clientService->expects($this->once())
+ ->method('newClient')
+ ->willReturn($client);
+ } else {
+ $searchResult->expects($this->never())->method('addResultSet');
+ $this->config->expects($this->exactly(2))
+ ->method('getSystemValueBool')
+ ->willReturnMap([
+ ['gs.enabled', false, $GSEnabled],
+ ['has_internet_connection', true, true],
+ ]);
+ }
+ $moreResults = $this->plugin->search(
+ $searchParams['search'],
+ $searchParams['limit'],
+ $searchParams['offset'],
+ $searchResult
+ );
+
+ $this->assertFalse($moreResults);
+ }
+
+
+ public function testSearchGSDisabled(): void {
+ $this->config->expects($this->atLeastOnce())
+ ->method('getSystemValueBool')
+ ->willReturnMap([
+ ['has_internet_connection', true, true],
+ ['gs.enabled', false, false],
+ ]);
+
+ /** @var ISearchResult|MockObject $searchResult */
+ $searchResult = $this->createMock(ISearchResult::class);
+ $searchResult->expects($this->never())
+ ->method('addResultSet');
+ $searchResult->expects($this->never())
+ ->method('markExactIdMatch');
+
+ $this->assertFalse($this->plugin->search('irr', 10, 0, $searchResult));
+ }
+
+ public static function dataSearchEnableDisableLookupServer(): array {
+ $fedIDs = [
+ 'foo@enceladus.moon',
+ 'foobar@enceladus.moon',
+ 'foongus@enceladus.moon',
+ ];
+
+ return [
+ [[
+ 'search' => 'foo',
+ 'limit' => 10,
+ 'offset' => 0,
+ 'server' => 'https://lookup.example.io',
+ 'resultBody' => [
+ ['federationId' => $fedIDs[0]],
+ ['federationId' => $fedIDs[1]],
+ ['federationId' => $fedIDs[2]],
+ ],
+ 'expectedResult' => [
+ [
+ 'label' => $fedIDs[0],
+ 'value' => [
+ 'shareType' => IShare::TYPE_REMOTE,
+ 'globalScale' => true,
+ 'shareWith' => $fedIDs[0]
+ ],
+ 'extra' => ['federationId' => $fedIDs[0]],
+ ],
+ [
+ 'label' => $fedIDs[1],
+ 'value' => [
+ 'shareType' => IShare::TYPE_REMOTE,
+ 'globalScale' => true,
+ 'shareWith' => $fedIDs[1]
+ ],
+ 'extra' => ['federationId' => $fedIDs[1]],
+ ],
+ [
+ 'label' => $fedIDs[2],
+ 'value' => [
+ 'shareType' => IShare::TYPE_REMOTE,
+ 'globalScale' => true,
+ 'shareWith' => $fedIDs[2]
+ ],
+ 'extra' => ['federationId' => $fedIDs[2]],
+ ],
+ ]
+ ],// GS , Lookup
+ true, true
+ ],
+ [[
+ 'search' => 'foo',
+ 'limit' => 10,
+ 'offset' => 0,
+ 'server' => 'https://lookup.example.io',
+ 'resultBody' => [
+ ['federationId' => $fedIDs[0]],
+ ['federationId' => $fedIDs[1]],
+ ['federationId' => $fedIDs[2]],
+ ],
+ 'expectedResult' => [
+ [
+ 'label' => $fedIDs[0],
+ 'value' => [
+ 'shareType' => IShare::TYPE_REMOTE,
+ 'globalScale' => true,
+ 'shareWith' => $fedIDs[0]
+ ],
+ 'extra' => ['federationId' => $fedIDs[0]],
+ ],
+ [
+ 'label' => $fedIDs[1],
+ 'value' => [
+ 'shareType' => IShare::TYPE_REMOTE,
+ 'globalScale' => true,
+ 'shareWith' => $fedIDs[1]
+ ],
+ 'extra' => ['federationId' => $fedIDs[1]],
+ ],
+ [
+ 'label' => $fedIDs[2],
+ 'value' => [
+ 'shareType' => IShare::TYPE_REMOTE,
+ 'globalScale' => true,
+ 'shareWith' => $fedIDs[2]
+ ],
+ 'extra' => ['federationId' => $fedIDs[2]],
+ ],
+ ]
+ ],// GS , Lookup
+ true, false
+ ],
+ [[
+ 'search' => 'foo',
+ 'limit' => 10,
+ 'offset' => 0,
+ 'server' => 'https://lookup.example.io',
+ 'resultBody' => [
+ ['federationId' => $fedIDs[0]],
+ ['federationId' => $fedIDs[1]],
+ ['federationId' => $fedIDs[2]],
+ ],
+ 'expectedResult' => [
+ [
+ 'label' => $fedIDs[0],
+ 'value' => [
+ 'shareType' => IShare::TYPE_REMOTE,
+ 'shareWith' => $fedIDs[0]
+ ],
+ 'extra' => ['federationId' => $fedIDs[0]],
+ ],
+ [
+ 'label' => $fedIDs[1],
+ 'value' => [
+ 'shareType' => IShare::TYPE_REMOTE,
+ 'shareWith' => $fedIDs[1]
+ ],
+ 'extra' => ['federationId' => $fedIDs[1]],
+ ],
+ [
+ 'label' => $fedIDs[2],
+ 'value' => [
+ 'shareType' => IShare::TYPE_REMOTE,
+ 'shareWith' => $fedIDs[2]
+ ],
+ 'extra' => ['federationId' => $fedIDs[2]],
+ ],
+ ]
+ ],// GS , Lookup
+ false, true
+ ],
+ [[
+ 'search' => 'foo',
+ 'limit' => 10,
+ 'offset' => 0,
+ 'server' => 'https://lookup.example.io',
+ 'resultBody' => [
+ ['federationId' => $fedIDs[0]],
+ ['federationId' => $fedIDs[1]],
+ ['federationId' => $fedIDs[2]],
+ ],
+ 'expectedResult' => [
+ [
+ 'label' => $fedIDs[0],
+ 'value' => [
+ 'shareType' => IShare::TYPE_REMOTE,
+ 'shareWith' => $fedIDs[0]
+ ],
+ 'extra' => ['federationId' => $fedIDs[0]],
+ ],
+ [
+ 'label' => $fedIDs[1],
+ 'value' => [
+ 'shareType' => IShare::TYPE_REMOTE,
+ 'shareWith' => $fedIDs[1]
+ ],
+ 'extra' => ['federationId' => $fedIDs[1]],
+ ],
+ [
+ 'label' => $fedIDs[2],
+ 'value' => [
+ 'shareType' => IShare::TYPE_REMOTE,
+ 'shareWith' => $fedIDs[2]
+ ],
+ 'extra' => ['federationId' => $fedIDs[2]],
+ ],
+ ]
+ ],// GS , Lookup
+ false, false
+ ],
+ ];
+ }
+
+ public static function searchDataProvider(): array {
+ $fedIDs = [
+ 'foo@enceladus.moon',
+ 'foobar@enceladus.moon',
+ 'foongus@enceladus.moon',
+ ];
+
+ return [
+ // #0, standard search with results
+ [[
+ 'search' => 'foo',
+ 'limit' => 10,
+ 'offset' => 0,
+ 'server' => 'https://lookup.example.io',
+ 'resultBody' => [
+ ['federationId' => $fedIDs[0]],
+ ['federationId' => $fedIDs[1]],
+ ['federationId' => $fedIDs[2]],
+ ],
+ 'expectedResult' => [
+ [
+ 'label' => $fedIDs[0],
+ 'value' => [
+ 'shareType' => IShare::TYPE_REMOTE,
+ 'globalScale' => true,
+ 'shareWith' => $fedIDs[0]
+ ],
+ 'extra' => ['federationId' => $fedIDs[0]],
+ ],
+ [
+ 'label' => $fedIDs[1],
+ 'value' => [
+ 'shareType' => IShare::TYPE_REMOTE,
+ 'globalScale' => true,
+ 'shareWith' => $fedIDs[1]
+ ],
+ 'extra' => ['federationId' => $fedIDs[1]],
+ ],
+ [
+ 'label' => $fedIDs[2],
+ 'value' => [
+ 'shareType' => IShare::TYPE_REMOTE,
+ 'globalScale' => true,
+ 'shareWith' => $fedIDs[2]
+ ],
+ 'extra' => ['federationId' => $fedIDs[2]],
+ ],
+ ]
+ ]],
+ // #1, search without results
+ [[
+ 'search' => 'foo',
+ 'limit' => 10,
+ 'offset' => 0,
+ 'server' => 'https://lookup.example.io',
+ 'resultBody' => [],
+ 'expectedResult' => [],
+ ]],
+ ];
+ }
+}
diff --git a/tests/lib/Collaboration/Collaborators/MailPluginTest.php b/tests/lib/Collaboration/Collaborators/MailPluginTest.php
new file mode 100644
index 00000000000..b38b961a512
--- /dev/null
+++ b/tests/lib/Collaboration/Collaborators/MailPluginTest.php
@@ -0,0 +1,707 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Test\Collaboration\Collaborators;
+
+use OC\Collaboration\Collaborators\MailPlugin;
+use OC\Collaboration\Collaborators\SearchResult;
+use OC\Federation\CloudIdManager;
+use OC\KnownUser\KnownUserService;
+use OCP\Collaboration\Collaborators\SearchResultType;
+use OCP\Contacts\IManager;
+use OCP\EventDispatcher\IEventDispatcher;
+use OCP\Federation\ICloudIdManager;
+use OCP\ICacheFactory;
+use OCP\IConfig;
+use OCP\IGroupManager;
+use OCP\IURLGenerator;
+use OCP\IUser;
+use OCP\IUserManager;
+use OCP\IUserSession;
+use OCP\Mail\IMailer;
+use OCP\Share\IShare;
+use Test\TestCase;
+
+class MailPluginTest extends TestCase {
+ /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
+ protected $config;
+
+ /** @var IManager|\PHPUnit\Framework\MockObject\MockObject */
+ protected $contactsManager;
+
+ /** @var ICloudIdManager|\PHPUnit\Framework\MockObject\MockObject */
+ protected $cloudIdManager;
+
+ /** @var MailPlugin */
+ protected $plugin;
+
+ /** @var SearchResult */
+ protected $searchResult;
+
+ /** @var IGroupManager|\PHPUnit\Framework\MockObject\MockObject */
+ protected $groupManager;
+
+ /** @var KnownUserService|\PHPUnit\Framework\MockObject\MockObject */
+ protected $knownUserService;
+
+ /** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */
+ protected $userSession;
+
+ /** @var IMailer|\PHPUnit\Framework\MockObject\MockObject */
+ protected $mailer;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->config = $this->createMock(IConfig::class);
+ $this->contactsManager = $this->createMock(IManager::class);
+ $this->groupManager = $this->createMock(IGroupManager::class);
+ $this->knownUserService = $this->createMock(KnownUserService::class);
+ $this->userSession = $this->createMock(IUserSession::class);
+ $this->mailer = $this->createMock(IMailer::class);
+ $this->cloudIdManager = new CloudIdManager(
+ $this->createMock(ICacheFactory::class),
+ $this->createMock(IEventDispatcher::class),
+ $this->contactsManager,
+ $this->createMock(IURLGenerator::class),
+ $this->createMock(IUserManager::class),
+ );
+
+ $this->searchResult = new SearchResult();
+ }
+
+ public function instantiatePlugin() {
+ $this->plugin = new MailPlugin(
+ $this->contactsManager,
+ $this->cloudIdManager,
+ $this->config,
+ $this->groupManager,
+ $this->knownUserService,
+ $this->userSession,
+ $this->mailer
+ );
+ }
+
+ /**
+ *
+ * @param string $searchTerm
+ * @param array $contacts
+ * @param bool $shareeEnumeration
+ * @param array $expected
+ * @param bool $reachedEnd
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataGetEmail')]
+ public function testSearch($searchTerm, $contacts, $shareeEnumeration, $expected, $exactIdMatch, $reachedEnd, $validEmail): void {
+ $this->config->expects($this->any())
+ ->method('getAppValue')
+ ->willReturnCallback(
+ function ($appName, $key, $default) use ($shareeEnumeration) {
+ if ($appName === 'core' && $key === 'shareapi_allow_share_dialog_user_enumeration') {
+ return $shareeEnumeration ? 'yes' : 'no';
+ }
+ return $default;
+ }
+ );
+
+ $this->instantiatePlugin();
+
+ $currentUser = $this->createMock(IUser::class);
+ $currentUser->method('getUID')
+ ->willReturn('current');
+ $this->userSession->method('getUser')
+ ->willReturn($currentUser);
+
+ $this->mailer->method('validateMailAddress')
+ ->willReturn($validEmail);
+
+ $this->contactsManager->expects($this->any())
+ ->method('search')
+ ->willReturnCallback(function ($search, $searchAttributes) use ($searchTerm, $contacts) {
+ if ($search === $searchTerm) {
+ return $contacts;
+ }
+ return [];
+ });
+
+ $moreResults = $this->plugin->search($searchTerm, 2, 0, $this->searchResult);
+ $result = $this->searchResult->asArray();
+
+ $this->assertSame($exactIdMatch, $this->searchResult->hasExactIdMatch(new SearchResultType('emails')));
+ $this->assertEquals($expected, $result);
+ $this->assertSame($reachedEnd, $moreResults);
+ }
+
+ public static function dataGetEmail(): array {
+ return [
+ // data set 0
+ ['test', [], true, ['emails' => [], 'exact' => ['emails' => []]], false, false, false],
+ // data set 1
+ ['test', [], false, ['emails' => [], 'exact' => ['emails' => []]], false, false, false],
+ // data set 2
+ [
+ 'test@remote.com',
+ [],
+ true,
+ ['emails' => [], 'exact' => ['emails' => [['uuid' => 'test@remote.com', 'label' => 'test@remote.com', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'test@remote.com']]]]],
+ false,
+ false,
+ true,
+ ],
+ // data set 3
+ [ // no valid email address
+ 'test@remote',
+ [],
+ true,
+ ['emails' => [], 'exact' => ['emails' => []]],
+ false,
+ false,
+ false,
+ ],
+ // data set 4
+ [
+ 'test@remote.com',
+ [],
+ false,
+ ['emails' => [], 'exact' => ['emails' => [['uuid' => 'test@remote.com', 'label' => 'test@remote.com', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'test@remote.com']]]]],
+ false,
+ false,
+ true,
+ ],
+ // data set 5
+ [
+ 'test',
+ [
+ [
+ 'UID' => 'uid3',
+ 'FN' => 'User3 @ Localhost',
+ ],
+ [
+ 'UID' => 'uid2',
+ 'FN' => 'User2 @ Localhost',
+ 'EMAIL' => [
+ ],
+ ],
+ [
+ 'UID' => 'uid1',
+ 'FN' => 'User @ Localhost',
+ 'EMAIL' => [
+ 'username@example.com',
+ ],
+ ],
+ ],
+ true,
+ ['emails' => [['uuid' => 'uid1', 'name' => 'User @ Localhost', 'type' => '', 'label' => 'User @ Localhost (username@example.com)', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'username@example.com']]], 'exact' => ['emails' => []]],
+ false,
+ false,
+ false,
+ ],
+ // data set 6
+ [
+ 'test',
+ [
+ [
+ 'UID' => 'uid3',
+ 'FN' => 'User3 @ Localhost',
+ ],
+ [
+ 'UID' => 'uid2',
+ 'FN' => 'User2 @ Localhost',
+ 'EMAIL' => [
+ ],
+ ],
+ [
+ 'isLocalSystemBook' => true,
+ 'UID' => 'uid1',
+ 'FN' => 'User @ Localhost',
+ 'EMAIL' => [
+ 'username@localhost',
+ ],
+ ],
+ ],
+ false,
+ ['emails' => [], 'exact' => ['emails' => []]],
+ false,
+ false,
+ false,
+ ],
+ // data set 7
+ [
+ 'test@remote.com',
+ [
+ [
+ 'UID' => 'uid3',
+ 'FN' => 'User3 @ example.com',
+ ],
+ [
+ 'UID' => 'uid2',
+ 'FN' => 'User2 @ example.com',
+ 'EMAIL' => [
+ ],
+ ],
+ [
+ 'UID' => 'uid1',
+ 'FN' => 'User @ example.com',
+ 'EMAIL' => [
+ 'username@example.com',
+ ],
+ ],
+ ],
+ true,
+ ['emails' => [['uuid' => 'uid1', 'name' => 'User @ example.com', 'type' => '', 'label' => 'User @ example.com (username@example.com)', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'username@example.com']]], 'exact' => ['emails' => [['label' => 'test@remote.com', 'uuid' => 'test@remote.com', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'test@remote.com']]]]],
+ false,
+ false,
+ true,
+ ],
+ // data set 8
+ [
+ 'test@remote.com',
+ [
+ [
+ 'UID' => 'uid3',
+ 'FN' => 'User3 @ Localhost',
+ ],
+ [
+ 'UID' => 'uid2',
+ 'FN' => 'User2 @ Localhost',
+ 'EMAIL' => [
+ ],
+ ],
+ [
+ 'isLocalSystemBook' => true,
+ 'UID' => 'uid1',
+ 'FN' => 'User @ Localhost',
+ 'EMAIL' => [
+ 'username@localhost',
+ ],
+ ],
+ ],
+ false,
+ ['emails' => [], 'exact' => ['emails' => [['label' => 'test@remote.com', 'uuid' => 'test@remote.com', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'test@remote.com']]]]],
+ false,
+ false,
+ true,
+ ],
+ // data set 9
+ [
+ 'username@example.com',
+ [
+ [
+ 'UID' => 'uid3',
+ 'FN' => 'User3 @ example.com',
+ ],
+ [
+ 'UID' => 'uid2',
+ 'FN' => 'User2 @ example.com',
+ 'EMAIL' => [
+ ],
+ ],
+ [
+ 'UID' => 'uid1',
+ 'FN' => 'User @ example.com',
+ 'EMAIL' => [
+ 'username@example.com',
+ ],
+ ],
+ ],
+ true,
+ ['emails' => [], 'exact' => ['emails' => [['name' => 'User @ example.com', 'uuid' => 'uid1', 'type' => '', 'label' => 'User @ example.com (username@example.com)', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'username@example.com']]]]],
+ true,
+ false,
+ false,
+ ],
+ // data set 10
+ [
+ 'username@example.com',
+ [
+ [
+ 'UID' => 'uid1',
+ 'FN' => 'User3 @ example.com',
+ ],
+ [
+ 'UID' => 'uid2',
+ 'FN' => 'User2 @ example.com',
+ 'EMAIL' => [
+ ],
+ ],
+ [
+ 'UID' => 'uid1',
+ 'FN' => 'User @ example.com',
+ 'EMAIL' => [
+ 'username@example.com',
+ ],
+ ],
+ ],
+ false,
+ ['emails' => [], 'exact' => ['emails' => [['name' => 'User @ example.com', 'uuid' => 'uid1', 'type' => '', 'label' => 'User @ example.com (username@example.com)', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'username@example.com']]]]],
+ true,
+ false,
+ false,
+ ],
+ // data set 11
+ // contact with space
+ [
+ 'user name@localhost',
+ [
+ [
+ 'UID' => 'uid3',
+ 'FN' => 'User3 @ Localhost',
+ ],
+ [
+ 'UID' => 'uid2',
+ 'FN' => 'User2 @ Localhost',
+ 'EMAIL' => [
+ ],
+ ],
+ [
+ 'UID' => 'uid1',
+ 'FN' => 'User Name @ Localhost',
+ 'EMAIL' => [
+ 'user name@localhost',
+ ],
+ ],
+ ],
+ false,
+ ['emails' => [], 'exact' => ['emails' => []]],
+ false,
+ false,
+ false,
+ ],
+ // data set 12
+ // remote with space, no contact
+ [
+ 'user space@remote.com',
+ [
+ [
+ 'UID' => 'uid3',
+ 'FN' => 'User3 @ Localhost',
+ ],
+ [
+ 'UID' => 'uid2',
+ 'FN' => 'User2 @ Localhost',
+ 'EMAIL' => [
+ ],
+ ],
+ [
+ 'isLocalSystemBook' => true,
+ 'UID' => 'uid1',
+ 'FN' => 'User @ Localhost',
+ 'EMAIL' => [
+ 'username@localhost',
+ ],
+ ],
+ ],
+ false,
+ ['emails' => [], 'exact' => ['emails' => []]],
+ false,
+ false,
+ false,
+ ],
+ // data set 13
+ // Local user found by email
+ [
+ 'test@example.com',
+ [
+ [
+ 'UID' => 'uid1',
+ 'FN' => 'User',
+ 'EMAIL' => ['test@example.com'],
+ 'CLOUD' => ['test@localhost'],
+ 'isLocalSystemBook' => true,
+ ]
+ ],
+ false,
+ ['users' => [], 'exact' => ['users' => [['uuid' => 'uid1', 'name' => 'User', 'label' => 'User (test@example.com)','value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test'], 'shareWithDisplayNameUnique' => 'test@example.com']]]],
+ true,
+ false,
+ true,
+ ],
+ // data set 14
+ // Current local user found by email => no result
+ [
+ 'test@example.com',
+ [
+ [
+ 'UID' => 'uid1',
+ 'FN' => 'User',
+ 'EMAIL' => ['test@example.com'],
+ 'CLOUD' => ['current@localhost'],
+ 'isLocalSystemBook' => true,
+ ]
+ ],
+ true,
+ ['exact' => []],
+ false,
+ false,
+ true,
+ ],
+ // data set 15
+ // Pagination and "more results" for user matches byyyyyyy emails
+ [
+ 'test@example',
+ [
+ [
+ 'UID' => 'uid1',
+ 'FN' => 'User1',
+ 'EMAIL' => ['test@example.com'],
+ 'CLOUD' => ['test1@localhost'],
+ 'isLocalSystemBook' => true,
+ ],
+ [
+ 'UID' => 'uid2',
+ 'FN' => 'User2',
+ 'EMAIL' => ['test@example.de'],
+ 'CLOUD' => ['test2@localhost'],
+ 'isLocalSystemBook' => true,
+ ],
+ [
+ 'UID' => 'uid3',
+ 'FN' => 'User3',
+ 'EMAIL' => ['test@example.org'],
+ 'CLOUD' => ['test3@localhost'],
+ 'isLocalSystemBook' => true,
+ ],
+ [
+ 'UID' => 'uid4',
+ 'FN' => 'User4',
+ 'EMAIL' => ['test@example.net'],
+ 'CLOUD' => ['test4@localhost'],
+ 'isLocalSystemBook' => true,
+ ],
+ ],
+ true,
+ ['users' => [
+ ['uuid' => 'uid1', 'name' => 'User1', 'label' => 'User1 (test@example.com)', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1'], 'shareWithDisplayNameUnique' => 'test@example.com'],
+ ['uuid' => 'uid2', 'name' => 'User2', 'label' => 'User2 (test@example.de)', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test2'], 'shareWithDisplayNameUnique' => 'test@example.de'],
+ ], 'emails' => [], 'exact' => ['users' => [], 'emails' => []]],
+ false,
+ true,
+ false,
+ ],
+ // data set 16
+ // Pagination and "more results" for normal emails
+ [
+ 'test@example',
+ [
+ [
+ 'UID' => 'uid1',
+ 'FN' => 'User1',
+ 'EMAIL' => ['test@example.com'],
+ 'CLOUD' => ['test1@localhost'],
+ ],
+ [
+ 'UID' => 'uid2',
+ 'FN' => 'User2',
+ 'EMAIL' => ['test@example.de'],
+ 'CLOUD' => ['test2@localhost'],
+ ],
+ [
+ 'UID' => 'uid3',
+ 'FN' => 'User3',
+ 'EMAIL' => ['test@example.org'],
+ 'CLOUD' => ['test3@localhost'],
+ ],
+ [
+ 'UID' => 'uid4',
+ 'FN' => 'User4',
+ 'EMAIL' => ['test@example.net'],
+ 'CLOUD' => ['test4@localhost'],
+ ],
+ ],
+ true,
+ ['emails' => [
+ ['uuid' => 'uid1', 'name' => 'User1', 'type' => '', 'label' => 'User1 (test@example.com)', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'test@example.com']],
+ ['uuid' => 'uid2', 'name' => 'User2', 'type' => '', 'label' => 'User2 (test@example.de)', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'test@example.de']],
+ ], 'exact' => ['emails' => []]],
+ false,
+ true,
+ false,
+ ],
+ // data set 17
+ // multiple email addresses with type
+ [
+ 'User Name',
+ [
+ [
+ 'UID' => 'uid3',
+ 'FN' => 'User3',
+ ],
+ [
+ 'UID' => 'uid2',
+ 'FN' => 'User2',
+ 'EMAIL' => [
+ ],
+ ],
+ [
+ 'UID' => 'uid1',
+ 'FN' => 'User Name',
+ 'EMAIL' => [
+ ['type' => 'HOME', 'value' => 'username@example.com'],
+ ['type' => 'WORK', 'value' => 'other@example.com'],
+ ],
+ ],
+ ],
+ false,
+ ['emails' => [
+ ], 'exact' => ['emails' => [
+ ['name' => 'User Name', 'uuid' => 'uid1', 'type' => 'HOME', 'label' => 'User Name (username@example.com)', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'username@example.com']],
+ ['name' => 'User Name', 'uuid' => 'uid1', 'type' => 'WORK', 'label' => 'User Name (other@example.com)', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'other@example.com']]
+ ]]],
+ false,
+ false,
+ false,
+ ],
+ // data set 18
+ // idn email
+ [
+ 'test@lölölölölölölöl.com',
+ [],
+ true,
+ ['emails' => [], 'exact' => ['emails' => [['uuid' => 'test@lölölölölölölöl.com', 'label' => 'test@lölölölölölölöl.com', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'test@lölölölölölölöl.com']]]]],
+ false,
+ false,
+ true,
+ ],
+ ];
+ }
+
+ /**
+ *
+ * @param string $searchTerm
+ * @param array $contacts
+ * @param array $expected
+ * @param bool $exactIdMatch
+ * @param bool $reachedEnd
+ * @param array groups
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataGetEmailGroupsOnly')]
+ public function testSearchGroupsOnly($searchTerm, $contacts, $expected, $exactIdMatch, $reachedEnd, $userToGroupMapping, $validEmail): void {
+ $this->config->expects($this->any())
+ ->method('getAppValue')
+ ->willReturnCallback(
+ function ($appName, $key, $default) {
+ if ($appName === 'core' && $key === 'shareapi_allow_share_dialog_user_enumeration') {
+ return 'yes';
+ } elseif ($appName === 'core' && $key === 'shareapi_only_share_with_group_members') {
+ return 'yes';
+ }
+ return $default;
+ }
+ );
+
+ $this->instantiatePlugin();
+
+ /** @var IUser|\PHPUnit\Framework\MockObject\MockObject */
+ $currentUser = $this->createMock('\OCP\IUser');
+
+ $currentUser->expects($this->any())
+ ->method('getUID')
+ ->willReturn('currentUser');
+
+ $this->mailer->method('validateMailAddress')
+ ->willReturn($validEmail);
+
+ $this->contactsManager->expects($this->any())
+ ->method('search')
+ ->willReturnCallback(function ($search, $searchAttributes) use ($searchTerm, $contacts) {
+ if ($search === $searchTerm) {
+ return $contacts;
+ }
+ return [];
+ });
+
+ $this->userSession->expects($this->any())
+ ->method('getUser')
+ ->willReturn($currentUser);
+
+ $this->groupManager->expects($this->any())
+ ->method('getUserGroupIds')
+ ->willReturnCallback(function (IUser $user) use ($userToGroupMapping) {
+ return $userToGroupMapping[$user->getUID()];
+ });
+
+ $this->groupManager->expects($this->any())
+ ->method('isInGroup')
+ ->willReturnCallback(function ($userId, $group) use ($userToGroupMapping) {
+ return in_array($group, $userToGroupMapping[$userId]);
+ });
+
+ $moreResults = $this->plugin->search($searchTerm, 2, 0, $this->searchResult);
+ $result = $this->searchResult->asArray();
+
+ $this->assertSame($exactIdMatch, $this->searchResult->hasExactIdMatch(new SearchResultType('emails')));
+ $this->assertEquals($expected, $result);
+ $this->assertSame($reachedEnd, $moreResults);
+ }
+
+ public static function dataGetEmailGroupsOnly(): array {
+ return [
+ // The user `User` can share with the current user
+ [
+ 'test',
+ [
+ [
+ 'FN' => 'User',
+ 'EMAIL' => ['test@example.com'],
+ 'CLOUD' => ['test@localhost'],
+ 'isLocalSystemBook' => true,
+ 'UID' => 'User'
+ ]
+ ],
+ ['users' => [['label' => 'User (test@example.com)', 'uuid' => 'User', 'name' => 'User', 'value' => ['shareType' => 0, 'shareWith' => 'test'],'shareWithDisplayNameUnique' => 'test@example.com',]], 'emails' => [], 'exact' => ['emails' => [], 'users' => []]],
+ false,
+ false,
+ [
+ 'currentUser' => ['group1'],
+ 'User' => ['group1']
+ ],
+ false,
+ ],
+ // The user `User` cannot share with the current user
+ [
+ 'test',
+ [
+ [
+ 'FN' => 'User',
+ 'EMAIL' => ['test@example.com'],
+ 'CLOUD' => ['test@localhost'],
+ 'isLocalSystemBook' => true,
+ 'UID' => 'User'
+ ]
+ ],
+ ['emails' => [], 'exact' => ['emails' => []]],
+ false,
+ false,
+ [
+ 'currentUser' => ['group1'],
+ 'User' => ['group2']
+ ],
+ false,
+ ],
+ // The user `User` cannot share with the current user, but there is an exact match on the e-mail address -> share by e-mail
+ [
+ 'test@example.com',
+ [
+ [
+ 'FN' => 'User',
+ 'EMAIL' => ['test@example.com'],
+ 'CLOUD' => ['test@localhost'],
+ 'isLocalSystemBook' => true,
+ 'UID' => 'User'
+ ]
+ ],
+ ['emails' => [], 'exact' => ['emails' => [['label' => 'test@example.com', 'uuid' => 'test@example.com', 'value' => ['shareType' => 4,'shareWith' => 'test@example.com']]]]],
+ false,
+ false,
+ [
+ 'currentUser' => ['group1'],
+ 'User' => ['group2']
+ ],
+ true,
+ ]
+ ];
+ }
+}
diff --git a/tests/lib/Collaboration/Collaborators/RemotePluginTest.php b/tests/lib/Collaboration/Collaborators/RemotePluginTest.php
new file mode 100644
index 00000000000..a9a5e05dfe4
--- /dev/null
+++ b/tests/lib/Collaboration/Collaborators/RemotePluginTest.php
@@ -0,0 +1,430 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Test\Collaboration\Collaborators;
+
+use OC\Collaboration\Collaborators\RemotePlugin;
+use OC\Collaboration\Collaborators\SearchResult;
+use OC\Federation\CloudIdManager;
+use OCP\Collaboration\Collaborators\SearchResultType;
+use OCP\Contacts\IManager;
+use OCP\EventDispatcher\IEventDispatcher;
+use OCP\Federation\ICloudIdManager;
+use OCP\ICacheFactory;
+use OCP\IConfig;
+use OCP\IURLGenerator;
+use OCP\IUser;
+use OCP\IUserManager;
+use OCP\IUserSession;
+use OCP\Share\IShare;
+use Test\TestCase;
+
+class RemotePluginTest extends TestCase {
+ /** @var IUserManager|\PHPUnit\Framework\MockObject\MockObject */
+ protected $userManager;
+
+ /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
+ protected $config;
+
+ /** @var IManager|\PHPUnit\Framework\MockObject\MockObject */
+ protected $contactsManager;
+
+ /** @var ICloudIdManager|\PHPUnit\Framework\MockObject\MockObject */
+ protected $cloudIdManager;
+
+ /** @var RemotePlugin */
+ protected $plugin;
+
+ /** @var SearchResult */
+ protected $searchResult;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->userManager = $this->createMock(IUserManager::class);
+ $this->config = $this->createMock(IConfig::class);
+ $this->contactsManager = $this->createMock(IManager::class);
+ $this->cloudIdManager = new CloudIdManager(
+ $this->createMock(ICacheFactory::class),
+ $this->createMock(IEventDispatcher::class),
+ $this->contactsManager,
+ $this->createMock(IURLGenerator::class),
+ $this->createMock(IUserManager::class),
+ );
+ $this->searchResult = new SearchResult();
+ }
+
+ public function instantiatePlugin() {
+ $user = $this->createMock(IUser::class);
+ $user->expects($this->any())
+ ->method('getUID')
+ ->willReturn('admin');
+ $userSession = $this->createMock(IUserSession::class);
+ $userSession->expects($this->any())
+ ->method('getUser')
+ ->willReturn($user);
+ $this->plugin = new RemotePlugin($this->contactsManager, $this->cloudIdManager, $this->config, $this->userManager, $userSession);
+ }
+
+ /**
+ *
+ * @param string $searchTerm
+ * @param array $contacts
+ * @param bool $shareeEnumeration
+ * @param array $expected
+ * @param bool $exactIdMatch
+ * @param bool $reachedEnd
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataGetRemote')]
+ public function testSearch($searchTerm, array $contacts, $shareeEnumeration, array $expected, $exactIdMatch, $reachedEnd): void {
+ $this->config->expects($this->any())
+ ->method('getAppValue')
+ ->willReturnCallback(
+ function ($appName, $key, $default) use ($shareeEnumeration) {
+ if ($appName === 'core' && $key === 'shareapi_allow_share_dialog_user_enumeration') {
+ return $shareeEnumeration ? 'yes' : 'no';
+ }
+ return $default;
+ }
+ );
+
+ $this->instantiatePlugin();
+
+ $this->contactsManager->expects($this->any())
+ ->method('search')
+ ->willReturnCallback(function ($search, $searchAttributes) use ($searchTerm, $contacts) {
+ if ($search === $searchTerm) {
+ return $contacts;
+ }
+ return [];
+ });
+
+ $moreResults = $this->plugin->search($searchTerm, 2, 0, $this->searchResult);
+ $result = $this->searchResult->asArray();
+
+ $this->assertSame($exactIdMatch, $this->searchResult->hasExactIdMatch(new SearchResultType('remotes')));
+ $this->assertEquals($expected, $result);
+ $this->assertSame($reachedEnd, $moreResults);
+ }
+
+ /**
+ *
+ * @param string $remote
+ * @param string $expectedUser
+ * @param string $expectedUrl
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTestSplitUserRemote')]
+ public function testSplitUserRemote($remote, $expectedUser, $expectedUrl): void {
+ $this->instantiatePlugin();
+
+ $this->contactsManager->expects($this->any())
+ ->method('search')
+ ->willReturn([]);
+
+ [$remoteUser, $remoteUrl] = $this->plugin->splitUserRemote($remote);
+ $this->assertSame($expectedUser, $remoteUser);
+ $this->assertSame($expectedUrl, $remoteUrl);
+ }
+
+ /**
+ * @param string $id
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataTestSplitUserRemoteError')]
+ public function testSplitUserRemoteError($id): void {
+ $this->expectException(\Exception::class);
+
+ $this->instantiatePlugin();
+ $this->plugin->splitUserRemote($id);
+ }
+
+ public static function dataGetRemote() {
+ return [
+ ['test', [], true, ['remotes' => [], 'exact' => ['remotes' => []]], false, true],
+ ['test', [], false, ['remotes' => [], 'exact' => ['remotes' => []]], false, true],
+ [
+ 'test@remote',
+ [],
+ true,
+ ['remotes' => [], 'exact' => ['remotes' => [['label' => 'test (remote)', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'test@remote', 'server' => 'remote'], 'uuid' => 'test', 'name' => 'test']]]],
+ false,
+ true,
+ ],
+ [
+ 'test@remote',
+ [],
+ false,
+ ['remotes' => [], 'exact' => ['remotes' => [['label' => 'test (remote)', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'test@remote', 'server' => 'remote'], 'uuid' => 'test', 'name' => 'test']]]],
+ false,
+ true,
+ ],
+ [
+ 'test',
+ [
+ [
+ 'UID' => 'uid',
+ 'FN' => 'User3 @ Localhost',
+ ],
+ [
+ 'UID' => 'uid',
+ 'FN' => 'User2 @ Localhost',
+ 'CLOUD' => [
+ ],
+ ],
+ [
+ 'UID' => 'uid1',
+ 'FN' => 'User @ Localhost',
+ 'CLOUD' => [
+ 'username@localhost',
+ ],
+ ],
+ ],
+ true,
+ ['remotes' => [['name' => 'User @ Localhost', 'label' => 'User @ Localhost (username@localhost)', 'uuid' => 'uid1', 'type' => '', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]], 'exact' => ['remotes' => []]],
+ false,
+ true,
+ ],
+ [
+ 'test',
+ [
+ [
+ 'UID' => 'uid',
+ 'FN' => 'User3 @ Localhost',
+ ],
+ [
+ 'UID' => 'uid',
+ 'FN' => 'User2 @ Localhost',
+ 'CLOUD' => [
+ ],
+ ],
+ [
+ 'UID' => 'uid',
+ 'FN' => 'User @ Localhost',
+ 'CLOUD' => [
+ 'username@localhost',
+ ],
+ ],
+ ],
+ false,
+ ['remotes' => [], 'exact' => ['remotes' => []]],
+ false,
+ true,
+ ],
+ [
+ 'test@remote',
+ [
+ [
+ 'UID' => 'uid',
+ 'FN' => 'User3 @ Localhost',
+ ],
+ [
+ 'UID' => 'uid',
+ 'FN' => 'User2 @ Localhost',
+ 'CLOUD' => [
+ ],
+ ],
+ [
+ 'UID' => 'uid',
+ 'FN' => 'User @ Localhost',
+ 'CLOUD' => [
+ 'username@localhost',
+ ],
+ ],
+ ],
+ true,
+ ['remotes' => [['name' => 'User @ Localhost', 'label' => 'User @ Localhost (username@localhost)', 'uuid' => 'uid', 'type' => '', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]], 'exact' => ['remotes' => [['label' => 'test (remote)', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'test@remote', 'server' => 'remote'], 'uuid' => 'test', 'name' => 'test']]]],
+ false,
+ true,
+ ],
+ [
+ 'test@remote',
+ [
+ [
+ 'UID' => 'uid',
+ 'FN' => 'User3 @ Localhost',
+ ],
+ [
+ 'UID' => 'uid',
+ 'FN' => 'User2 @ Localhost',
+ 'CLOUD' => [
+ ],
+ ],
+ [
+ 'UID' => 'uid',
+ 'FN' => 'User @ Localhost',
+ 'CLOUD' => [
+ 'username@localhost',
+ ],
+ ],
+ ],
+ false,
+ ['remotes' => [], 'exact' => ['remotes' => [['label' => 'test (remote)', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'test@remote', 'server' => 'remote'], 'uuid' => 'test', 'name' => 'test']]]],
+ false,
+ true,
+ ],
+ [
+ 'username@localhost',
+ [
+ [
+ 'UID' => 'uid3',
+ 'FN' => 'User3 @ Localhost',
+ ],
+ [
+ 'UID' => '2',
+ 'FN' => 'User2 @ Localhost',
+ 'CLOUD' => [
+ ],
+ ],
+ [
+ 'UID' => 'uid1',
+ 'FN' => 'User @ Localhost',
+ 'CLOUD' => [
+ 'username@localhost',
+ ],
+ ],
+ ],
+ true,
+ ['remotes' => [], 'exact' => ['remotes' => [['name' => 'User @ Localhost', 'label' => 'User @ Localhost (username@localhost)', 'uuid' => 'uid1', 'type' => '', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]]]],
+ true,
+ true,
+ ],
+ [
+ 'username@localhost',
+ [
+ [
+ 'UID' => 'uid3',
+ 'FN' => 'User3 @ Localhost',
+ ],
+ [
+ 'UID' => 'uid2',
+ 'FN' => 'User2 @ Localhost',
+ 'CLOUD' => [
+ ],
+ ],
+ [
+ 'UID' => 'uid1',
+ 'FN' => 'User @ Localhost',
+ 'CLOUD' => [
+ 'username@localhost',
+ ],
+ ],
+ ],
+ false,
+ ['remotes' => [], 'exact' => ['remotes' => [['name' => 'User @ Localhost', 'label' => 'User @ Localhost (username@localhost)', 'uuid' => 'uid1', 'type' => '', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]]]],
+ true,
+ true,
+ ],
+ // contact with space
+ [
+ 'user name@localhost',
+ [
+ [
+ 'UID' => 'uid1',
+ 'FN' => 'User3 @ Localhost',
+ ],
+ [
+ 'UID' => 'uid2',
+ 'FN' => 'User2 @ Localhost',
+ 'CLOUD' => [
+ ],
+ ],
+ [
+ 'UID' => 'uid3',
+ 'FN' => 'User Name @ Localhost',
+ 'CLOUD' => [
+ 'user name@localhost',
+ ],
+ ],
+ ],
+ false,
+ ['remotes' => [], 'exact' => ['remotes' => [['name' => 'User Name @ Localhost', 'label' => 'User Name @ Localhost (user name@localhost)', 'uuid' => 'uid3', 'type' => '', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'user name@localhost', 'server' => 'localhost']]]]],
+ true,
+ true,
+ ],
+ // remote with space, no contact
+ [
+ 'user space@remote',
+ [
+ [
+ 'UID' => 'uid3',
+ 'FN' => 'User3 @ Localhost',
+ ],
+ [
+ 'UID' => 'uid2',
+ 'FN' => 'User2 @ Localhost',
+ 'CLOUD' => [
+ ],
+ ],
+ [
+ 'UID' => 'uid1',
+ 'FN' => 'User @ Localhost',
+ 'CLOUD' => [
+ 'username@localhost',
+ ],
+ ],
+ ],
+ false,
+ ['remotes' => [], 'exact' => ['remotes' => [['label' => 'user space (remote)', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'user space@remote', 'server' => 'remote'], 'uuid' => 'user space', 'name' => 'user space']]]],
+ false,
+ true,
+ ],
+ ];
+ }
+
+ public static function dataTestSplitUserRemote(): array {
+ $userPrefix = ['user@name', 'username'];
+ $protocols = ['', 'http://', 'https://'];
+ $remotes = [
+ 'localhost',
+ 'local.host',
+ 'dev.local.host',
+ 'dev.local.host/path',
+ 'dev.local.host/at@inpath',
+ '127.0.0.1',
+ '::1',
+ '::192.0.2.128',
+ '::192.0.2.128/at@inpath',
+ ];
+
+ $testCases = [];
+ foreach ($userPrefix as $user) {
+ foreach ($remotes as $remote) {
+ foreach ($protocols as $protocol) {
+ $baseUrl = $user . '@' . $protocol . $remote;
+
+ if ($protocol === 'https://') {
+ // https:// protocol is not expected in the final result
+ $protocol = '';
+ }
+
+ $testCases[] = [$baseUrl, $user, $protocol . $remote];
+ $testCases[] = [$baseUrl . '/', $user, $protocol . $remote];
+ $testCases[] = [$baseUrl . '/index.php', $user, $protocol . $remote];
+ $testCases[] = [$baseUrl . '/index.php/s/token', $user, $protocol . $remote];
+ }
+ }
+ }
+ return $testCases;
+ }
+
+ public static function dataTestSplitUserRemoteError(): array {
+ return [
+ // Invalid path
+ ['user@'],
+
+ // Invalid user
+ ['@server'],
+ ['us/er@server'],
+ ['us:er@server'],
+
+ // Invalid splitting
+ ['user'],
+ [''],
+ ['us/erserver'],
+ ['us:erserver'],
+ ];
+ }
+}
diff --git a/tests/lib/Collaboration/Collaborators/SearchResultTest.php b/tests/lib/Collaboration/Collaborators/SearchResultTest.php
new file mode 100644
index 00000000000..687901c47a6
--- /dev/null
+++ b/tests/lib/Collaboration/Collaborators/SearchResultTest.php
@@ -0,0 +1,85 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Test\Collaboration\Collaborators;
+
+use OC\Collaboration\Collaborators\Search;
+use OC\Collaboration\Collaborators\SearchResult;
+use OCP\Collaboration\Collaborators\ISearch;
+use OCP\Collaboration\Collaborators\SearchResultType;
+use OCP\IContainer;
+use Test\TestCase;
+
+class SearchResultTest extends TestCase {
+ /** @var IContainer|\PHPUnit\Framework\MockObject\MockObject */
+ protected $container;
+ /** @var ISearch */
+ protected $search;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->container = $this->createMock(IContainer::class);
+
+ $this->search = new Search($this->container);
+ }
+
+ public static function dataAddResultSet(): array {
+ return [
+ [[], ['exact' => []]],
+ [['users' => ['exact' => null, 'loose' => []]], ['exact' => ['users' => []], 'users' => []]],
+ [['groups' => ['exact' => null, 'loose' => ['l1']]], ['exact' => ['groups' => []], 'groups' => ['l1']]],
+ [['users' => ['exact' => ['e1'], 'loose' => []]], ['exact' => ['users' => ['e1']], 'users' => []]],
+ ];
+ }
+
+ /**
+ * @param array $toAdd
+ * @param array $expected
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataAddResultSet')]
+ public function testAddResultSet(array $toAdd, array $expected): void {
+ $result = new SearchResult();
+
+ foreach ($toAdd as $type => $results) {
+ $result->addResultSet(new SearchResultType($type), $results['loose'], $results['exact']);
+ }
+
+ $this->assertEquals($expected, $result->asArray());
+ }
+
+ public static function dataHasResult(): array {
+ $result = ['value' => ['shareWith' => 'l1']];
+ return [
+ [[],'users', 'n1', false],
+ [['users' => ['exact' => null, 'loose' => [$result]]], 'users', 'l1', true],
+ [['users' => ['exact' => null, 'loose' => [$result]]], 'users', 'l2', false],
+ [['users' => ['exact' => null, 'loose' => [$result]]], 'groups', 'l1', false],
+ [['users' => ['exact' => [$result], 'loose' => []]], 'users', 'l1', true],
+ [['users' => ['exact' => [$result], 'loose' => []]], 'users', 'l2', false],
+ [['users' => ['exact' => [$result], 'loose' => []]], 'groups', 'l1', false],
+
+ ];
+ }
+
+ /**
+ * @param array $toAdd
+ * @param string $type
+ * @param string $id
+ * @param bool $expected
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataHasResult')]
+ public function testHasResult(array $toAdd, $type, $id, $expected): void {
+ $result = new SearchResult();
+
+ foreach ($toAdd as $addType => $results) {
+ $result->addResultSet(new SearchResultType($addType), $results['loose'], $results['exact']);
+ }
+
+ $this->assertSame($expected, $result->hasResult(new SearchResultType($type), $id));
+ }
+}
diff --git a/tests/lib/Collaboration/Collaborators/SearchTest.php b/tests/lib/Collaboration/Collaborators/SearchTest.php
new file mode 100644
index 00000000000..ade995ea526
--- /dev/null
+++ b/tests/lib/Collaboration/Collaborators/SearchTest.php
@@ -0,0 +1,253 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Test\Collaboration\Collaborators;
+
+use OC\Collaboration\Collaborators\Search;
+use OC\Collaboration\Collaborators\SearchResult;
+use OCP\Collaboration\Collaborators\ISearch;
+use OCP\Collaboration\Collaborators\ISearchPlugin;
+use OCP\Collaboration\Collaborators\SearchResultType;
+use OCP\IContainer;
+use OCP\Share\IShare;
+use Test\TestCase;
+
+class SearchTest extends TestCase {
+ /** @var IContainer|\PHPUnit\Framework\MockObject\MockObject */
+ protected $container;
+ /** @var ISearch */
+ protected $search;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->container = $this->createMock(IContainer::class);
+
+ $this->search = new Search($this->container);
+ }
+
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataSearchSharees')]
+ public function testSearch(
+ string $searchTerm,
+ array $shareTypes,
+ int $page,
+ int $perPage,
+ array $mockedUserResult,
+ array $mockedGroupsResult,
+ array $mockedRemotesResult,
+ array $mockedMailResult,
+ array $expected,
+ bool $expectedMoreResults,
+ ): void {
+ $searchResult = new SearchResult();
+
+ $userPlugin = $this->createMock(ISearchPlugin::class);
+ $userPlugin->expects($this->any())
+ ->method('search')
+ ->willReturnCallback(function () use ($searchResult, $mockedUserResult, $expectedMoreResults) {
+ $type = new SearchResultType('users');
+ $searchResult->addResultSet($type, $mockedUserResult);
+ return $expectedMoreResults;
+ });
+
+ $groupPlugin = $this->createMock(ISearchPlugin::class);
+ $groupPlugin->expects($this->any())
+ ->method('search')
+ ->willReturnCallback(function () use ($searchResult, $mockedGroupsResult, $expectedMoreResults) {
+ $type = new SearchResultType('groups');
+ $searchResult->addResultSet($type, $mockedGroupsResult);
+ return $expectedMoreResults;
+ });
+
+ $remotePlugin = $this->createMock(ISearchPlugin::class);
+ $remotePlugin->expects($this->any())
+ ->method('search')
+ ->willReturnCallback(function () use ($searchResult, $mockedRemotesResult, $expectedMoreResults) {
+ if ($mockedRemotesResult !== null) {
+ $type = new SearchResultType('remotes');
+ $searchResult->addResultSet($type, $mockedRemotesResult['results'], $mockedRemotesResult['exact']);
+ if ($mockedRemotesResult['exactIdMatch'] === true) {
+ $searchResult->markExactIdMatch($type);
+ }
+ }
+ return $expectedMoreResults;
+ });
+
+ $mailPlugin = $this->createMock(ISearchPlugin::class);
+ $mailPlugin->expects($this->any())
+ ->method('search')
+ ->willReturnCallback(function () use ($searchResult, $mockedMailResult, $expectedMoreResults) {
+ $type = new SearchResultType('emails');
+ $searchResult->addResultSet($type, $mockedMailResult);
+ return $expectedMoreResults;
+ });
+
+ $this->container->expects($this->any())
+ ->method('resolve')
+ ->willReturnCallback(function ($class) use ($searchResult, $userPlugin, $groupPlugin, $remotePlugin, $mailPlugin) {
+ if ($class === SearchResult::class) {
+ return $searchResult;
+ } elseif ($class === $userPlugin) {
+ return $userPlugin;
+ } elseif ($class === $groupPlugin) {
+ return $groupPlugin;
+ } elseif ($class === $remotePlugin) {
+ return $remotePlugin;
+ } elseif ($class === $mailPlugin) {
+ return $mailPlugin;
+ }
+ return null;
+ });
+
+ $this->search->registerPlugin(['shareType' => 'SHARE_TYPE_USER', 'class' => $userPlugin]);
+ $this->search->registerPlugin(['shareType' => 'SHARE_TYPE_GROUP', 'class' => $groupPlugin]);
+ $this->search->registerPlugin(['shareType' => 'SHARE_TYPE_REMOTE', 'class' => $remotePlugin]);
+ $this->search->registerPlugin(['shareType' => 'SHARE_TYPE_EMAIL', 'class' => $mailPlugin]);
+
+ [$results, $moreResults] = $this->search->search($searchTerm, $shareTypes, false, $perPage, $perPage * ($page - 1));
+
+ $this->assertEquals($expected, $results);
+ $this->assertSame($expectedMoreResults, $moreResults);
+ }
+
+ public static function dataSearchSharees(): array {
+ return [
+ // #0
+ [
+ 'test', [IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_REMOTE], 1, 2, [], [], ['results' => [], 'exact' => [], 'exactIdMatch' => false],
+ [],
+ [
+ 'exact' => ['users' => [], 'groups' => [], 'remotes' => []],
+ 'users' => [],
+ 'groups' => [],
+ 'remotes' => [],
+ ],
+ false
+ ],
+ // #1
+ [
+ 'test', [IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_REMOTE], 1, 2, [], [], ['results' => [], 'exact' => [], 'exactIdMatch' => false],
+ [],
+ [
+ 'exact' => ['users' => [], 'groups' => [], 'remotes' => []],
+ 'users' => [],
+ 'groups' => [],
+ 'remotes' => [],
+ ],
+ false
+ ],
+ // #2
+ [
+ 'test', [IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_REMOTE], 1, 2, [
+ ['label' => 'test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1']],
+ ], [
+ ['label' => 'testgroup1', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'testgroup1']],
+ ], [
+ 'results' => [['label' => 'testz@remote', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'testz@remote']]], 'exact' => [], 'exactIdMatch' => false,
+ ],
+ [],
+ [
+ 'exact' => ['users' => [], 'groups' => [], 'remotes' => []],
+ 'users' => [
+ ['label' => 'test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1']],
+ ],
+ 'groups' => [
+ ['label' => 'testgroup1', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'testgroup1']],
+ ],
+ 'remotes' => [
+ ['label' => 'testz@remote', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'testz@remote']],
+ ],
+ ], true,
+ ],
+ // #3 No groups requested
+ [
+ 'test', [IShare::TYPE_USER, IShare::TYPE_REMOTE], 1, 2, [
+ ['label' => 'test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1']],
+ ], [], [
+ 'results' => [['label' => 'testz@remote', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'testz@remote']]], 'exact' => [], 'exactIdMatch' => false
+ ],
+ [],
+ [
+ 'exact' => ['users' => [], 'remotes' => []],
+ 'users' => [
+ ['label' => 'test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1']],
+ ],
+ 'remotes' => [
+ ['label' => 'testz@remote', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'testz@remote']],
+ ],
+ ], false,
+ ],
+ // #4 Share type restricted to user - Only one user
+ [
+ 'test', [IShare::TYPE_USER], 1, 2, [
+ ['label' => 'test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1']],
+ ], [], [], [],
+ [
+ 'exact' => ['users' => []],
+ 'users' => [
+ ['label' => 'test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1']],
+ ],
+ ], false,
+ ],
+ // #5 Share type restricted to user - Multipage result
+ [
+ 'test', [IShare::TYPE_USER], 1, 2, [
+ ['label' => 'test 1', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1']],
+ ['label' => 'test 2', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test2']],
+ ], [], [], [],
+ [
+ 'exact' => ['users' => []],
+ 'users' => [
+ ['label' => 'test 1', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1']],
+ ['label' => 'test 2', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test2']],
+ ],
+ ], true,
+ ],
+ // #6 Mail shares filtered out in favor of remote shares
+ [
+ 'test', // search term
+ [IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_REMOTE, IShare::TYPE_EMAIL], // plugins
+ 1, // page
+ 10, // per page
+ [ // user result
+ ['label' => 'test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1']],
+ ],
+ [ // group result
+ ['label' => 'testgroup1', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'testgroup1']],
+ ],
+ [ // remote result
+ 'results' => [
+ ['label' => 'testz@remote.tld', 'uuid' => 'f3d78140-abcc-46df-b58d-c7cc1176aadf','value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'testz@remote.tld']]
+ ],
+ 'exact' => [],
+ 'exactIdMatch' => false,
+ ],
+ [ // mail result
+ ['label' => 'test Two', 'uuid' => 'b2321e9e-31af-43ac-a406-583fb26d1964', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'test2@remote.tld']],
+ ['label' => 'test One', 'uuid' => 'f3d78140-abcc-46df-b58d-c7cc1176aadf', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'testz@remote.tld']],
+ ],
+ [ // expected result
+ 'exact' => ['users' => [], 'groups' => [], 'remotes' => [], 'emails' => []],
+ 'users' => [
+ ['label' => 'test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1']],
+ ],
+ 'groups' => [
+ ['label' => 'testgroup1', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'testgroup1']],
+ ],
+ 'remotes' => [
+ ['label' => 'testz@remote.tld', 'uuid' => 'f3d78140-abcc-46df-b58d-c7cc1176aadf', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'testz@remote.tld']],
+ ],
+ 'emails' => [
+ // one passes, another is filtered out
+ ['label' => 'test Two', 'uuid' => 'b2321e9e-31af-43ac-a406-583fb26d1964', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'test2@remote.tld']]
+ ]
+ ],
+ false, // expected more results indicator
+ ],
+ ];
+ }
+}
diff --git a/tests/lib/Collaboration/Collaborators/UserPluginTest.php b/tests/lib/Collaboration/Collaborators/UserPluginTest.php
new file mode 100644
index 00000000000..cb4949fb86d
--- /dev/null
+++ b/tests/lib/Collaboration/Collaborators/UserPluginTest.php
@@ -0,0 +1,810 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Test\Collaboration\Collaborators;
+
+use OC\Collaboration\Collaborators\SearchResult;
+use OC\Collaboration\Collaborators\UserPlugin;
+use OC\KnownUser\KnownUserService;
+use OCP\Collaboration\Collaborators\ISearchResult;
+use OCP\IConfig;
+use OCP\IGroup;
+use OCP\IGroupManager;
+use OCP\IUser;
+use OCP\IUserManager;
+use OCP\IUserSession;
+use OCP\Share\IShare;
+use OCP\UserStatus\IManager as IUserStatusManager;
+use PHPUnit\Framework\MockObject\MockObject;
+use Test\TestCase;
+
+class UserPluginTest extends TestCase {
+ /** @var IConfig|MockObject */
+ protected $config;
+
+ /** @var IUserManager|MockObject */
+ protected $userManager;
+
+ /** @var IGroupManager|MockObject */
+ protected $groupManager;
+
+ /** @var IUserSession|MockObject */
+ protected $session;
+
+ /** @var KnownUserService|MockObject */
+ protected $knownUserService;
+
+ /** @var IUserStatusManager|MockObject */
+ protected $userStatusManager;
+
+ /** @var UserPlugin */
+ protected $plugin;
+
+ /** @var ISearchResult */
+ protected $searchResult;
+
+ protected int $limit = 2;
+
+ protected int $offset = 0;
+
+ /** @var IUser|MockObject */
+ protected $user;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->config = $this->createMock(IConfig::class);
+
+ $this->userManager = $this->createMock(IUserManager::class);
+
+ $this->groupManager = $this->createMock(IGroupManager::class);
+
+ $this->session = $this->createMock(IUserSession::class);
+
+ $this->knownUserService = $this->createMock(KnownUserService::class);
+
+ $this->userStatusManager = $this->createMock(IUserStatusManager::class);
+
+ $this->searchResult = new SearchResult();
+
+ $this->user = $this->getUserMock('admin', 'Administrator');
+ }
+
+ public function instantiatePlugin() {
+ // cannot be done within setUp, because dependent mocks needs to be set
+ // up with configuration etc. first
+ $this->plugin = new UserPlugin(
+ $this->config,
+ $this->userManager,
+ $this->groupManager,
+ $this->session,
+ $this->knownUserService,
+ $this->userStatusManager
+ );
+ }
+
+ public function mockConfig($mockedSettings) {
+ $this->config->expects($this->any())
+ ->method('getAppValue')
+ ->willReturnCallback(
+ function ($appName, $key, $default) use ($mockedSettings) {
+ return $mockedSettings[$appName][$key] ?? $default;
+ }
+ );
+ }
+
+ public function getUserMock($uid, $displayName, $enabled = true, $groups = []) {
+ $user = $this->createMock(IUser::class);
+
+ $user->expects($this->any())
+ ->method('getUID')
+ ->willReturn($uid);
+
+ $user->expects($this->any())
+ ->method('getDisplayName')
+ ->willReturn($displayName);
+
+ $user->expects($this->any())
+ ->method('isEnabled')
+ ->willReturn($enabled);
+
+ return $user;
+ }
+
+ public function getGroupMock($gid) {
+ $group = $this->createMock(IGroup::class);
+
+ $group->expects($this->any())
+ ->method('getGID')
+ ->willReturn($gid);
+
+ return $group;
+ }
+
+ public function dataGetUsers(): array {
+ return [
+ ['test', false, true, [], [], [], [], true, false],
+ ['test', false, false, [], [], [], [], true, false],
+ ['test', true, true, [], [], [], [], true, false],
+ ['test', true, false, [], [], [], [], true, false],
+ [
+ 'test', false, true, [], [],
+ [
+ ['label' => 'Test', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test'],
+ ], [], true, $this->getUserMock('test', 'Test'),
+ ],
+ [
+ 'test', false, false, [], [],
+ [
+ ['label' => 'Test', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test'],
+ ], [], true, $this->getUserMock('test', 'Test'),
+ ],
+ [
+ 'test', true, true, [], [],
+ [], [], true, $this->getUserMock('test', 'Test'),
+ ],
+ [
+ 'test', true, false, [], [],
+ [], [], true, $this->getUserMock('test', 'Test'),
+ ],
+ [
+ 'test', true, true, ['test-group'], [['test-group', 'test', 2, 0, []]],
+ [
+ ['label' => 'Test', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test'],
+ ], [], true, $this->getUserMock('test', 'Test'),
+ ],
+ [
+ 'test', true, false, ['test-group'], [['test-group', 'test', 2, 0, []]],
+ [
+ ['label' => 'Test', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test'],
+ ], [], true, $this->getUserMock('test', 'Test'),
+ ],
+ [
+ 'test',
+ false,
+ true,
+ [],
+ [
+ $this->getUserMock('test1', 'Test One'),
+ ],
+ [],
+ [
+ ['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test1'],
+ ],
+ true,
+ false,
+ ],
+ [
+ 'test',
+ false,
+ false,
+ [],
+ [
+ $this->getUserMock('test1', 'Test One'),
+ ],
+ [],
+ [],
+ true,
+ false,
+ ],
+ [
+ 'test',
+ false,
+ true,
+ [],
+ [
+ $this->getUserMock('test1', 'Test One'),
+ $this->getUserMock('test2', 'Test Two'),
+ ],
+ [],
+ [
+ ['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test1'],
+ ['label' => 'Test Two', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test2'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test2'],
+ ],
+ false,
+ false,
+ ],
+ [
+ 'test',
+ false,
+ false,
+ [],
+ [
+ $this->getUserMock('test1', 'Test One'),
+ $this->getUserMock('test2', 'Test Two'),
+ ],
+ [],
+ [],
+ true,
+ false,
+ ],
+ [
+ 'test',
+ false,
+ true,
+ [],
+ [
+ $this->getUserMock('test0', 'Test'),
+ $this->getUserMock('test1', 'Test One'),
+ $this->getUserMock('test2', 'Test Two'),
+ ],
+ [
+ ['label' => 'Test', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test0'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test0'],
+ ],
+ [
+ ['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test1'],
+ ['label' => 'Test Two', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test2'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test2'],
+ ],
+ false,
+ false,
+ ],
+ [
+ 'test',
+ false,
+ true,
+ [],
+ [
+ $this->getUserMock('test0', 'Test'),
+ $this->getUserMock('test1', 'Test One'),
+ $this->getUserMock('test2', 'Test Two'),
+ ],
+ [
+ ['label' => 'Test', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test0'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test0'],
+ ],
+ [
+ ['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test1'],
+ ['label' => 'Test Two', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test2'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test2'],
+ ],
+ false,
+ false,
+ [],
+ true,
+ ],
+ [
+ 'test',
+ false,
+ false,
+ [],
+ [
+ $this->getUserMock('test0', 'Test'),
+ $this->getUserMock('test1', 'Test One'),
+ $this->getUserMock('test2', 'Test Two'),
+ ],
+ [
+ ['label' => 'Test', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test0'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test0'],
+ ],
+ [],
+ true,
+ false,
+ ],
+ [
+ 'test',
+ true,
+ true,
+ ['abc', 'xyz'],
+ [
+ ['abc', 'test', 2, 0, ['test1' => 'Test One']],
+ ['xyz', 'test', 2, 0, []],
+ ],
+ [],
+ [
+ ['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test1'],
+ ],
+ true,
+ false,
+ [['test1', $this->getUserMock('test1', 'Test One')]],
+ ],
+ [
+ 'test',
+ true,
+ false,
+ ['abc', 'xyz'],
+ [
+ ['abc', 'test', 2, 0, ['test1' => 'Test One']],
+ ['xyz', 'test', 2, 0, []],
+ ],
+ [],
+ [],
+ true,
+ false,
+ [['test1', $this->getUserMock('test1', 'Test One')]],
+ ],
+ [
+ 'test',
+ true,
+ true,
+ ['abc', 'xyz'],
+ [
+ ['abc', 'test', 2, 0, [
+ 'test1' => 'Test One',
+ 'test2' => 'Test Two',
+ ]],
+ ['xyz', 'test', 2, 0, [
+ 'test1' => 'Test One',
+ 'test2' => 'Test Two',
+ ]],
+ ],
+ [],
+ [
+ ['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test1'],
+ ['label' => 'Test Two', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test2'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test2'],
+ ],
+ true,
+ false,
+ [
+ ['test1', $this->getUserMock('test1', 'Test One')],
+ ['test2', $this->getUserMock('test2', 'Test Two')],
+ ],
+ ],
+ [
+ 'test',
+ true,
+ false,
+ ['abc', 'xyz'],
+ [
+ ['abc', 'test', 2, 0, [
+ 'test1' => 'Test One',
+ 'test2' => 'Test Two',
+ ]],
+ ['xyz', 'test', 2, 0, [
+ 'test1' => 'Test One',
+ 'test2' => 'Test Two',
+ ]],
+ ],
+ [],
+ [],
+ true,
+ false,
+ [
+ ['test1', $this->getUserMock('test1', 'Test One')],
+ ['test2', $this->getUserMock('test2', 'Test Two')],
+ ],
+ ],
+ [
+ 'test',
+ true,
+ true,
+ ['abc', 'xyz'],
+ [
+ ['abc', 'test', 2, 0, [
+ 'test' => 'Test One',
+ ]],
+ ['xyz', 'test', 2, 0, [
+ 'test2' => 'Test Two',
+ ]],
+ ],
+ [
+ ['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test'],
+ ],
+ [
+ ['label' => 'Test Two', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test2'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test2'],
+ ],
+ false,
+ false,
+ [
+ ['test', $this->getUserMock('test', 'Test One')],
+ ['test2', $this->getUserMock('test2', 'Test Two')],
+ ],
+ ],
+ [
+ 'test',
+ true,
+ false,
+ ['abc', 'xyz'],
+ [
+ ['abc', 'test', 2, 0, [
+ 'test' => 'Test One',
+ ]],
+ ['xyz', 'test', 2, 0, [
+ 'test2' => 'Test Two',
+ ]],
+ ],
+ [
+ ['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test'], 'icon' => 'icon-user', 'subline' => null, 'status' => [], 'shareWithDisplayNameUnique' => 'test'],
+ ],
+ [],
+ true,
+ false,
+ [
+ ['test', $this->getUserMock('test', 'Test One')],
+ ['test2', $this->getUserMock('test2', 'Test Two')],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ *
+ * @param string $searchTerm
+ * @param bool $shareWithGroupOnly
+ * @param bool $shareeEnumeration
+ * @param array $groupResponse
+ * @param array $userResponse
+ * @param array $exactExpected
+ * @param array $expected
+ * @param bool $reachedEnd
+ * @param bool|IUser $singleUser
+ * @param array $users
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataGetUsers')]
+ public function testSearch(
+ $searchTerm,
+ $shareWithGroupOnly,
+ $shareeEnumeration,
+ array $groupResponse,
+ array $userResponse,
+ array $exactExpected,
+ array $expected,
+ $reachedEnd,
+ $singleUser,
+ array $users = [],
+ $shareeEnumerationPhone = false,
+ ): void {
+ $this->mockConfig(['core' => [
+ 'shareapi_only_share_with_group_members' => $shareWithGroupOnly ? 'yes' : 'no',
+ 'shareapi_allow_share_dialog_user_enumeration' => $shareeEnumeration? 'yes' : 'no',
+ 'shareapi_restrict_user_enumeration_to_group' => false ? 'yes' : 'no',
+ 'shareapi_restrict_user_enumeration_to_phone' => $shareeEnumerationPhone ? 'yes' : 'no',
+ ]]);
+
+ $this->instantiatePlugin();
+
+ $this->session->expects($this->any())
+ ->method('getUser')
+ ->willReturn($this->user);
+
+ if (!$shareWithGroupOnly) {
+ if ($shareeEnumerationPhone) {
+ $this->userManager->expects($this->once())
+ ->method('searchKnownUsersByDisplayName')
+ ->with($this->user->getUID(), $searchTerm, $this->limit, $this->offset)
+ ->willReturn($userResponse);
+
+ $this->knownUserService->method('isKnownToUser')
+ ->willReturnMap([
+ [$this->user->getUID(), 'test0', true],
+ [$this->user->getUID(), 'test1', true],
+ [$this->user->getUID(), 'test2', true],
+ ]);
+ } else {
+ $this->userManager->expects($this->once())
+ ->method('searchDisplayName')
+ ->with($searchTerm, $this->limit, $this->offset)
+ ->willReturn($userResponse);
+ }
+ } else {
+ $this->groupManager->method('getUserGroupIds')
+ ->with($this->user)
+ ->willReturn($groupResponse);
+
+ if ($singleUser !== false) {
+ $this->groupManager->method('getUserGroupIds')
+ ->with($singleUser)
+ ->willReturn($groupResponse);
+ }
+
+ $this->groupManager->method('displayNamesInGroup')
+ ->willReturnMap($userResponse);
+ }
+
+ if ($singleUser !== false) {
+ $users[] = [$searchTerm, $singleUser];
+ }
+
+ if (!empty($users)) {
+ $this->userManager->expects($this->atLeastOnce())
+ ->method('get')
+ ->willReturnMap($users);
+ }
+
+ $moreResults = $this->plugin->search($searchTerm, $this->limit, $this->offset, $this->searchResult);
+ $result = $this->searchResult->asArray();
+
+ $this->assertEquals($exactExpected, $result['exact']['users']);
+ $this->assertEquals($expected, $result['users']);
+ $this->assertSame($reachedEnd, $moreResults);
+ }
+
+ public static function takeOutCurrentUserProvider(): array {
+ $inputUsers = [
+ 'alice' => 'Alice',
+ 'bob' => 'Bob',
+ 'carol' => 'Carol',
+ ];
+ return [
+ [
+ $inputUsers,
+ ['alice', 'carol'],
+ 'bob',
+ ],
+ [
+ $inputUsers,
+ ['alice', 'bob', 'carol'],
+ 'dave',
+ ],
+ [
+ $inputUsers,
+ ['alice', 'bob', 'carol'],
+ null,
+ ],
+ ];
+ }
+
+ /**
+ * @param array $users
+ * @param array $expectedUIDs
+ * @param $currentUserId
+ */
+ #[\PHPUnit\Framework\Attributes\DataProvider('takeOutCurrentUserProvider')]
+ public function testTakeOutCurrentUser(array $users, array $expectedUIDs, $currentUserId): void {
+ $this->instantiatePlugin();
+
+ $this->session->expects($this->once())
+ ->method('getUser')
+ ->willReturnCallback(function () use ($currentUserId) {
+ if ($currentUserId !== null) {
+ return $this->getUserMock($currentUserId, $currentUserId);
+ }
+ return null;
+ });
+
+ $this->plugin->takeOutCurrentUser($users);
+ $this->assertSame($expectedUIDs, array_keys($users));
+ }
+
+ public static function dataSearchEnumeration(): array {
+ return [
+ [
+ 'test',
+ ['groupA'],
+ [
+ ['uid' => 'test1', 'groups' => ['groupA']],
+ ['uid' => 'test2', 'groups' => ['groupB']],
+ ],
+ ['exact' => [], 'wide' => ['test1']],
+ ['core' => ['shareapi_restrict_user_enumeration_to_group' => 'yes']],
+ ],
+ [
+ 'test',
+ ['groupA'],
+ [
+ ['uid' => 'test1', 'displayName' => 'Test user 1', 'groups' => ['groupA']],
+ ['uid' => 'test2', 'displayName' => 'Test user 2', 'groups' => ['groupA']],
+ ],
+ ['exact' => [], 'wide' => []],
+ ['core' => ['shareapi_allow_share_dialog_user_enumeration' => 'no']],
+ ],
+ [
+ 'test1',
+ ['groupA'],
+ [
+ ['uid' => 'test1', 'displayName' => 'Test user 1', 'groups' => ['groupA']],
+ ['uid' => 'test2', 'displayName' => 'Test user 2', 'groups' => ['groupA']],
+ ],
+ ['exact' => ['test1'], 'wide' => []],
+ ['core' => ['shareapi_allow_share_dialog_user_enumeration' => 'no']],
+ ],
+ [
+ 'test1',
+ ['groupA'],
+ [
+ ['uid' => 'test1', 'displayName' => 'Test user 1', 'groups' => ['groupA']],
+ ['uid' => 'test2', 'displayName' => 'Test user 2', 'groups' => ['groupA']],
+ ],
+ ['exact' => [], 'wide' => []],
+ [
+ 'core' => [
+ 'shareapi_allow_share_dialog_user_enumeration' => 'no',
+ 'shareapi_restrict_user_enumeration_full_match_userid' => 'no',
+ ],
+ ]
+ ],
+ [
+ 'Test user 1',
+ ['groupA'],
+ [
+ ['uid' => 'test1', 'displayName' => 'Test user 1', 'groups' => ['groupA']],
+ ['uid' => 'test2', 'displayName' => 'Test user 2', 'groups' => ['groupA']],
+ ],
+ ['exact' => ['test1'], 'wide' => []],
+ [
+ 'core' => [
+ 'shareapi_allow_share_dialog_user_enumeration' => 'no',
+ 'shareapi_restrict_user_enumeration_full_match_userid' => 'no',
+ ],
+ ]
+ ],
+ [
+ 'Test user 1',
+ ['groupA'],
+ [
+ ['uid' => 'test1', 'displayName' => 'Test user 1 (Second displayName for user 1)', 'groups' => ['groupA']],
+ ['uid' => 'test2', 'displayName' => 'Test user 2 (Second displayName for user 2)', 'groups' => ['groupA']],
+ ],
+ ['exact' => [], 'wide' => []],
+ ['core' => ['shareapi_allow_share_dialog_user_enumeration' => 'no'],
+ ]
+ ],
+ [
+ 'Test user 1',
+ ['groupA'],
+ [
+ ['uid' => 'test1', 'displayName' => 'Test user 1 (Second displayName for user 1)', 'groups' => ['groupA']],
+ ['uid' => 'test2', 'displayName' => 'Test user 2 (Second displayName for user 2)', 'groups' => ['groupA']],
+ ],
+ ['exact' => ['test1'], 'wide' => []],
+ [
+ 'core' => [
+ 'shareapi_allow_share_dialog_user_enumeration' => 'no',
+ 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn' => 'yes',
+ ],
+ ]
+ ],
+ [
+ 'test1',
+ ['groupA'],
+ [
+ ['uid' => 'test1', 'groups' => ['groupA']],
+ ['uid' => 'test2', 'groups' => ['groupB']],
+ ],
+ ['exact' => ['test1'], 'wide' => []],
+ ['core' => ['shareapi_restrict_user_enumeration_to_group' => 'yes']],
+ ],
+ [
+ 'test',
+ ['groupA'],
+ [
+ ['uid' => 'test1', 'groups' => ['groupA']],
+ ['uid' => 'test2', 'groups' => ['groupB', 'groupA']],
+ ],
+ ['exact' => [], 'wide' => ['test1', 'test2']],
+ ['core' => ['shareapi_restrict_user_enumeration_to_group' => 'yes']],
+ ],
+ [
+ 'test',
+ ['groupA'],
+ [
+ ['uid' => 'test1', 'groups' => ['groupA', 'groupC']],
+ ['uid' => 'test2', 'groups' => ['groupB', 'groupA']],
+ ],
+ ['exact' => [], 'wide' => ['test1', 'test2']],
+ ['core' => ['shareapi_restrict_user_enumeration_to_group' => 'yes']],
+ ],
+ [
+ 'test',
+ ['groupC', 'groupB'],
+ [
+ ['uid' => 'test1', 'groups' => ['groupA', 'groupC']],
+ ['uid' => 'test2', 'groups' => ['groupB', 'groupA']],
+ ],
+ ['exact' => [], 'wide' => ['test1', 'test2']],
+ ['core' => ['shareapi_restrict_user_enumeration_to_group' => 'yes']],
+ ],
+ [
+ 'test',
+ [],
+ [
+ ['uid' => 'test1', 'groups' => ['groupA']],
+ ['uid' => 'test2', 'groups' => ['groupB', 'groupA']],
+ ],
+ ['exact' => [], 'wide' => []],
+ ['core' => ['shareapi_restrict_user_enumeration_to_group' => 'yes']],
+ ],
+ [
+ 'test',
+ ['groupC', 'groupB'],
+ [
+ ['uid' => 'test1', 'groups' => []],
+ ['uid' => 'test2', 'groups' => []],
+ ],
+ ['exact' => [], 'wide' => []],
+ ['core' => ['shareapi_restrict_user_enumeration_to_group' => 'yes']],
+ ],
+ [
+ 'test',
+ ['groupC', 'groupB'],
+ [
+ ['uid' => 'test1', 'groups' => []],
+ ['uid' => 'test2', 'groups' => []],
+ ],
+ ['exact' => [], 'wide' => []],
+ ['core' => ['shareapi_restrict_user_enumeration_to_group' => 'yes']],
+ ],
+ ];
+ }
+
+ #[\PHPUnit\Framework\Attributes\DataProvider('dataSearchEnumeration')]
+ public function testSearchEnumerationLimit($search, $userGroups, $matchingUsers, $result, $mockedSettings): void {
+ $this->mockConfig($mockedSettings);
+
+ $userResults = [];
+ foreach ($matchingUsers as $user) {
+ $userResults[$user['uid']] = $user['uid'];
+ }
+
+ $usersById = [];
+ foreach ($matchingUsers as $user) {
+ $usersById[$user['uid']] = $user;
+ }
+
+ $mappedResultExact = array_map(function ($user) use ($usersById, $search) {
+ return [
+ 'label' => $search === $user ? $user : $usersById[$user]['displayName'],
+ 'value' => ['shareType' => 0, 'shareWith' => $user],
+ 'icon' => 'icon-user',
+ 'subline' => null,
+ 'status' => [],
+ 'shareWithDisplayNameUnique' => $user,
+ ];
+ }, $result['exact']);
+ $mappedResultWide = array_map(function ($user) {
+ return [
+ 'label' => $user,
+ 'value' => ['shareType' => 0, 'shareWith' => $user],
+ 'icon' => 'icon-user',
+ 'subline' => null,
+ 'status' => [],
+ 'shareWithDisplayNameUnique' => $user,
+ ];
+ }, $result['wide']);
+
+ $this->userManager
+ ->method('get')
+ ->willReturnCallback(function ($userId) use ($userResults) {
+ if (isset($userResults[$userId])) {
+ return $this->getUserMock($userId, $userId);
+ }
+ return null;
+ });
+ $this->userManager
+ ->method('searchDisplayName')
+ ->willReturnCallback(function ($search) use ($matchingUsers) {
+ $users = array_filter(
+ $matchingUsers,
+ fn ($user) => str_contains(strtolower($user['displayName']), strtolower($search))
+ );
+ return array_map(
+ fn ($user) => $this->getUserMock($user['uid'], $user['displayName']),
+ $users);
+ });
+
+ $this->groupManager->method('displayNamesInGroup')
+ ->willReturn($userResults);
+
+
+ $this->session->expects($this->any())
+ ->method('getUser')
+ ->willReturn($this->getUserMock('test', 'foo'));
+ $this->groupManager->expects($this->any())
+ ->method('getUserGroupIds')
+ ->willReturnCallback(function ($user) use ($matchingUsers, $userGroups) {
+ static $firstCall = true;
+ if ($firstCall) {
+ $firstCall = false;
+ // current user
+ return $userGroups;
+ }
+ $neededObject = array_filter(
+ $matchingUsers,
+ function ($e) use ($user) {
+ return $user->getUID() === $e['uid'];
+ }
+ );
+ if (count($neededObject) > 0) {
+ return array_shift($neededObject)['groups'];
+ }
+ return [];
+ });
+
+ $this->instantiatePlugin();
+ $this->plugin->search($search, $this->limit, $this->offset, $this->searchResult);
+ $result = $this->searchResult->asArray();
+
+ $this->assertEquals($mappedResultExact, $result['exact']['users']);
+ $this->assertEquals($mappedResultWide, $result['users']);
+ }
+}
diff --git a/tests/lib/Collaboration/Resources/ManagerTest.php b/tests/lib/Collaboration/Resources/ManagerTest.php
new file mode 100644
index 00000000000..0e4e42458e2
--- /dev/null
+++ b/tests/lib/Collaboration/Resources/ManagerTest.php
@@ -0,0 +1,46 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Test\Collaboration\Resources;
+
+use OC\Collaboration\Resources\Manager;
+use OCP\Collaboration\Resources\IManager;
+use OCP\Collaboration\Resources\IProviderManager;
+use OCP\IDBConnection;
+use PHPUnit\Framework\MockObject\MockObject;
+use Psr\Log\LoggerInterface;
+use Test\TestCase;
+
+class ManagerTest extends TestCase {
+
+ protected LoggerInterface&MockObject $logger;
+ protected IProviderManager&MockObject $providerManager;
+ protected IManager $manager;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->logger = $this->createMock(LoggerInterface::class);
+ $this->providerManager = $this->createMock(IProviderManager::class);
+
+ /** @var IDBConnection $connection */
+ $connection = $this->createMock(IDBConnection::class);
+ $this->manager = new Manager($connection, $this->providerManager, $this->logger);
+ }
+
+ public function testRegisterResourceProvider(): void {
+ $this->logger->expects($this->once())
+ ->method('debug')
+ ->with($this->equalTo('\OC\Collaboration\Resources\Manager::registerResourceProvider is deprecated'), $this->equalTo(['provider' => 'AwesomeResourceProvider']));
+ $this->providerManager->expects($this->once())
+ ->method('registerResourceProvider')
+ ->with($this->equalTo('AwesomeResourceProvider'));
+
+ $this->manager->registerResourceProvider('AwesomeResourceProvider');
+ }
+}
diff --git a/tests/lib/Collaboration/Resources/ProviderManagerTest.php b/tests/lib/Collaboration/Resources/ProviderManagerTest.php
new file mode 100644
index 00000000000..b063d89f06e
--- /dev/null
+++ b/tests/lib/Collaboration/Resources/ProviderManagerTest.php
@@ -0,0 +1,98 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace Test\Collaboration\Resources;
+
+use OC\Collaboration\Resources\ProviderManager;
+use OCA\Files\Collaboration\Resources\ResourceProvider;
+use OCP\AppFramework\QueryException;
+use OCP\Collaboration\Resources\IProviderManager;
+use OCP\IServerContainer;
+use Psr\Log\LoggerInterface;
+use Test\TestCase;
+
+class ProviderManagerTest extends TestCase {
+ /** @var IServerContainer */
+ protected $serverContainer;
+ /** @var LoggerInterface */
+ protected $logger;
+ /** @var IProviderManager */
+ protected $providerManager;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->serverContainer = $this->createMock(IServerContainer::class);
+ $this->logger = $this->createMock(LoggerInterface::class);
+
+ $this->providerManager = new class($this->serverContainer, $this->logger) extends ProviderManager {
+ public function countProviders(): int {
+ return count($this->providers);
+ }
+ };
+ }
+
+ public function testRegisterResourceProvider(): void {
+ $this->providerManager->registerResourceProvider('AwesomeResourceProvider');
+ $this->assertSame(1, $this->providerManager->countProviders());
+ }
+
+ public function testGetResourceProvidersNoProvider(): void {
+ $this->assertCount(0, $this->providerManager->getResourceProviders());
+ }
+
+ public function testGetResourceProvidersValidProvider(): void {
+ $this->serverContainer->expects($this->once())
+ ->method('query')
+ ->with($this->equalTo(ResourceProvider::class))
+ ->willReturn($this->createMock(ResourceProvider::class));
+
+ $this->providerManager->registerResourceProvider(ResourceProvider::class);
+ $resourceProviders = $this->providerManager->getResourceProviders();
+
+ $this->assertCount(1, $resourceProviders);
+ $this->assertInstanceOf(ResourceProvider::class, $resourceProviders[0]);
+ }
+
+ public function testGetResourceProvidersInvalidProvider(): void {
+ $this->serverContainer->expects($this->once())
+ ->method('query')
+ ->with($this->equalTo('InvalidResourceProvider'))
+ ->willThrowException(new QueryException('A meaningful error message'));
+
+ $this->logger->expects($this->once())
+ ->method('error');
+
+ $this->providerManager->registerResourceProvider('InvalidResourceProvider');
+ $resourceProviders = $this->providerManager->getResourceProviders();
+
+ $this->assertCount(0, $resourceProviders);
+ }
+
+ public function testGetResourceProvidersValidAndInvalidProvider(): void {
+ $this->serverContainer->expects($this->exactly(2))
+ ->method('query')
+ ->willReturnCallback(function (string $service) {
+ if ($service === 'InvalidResourceProvider') {
+ throw new QueryException('A meaningful error message');
+ }
+ if ($service === ResourceProvider::class) {
+ return $this->createMock(ResourceProvider::class);
+ }
+ });
+
+ $this->logger->expects($this->once())
+ ->method('error');
+
+ $this->providerManager->registerResourceProvider('InvalidResourceProvider');
+ $this->providerManager->registerResourceProvider(ResourceProvider::class);
+ $resourceProviders = $this->providerManager->getResourceProviders();
+
+ $this->assertCount(1, $resourceProviders);
+ }
+}