diff options
Diffstat (limited to 'apps/dav/tests/unit/DAV')
-rw-r--r-- | apps/dav/tests/unit/DAV/AnonymousOptionsTest.php | 48 | ||||
-rw-r--r-- | apps/dav/tests/unit/DAV/BrowserErrorPagePluginTest.php | 46 | ||||
-rw-r--r-- | apps/dav/tests/unit/DAV/CustomPropertiesBackendTest.php | 343 | ||||
-rw-r--r-- | apps/dav/tests/unit/DAV/GroupPrincipalTest.php | 110 | ||||
-rw-r--r-- | apps/dav/tests/unit/DAV/HookManagerTest.php | 250 | ||||
-rw-r--r-- | apps/dav/tests/unit/DAV/Listener/UserEventsListenerTest.php | 183 | ||||
-rw-r--r-- | apps/dav/tests/unit/DAV/Sharing/BackendTest.php | 399 | ||||
-rw-r--r-- | apps/dav/tests/unit/DAV/Sharing/PluginTest.php | 51 | ||||
-rw-r--r-- | apps/dav/tests/unit/DAV/SystemPrincipalBackendTest.php | 84 | ||||
-rw-r--r-- | apps/dav/tests/unit/DAV/ViewOnlyPluginTest.php | 167 |
10 files changed, 1123 insertions, 558 deletions
diff --git a/apps/dav/tests/unit/DAV/AnonymousOptionsTest.php b/apps/dav/tests/unit/DAV/AnonymousOptionsTest.php index 8c178ba8e44..c99ebf327c8 100644 --- a/apps/dav/tests/unit/DAV/AnonymousOptionsTest.php +++ b/apps/dav/tests/unit/DAV/AnonymousOptionsTest.php @@ -1,29 +1,11 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl> - * - * @author Bastien Durel <bastien@durel.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Julius Härtl <jus@bitgrid.net> - * @author Robin Appelman <robin@icewind.nl> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ -namespace OCA\DAV\tests\unit\DAV; +namespace OCA\DAV\Tests\unit\DAV; use OCA\DAV\Connector\Sabre\AnonymousOptionsPlugin; use Sabre\DAV\Auth\Backend\BasicCallBack; @@ -34,7 +16,7 @@ use Sabre\HTTP\Sapi; use Test\TestCase; class AnonymousOptionsTest extends TestCase { - private function sendRequest($method, $path, $userAgent = '') { + private function sendRequest(string $method, string $path, string $userAgent = '') { $server = new Server(); $server->addPlugin(new AnonymousOptionsPlugin()); $server->addPlugin(new Plugin(new BasicCallBack(function () { @@ -50,49 +32,49 @@ class AnonymousOptionsTest extends TestCase { return $server->httpResponse; } - public function testAnonymousOptionsRoot() { + public function testAnonymousOptionsRoot(): void { $response = $this->sendRequest('OPTIONS', ''); $this->assertEquals(401, $response->getStatus()); } - public function testAnonymousOptionsNonRoot() { + public function testAnonymousOptionsNonRoot(): void { $response = $this->sendRequest('OPTIONS', 'foo'); $this->assertEquals(401, $response->getStatus()); } - public function testAnonymousOptionsNonRootSubDir() { + public function testAnonymousOptionsNonRootSubDir(): void { $response = $this->sendRequest('OPTIONS', 'foo/bar'); $this->assertEquals(401, $response->getStatus()); } - public function testAnonymousOptionsRootOffice() { + public function testAnonymousOptionsRootOffice(): void { $response = $this->sendRequest('OPTIONS', '', 'Microsoft Office does strange things'); $this->assertEquals(200, $response->getStatus()); } - public function testAnonymousOptionsNonRootOffice() { + public function testAnonymousOptionsNonRootOffice(): void { $response = $this->sendRequest('OPTIONS', 'foo', 'Microsoft Office does strange things'); $this->assertEquals(200, $response->getStatus()); } - public function testAnonymousOptionsNonRootSubDirOffice() { + public function testAnonymousOptionsNonRootSubDirOffice(): void { $response = $this->sendRequest('OPTIONS', 'foo/bar', 'Microsoft Office does strange things'); $this->assertEquals(200, $response->getStatus()); } - public function testAnonymousHead() { + public function testAnonymousHead(): void { $response = $this->sendRequest('HEAD', '', 'Microsoft Office does strange things'); $this->assertEquals(200, $response->getStatus()); } - public function testAnonymousHeadNoOffice() { + public function testAnonymousHeadNoOffice(): void { $response = $this->sendRequest('HEAD', ''); $this->assertEquals(401, $response->getStatus(), 'curl'); @@ -105,6 +87,6 @@ class SapiMock extends Sapi { * * @return void */ - public static function sendResponse(ResponseInterface $response) { + public static function sendResponse(ResponseInterface $response): void { } } diff --git a/apps/dav/tests/unit/DAV/BrowserErrorPagePluginTest.php b/apps/dav/tests/unit/DAV/BrowserErrorPagePluginTest.php index a0733a68550..0e82ef0a3ae 100644 --- a/apps/dav/tests/unit/DAV/BrowserErrorPagePluginTest.php +++ b/apps/dav/tests/unit/DAV/BrowserErrorPagePluginTest.php @@ -1,48 +1,30 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OCA\DAV\Tests\unit\DAV; use OCA\DAV\Files\BrowserErrorPagePlugin; +use PHPUnit\Framework\MockObject\MockObject; use Sabre\DAV\Exception\NotFound; use Sabre\HTTP\Response; class BrowserErrorPagePluginTest extends \Test\TestCase { - /** - * @dataProvider providesExceptions - * @param $expectedCode - * @param $exception - */ - public function test($expectedCode, $exception) { - /** @var BrowserErrorPagePlugin | \PHPUnit\Framework\MockObject\MockObject $plugin */ - $plugin = $this->getMockBuilder(BrowserErrorPagePlugin::class)->setMethods(['sendResponse', 'generateBody'])->getMock(); + #[\PHPUnit\Framework\Attributes\DataProvider('providesExceptions')] + public function test(int $expectedCode, \Throwable $exception): void { + /** @var BrowserErrorPagePlugin&MockObject $plugin */ + $plugin = $this->getMockBuilder(BrowserErrorPagePlugin::class)->onlyMethods(['sendResponse', 'generateBody'])->getMock(); $plugin->expects($this->once())->method('generateBody')->willReturn(':boom:'); $plugin->expects($this->once())->method('sendResponse'); - /** @var \Sabre\DAV\Server | \PHPUnit\Framework\MockObject\MockObject $server */ - $server = $this->getMockBuilder('Sabre\DAV\Server')->disableOriginalConstructor()->getMock(); + /** @var \Sabre\DAV\Server&MockObject $server */ + $server = $this->createMock('Sabre\DAV\Server'); $server->expects($this->once())->method('on'); - $httpResponse = $this->getMockBuilder(Response::class)->disableOriginalConstructor()->getMock(); + $httpResponse = $this->createMock(Response::class); $httpResponse->expects($this->once())->method('addHeaders'); $httpResponse->expects($this->once())->method('setStatus')->with($expectedCode); $httpResponse->expects($this->once())->method('setBody')->with(':boom:'); @@ -51,7 +33,7 @@ class BrowserErrorPagePluginTest extends \Test\TestCase { $plugin->logException($exception); } - public function providesExceptions() { + public static function providesExceptions(): array { return [ [ 404, new NotFound()], [ 500, new \RuntimeException()], diff --git a/apps/dav/tests/unit/DAV/CustomPropertiesBackendTest.php b/apps/dav/tests/unit/DAV/CustomPropertiesBackendTest.php index 2e7939aa614..2a85c0cbecd 100644 --- a/apps/dav/tests/unit/DAV/CustomPropertiesBackendTest.php +++ b/apps/dav/tests/unit/DAV/CustomPropertiesBackendTest.php @@ -1,71 +1,62 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2017, Georg Ehrke <oc.list@georgehrke.com> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ -namespace OCA\DAV\Tests\DAV; +namespace OCA\DAV\Tests\unit\DAV; +use OCA\DAV\CalDAV\Calendar; +use OCA\DAV\CalDAV\DefaultCalendarValidator; use OCA\DAV\DAV\CustomPropertiesBackend; +use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\IUser; +use PHPUnit\Framework\MockObject\MockObject; +use Sabre\DAV\Exception\NotFound; use Sabre\DAV\PropFind; use Sabre\DAV\PropPatch; +use Sabre\DAV\Server; use Sabre\DAV\Tree; +use Sabre\DAV\Xml\Property\Href; +use Sabre\DAVACL\IACL; +use Sabre\DAVACL\IPrincipal; use Test\TestCase; /** * @group DB */ class CustomPropertiesBackendTest extends TestCase { + private const BASE_URI = '/remote.php/dav/'; - /** @var Tree | \PHPUnit\Framework\MockObject\MockObject */ - private $tree; - - /** @var IDBConnection */ - private $dbConnection; - - /** @var IUser | \PHPUnit\Framework\MockObject\MockObject */ - private $user; - - /** @var CustomPropertiesBackend | \PHPUnit\Framework\MockObject\MockObject */ - private $backend; + private Server&MockObject $server; + private Tree&MockObject $tree; + private IDBConnection $dbConnection; + private IUser&MockObject $user; + private DefaultCalendarValidator&MockObject $defaultCalendarValidator; + private CustomPropertiesBackend $backend; protected function setUp(): void { parent::setUp(); + $this->server = $this->createMock(Server::class); + $this->server->method('getBaseUri') + ->willReturn(self::BASE_URI); $this->tree = $this->createMock(Tree::class); $this->user = $this->createMock(IUser::class); $this->user->method('getUID') ->with() ->willReturn('dummy_user_42'); - $this->dbConnection = \OC::$server->getDatabaseConnection(); + $this->dbConnection = \OCP\Server::get(IDBConnection::class); + $this->defaultCalendarValidator = $this->createMock(DefaultCalendarValidator::class); $this->backend = new CustomPropertiesBackend( + $this->server, $this->tree, $this->dbConnection, - $this->user + $this->user, + $this->defaultCalendarValidator, ); } @@ -85,13 +76,19 @@ class CustomPropertiesBackendTest extends TestCase { } } - protected function insertProps(string $user, string $path, array $props) { + protected function insertProps(string $user, string $path, array $props): void { foreach ($props as $name => $value) { $this->insertProp($user, $path, $name, $value); } } - protected function insertProp(string $user, string $path, string $name, string $value) { + protected function insertProp(string $user, string $path, string $name, mixed $value): void { + $type = CustomPropertiesBackend::PROPERTY_TYPE_STRING; + if ($value instanceof Href) { + $value = $value->getHref(); + $type = CustomPropertiesBackend::PROPERTY_TYPE_HREF; + } + $query = $this->dbConnection->getQueryBuilder(); $query->insert('properties') ->values([ @@ -99,37 +96,44 @@ class CustomPropertiesBackendTest extends TestCase { 'propertypath' => $query->createNamedParameter($this->formatPath($path)), 'propertyname' => $query->createNamedParameter($name), 'propertyvalue' => $query->createNamedParameter($value), + 'valuetype' => $query->createNamedParameter($type, IQueryBuilder::PARAM_INT) ]); $query->execute(); } - protected function getProps(string $user, string $path) { + protected function getProps(string $user, string $path): array { $query = $this->dbConnection->getQueryBuilder(); - $query->select('propertyname', 'propertyvalue') + $query->select('propertyname', 'propertyvalue', 'valuetype') ->from('properties') ->where($query->expr()->eq('userid', $query->createNamedParameter($user))) - ->where($query->expr()->eq('propertypath', $query->createNamedParameter($this->formatPath($path)))); + ->andWhere($query->expr()->eq('propertypath', $query->createNamedParameter($this->formatPath($path)))); $result = $query->execute(); $data = []; while ($row = $result->fetch()) { - $data[$row['propertyname']] = $row['propertyvalue']; + $value = $row['propertyvalue']; + if ((int)$row['valuetype'] === CustomPropertiesBackend::PROPERTY_TYPE_HREF) { + $value = new Href($value); + } + $data[$row['propertyname']] = $value; } $result->closeCursor(); return $data; } - public function testPropFindNoDbCalls() { + public function testPropFindNoDbCalls(): void { $db = $this->createMock(IDBConnection::class); $backend = new CustomPropertiesBackend( + $this->server, $this->tree, $db, - $this->user + $this->user, + $this->defaultCalendarValidator, ); $propFind = $this->createMock(PropFind::class); - $propFind->expects($this->at(0)) + $propFind->expects($this->once()) ->method('get404Properties') ->with() ->willReturn([ @@ -145,7 +149,7 @@ class CustomPropertiesBackendTest extends TestCase { $backend->propFind('foo_bar_path_1337_0', $propFind); } - public function testPropFindCalendarCall() { + public function testPropFindCalendarCall(): void { $propFind = $this->createMock(PropFind::class); $propFind->method('get404Properties') ->with() @@ -179,7 +183,7 @@ class CustomPropertiesBackendTest extends TestCase { $setProps = []; $propFind->method('set') - ->willReturnCallback(function ($name, $value, $status) use (&$setProps) { + ->willReturnCallback(function ($name, $value, $status) use (&$setProps): void { $setProps[$name] = $value; }); @@ -187,10 +191,165 @@ class CustomPropertiesBackendTest extends TestCase { $this->assertEquals($props, $setProps); } - /** - * @dataProvider propPatchProvider - */ - public function testPropPatch(string $path, array $existing, array $props, array $result) { + public function testPropFindPrincipalCall(): void { + $this->tree->method('getNodeForPath') + ->willReturnCallback(function ($uri) { + $node = $this->createMock(Calendar::class); + $node->method('getOwner') + ->willReturn('principals/users/dummy_user_42'); + return $node; + }); + + $propFind = $this->createMock(PropFind::class); + $propFind->method('get404Properties') + ->with() + ->willReturn([ + '{DAV:}getcontentlength', + '{DAV:}getcontenttype', + '{DAV:}getetag', + '{abc}def', + ]); + + $propFind->method('getRequestedProperties') + ->with() + ->willReturn([ + '{DAV:}getcontentlength', + '{DAV:}getcontenttype', + '{DAV:}getetag', + '{abc}def', + '{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL', + ]); + + $props = [ + '{abc}def' => 'a', + '{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL' => new Href('calendars/admin/personal'), + ]; + $this->insertProps('dummy_user_42', 'principals/users/dummy_user_42', $props); + + $setProps = []; + $propFind->method('set') + ->willReturnCallback(function ($name, $value, $status) use (&$setProps): void { + $setProps[$name] = $value; + }); + + $this->backend->propFind('principals/users/dummy_user_42', $propFind); + $this->assertEquals($props, $setProps); + } + + public static function propFindPrincipalScheduleDefaultCalendarProviderUrlProvider(): array { + // [ user, nodes, existingProps, requestedProps, returnedProps ] + return [ + [ // Exists + 'dummy_user_42', + ['calendars/dummy_user_42/foo/' => Calendar::class], + ['{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL' => new Href('calendars/dummy_user_42/foo/')], + ['{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL'], + ['{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL' => new Href('calendars/dummy_user_42/foo/')], + ], + [ // Doesn't exist + 'dummy_user_42', + ['calendars/dummy_user_42/foo/' => Calendar::class], + ['{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL' => new Href('calendars/dummy_user_42/bar/')], + ['{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL'], + [], + ], + [ // No privilege + 'dummy_user_42', + ['calendars/user2/baz/' => Calendar::class], + ['{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL' => new Href('calendars/user2/baz/')], + ['{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL'], + [], + ], + [ // Not a calendar + 'dummy_user_42', + ['foo/dummy_user_42/bar/' => IACL::class], + ['{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL' => new Href('foo/dummy_user_42/bar/')], + ['{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL'], + [], + ], + ]; + + } + + #[\PHPUnit\Framework\Attributes\DataProvider('propFindPrincipalScheduleDefaultCalendarProviderUrlProvider')] + public function testPropFindPrincipalScheduleDefaultCalendarUrl( + string $user, + array $nodes, + array $existingProps, + array $requestedProps, + array $returnedProps, + ): void { + $propFind = $this->createMock(PropFind::class); + $propFind->method('get404Properties') + ->with() + ->willReturn([ + '{DAV:}getcontentlength', + '{DAV:}getcontenttype', + '{DAV:}getetag', + ]); + + $propFind->method('getRequestedProperties') + ->with() + ->willReturn(array_merge([ + '{DAV:}getcontentlength', + '{DAV:}getcontenttype', + '{DAV:}getetag', + '{abc}def', + ], + $requestedProps, + )); + + $this->server->method('calculateUri') + ->willReturnCallback(function ($uri) { + if (!str_starts_with($uri, self::BASE_URI)) { + return trim(substr($uri, strlen(self::BASE_URI)), '/'); + } + return null; + }); + $this->tree->method('getNodeForPath') + ->willReturnCallback(function ($uri) use ($nodes) { + if (str_starts_with($uri, 'principals/')) { + return $this->createMock(IPrincipal::class); + } + if (array_key_exists($uri, $nodes)) { + $owner = explode('/', $uri)[1]; + $node = $this->createMock($nodes[$uri]); + $node->method('getOwner') + ->willReturn("principals/users/$owner"); + return $node; + } + throw new NotFound('Node not found'); + }); + + $this->insertProps($user, "principals/users/$user", $existingProps); + + $setProps = []; + $propFind->method('set') + ->willReturnCallback(function ($name, $value, $status) use (&$setProps): void { + $setProps[$name] = $value; + }); + + $this->backend->propFind("principals/users/$user", $propFind); + $this->assertEquals($returnedProps, $setProps); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('propPatchProvider')] + public function testPropPatch(string $path, array $existing, array $props, array $result): void { + $this->server->method('calculateUri') + ->willReturnCallback(function ($uri) { + if (str_starts_with($uri, self::BASE_URI)) { + return trim(substr($uri, strlen(self::BASE_URI)), '/'); + } + return null; + }); + $this->tree->method('getNodeForPath') + ->willReturnCallback(function ($uri) { + $node = $this->createMock(Calendar::class); + $node->method('getOwner') + ->willReturn('principals/users/' . $this->user->getUID()); + return $node; + }); + $this->insertProps($this->user->getUID(), $path, $existing); $propPatch = new PropPatch($props); @@ -201,46 +360,102 @@ class CustomPropertiesBackendTest extends TestCase { $this->assertEquals($result, $storedProps); } - public function propPatchProvider() { + public static function propPatchProvider(): array { $longPath = str_repeat('long_path', 100); return [ ['foo_bar_path_1337', [], ['{DAV:}displayname' => 'anything'], ['{DAV:}displayname' => 'anything']], ['foo_bar_path_1337', ['{DAV:}displayname' => 'foo'], ['{DAV:}displayname' => 'anything'], ['{DAV:}displayname' => 'anything']], ['foo_bar_path_1337', ['{DAV:}displayname' => 'foo'], ['{DAV:}displayname' => null], []], [$longPath, [], ['{DAV:}displayname' => 'anything'], ['{DAV:}displayname' => 'anything']], + ['principals/users/dummy_user_42', [], ['{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL' => new Href('foo/bar/')], ['{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL' => new Href('foo/bar/')]], + ['principals/users/dummy_user_42', [], ['{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL' => new Href(self::BASE_URI . 'foo/bar/')], ['{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL' => new Href('foo/bar/')]], ]; } - /** - * @dataProvider deleteProvider - */ - public function testDelete(string $path) { + public function testPropPatchWithUnsuitableCalendar(): void { + $path = 'principals/users/' . $this->user->getUID(); + + $node = $this->createMock(Calendar::class); + $node->expects(self::once()) + ->method('getOwner') + ->willReturn($path); + + $this->defaultCalendarValidator->expects(self::once()) + ->method('validateScheduleDefaultCalendar') + ->with($node) + ->willThrowException(new \Sabre\DAV\Exception('Invalid calendar')); + + $this->server->method('calculateUri') + ->willReturnCallback(function ($uri) { + if (str_starts_with($uri, self::BASE_URI)) { + return trim(substr($uri, strlen(self::BASE_URI)), '/'); + } + return null; + }); + $this->tree->expects(self::once()) + ->method('getNodeForPath') + ->with('foo/bar/') + ->willReturn($node); + + $storedProps = $this->getProps($this->user->getUID(), $path); + $this->assertEquals([], $storedProps); + + $propPatch = new PropPatch([ + '{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL' => new Href('foo/bar/'), + ]); + $this->backend->propPatch($path, $propPatch); + try { + $propPatch->commit(); + } catch (\Throwable $e) { + $this->assertInstanceOf(\Sabre\DAV\Exception::class, $e); + } + + $storedProps = $this->getProps($this->user->getUID(), $path); + $this->assertEquals([], $storedProps); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('deleteProvider')] + public function testDelete(string $path): void { $this->insertProps('dummy_user_42', $path, ['foo' => 'bar']); $this->backend->delete($path); $this->assertEquals([], $this->getProps('dummy_user_42', $path)); } - public function deleteProvider() { + public static function deleteProvider(): array { return [ ['foo_bar_path_1337'], [str_repeat('long_path', 100)] ]; } - /** - * @dataProvider moveProvider - */ - public function testMove(string $source, string $target) { + #[\PHPUnit\Framework\Attributes\DataProvider('moveProvider')] + public function testMove(string $source, string $target): void { $this->insertProps('dummy_user_42', $source, ['foo' => 'bar']); $this->backend->move($source, $target); $this->assertEquals([], $this->getProps('dummy_user_42', $source)); $this->assertEquals(['foo' => 'bar'], $this->getProps('dummy_user_42', $target)); } - public function moveProvider() { + public static function moveProvider(): array { return [ ['foo_bar_path_1337', 'foo_bar_path_7333'], [str_repeat('long_path1', 100), str_repeat('long_path2', 100)] ]; } + + public function testDecodeValueFromDatabaseObjectCurrent(): void { + $propertyValue = 'O:48:"Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp":1:{s:8:"\x00*\x00value";s:6:"opaque";}'; + $propertyType = 3; + $decodeValue = $this->invokePrivate($this->backend, 'decodeValueFromDatabase', [$propertyValue, $propertyType]); + $this->assertInstanceOf(\Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp::class, $decodeValue); + $this->assertEquals('opaque', $decodeValue->getValue()); + } + + public function testDecodeValueFromDatabaseObjectLegacy(): void { + $propertyValue = 'O:48:"Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp":1:{s:8:"' . chr(0) . '*' . chr(0) . 'value";s:6:"opaque";}'; + $propertyType = 3; + $decodeValue = $this->invokePrivate($this->backend, 'decodeValueFromDatabase', [$propertyValue, $propertyType]); + $this->assertInstanceOf(\Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp::class, $decodeValue); + $this->assertEquals('opaque', $decodeValue->getValue()); + } } diff --git a/apps/dav/tests/unit/DAV/GroupPrincipalTest.php b/apps/dav/tests/unit/DAV/GroupPrincipalTest.php index 8f86961a6af..2756152a6e2 100644 --- a/apps/dav/tests/unit/DAV/GroupPrincipalTest.php +++ b/apps/dav/tests/unit/DAV/GroupPrincipalTest.php @@ -1,32 +1,10 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @copyright Copyright (c) 2018, Georg Ehrke - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Citharel <nextcloud@tcit.fr> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OCA\DAV\Tests\unit\DAV; @@ -42,20 +20,11 @@ use PHPUnit\Framework\MockObject\MockObject; use Sabre\DAV\PropPatch; class GroupPrincipalTest extends \Test\TestCase { - /** @var IConfig|MockObject */ - private $config; - - /** @var IGroupManager | MockObject */ - private $groupManager; - - /** @var IUserSession | MockObject */ - private $userSession; - - /** @var IManager | MockObject */ - private $shareManager; - - /** @var GroupPrincipalBackend */ - private $connector; + private IConfig&MockObject $config; + private IGroupManager&MockObject $groupManager; + private IUserSession&MockObject $userSession; + private IManager&MockObject $shareManager; + private GroupPrincipalBackend $connector; protected function setUp(): void { $this->groupManager = $this->createMock(IGroupManager::class); @@ -72,12 +41,12 @@ class GroupPrincipalTest extends \Test\TestCase { parent::setUp(); } - public function testGetPrincipalsByPrefixWithoutPrefix() { + public function testGetPrincipalsByPrefixWithoutPrefix(): void { $response = $this->connector->getPrincipalsByPrefix(''); $this->assertSame([], $response); } - public function testGetPrincipalsByPrefixWithUsers() { + public function testGetPrincipalsByPrefixWithUsers(): void { $group1 = $this->mockGroup('foo'); $group2 = $this->mockGroup('bar'); $this->groupManager @@ -102,7 +71,7 @@ class GroupPrincipalTest extends \Test\TestCase { $this->assertSame($expectedResponse, $response); } - public function testGetPrincipalsByPrefixEmpty() { + public function testGetPrincipalsByPrefixEmpty(): void { $this->groupManager ->expects($this->once()) ->method('search') @@ -113,7 +82,7 @@ class GroupPrincipalTest extends \Test\TestCase { $this->assertSame([], $response); } - public function testGetPrincipalsByPathWithoutMail() { + public function testGetPrincipalsByPathWithoutMail(): void { $group1 = $this->mockGroup('foo'); $this->groupManager ->expects($this->once()) @@ -130,7 +99,7 @@ class GroupPrincipalTest extends \Test\TestCase { $this->assertSame($expectedResponse, $response); } - public function testGetPrincipalsByPathWithMail() { + public function testGetPrincipalsByPathWithMail(): void { $fooUser = $this->mockGroup('foo'); $this->groupManager ->expects($this->once()) @@ -147,7 +116,7 @@ class GroupPrincipalTest extends \Test\TestCase { $this->assertSame($expectedResponse, $response); } - public function testGetPrincipalsByPathEmpty() { + public function testGetPrincipalsByPathEmpty(): void { $this->groupManager ->expects($this->once()) ->method('get') @@ -158,7 +127,7 @@ class GroupPrincipalTest extends \Test\TestCase { $this->assertSame(null, $response); } - public function testGetPrincipalsByPathGroupWithSlash() { + public function testGetPrincipalsByPathGroupWithSlash(): void { $group1 = $this->mockGroup('foo/bar'); $this->groupManager ->expects($this->once()) @@ -175,7 +144,7 @@ class GroupPrincipalTest extends \Test\TestCase { $this->assertSame($expectedResponse, $response); } - public function testGetPrincipalsByPathGroupWithHash() { + public function testGetPrincipalsByPathGroupWithHash(): void { $group1 = $this->mockGroup('foo#bar'); $this->groupManager ->expects($this->once()) @@ -192,45 +161,38 @@ class GroupPrincipalTest extends \Test\TestCase { $this->assertSame($expectedResponse, $response); } - public function testGetGroupMemberSet() { + public function testGetGroupMemberSet(): void { $response = $this->connector->getGroupMemberSet('principals/groups/foo'); $this->assertSame([], $response); } - public function testGetGroupMembership() { + public function testGetGroupMembership(): void { $response = $this->connector->getGroupMembership('principals/groups/foo'); $this->assertSame([], $response); } - public function testSetGroupMembership() { + public function testSetGroupMembership(): void { $this->expectException(\Sabre\DAV\Exception::class); $this->expectExceptionMessage('Setting members of the group is not supported yet'); $this->connector->setGroupMemberSet('principals/groups/foo', ['foo']); } - public function testUpdatePrincipal() { + public function testUpdatePrincipal(): void { $this->assertSame(0, $this->connector->updatePrincipal('foo', new PropPatch([]))); } - public function testSearchPrincipalsWithEmptySearchProperties() { + public function testSearchPrincipalsWithEmptySearchProperties(): void { $this->assertSame([], $this->connector->searchPrincipals('principals/groups', [])); } - public function testSearchPrincipalsWithWrongPrefixPath() { + public function testSearchPrincipalsWithWrongPrefixPath(): void { $this->assertSame([], $this->connector->searchPrincipals('principals/users', ['{DAV:}displayname' => 'Foo'])); } - /** - * @dataProvider searchPrincipalsDataProvider - * @param bool $sharingEnabled - * @param bool $groupSharingEnabled - * @param bool $groupsOnly - * @param string $test - * @param array $result - */ + #[\PHPUnit\Framework\Attributes\DataProvider('searchPrincipalsDataProvider')] public function testSearchPrincipals(bool $sharingEnabled, bool $groupSharingEnabled, bool $groupsOnly, string $test, array $result): void { $this->shareManager->expects($this->once()) ->method('shareAPIEnabled') @@ -288,7 +250,7 @@ class GroupPrincipalTest extends \Test\TestCase { ['{DAV:}displayname' => 'Foo'], $test)); } - public function searchPrincipalsDataProvider() { + public static function searchPrincipalsDataProvider(): array { return [ [true, true, false, 'allof', ['principals/groups/group1', 'principals/groups/group2', 'principals/groups/group3', 'principals/groups/group4', 'principals/groups/group5']], [true, true, false, 'anyof', ['principals/groups/group1', 'principals/groups/group2', 'principals/groups/group3', 'principals/groups/group4', 'principals/groups/group5']], @@ -301,14 +263,7 @@ class GroupPrincipalTest extends \Test\TestCase { ]; } - /** - * @dataProvider findByUriDataProvider - * @param bool $sharingEnabled - * @param bool $groupSharingEnabled - * @param bool $groupsOnly - * @param string $findUri - * @param string|null $result - */ + #[\PHPUnit\Framework\Attributes\DataProvider('findByUriDataProvider')] public function testFindByUri(bool $sharingEnabled, bool $groupSharingEnabled, bool $groupsOnly, string $findUri, ?string $result): void { $this->shareManager->expects($this->once()) ->method('shareAPIEnabled') @@ -329,7 +284,7 @@ class GroupPrincipalTest extends \Test\TestCase { ->method('getUser') ->willReturn($user); - $this->groupManager->expects($this->at(0)) + $this->groupManager->expects($this->once()) ->method('getUserGroupIds') ->with($user) ->willReturn(['group1', 'group2', 'group5']); @@ -344,7 +299,7 @@ class GroupPrincipalTest extends \Test\TestCase { $this->assertEquals($result, $this->connector->findByUri($findUri, 'principals/groups')); } - public function findByUriDataProvider() { + public static function findByUriDataProvider(): array { return [ [false, false, false, 'principal:principals/groups/group1', null], [false, false, false, 'principal:principals/groups/group3', null], @@ -361,10 +316,7 @@ class GroupPrincipalTest extends \Test\TestCase { ]; } - /** - * @return Group|MockObject - */ - private function mockGroup($gid) { + private function mockGroup(string $gid): Group&MockObject { $fooGroup = $this->createMock(Group::class); $fooGroup ->expects($this->exactly(1)) @@ -373,7 +325,7 @@ class GroupPrincipalTest extends \Test\TestCase { $fooGroup ->expects($this->exactly(1)) ->method('getDisplayName') - ->willReturn('Group '.$gid); + ->willReturn('Group ' . $gid); return $fooGroup; } } diff --git a/apps/dav/tests/unit/DAV/HookManagerTest.php b/apps/dav/tests/unit/DAV/HookManagerTest.php deleted file mode 100644 index 503062c75db..00000000000 --- a/apps/dav/tests/unit/DAV/HookManagerTest.php +++ /dev/null @@ -1,250 +0,0 @@ -<?php -/** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Citharel <nextcloud@tcit.fr> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ -namespace OCA\DAV\Tests\unit\DAV; - -use OCA\DAV\CalDAV\CalDavBackend; -use OCA\DAV\CardDAV\CardDavBackend; -use OCA\DAV\CardDAV\SyncService; -use OCA\DAV\HookManager; -use OCP\Defaults; -use OCP\IL10N; -use OCP\IUser; -use OCP\IUserManager; -use PHPUnit\Framework\MockObject\MockObject; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Test\TestCase; - -class HookManagerTest extends TestCase { - /** @var IL10N */ - private $l10n; - - /** @var EventDispatcherInterface | MockObject */ - private $eventDispatcher; - - protected function setUp(): void { - parent::setUp(); - $this->eventDispatcher = $this->createMock(EventDispatcherInterface::class); - $this->l10n = $this->createMock(IL10N::class); - $this->l10n - ->expects($this->any()) - ->method('t') - ->willReturnCallback(function ($text, $parameters = []) { - return vsprintf($text, $parameters); - }); - } - - public function test() { - $user = $this->getMockBuilder(IUser::class) - ->disableOriginalConstructor() - ->getMock(); - $user->expects($this->once())->method('getUID')->willReturn('newUser'); - - /** @var IUserManager | MockObject $userManager */ - $userManager = $this->getMockBuilder(IUserManager::class) - ->disableOriginalConstructor() - ->getMock(); - - /** @var SyncService | MockObject $syncService */ - $syncService = $this->getMockBuilder(SyncService::class) - ->disableOriginalConstructor() - ->getMock(); - - /** @var Defaults | MockObject $syncService */ - $defaults = $this->getMockBuilder(Defaults::class) - ->disableOriginalConstructor() - ->getMock(); - - $defaults->expects($this->once())->method('getColorPrimary')->willReturn('#745bca'); - - /** @var CalDavBackend | MockObject $cal */ - $cal = $this->getMockBuilder(CalDavBackend::class) - ->disableOriginalConstructor() - ->getMock(); - $cal->expects($this->once())->method('getCalendarsForUserCount')->willReturn(0); - $cal->expects($this->once())->method('createCalendar')->with( - 'principals/users/newUser', - 'personal', [ - '{DAV:}displayname' => 'Personal', - '{http://apple.com/ns/ical/}calendar-color' => '#745bca', - 'components' => 'VEVENT' - ]); - - /** @var CardDavBackend | MockObject $card */ - $card = $this->getMockBuilder(CardDavBackend::class) - ->disableOriginalConstructor() - ->getMock(); - $card->expects($this->once())->method('getAddressBooksForUserCount')->willReturn(0); - $card->expects($this->once())->method('createAddressBook')->with( - 'principals/users/newUser', - 'contacts', ['{DAV:}displayname' => 'Contacts']); - - $hm = new HookManager($userManager, $syncService, $cal, $card, $defaults, $this->eventDispatcher); - $hm->firstLogin($user); - } - - public function testWithExisting() { - $user = $this->getMockBuilder(IUser::class) - ->disableOriginalConstructor() - ->getMock(); - $user->expects($this->once())->method('getUID')->willReturn('newUser'); - - /** @var IUserManager | MockObject $userManager */ - $userManager = $this->getMockBuilder(IUserManager::class) - ->disableOriginalConstructor() - ->getMock(); - - /** @var SyncService | MockObject $syncService */ - $syncService = $this->getMockBuilder(SyncService::class) - ->disableOriginalConstructor() - ->getMock(); - - /** @var Defaults | MockObject $syncService */ - $defaults = $this->getMockBuilder(Defaults::class) - ->disableOriginalConstructor() - ->getMock(); - - /** @var CalDavBackend | MockObject $cal */ - $cal = $this->getMockBuilder(CalDavBackend::class) - ->disableOriginalConstructor() - ->getMock(); - $cal->expects($this->once())->method('getCalendarsForUserCount')->willReturn(1); - $cal->expects($this->never())->method('createCalendar'); - - /** @var CardDavBackend | MockObject $card */ - $card = $this->getMockBuilder(CardDavBackend::class) - ->disableOriginalConstructor() - ->getMock(); - $card->expects($this->once())->method('getAddressBooksForUserCount')->willReturn(1); - $card->expects($this->never())->method('createAddressBook'); - - $hm = new HookManager($userManager, $syncService, $cal, $card, $defaults, $this->eventDispatcher); - $hm->firstLogin($user); - } - - public function testWithBirthdayCalendar() { - $user = $this->getMockBuilder(IUser::class) - ->disableOriginalConstructor() - ->getMock(); - $user->expects($this->once())->method('getUID')->willReturn('newUser'); - - /** @var IUserManager | MockObject $userManager */ - $userManager = $this->getMockBuilder(IUserManager::class) - ->disableOriginalConstructor() - ->getMock(); - - /** @var SyncService | MockObject $syncService */ - $syncService = $this->getMockBuilder(SyncService::class) - ->disableOriginalConstructor() - ->getMock(); - - /** @var Defaults | MockObject $syncService */ - $defaults = $this->getMockBuilder(Defaults::class) - ->disableOriginalConstructor() - ->getMock(); - $defaults->expects($this->once())->method('getColorPrimary')->willReturn('#745bca'); - - /** @var CalDavBackend | MockObject $cal */ - $cal = $this->getMockBuilder(CalDavBackend::class) - ->disableOriginalConstructor() - ->getMock(); - $cal->expects($this->once())->method('getCalendarsForUserCount')->willReturn(0); - $cal->expects($this->once())->method('createCalendar')->with( - 'principals/users/newUser', - 'personal', [ - '{DAV:}displayname' => 'Personal', - '{http://apple.com/ns/ical/}calendar-color' => '#745bca', - 'components' => 'VEVENT' - ]); - - /** @var CardDavBackend | MockObject $card */ - $card = $this->getMockBuilder(CardDavBackend::class) - ->disableOriginalConstructor() - ->getMock(); - $card->expects($this->once())->method('getAddressBooksForUserCount')->willReturn(0); - $card->expects($this->once())->method('createAddressBook')->with( - 'principals/users/newUser', - 'contacts', ['{DAV:}displayname' => 'Contacts']); - - $hm = new HookManager($userManager, $syncService, $cal, $card, $defaults, $this->eventDispatcher); - $hm->firstLogin($user); - } - - public function testDeleteCalendar() { - $user = $this->getMockBuilder(IUser::class) - ->disableOriginalConstructor() - ->getMock(); - - /** @var IUserManager | MockObject $userManager */ - $userManager = $this->getMockBuilder(IUserManager::class) - ->disableOriginalConstructor() - ->getMock(); - $userManager->expects($this->once())->method('get')->willReturn($user); - - /** @var SyncService | MockObject $syncService */ - $syncService = $this->getMockBuilder(SyncService::class) - ->disableOriginalConstructor() - ->getMock(); - $syncService->expects($this->once()) - ->method('deleteUser'); - - /** @var Defaults | MockObject $syncService */ - $defaults = $this->getMockBuilder(Defaults::class) - ->disableOriginalConstructor() - ->getMock(); - - /** @var CalDavBackend | MockObject $cal */ - $cal = $this->getMockBuilder(CalDavBackend::class) - ->disableOriginalConstructor() - ->getMock(); - $cal->expects($this->once())->method('getUsersOwnCalendars')->willReturn([ - ['id' => 'personal'] - ]); - $cal->expects($this->once())->method('getSubscriptionsForUser')->willReturn([ - ['id' => 'some-subscription'] - ]); - $cal->expects($this->once())->method('deleteCalendar')->with('personal'); - $cal->expects($this->once())->method('deleteSubscription')->with('some-subscription'); - $cal->expects($this->once())->method('deleteAllSharesByUser'); - - /** @var CardDavBackend | MockObject $card */ - $card = $this->getMockBuilder(CardDavBackend::class) - ->disableOriginalConstructor() - ->getMock(); - $card->expects($this->once())->method('getUsersOwnAddressBooks')->willReturn([ - ['id' => 'personal'] - ]); - $card->expects($this->once())->method('deleteAddressBook'); - - $hm = new HookManager($userManager, $syncService, $cal, $card, $defaults, $this->eventDispatcher); - $hm->preDeleteUser(['uid' => 'newUser']); - $hm->postDeleteUser(['uid' => 'newUser']); - } -} diff --git a/apps/dav/tests/unit/DAV/Listener/UserEventsListenerTest.php b/apps/dav/tests/unit/DAV/Listener/UserEventsListenerTest.php new file mode 100644 index 00000000000..8e410eb0a78 --- /dev/null +++ b/apps/dav/tests/unit/DAV/Listener/UserEventsListenerTest.php @@ -0,0 +1,183 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ + +namespace OCA\DAV\Tests\unit\DAV\Listener; + +use OCA\DAV\BackgroundJob\UserStatusAutomation; +use OCA\DAV\CalDAV\CalDavBackend; +use OCA\DAV\CardDAV\CardDavBackend; +use OCA\DAV\CardDAV\SyncService; +use OCA\DAV\Listener\UserEventsListener; +use OCA\DAV\Service\ExampleContactService; +use OCA\DAV\Service\ExampleEventService; +use OCP\BackgroundJob\IJobList; +use OCP\Defaults; +use OCP\IUser; +use OCP\IUserManager; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use Test\TestCase; + +class UserEventsListenerTest extends TestCase { + private IUserManager&MockObject $userManager; + private SyncService&MockObject $syncService; + private CalDavBackend&MockObject $calDavBackend; + private CardDavBackend&MockObject $cardDavBackend; + private Defaults&MockObject $defaults; + private ExampleContactService&MockObject $exampleContactService; + private ExampleEventService&MockObject $exampleEventService; + private LoggerInterface&MockObject $logger; + + private UserEventsListener $userEventsListener; + + protected function setUp(): void { + parent::setUp(); + + $this->userManager = $this->createMock(IUserManager::class); + $this->syncService = $this->createMock(SyncService::class); + $this->calDavBackend = $this->createMock(CalDavBackend::class); + $this->cardDavBackend = $this->createMock(CardDavBackend::class); + $this->defaults = $this->createMock(Defaults::class); + $this->exampleContactService = $this->createMock(ExampleContactService::class); + $this->exampleEventService = $this->createMock(ExampleEventService::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->jobList = $this->createMock(IJobList::class); + + $this->userEventsListener = new UserEventsListener( + $this->userManager, + $this->syncService, + $this->calDavBackend, + $this->cardDavBackend, + $this->defaults, + $this->exampleContactService, + $this->exampleEventService, + $this->logger, + $this->jobList, + ); + } + + public function test(): void { + $user = $this->createMock(IUser::class); + $user->expects($this->once())->method('getUID')->willReturn('newUser'); + + $this->defaults->expects($this->once())->method('getColorPrimary')->willReturn('#745bca'); + + $this->calDavBackend->expects($this->once())->method('getCalendarsForUserCount')->willReturn(0); + $this->calDavBackend->expects($this->once())->method('createCalendar')->with( + 'principals/users/newUser', + 'personal', [ + '{DAV:}displayname' => 'Personal', + '{http://apple.com/ns/ical/}calendar-color' => '#745bca', + 'components' => 'VEVENT' + ]) + ->willReturn(1000); + $this->calDavBackend->expects(self::never()) + ->method('getCalendarsForUser'); + $this->exampleEventService->expects(self::once()) + ->method('createExampleEvent') + ->with(1000); + + $this->cardDavBackend->expects($this->once())->method('getAddressBooksForUserCount')->willReturn(0); + $this->cardDavBackend->expects($this->once())->method('createAddressBook')->with( + 'principals/users/newUser', + 'contacts', ['{DAV:}displayname' => 'Contacts']); + + $this->userEventsListener->firstLogin($user); + } + + public function testWithExisting(): void { + $user = $this->createMock(IUser::class); + $user->expects($this->once())->method('getUID')->willReturn('newUser'); + + $this->calDavBackend->expects($this->once())->method('getCalendarsForUserCount')->willReturn(1); + $this->calDavBackend->expects($this->never())->method('createCalendar'); + $this->calDavBackend->expects(self::never()) + ->method('createCalendar'); + $this->exampleEventService->expects(self::never()) + ->method('createExampleEvent'); + + $this->cardDavBackend->expects($this->once())->method('getAddressBooksForUserCount')->willReturn(1); + $this->cardDavBackend->expects($this->never())->method('createAddressBook'); + + $this->userEventsListener->firstLogin($user); + } + + public function testWithBirthdayCalendar(): void { + $user = $this->createMock(IUser::class); + $user->expects($this->once())->method('getUID')->willReturn('newUser'); + + $this->defaults->expects($this->once())->method('getColorPrimary')->willReturn('#745bca'); + + $this->calDavBackend->expects($this->once())->method('getCalendarsForUserCount')->willReturn(0); + $this->calDavBackend->expects($this->once())->method('createCalendar')->with( + 'principals/users/newUser', + 'personal', [ + '{DAV:}displayname' => 'Personal', + '{http://apple.com/ns/ical/}calendar-color' => '#745bca', + 'components' => 'VEVENT' + ]); + + $this->cardDavBackend->expects($this->once())->method('getAddressBooksForUserCount')->willReturn(0); + $this->cardDavBackend->expects($this->once())->method('createAddressBook')->with( + 'principals/users/newUser', + 'contacts', ['{DAV:}displayname' => 'Contacts']); + + $this->userEventsListener->firstLogin($user); + } + + public function testDeleteCalendar(): void { + $user = $this->createMock(IUser::class); + $user->expects($this->once())->method('getUID')->willReturn('newUser'); + + $this->syncService->expects($this->once()) + ->method('deleteUser'); + + $this->calDavBackend->expects($this->once())->method('getUsersOwnCalendars')->willReturn([ + ['id' => 'personal'] + ]); + $this->calDavBackend->expects($this->once())->method('getSubscriptionsForUser')->willReturn([ + ['id' => 'some-subscription'] + ]); + $this->calDavBackend->expects($this->once())->method('deleteCalendar')->with('personal'); + $this->calDavBackend->expects($this->once())->method('deleteSubscription')->with('some-subscription'); + $this->calDavBackend->expects($this->once())->method('deleteAllSharesByUser'); + + $this->cardDavBackend->expects($this->once())->method('getUsersOwnAddressBooks')->willReturn([ + ['id' => 'personal'] + ]); + $this->cardDavBackend->expects($this->once())->method('deleteAddressBook'); + + $this->userEventsListener->preDeleteUser($user); + $this->userEventsListener->postDeleteUser('newUser'); + } + + public function testDeleteUserAutomationEvent(): void { + $user = $this->createMock(IUser::class); + $user->expects($this->once())->method('getUID')->willReturn('newUser'); + + $this->syncService->expects($this->once()) + ->method('deleteUser'); + + $this->calDavBackend->expects($this->once())->method('getUsersOwnCalendars')->willReturn([ + ['id' => []] + ]); + $this->calDavBackend->expects($this->once())->method('getSubscriptionsForUser')->willReturn([ + ['id' => []] + ]); + $this->cardDavBackend->expects($this->once())->method('getUsersOwnAddressBooks')->willReturn([ + ['id' => []] + ]); + + $this->jobList->expects(self::once())->method('remove')->with(UserStatusAutomation::class, ['userId' => 'newUser']); + + $this->userEventsListener->preDeleteUser($user); + $this->userEventsListener->postDeleteUser('newUser'); + } +} diff --git a/apps/dav/tests/unit/DAV/Sharing/BackendTest.php b/apps/dav/tests/unit/DAV/Sharing/BackendTest.php new file mode 100644 index 00000000000..556a623a73f --- /dev/null +++ b/apps/dav/tests/unit/DAV/Sharing/BackendTest.php @@ -0,0 +1,399 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\DAV\Tests\unit\DAV\Sharing; + +use OCA\DAV\CalDAV\Sharing\Backend as CalendarSharingBackend; +use OCA\DAV\CalDAV\Sharing\Service; +use OCA\DAV\CardDAV\Sharing\Backend as ContactsSharingBackend; +use OCA\DAV\Connector\Sabre\Principal; +use OCA\DAV\DAV\Sharing\Backend; +use OCA\DAV\DAV\Sharing\IShareable; +use OCP\ICache; +use OCP\ICacheFactory; +use OCP\IDBConnection; +use OCP\IGroupManager; +use OCP\IUserManager; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use Test\TestCase; + +class BackendTest extends TestCase { + + private IDBConnection&MockObject $db; + private IUserManager&MockObject $userManager; + private IGroupManager&MockObject $groupManager; + private Principal&MockObject $principalBackend; + private ICache&MockObject $shareCache; + private LoggerInterface&MockObject $logger; + private ICacheFactory&MockObject $cacheFactory; + private Service&MockObject $calendarService; + private CalendarSharingBackend $backend; + + protected function setUp(): void { + parent::setUp(); + $this->db = $this->createMock(IDBConnection::class); + $this->userManager = $this->createMock(IUserManager::class); + $this->groupManager = $this->createMock(IGroupManager::class); + $this->principalBackend = $this->createMock(Principal::class); + $this->cacheFactory = $this->createMock(ICacheFactory::class); + $this->shareCache = $this->createMock(ICache::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->calendarService = $this->createMock(Service::class); + $this->cacheFactory->expects(self::any()) + ->method('createInMemory') + ->willReturn($this->shareCache); + + $this->backend = new CalendarSharingBackend( + $this->userManager, + $this->groupManager, + $this->principalBackend, + $this->cacheFactory, + $this->calendarService, + $this->logger, + ); + } + + public function testUpdateShareCalendarBob(): void { + $shareable = $this->createConfiguredMock(IShareable::class, [ + 'getOwner' => 'principals/users/alice', + 'getResourceId' => 42, + ]); + $add = [ + [ + 'href' => 'principal:principals/users/bob', + 'readOnly' => true, + ] + ]; + $principal = 'principals/users/bob'; + + $this->shareCache->expects(self::once()) + ->method('clear'); + $this->principalBackend->expects(self::once()) + ->method('findByUri') + ->willReturn($principal); + $this->userManager->expects(self::once()) + ->method('userExists') + ->willReturn(true); + $this->groupManager->expects(self::never()) + ->method('groupExists'); + $this->calendarService->expects(self::once()) + ->method('shareWith') + ->with($shareable->getResourceId(), $principal, Backend::ACCESS_READ); + + $this->backend->updateShares($shareable, $add, []); + } + + public function testUpdateShareCalendarGroup(): void { + $shareable = $this->createConfiguredMock(IShareable::class, [ + 'getOwner' => 'principals/users/alice', + 'getResourceId' => 42, + ]); + $add = [ + [ + 'href' => 'principal:principals/groups/bob', + 'readOnly' => true, + ] + ]; + $principal = 'principals/groups/bob'; + + $this->shareCache->expects(self::once()) + ->method('clear'); + $this->principalBackend->expects(self::once()) + ->method('findByUri') + ->willReturn($principal); + $this->userManager->expects(self::never()) + ->method('userExists'); + $this->groupManager->expects(self::once()) + ->method('groupExists') + ->willReturn(true); + $this->calendarService->expects(self::once()) + ->method('shareWith') + ->with($shareable->getResourceId(), $principal, Backend::ACCESS_READ); + + $this->backend->updateShares($shareable, $add, []); + } + + public function testUpdateShareContactsBob(): void { + $shareable = $this->createConfiguredMock(IShareable::class, [ + 'getOwner' => 'principals/users/alice', + 'getResourceId' => 42, + ]); + $add = [ + [ + 'href' => 'principal:principals/users/bob', + 'readOnly' => true, + ] + ]; + $principal = 'principals/users/bob'; + + $this->shareCache->expects(self::once()) + ->method('clear'); + $this->principalBackend->expects(self::once()) + ->method('findByUri') + ->willReturn($principal); + $this->userManager->expects(self::once()) + ->method('userExists') + ->willReturn(true); + $this->groupManager->expects(self::never()) + ->method('groupExists'); + $this->calendarService->expects(self::once()) + ->method('shareWith') + ->with($shareable->getResourceId(), $principal, Backend::ACCESS_READ); + + $this->backend->updateShares($shareable, $add, []); + } + + public function testUpdateShareContactsGroup(): void { + $shareable = $this->createConfiguredMock(IShareable::class, [ + 'getOwner' => 'principals/users/alice', + 'getResourceId' => 42, + ]); + $add = [ + [ + 'href' => 'principal:principals/groups/bob', + 'readOnly' => true, + ] + ]; + $principal = 'principals/groups/bob'; + + $this->shareCache->expects(self::once()) + ->method('clear'); + $this->principalBackend->expects(self::once()) + ->method('findByUri') + ->willReturn($principal); + $this->userManager->expects(self::never()) + ->method('userExists'); + $this->groupManager->expects(self::once()) + ->method('groupExists') + ->willReturn(true); + $this->calendarService->expects(self::once()) + ->method('shareWith') + ->with($shareable->getResourceId(), $principal, Backend::ACCESS_READ); + + $this->backend->updateShares($shareable, $add, []); + } + + public function testUpdateShareCircle(): void { + $shareable = $this->createConfiguredMock(IShareable::class, [ + 'getOwner' => 'principals/users/alice', + 'getResourceId' => 42, + ]); + $add = [ + [ + 'href' => 'principal:principals/circles/bob', + 'readOnly' => true, + ] + ]; + $principal = 'principals/groups/bob'; + + $this->shareCache->expects(self::once()) + ->method('clear'); + $this->principalBackend->expects(self::once()) + ->method('findByUri') + ->willReturn($principal); + $this->userManager->expects(self::never()) + ->method('userExists'); + $this->groupManager->expects(self::once()) + ->method('groupExists') + ->willReturn(true); + $this->calendarService->expects(self::once()) + ->method('shareWith') + ->with($shareable->getResourceId(), $principal, Backend::ACCESS_READ); + + $this->backend->updateShares($shareable, $add, []); + } + + public function testUnshareBob(): void { + $shareable = $this->createConfiguredMock(IShareable::class, [ + 'getOwner' => 'principals/users/alice', + 'getResourceId' => 42, + ]); + $remove = [ + 'principal:principals/users/bob', + ]; + $principal = 'principals/users/bob'; + + $this->shareCache->expects(self::once()) + ->method('clear'); + $this->principalBackend->expects(self::once()) + ->method('findByUri') + ->willReturn($principal); + $this->calendarService->expects(self::once()) + ->method('deleteShare') + ->with($shareable->getResourceId(), $principal); + $this->calendarService->expects(self::never()) + ->method('unshare'); + + $this->backend->updateShares($shareable, [], $remove); + } + + public function testUnshareWithBobGroup(): void { + $shareable = $this->createConfiguredMock(IShareable::class, [ + 'getOwner' => 'principals/users/alice', + 'getResourceId' => 42, + ]); + $remove = [ + 'principal:principals/users/bob', + ]; + $oldShares = [ + [ + 'href' => 'principal:principals/groups/bob', + 'commonName' => 'bob', + 'status' => 1, + 'readOnly' => true, + '{http://owncloud.org/ns}principal' => 'principals/groups/bob', + '{http://owncloud.org/ns}group-share' => true, + ] + ]; + + + $this->shareCache->expects(self::once()) + ->method('clear'); + $this->principalBackend->expects(self::once()) + ->method('findByUri') + ->willReturn('principals/users/bob'); + $this->calendarService->expects(self::once()) + ->method('deleteShare') + ->with($shareable->getResourceId(), 'principals/users/bob'); + $this->calendarService->expects(self::never()) + ->method('unshare'); + + $this->backend->updateShares($shareable, [], $remove, $oldShares); + } + + public function testGetShares(): void { + $resourceId = 42; + $principal = 'principals/groups/bob'; + $rows = [ + [ + 'principaluri' => $principal, + 'access' => Backend::ACCESS_READ, + ] + ]; + $expected = [ + [ + 'href' => 'principal:principals/groups/bob', + 'commonName' => 'bob', + 'status' => 1, + 'readOnly' => true, + '{http://owncloud.org/ns}principal' => $principal, + '{http://owncloud.org/ns}group-share' => true, + ] + ]; + + + $this->shareCache->expects(self::once()) + ->method('get') + ->with((string)$resourceId) + ->willReturn(null); + $this->calendarService->expects(self::once()) + ->method('getShares') + ->with($resourceId) + ->willReturn($rows); + $this->principalBackend->expects(self::once()) + ->method('getPrincipalByPath') + ->with($principal) + ->willReturn(['uri' => $principal, '{DAV:}displayname' => 'bob']); + $this->shareCache->expects(self::once()) + ->method('set') + ->with((string)$resourceId, $expected); + + $result = $this->backend->getShares($resourceId); + $this->assertEquals($expected, $result); + } + + public function testGetSharesAddressbooks(): void { + $service = $this->createMock(\OCA\DAV\CardDAV\Sharing\Service::class); + $backend = new ContactsSharingBackend( + $this->userManager, + $this->groupManager, + $this->principalBackend, + $this->cacheFactory, + $service, + $this->logger); + $resourceId = 42; + $principal = 'principals/groups/bob'; + $rows = [ + [ + 'principaluri' => $principal, + 'access' => Backend::ACCESS_READ, + ] + ]; + $expected = [ + [ + 'href' => 'principal:principals/groups/bob', + 'commonName' => 'bob', + 'status' => 1, + 'readOnly' => true, + '{http://owncloud.org/ns}principal' => $principal, + '{http://owncloud.org/ns}group-share' => true, + ] + ]; + + $this->shareCache->expects(self::once()) + ->method('get') + ->with((string)$resourceId) + ->willReturn(null); + $service->expects(self::once()) + ->method('getShares') + ->with($resourceId) + ->willReturn($rows); + $this->principalBackend->expects(self::once()) + ->method('getPrincipalByPath') + ->with($principal) + ->willReturn(['uri' => $principal, '{DAV:}displayname' => 'bob']); + $this->shareCache->expects(self::once()) + ->method('set') + ->with((string)$resourceId, $expected); + + $result = $backend->getShares($resourceId); + $this->assertEquals($expected, $result); + } + + public function testPreloadShares(): void { + $resourceIds = [42, 99]; + $rows = [ + [ + 'resourceid' => 42, + 'principaluri' => 'principals/groups/bob', + 'access' => Backend::ACCESS_READ, + ], + [ + 'resourceid' => 99, + 'principaluri' => 'principals/users/carlos', + 'access' => Backend::ACCESS_READ_WRITE, + ] + ]; + $principalResults = [ + ['uri' => 'principals/groups/bob', '{DAV:}displayname' => 'bob'], + ['uri' => 'principals/users/carlos', '{DAV:}displayname' => 'carlos'], + ]; + + $this->shareCache->expects(self::exactly(2)) + ->method('get') + ->willReturn(null); + $this->calendarService->expects(self::once()) + ->method('getSharesForIds') + ->with($resourceIds) + ->willReturn($rows); + $this->principalBackend->expects(self::exactly(2)) + ->method('getPrincipalByPath') + ->willReturnCallback(function (string $principal) use ($principalResults) { + switch ($principal) { + case 'principals/groups/bob': + return $principalResults[0]; + default: + return $principalResults[1]; + } + }); + $this->shareCache->expects(self::exactly(2)) + ->method('set'); + + $this->backend->preloadShares($resourceIds); + } +} diff --git a/apps/dav/tests/unit/DAV/Sharing/PluginTest.php b/apps/dav/tests/unit/DAV/Sharing/PluginTest.php index 1c4bb5e12e4..7a88f7cc5dd 100644 --- a/apps/dav/tests/unit/DAV/Sharing/PluginTest.php +++ b/apps/dav/tests/unit/DAV/Sharing/PluginTest.php @@ -1,28 +1,10 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OCA\DAV\Tests\unit\DAV\Sharing; @@ -31,6 +13,7 @@ use OCA\DAV\DAV\Sharing\IShareable; use OCA\DAV\DAV\Sharing\Plugin; use OCP\IConfig; use OCP\IRequest; +use PHPUnit\Framework\MockObject\MockObject; use Sabre\DAV\Server; use Sabre\DAV\SimpleCollection; use Sabre\HTTP\Request; @@ -38,38 +21,30 @@ use Sabre\HTTP\Response; use Test\TestCase; class PluginTest extends TestCase { - - /** @var Plugin */ - private $plugin; - /** @var Server */ - private $server; - /** @var IShareable | \PHPUnit\Framework\MockObject\MockObject */ - private $book; + private Plugin $plugin; + private Server $server; + private IShareable&MockObject $book; protected function setUp(): void { parent::setUp(); - /** @var Auth | \PHPUnit\Framework\MockObject\MockObject $authBackend */ - $authBackend = $this->getMockBuilder(Auth::class)->disableOriginalConstructor()->getMock(); + $authBackend = $this->createMock(Auth::class); $authBackend->method('isDavAuthenticated')->willReturn(true); - /** @var IRequest $request */ - $request = $this->getMockBuilder(IRequest::class)->disableOriginalConstructor()->getMock(); + $request = $this->createMock(IRequest::class); $config = $this->createMock(IConfig::class); $this->plugin = new Plugin($authBackend, $request, $config); $root = new SimpleCollection('root'); $this->server = new \Sabre\DAV\Server($root); /** @var SimpleCollection $node */ - $this->book = $this->getMockBuilder(IShareable::class)-> - disableOriginalConstructor()-> - getMock(); + $this->book = $this->createMock(IShareable::class); $this->book->method('getName')->willReturn('addressbook1.vcf'); $root->addChild($this->book); $this->plugin->initialize($this->server); } - public function testSharing() { + public function testSharing(): void { $this->book->expects($this->once())->method('updateShares')->with([[ 'href' => 'principal:principals/admin', 'commonName' => null, diff --git a/apps/dav/tests/unit/DAV/SystemPrincipalBackendTest.php b/apps/dav/tests/unit/DAV/SystemPrincipalBackendTest.php index 05e6699cc4c..3df861accf2 100644 --- a/apps/dav/tests/unit/DAV/SystemPrincipalBackendTest.php +++ b/apps/dav/tests/unit/DAV/SystemPrincipalBackendTest.php @@ -1,47 +1,27 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Citharel <nextcloud@tcit.fr> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OCA\DAV\Tests\unit\DAV; use OCA\DAV\DAV\SystemPrincipalBackend; +use Sabre\DAV\Exception; use Test\TestCase; class SystemPrincipalBackendTest extends TestCase { - /** - * @dataProvider providesPrefix - * @param $expected - * @param $prefix - */ - public function testGetPrincipalsByPrefix($expected, $prefix) { + #[\PHPUnit\Framework\Attributes\DataProvider('providesPrefix')] + public function testGetPrincipalsByPrefix(array $expected, string $prefix): void { $backend = new SystemPrincipalBackend(); $result = $backend->getPrincipalsByPrefix($prefix); $this->assertEquals($expected, $result); } - public function providesPrefix() { + public static function providesPrefix(): array { return [ [[], ''], [[[ @@ -56,18 +36,14 @@ class SystemPrincipalBackendTest extends TestCase { ]; } - /** - * @dataProvider providesPath - * @param $expected - * @param $path - */ - public function testGetPrincipalByPath($expected, $path) { + #[\PHPUnit\Framework\Attributes\DataProvider('providesPath')] + public function testGetPrincipalByPath(?array $expected, string $path): void { $backend = new SystemPrincipalBackend(); $result = $backend->getPrincipalByPath($path); $this->assertEquals($expected, $result); } - public function providesPath() { + public static function providesPath(): array { return [ [null, ''], [null, 'principals'], @@ -79,60 +55,44 @@ class SystemPrincipalBackendTest extends TestCase { ]; } - /** - * @dataProvider providesPrincipalForGetGroupMemberSet - * - * @param string $principal - * @throws \Sabre\DAV\Exception - */ - public function testGetGroupMemberSetExceptional($principal) { - $this->expectException(\Sabre\DAV\Exception::class); + #[\PHPUnit\Framework\Attributes\DataProvider('providesPrincipalForGetGroupMemberSet')] + public function testGetGroupMemberSetExceptional(?string $principal): void { + $this->expectException(Exception::class); $this->expectExceptionMessage('Principal not found'); $backend = new SystemPrincipalBackend(); $backend->getGroupMemberSet($principal); } - public function providesPrincipalForGetGroupMemberSet() { + public static function providesPrincipalForGetGroupMemberSet(): array { return [ [null], ['principals/system'], ]; } - /** - * @throws \Sabre\DAV\Exception - */ - public function testGetGroupMemberSet() { + public function testGetGroupMemberSet(): void { $backend = new SystemPrincipalBackend(); $result = $backend->getGroupMemberSet('principals/system/system'); $this->assertEquals(['principals/system/system'], $result); } - /** - * @dataProvider providesPrincipalForGetGroupMembership - * - * @param string $principal - * @throws \Sabre\DAV\Exception - */ - public function testGetGroupMembershipExceptional($principal) { - $this->expectException(\Sabre\DAV\Exception::class); + #[\PHPUnit\Framework\Attributes\DataProvider('providesPrincipalForGetGroupMembership')] + public function testGetGroupMembershipExceptional(string $principal): void { + $this->expectException(Exception::class); $this->expectExceptionMessage('Principal not found'); $backend = new SystemPrincipalBackend(); $backend->getGroupMembership($principal); } - public function providesPrincipalForGetGroupMembership() { + public static function providesPrincipalForGetGroupMembership(): array { return [ ['principals/system/a'], ]; } - /** - * @throws \Sabre\DAV\Exception - */ - public function testGetGroupMembership() { + public function testGetGroupMembership(): void { $backend = new SystemPrincipalBackend(); $result = $backend->getGroupMembership('principals/system/system'); $this->assertEquals([], $result); diff --git a/apps/dav/tests/unit/DAV/ViewOnlyPluginTest.php b/apps/dav/tests/unit/DAV/ViewOnlyPluginTest.php new file mode 100644 index 00000000000..eefbc53fd22 --- /dev/null +++ b/apps/dav/tests/unit/DAV/ViewOnlyPluginTest.php @@ -0,0 +1,167 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2022-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2019 ownCloud GmbH + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OCA\DAV\Tests\unit\DAV; + +use OCA\DAV\Connector\Sabre\Exception\Forbidden; +use OCA\DAV\Connector\Sabre\File as DavFile; +use OCA\DAV\DAV\ViewOnlyPlugin; +use OCA\Files_Sharing\SharedStorage; +use OCA\Files_Versions\Sabre\VersionFile; +use OCA\Files_Versions\Versions\IVersion; +use OCP\Files\File; +use OCP\Files\Folder; +use OCP\Files\Storage\ISharedStorage; +use OCP\Files\Storage\IStorage; +use OCP\IUser; +use OCP\Share\IAttributes; +use OCP\Share\IShare; +use PHPUnit\Framework\MockObject\MockObject; +use Sabre\DAV\Server; +use Sabre\DAV\Tree; +use Sabre\HTTP\RequestInterface; +use Test\TestCase; + +class ViewOnlyPluginTest extends TestCase { + private Tree&MockObject $tree; + private RequestInterface&MockObject $request; + private Folder&MockObject $userFolder; + private ViewOnlyPlugin $plugin; + + public function setUp(): void { + parent::setUp(); + + $this->userFolder = $this->createMock(Folder::class); + $this->request = $this->createMock(RequestInterface::class); + $this->tree = $this->createMock(Tree::class); + $server = $this->createMock(Server::class); + + $this->plugin = new ViewOnlyPlugin( + $this->userFolder, + ); + $server->tree = $this->tree; + + $this->plugin->initialize($server); + } + + public function testCanGetNonDav(): void { + $this->request->expects($this->once())->method('getPath')->willReturn('files/test/target'); + $this->tree->method('getNodeForPath')->willReturn(null); + + $this->assertTrue($this->plugin->checkViewOnly($this->request)); + } + + public function testCanGetNonShared(): void { + $this->request->expects($this->once())->method('getPath')->willReturn('files/test/target'); + $davNode = $this->createMock(DavFile::class); + $this->tree->method('getNodeForPath')->willReturn($davNode); + + $file = $this->createMock(File::class); + $davNode->method('getNode')->willReturn($file); + + $storage = $this->createMock(IStorage::class); + $file->method('getStorage')->willReturn($storage); + $storage->method('instanceOfStorage')->with(ISharedStorage::class)->willReturn(false); + + $this->assertTrue($this->plugin->checkViewOnly($this->request)); + } + + public static function providesDataForCanGet(): array { + return [ + // has attribute permissions-download enabled - can get file + [false, true, true, true], + // has no attribute permissions-download - can get file + [false, null, true, true], + // has attribute permissions-download enabled - can get file version + [true, true, true, true], + // has no attribute permissions-download - can get file version + [true, null, true, true], + // has attribute permissions-download disabled - cannot get the file + [false, false, false, false], + // has attribute permissions-download disabled - cannot get the file version + [true, false, false, false], + + // Has global allowViewWithoutDownload option enabled + // has attribute permissions-download disabled - can get file + [false, false, false, true], + // has attribute permissions-download disabled - can get file version + [true, false, false, true], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('providesDataForCanGet')] + public function testCanGet(bool $isVersion, ?bool $attrEnabled, bool $expectCanDownloadFile, bool $allowViewWithoutDownload): void { + $nodeInfo = $this->createMock(File::class); + if ($isVersion) { + $davPath = 'versions/alice/versions/117/123456'; + $version = $this->createMock(IVersion::class); + $version->expects($this->once()) + ->method('getSourceFile') + ->willReturn($nodeInfo); + $davNode = $this->createMock(VersionFile::class); + $davNode->expects($this->once()) + ->method('getVersion') + ->willReturn($version); + + $currentUser = $this->createMock(IUser::class); + $currentUser->expects($this->once()) + ->method('getUID') + ->willReturn('alice'); + $nodeInfo->expects($this->once()) + ->method('getOwner') + ->willReturn($currentUser); + + $nodeInfo = $this->createMock(File::class); + $owner = $this->createMock(IUser::class); + $owner->expects($this->once()) + ->method('getUID') + ->willReturn('bob'); + $this->userFolder->expects($this->once()) + ->method('getById') + ->willReturn([$nodeInfo]); + $this->userFolder->expects($this->once()) + ->method('getOwner') + ->willReturn($owner); + } else { + $davPath = 'files/path/to/file.odt'; + $davNode = $this->createMock(DavFile::class); + $davNode->method('getNode')->willReturn($nodeInfo); + } + + $this->request->expects($this->once())->method('getPath')->willReturn($davPath); + + $this->tree->expects($this->once()) + ->method('getNodeForPath') + ->with($davPath) + ->willReturn($davNode); + + $storage = $this->createMock(SharedStorage::class); + $share = $this->createMock(IShare::class); + $nodeInfo->expects($this->once()) + ->method('getStorage') + ->willReturn($storage); + $storage->method('instanceOfStorage')->with(ISharedStorage::class)->willReturn(true); + $storage->method('getShare')->willReturn($share); + + $extAttr = $this->createMock(IAttributes::class); + $share->method('getAttributes')->willReturn($extAttr); + $extAttr->expects($this->once()) + ->method('getAttribute') + ->with('permissions', 'download') + ->willReturn($attrEnabled); + + $share->expects($this->once()) + ->method('canSeeContent') + ->willReturn($allowViewWithoutDownload); + + if (!$expectCanDownloadFile) { + $this->expectException(Forbidden::class); + } + $this->plugin->checkViewOnly($this->request); + } +} |