549 lines
17 KiB
PHP
549 lines
17 KiB
PHP
<?php
|
|
/**
|
|
* @copyright Copyright (c) 2020 Matthias Heinisch <nextcloud@matthiasheinisch.de>
|
|
*
|
|
* @author Matthias Heinisch <nextcloud@matthiasheinisch.de>
|
|
*
|
|
* @license GNU AGPL version 3 or any later version
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
|
|
namespace OCA\Contacts\Service;
|
|
|
|
use OCA\Contacts\Service\Social\CompositeSocialProvider;
|
|
use OCA\Contacts\Service\Social\ISocialProvider;
|
|
|
|
use OCP\AppFramework\Http;
|
|
use OCP\Http\Client\IClient;
|
|
use OCP\Http\Client\IResponse;
|
|
use OCP\Http\Client\IClientService;
|
|
use OCP\IConfig;
|
|
use OCP\Contacts\IManager;
|
|
use OCP\IAddressBook;
|
|
use OCA\DAV\CardDAV\CardDavBackend;
|
|
use OCP\IURLGenerator;
|
|
use OCP\IL10N;
|
|
use OCP\Util;
|
|
use OCP\AppFramework\Utility\ITimeFactory;
|
|
|
|
use PHPUnit\Framework\MockObject\MockObject;
|
|
use ChristophWurst\Nextcloud\Testing\TestCase;
|
|
|
|
class SocialApiServiceTest extends TestCase {
|
|
private $service;
|
|
|
|
/** @var CompositeSocialProvider|MockObject */
|
|
private $socialProvider;
|
|
/** @var IManager|MockObject */
|
|
private $manager;
|
|
/** @var IConfig|MockObject */
|
|
private $config;
|
|
/** @var IClientService|MockObject */
|
|
private $clientService;
|
|
/** @var IL10N|MockObject */
|
|
private $l10n;
|
|
/** @var IURLGenerator|MockObject */
|
|
private $urlGen;
|
|
/** @var CardDavBackend|MockObject */
|
|
private $davBackend;
|
|
/** @var ITimeFactory|MockObject */
|
|
private $timeFactory;
|
|
|
|
public function allSocialProfileProviders() {
|
|
$body = "the body";
|
|
$imageType = "jpg";
|
|
$contact = [
|
|
'URI' => '3225c0d5-1bd2-43e5-a08c-4e65eaa406b0',
|
|
'VERSION' => '4.0'
|
|
];
|
|
$connector = $this->createMock(ISocialProvider::class);
|
|
$connector->method('supportsContact')->willReturn(true);
|
|
$connector->method('getImageUrls')->willReturn(["url1"]);
|
|
|
|
$connectorNoSupport = $this->createMock(ISocialProvider::class);
|
|
$connectorNoSupport->method('supportsContact')->willReturn(false);
|
|
|
|
$connectorNoUrl = $this->createMock(ISocialProvider::class);
|
|
$connectorNoUrl->method('supportsContact')->willReturn(true);
|
|
$connectorNoUrl->method('getImageUrls')->willReturn([]);
|
|
|
|
$addressbookEmpty = $this->createMock(IAddressBook::class);
|
|
$addressbookEmpty
|
|
->method('getUri')
|
|
->willReturn('contacts');
|
|
$addressbookEmpty
|
|
->method('search')
|
|
->willReturn(null);
|
|
|
|
$addressbook = $this->createMock(IAddressBook::class);
|
|
$addressbook
|
|
->method('getUri')
|
|
->willReturn('contacts');
|
|
$addressbook
|
|
->method('search')
|
|
->willReturn([$contact]);
|
|
|
|
return [
|
|
'no address book found' => [null, [], "", "", Http::STATUS_BAD_REQUEST],
|
|
'no contact found' => [[$addressbookEmpty], [], "", "", Http::STATUS_PRECONDITION_FAILED],
|
|
'no supporting contacts found' => [[$addressbook], [$connectorNoSupport], "", "", Http::STATUS_PRECONDITION_FAILED],
|
|
'no url found' => [[$addressbook], [$connectorNoUrl], "", "", Http::STATUS_BAD_REQUEST],
|
|
'no image found' => [[$addressbook], [$connector], "", "", Http::STATUS_NOT_FOUND],
|
|
'image found' => [[$addressbook], [$connector], $body, $imageType, Http::STATUS_OK]
|
|
];
|
|
}
|
|
|
|
public function updateAddressbookProvider() {
|
|
return [
|
|
'not user enabled' => ['yes', 'no', Http::STATUS_FORBIDDEN],
|
|
'not admin allowed' => ['no', 'yes', Http::STATUS_FORBIDDEN],
|
|
'not allowed, not enabled' => ['no', 'no', Http::STATUS_FORBIDDEN],
|
|
'allowed and enabled' => ['yes', 'yes', Http::STATUS_OK],
|
|
];
|
|
}
|
|
|
|
protected function setUp(): void {
|
|
parent::setUp();
|
|
|
|
$this->manager = $this->createMock(IManager::class);
|
|
$this->config = $this->createMock(IConfig::class);
|
|
$this->socialProvider = $this->createMock(CompositeSocialProvider::class);
|
|
$this->clientService = $this->createMock(IClientService::class);
|
|
$this->l10n = $this->createMock(IL10N::class);
|
|
$this->urlGen = $this->createMock(IURLGenerator::class);
|
|
$this->davBackend = $this->createMock(CardDavBackend::class);
|
|
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
|
$this->service = new SocialApiService(
|
|
$this->socialProvider,
|
|
$this->manager,
|
|
$this->config,
|
|
$this->clientService,
|
|
$this->l10n,
|
|
$this->urlGen,
|
|
$this->davBackend,
|
|
$this->timeFactory
|
|
);
|
|
}
|
|
|
|
public function testDeactivatedSocial() {
|
|
$this->config
|
|
->method('getAppValue')
|
|
->willReturn('no');
|
|
|
|
$result = $this->service->getSupportedNetworks();
|
|
$this->assertEmpty($result);
|
|
}
|
|
|
|
/**
|
|
* @dataProvider allSocialProfileProviders
|
|
*/
|
|
public function testUpdateContactWithoutNetwork($addressbooks, $providers, $body, $imageType, $status) {
|
|
$this->manager
|
|
->method('getUserAddressBooks')
|
|
->willReturn($addressbooks);
|
|
|
|
$this->socialProvider
|
|
->method('getSocialConnectors')
|
|
->willReturn($providers);
|
|
|
|
$response = $this->createMock(IResponse::class);
|
|
$response
|
|
->method('getBody')
|
|
->willReturn($body);
|
|
$response
|
|
->method('getHeader')
|
|
->willReturn($imageType);
|
|
$client = $this->createMock(IClient::class);
|
|
$client
|
|
->method('get')
|
|
->willReturn($response);
|
|
$this->clientService
|
|
->method('NewClient')
|
|
->willReturn($client);
|
|
|
|
$result = $this->service
|
|
->updateContact(
|
|
'contacts',
|
|
'3225c0d5-1bd2-43e5-a08c-4e65eaa406b0',
|
|
null);
|
|
$this->assertEquals($status, $result->getStatus());
|
|
}
|
|
|
|
public function testUpdateContactWithNetworkVersion3() {
|
|
$network = "mastodon";
|
|
$body = "the body";
|
|
$imageType = "jpg";
|
|
$addressBookId = "contacts";
|
|
$contactId = "3225c0d5-1bd2-43e5-a08c-4e65eaa406b0";
|
|
$contact = [
|
|
'URI' => $contactId,
|
|
'VERSION' => '3.0'
|
|
];
|
|
$provider = $this->createMock(ISocialProvider::class);
|
|
$provider->method('supportsContact')->willReturn(true);
|
|
$provider->method('getImageUrls')->willReturn(["url1"]);
|
|
|
|
$addressbook = $this->createMock(IAddressBook::class);
|
|
$addressbook
|
|
->method('getUri')
|
|
->willReturn('contacts');
|
|
$addressbook
|
|
->method('search')
|
|
->willReturn([$contact]);
|
|
|
|
$this->manager
|
|
->method('getUserAddressBooks')
|
|
->willReturn([$addressbook]);
|
|
|
|
$this->socialProvider
|
|
->method('getSocialConnectors')
|
|
->willReturn([$provider]);
|
|
|
|
$this->socialProvider
|
|
->method('getSocialConnector')
|
|
->willReturn($provider);
|
|
|
|
$response = $this->createMock(IResponse::class);
|
|
$response
|
|
->method('getBody')
|
|
->willReturn($body);
|
|
$response
|
|
->method('getHeader')
|
|
->willReturn($imageType);
|
|
$client = $this->createMock(IClient::class);
|
|
$client
|
|
->method('get')
|
|
->willReturn($response);
|
|
$this->clientService
|
|
->method('NewClient')
|
|
->willReturn($client);
|
|
|
|
$changes = [
|
|
'URI' => $contact['URI'],
|
|
'VERSION' => $contact['VERSION'],
|
|
'PHOTO;ENCODING=b;TYPE=' . $imageType . ';VALUE=BINARY' => base64_encode($body)
|
|
];
|
|
|
|
$this->socialProvider
|
|
->expects($this->once())->method("getSocialConnector")->with($network);
|
|
$provider->expects($this->once())->method("supportsContact")->with($contact);
|
|
$addressbook->expects($this->once())->method("createOrUpdate")->with($changes, $addressBookId);
|
|
|
|
$result = $this->service
|
|
->updateContact(
|
|
$addressBookId,
|
|
$contactId,
|
|
$network);
|
|
|
|
$this->assertEquals(Http::STATUS_OK, $result->getStatus());
|
|
}
|
|
|
|
public function testUpdateContactWithNetworkVersion4() {
|
|
$network = "mastodon";
|
|
$body = "the body";
|
|
$imageType = "jpg";
|
|
$addressBookId = "contacts";
|
|
$contactId = "3225c0d5-1bd2-43e5-a08c-4e65eaa406b0";
|
|
$contact = [
|
|
'URI' => $contactId,
|
|
'VERSION' => '4.0'
|
|
];
|
|
$provider = $this->createMock(ISocialProvider::class);
|
|
$provider->method('supportsContact')->willReturn(true);
|
|
$provider->method('getImageUrls')->willReturn(["url1"]);
|
|
|
|
$addressbook = $this->createMock(IAddressBook::class);
|
|
$addressbook
|
|
->method('getUri')
|
|
->willReturn('contacts');
|
|
$addressbook
|
|
->method('search')
|
|
->willReturn([$contact]);
|
|
|
|
$this->manager
|
|
->method('getUserAddressBooks')
|
|
->willReturn([$addressbook]);
|
|
|
|
$this->socialProvider
|
|
->method('getSocialConnectors')
|
|
->willReturn([$provider]);
|
|
|
|
$this->socialProvider
|
|
->method('getSocialConnector')
|
|
->willReturn($provider);
|
|
|
|
$response = $this->createMock(IResponse::class);
|
|
$response
|
|
->method('getBody')
|
|
->willReturn($body);
|
|
$response
|
|
->method('getHeader')
|
|
->willReturn($imageType);
|
|
$client = $this->createMock(IClient::class);
|
|
$client
|
|
->method('get')
|
|
->willReturn($response);
|
|
$this->clientService
|
|
->method('NewClient')
|
|
->willReturn($client);
|
|
|
|
$changes = [
|
|
'URI' => $contact['URI'],
|
|
'VERSION' => $contact['VERSION'],
|
|
'PHOTO' => "data:".$imageType.";base64," . base64_encode($body)
|
|
];
|
|
|
|
$this->socialProvider
|
|
->expects($this->once())->method("getSocialConnector")->with($network);
|
|
$provider->expects($this->once())->method("supportsContact")->with($contact);
|
|
$addressbook->expects($this->once())->method("createOrUpdate")->with($changes, $addressBookId);
|
|
|
|
$result = $this->service
|
|
->updateContact(
|
|
$addressBookId,
|
|
$contactId,
|
|
$network);
|
|
|
|
$this->assertEquals(Http::STATUS_OK, $result->getStatus());
|
|
}
|
|
|
|
protected function setupAddressbooks() {
|
|
$validContact1 = [
|
|
'UID' => '11111111-1111-1111-1111-111111111111',
|
|
'FN' => 'Valid Contact One',
|
|
'VERSION' => '4.0',
|
|
'X-SOCIALPROFILE' => [['type' => 'someNetwork', 'value' => 'someId1']],
|
|
];
|
|
$validContact2 = [
|
|
'UID' => '22222222-2222-2222-2222-222222222222',
|
|
'FN' => 'Valid Contact Two',
|
|
'VERSION' => '4.0',
|
|
'PHOTO' => "data:someHeader;base64," . base64_encode('someBody'),
|
|
'X-SOCIALPROFILE' => [['type' => 'someNetwork', 'value' => 'someId2']],
|
|
];
|
|
$emptyContact = [
|
|
'UID' => '00000000-0000-0000-0000-000000000000',
|
|
'FN' => 'Empty Contact',
|
|
'VERSION' => '4.0',
|
|
];
|
|
$invalidContact = [
|
|
'UID' => 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
|
'FN' => 'Invalid Contact',
|
|
'VERSION' => '4.0',
|
|
'X-SOCIALPROFILE' => [['type' => 'someNetwork', 'value' => 'invalidId']],
|
|
];
|
|
|
|
$addressbook1 = $this->createMock(IAddressBook::class);
|
|
$addressbook1->method('getUri')->willReturn('contacts1');
|
|
$addressbook2 = $this->createMock(IAddressBook::class);
|
|
$addressbook2->method('getUri')->willReturn('contacts2');
|
|
if (Util::getVersion()[0] >= 20) {
|
|
// TODO: check can be removed as soon as min-dependency of contacts is NCv20
|
|
$addressbook1->method('isShared')->willReturn(false);
|
|
$addressbook1->method('isSystemAddressBook')->willReturn(false);
|
|
$addressbook2->method('isShared')->willReturn(false);
|
|
$addressbook2->method('isSystemAddressBook')->willReturn(false);
|
|
}
|
|
|
|
$searchMap1 = [
|
|
['', ['UID'], ['types' => true], [$validContact1, $invalidContact]],
|
|
['', ['X-SOCIALPROFILE'], ['types' => true], [$validContact1, $invalidContact]],
|
|
[$validContact1['UID'], ['UID'], ['types' => true], [$validContact1]],
|
|
[$invalidContact['UID'], ['UID'], ['types' => true], [$invalidContact]],
|
|
];
|
|
$searchMap2 = [
|
|
['', ['UID'], ['types' => true], [$validContact2, $emptyContact]],
|
|
['', ['X-SOCIALPROFILE'], ['types' => true], [$validContact2]],
|
|
[$validContact2['UID'], ['UID'], ['types' => true], [$validContact2]],
|
|
[$emptyContact['UID'], ['UID'], ['types' => true], [$emptyContact]],
|
|
];
|
|
$addressbook1
|
|
->method('search')
|
|
->will($this->returnValueMap($searchMap1));
|
|
$addressbook2
|
|
->method('search')
|
|
->will($this->returnValueMap($searchMap2));
|
|
$this->manager
|
|
->method('getUserAddressBooks')
|
|
->willReturn([$addressbook1, $addressbook2]);
|
|
|
|
$providerSupportsMap = [
|
|
[$validContact1, true],
|
|
[$emptyContact, false],
|
|
[$invalidContact, false],
|
|
[$validContact2, true]
|
|
];
|
|
|
|
$providerUrlMap = [
|
|
[$validContact1, ["url1"]],
|
|
[$emptyContact, []],
|
|
[$invalidContact, []],
|
|
[$validContact2, ["url1"]]
|
|
];
|
|
|
|
$provider = $this->createMock(ISocialProvider::class);
|
|
$provider->method('getImageUrls')
|
|
->will($this->returnValueMap($providerUrlMap));
|
|
$provider->method('supportsContact')
|
|
->will($this->returnValueMap($providerSupportsMap));
|
|
|
|
$this->socialProvider
|
|
->method('getSocialConnectors')
|
|
->willReturn([$provider]);
|
|
|
|
$this->socialProvider
|
|
->method('getSocialConnector')
|
|
->willReturn($provider);
|
|
|
|
$validResponse = $this->createMock(IResponse::class);
|
|
$validResponse
|
|
->method('getBody')
|
|
->willReturn('someBody');
|
|
$validResponse
|
|
->method('getHeader')
|
|
->willReturn('someHeader');
|
|
|
|
$client = $this->createMock(IClient::class);
|
|
$client
|
|
->method('get')
|
|
->willReturn($validResponse);
|
|
$this->clientService
|
|
->method('NewClient')
|
|
->willReturn($client);
|
|
}
|
|
|
|
/**
|
|
* @dataProvider updateAddressbookProvider
|
|
*/
|
|
public function testUpdateAddressbooks($syncAllowedByAdmin, $bgSyncEnabledByUser, $expected) {
|
|
$this->config
|
|
->method('getAppValue')
|
|
->willReturn($syncAllowedByAdmin);
|
|
$this->config
|
|
->method('getUserValue')
|
|
->willReturn($bgSyncEnabledByUser);
|
|
$this->timeFactory
|
|
->method('getTime')
|
|
->willReturn(10);
|
|
|
|
$this->setupAddressbooks();
|
|
|
|
if ($syncAllowedByAdmin === 'yes' && $bgSyncEnabledByUser === 'yes') {
|
|
$this->socialProvider
|
|
->expects($this->atLeastOnce())
|
|
->method('getSocialConnectors');
|
|
}
|
|
|
|
$this->socialProvider
|
|
->expects($this->never())
|
|
->method('getSocialConnector');
|
|
|
|
$result = $this->service->updateAddressbooks('mrstest');
|
|
|
|
$this->assertEquals($expected, $result->getStatus());
|
|
|
|
if (($syncAllowedByAdmin === 'yes') && ($bgSyncEnabledByUser === 'yes')) {
|
|
$report = $result->getData();
|
|
$this->assertArrayHasKey('0', $report);
|
|
$this->assertArrayHasKey('updated', $report[0]);
|
|
$this->assertContains('Valid Contact One', $report[0]['updated']);
|
|
$this->assertArrayHasKey('checked', $report[0]);
|
|
$this->assertContains('Valid Contact Two', $report[0]['checked']);
|
|
$this->assertArrayHasKey('failed', $report[0]);
|
|
$this->assertArrayHasKey('412', $report[0]['failed']);
|
|
$this->assertContains('Invalid Contact', $report[0]['failed']['412']);
|
|
$this->assertContains('Empty Contact', $report[0]['failed']['412']);
|
|
}
|
|
}
|
|
|
|
|
|
public function testUpdateAddressbooksTimeout() {
|
|
$this->config
|
|
->method('getAppValue')
|
|
->willReturn('yes');
|
|
$this->config
|
|
->method('getUserValue')
|
|
->willReturn('yes');
|
|
$this->timeFactory
|
|
->method('getTime')
|
|
->willReturnOnConsecutiveCalls(10, 11, 999);
|
|
|
|
$this->setupAddressbooks();
|
|
|
|
$result = $this->service->updateAddressbooks('msstest');
|
|
|
|
$this->assertEquals(Http::STATUS_PARTIAL_CONTENT, $result->getStatus());
|
|
|
|
$report = $result->getData();
|
|
|
|
$this->assertArrayHasKey('0', $report);
|
|
$this->assertArrayHasKey('stoppedAt', $report[0]);
|
|
$this->assertArrayHasKey('addressBook', $report[0]['stoppedAt']);
|
|
$this->assertArrayHasKey('contact', $report[0]['stoppedAt']);
|
|
}
|
|
|
|
/**
|
|
* @dataProvider updateAddressbookProvider
|
|
*/
|
|
public function testUpdateAddressbooksContinued($syncAllowedByAdmin, $bgSyncEnabledByUser, $expected) {
|
|
$this->config
|
|
->method('getAppValue')
|
|
->willReturn($syncAllowedByAdmin);
|
|
$this->config
|
|
->method('getUserValue')
|
|
->willReturn($bgSyncEnabledByUser);
|
|
$this->timeFactory
|
|
->method('getTime')
|
|
->willReturn(10);
|
|
|
|
$this->setupAddressbooks();
|
|
|
|
$result = $this->service->updateAddressbooks('mrstest', 'contacts2', '22222222-2222-2222-2222-222222222222');
|
|
|
|
$this->assertEquals($expected, $result->getStatus());
|
|
|
|
if (($syncAllowedByAdmin === 'yes') && ($bgSyncEnabledByUser === 'yes')) {
|
|
$report = $result->getData();
|
|
$this->assertArrayHasKey('0', $report);
|
|
$this->assertArrayHasKey('updated', $report[0]);
|
|
$this->assertNotContains('Valid Contact One', $report[0]['updated']);
|
|
$this->assertArrayHasKey('checked', $report[0]);
|
|
$this->assertContains('Valid Contact Two', $report[0]['checked']);
|
|
}
|
|
}
|
|
|
|
public function testExistsContact() {
|
|
$this->setupAddressbooks();
|
|
|
|
// all good:
|
|
$result = $this->service->existsContact('11111111-1111-1111-1111-111111111111', 'contacts1', 'admin');
|
|
$this->assertEquals(true, $result);
|
|
|
|
// wrong address book:
|
|
$result = $this->service->existsContact('22222222-2222-2222-2222-222222222222', 'contacts1', 'admin');
|
|
$this->assertEquals(false, $result);
|
|
|
|
// invalid contactId:
|
|
$result = $this->service->existsContact('not-existing', 'contacts1', 'admin');
|
|
$this->assertEquals(false, $result);
|
|
|
|
// invalid addressbookId:
|
|
$result = $this->service->existsContact('11111111-1111-1111-1111-111111111111', 'not-existing', 'admin');
|
|
$this->assertEquals(false, $result);
|
|
}
|
|
}
|