diff options
Diffstat (limited to 'apps/dav/tests/unit/Connector')
36 files changed, 2836 insertions, 3161 deletions
diff --git a/apps/dav/tests/unit/Connector/PublicAuthTest.php b/apps/dav/tests/unit/Connector/LegacyPublicAuthTest.php index 89068c0e6ef..8b8c775c8ec 100644 --- a/apps/dav/tests/unit/Connector/PublicAuthTest.php +++ b/apps/dav/tests/unit/Connector/LegacyPublicAuthTest.php @@ -1,78 +1,46 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @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\Connector; -use OC\Security\Bruteforce\Throttler; +use OCA\DAV\Connector\LegacyPublicAuth; use OCP\IRequest; use OCP\ISession; +use OCP\Security\Bruteforce\IThrottler; use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IManager; use OCP\Share\IShare; +use PHPUnit\Framework\MockObject\MockObject; /** - * Class PublicAuthTest + * Class LegacyPublicAuthTest * * @group DB * * @package OCA\DAV\Tests\unit\Connector */ -class PublicAuthTest extends \Test\TestCase { - - /** @var ISession|\PHPUnit\Framework\MockObject\MockObject */ - private $session; - /** @var IRequest|\PHPUnit\Framework\MockObject\MockObject */ - private $request; - /** @var IManager|\PHPUnit\Framework\MockObject\MockObject */ - private $shareManager; - /** @var \OCA\DAV\Connector\PublicAuth */ - private $auth; - /** @var Throttler|\PHPUnit\Framework\MockObject\MockObject */ - private $throttler; - - /** @var string */ - private $oldUser; +class LegacyPublicAuthTest extends \Test\TestCase { + private ISession&MockObject $session; + private IRequest&MockObject $request; + private IManager&MockObject $shareManager; + private IThrottler&MockObject $throttler; + private LegacyPublicAuth $auth; + private string|false $oldUser; protected function setUp(): void { parent::setUp(); - $this->session = $this->getMockBuilder(ISession::class) - ->disableOriginalConstructor() - ->getMock(); - $this->request = $this->getMockBuilder(IRequest::class) - ->disableOriginalConstructor() - ->getMock(); - $this->shareManager = $this->getMockBuilder(IManager::class) - ->disableOriginalConstructor() - ->getMock(); - $this->throttler = $this->getMockBuilder(Throttler::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->auth = new \OCA\DAV\Connector\PublicAuth( + $this->session = $this->createMock(ISession::class); + $this->request = $this->createMock(IRequest::class); + $this->shareManager = $this->createMock(IManager::class); + $this->throttler = $this->createMock(IThrottler::class); + + $this->auth = new LegacyPublicAuth( $this->request, $this->shareManager, $this->session, @@ -88,12 +56,14 @@ class PublicAuthTest extends \Test\TestCase { // Set old user \OC_User::setUserId($this->oldUser); - \OC_Util::setupFS($this->oldUser); + if ($this->oldUser !== false) { + \OC_Util::setupFS($this->oldUser); + } parent::tearDown(); } - public function testNoShare() { + public function testNoShare(): void { $this->shareManager->expects($this->once()) ->method('getShareByToken') ->willThrowException(new ShareNotFound()); @@ -103,10 +73,8 @@ class PublicAuthTest extends \Test\TestCase { $this->assertFalse($result); } - public function testShareNoPassword() { - $share = $this->getMockBuilder(IShare::class) - ->disableOriginalConstructor() - ->getMock(); + public function testShareNoPassword(): void { + $share = $this->createMock(IShare::class); $share->method('getPassword')->willReturn(null); $this->shareManager->expects($this->once()) @@ -118,10 +86,8 @@ class PublicAuthTest extends \Test\TestCase { $this->assertTrue($result); } - public function testSharePasswordFancyShareType() { - $share = $this->getMockBuilder(IShare::class) - ->disableOriginalConstructor() - ->getMock(); + public function testSharePasswordFancyShareType(): void { + $share = $this->createMock(IShare::class); $share->method('getPassword')->willReturn('password'); $share->method('getShareType')->willReturn(42); @@ -135,10 +101,8 @@ class PublicAuthTest extends \Test\TestCase { } - public function testSharePasswordRemote() { - $share = $this->getMockBuilder(IShare::class) - ->disableOriginalConstructor() - ->getMock(); + public function testSharePasswordRemote(): void { + $share = $this->createMock(IShare::class); $share->method('getPassword')->willReturn('password'); $share->method('getShareType')->willReturn(IShare::TYPE_REMOTE); @@ -151,10 +115,8 @@ class PublicAuthTest extends \Test\TestCase { $this->assertTrue($result); } - public function testSharePasswordLinkValidPassword() { - $share = $this->getMockBuilder(IShare::class) - ->disableOriginalConstructor() - ->getMock(); + public function testSharePasswordLinkValidPassword(): void { + $share = $this->createMock(IShare::class); $share->method('getPassword')->willReturn('password'); $share->method('getShareType')->willReturn(IShare::TYPE_LINK); @@ -164,19 +126,17 @@ class PublicAuthTest extends \Test\TestCase { $this->shareManager->expects($this->once()) ->method('checkPassword')->with( - $this->equalTo($share), - $this->equalTo('password') - )->willReturn(true); + $this->equalTo($share), + $this->equalTo('password') + )->willReturn(true); $result = $this->invokePrivate($this->auth, 'validateUserPass', ['username', 'password']); $this->assertTrue($result); } - public function testSharePasswordMailValidPassword() { - $share = $this->getMockBuilder(IShare::class) - ->disableOriginalConstructor() - ->getMock(); + public function testSharePasswordMailValidPassword(): void { + $share = $this->createMock(IShare::class); $share->method('getPassword')->willReturn('password'); $share->method('getShareType')->willReturn(IShare::TYPE_EMAIL); @@ -195,10 +155,8 @@ class PublicAuthTest extends \Test\TestCase { $this->assertTrue($result); } - public function testSharePasswordLinkValidSession() { - $share = $this->getMockBuilder(IShare::class) - ->disableOriginalConstructor() - ->getMock(); + public function testInvalidSharePasswordLinkValidSession(): void { + $share = $this->createMock(IShare::class); $share->method('getPassword')->willReturn('password'); $share->method('getShareType')->willReturn(IShare::TYPE_LINK); $share->method('getId')->willReturn('42'); @@ -221,10 +179,8 @@ class PublicAuthTest extends \Test\TestCase { $this->assertTrue($result); } - public function testSharePasswordLinkInvalidSession() { - $share = $this->getMockBuilder(IShare::class) - ->disableOriginalConstructor() - ->getMock(); + public function testSharePasswordLinkInvalidSession(): void { + $share = $this->createMock(IShare::class); $share->method('getPassword')->willReturn('password'); $share->method('getShareType')->willReturn(IShare::TYPE_LINK); $share->method('getId')->willReturn('42'); @@ -248,10 +204,8 @@ class PublicAuthTest extends \Test\TestCase { } - public function testSharePasswordMailInvalidSession() { - $share = $this->getMockBuilder(IShare::class) - ->disableOriginalConstructor() - ->getMock(); + public function testSharePasswordMailInvalidSession(): void { + $share = $this->createMock(IShare::class); $share->method('getPassword')->willReturn('password'); $share->method('getShareType')->willReturn(IShare::TYPE_EMAIL); $share->method('getId')->willReturn('42'); diff --git a/apps/dav/tests/unit/Connector/Sabre/AuthTest.php b/apps/dav/tests/unit/Connector/Sabre/AuthTest.php index 9355b34d66a..4b42a815708 100644 --- a/apps/dav/tests/unit/Connector/Sabre/AuthTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/AuthTest.php @@ -1,40 +1,23 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @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\Connector\Sabre; +use OC\Authentication\Exceptions\PasswordLoginForbiddenException; use OC\Authentication\TwoFactorAuth\Manager; -use OC\Security\Bruteforce\Throttler; use OC\User\Session; +use OCA\DAV\Connector\Sabre\Auth; +use OCA\DAV\Connector\Sabre\Exception\PasswordLoginForbidden; use OCP\IRequest; use OCP\ISession; use OCP\IUser; +use OCP\Security\Bruteforce\IThrottler; +use PHPUnit\Framework\MockObject\MockObject; use Sabre\DAV\Server; use Sabre\HTTP\RequestInterface; use Sabre\HTTP\ResponseInterface; @@ -47,34 +30,21 @@ use Test\TestCase; * @group DB */ class AuthTest extends TestCase { - /** @var ISession */ - private $session; - /** @var \OCA\DAV\Connector\Sabre\Auth */ - private $auth; - /** @var Session */ - private $userSession; - /** @var IRequest */ - private $request; - /** @var Manager */ - private $twoFactorManager; - /** @var Throttler */ - private $throttler; + private ISession&MockObject $session; + private Session&MockObject $userSession; + private IRequest&MockObject $request; + private Manager&MockObject $twoFactorManager; + private IThrottler&MockObject $throttler; + private Auth $auth; protected function setUp(): void { parent::setUp(); - $this->session = $this->getMockBuilder(ISession::class) - ->disableOriginalConstructor()->getMock(); - $this->userSession = $this->getMockBuilder(Session::class) - ->disableOriginalConstructor()->getMock(); - $this->request = $this->getMockBuilder(IRequest::class) - ->disableOriginalConstructor()->getMock(); - $this->twoFactorManager = $this->getMockBuilder(Manager::class) - ->disableOriginalConstructor() - ->getMock(); - $this->throttler = $this->getMockBuilder(Throttler::class) - ->disableOriginalConstructor() - ->getMock(); - $this->auth = new \OCA\DAV\Connector\Sabre\Auth( + $this->session = $this->createMock(ISession::class); + $this->userSession = $this->createMock(Session::class); + $this->request = $this->createMock(IRequest::class); + $this->twoFactorManager = $this->createMock(Manager::class); + $this->throttler = $this->createMock(IThrottler::class); + $this->auth = new Auth( $this->session, $this->userSession, $this->request, @@ -83,41 +53,39 @@ class AuthTest extends TestCase { ); } - public function testIsDavAuthenticatedWithoutDavSession() { + public function testIsDavAuthenticatedWithoutDavSession(): void { $this->session ->expects($this->once()) ->method('get') ->with('AUTHENTICATED_TO_DAV_BACKEND') ->willReturn(null); - $this->assertFalse($this->invokePrivate($this->auth, 'isDavAuthenticated', ['MyTestUser'])); + $this->assertFalse(self::invokePrivate($this->auth, 'isDavAuthenticated', ['MyTestUser'])); } - public function testIsDavAuthenticatedWithWrongDavSession() { + public function testIsDavAuthenticatedWithWrongDavSession(): void { $this->session ->expects($this->exactly(2)) ->method('get') ->with('AUTHENTICATED_TO_DAV_BACKEND') ->willReturn('AnotherUser'); - $this->assertFalse($this->invokePrivate($this->auth, 'isDavAuthenticated', ['MyTestUser'])); + $this->assertFalse(self::invokePrivate($this->auth, 'isDavAuthenticated', ['MyTestUser'])); } - public function testIsDavAuthenticatedWithCorrectDavSession() { + public function testIsDavAuthenticatedWithCorrectDavSession(): void { $this->session ->expects($this->exactly(2)) ->method('get') ->with('AUTHENTICATED_TO_DAV_BACKEND') ->willReturn('MyTestUser'); - $this->assertTrue($this->invokePrivate($this->auth, 'isDavAuthenticated', ['MyTestUser'])); + $this->assertTrue(self::invokePrivate($this->auth, 'isDavAuthenticated', ['MyTestUser'])); } - public function testValidateUserPassOfAlreadyDAVAuthenticatedUser() { - $user = $this->getMockBuilder(IUser::class) - ->disableOriginalConstructor() - ->getMock(); - $user->expects($this->exactly(2)) + public function testValidateUserPassOfAlreadyDAVAuthenticatedUser(): void { + $user = $this->createMock(IUser::class); + $user->expects($this->exactly(1)) ->method('getUID') ->willReturn('MyTestUser'); $this->userSession @@ -125,7 +93,7 @@ class AuthTest extends TestCase { ->method('isLoggedIn') ->willReturn(true); $this->userSession - ->expects($this->exactly(2)) + ->expects($this->exactly(1)) ->method('getUser') ->willReturn($user); $this->session @@ -137,13 +105,11 @@ class AuthTest extends TestCase { ->expects($this->once()) ->method('close'); - $this->assertTrue($this->invokePrivate($this->auth, 'validateUserPass', ['MyTestUser', 'MyTestPassword'])); + $this->assertTrue(self::invokePrivate($this->auth, 'validateUserPass', ['MyTestUser', 'MyTestPassword'])); } - public function testValidateUserPassOfInvalidDAVAuthenticatedUser() { - $user = $this->getMockBuilder(IUser::class) - ->disableOriginalConstructor() - ->getMock(); + public function testValidateUserPassOfInvalidDAVAuthenticatedUser(): void { + $user = $this->createMock(IUser::class); $user->expects($this->once()) ->method('getUID') ->willReturn('MyTestUser'); @@ -164,14 +130,12 @@ class AuthTest extends TestCase { ->expects($this->once()) ->method('close'); - $this->assertFalse($this->invokePrivate($this->auth, 'validateUserPass', ['MyTestUser', 'MyTestPassword'])); + $this->assertFalse(self::invokePrivate($this->auth, 'validateUserPass', ['MyTestUser', 'MyTestPassword'])); } - public function testValidateUserPassOfInvalidDAVAuthenticatedUserWithValidPassword() { - $user = $this->getMockBuilder(IUser::class) - ->disableOriginalConstructor() - ->getMock(); - $user->expects($this->exactly(3)) + public function testValidateUserPassOfInvalidDAVAuthenticatedUserWithValidPassword(): void { + $user = $this->createMock(IUser::class); + $user->expects($this->exactly(2)) ->method('getUID') ->willReturn('MyTestUser'); $this->userSession @@ -179,7 +143,7 @@ class AuthTest extends TestCase { ->method('isLoggedIn') ->willReturn(true); $this->userSession - ->expects($this->exactly(3)) + ->expects($this->exactly(2)) ->method('getUser') ->willReturn($user); $this->session @@ -200,10 +164,10 @@ class AuthTest extends TestCase { ->expects($this->once()) ->method('close'); - $this->assertTrue($this->invokePrivate($this->auth, 'validateUserPass', ['MyTestUser', 'MyTestPassword'])); + $this->assertTrue(self::invokePrivate($this->auth, 'validateUserPass', ['MyTestUser', 'MyTestPassword'])); } - public function testValidateUserPassWithInvalidPassword() { + public function testValidateUserPassWithInvalidPassword(): void { $this->userSession ->expects($this->once()) ->method('isLoggedIn') @@ -217,12 +181,12 @@ class AuthTest extends TestCase { ->expects($this->once()) ->method('close'); - $this->assertFalse($this->invokePrivate($this->auth, 'validateUserPass', ['MyTestUser', 'MyTestPassword'])); + $this->assertFalse(self::invokePrivate($this->auth, 'validateUserPass', ['MyTestUser', 'MyTestPassword'])); } - - public function testValidateUserPassWithPasswordLoginForbidden() { - $this->expectException(\OCA\DAV\Connector\Sabre\Exception\PasswordLoginForbidden::class); + + public function testValidateUserPassWithPasswordLoginForbidden(): void { + $this->expectException(PasswordLoginForbidden::class); $this->userSession ->expects($this->once()) @@ -232,21 +196,17 @@ class AuthTest extends TestCase { ->expects($this->once()) ->method('logClientIn') ->with('MyTestUser', 'MyTestPassword') - ->will($this->throwException(new \OC\Authentication\Exceptions\PasswordLoginForbiddenException())); + ->willThrowException(new PasswordLoginForbiddenException()); $this->session ->expects($this->once()) ->method('close'); - $this->invokePrivate($this->auth, 'validateUserPass', ['MyTestUser', 'MyTestPassword']); + self::invokePrivate($this->auth, 'validateUserPass', ['MyTestUser', 'MyTestPassword']); } - public function testAuthenticateAlreadyLoggedInWithoutCsrfTokenForNonGet() { - $request = $this->getMockBuilder(RequestInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $response = $this->getMockBuilder(ResponseInterface::class) - ->disableOriginalConstructor() - ->getMock(); + public function testAuthenticateAlreadyLoggedInWithoutCsrfTokenForNonGet(): void { + $request = $this->createMock(RequestInterface::class); + $response = $this->createMock(ResponseInterface::class); $this->userSession ->expects($this->any()) ->method('isLoggedIn') @@ -260,9 +220,7 @@ class AuthTest extends TestCase { ->method('get') ->with('AUTHENTICATED_TO_DAV_BACKEND') ->willReturn(null); - $user = $this->getMockBuilder(IUser::class) - ->disableOriginalConstructor() - ->getMock(); + $user = $this->createMock(IUser::class); $user->expects($this->any()) ->method('getUID') ->willReturn('MyWrongDavUser'); @@ -283,13 +241,9 @@ class AuthTest extends TestCase { $this->assertSame($expectedResponse, $response); } - public function testAuthenticateAlreadyLoggedInWithoutCsrfTokenAndCorrectlyDavAuthenticated() { - $request = $this->getMockBuilder(RequestInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $response = $this->getMockBuilder(ResponseInterface::class) - ->disableOriginalConstructor() - ->getMock(); + public function testAuthenticateAlreadyLoggedInWithoutCsrfTokenAndCorrectlyDavAuthenticated(): void { + $request = $this->createMock(RequestInterface::class); + $response = $this->createMock(ResponseInterface::class); $this->userSession ->expects($this->any()) ->method('isLoggedIn') @@ -301,20 +255,13 @@ class AuthTest extends TestCase { $this->request ->expects($this->any()) ->method('isUserAgent') - ->with([ - '/^Mozilla\/5\.0 \([A-Za-z ]+\) (mirall|csyncoC)\/.*$/', - '/^Mozilla\/5\.0 \(Android\) (ownCloud|Nextcloud)\-android.*$/', - '/^Mozilla\/5\.0 \(iOS\) (ownCloud|Nextcloud)\-iOS.*$/', - ]) ->willReturn(false); $this->session ->expects($this->any()) ->method('get') ->with('AUTHENTICATED_TO_DAV_BACKEND') ->willReturn('LoggedInUser'); - $user = $this->getMockBuilder(IUser::class) - ->disableOriginalConstructor() - ->getMock(); + $user = $this->createMock(IUser::class); $user->expects($this->any()) ->method('getUID') ->willReturn('LoggedInUser'); @@ -329,17 +276,13 @@ class AuthTest extends TestCase { $this->auth->check($request, $response); } - - public function testAuthenticateAlreadyLoggedInWithoutTwoFactorChallengePassed() { + + public function testAuthenticateAlreadyLoggedInWithoutTwoFactorChallengePassed(): void { $this->expectException(\Sabre\DAV\Exception\NotAuthenticated::class); $this->expectExceptionMessage('2FA challenge not passed.'); - $request = $this->getMockBuilder(RequestInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $response = $this->getMockBuilder(ResponseInterface::class) - ->disableOriginalConstructor() - ->getMock(); + $request = $this->createMock(RequestInterface::class); + $response = $this->createMock(ResponseInterface::class); $this->userSession ->expects($this->any()) ->method('isLoggedIn') @@ -351,20 +294,13 @@ class AuthTest extends TestCase { $this->request ->expects($this->any()) ->method('isUserAgent') - ->with([ - '/^Mozilla\/5\.0 \([A-Za-z ]+\) (mirall|csyncoC)\/.*$/', - '/^Mozilla\/5\.0 \(Android\) ownCloud\-android.*$/', - '/^Mozilla\/5\.0 \(iOS\) (ownCloud|Nextcloud)\-iOS.*$/', - ]) ->willReturn(false); $this->session ->expects($this->any()) ->method('get') ->with('AUTHENTICATED_TO_DAV_BACKEND') ->willReturn('LoggedInUser'); - $user = $this->getMockBuilder(IUser::class) - ->disableOriginalConstructor() - ->getMock(); + $user = $this->createMock(IUser::class); $user->expects($this->any()) ->method('getUID') ->willReturn('LoggedInUser'); @@ -383,17 +319,13 @@ class AuthTest extends TestCase { $this->auth->check($request, $response); } - - public function testAuthenticateAlreadyLoggedInWithoutCsrfTokenAndIncorrectlyDavAuthenticated() { + + public function testAuthenticateAlreadyLoggedInWithoutCsrfTokenAndIncorrectlyDavAuthenticated(): void { $this->expectException(\Sabre\DAV\Exception\NotAuthenticated::class); $this->expectExceptionMessage('CSRF check not passed.'); - $request = $this->getMockBuilder(RequestInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $response = $this->getMockBuilder(ResponseInterface::class) - ->disableOriginalConstructor() - ->getMock(); + $request = $this->createMock(RequestInterface::class); + $response = $this->createMock(ResponseInterface::class); $this->userSession ->expects($this->any()) ->method('isLoggedIn') @@ -405,20 +337,13 @@ class AuthTest extends TestCase { $this->request ->expects($this->any()) ->method('isUserAgent') - ->with([ - '/^Mozilla\/5\.0 \([A-Za-z ]+\) (mirall|csyncoC)\/.*$/', - '/^Mozilla\/5\.0 \(Android\) (ownCloud|Nextcloud)\-android.*$/', - '/^Mozilla\/5\.0 \(iOS\) (ownCloud|Nextcloud)\-iOS.*$/', - ]) ->willReturn(false); $this->session ->expects($this->any()) ->method('get') ->with('AUTHENTICATED_TO_DAV_BACKEND') ->willReturn('AnotherUser'); - $user = $this->getMockBuilder(IUser::class) - ->disableOriginalConstructor() - ->getMock(); + $user = $this->createMock(IUser::class); $user->expects($this->any()) ->method('getUID') ->willReturn('LoggedInUser'); @@ -433,13 +358,9 @@ class AuthTest extends TestCase { $this->auth->check($request, $response); } - public function testAuthenticateAlreadyLoggedInWithoutCsrfTokenForNonGetAndDesktopClient() { - $request = $this->getMockBuilder(RequestInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $response = $this->getMockBuilder(ResponseInterface::class) - ->disableOriginalConstructor() - ->getMock(); + public function testAuthenticateAlreadyLoggedInWithoutCsrfTokenForNonGetAndDesktopClient(): void { + $request = $this->createMock(RequestInterface::class); + $response = $this->createMock(ResponseInterface::class); $this->userSession ->expects($this->any()) ->method('isLoggedIn') @@ -451,20 +372,13 @@ class AuthTest extends TestCase { $this->request ->expects($this->any()) ->method('isUserAgent') - ->with([ - '/^Mozilla\/5\.0 \([A-Za-z ]+\) (mirall|csyncoC)\/.*$/', - '/^Mozilla\/5\.0 \(Android\) (ownCloud|Nextcloud)\-android.*$/', - '/^Mozilla\/5\.0 \(iOS\) (ownCloud|Nextcloud)\-iOS.*$/', - ]) ->willReturn(true); $this->session ->expects($this->any()) ->method('get') ->with('AUTHENTICATED_TO_DAV_BACKEND') ->willReturn(null); - $user = $this->getMockBuilder(IUser::class) - ->disableOriginalConstructor() - ->getMock(); + $user = $this->createMock(IUser::class); $user->expects($this->any()) ->method('getUID') ->willReturn('MyWrongDavUser'); @@ -480,13 +394,9 @@ class AuthTest extends TestCase { $this->auth->check($request, $response); } - public function testAuthenticateAlreadyLoggedInWithoutCsrfTokenForGet() { - $request = $this->getMockBuilder(RequestInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $response = $this->getMockBuilder(ResponseInterface::class) - ->disableOriginalConstructor() - ->getMock(); + public function testAuthenticateAlreadyLoggedInWithoutCsrfTokenForGet(): void { + $request = $this->createMock(RequestInterface::class); + $response = $this->createMock(ResponseInterface::class); $this->userSession ->expects($this->any()) ->method('isLoggedIn') @@ -496,9 +406,7 @@ class AuthTest extends TestCase { ->method('get') ->with('AUTHENTICATED_TO_DAV_BACKEND') ->willReturn(null); - $user = $this->getMockBuilder(IUser::class) - ->disableOriginalConstructor() - ->getMock(); + $user = $this->createMock(IUser::class); $user->expects($this->any()) ->method('getUID') ->willReturn('MyWrongDavUser'); @@ -515,13 +423,9 @@ class AuthTest extends TestCase { $this->assertEquals([true, 'principals/users/MyWrongDavUser'], $response); } - public function testAuthenticateAlreadyLoggedInWithCsrfTokenForGet() { - $request = $this->getMockBuilder(RequestInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $response = $this->getMockBuilder(ResponseInterface::class) - ->disableOriginalConstructor() - ->getMock(); + public function testAuthenticateAlreadyLoggedInWithCsrfTokenForGet(): void { + $request = $this->createMock(RequestInterface::class); + $response = $this->createMock(ResponseInterface::class); $this->userSession ->expects($this->any()) ->method('isLoggedIn') @@ -531,9 +435,7 @@ class AuthTest extends TestCase { ->method('get') ->with('AUTHENTICATED_TO_DAV_BACKEND') ->willReturn(null); - $user = $this->getMockBuilder(IUser::class) - ->disableOriginalConstructor() - ->getMock(); + $user = $this->createMock(IUser::class); $user->expects($this->any()) ->method('getUID') ->willReturn('MyWrongDavUser'); @@ -550,58 +452,84 @@ class AuthTest extends TestCase { $this->assertEquals([true, 'principals/users/MyWrongDavUser'], $response); } - public function testAuthenticateNoBasicAuthenticateHeadersProvided() { - $server = $this->getMockBuilder(Server::class) - ->disableOriginalConstructor() - ->getMock(); - $server->httpRequest = $this->getMockBuilder(RequestInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $server->httpResponse = $this->getMockBuilder(ResponseInterface::class) - ->disableOriginalConstructor() - ->getMock(); + public function testAuthenticateNoBasicAuthenticateHeadersProvided(): void { + $server = $this->createMock(Server::class); + $server->httpRequest = $this->createMock(RequestInterface::class); + $server->httpResponse = $this->createMock(ResponseInterface::class); $response = $this->auth->check($server->httpRequest, $server->httpResponse); $this->assertEquals([false, 'No \'Authorization: Basic\' header found. Either the client didn\'t send one, or the server is misconfigured'], $response); } - - public function testAuthenticateNoBasicAuthenticateHeadersProvidedWithAjax() { + + public function testAuthenticateNoBasicAuthenticateHeadersProvidedWithAjax(): void { $this->expectException(\Sabre\DAV\Exception\NotAuthenticated::class); $this->expectExceptionMessage('Cannot authenticate over ajax calls'); - /** @var \Sabre\HTTP\RequestInterface $httpRequest */ - $httpRequest = $this->getMockBuilder(RequestInterface::class) - ->disableOriginalConstructor() - ->getMock(); - /** @var \Sabre\HTTP\ResponseInterface $httpResponse */ - $httpResponse = $this->getMockBuilder(ResponseInterface::class) - ->disableOriginalConstructor() - ->getMock(); + /** @var \Sabre\HTTP\RequestInterface&MockObject $httpRequest */ + $httpRequest = $this->createMock(RequestInterface::class); + /** @var \Sabre\HTTP\ResponseInterface&MockObject $httpResponse */ + $httpResponse = $this->createMock(ResponseInterface::class); $this->userSession ->expects($this->any()) ->method('isLoggedIn') ->willReturn(false); $httpRequest + ->expects($this->exactly(2)) + ->method('getHeader') + ->willReturnMap([ + ['X-Requested-With', 'XMLHttpRequest'], + ['Authorization', null], + ]); + + $this->auth->check($httpRequest, $httpResponse); + } + + public function testAuthenticateWithBasicAuthenticateHeadersProvidedWithAjax(): void { + // No CSRF + $this->request ->expects($this->once()) + ->method('passesCSRFCheck') + ->willReturn(false); + + /** @var \Sabre\HTTP\RequestInterface&MockObject $httpRequest */ + $httpRequest = $this->createMock(RequestInterface::class); + /** @var \Sabre\HTTP\ResponseInterface&MockObject $httpResponse */ + $httpResponse = $this->createMock(ResponseInterface::class); + $httpRequest + ->expects($this->any()) ->method('getHeader') - ->with('X-Requested-With') - ->willReturn('XMLHttpRequest'); + ->willReturnMap([ + ['X-Requested-With', 'XMLHttpRequest'], + ['Authorization', 'basic dXNlcm5hbWU6cGFzc3dvcmQ='], + ]); + + $user = $this->createMock(IUser::class); + $user->expects($this->any()) + ->method('getUID') + ->willReturn('MyDavUser'); + $this->userSession + ->expects($this->any()) + ->method('isLoggedIn') + ->willReturn(false); + $this->userSession + ->expects($this->once()) + ->method('logClientIn') + ->with('username', 'password') + ->willReturn(true); + $this->userSession + ->expects($this->any()) + ->method('getUser') + ->willReturn($user); + $this->auth->check($httpRequest, $httpResponse); } - public function testAuthenticateNoBasicAuthenticateHeadersProvidedWithAjaxButUserIsStillLoggedIn() { + public function testAuthenticateNoBasicAuthenticateHeadersProvidedWithAjaxButUserIsStillLoggedIn(): void { /** @var \Sabre\HTTP\RequestInterface $httpRequest */ - $httpRequest = $this->getMockBuilder(RequestInterface::class) - ->disableOriginalConstructor() - ->getMock(); + $httpRequest = $this->createMock(RequestInterface::class); /** @var \Sabre\HTTP\ResponseInterface $httpResponse */ - $httpResponse = $this->getMockBuilder(ResponseInterface::class) - ->disableOriginalConstructor() - ->getMock(); - /** @var IUser */ - $user = $this->getMockBuilder(IUser::class) - ->disableOriginalConstructor() - ->getMock(); + $httpResponse = $this->createMock(ResponseInterface::class); + $user = $this->createMock(IUser::class); $user->method('getUID')->willReturn('MyTestUser'); $this->userSession ->expects($this->any()) @@ -631,65 +559,44 @@ class AuthTest extends TestCase { ); } - public function testAuthenticateValidCredentials() { - $server = $this->getMockBuilder(Server::class) - ->disableOriginalConstructor() - ->getMock(); - $server->httpRequest = $this->getMockBuilder(RequestInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $server->httpRequest - ->expects($this->at(0)) - ->method('getHeader') - ->with('X-Requested-With') - ->willReturn(null); + public function testAuthenticateValidCredentials(): void { + $server = $this->createMock(Server::class); + $server->httpRequest = $this->createMock(RequestInterface::class); $server->httpRequest - ->expects($this->at(1)) + ->expects($this->once()) ->method('getHeader') ->with('Authorization') ->willReturn('basic dXNlcm5hbWU6cGFzc3dvcmQ='); - $server->httpResponse = $this->getMockBuilder(ResponseInterface::class) - ->disableOriginalConstructor() - ->getMock(); + + $server->httpResponse = $this->createMock(ResponseInterface::class); $this->userSession ->expects($this->once()) ->method('logClientIn') ->with('username', 'password') ->willReturn(true); - $user = $this->getMockBuilder(IUser::class) - ->disableOriginalConstructor() - ->getMock(); - $user->expects($this->exactly(3)) + $user = $this->createMock(IUser::class); + $user->expects($this->exactly(2)) ->method('getUID') ->willReturn('MyTestUser'); $this->userSession - ->expects($this->exactly(4)) + ->expects($this->exactly(3)) ->method('getUser') ->willReturn($user); $response = $this->auth->check($server->httpRequest, $server->httpResponse); $this->assertEquals([true, 'principals/users/MyTestUser'], $response); } - public function testAuthenticateInvalidCredentials() { - $server = $this->getMockBuilder(Server::class) - ->disableOriginalConstructor() - ->getMock(); - $server->httpRequest = $this->getMockBuilder(RequestInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $server->httpRequest - ->expects($this->at(0)) - ->method('getHeader') - ->with('X-Requested-With') - ->willReturn(null); + public function testAuthenticateInvalidCredentials(): void { + $server = $this->createMock(Server::class); + $server->httpRequest = $this->createMock(RequestInterface::class); $server->httpRequest - ->expects($this->at(1)) + ->expects($this->exactly(2)) ->method('getHeader') - ->with('Authorization') - ->willReturn('basic dXNlcm5hbWU6cGFzc3dvcmQ='); - $server->httpResponse = $this->getMockBuilder(ResponseInterface::class) - ->disableOriginalConstructor() - ->getMock(); + ->willReturnMap([ + ['Authorization', 'basic dXNlcm5hbWU6cGFzc3dvcmQ='], + ['X-Requested-With', null], + ]); + $server->httpResponse = $this->createMock(ResponseInterface::class); $this->userSession ->expects($this->once()) ->method('logClientIn') diff --git a/apps/dav/tests/unit/Connector/Sabre/BearerAuthTest.php b/apps/dav/tests/unit/Connector/Sabre/BearerAuthTest.php index 007b5c90295..1e6267d4cbb 100644 --- a/apps/dav/tests/unit/Connector/Sabre/BearerAuthTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/BearerAuthTest.php @@ -1,34 +1,20 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> - * - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @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\unit\Connector\Sabre; +use OC\User\Session; use OCA\DAV\Connector\Sabre\BearerAuth; +use OCP\IConfig; use OCP\IRequest; use OCP\ISession; use OCP\IUser; use OCP\IUserSession; +use PHPUnit\Framework\MockObject\MockObject; use Sabre\HTTP\RequestInterface; use Sabre\HTTP\ResponseInterface; use Test\TestCase; @@ -37,42 +23,41 @@ use Test\TestCase; * @group DB */ class BearerAuthTest extends TestCase { - /** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */ - private $userSession; - /** @var ISession|\PHPUnit\Framework\MockObject\MockObject */ - private $session; - /** @var IRequest|\PHPUnit\Framework\MockObject\MockObject */ - private $request; - /** @var BearerAuth */ - private $bearerAuth; + private IUserSession&MockObject $userSession; + private ISession&MockObject $session; + private IRequest&MockObject $request; + private BearerAuth $bearerAuth; + + private IConfig&MockObject $config; protected function setUp(): void { parent::setUp(); - $this->userSession = $this->createMock(\OC\User\Session::class); + $this->userSession = $this->createMock(Session::class); $this->session = $this->createMock(ISession::class); $this->request = $this->createMock(IRequest::class); + $this->config = $this->createMock(IConfig::class); $this->bearerAuth = new BearerAuth( $this->userSession, $this->session, - $this->request + $this->request, + $this->config, ); } - public function testValidateBearerTokenNotLoggedIn() { + public function testValidateBearerTokenNotLoggedIn(): void { $this->assertFalse($this->bearerAuth->validateBearerToken('Token')); } - public function testValidateBearerToken() { - $this->userSession - ->expects($this->at(0)) - ->method('isLoggedIn') - ->willReturn(false); + public function testValidateBearerToken(): void { $this->userSession - ->expects($this->at(2)) + ->expects($this->exactly(2)) ->method('isLoggedIn') - ->willReturn(true); + ->willReturnOnConsecutiveCalls( + false, + true, + ); $user = $this->createMock(IUser::class); $user ->expects($this->once()) @@ -86,10 +71,10 @@ class BearerAuthTest extends TestCase { $this->assertSame('principals/users/admin', $this->bearerAuth->validateBearerToken('Token')); } - public function testChallenge() { - /** @var \PHPUnit\Framework\MockObject\MockObject|RequestInterface $request */ + public function testChallenge(): void { + /** @var RequestInterface&MockObject $request */ $request = $this->createMock(RequestInterface::class); - /** @var \PHPUnit\Framework\MockObject\MockObject|ResponseInterface $response */ + /** @var ResponseInterface&MockObject $response */ $response = $this->createMock(ResponseInterface::class); $result = $this->bearerAuth->challenge($request, $response); $this->assertEmpty($result); diff --git a/apps/dav/tests/unit/Connector/Sabre/BlockLegacyClientPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/BlockLegacyClientPluginTest.php index 4d2771132cc..366c9475b1b 100644 --- a/apps/dav/tests/unit/Connector/Sabre/BlockLegacyClientPluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/BlockLegacyClientPluginTest.php @@ -1,77 +1,124 @@ <?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 Lukas Reschke <lukas@statuscode.ch> - * @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\Connector\Sabre; use OCA\DAV\Connector\Sabre\BlockLegacyClientPlugin; +use OCA\Theming\ThemingDefaults; use OCP\IConfig; +use PHPUnit\Framework\MockObject\MockObject; +use Sabre\HTTP\RequestInterface; use Test\TestCase; +enum ERROR_TYPE { + case MIN_ERROR; + case MAX_ERROR; + case NONE; +} + /** * Class BlockLegacyClientPluginTest * * @package OCA\DAV\Tests\unit\Connector\Sabre */ class BlockLegacyClientPluginTest extends TestCase { - /** @var IConfig | \PHPUnit\Framework\MockObject\MockObject */ - private $config; - /** @var BlockLegacyClientPlugin */ - private $blockLegacyClientVersionPlugin; + + private IConfig&MockObject $config; + private ThemingDefaults&MockObject $themingDefaults; + private BlockLegacyClientPlugin $blockLegacyClientVersionPlugin; protected function setUp(): void { parent::setUp(); - $this->config = $this->getMockBuilder(IConfig::class) - ->disableOriginalConstructor() - ->getMock(); - $this->blockLegacyClientVersionPlugin = new BlockLegacyClientPlugin($this->config); + $this->config = $this->createMock(IConfig::class); + $this->themingDefaults = $this->createMock(ThemingDefaults::class); + $this->blockLegacyClientVersionPlugin = new BlockLegacyClientPlugin( + $this->config, + $this->themingDefaults, + ); } - /** - * @return array - */ - public function oldDesktopClientProvider() { + public static function oldDesktopClientProvider(): array { return [ - ['Mozilla/5.0 (1.5.0) mirall/1.5.0'], - ['mirall/1.5.0'], - ['mirall/1.5.4'], - ['mirall/1.6.0'], - ['Mozilla/5.0 (Bogus Text) mirall/1.6.9'], + ['Mozilla/5.0 (Windows) mirall/1.5.0', ERROR_TYPE::MIN_ERROR], + ['Mozilla/5.0 (Bogus Text) mirall/1.6.9', ERROR_TYPE::MIN_ERROR], + ['Mozilla/5.0 (Windows) mirall/2.5.0', ERROR_TYPE::MAX_ERROR], + ['Mozilla/5.0 (Bogus Text) mirall/2.0.1', ERROR_TYPE::MAX_ERROR], + ['Mozilla/5.0 (Windows) mirall/2.0.0', ERROR_TYPE::NONE], + ['Mozilla/5.0 (Bogus Text) mirall/2.0.0', ERROR_TYPE::NONE], ]; } + #[\PHPUnit\Framework\Attributes\DataProvider('oldDesktopClientProvider')] + public function testBeforeHandlerException(string $userAgent, ERROR_TYPE $errorType): void { + $this->themingDefaults + ->expects($this->atMost(1)) + ->method('getSyncClientUrl') + ->willReturn('https://nextcloud.com/install/#install-clients'); + + $this->config + ->expects($this->exactly(2)) + ->method('getSystemValueString') + ->willReturnCallback(function (string $key) { + if ($key === 'minimum.supported.desktop.version') { + return '1.7.0'; + } + return '2.0.0'; + }); + + if ($errorType !== ERROR_TYPE::NONE) { + $errorString = $errorType === ERROR_TYPE::MIN_ERROR + ? 'This version of the client is unsupported. Upgrade to <a href="https://nextcloud.com/install/#install-clients">version 1.7.0 or later</a>.' + : 'This version of the client is unsupported. Downgrade to <a href="https://nextcloud.com/install/#install-clients">version 2.0.0 or earlier</a>.'; + $this->expectException(\Sabre\DAV\Exception\Forbidden::class); + $this->expectExceptionMessage($errorString); + } + + /** @var RequestInterface|MockObject $request */ + $request = $this->createMock(RequestInterface::class); + $request + ->expects($this->once()) + ->method('getHeader') + ->with('User-Agent') + ->willReturn($userAgent); + + $this->blockLegacyClientVersionPlugin->beforeHandler($request); + } + /** - * @dataProvider oldDesktopClientProvider - * @param string $userAgent + * Ensure that there is no room for XSS attack through configured URL / version */ - public function testBeforeHandlerException($userAgent) { + #[\PHPUnit\Framework\Attributes\DataProvider('oldDesktopClientProvider')] + public function testBeforeHandlerExceptionPreventXSSAttack(string $userAgent, ERROR_TYPE $errorType): void { $this->expectException(\Sabre\DAV\Exception\Forbidden::class); - $this->expectExceptionMessage('Unsupported client version.'); - /** @var \Sabre\HTTP\RequestInterface | \PHPUnit\Framework\MockObject\MockObject $request */ + $this->themingDefaults + ->expects($this->atMost(1)) + ->method('getSyncClientUrl') + ->willReturn('https://example.com"><script>alter("hacked");</script>'); + + $this->config + ->expects($this->exactly(2)) + ->method('getSystemValueString') + ->willReturnCallback(function (string $key) { + if ($key === 'minimum.supported.desktop.version') { + return '1.7.0 <script>alert("unsafe")</script>'; + } + return '2.0.0 <script>alert("unsafe")</script>'; + }); + + $errorString = $errorType === ERROR_TYPE::MIN_ERROR + ? 'This version of the client is unsupported. Upgrade to <a href="https://example.com"><script>alter("hacked");</script>">version 1.7.0 <script>alert("unsafe")</script> or later</a>.' + : 'This version of the client is unsupported. Downgrade to <a href="https://example.com"><script>alter("hacked");</script>">version 2.0.0 <script>alert("unsafe")</script> or earlier</a>.'; + $this->expectExceptionMessage($errorString); + + /** @var RequestInterface|MockObject $request */ $request = $this->createMock('\Sabre\HTTP\RequestInterface'); $request ->expects($this->once()) @@ -79,35 +126,24 @@ class BlockLegacyClientPluginTest extends TestCase { ->with('User-Agent') ->willReturn($userAgent); - $this->config - ->expects($this->once()) - ->method('getSystemValue') - ->with('minimum.supported.desktop.version', '2.0.0') - ->willReturn('1.7.0'); - $this->blockLegacyClientVersionPlugin->beforeHandler($request); } - /** - * @return array - */ - public function newAndAlternateDesktopClientProvider() { + public static function newAndAlternateDesktopClientProvider(): array { return [ - ['Mozilla/5.0 (1.7.0) mirall/1.7.0'], - ['mirall/1.8.3'], - ['mirall/1.7.2'], - ['mirall/1.7.0'], + ['Mozilla/5.0 (Windows) mirall/1.7.0'], ['Mozilla/5.0 (Bogus Text) mirall/1.9.3'], + ['Mozilla/5.0 (Not Our Client But Old Version) LegacySync/1.1.0'], + ['Mozilla/5.0 (Windows) mirall/4.7.0'], + ['Mozilla/5.0 (Bogus Text) mirall/3.9.3'], + ['Mozilla/5.0 (Not Our Client But Old Version) LegacySync/45.0.0'], ]; } - /** - * @dataProvider newAndAlternateDesktopClientProvider - * @param string $userAgent - */ - public function testBeforeHandlerSuccess($userAgent) { - /** @var \Sabre\HTTP\RequestInterface | \PHPUnit\Framework\MockObject\MockObject $request */ - $request = $this->createMock('\Sabre\HTTP\RequestInterface'); + #[\PHPUnit\Framework\Attributes\DataProvider('newAndAlternateDesktopClientProvider')] + public function testBeforeHandlerSuccess(string $userAgent): void { + /** @var RequestInterface|MockObject $request */ + $request = $this->createMock(RequestInterface::class); $request ->expects($this->once()) ->method('getHeader') @@ -115,22 +151,27 @@ class BlockLegacyClientPluginTest extends TestCase { ->willReturn($userAgent); $this->config - ->expects($this->once()) - ->method('getSystemValue') - ->with('minimum.supported.desktop.version', '2.0.0') - ->willReturn('1.7.0'); + ->expects($this->exactly(2)) + ->method('getSystemValueString') + ->willReturnCallback(function (string $key) { + if ($key === 'minimum.supported.desktop.version') { + return '1.7.0'; + } + return '10.0.0'; + }); $this->blockLegacyClientVersionPlugin->beforeHandler($request); } - public function testBeforeHandlerNoUserAgent() { - /** @var \Sabre\HTTP\RequestInterface | \PHPUnit\Framework\MockObject\MockObject $request */ - $request = $this->createMock('\Sabre\HTTP\RequestInterface'); + public function testBeforeHandlerNoUserAgent(): void { + /** @var RequestInterface|MockObject $request */ + $request = $this->createMock(RequestInterface::class); $request ->expects($this->once()) ->method('getHeader') ->with('User-Agent') ->willReturn(null); + $this->blockLegacyClientVersionPlugin->beforeHandler($request); } } diff --git a/apps/dav/tests/unit/Connector/Sabre/CommentsPropertiesPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/CommentsPropertiesPluginTest.php index ea49cef5d0f..a934d6401c2 100644 --- a/apps/dav/tests/unit/Connector/Sabre/CommentsPropertiesPluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/CommentsPropertiesPluginTest.php @@ -1,87 +1,51 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @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\Connector\Sabre; use OCA\DAV\Connector\Sabre\CommentPropertiesPlugin as CommentPropertiesPluginImplementation; +use OCA\DAV\Connector\Sabre\Directory; use OCA\DAV\Connector\Sabre\File; use OCP\Comments\ICommentsManager; use OCP\IUser; use OCP\IUserSession; +use PHPUnit\Framework\MockObject\MockObject; use Sabre\DAV\PropFind; +use Sabre\DAV\Server; class CommentsPropertiesPluginTest extends \Test\TestCase { - - /** @var CommentPropertiesPluginImplementation */ - protected $plugin; - protected $commentsManager; - protected $userSession; - protected $server; + protected CommentPropertiesPluginImplementation $plugin; + protected ICommentsManager&MockObject $commentsManager; + protected IUserSession&MockObject $userSession; + protected Server&MockObject $server; protected function setUp(): void { parent::setUp(); - $this->commentsManager = $this->getMockBuilder(ICommentsManager::class) - ->disableOriginalConstructor() - ->getMock(); - $this->userSession = $this->getMockBuilder(IUserSession::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->server = $this->getMockBuilder('\Sabre\DAV\Server') - ->disableOriginalConstructor() - ->getMock(); + $this->commentsManager = $this->createMock(ICommentsManager::class); + $this->userSession = $this->createMock(IUserSession::class); + $this->server = $this->createMock(Server::class); $this->plugin = new CommentPropertiesPluginImplementation($this->commentsManager, $this->userSession); $this->plugin->initialize($this->server); } - public function nodeProvider() { - $mocks = []; - foreach (['\OCA\DAV\Connector\Sabre\File', '\OCA\DAV\Connector\Sabre\Directory', '\Sabre\DAV\INode'] as $class) { - $mocks[] = $this->getMockBuilder($class) - ->disableOriginalConstructor() - ->getMock(); - } - + public static function nodeProvider(): array { return [ - [$mocks[0], true], - [$mocks[1], true], - [$mocks[2], false] + [File::class, true], + [Directory::class, true], + [\Sabre\DAV\INode::class, false] ]; } - /** - * @dataProvider nodeProvider - * @param $node - * @param $expectedSuccessful - */ - public function testHandleGetProperties($node, $expectedSuccessful) { - $propFind = $this->getMockBuilder(PropFind::class) - ->disableOriginalConstructor() - ->getMock(); + #[\PHPUnit\Framework\Attributes\DataProvider('nodeProvider')] + public function testHandleGetProperties(string $class, bool $expectedSuccessful): void { + $propFind = $this->createMock(PropFind::class); if ($expectedSuccessful) { $propFind->expects($this->exactly(3)) @@ -91,10 +55,11 @@ class CommentsPropertiesPluginTest extends \Test\TestCase { ->method('handle'); } + $node = $this->createMock($class); $this->plugin->handleGetProperties($propFind, $node); } - public function baseUriProvider() { + public static function baseUriProvider(): array { return [ ['owncloud/remote.php/webdav/', '4567', 'owncloud/remote.php/dav/comments/files/4567'], ['owncloud/remote.php/files/', '4567', 'owncloud/remote.php/dav/comments/files/4567'], @@ -102,16 +67,9 @@ class CommentsPropertiesPluginTest extends \Test\TestCase { ]; } - /** - * @dataProvider baseUriProvider - * @param $baseUri - * @param $fid - * @param $expectedHref - */ - public function testGetCommentsLink($baseUri, $fid, $expectedHref) { - $node = $this->getMockBuilder(File::class) - ->disableOriginalConstructor() - ->getMock(); + #[\PHPUnit\Framework\Attributes\DataProvider('baseUriProvider')] + public function testGetCommentsLink(string $baseUri, string $fid, ?string $expectedHref): void { + $node = $this->createMock(File::class); $node->expects($this->any()) ->method('getId') ->willReturn($fid); @@ -124,29 +82,23 @@ class CommentsPropertiesPluginTest extends \Test\TestCase { $this->assertSame($expectedHref, $href); } - public function userProvider() { + public static function userProvider(): array { return [ - [ - $this->getMockBuilder(IUser::class) - ->disableOriginalConstructor() - ->getMock() - ], + [IUser::class], [null] ]; } - /** - * @dataProvider userProvider - * @param $user - */ - public function testGetUnreadCount($user) { - $node = $this->getMockBuilder(File::class) - ->disableOriginalConstructor() - ->getMock(); + #[\PHPUnit\Framework\Attributes\DataProvider('userProvider')] + public function testGetUnreadCount(?string $user): void { + $node = $this->createMock(File::class); $node->expects($this->any()) ->method('getId') ->willReturn('4567'); + if ($user !== null) { + $user = $this->createMock($user); + } $this->userSession->expects($this->once()) ->method('getUser') ->willReturn($user); diff --git a/apps/dav/tests/unit/Connector/Sabre/CopyEtagHeaderPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/CopyEtagHeaderPluginTest.php index 858e5c8199b..7067cf335ed 100644 --- a/apps/dav/tests/unit/Connector/Sabre/CopyEtagHeaderPluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/CopyEtagHeaderPluginTest.php @@ -1,31 +1,10 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * Copyright (c) 2015 Vincent Petry <pvince81@owncloud.com> - * Copyright (c) 2015 Vincent Petry <pvince81@owncloud.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 Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @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\Connector\Sabre; @@ -36,19 +15,9 @@ use Sabre\DAV\Server; use Sabre\DAV\Tree; use Test\TestCase; -/** - * Copyright (c) 2015 Vincent Petry <pvince81@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ class CopyEtagHeaderPluginTest extends TestCase { - - /** @var CopyEtagHeaderPlugin */ - private $plugin; - - /** @var Server */ - private $server; + private CopyEtagHeaderPlugin $plugin; + private Server $server; protected function setUp(): void { parent::setUp(); @@ -57,7 +26,7 @@ class CopyEtagHeaderPluginTest extends TestCase { $this->plugin->initialize($this->server); } - public function testCopyEtag() { + public function testCopyEtag(): void { $request = new \Sabre\Http\Request('GET', 'dummy.file'); $response = new \Sabre\Http\Response(); $response->setHeader('Etag', 'abcd'); @@ -67,7 +36,7 @@ class CopyEtagHeaderPluginTest extends TestCase { $this->assertEquals('abcd', $response->getHeader('OC-Etag')); } - public function testNoopWhenEmpty() { + public function testNoopWhenEmpty(): void { $request = new \Sabre\Http\Request('GET', 'dummy.file'); $response = new \Sabre\Http\Response(); @@ -89,16 +58,12 @@ class CopyEtagHeaderPluginTest extends TestCase { // Nothing to assert, we are just testing if the exception is handled } - public function testAfterMove() { - $node = $this->getMockBuilder(File::class) - ->disableOriginalConstructor() - ->getMock(); + public function testAfterMove(): void { + $node = $this->createMock(File::class); $node->expects($this->once()) ->method('getETag') ->willReturn('123456'); - $tree = $this->getMockBuilder(Tree::class) - ->disableOriginalConstructor() - ->getMock(); + $tree = $this->createMock(Tree::class); $tree->expects($this->once()) ->method('getNodeForPath') ->with('test.txt') diff --git a/apps/dav/tests/unit/Connector/Sabre/CustomPropertiesBackendTest.php b/apps/dav/tests/unit/Connector/Sabre/CustomPropertiesBackendTest.php index 48658f3ffa3..d4021a66299 100644 --- a/apps/dav/tests/unit/Connector/Sabre/CustomPropertiesBackendTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/CustomPropertiesBackendTest.php @@ -1,44 +1,21 @@ <?php -/** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * Copyright (c) 2015 Vincent Petry <pvince81@owncloud.com> - * Copyright (c) 2015 Vincent Petry <pvince81@owncloud.com> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @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\Connector\Sabre; +declare(strict_types=1); /** - * Copyright (c) 2015 Vincent Petry <pvince81@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * 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\Connector\Sabre; +use OCA\DAV\CalDAV\DefaultCalendarValidator; use OCA\DAV\Connector\Sabre\Directory; use OCA\DAV\Connector\Sabre\File; +use OCA\DAV\DAV\CustomPropertiesBackend; +use OCP\IDBConnection; use OCP\IUser; +use OCP\Server; +use PHPUnit\Framework\MockObject\MockObject; use Sabre\DAV\Tree; /** @@ -49,55 +26,41 @@ use Sabre\DAV\Tree; * @package OCA\DAV\Tests\unit\Connector\Sabre */ class CustomPropertiesBackendTest extends \Test\TestCase { - - /** - * @var \Sabre\DAV\Server - */ - private $server; - - /** - * @var \Sabre\DAV\Tree - */ - private $tree; - - /** - * @var \OCA\DAV\DAV\CustomPropertiesBackend - */ - private $plugin; - - /** - * @var \OCP\IUser - */ - private $user; + private \Sabre\DAV\Server $server; + private \Sabre\DAV\Tree&MockObject $tree; + private IUser&MockObject $user; + private DefaultCalendarValidator&MockObject $defaultCalendarValidator; + private CustomPropertiesBackend $plugin; protected function setUp(): void { parent::setUp(); + $this->server = new \Sabre\DAV\Server(); - $this->tree = $this->getMockBuilder(Tree::class) - ->disableOriginalConstructor() - ->getMock(); + $this->tree = $this->createMock(Tree::class); - $userId = $this->getUniqueID('testcustompropertiesuser'); + $userId = self::getUniqueID('testcustompropertiesuser'); - $this->user = $this->getMockBuilder(IUser::class) - ->disableOriginalConstructor() - ->getMock(); + $this->user = $this->createMock(IUser::class); $this->user->expects($this->any()) ->method('getUID') ->willReturn($userId); - $this->plugin = new \OCA\DAV\DAV\CustomPropertiesBackend( + $this->defaultCalendarValidator = $this->createMock(DefaultCalendarValidator::class); + + $this->plugin = new CustomPropertiesBackend( + $this->server, $this->tree, - \OC::$server->getDatabaseConnection(), - $this->user + Server::get(IDBConnection::class), + $this->user, + $this->defaultCalendarValidator, ); } protected function tearDown(): void { - $connection = \OC::$server->getDatabaseConnection(); + $connection = Server::get(IDBConnection::class); $deleteStatement = $connection->prepare( - 'DELETE FROM `*PREFIX*properties`' . - ' WHERE `userid` = ?' + 'DELETE FROM `*PREFIX*properties`' + . ' WHERE `userid` = ?' ); $deleteStatement->execute( [ @@ -105,12 +68,12 @@ class CustomPropertiesBackendTest extends \Test\TestCase { ] ); $deleteStatement->closeCursor(); + + parent::tearDown(); } - private function createTestNode($class) { - $node = $this->getMockBuilder($class) - ->disableOriginalConstructor() - ->getMock(); + private function createTestNode(string $class) { + $node = $this->createMock($class); $node->expects($this->any()) ->method('getId') ->willReturn(123); @@ -122,7 +85,7 @@ class CustomPropertiesBackendTest extends \Test\TestCase { return $node; } - private function applyDefaultProps($path = '/dummypath') { + private function applyDefaultProps($path = '/dummypath'): void { // properties to set $propPatch = new \Sabre\DAV\PropPatch([ 'customprop' => 'value1', @@ -146,7 +109,7 @@ class CustomPropertiesBackendTest extends \Test\TestCase { /** * Test that propFind on a missing file soft fails */ - public function testPropFindMissingFileSoftFail() { + public function testPropFindMissingFileSoftFail(): void { $propFind = new \Sabre\DAV\PropFind( '/dummypath', [ @@ -174,7 +137,7 @@ class CustomPropertiesBackendTest extends \Test\TestCase { /** * Test setting/getting properties */ - public function testSetGetPropertiesForFile() { + public function testSetGetPropertiesForFile(): void { $this->applyDefaultProps(); $propFind = new \Sabre\DAV\PropFind( @@ -200,7 +163,7 @@ class CustomPropertiesBackendTest extends \Test\TestCase { /** * Test getting properties from directory */ - public function testGetPropertiesForDirectory() { + public function testGetPropertiesForDirectory(): void { $this->applyDefaultProps('/dummypath'); $this->applyDefaultProps('/dummypath/test.txt'); @@ -247,7 +210,7 @@ class CustomPropertiesBackendTest extends \Test\TestCase { /** * Test delete property */ - public function testDeleteProperty() { + public function testDeleteProperty(): void { $this->applyDefaultProps(); $propPatch = new \Sabre\DAV\PropPatch([ diff --git a/apps/dav/tests/unit/Connector/Sabre/DirectoryTest.php b/apps/dav/tests/unit/Connector/Sabre/DirectoryTest.php index e8297c2ac66..421ee1bdc12 100644 --- a/apps/dav/tests/unit/Connector/Sabre/DirectoryTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/DirectoryTest.php @@ -1,49 +1,36 @@ <?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 Julius Härtl <jus@bitgrid.net> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @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\Connector\Sabre; +namespace OCA\DAV\Tests\unit\Connector\Sabre; use OC\Files\FileInfo; +use OC\Files\Filesystem; use OC\Files\Node\Node; use OC\Files\Storage\Wrapper\Quota; +use OC\Files\View; use OCA\DAV\Connector\Sabre\Directory; +use OCA\DAV\Connector\Sabre\Exception\Forbidden; +use OCA\DAV\Connector\Sabre\Exception\InvalidPath; +use OCA\Files_Sharing\External\Storage; +use OCP\Constants; use OCP\Files\ForbiddenException; +use OCP\Files\InvalidPathException; use OCP\Files\Mount\IMountPoint; - -class TestViewDirectory extends \OC\Files\View { - private $updatables; - private $deletables; - private $canRename; - - public function __construct($updatables, $deletables, $canRename = true) { - $this->updatables = $updatables; - $this->deletables = $deletables; - $this->canRename = $canRename; +use OCP\Files\StorageNotAvailableException; +use PHPUnit\Framework\MockObject\MockObject; +use Test\Traits\UserTrait; + +class TestViewDirectory extends View { + public function __construct( + private $updatables, + private $deletables, + private $canRename = true, + ) { } public function isUpdatable($path) { @@ -58,11 +45,11 @@ class TestViewDirectory extends \OC\Files\View { return $this->deletables[$path]; } - public function rename($path1, $path2) { + public function rename($source, $target, array $options = []) { return $this->canRename; } - public function getRelativePath($path) { + public function getRelativePath($path): ?string { return $path; } } @@ -72,26 +59,29 @@ class TestViewDirectory extends \OC\Files\View { * @group DB */ class DirectoryTest extends \Test\TestCase { + use UserTrait; - /** @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject */ - private $view; - /** @var \OC\Files\FileInfo | \PHPUnit\Framework\MockObject\MockObject */ - private $info; + private View&MockObject $view; + private FileInfo&MockObject $info; protected function setUp(): void { parent::setUp(); - $this->view = $this->createMock('OC\Files\View'); - $this->info = $this->createMock('OC\Files\FileInfo'); + $this->view = $this->createMock(View::class); + $this->info = $this->createMock(FileInfo::class); $this->info->method('isReadable') ->willReturn(true); $this->info->method('getType') ->willReturn(Node::TYPE_FOLDER); $this->info->method('getName') - ->willReturn("folder"); + ->willReturn('folder'); + $this->info->method('getPath') + ->willReturn('/admin/files/folder'); + $this->info->method('getPermissions') + ->willReturn(Constants::PERMISSION_READ); } - private function getDir($path = '/') { + private function getDir(string $path = '/'): Directory { $this->view->expects($this->once()) ->method('getRelativePath') ->willReturn($path); @@ -104,7 +94,7 @@ class DirectoryTest extends \Test\TestCase { } - public function testDeleteRootFolderFails() { + public function testDeleteRootFolderFails(): void { $this->expectException(\Sabre\DAV\Exception\Forbidden::class); $this->info->expects($this->any()) @@ -117,8 +107,8 @@ class DirectoryTest extends \Test\TestCase { } - public function testDeleteForbidden() { - $this->expectException(\OCA\DAV\Connector\Sabre\Exception\Forbidden::class); + public function testDeleteForbidden(): void { + $this->expectException(Forbidden::class); // deletion allowed $this->info->expects($this->once()) @@ -136,7 +126,7 @@ class DirectoryTest extends \Test\TestCase { } - public function testDeleteFolderWhenAllowed() { + public function testDeleteFolderWhenAllowed(): void { // deletion allowed $this->info->expects($this->once()) ->method('isDeletable') @@ -153,7 +143,7 @@ class DirectoryTest extends \Test\TestCase { } - public function testDeleteFolderFailsWhenNotAllowed() { + public function testDeleteFolderFailsWhenNotAllowed(): void { $this->expectException(\Sabre\DAV\Exception\Forbidden::class); $this->info->expects($this->once()) @@ -165,7 +155,7 @@ class DirectoryTest extends \Test\TestCase { } - public function testDeleteFolderThrowsWhenDeletionFailed() { + public function testDeleteFolderThrowsWhenDeletionFailed(): void { $this->expectException(\Sabre\DAV\Exception\Forbidden::class); // deletion allowed @@ -183,13 +173,9 @@ class DirectoryTest extends \Test\TestCase { $dir->delete(); } - public function testGetChildren() { - $info1 = $this->getMockBuilder(FileInfo::class) - ->disableOriginalConstructor() - ->getMock(); - $info2 = $this->getMockBuilder(FileInfo::class) - ->disableOriginalConstructor() - ->getMock(); + public function testGetChildren(): void { + $info1 = $this->createMock(FileInfo::class); + $info2 = $this->createMock(FileInfo::class); $info1->method('getName') ->willReturn('first'); $info1->method('getPath') @@ -205,17 +191,26 @@ class DirectoryTest extends \Test\TestCase { $this->view->expects($this->once()) ->method('getDirectoryContent') - ->with('') ->willReturn([$info1, $info2]); $this->view->expects($this->any()) ->method('getRelativePath') - ->willReturn(''); + ->willReturnCallback(function ($path) { + return str_replace('/admin/files/', '', $path); + }); + + $this->view->expects($this->any()) + ->method('getAbsolutePath') + ->willReturnCallback(function ($path) { + return Filesystem::normalizePath('/admin/files' . $path); + }); + + $this->overwriteService(View::class, $this->view); $dir = new Directory($this->view, $this->info); $nodes = $dir->getChildren(); - $this->assertEquals(2, count($nodes)); + $this->assertCount(2, $nodes); // calling a second time just returns the cached values, // does not call getDirectoryContents again @@ -223,7 +218,7 @@ class DirectoryTest extends \Test\TestCase { } - public function testGetChildrenNoPermission() { + public function testGetChildrenNoPermission(): void { $this->expectException(\Sabre\DAV\Exception\Forbidden::class); $info = $this->createMock(FileInfo::class); @@ -236,7 +231,7 @@ class DirectoryTest extends \Test\TestCase { } - public function testGetChildNoPermission() { + public function testGetChildNoPermission(): void { $this->expectException(\Sabre\DAV\Exception\NotFound::class); $this->info->expects($this->any()) @@ -248,24 +243,24 @@ class DirectoryTest extends \Test\TestCase { } - public function testGetChildThrowStorageNotAvailableException() { + public function testGetChildThrowStorageNotAvailableException(): void { $this->expectException(\Sabre\DAV\Exception\ServiceUnavailable::class); $this->view->expects($this->once()) ->method('getFileInfo') - ->willThrowException(new \OCP\Files\StorageNotAvailableException()); + ->willThrowException(new StorageNotAvailableException()); $dir = new Directory($this->view, $this->info); $dir->getChild('.'); } - public function testGetChildThrowInvalidPath() { - $this->expectException(\OCA\DAV\Connector\Sabre\Exception\InvalidPath::class); + public function testGetChildThrowInvalidPath(): void { + $this->expectException(InvalidPath::class); $this->view->expects($this->once()) ->method('verifyPath') - ->willThrowException(new \OCP\Files\InvalidPathException()); + ->willThrowException(new InvalidPathException()); $this->view->expects($this->never()) ->method('getFileInfo'); @@ -273,21 +268,26 @@ class DirectoryTest extends \Test\TestCase { $dir->getChild('.'); } - public function testGetQuotaInfoUnlimited() { + public function testGetQuotaInfoUnlimited(): void { + $this->createUser('user', 'password'); + self::loginAsUser('user'); $mountPoint = $this->createMock(IMountPoint::class); - $storage = $this->getMockBuilder(Quota::class) - ->disableOriginalConstructor() - ->getMock(); + $storage = $this->createMock(Quota::class); $mountPoint->method('getStorage') ->willReturn($storage); $storage->expects($this->any()) ->method('instanceOfStorage') ->willReturnMap([ - '\OCA\Files_Sharing\SharedStorage' => false, - '\OC\Files\Storage\Wrapper\Quota' => false, + ['\OCA\Files_Sharing\SharedStorage', false], + ['\OC\Files\Storage\Wrapper\Quota', false], + [Storage::class, false], ]); + $storage->expects($this->once()) + ->method('getOwner') + ->willReturn('user'); + $storage->expects($this->never()) ->method('getQuota'); @@ -295,6 +295,10 @@ class DirectoryTest extends \Test\TestCase { ->method('free_space') ->willReturn(800); + $this->info->expects($this->any()) + ->method('getPath') + ->willReturn('/admin/files/foo'); + $this->info->expects($this->once()) ->method('getSize') ->willReturn(200); @@ -303,6 +307,14 @@ class DirectoryTest extends \Test\TestCase { ->method('getMountPoint') ->willReturn($mountPoint); + $this->view->expects($this->any()) + ->method('getRelativePath') + ->willReturn('/foo'); + + $this->info->expects($this->once()) + ->method('getInternalPath') + ->willReturn('/foo'); + $mountPoint->method('getMountPoint') ->willReturn('/user/files/mymountpoint'); @@ -310,11 +322,11 @@ class DirectoryTest extends \Test\TestCase { $this->assertEquals([200, -3], $dir->getQuotaInfo()); //200 used, unlimited } - public function testGetQuotaInfoSpecific() { + public function testGetQuotaInfoSpecific(): void { + $this->createUser('user', 'password'); + self::loginAsUser('user'); $mountPoint = $this->createMock(IMountPoint::class); - $storage = $this->getMockBuilder(Quota::class) - ->disableOriginalConstructor() - ->getMock(); + $storage = $this->createMock(Quota::class); $mountPoint->method('getStorage') ->willReturn($storage); @@ -323,9 +335,14 @@ class DirectoryTest extends \Test\TestCase { ->willReturnMap([ ['\OCA\Files_Sharing\SharedStorage', false], ['\OC\Files\Storage\Wrapper\Quota', true], + [Storage::class, false], ]); $storage->expects($this->once()) + ->method('getOwner') + ->willReturn('user'); + + $storage->expects($this->once()) ->method('getQuota') ->willReturn(1000); @@ -341,46 +358,48 @@ class DirectoryTest extends \Test\TestCase { ->method('getMountPoint') ->willReturn($mountPoint); + $this->info->expects($this->once()) + ->method('getInternalPath') + ->willReturn('/foo'); + $mountPoint->method('getMountPoint') ->willReturn('/user/files/mymountpoint'); + $this->view->expects($this->any()) + ->method('getRelativePath') + ->willReturn('/foo'); + $dir = new Directory($this->view, $this->info); $this->assertEquals([200, 800], $dir->getQuotaInfo()); //200 used, 800 free } - /** - * @dataProvider moveFailedProvider - */ - public function testMoveFailed($source, $destination, $updatables, $deletables) { + #[\PHPUnit\Framework\Attributes\DataProvider('moveFailedProvider')] + public function testMoveFailed(string $source, string $destination, array $updatables, array $deletables): void { $this->expectException(\Sabre\DAV\Exception\Forbidden::class); $this->moveTest($source, $destination, $updatables, $deletables); } - /** - * @dataProvider moveSuccessProvider - */ - public function testMoveSuccess($source, $destination, $updatables, $deletables) { + #[\PHPUnit\Framework\Attributes\DataProvider('moveSuccessProvider')] + public function testMoveSuccess(string $source, string $destination, array $updatables, array $deletables): void { $this->moveTest($source, $destination, $updatables, $deletables); $this->addToAssertionCount(1); } - /** - * @dataProvider moveFailedInvalidCharsProvider - */ - public function testMoveFailedInvalidChars($source, $destination, $updatables, $deletables) { - $this->expectException(\OCA\DAV\Connector\Sabre\Exception\InvalidPath::class); + #[\PHPUnit\Framework\Attributes\DataProvider('moveFailedInvalidCharsProvider')] + public function testMoveFailedInvalidChars(string $source, string $destination, array $updatables, array $deletables): void { + $this->expectException(InvalidPath::class); $this->moveTest($source, $destination, $updatables, $deletables); } - public function moveFailedInvalidCharsProvider() { + public static function moveFailedInvalidCharsProvider(): array { return [ - ['a/b', 'a/*', ['a' => true, 'a/b' => true, 'a/c*' => false], []], + ['a/valid', "a/i\nvalid", ['a' => true, 'a/valid' => true, 'a/c*' => false], []], ]; } - public function moveFailedProvider() { + public static function moveFailedProvider(): array { return [ ['a/b', 'a/c', ['a' => false, 'a/b' => false, 'a/c' => false], []], ['a/b', 'b/b', ['a' => false, 'a/b' => false, 'b' => false, 'b/b' => false], []], @@ -391,7 +410,7 @@ class DirectoryTest extends \Test\TestCase { ]; } - public function moveSuccessProvider() { + public static function moveSuccessProvider(): array { return [ ['a/b', 'b/b', ['a' => true, 'a/b' => true, 'b' => true, 'b/b' => false], ['a/b' => true]], // older files with special chars can still be renamed to valid names @@ -399,12 +418,7 @@ class DirectoryTest extends \Test\TestCase { ]; } - /** - * @param $source - * @param $destination - * @param $updatables - */ - private function moveTest($source, $destination, $updatables, $deletables) { + private function moveTest(string $source, string $destination, array $updatables, array $deletables): void { $view = new TestViewDirectory($updatables, $deletables); $sourceInfo = new FileInfo($source, null, null, [ @@ -416,7 +430,7 @@ class DirectoryTest extends \Test\TestCase { $sourceNode = new Directory($view, $sourceInfo); $targetNode = $this->getMockBuilder(Directory::class) - ->setMethods(['childExists']) + ->onlyMethods(['childExists']) ->setConstructorArgs([$view, $targetInfo]) ->getMock(); $targetNode->expects($this->any())->method('childExists') @@ -426,7 +440,7 @@ class DirectoryTest extends \Test\TestCase { } - public function testFailingMove() { + public function testFailingMove(): void { $this->expectException(\Sabre\DAV\Exception\Forbidden::class); $this->expectExceptionMessage('Could not copy directory b, target exists'); @@ -442,7 +456,7 @@ class DirectoryTest extends \Test\TestCase { $sourceNode = new Directory($view, $sourceInfo); $targetNode = $this->getMockBuilder(Directory::class) - ->setMethods(['childExists']) + ->onlyMethods(['childExists']) ->setConstructorArgs([$view, $targetInfo]) ->getMock(); $targetNode->expects($this->once())->method('childExists') diff --git a/apps/dav/tests/unit/Connector/Sabre/DummyGetResponsePluginTest.php b/apps/dav/tests/unit/Connector/Sabre/DummyGetResponsePluginTest.php index c4b7ed15f2b..2d688d64600 100644 --- a/apps/dav/tests/unit/Connector/Sabre/DummyGetResponsePluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/DummyGetResponsePluginTest.php @@ -1,27 +1,10 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @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\Connector\Sabre; @@ -37,8 +20,7 @@ use Test\TestCase; * @package OCA\DAV\Tests\unit\Connector\Sabre */ class DummyGetResponsePluginTest extends TestCase { - /** @var DummyGetResponsePlugin */ - private $dummyGetResponsePlugin; + private DummyGetResponsePlugin $dummyGetResponsePlugin; protected function setUp(): void { parent::setUp(); @@ -46,11 +28,8 @@ class DummyGetResponsePluginTest extends TestCase { $this->dummyGetResponsePlugin = new DummyGetResponsePlugin(); } - public function testInitialize() { - /** @var Server $server */ - $server = $this->getMockBuilder(Server::class) - ->disableOriginalConstructor() - ->getMock(); + public function testInitialize(): void { + $server = $this->createMock(Server::class); $server ->expects($this->once()) ->method('on') @@ -60,15 +39,11 @@ class DummyGetResponsePluginTest extends TestCase { } - public function testHttpGet() { + public function testHttpGet(): void { /** @var \Sabre\HTTP\RequestInterface $request */ - $request = $this->getMockBuilder(RequestInterface::class) - ->disableOriginalConstructor() - ->getMock(); + $request = $this->createMock(RequestInterface::class); /** @var \Sabre\HTTP\ResponseInterface $response */ - $response = $server = $this->getMockBuilder(ResponseInterface::class) - ->disableOriginalConstructor() - ->getMock(); + $response = $this->createMock(ResponseInterface::class); $response ->expects($this->once()) ->method('setBody'); diff --git a/apps/dav/tests/unit/Connector/Sabre/Exception/ForbiddenTest.php b/apps/dav/tests/unit/Connector/Sabre/Exception/ForbiddenTest.php index 4d316bf870a..2f9e0ae9196 100644 --- a/apps/dav/tests/unit/Connector/Sabre/Exception/ForbiddenTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/Exception/ForbiddenTest.php @@ -1,41 +1,28 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @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\Connector\Sabre\Exception; use OCA\DAV\Connector\Sabre\Exception\Forbidden; +use Sabre\DAV\Server; class ForbiddenTest extends \Test\TestCase { - public function testSerialization() { + public function testSerialization(): void { // create xml doc - $DOM = new \DOMDocument('1.0','utf-8'); + $DOM = new \DOMDocument('1.0', 'utf-8'); $DOM->formatOutput = true; - $error = $DOM->createElementNS('DAV:','d:error'); + $error = $DOM->createElementNS('DAV:', 'd:error'); $error->setAttribute('xmlns:s', \Sabre\DAV\Server::NS_SABREDAV); $DOM->appendChild($error); // serialize the exception - $message = "1234567890"; + $message = '1234567890'; $retry = false; $expectedXml = <<<EOD <?xml version="1.0" encoding="utf-8"?> @@ -47,9 +34,7 @@ class ForbiddenTest extends \Test\TestCase { EOD; $ex = new Forbidden($message, $retry); - $server = $this->getMockBuilder('Sabre\DAV\Server') - ->disableOriginalConstructor() - ->getMock(); + $server = $this->createMock(Server::class); $ex->serialize($server, $error); // assert diff --git a/apps/dav/tests/unit/Connector/Sabre/Exception/InvalidPathTest.php b/apps/dav/tests/unit/Connector/Sabre/Exception/InvalidPathTest.php index 3c68d780ff3..6f62bef86a3 100644 --- a/apps/dav/tests/unit/Connector/Sabre/Exception/InvalidPathTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/Exception/InvalidPathTest.php @@ -1,42 +1,28 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @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\Connector\Sabre\Exception; use OCA\DAV\Connector\Sabre\Exception\InvalidPath; +use Sabre\DAV\Server; class InvalidPathTest extends \Test\TestCase { - public function testSerialization() { + public function testSerialization(): void { // create xml doc - $DOM = new \DOMDocument('1.0','utf-8'); + $DOM = new \DOMDocument('1.0', 'utf-8'); $DOM->formatOutput = true; - $error = $DOM->createElementNS('DAV:','d:error'); + $error = $DOM->createElementNS('DAV:', 'd:error'); $error->setAttribute('xmlns:s', \Sabre\DAV\Server::NS_SABREDAV); $DOM->appendChild($error); // serialize the exception - $message = "1234567890"; + $message = '1234567890'; $retry = false; $expectedXml = <<<EOD <?xml version="1.0" encoding="utf-8"?> @@ -48,9 +34,7 @@ class InvalidPathTest extends \Test\TestCase { EOD; $ex = new InvalidPath($message, $retry); - $server = $this->getMockBuilder('Sabre\DAV\Server') - ->disableOriginalConstructor() - ->getMock(); + $server = $this->createMock(Server::class); $ex->serialize($server, $error); // assert diff --git a/apps/dav/tests/unit/Connector/Sabre/ExceptionLoggerPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/ExceptionLoggerPluginTest.php index 83f8c416577..416ac8a75c9 100644 --- a/apps/dav/tests/unit/Connector/Sabre/ExceptionLoggerPluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/ExceptionLoggerPluginTest.php @@ -1,63 +1,29 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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> - * @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\Connector\Sabre; -use OC\Log; use OC\SystemConfig; use OCA\DAV\Connector\Sabre\Exception\InvalidPath; -use OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin as PluginToTest; +use OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin; +use OCA\DAV\Exception\ServerMaintenanceMode; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; use Sabre\DAV\Exception\NotFound; -use Sabre\DAV\Exception\ServiceUnavailable; use Sabre\DAV\Server; use Test\TestCase; -class TestLogger extends Log { - public $message; - public $level; - - public function writeLog(string $app, $entry, int $level) { - $this->level = $level; - $this->message = $entry; - } -} - class ExceptionLoggerPluginTest extends TestCase { + private Server $server; + private ExceptionLoggerPlugin $plugin; + private LoggerInterface&MockObject $logger; - /** @var Server */ - private $server; - - /** @var PluginToTest */ - private $plugin; - - /** @var TestLogger | \PHPUnit\Framework\MockObject\MockObject */ - private $logger; - - private function init() { + private function init(): void { $config = $this->createMock(SystemConfig::class); $config->expects($this->any()) ->method('getValue') @@ -71,29 +37,30 @@ class ExceptionLoggerPluginTest extends TestCase { }); $this->server = new Server(); - $this->logger = new TestLogger(new Log\File(\OC::$SERVERROOT.'/data/nextcloud.log', '', $config), $config); - $this->plugin = new PluginToTest('unit-test', $this->logger); + $this->logger = $this->createMock(LoggerInterface::class); + $this->plugin = new ExceptionLoggerPlugin('unit-test', $this->logger); $this->plugin->initialize($this->server); } - /** - * @dataProvider providesExceptions - */ - public function testLogging($expectedLogLevel, $expectedMessage, $exception) { + #[\PHPUnit\Framework\Attributes\DataProvider('providesExceptions')] + public function testLogging(string $expectedLogLevel, \Throwable $e): void { $this->init(); - $this->plugin->logException($exception); - $this->assertEquals($expectedLogLevel, $this->logger->level); - $this->assertEquals(get_class($exception), $this->logger->message['Exception']); - $this->assertEquals($expectedMessage, $this->logger->message['Message']); + $this->logger->expects($this->once()) + ->method($expectedLogLevel) + ->with($e->getMessage(), ['app' => 'unit-test','exception' => $e]); + + $this->plugin->logException($e); } - public function providesExceptions() { + public static function providesExceptions(): array { return [ - [0, '', new NotFound()], - [0, 'System in maintenance mode.', new ServiceUnavailable('System in maintenance mode.')], - [4, 'Upgrade needed', new ServiceUnavailable('Upgrade needed')], - [4, 'This path leads to nowhere', new InvalidPath('This path leads to nowhere')] + ['debug', new NotFound()], + ['debug', new ServerMaintenanceMode('System is in maintenance mode.')], + // Faking a translation + ['debug', new ServerMaintenanceMode('Syst3m 1s 1n m41nt3n4nc3 m0d3.')], + ['debug', new ServerMaintenanceMode('Upgrade needed')], + ['critical', new InvalidPath('This path leads to nowhere')] ]; } } diff --git a/apps/dav/tests/unit/Connector/Sabre/FakeLockerPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/FakeLockerPluginTest.php index 578576e3f07..366932137f4 100644 --- a/apps/dav/tests/unit/Connector/Sabre/FakeLockerPluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/FakeLockerPluginTest.php @@ -1,29 +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 Lukas Reschke <lukas@statuscode.ch> - * @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\Connector\Sabre; @@ -42,40 +23,33 @@ use Test\TestCase; * @package OCA\DAV\Tests\unit\Connector\Sabre */ class FakeLockerPluginTest extends TestCase { - /** @var FakeLockerPlugin */ - private $fakeLockerPlugin; + private FakeLockerPlugin $fakeLockerPlugin; protected function setUp(): void { parent::setUp(); $this->fakeLockerPlugin = new FakeLockerPlugin(); } - public function testInitialize() { + public function testInitialize(): void { /** @var Server $server */ - $server = $this->getMockBuilder(Server::class) - ->disableOriginalConstructor() - ->getMock(); - $server - ->expects($this->at(0)) - ->method('on') - ->with('method:LOCK', [$this->fakeLockerPlugin, 'fakeLockProvider'], 1); - $server - ->expects($this->at(1)) - ->method('on') - ->with('method:UNLOCK', [$this->fakeLockerPlugin, 'fakeUnlockProvider'], 1); - $server - ->expects($this->at(2)) - ->method('on') - ->with('propFind', [$this->fakeLockerPlugin, 'propFind']); - $server - ->expects($this->at(3)) + $server = $this->createMock(Server::class); + $calls = [ + ['method:LOCK', [$this->fakeLockerPlugin, 'fakeLockProvider'], 1], + ['method:UNLOCK', [$this->fakeLockerPlugin, 'fakeUnlockProvider'], 1], + ['propFind', [$this->fakeLockerPlugin, 'propFind'], 100], + ['validateTokens', [$this->fakeLockerPlugin, 'validateTokens'], 100], + ]; + $server->expects($this->exactly(count($calls))) ->method('on') - ->with('validateTokens', [$this->fakeLockerPlugin, 'validateTokens']); + ->willReturnCallback(function () use (&$calls): void { + $expected = array_shift($calls); + $this->assertEquals($expected, func_get_args()); + }); $this->fakeLockerPlugin->initialize($server); } - public function testGetHTTPMethods() { + public function testGetHTTPMethods(): void { $expected = [ 'LOCK', 'UNLOCK', @@ -83,32 +57,32 @@ class FakeLockerPluginTest extends TestCase { $this->assertSame($expected, $this->fakeLockerPlugin->getHTTPMethods('Test')); } - public function testGetFeatures() { + public function testGetFeatures(): void { $expected = [ 2, ]; $this->assertSame($expected, $this->fakeLockerPlugin->getFeatures()); } - public function testPropFind() { - $propFind = $this->getMockBuilder(PropFind::class) - ->disableOriginalConstructor() - ->getMock(); - $node = $this->getMockBuilder(INode::class) - ->disableOriginalConstructor() - ->getMock(); + public function testPropFind(): void { + $propFind = $this->createMock(PropFind::class); + $node = $this->createMock(INode::class); - $propFind->expects($this->at(0)) - ->method('handle') - ->with('{DAV:}supportedlock'); - $propFind->expects($this->at(1)) + $calls = [ + '{DAV:}supportedlock', + '{DAV:}lockdiscovery', + ]; + $propFind->expects($this->exactly(count($calls))) ->method('handle') - ->with('{DAV:}lockdiscovery'); + ->willReturnCallback(function ($propertyName) use (&$calls): void { + $expected = array_shift($calls); + $this->assertEquals($expected, $propertyName); + }); $this->fakeLockerPlugin->propFind($propFind, $node); } - public function tokenDataProvider() { + public static function tokenDataProvider(): array { return [ [ [ @@ -145,23 +119,15 @@ class FakeLockerPluginTest extends TestCase { ]; } - /** - * @dataProvider tokenDataProvider - * @param array $input - * @param array $expected - */ - public function testValidateTokens(array $input, array $expected) { - $request = $this->getMockBuilder(RequestInterface::class) - ->disableOriginalConstructor() - ->getMock(); + #[\PHPUnit\Framework\Attributes\DataProvider('tokenDataProvider')] + public function testValidateTokens(array $input, array $expected): void { + $request = $this->createMock(RequestInterface::class); $this->fakeLockerPlugin->validateTokens($request, $input); $this->assertSame($expected, $input); } - public function testFakeLockProvider() { - $request = $this->getMockBuilder(RequestInterface::class) - ->disableOriginalConstructor() - ->getMock(); + public function testFakeLockProvider(): void { + $request = $this->createMock(RequestInterface::class); $response = new Response(); $server = $this->getMockBuilder(Server::class) ->getMock(); @@ -178,20 +144,16 @@ class FakeLockerPluginTest extends TestCase { $this->assertXmlStringEqualsXmlString($expectedXml, $response->getBody()); } - public function testFakeUnlockProvider() { - $request = $this->getMockBuilder(RequestInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $response = $this->getMockBuilder(ResponseInterface::class) - ->disableOriginalConstructor() - ->getMock(); + public function testFakeUnlockProvider(): void { + $request = $this->createMock(RequestInterface::class); + $response = $this->createMock(ResponseInterface::class); $response->expects($this->once()) - ->method('setStatus') - ->with('204'); + ->method('setStatus') + ->with('204'); $response->expects($this->once()) - ->method('setHeader') - ->with('Content-Length', '0'); + ->method('setHeader') + ->with('Content-Length', '0'); $this->assertSame(false, $this->fakeLockerPlugin->fakeUnlockProvider($request, $response)); } diff --git a/apps/dav/tests/unit/Connector/Sabre/FileTest.php b/apps/dav/tests/unit/Connector/Sabre/FileTest.php index 9870a62845c..60c8382e131 100644 --- a/apps/dav/tests/unit/Connector/Sabre/FileTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/FileTest.php @@ -1,31 +1,10 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Calviño Sánchez <danxuliu@gmail.com> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @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> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @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\Connector\Sabre; @@ -35,14 +14,29 @@ use OC\Files\Storage\Local; use OC\Files\Storage\Temporary; use OC\Files\Storage\Wrapper\PermissionsMask; use OC\Files\View; +use OCA\DAV\Connector\Sabre\Exception\FileLocked; +use OCA\DAV\Connector\Sabre\Exception\Forbidden; +use OCA\DAV\Connector\Sabre\Exception\InvalidPath; use OCA\DAV\Connector\Sabre\File; use OCP\Constants; +use OCP\Encryption\Exceptions\GenericEncryptionException; +use OCP\Files\EntityTooLargeException; use OCP\Files\FileInfo; use OCP\Files\ForbiddenException; -use OCP\Files\Storage; +use OCP\Files\InvalidContentException; +use OCP\Files\InvalidPathException; +use OCP\Files\LockNotAcquiredException; +use OCP\Files\NotPermittedException; +use OCP\Files\Storage\IStorage; +use OCP\Files\StorageNotAvailableException; use OCP\IConfig; use OCP\IRequestId; +use OCP\ITempManager; +use OCP\IUserManager; use OCP\Lock\ILockingProvider; +use OCP\Lock\LockedException; +use OCP\Server; +use OCP\Util; use PHPUnit\Framework\MockObject\MockObject; use Test\HookHelper; use Test\TestCase; @@ -60,58 +54,39 @@ class FileTest extends TestCase { use MountProviderTrait; use UserTrait; - /** - * @var string - */ - private $user; - - /** @var IConfig|MockObject */ - protected $config; - - /** @var IRequestId|MockObject */ - protected $requestId; + private string $user; + protected IConfig&MockObject $config; + protected IRequestId&MockObject $requestId; protected function setUp(): void { parent::setUp(); - unset($_SERVER['HTTP_OC_CHUNKED']); - unset($_SERVER['CONTENT_LENGTH']); - unset($_SERVER['REQUEST_METHOD']); \OC_Hook::clear(); $this->user = 'test_user'; $this->createUser($this->user, 'pass'); - $this->loginAsUser($this->user); + self::loginAsUser($this->user); $this->config = $this->createMock(IConfig::class); $this->requestId = $this->createMock(IRequestId::class); } protected function tearDown(): void { - $userManager = \OC::$server->getUserManager(); + $userManager = Server::get(IUserManager::class); $userManager->get($this->user)->delete(); - unset($_SERVER['HTTP_OC_CHUNKED']); parent::tearDown(); } - /** - * @return MockObject|Storage - */ - private function getMockStorage() { - $storage = $this->getMockBuilder(Storage::class) - ->disableOriginalConstructor() - ->getMock(); + private function getMockStorage(): MockObject&IStorage { + $storage = $this->createMock(IStorage::class); $storage->method('getId') ->willReturn('home::someuser'); return $storage; } - /** - * @param string $string - */ - private function getStream($string) { + private function getStream(string $string) { $stream = fopen('php://temp', 'r+'); fwrite($stream, $string); fseek($stream, 0); @@ -119,7 +94,7 @@ class FileTest extends TestCase { } - public function fopenFailuresProvider() { + public static function fopenFailuresProvider(): array { return [ [ // return false @@ -128,39 +103,39 @@ class FileTest extends TestCase { false ], [ - new \OCP\Files\NotPermittedException(), + new NotPermittedException(), 'Sabre\DAV\Exception\Forbidden' ], [ - new \OCP\Files\EntityTooLargeException(), + new EntityTooLargeException(), 'OCA\DAV\Connector\Sabre\Exception\EntityTooLarge' ], [ - new \OCP\Files\InvalidContentException(), + new InvalidContentException(), 'OCA\DAV\Connector\Sabre\Exception\UnsupportedMediaType' ], [ - new \OCP\Files\InvalidPathException(), + new InvalidPathException(), 'Sabre\DAV\Exception\Forbidden' ], [ - new \OCP\Files\ForbiddenException('', true), + new ForbiddenException('', true), 'OCA\DAV\Connector\Sabre\Exception\Forbidden' ], [ - new \OCP\Files\LockNotAcquiredException('/test.txt', 1), + new LockNotAcquiredException('/test.txt', 1), 'OCA\DAV\Connector\Sabre\Exception\FileLocked' ], [ - new \OCP\Lock\LockedException('/test.txt'), + new LockedException('/test.txt'), 'OCA\DAV\Connector\Sabre\Exception\FileLocked' ], [ - new \OCP\Encryption\Exceptions\GenericEncryptionException(), + new GenericEncryptionException(), 'Sabre\DAV\Exception\ServiceUnavailable' ], [ - new \OCP\Files\StorageNotAvailableException(), + new StorageNotAvailableException(), 'Sabre\DAV\Exception\ServiceUnavailable' ], [ @@ -175,19 +150,17 @@ class FileTest extends TestCase { ]; } - /** - * @dataProvider fopenFailuresProvider - */ - public function testSimplePutFails($thrownException, $expectedException, $checkPreviousClass = true) { + #[\PHPUnit\Framework\Attributes\DataProvider('fopenFailuresProvider')] + public function testSimplePutFails(?\Throwable $thrownException, string $expectedException, bool $checkPreviousClass = true): void { // setup $storage = $this->getMockBuilder(Local::class) - ->setMethods(['writeStream']) - ->setConstructorArgs([['datadir' => \OC::$server->getTempManager()->getTemporaryFolder()]]) + ->onlyMethods(['writeStream']) + ->setConstructorArgs([['datadir' => Server::get(ITempManager::class)->getTemporaryFolder()]]) ->getMock(); - \OC\Files\Filesystem::mount($storage, [], $this->user . '/'); - /** @var View | MockObject $view */ + Filesystem::mount($storage, [], $this->user . '/'); + /** @var View&MockObject $view */ $view = $this->getMockBuilder(View::class) - ->setMethods(['getRelativePath', 'resolvePath']) + ->onlyMethods(['getRelativePath', 'resolvePath']) ->getMock(); $view->expects($this->atLeastOnce()) ->method('resolvePath') @@ -200,7 +173,7 @@ class FileTest extends TestCase { if ($thrownException !== null) { $storage->expects($this->once()) ->method('writeStream') - ->will($this->throwException($thrownException)); + ->willThrowException($thrownException); } else { $storage->expects($this->once()) ->method('writeStream') @@ -212,11 +185,11 @@ class FileTest extends TestCase { ->willReturnArgument(0); $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [ - 'permissions' => \OCP\Constants::PERMISSION_ALL, + 'permissions' => Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, ], null); - $file = new \OCA\DAV\Connector\Sabre\File($view, $info); + $file = new File($view, $info); // action $caughtException = null; @@ -235,93 +208,18 @@ class FileTest extends TestCase { } /** - * Test putting a file using chunking - * - * @dataProvider fopenFailuresProvider - */ - public function testChunkedPutFails($thrownException, $expectedException, $checkPreviousClass = false) { - // setup - $storage = $this->getMockBuilder(Local::class) - ->setMethods(['fopen']) - ->setConstructorArgs([['datadir' => \OC::$server->getTempManager()->getTemporaryFolder()]]) - ->getMock(); - \OC\Files\Filesystem::mount($storage, [], $this->user . '/'); - $view = $this->getMockBuilder(View::class) - ->setMethods(['getRelativePath', 'resolvePath']) - ->getMock(); - $view->expects($this->atLeastOnce()) - ->method('resolvePath') - ->willReturnCallback( - function ($path) use ($storage) { - return [$storage, $path]; - } - ); - - if ($thrownException !== null) { - $storage->expects($this->once()) - ->method('fopen') - ->will($this->throwException($thrownException)); - } else { - $storage->expects($this->once()) - ->method('fopen') - ->willReturn(false); - } - - $view->expects($this->any()) - ->method('getRelativePath') - ->willReturnArgument(0); - - $_SERVER['HTTP_OC_CHUNKED'] = true; - - $info = new \OC\Files\FileInfo('/test.txt-chunking-12345-2-0', $this->getMockStorage(), null, [ - 'permissions' => \OCP\Constants::PERMISSION_ALL, - 'type' => FileInfo::TYPE_FOLDER, - ], null); - $file = new \OCA\DAV\Connector\Sabre\File($view, $info); - - // put first chunk - $file->acquireLock(ILockingProvider::LOCK_SHARED); - $this->assertNull($file->put('test data one')); - $file->releaseLock(ILockingProvider::LOCK_SHARED); - - $info = new \OC\Files\FileInfo('/test.txt-chunking-12345-2-1', $this->getMockStorage(), null, [ - 'permissions' => \OCP\Constants::PERMISSION_ALL, - 'type' => FileInfo::TYPE_FOLDER, - ], null); - $file = new \OCA\DAV\Connector\Sabre\File($view, $info); - - // action - $caughtException = null; - try { - // last chunk - $file->acquireLock(ILockingProvider::LOCK_SHARED); - $file->put('test data two'); - $file->releaseLock(ILockingProvider::LOCK_SHARED); - } catch (\Exception $e) { - $caughtException = $e; - } - - $this->assertInstanceOf($expectedException, $caughtException); - if ($checkPreviousClass) { - $this->assertInstanceOf(get_class($thrownException), $caughtException->getPrevious()); - } - - $this->assertEmpty($this->listPartFiles($view, ''), 'No stray part files'); - } - - /** * Simulate putting a file to the given path. * * @param string $path path to put the file into - * @param string $viewRoot root to use for the view + * @param ?string $viewRoot root to use for the view * @param null|Request $request the HTTP request * - * @return null|string of the PUT operaiton which is usually the etag + * @return null|string of the PUT operation which is usually the etag */ - private function doPut($path, $viewRoot = null, Request $request = null) { - $view = \OC\Files\Filesystem::getView(); + private function doPut(string $path, ?string $viewRoot = null, ?Request $request = null) { + $view = Filesystem::getView(); if (!is_null($viewRoot)) { - $view = new \OC\Files\View($viewRoot); + $view = new View($viewRoot); } else { $viewRoot = '/' . $this->user . '/files'; } @@ -331,16 +229,16 @@ class FileTest extends TestCase { $this->getMockStorage(), null, [ - 'permissions' => \OCP\Constants::PERMISSION_ALL, + 'permissions' => Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, ], null ); - /** @var \OCA\DAV\Connector\Sabre\File | MockObject $file */ - $file = $this->getMockBuilder(\OCA\DAV\Connector\Sabre\File::class) + /** @var File&MockObject $file */ + $file = $this->getMockBuilder(File::class) ->setConstructorArgs([$view, $info, null, $request]) - ->setMethods(['header']) + ->onlyMethods(['header']) ->getMock(); // beforeMethod locks @@ -357,71 +255,71 @@ class FileTest extends TestCase { /** * Test putting a single file */ - public function testPutSingleFile() { + public function testPutSingleFile(): void { $this->assertNotEmpty($this->doPut('/foo.txt')); } - public function legalMtimeProvider() { + public static function legalMtimeProvider(): array { return [ - "string" => [ - 'HTTP_X_OC_MTIME' => "string", - 'expected result' => null + 'string' => [ + 'requestMtime' => 'string', + 'resultMtime' => null ], - "castable string (int)" => [ - 'HTTP_X_OC_MTIME' => "987654321", - 'expected result' => 987654321 + 'castable string (int)' => [ + 'requestMtime' => '987654321', + 'resultMtime' => 987654321 ], - "castable string (float)" => [ - 'HTTP_X_OC_MTIME' => "123456789.56", - 'expected result' => 123456789 + 'castable string (float)' => [ + 'requestMtime' => '123456789.56', + 'resultMtime' => 123456789 ], - "float" => [ - 'HTTP_X_OC_MTIME' => 123456789.56, - 'expected result' => 123456789 + 'float' => [ + 'requestMtime' => 123456789.56, + 'resultMtime' => 123456789 ], - "zero" => [ - 'HTTP_X_OC_MTIME' => 0, - 'expected result' => null + 'zero' => [ + 'requestMtime' => 0, + 'resultMtime' => null ], - "zero string" => [ - 'HTTP_X_OC_MTIME' => "0", - 'expected result' => null + 'zero string' => [ + 'requestMtime' => '0', + 'resultMtime' => null ], - "negative zero string" => [ - 'HTTP_X_OC_MTIME' => "-0", - 'expected result' => null + 'negative zero string' => [ + 'requestMtime' => '-0', + 'resultMtime' => null ], - "string starting with number following by char" => [ - 'HTTP_X_OC_MTIME' => "2345asdf", - 'expected result' => null + 'string starting with number following by char' => [ + 'requestMtime' => '2345asdf', + 'resultMtime' => null ], - "string castable hex int" => [ - 'HTTP_X_OC_MTIME' => "0x45adf", - 'expected result' => null + 'string castable hex int' => [ + 'requestMtime' => '0x45adf', + 'resultMtime' => null ], - "string that looks like invalid hex int" => [ - 'HTTP_X_OC_MTIME' => "0x123g", - 'expected result' => null + 'string that looks like invalid hex int' => [ + 'requestMtime' => '0x123g', + 'resultMtime' => null ], - "negative int" => [ - 'HTTP_X_OC_MTIME' => -34, - 'expected result' => null + 'negative int' => [ + 'requestMtime' => -34, + 'resultMtime' => null ], - "negative float" => [ - 'HTTP_X_OC_MTIME' => -34.43, - 'expected result' => null + 'negative float' => [ + 'requestMtime' => -34.43, + 'resultMtime' => null ], ]; } /** * Test putting a file with string Mtime - * @dataProvider legalMtimeProvider */ - public function testPutSingleFileLegalMtime($requestMtime, $resultMtime) { + #[\PHPUnit\Framework\Attributes\DataProvider('legalMtimeProvider')] + public function testPutSingleFileLegalMtime(mixed $requestMtime, ?int $resultMtime): void { $request = new Request([ 'server' => [ - 'HTTP_X_OC_MTIME' => $requestMtime, + 'HTTP_X_OC_MTIME' => (string)$requestMtime, ] ], $this->requestId, $this->config, null); $file = 'foo.txt'; @@ -438,44 +336,9 @@ class FileTest extends TestCase { } /** - * Test putting a file with string Mtime using chunking - * @dataProvider legalMtimeProvider - */ - public function testChunkedPutLegalMtime($requestMtime, $resultMtime) { - $request = new Request([ - 'server' => [ - 'HTTP_X_OC_MTIME' => $requestMtime, - ] - ], $this->requestId, $this->config, null); - - $_SERVER['HTTP_OC_CHUNKED'] = true; - $file = 'foo.txt'; - - if ($resultMtime === null) { - $this->expectException(\Sabre\DAV\Exception::class); - } - - $this->doPut($file.'-chunking-12345-2-0', null, $request); - $this->doPut($file.'-chunking-12345-2-1', null, $request); - - if ($resultMtime !== null) { - $this->assertEquals($resultMtime, $this->getFileInfos($file)['mtime']); - } - } - - /** - * Test putting a file using chunking - */ - public function testChunkedPut() { - $_SERVER['HTTP_OC_CHUNKED'] = true; - $this->assertNull($this->doPut('/test.txt-chunking-12345-2-0')); - $this->assertNotEmpty($this->doPut('/test.txt-chunking-12345-2-1')); - } - - /** * Test that putting a file triggers create hooks */ - public function testPutSingleFileTriggersHooks() { + public function testPutSingleFileTriggersHooks(): void { HookHelper::setUpHooks(); $this->assertNotEmpty($this->doPut('/foo.txt')); @@ -506,8 +369,8 @@ class FileTest extends TestCase { /** * Test that putting a file triggers update hooks */ - public function testPutOverwriteFileTriggersHooks() { - $view = \OC\Files\Filesystem::getView(); + public function testPutOverwriteFileTriggersHooks(): void { + $view = Filesystem::getView(); $view->file_put_contents('/foo.txt', 'some content that will be replaced'); HookHelper::setUpHooks(); @@ -542,8 +405,8 @@ class FileTest extends TestCase { * if the passed view was chrooted (can happen with public webdav * where the root is the share root) */ - public function testPutSingleFileTriggersHooksDifferentRoot() { - $view = \OC\Files\Filesystem::getView(); + public function testPutSingleFileTriggersHooksDifferentRoot(): void { + $view = Filesystem::getView(); $view->mkdir('noderoot'); HookHelper::setUpHooks(); @@ -574,76 +437,7 @@ class FileTest extends TestCase { ); } - /** - * Test that putting a file with chunks triggers create hooks - */ - public function testPutChunkedFileTriggersHooks() { - HookHelper::setUpHooks(); - - $_SERVER['HTTP_OC_CHUNKED'] = true; - $this->assertNull($this->doPut('/foo.txt-chunking-12345-2-0')); - $this->assertNotEmpty($this->doPut('/foo.txt-chunking-12345-2-1')); - - $this->assertCount(4, HookHelper::$hookCalls); - $this->assertHookCall( - HookHelper::$hookCalls[0], - Filesystem::signal_create, - '/foo.txt' - ); - $this->assertHookCall( - HookHelper::$hookCalls[1], - Filesystem::signal_write, - '/foo.txt' - ); - $this->assertHookCall( - HookHelper::$hookCalls[2], - Filesystem::signal_post_create, - '/foo.txt' - ); - $this->assertHookCall( - HookHelper::$hookCalls[3], - Filesystem::signal_post_write, - '/foo.txt' - ); - } - - /** - * Test that putting a chunked file triggers update hooks - */ - public function testPutOverwriteChunkedFileTriggersHooks() { - $view = \OC\Files\Filesystem::getView(); - $view->file_put_contents('/foo.txt', 'some content that will be replaced'); - - HookHelper::setUpHooks(); - - $_SERVER['HTTP_OC_CHUNKED'] = true; - $this->assertNull($this->doPut('/foo.txt-chunking-12345-2-0')); - $this->assertNotEmpty($this->doPut('/foo.txt-chunking-12345-2-1')); - - $this->assertCount(4, HookHelper::$hookCalls); - $this->assertHookCall( - HookHelper::$hookCalls[0], - Filesystem::signal_update, - '/foo.txt' - ); - $this->assertHookCall( - HookHelper::$hookCalls[1], - Filesystem::signal_write, - '/foo.txt' - ); - $this->assertHookCall( - HookHelper::$hookCalls[2], - Filesystem::signal_post_update, - '/foo.txt' - ); - $this->assertHookCall( - HookHelper::$hookCalls[3], - Filesystem::signal_post_write, - '/foo.txt' - ); - } - - public static function cancellingHook($params) { + public static function cancellingHook($params): void { self::$hookCalls[] = [ 'signal' => Filesystem::signal_post_create, 'params' => $params @@ -653,8 +447,8 @@ class FileTest extends TestCase { /** * Test put file with cancelled hook */ - public function testPutSingleFileCancelPreHook() { - \OCP\Util::connectHook( + public function testPutSingleFileCancelPreHook(): void { + Util::connectHook( Filesystem::CLASSNAME, Filesystem::signal_create, '\Test\HookHelper', @@ -676,10 +470,11 @@ class FileTest extends TestCase { /** * Test exception when the uploaded size did not match */ - public function testSimplePutFailsSizeCheck() { + public function testSimplePutFailsSizeCheck(): void { // setup + /** @var View&MockObject */ $view = $this->getMockBuilder(View::class) - ->setMethods(['rename', 'getRelativePath', 'filesize']) + ->onlyMethods(['rename', 'getRelativePath', 'filesize']) ->getMock(); $view->expects($this->any()) ->method('rename') @@ -693,15 +488,19 @@ class FileTest extends TestCase { ->method('filesize') ->willReturn(123456); - $_SERVER['CONTENT_LENGTH'] = 123456; - $_SERVER['REQUEST_METHOD'] = 'PUT'; + $request = new Request([ + 'server' => [ + 'CONTENT_LENGTH' => '123456', + ], + 'method' => 'PUT', + ], $this->requestId, $this->config, null); $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [ - 'permissions' => \OCP\Constants::PERMISSION_ALL, + 'permissions' => Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, ], null); - $file = new \OCA\DAV\Connector\Sabre\File($view, $info); + $file = new File($view, $info, null, $request); // action $thrown = false; @@ -724,18 +523,18 @@ class FileTest extends TestCase { /** * Test exception during final rename in simple upload mode */ - public function testSimplePutFailsMoveFromStorage() { - $view = new \OC\Files\View('/' . $this->user . '/files'); + public function testSimplePutFailsMoveFromStorage(): void { + $view = new View('/' . $this->user . '/files'); // simulate situation where the target file is locked $view->lockFile('/test.txt', ILockingProvider::LOCK_EXCLUSIVE); $info = new \OC\Files\FileInfo('/' . $this->user . '/files/test.txt', $this->getMockStorage(), null, [ - 'permissions' => \OCP\Constants::PERMISSION_ALL, + 'permissions' => Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, ], null); - $file = new \OCA\DAV\Connector\Sabre\File($view, $info); + $file = new File($view, $info); // action $thrown = false; @@ -747,47 +546,7 @@ class FileTest extends TestCase { // afterMethod unlocks $view->unlockFile($info->getPath(), ILockingProvider::LOCK_SHARED); - } catch (\OCA\DAV\Connector\Sabre\Exception\FileLocked $e) { - $thrown = true; - } - - $this->assertTrue($thrown); - $this->assertEmpty($this->listPartFiles($view, ''), 'No stray part files'); - } - - /** - * Test exception during final rename in chunk upload mode - */ - public function testChunkedPutFailsFinalRename() { - $view = new \OC\Files\View('/' . $this->user . '/files'); - - // simulate situation where the target file is locked - $view->lockFile('/test.txt', ILockingProvider::LOCK_EXCLUSIVE); - - $_SERVER['HTTP_OC_CHUNKED'] = true; - - $info = new \OC\Files\FileInfo('/' . $this->user . '/files/test.txt-chunking-12345-2-0', $this->getMockStorage(), null, [ - 'permissions' => \OCP\Constants::PERMISSION_ALL, - 'type' => FileInfo::TYPE_FOLDER, - ], null); - $file = new \OCA\DAV\Connector\Sabre\File($view, $info); - $file->acquireLock(ILockingProvider::LOCK_SHARED); - $this->assertNull($file->put('test data one')); - $file->releaseLock(ILockingProvider::LOCK_SHARED); - - $info = new \OC\Files\FileInfo('/' . $this->user . '/files/test.txt-chunking-12345-2-1', $this->getMockStorage(), null, [ - 'permissions' => \OCP\Constants::PERMISSION_ALL, - 'type' => FileInfo::TYPE_FOLDER, - ], null); - $file = new \OCA\DAV\Connector\Sabre\File($view, $info); - - // action - $thrown = false; - try { - $file->acquireLock(ILockingProvider::LOCK_SHARED); - $file->put($this->getStream('test data')); - $file->releaseLock(ILockingProvider::LOCK_SHARED); - } catch (\OCA\DAV\Connector\Sabre\Exception\FileLocked $e) { + } catch (FileLocked $e) { $thrown = true; } @@ -798,20 +557,21 @@ class FileTest extends TestCase { /** * Test put file with invalid chars */ - public function testSimplePutInvalidChars() { + public function testSimplePutInvalidChars(): void { // setup + /** @var View&MockObject */ $view = $this->getMockBuilder(View::class) - ->setMethods(['getRelativePath']) + ->onlyMethods(['getRelativePath']) ->getMock(); $view->expects($this->any()) ->method('getRelativePath') ->willReturnArgument(0); - $info = new \OC\Files\FileInfo('/*', $this->getMockStorage(), null, [ - 'permissions' => \OCP\Constants::PERMISSION_ALL, + $info = new \OC\Files\FileInfo("/i\nvalid", $this->getMockStorage(), null, [ + 'permissions' => Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, ], null); - $file = new \OCA\DAV\Connector\Sabre\File($view, $info); + $file = new File($view, $info); // action $thrown = false; @@ -823,7 +583,7 @@ class FileTest extends TestCase { // afterMethod unlocks $view->unlockFile($info->getPath(), ILockingProvider::LOCK_SHARED); - } catch (\OCA\DAV\Connector\Sabre\Exception\InvalidPath $e) { + } catch (InvalidPath $e) { $thrown = true; } @@ -835,31 +595,34 @@ class FileTest extends TestCase { * Test setting name with setName() with invalid chars * */ - public function testSetNameInvalidChars() { - $this->expectException(\OCA\DAV\Connector\Sabre\Exception\InvalidPath::class); + public function testSetNameInvalidChars(): void { + $this->expectException(InvalidPath::class); // setup + /** @var View&MockObject */ $view = $this->getMockBuilder(View::class) - ->setMethods(['getRelativePath']) + ->onlyMethods(['getRelativePath']) ->getMock(); $view->expects($this->any()) ->method('getRelativePath') ->willReturnArgument(0); - $info = new \OC\Files\FileInfo('/*', $this->getMockStorage(), null, [ - 'permissions' => \OCP\Constants::PERMISSION_ALL, + $info = new \OC\Files\FileInfo('/valid', $this->getMockStorage(), null, [ + 'permissions' => Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, ], null); - $file = new \OCA\DAV\Connector\Sabre\File($view, $info); - $file->setName('/super*star.txt'); + $file = new File($view, $info); + + $file->setName("/i\nvalid"); } - public function testUploadAbort() { + public function testUploadAbort(): void { // setup + /** @var View&MockObject */ $view = $this->getMockBuilder(View::class) - ->setMethods(['rename', 'getRelativePath', 'filesize']) + ->onlyMethods(['rename', 'getRelativePath', 'filesize']) ->getMock(); $view->expects($this->any()) ->method('rename') @@ -872,15 +635,19 @@ class FileTest extends TestCase { ->method('filesize') ->willReturn(123456); - $_SERVER['CONTENT_LENGTH'] = 12345; - $_SERVER['REQUEST_METHOD'] = 'PUT'; + $request = new Request([ + 'server' => [ + 'CONTENT_LENGTH' => '123456', + ], + 'method' => 'PUT', + ], $this->requestId, $this->config, null); $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [ - 'permissions' => \OCP\Constants::PERMISSION_ALL, + 'permissions' => Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, ], null); - $file = new \OCA\DAV\Connector\Sabre\File($view, $info); + $file = new File($view, $info, null, $request); // action $thrown = false; @@ -901,8 +668,9 @@ class FileTest extends TestCase { } - public function testDeleteWhenAllowed() { + public function testDeleteWhenAllowed(): void { // setup + /** @var View&MockObject */ $view = $this->getMockBuilder(View::class) ->getMock(); @@ -911,21 +679,22 @@ class FileTest extends TestCase { ->willReturn(true); $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [ - 'permissions' => \OCP\Constants::PERMISSION_ALL, + 'permissions' => Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, ], null); - $file = new \OCA\DAV\Connector\Sabre\File($view, $info); + $file = new File($view, $info); // action $file->delete(); } - public function testDeleteThrowsWhenDeletionNotAllowed() { + public function testDeleteThrowsWhenDeletionNotAllowed(): void { $this->expectException(\Sabre\DAV\Exception\Forbidden::class); // setup + /** @var View&MockObject */ $view = $this->getMockBuilder(View::class) ->getMock(); @@ -934,17 +703,18 @@ class FileTest extends TestCase { 'type' => FileInfo::TYPE_FOLDER, ], null); - $file = new \OCA\DAV\Connector\Sabre\File($view, $info); + $file = new File($view, $info); // action $file->delete(); } - public function testDeleteThrowsWhenDeletionFailed() { + public function testDeleteThrowsWhenDeletionFailed(): void { $this->expectException(\Sabre\DAV\Exception\Forbidden::class); // setup + /** @var View&MockObject */ $view = $this->getMockBuilder(View::class) ->getMock(); @@ -954,21 +724,22 @@ class FileTest extends TestCase { ->willReturn(false); $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [ - 'permissions' => \OCP\Constants::PERMISSION_ALL, + 'permissions' => Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, ], null); - $file = new \OCA\DAV\Connector\Sabre\File($view, $info); + $file = new File($view, $info); // action $file->delete(); } - public function testDeleteThrowsWhenDeletionThrows() { - $this->expectException(\OCA\DAV\Connector\Sabre\Exception\Forbidden::class); + public function testDeleteThrowsWhenDeletionThrows(): void { + $this->expectException(Forbidden::class); // setup + /** @var View&MockObject */ $view = $this->getMockBuilder(View::class) ->getMock(); @@ -978,11 +749,11 @@ class FileTest extends TestCase { ->willThrowException(new ForbiddenException('', true)); $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [ - 'permissions' => \OCP\Constants::PERMISSION_ALL, + 'permissions' => Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, ], null); - $file = new \OCA\DAV\Connector\Sabre\File($view, $info); + $file = new File($view, $info); // action $file->delete(); @@ -1007,8 +778,8 @@ class FileTest extends TestCase { /** * Test whether locks are set before and after the operation */ - public function testPutLocking() { - $view = new \OC\Files\View('/' . $this->user . '/files/'); + public function testPutLocking(): void { + $view = new View('/' . $this->user . '/files/'); $path = 'test-locking.txt'; $info = new \OC\Files\FileInfo( @@ -1016,27 +787,27 @@ class FileTest extends TestCase { $this->getMockStorage(), null, [ - 'permissions' => \OCP\Constants::PERMISSION_ALL, + 'permissions' => Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, ], null ); - $file = new \OCA\DAV\Connector\Sabre\File($view, $info); + $file = new File($view, $info); $this->assertFalse( - $this->isFileLocked($view, $path, \OCP\Lock\ILockingProvider::LOCK_SHARED), + $this->isFileLocked($view, $path, ILockingProvider::LOCK_SHARED), 'File unlocked before put' ); $this->assertFalse( - $this->isFileLocked($view, $path, \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE), + $this->isFileLocked($view, $path, ILockingProvider::LOCK_EXCLUSIVE), 'File unlocked before put' ); $wasLockedPre = false; $wasLockedPost = false; $eventHandler = $this->getMockBuilder(\stdclass::class) - ->setMethods(['writeCallback', 'postWriteCallback']) + ->addMethods(['writeCallback', 'postWriteCallback']) ->getMock(); // both pre and post hooks might need access to the file, @@ -1044,27 +815,27 @@ class FileTest extends TestCase { $eventHandler->expects($this->once()) ->method('writeCallback') ->willReturnCallback( - function () use ($view, $path, &$wasLockedPre) { - $wasLockedPre = $this->isFileLocked($view, $path, \OCP\Lock\ILockingProvider::LOCK_SHARED); - $wasLockedPre = $wasLockedPre && !$this->isFileLocked($view, $path, \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE); + function () use ($view, $path, &$wasLockedPre): void { + $wasLockedPre = $this->isFileLocked($view, $path, ILockingProvider::LOCK_SHARED); + $wasLockedPre = $wasLockedPre && !$this->isFileLocked($view, $path, ILockingProvider::LOCK_EXCLUSIVE); } ); $eventHandler->expects($this->once()) ->method('postWriteCallback') ->willReturnCallback( - function () use ($view, $path, &$wasLockedPost) { - $wasLockedPost = $this->isFileLocked($view, $path, \OCP\Lock\ILockingProvider::LOCK_SHARED); - $wasLockedPost = $wasLockedPost && !$this->isFileLocked($view, $path, \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE); + function () use ($view, $path, &$wasLockedPost): void { + $wasLockedPost = $this->isFileLocked($view, $path, ILockingProvider::LOCK_SHARED); + $wasLockedPost = $wasLockedPost && !$this->isFileLocked($view, $path, ILockingProvider::LOCK_EXCLUSIVE); } ); - \OCP\Util::connectHook( + Util::connectHook( Filesystem::CLASSNAME, Filesystem::signal_write, $eventHandler, 'writeCallback' ); - \OCP\Util::connectHook( + Util::connectHook( Filesystem::CLASSNAME, Filesystem::signal_post_write, $eventHandler, @@ -1083,11 +854,11 @@ class FileTest extends TestCase { $this->assertTrue($wasLockedPost, 'File was locked during post-hooks'); $this->assertFalse( - $this->isFileLocked($view, $path, \OCP\Lock\ILockingProvider::LOCK_SHARED), + $this->isFileLocked($view, $path, ILockingProvider::LOCK_SHARED), 'File unlocked after put' ); $this->assertFalse( - $this->isFileLocked($view, $path, \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE), + $this->isFileLocked($view, $path, ILockingProvider::LOCK_EXCLUSIVE), 'File unlocked after put' ); } @@ -1100,9 +871,9 @@ class FileTest extends TestCase { * * @return array list of part files */ - private function listPartFiles(\OC\Files\View $userView = null, $path = '') { + private function listPartFiles(?View $userView = null, $path = '') { if ($userView === null) { - $userView = \OC\Files\Filesystem::getView(); + $userView = Filesystem::getView(); } $files = []; [$storage, $internalPath] = $userView->resolvePath($path); @@ -1110,7 +881,7 @@ class FileTest extends TestCase { $realPath = $storage->getSourcePath($internalPath); $dh = opendir($realPath); while (($file = readdir($dh)) !== false) { - if (substr($file, strlen($file) - 5, 5) === '.part') { + if (str_ends_with($file, '.part')) { $files[] = $file; } } @@ -1126,81 +897,84 @@ class FileTest extends TestCase { * @param View $userView * @return array */ - private function getFileInfos($path = '', View $userView = null) { + private function getFileInfos($path = '', ?View $userView = null) { if ($userView === null) { $userView = Filesystem::getView(); } return [ - "filesize" => $userView->filesize($path), - "mtime" => $userView->filemtime($path), - "filetype" => $userView->filetype($path), - "mimetype" => $userView->getMimeType($path) + 'filesize' => $userView->filesize($path), + 'mtime' => $userView->filemtime($path), + 'filetype' => $userView->filetype($path), + 'mimetype' => $userView->getMimeType($path) ]; } - public function testGetFopenFails() { + public function testGetFopenFails(): void { $this->expectException(\Sabre\DAV\Exception\ServiceUnavailable::class); + /** @var View&MockObject */ $view = $this->getMockBuilder(View::class) - ->setMethods(['fopen']) + ->onlyMethods(['fopen']) ->getMock(); $view->expects($this->atLeastOnce()) ->method('fopen') ->willReturn(false); $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [ - 'permissions' => \OCP\Constants::PERMISSION_ALL, - 'type' => FileInfo::TYPE_FOLDER, + 'permissions' => Constants::PERMISSION_ALL, + 'type' => FileInfo::TYPE_FILE, ], null); - $file = new \OCA\DAV\Connector\Sabre\File($view, $info); + $file = new File($view, $info); $file->get(); } - public function testGetFopenThrows() { - $this->expectException(\OCA\DAV\Connector\Sabre\Exception\Forbidden::class); + public function testGetFopenThrows(): void { + $this->expectException(Forbidden::class); + /** @var View&MockObject */ $view = $this->getMockBuilder(View::class) - ->setMethods(['fopen']) + ->onlyMethods(['fopen']) ->getMock(); $view->expects($this->atLeastOnce()) ->method('fopen') ->willThrowException(new ForbiddenException('', true)); $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [ - 'permissions' => \OCP\Constants::PERMISSION_ALL, - 'type' => FileInfo::TYPE_FOLDER, + 'permissions' => Constants::PERMISSION_ALL, + 'type' => FileInfo::TYPE_FILE, ], null); - $file = new \OCA\DAV\Connector\Sabre\File($view, $info); + $file = new File($view, $info); $file->get(); } - public function testGetThrowsIfNoPermission() { + public function testGetThrowsIfNoPermission(): void { $this->expectException(\Sabre\DAV\Exception\NotFound::class); + /** @var View&MockObject */ $view = $this->getMockBuilder(View::class) - ->setMethods(['fopen']) + ->onlyMethods(['fopen']) ->getMock(); $view->expects($this->never()) ->method('fopen'); $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [ - 'permissions' => \OCP\Constants::PERMISSION_CREATE, // no read perm + 'permissions' => Constants::PERMISSION_CREATE, // no read perm 'type' => FileInfo::TYPE_FOLDER, ], null); - $file = new \OCA\DAV\Connector\Sabre\File($view, $info); + $file = new File($view, $info); $file->get(); } - public function testSimplePutNoCreatePermissions() { + public function testSimplePutNoCreatePermissions(): void { $this->logout(); $storage = new Temporary([]); @@ -1231,8 +1005,8 @@ class FileTest extends TestCase { $this->assertEquals('new content', $view->file_get_contents('root/file.txt')); } - public function testPutLockExpired() { - $view = new \OC\Files\View('/' . $this->user . '/files/'); + public function testPutLockExpired(): void { + $view = new View('/' . $this->user . '/files/'); $path = 'test-locking.txt'; $info = new \OC\Files\FileInfo( @@ -1240,13 +1014,13 @@ class FileTest extends TestCase { $this->getMockStorage(), null, [ - 'permissions' => \OCP\Constants::PERMISSION_ALL, + 'permissions' => Constants::PERMISSION_ALL, 'type' => FileInfo::TYPE_FOLDER, ], null ); - $file = new \OCA\DAV\Connector\Sabre\File($view, $info); + $file = new File($view, $info); // don't lock before the PUT to simulate an expired shared lock $this->assertNotEmpty($file->put($this->getStream('test data'))); diff --git a/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php index 777a730ffd1..4df3accfda9 100644 --- a/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php @@ -1,42 +1,25 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * Copyright (c) 2015 Vincent Petry <pvince81@owncloud.com> - * Copyright (c) 2015 Vincent Petry <pvince81@owncloud.com> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Markus Goetz <markus@woboq.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Stefan Weil <sw@weilnetz.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @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\Connector\Sabre; +use OC\Accounts\Account; +use OC\Accounts\AccountProperty; use OC\User\User; use OCA\DAV\Connector\Sabre\Directory; +use OCA\DAV\Connector\Sabre\Exception\InvalidPath; use OCA\DAV\Connector\Sabre\File; use OCA\DAV\Connector\Sabre\FilesPlugin; use OCA\DAV\Connector\Sabre\Node; +use OCP\Accounts\IAccountManager; use OCP\Files\FileInfo; +use OCP\Files\IFilenameValidator; +use OCP\Files\InvalidPathException; use OCP\Files\StorageNotAvailableException; use OCP\IConfig; use OCP\IPreview; @@ -53,57 +36,19 @@ use Sabre\Xml\Service; use Test\TestCase; /** - * Copyright (c) 2015 Vincent Petry <pvince81@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * @group DB */ class FilesPluginTest extends TestCase { - public const GETETAG_PROPERTYNAME = FilesPlugin::GETETAG_PROPERTYNAME; - public const FILEID_PROPERTYNAME = FilesPlugin::FILEID_PROPERTYNAME; - public const INTERNAL_FILEID_PROPERTYNAME = FilesPlugin::INTERNAL_FILEID_PROPERTYNAME; - public const SIZE_PROPERTYNAME = FilesPlugin::SIZE_PROPERTYNAME; - public const PERMISSIONS_PROPERTYNAME = FilesPlugin::PERMISSIONS_PROPERTYNAME; - public const LASTMODIFIED_PROPERTYNAME = FilesPlugin::LASTMODIFIED_PROPERTYNAME; - public const CREATIONDATE_PROPERTYNAME = FilesPlugin::CREATIONDATE_PROPERTYNAME; - public const DOWNLOADURL_PROPERTYNAME = FilesPlugin::DOWNLOADURL_PROPERTYNAME; - public const OWNER_ID_PROPERTYNAME = FilesPlugin::OWNER_ID_PROPERTYNAME; - public const OWNER_DISPLAY_NAME_PROPERTYNAME = FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME; - public const DATA_FINGERPRINT_PROPERTYNAME = FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME; - public const HAS_PREVIEW_PROPERTYNAME = FilesPlugin::HAS_PREVIEW_PROPERTYNAME; - - /** - * @var \Sabre\DAV\Server | \PHPUnit\Framework\MockObject\MockObject - */ - private $server; - - /** - * @var \Sabre\DAV\Tree | \PHPUnit\Framework\MockObject\MockObject - */ - private $tree; - - /** - * @var FilesPlugin - */ - private $plugin; - - /** - * @var \OCP\IConfig | \PHPUnit\Framework\MockObject\MockObject - */ - private $config; - - /** - * @var \OCP\IRequest | \PHPUnit\Framework\MockObject\MockObject - */ - private $request; - /** - * @var \OCP\IPreview | \PHPUnit\Framework\MockObject\MockObject - */ - private $previewManager; - - /** @var IUserSession|MockObject */ - private $userSession; + private Tree&MockObject $tree; + private Server&MockObject $server; + private IConfig&MockObject $config; + private IRequest&MockObject $request; + private IPreview&MockObject $previewManager; + private IUserSession&MockObject $userSession; + private IFilenameValidator&MockObject $filenameValidator; + private IAccountManager&MockObject $accountManager; + private FilesPlugin $plugin; protected function setUp(): void { parent::setUp(); @@ -116,32 +61,28 @@ class FilesPluginTest extends TestCase { $this->request = $this->createMock(IRequest::class); $this->previewManager = $this->createMock(IPreview::class); $this->userSession = $this->createMock(IUserSession::class); + $this->filenameValidator = $this->createMock(IFilenameValidator::class); + $this->accountManager = $this->createMock(IAccountManager::class); $this->plugin = new FilesPlugin( $this->tree, $this->config, $this->request, $this->previewManager, - $this->userSession + $this->userSession, + $this->filenameValidator, + $this->accountManager, ); - $response = $this->getMockBuilder(ResponseInterface::class) - ->disableOriginalConstructor() - ->getMock(); + $response = $this->createMock(ResponseInterface::class); $this->server->httpResponse = $response; $this->server->xml = new Service(); $this->plugin->initialize($this->server); } - /** - * @param string $class - * @return \PHPUnit\Framework\MockObject\MockObject - */ - private function createTestNode($class, $path = '/dummypath') { - $node = $this->getMockBuilder($class) - ->disableOriginalConstructor() - ->getMock(); + private function createTestNode(string $class, string $path = '/dummypath'): MockObject { + $node = $this->createMock($class); $node->expects($this->any()) ->method('getId') @@ -180,29 +121,28 @@ class FilesPluginTest extends TestCase { return $node; } - public function testGetPropertiesForFile() { - /** @var \OCA\DAV\Connector\Sabre\File | \PHPUnit\Framework\MockObject\MockObject $node */ - $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File'); + public function testGetPropertiesForFile(): void { + /** @var File&MockObject $node */ + $node = $this->createTestNode(File::class); $propFind = new PropFind( '/dummyPath', [ - self::GETETAG_PROPERTYNAME, - self::FILEID_PROPERTYNAME, - self::INTERNAL_FILEID_PROPERTYNAME, - self::SIZE_PROPERTYNAME, - self::PERMISSIONS_PROPERTYNAME, - self::DOWNLOADURL_PROPERTYNAME, - self::OWNER_ID_PROPERTYNAME, - self::OWNER_DISPLAY_NAME_PROPERTYNAME, - self::DATA_FINGERPRINT_PROPERTYNAME, - self::CREATIONDATE_PROPERTYNAME, + FilesPlugin::GETETAG_PROPERTYNAME, + FilesPlugin::FILEID_PROPERTYNAME, + FilesPlugin::INTERNAL_FILEID_PROPERTYNAME, + FilesPlugin::SIZE_PROPERTYNAME, + FilesPlugin::PERMISSIONS_PROPERTYNAME, + FilesPlugin::DOWNLOADURL_PROPERTYNAME, + FilesPlugin::OWNER_ID_PROPERTYNAME, + FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME, + FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME, + FilesPlugin::CREATIONDATE_PROPERTYNAME, ], 0 ); - $user = $this->getMockBuilder(User::class) - ->disableOriginalConstructor()->getMock(); + $user = $this->createMock(User::class); $user ->expects($this->once()) ->method('getUID') @@ -212,6 +152,12 @@ class FilesPluginTest extends TestCase { ->method('getDisplayName') ->willReturn('M. Foo'); + $owner = $this->createMock(Account::class); + $this->accountManager->expects($this->once()) + ->method('getAccount') + ->with($user) + ->willReturn($owner); + $node->expects($this->once()) ->method('getDirectDownload') ->willReturn(['url' => 'http://example.com/']); @@ -219,70 +165,170 @@ class FilesPluginTest extends TestCase { ->method('getOwner') ->willReturn($user); + $displayNameProp = $this->createMock(AccountProperty::class); + $owner + ->expects($this->once()) + ->method('getProperty') + ->with(IAccountManager::PROPERTY_DISPLAYNAME) + ->willReturn($displayNameProp); + $displayNameProp + ->expects($this->once()) + ->method('getScope') + ->willReturn(IAccountManager::SCOPE_PUBLISHED); + + $this->plugin->handleGetProperties( + $propFind, + $node + ); + + $this->assertEquals('"abc"', $propFind->get(FilesPlugin::GETETAG_PROPERTYNAME)); + $this->assertEquals('00000123instanceid', $propFind->get(FilesPlugin::FILEID_PROPERTYNAME)); + $this->assertEquals('123', $propFind->get(FilesPlugin::INTERNAL_FILEID_PROPERTYNAME)); + $this->assertEquals('1973-11-29T21:33:09+00:00', $propFind->get(FilesPlugin::CREATIONDATE_PROPERTYNAME)); + $this->assertEquals(0, $propFind->get(FilesPlugin::SIZE_PROPERTYNAME)); + $this->assertEquals('DWCKMSR', $propFind->get(FilesPlugin::PERMISSIONS_PROPERTYNAME)); + $this->assertEquals('http://example.com/', $propFind->get(FilesPlugin::DOWNLOADURL_PROPERTYNAME)); + $this->assertEquals('foo', $propFind->get(FilesPlugin::OWNER_ID_PROPERTYNAME)); + $this->assertEquals('M. Foo', $propFind->get(FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME)); + $this->assertEquals('my_fingerprint', $propFind->get(FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME)); + $this->assertEquals([], $propFind->get404Properties()); + } + + public function testGetDisplayNamePropertyWhenNotPublished(): void { + $node = $this->createTestNode(File::class); + $propFind = new PropFind( + '/dummyPath', + [ + FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME, + ], + 0 + ); + + $this->userSession->expects($this->once()) + ->method('getUser') + ->willReturn(null); + + $user = $this->createMock(User::class); + + $user->expects($this->never()) + ->method('getDisplayName'); + + $owner = $this->createMock(Account::class); + $this->accountManager->expects($this->once()) + ->method('getAccount') + ->with($user) + ->willReturn($owner); + + $node->expects($this->once()) + ->method('getOwner') + ->willReturn($user); + + $displayNameProp = $this->createMock(AccountProperty::class); + $owner + ->expects($this->once()) + ->method('getProperty') + ->with(IAccountManager::PROPERTY_DISPLAYNAME) + ->willReturn($displayNameProp); + $displayNameProp + ->expects($this->once()) + ->method('getScope') + ->willReturn(IAccountManager::SCOPE_PRIVATE); + + $this->plugin->handleGetProperties( + $propFind, + $node + ); + + $this->assertEquals(null, $propFind->get(FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME)); + } + + public function testGetDisplayNamePropertyWhenNotPublishedButLoggedIn(): void { + $node = $this->createTestNode(File::class); + + $propFind = new PropFind( + '/dummyPath', + [ + FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME, + ], + 0 + ); + + $user = $this->createMock(User::class); + + $node->expects($this->once()) + ->method('getOwner') + ->willReturn($user); + + $loggedInUser = $this->createMock(User::class); + $this->userSession->expects($this->once()) + ->method('getUser') + ->willReturn($loggedInUser); + + $user + ->expects($this->once()) + ->method('getDisplayName') + ->willReturn('M. Foo'); + + $this->accountManager->expects($this->never()) + ->method('getAccount'); + $this->plugin->handleGetProperties( $propFind, $node ); - $this->assertEquals('"abc"', $propFind->get(self::GETETAG_PROPERTYNAME)); - $this->assertEquals('00000123instanceid', $propFind->get(self::FILEID_PROPERTYNAME)); - $this->assertEquals('123', $propFind->get(self::INTERNAL_FILEID_PROPERTYNAME)); - $this->assertEquals('1973-11-29T21:33:09+00:00', $propFind->get(self::CREATIONDATE_PROPERTYNAME)); - $this->assertEquals(null, $propFind->get(self::SIZE_PROPERTYNAME)); - $this->assertEquals('DWCKMSR', $propFind->get(self::PERMISSIONS_PROPERTYNAME)); - $this->assertEquals('http://example.com/', $propFind->get(self::DOWNLOADURL_PROPERTYNAME)); - $this->assertEquals('foo', $propFind->get(self::OWNER_ID_PROPERTYNAME)); - $this->assertEquals('M. Foo', $propFind->get(self::OWNER_DISPLAY_NAME_PROPERTYNAME)); - $this->assertEquals('my_fingerprint', $propFind->get(self::DATA_FINGERPRINT_PROPERTYNAME)); - $this->assertEquals([self::SIZE_PROPERTYNAME], $propFind->get404Properties()); + $this->assertEquals('M. Foo', $propFind->get(FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME)); } - public function testGetPropertiesStorageNotAvailable() { - /** @var \OCA\DAV\Connector\Sabre\File | \PHPUnit\Framework\MockObject\MockObject $node */ - $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File'); + public function testGetPropertiesStorageNotAvailable(): void { + /** @var File&MockObject $node */ + $node = $this->createTestNode(File::class); $propFind = new PropFind( '/dummyPath', [ - self::DOWNLOADURL_PROPERTYNAME, + FilesPlugin::DOWNLOADURL_PROPERTYNAME, ], 0 ); $node->expects($this->once()) ->method('getDirectDownload') - ->will($this->throwException(new StorageNotAvailableException())); + ->willThrowException(new StorageNotAvailableException()); $this->plugin->handleGetProperties( $propFind, $node ); - $this->assertEquals(null, $propFind->get(self::DOWNLOADURL_PROPERTYNAME)); + $this->assertEquals(null, $propFind->get(FilesPlugin::DOWNLOADURL_PROPERTYNAME)); } - public function testGetPublicPermissions() { + public function testGetPublicPermissions(): void { + /** @var IRequest&MockObject */ + $request = $this->createMock(IRequest::class); $this->plugin = new FilesPlugin( $this->tree, $this->config, - $this->getMockBuilder(IRequest::class) - ->disableOriginalConstructor() - ->getMock(), + $request, $this->previewManager, $this->userSession, - true); + $this->filenameValidator, + $this->accountManager, + true, + ); $this->plugin->initialize($this->server); $propFind = new PropFind( '/dummyPath', [ - self::PERMISSIONS_PROPERTYNAME, + FilesPlugin::PERMISSIONS_PROPERTYNAME, ], 0 ); - /** @var \OCA\DAV\Connector\Sabre\File | \PHPUnit\Framework\MockObject\MockObject $node */ - $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File'); + /** @var File&MockObject $node */ + $node = $this->createTestNode(File::class); $node->expects($this->any()) ->method('getDavPermissions') ->willReturn('DWCKMSR'); @@ -292,22 +338,22 @@ class FilesPluginTest extends TestCase { $node ); - $this->assertEquals('DWCKR', $propFind->get(self::PERMISSIONS_PROPERTYNAME)); + $this->assertEquals('DWCKR', $propFind->get(FilesPlugin::PERMISSIONS_PROPERTYNAME)); } - public function testGetPropertiesForDirectory() { - /** @var \OCA\DAV\Connector\Sabre\Directory | \PHPUnit\Framework\MockObject\MockObject $node */ - $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\Directory'); + public function testGetPropertiesForDirectory(): void { + /** @var Directory&MockObject $node */ + $node = $this->createTestNode(Directory::class); $propFind = new PropFind( '/dummyPath', [ - self::GETETAG_PROPERTYNAME, - self::FILEID_PROPERTYNAME, - self::SIZE_PROPERTYNAME, - self::PERMISSIONS_PROPERTYNAME, - self::DOWNLOADURL_PROPERTYNAME, - self::DATA_FINGERPRINT_PROPERTYNAME, + FilesPlugin::GETETAG_PROPERTYNAME, + FilesPlugin::FILEID_PROPERTYNAME, + FilesPlugin::SIZE_PROPERTYNAME, + FilesPlugin::PERMISSIONS_PROPERTYNAME, + FilesPlugin::DOWNLOADURL_PROPERTYNAME, + FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME, ], 0 ); @@ -321,20 +367,18 @@ class FilesPluginTest extends TestCase { $node ); - $this->assertEquals('"abc"', $propFind->get(self::GETETAG_PROPERTYNAME)); - $this->assertEquals('00000123instanceid', $propFind->get(self::FILEID_PROPERTYNAME)); - $this->assertEquals(1025, $propFind->get(self::SIZE_PROPERTYNAME)); - $this->assertEquals('DWCKMSR', $propFind->get(self::PERMISSIONS_PROPERTYNAME)); - $this->assertEquals(null, $propFind->get(self::DOWNLOADURL_PROPERTYNAME)); - $this->assertEquals('my_fingerprint', $propFind->get(self::DATA_FINGERPRINT_PROPERTYNAME)); - $this->assertEquals([self::DOWNLOADURL_PROPERTYNAME], $propFind->get404Properties()); + $this->assertEquals('"abc"', $propFind->get(FilesPlugin::GETETAG_PROPERTYNAME)); + $this->assertEquals('00000123instanceid', $propFind->get(FilesPlugin::FILEID_PROPERTYNAME)); + $this->assertEquals(1025, $propFind->get(FilesPlugin::SIZE_PROPERTYNAME)); + $this->assertEquals('DWCKMSR', $propFind->get(FilesPlugin::PERMISSIONS_PROPERTYNAME)); + $this->assertEquals(null, $propFind->get(FilesPlugin::DOWNLOADURL_PROPERTYNAME)); + $this->assertEquals('my_fingerprint', $propFind->get(FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME)); + $this->assertEquals([FilesPlugin::DOWNLOADURL_PROPERTYNAME], $propFind->get404Properties()); } - public function testGetPropertiesForRootDirectory() { - /** @var \OCA\DAV\Connector\Sabre\Directory|\PHPUnit\Framework\MockObject\MockObject $node */ - $node = $this->getMockBuilder(Directory::class) - ->disableOriginalConstructor() - ->getMock(); + public function testGetPropertiesForRootDirectory(): void { + /** @var Directory&MockObject $node */ + $node = $this->createMock(Directory::class); $node->expects($this->any())->method('getPath')->willReturn('/'); $fileInfo = $this->createMock(FileInfo::class); @@ -349,7 +393,7 @@ class FilesPluginTest extends TestCase { $propFind = new PropFind( '/', [ - self::DATA_FINGERPRINT_PROPERTYNAME, + FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME, ], 0 ); @@ -359,18 +403,15 @@ class FilesPluginTest extends TestCase { $node ); - $this->assertEquals('my_fingerprint', $propFind->get(self::DATA_FINGERPRINT_PROPERTYNAME)); + $this->assertEquals('my_fingerprint', $propFind->get(FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME)); } - public function testGetPropertiesWhenNoPermission() { + public function testGetPropertiesWhenNoPermission(): void { // No read permissions can be caused by files access control. // But we still want to load the directory list, so this is okay for us. // $this->expectException(\Sabre\DAV\Exception\NotFound::class); - - /** @var \OCA\DAV\Connector\Sabre\Directory|\PHPUnit\Framework\MockObject\MockObject $node */ - $node = $this->getMockBuilder(Directory::class) - ->disableOriginalConstructor() - ->getMock(); + /** @var Directory&MockObject $node */ + $node = $this->createMock(Directory::class); $node->expects($this->any())->method('getPath')->willReturn('/'); $fileInfo = $this->createMock(FileInfo::class); @@ -385,7 +426,7 @@ class FilesPluginTest extends TestCase { $propFind = new PropFind( '/test', [ - self::DATA_FINGERPRINT_PROPERTYNAME, + FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME, ], 0 ); @@ -398,8 +439,8 @@ class FilesPluginTest extends TestCase { $this->addToAssertionCount(1); } - public function testUpdateProps() { - $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File'); + public function testUpdateProps(): void { + $node = $this->createTestNode(File::class); $testDate = 'Fri, 13 Feb 2015 00:01:02 GMT'; $testCreationDate = '2007-08-31T16:47+00:00'; @@ -419,11 +460,12 @@ class FilesPluginTest extends TestCase { // properties to set $propPatch = new PropPatch([ - self::GETETAG_PROPERTYNAME => 'newetag', - self::LASTMODIFIED_PROPERTYNAME => $testDate, - self::CREATIONDATE_PROPERTYNAME => $testCreationDate, + FilesPlugin::GETETAG_PROPERTYNAME => 'newetag', + FilesPlugin::LASTMODIFIED_PROPERTYNAME => $testDate, + FilesPlugin::CREATIONDATE_PROPERTYNAME => $testCreationDate, ]); + $this->plugin->handleUpdateProperties( '/dummypath', $propPatch @@ -434,19 +476,19 @@ class FilesPluginTest extends TestCase { $this->assertEmpty($propPatch->getRemainingMutations()); $result = $propPatch->getResult(); - $this->assertEquals(200, $result[self::LASTMODIFIED_PROPERTYNAME]); - $this->assertEquals(200, $result[self::GETETAG_PROPERTYNAME]); - $this->assertEquals(200, $result[self::CREATIONDATE_PROPERTYNAME]); + $this->assertEquals(200, $result[FilesPlugin::LASTMODIFIED_PROPERTYNAME]); + $this->assertEquals(200, $result[FilesPlugin::GETETAG_PROPERTYNAME]); + $this->assertEquals(200, $result[FilesPlugin::CREATIONDATE_PROPERTYNAME]); } - public function testUpdatePropsForbidden() { + public function testUpdatePropsForbidden(): void { $propPatch = new PropPatch([ - self::OWNER_ID_PROPERTYNAME => 'user2', - self::OWNER_DISPLAY_NAME_PROPERTYNAME => 'User Two', - self::FILEID_PROPERTYNAME => 12345, - self::PERMISSIONS_PROPERTYNAME => 'C', - self::SIZE_PROPERTYNAME => 123, - self::DOWNLOADURL_PROPERTYNAME => 'http://example.com/', + FilesPlugin::OWNER_ID_PROPERTYNAME => 'user2', + FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME => 'User Two', + FilesPlugin::FILEID_PROPERTYNAME => 12345, + FilesPlugin::PERMISSIONS_PROPERTYNAME => 'C', + FilesPlugin::SIZE_PROPERTYNAME => 123, + FilesPlugin::DOWNLOADURL_PROPERTYNAME => 'http://example.com/', ]); $this->plugin->handleUpdateProperties( @@ -459,16 +501,16 @@ class FilesPluginTest extends TestCase { $this->assertEmpty($propPatch->getRemainingMutations()); $result = $propPatch->getResult(); - $this->assertEquals(403, $result[self::OWNER_ID_PROPERTYNAME]); - $this->assertEquals(403, $result[self::OWNER_DISPLAY_NAME_PROPERTYNAME]); - $this->assertEquals(403, $result[self::FILEID_PROPERTYNAME]); - $this->assertEquals(403, $result[self::PERMISSIONS_PROPERTYNAME]); - $this->assertEquals(403, $result[self::SIZE_PROPERTYNAME]); - $this->assertEquals(403, $result[self::DOWNLOADURL_PROPERTYNAME]); + $this->assertEquals(403, $result[FilesPlugin::OWNER_ID_PROPERTYNAME]); + $this->assertEquals(403, $result[FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME]); + $this->assertEquals(403, $result[FilesPlugin::FILEID_PROPERTYNAME]); + $this->assertEquals(403, $result[FilesPlugin::PERMISSIONS_PROPERTYNAME]); + $this->assertEquals(403, $result[FilesPlugin::SIZE_PROPERTYNAME]); + $this->assertEquals(403, $result[FilesPlugin::DOWNLOADURL_PROPERTYNAME]); } /** - * Testcase from https://github.com/owncloud/core/issues/5251 + * Test case from https://github.com/owncloud/core/issues/5251 * * |-FolderA * |-text.txt @@ -478,70 +520,126 @@ class FilesPluginTest extends TestCase { * Thus moving /FolderA/test.txt to /test.txt should fail already on that check * */ - public function testMoveSrcNotDeletable() { + public function testMoveSrcNotDeletable(): void { $this->expectException(\Sabre\DAV\Exception\Forbidden::class); $this->expectExceptionMessage('FolderA/test.txt cannot be deleted'); - $fileInfoFolderATestTXT = $this->getMockBuilder(FileInfo::class) - ->disableOriginalConstructor() - ->getMock(); + $fileInfoFolderATestTXT = $this->createMock(FileInfo::class); $fileInfoFolderATestTXT->expects($this->once()) ->method('isDeletable') ->willReturn(false); - $node = $this->getMockBuilder(Node::class) - ->disableOriginalConstructor() - ->getMock(); - $node->expects($this->once()) + $node = $this->createMock(Node::class); + $node->expects($this->atLeastOnce()) ->method('getFileInfo') ->willReturn($fileInfoFolderATestTXT); - $this->tree->expects($this->once())->method('getNodeForPath') + $this->tree->expects($this->atLeastOnce()) + ->method('getNodeForPath') ->willReturn($node); $this->plugin->checkMove('FolderA/test.txt', 'test.txt'); } - public function testMoveSrcDeletable() { - $fileInfoFolderATestTXT = $this->getMockBuilder(FileInfo::class) - ->disableOriginalConstructor() - ->getMock(); + public function testMoveSrcDeletable(): void { + $fileInfoFolderATestTXT = $this->createMock(FileInfo::class); $fileInfoFolderATestTXT->expects($this->once()) ->method('isDeletable') ->willReturn(true); - $node = $this->getMockBuilder(Node::class) - ->disableOriginalConstructor() - ->getMock(); - $node->expects($this->once()) + $node = $this->createMock(Node::class); + $node->expects($this->atLeastOnce()) ->method('getFileInfo') ->willReturn($fileInfoFolderATestTXT); - $this->tree->expects($this->once())->method('getNodeForPath') + $this->tree->expects($this->atLeastOnce()) + ->method('getNodeForPath') ->willReturn($node); $this->plugin->checkMove('FolderA/test.txt', 'test.txt'); } - - public function testMoveSrcNotExist() { + public function testMoveSrcNotExist(): void { $this->expectException(\Sabre\DAV\Exception\NotFound::class); $this->expectExceptionMessage('FolderA/test.txt does not exist'); - $node = $this->getMockBuilder(Node::class) - ->disableOriginalConstructor() - ->getMock(); - $node->expects($this->once()) + $node = $this->createMock(Node::class); + $node->expects($this->atLeastOnce()) ->method('getFileInfo') ->willReturn(null); - $this->tree->expects($this->once())->method('getNodeForPath') + $this->tree->expects($this->atLeastOnce()) + ->method('getNodeForPath') ->willReturn($node); $this->plugin->checkMove('FolderA/test.txt', 'test.txt'); } - public function downloadHeadersProvider() { + public function testMoveDestinationInvalid(): void { + $this->expectException(InvalidPath::class); + $this->expectExceptionMessage('Mocked exception'); + + $fileInfoFolderATestTXT = $this->createMock(FileInfo::class); + $fileInfoFolderATestTXT->expects(self::any()) + ->method('isDeletable') + ->willReturn(true); + + $node = $this->createMock(Node::class); + $node->expects($this->atLeastOnce()) + ->method('getFileInfo') + ->willReturn($fileInfoFolderATestTXT); + + $this->tree->expects($this->atLeastOnce()) + ->method('getNodeForPath') + ->willReturn($node); + + $this->filenameValidator->expects(self::once()) + ->method('validateFilename') + ->with('invalid\\path.txt') + ->willThrowException(new InvalidPathException('Mocked exception')); + + $this->plugin->checkMove('FolderA/test.txt', 'invalid\\path.txt'); + } + + public function testCopySrcNotExist(): void { + $this->expectException(\Sabre\DAV\Exception\NotFound::class); + $this->expectExceptionMessage('FolderA/test.txt does not exist'); + + $node = $this->createMock(Node::class); + $node->expects($this->atLeastOnce()) + ->method('getFileInfo') + ->willReturn(null); + + $this->tree->expects($this->atLeastOnce()) + ->method('getNodeForPath') + ->willReturn($node); + + $this->plugin->checkCopy('FolderA/test.txt', 'test.txt'); + } + + public function testCopyDestinationInvalid(): void { + $this->expectException(InvalidPath::class); + $this->expectExceptionMessage('Mocked exception'); + + $fileInfoFolderATestTXT = $this->createMock(FileInfo::class); + $node = $this->createMock(Node::class); + $node->expects($this->atLeastOnce()) + ->method('getFileInfo') + ->willReturn($fileInfoFolderATestTXT); + + $this->tree->expects($this->atLeastOnce()) + ->method('getNodeForPath') + ->willReturn($node); + + $this->filenameValidator->expects(self::once()) + ->method('validateFilename') + ->with('invalid\\path.txt') + ->willThrowException(new InvalidPathException('Mocked exception')); + + $this->plugin->checkCopy('FolderA/test.txt', 'invalid\\path.txt'); + } + + public static function downloadHeadersProvider(): array { return [ [ false, @@ -554,25 +652,17 @@ class FilesPluginTest extends TestCase { ]; } - /** - * @dataProvider downloadHeadersProvider - */ - public function testDownloadHeaders($isClumsyAgent, $contentDispositionHeader) { - $request = $this->getMockBuilder(RequestInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $response = $this->getMockBuilder(ResponseInterface::class) - ->disableOriginalConstructor() - ->getMock(); + #[\PHPUnit\Framework\Attributes\DataProvider('downloadHeadersProvider')] + public function testDownloadHeaders(bool $isClumsyAgent, string $contentDispositionHeader): void { + $request = $this->createMock(RequestInterface::class); + $response = $this->createMock(ResponseInterface::class); $request ->expects($this->once()) ->method('getPath') ->willReturn('test/somefile.xml'); - $node = $this->getMockBuilder(File::class) - ->disableOriginalConstructor() - ->getMock(); + $node = $this->createMock(File::class); $node ->expects($this->once()) ->method('getName') @@ -589,25 +679,29 @@ class FilesPluginTest extends TestCase { ->method('isUserAgent') ->willReturn($isClumsyAgent); + $calls = [ + ['Content-Disposition', $contentDispositionHeader], + ['X-Accel-Buffering', 'no'], + ]; $response - ->expects($this->exactly(2)) + ->expects($this->exactly(count($calls))) ->method('addHeader') - ->withConsecutive( - ['Content-Disposition', $contentDispositionHeader], - ['X-Accel-Buffering', 'no'] - ); + ->willReturnCallback(function () use (&$calls): void { + $expected = array_shift($calls); + $this->assertSame($expected, func_get_args()); + }); $this->plugin->httpGet($request, $response); } - public function testHasPreview() { - /** @var \OCA\DAV\Connector\Sabre\Directory | \PHPUnit\Framework\MockObject\MockObject $node */ - $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\Directory'); + public function testHasPreview(): void { + /** @var Directory&MockObject $node */ + $node = $this->createTestNode(Directory::class); $propFind = new PropFind( '/dummyPath', [ - self::HAS_PREVIEW_PROPERTYNAME + FilesPlugin::HAS_PREVIEW_PROPERTYNAME ], 0 ); @@ -621,6 +715,6 @@ class FilesPluginTest extends TestCase { $node ); - $this->assertEquals("false", $propFind->get(self::HAS_PREVIEW_PROPERTYNAME)); + $this->assertEquals('false', $propFind->get(FilesPlugin::HAS_PREVIEW_PROPERTYNAME)); } } diff --git a/apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php index f73434b33b6..176949f999c 100644 --- a/apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php @@ -1,38 +1,23 @@ <?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 Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @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\Connector\Sabre; use OC\Files\View; use OCA\DAV\Connector\Sabre\Directory; +use OCA\DAV\Connector\Sabre\FilesPlugin; use OCA\DAV\Connector\Sabre\FilesReportPlugin as FilesReportPluginImplementation; +use OCP\Accounts\IAccountManager; use OCP\App\IAppManager; use OCP\Files\File; use OCP\Files\FileInfo; use OCP\Files\Folder; +use OCP\Files\IFilenameValidator; use OCP\IConfig; use OCP\IGroupManager; use OCP\IPreview; @@ -44,95 +29,59 @@ use OCP\IUserSession; use OCP\SystemTag\ISystemTag; use OCP\SystemTag\ISystemTagManager; use OCP\SystemTag\ISystemTagObjectMapper; +use OCP\SystemTag\TagNotFoundException; +use PHPUnit\Framework\MockObject\MockObject; use Sabre\DAV\INode; +use Sabre\DAV\Server; use Sabre\DAV\Tree; use Sabre\HTTP\ResponseInterface; class FilesReportPluginTest extends \Test\TestCase { - /** @var \Sabre\DAV\Server|\PHPUnit\Framework\MockObject\MockObject */ - private $server; - - /** @var \Sabre\DAV\Tree|\PHPUnit\Framework\MockObject\MockObject */ - private $tree; - - /** @var ISystemTagObjectMapper|\PHPUnit\Framework\MockObject\MockObject */ - private $tagMapper; - - /** @var ISystemTagManager|\PHPUnit\Framework\MockObject\MockObject */ - private $tagManager; - - /** @var ITags|\PHPUnit\Framework\MockObject\MockObject */ - private $privateTags; - - /** @var \OCP\IUserSession */ - private $userSession; - - /** @var FilesReportPluginImplementation */ - private $plugin; - /** @var View|\PHPUnit\Framework\MockObject\MockObject **/ - private $view; - - /** @var IGroupManager|\PHPUnit\Framework\MockObject\MockObject **/ - private $groupManager; - - /** @var Folder|\PHPUnit\Framework\MockObject\MockObject **/ - private $userFolder; - - /** @var IPreview|\PHPUnit\Framework\MockObject\MockObject * */ - private $previewManager; - - /** @var IAppManager|\PHPUnit\Framework\MockObject\MockObject * */ - private $appManager; + private \Sabre\DAV\Server&MockObject $server; + private Tree&MockObject $tree; + private ISystemTagObjectMapper&MockObject $tagMapper; + private ISystemTagManager&MockObject $tagManager; + private ITags&MockObject $privateTags; + private ITagManager&MockObject $privateTagManager; + private IUserSession&MockObject $userSession; + private FilesReportPluginImplementation $plugin; + private View&MockObject $view; + private IGroupManager&MockObject $groupManager; + private Folder&MockObject $userFolder; + private IPreview&MockObject $previewManager; + private IAppManager&MockObject $appManager; protected function setUp(): void { parent::setUp(); - $this->tree = $this->getMockBuilder(Tree::class) - ->disableOriginalConstructor() - ->getMock(); - $this->view = $this->getMockBuilder(View::class) - ->disableOriginalConstructor() - ->getMock(); + $this->tree = $this->createMock(Tree::class); + $this->view = $this->createMock(View::class); - $this->server = $this->getMockBuilder('\Sabre\DAV\Server') + $this->server = $this->getMockBuilder(Server::class) ->setConstructorArgs([$this->tree]) - ->setMethods(['getRequestUri', 'getBaseUri']) + ->onlyMethods(['getRequestUri', 'getBaseUri']) ->getMock(); $this->server->expects($this->any()) ->method('getBaseUri') ->willReturn('http://example.com/owncloud/remote.php/dav'); - $this->groupManager = $this->getMockBuilder(IGroupManager::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->userFolder = $this->getMockBuilder(Folder::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->previewManager = $this->getMockBuilder(IPreview::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->appManager = $this->getMockBuilder(IAppManager::class) - ->disableOriginalConstructor() - ->getMock(); - + $this->groupManager = $this->createMock(IGroupManager::class); + $this->userFolder = $this->createMock(Folder::class); + $this->previewManager = $this->createMock(IPreview::class); + $this->appManager = $this->createMock(IAppManager::class); $this->tagManager = $this->createMock(ISystemTagManager::class); $this->tagMapper = $this->createMock(ISystemTagObjectMapper::class); $this->userSession = $this->createMock(IUserSession::class); $this->privateTags = $this->createMock(ITags::class); - $privateTagManager = $this->createMock(ITagManager::class); - $privateTagManager->expects($this->any()) + $this->privateTagManager = $this->createMock(ITagManager::class); + $this->privateTagManager->expects($this->any()) ->method('load') ->with('files') ->willReturn($this->privateTags); - $user = $this->getMockBuilder(IUser::class) - ->disableOriginalConstructor() - ->getMock(); + $user = $this->createMock(IUser::class); $user->expects($this->any()) ->method('getUID') ->willReturn('testuser'); @@ -145,7 +94,7 @@ class FilesReportPluginTest extends \Test\TestCase { $this->view, $this->tagManager, $this->tagMapper, - $privateTagManager, + $this->privateTagManager, $this->userSession, $this->groupManager, $this->userFolder, @@ -153,17 +102,13 @@ class FilesReportPluginTest extends \Test\TestCase { ); } - public function testOnReportInvalidNode() { + public function testOnReportInvalidNode(): void { $path = 'totally/unrelated/13'; $this->tree->expects($this->any()) ->method('getNodeForPath') ->with('/' . $path) - ->willReturn( - $this->getMockBuilder(INode::class) - ->disableOriginalConstructor() - ->getMock() - ); + ->willReturn($this->createMock(INode::class)); $this->server->expects($this->any()) ->method('getRequestUri') @@ -173,7 +118,7 @@ class FilesReportPluginTest extends \Test\TestCase { $this->assertNull($this->plugin->onReport(FilesReportPluginImplementation::REPORT_NAME, [], '/' . $path)); } - public function testOnReportInvalidReportName() { + public function testOnReportInvalidReportName(): void { $path = 'test'; $this->tree->expects($this->any()) @@ -193,7 +138,7 @@ class FilesReportPluginTest extends \Test\TestCase { $this->assertNull($this->plugin->onReport('{whoever}whatever', [], '/' . $path)); } - public function testOnReport() { + public function testOnReport(): void { $path = 'test'; $parameters = [ @@ -217,25 +162,12 @@ class FilesReportPluginTest extends \Test\TestCase { ->method('isAdmin') ->willReturn(true); - $this->tagMapper->expects($this->at(0)) - ->method('getObjectIdsForTags') - ->with('123', 'files') - ->willReturn(['111', '222']); - $this->tagMapper->expects($this->at(1)) - ->method('getObjectIdsForTags') - ->with('456', 'files') - ->willReturn(['111', '222', '333']); - - $reportTargetNode = $this->getMockBuilder(Directory::class) - ->disableOriginalConstructor() - ->getMock(); + $reportTargetNode = $this->createMock(Directory::class); $reportTargetNode->expects($this->any()) ->method('getPath') ->willReturn(''); - $response = $this->getMockBuilder(ResponseInterface::class) - ->disableOriginalConstructor() - ->getMock(); + $response = $this->createMock(ResponseInterface::class); $response->expects($this->once()) ->method('setHeader') @@ -253,21 +185,41 @@ class FilesReportPluginTest extends \Test\TestCase { ->with('/' . $path) ->willReturn($reportTargetNode); - $filesNode1 = $this->getMockBuilder(Folder::class) - ->disableOriginalConstructor() - ->getMock(); - $filesNode2 = $this->getMockBuilder(File::class) - ->disableOriginalConstructor() - ->getMock(); + $filesNode1 = $this->createMock(File::class); + $filesNode1->expects($this->any()) + ->method('getSize') + ->willReturn(12); + $filesNode2 = $this->createMock(Folder::class); + $filesNode2->expects($this->any()) + ->method('getSize') + ->willReturn(10); + + $tag123 = $this->createMock(ISystemTag::class); + $tag123->expects($this->any()) + ->method('getName') + ->willReturn('OneTwoThree'); + $tag123->expects($this->any()) + ->method('isUserVisible') + ->willReturn(true); + $tag456 = $this->createMock(ISystemTag::class); + $tag456->expects($this->any()) + ->method('getName') + ->willReturn('FourFiveSix'); + $tag456->expects($this->any()) + ->method('isUserVisible') + ->willReturn(true); - $this->userFolder->expects($this->at(0)) - ->method('getById') - ->with('111') - ->willReturn([$filesNode1]); - $this->userFolder->expects($this->at(1)) - ->method('getById') - ->with('222') - ->willReturn([$filesNode2]); + $this->tagManager->expects($this->once()) + ->method('getTagsByIds') + ->with(['123', '456']) + ->willReturn([$tag123, $tag456]); + + $this->userFolder->expects($this->exactly(2)) + ->method('searchBySystemTag') + ->willReturnMap([ + ['OneTwoThree', 'testuser', 0, 0, [$filesNode1]], + ['FourFiveSix', 'testuser', 0, 0, [$filesNode2]], + ]); $this->server->expects($this->any()) ->method('getRequestUri') @@ -278,110 +230,88 @@ class FilesReportPluginTest extends \Test\TestCase { $this->assertFalse($this->plugin->onReport(FilesReportPluginImplementation::REPORT_NAME, $parameters, '/' . $path)); } - public function testFindNodesByFileIdsRoot() { - $filesNode1 = $this->getMockBuilder(Folder::class) - ->disableOriginalConstructor() - ->getMock(); + public function testFindNodesByFileIdsRoot(): void { + $filesNode1 = $this->createMock(Folder::class); $filesNode1->expects($this->once()) ->method('getName') ->willReturn('first node'); - $filesNode2 = $this->getMockBuilder(File::class) - ->disableOriginalConstructor() - ->getMock(); + $filesNode2 = $this->createMock(File::class); $filesNode2->expects($this->once()) ->method('getName') ->willReturn('second node'); - $reportTargetNode = $this->getMockBuilder(Directory::class) - ->disableOriginalConstructor() - ->getMock(); + $reportTargetNode = $this->createMock(Directory::class); $reportTargetNode->expects($this->any()) ->method('getPath') ->willReturn('/'); - $this->userFolder->expects($this->at(0)) - ->method('getById') - ->with('111') - ->willReturn([$filesNode1]); - $this->userFolder->expects($this->at(1)) - ->method('getById') - ->with('222') - ->willReturn([$filesNode2]); + $this->userFolder->expects($this->exactly(2)) + ->method('getFirstNodeById') + ->willReturnMap([ + [111, $filesNode1], + [222, $filesNode2], + ]); - /** @var \OCA\DAV\Connector\Sabre\Directory|\PHPUnit\Framework\MockObject\MockObject $reportTargetNode */ + /** @var Directory&MockObject $reportTargetNode */ $result = $this->plugin->findNodesByFileIds($reportTargetNode, ['111', '222']); $this->assertCount(2, $result); - $this->assertInstanceOf('\OCA\DAV\Connector\Sabre\Directory', $result[0]); + $this->assertInstanceOf(Directory::class, $result[0]); $this->assertEquals('first node', $result[0]->getName()); - $this->assertInstanceOf('\OCA\DAV\Connector\Sabre\File', $result[1]); + $this->assertInstanceOf(\OCA\DAV\Connector\Sabre\File::class, $result[1]); $this->assertEquals('second node', $result[1]->getName()); } - public function testFindNodesByFileIdsSubDir() { - $filesNode1 = $this->getMockBuilder(Folder::class) - ->disableOriginalConstructor() - ->getMock(); + public function testFindNodesByFileIdsSubDir(): void { + $filesNode1 = $this->createMock(Folder::class); $filesNode1->expects($this->once()) ->method('getName') ->willReturn('first node'); - $filesNode2 = $this->getMockBuilder(File::class) - ->disableOriginalConstructor() - ->getMock(); + $filesNode2 = $this->createMock(File::class); $filesNode2->expects($this->once()) ->method('getName') ->willReturn('second node'); - $reportTargetNode = $this->getMockBuilder(Directory::class) - ->disableOriginalConstructor() - ->getMock(); + $reportTargetNode = $this->createMock(Directory::class); $reportTargetNode->expects($this->any()) ->method('getPath') ->willReturn('/sub1/sub2'); - $subNode = $this->getMockBuilder(Folder::class) - ->disableOriginalConstructor() - ->getMock(); + $subNode = $this->createMock(Folder::class); - $this->userFolder->expects($this->at(0)) + $this->userFolder->expects($this->once()) ->method('get') ->with('/sub1/sub2') ->willReturn($subNode); - $subNode->expects($this->at(0)) - ->method('getById') - ->with('111') - ->willReturn([$filesNode1]); - $subNode->expects($this->at(1)) - ->method('getById') - ->with('222') - ->willReturn([$filesNode2]); + $subNode->expects($this->exactly(2)) + ->method('getFirstNodeById') + ->willReturnMap([ + [111, $filesNode1], + [222, $filesNode2], + ]); - /** @var \OCA\DAV\Connector\Sabre\Directory|\PHPUnit\Framework\MockObject\MockObject $reportTargetNode */ + /** @var Directory&MockObject $reportTargetNode */ $result = $this->plugin->findNodesByFileIds($reportTargetNode, ['111', '222']); $this->assertCount(2, $result); - $this->assertInstanceOf('\OCA\DAV\Connector\Sabre\Directory', $result[0]); + $this->assertInstanceOf(Directory::class, $result[0]); $this->assertEquals('first node', $result[0]->getName()); - $this->assertInstanceOf('\OCA\DAV\Connector\Sabre\File', $result[1]); + $this->assertInstanceOf(\OCA\DAV\Connector\Sabre\File::class, $result[1]); $this->assertEquals('second node', $result[1]->getName()); } - public function testPrepareResponses() { + public function testPrepareResponses(): void { $requestedProps = ['{DAV:}getcontentlength', '{http://owncloud.org/ns}fileid', '{DAV:}resourcetype']; $fileInfo = $this->createMock(FileInfo::class); $fileInfo->method('isReadable')->willReturn(true); - $node1 = $this->getMockBuilder(Directory::class) - ->disableOriginalConstructor() - ->getMock(); - $node2 = $this->getMockBuilder(\OCA\DAV\Connector\Sabre\File::class) - ->disableOriginalConstructor() - ->getMock(); + $node1 = $this->createMock(Directory::class); + $node2 = $this->createMock(\OCA\DAV\Connector\Sabre\File::class); $node1->expects($this->once()) ->method('getInternalFileId') @@ -401,17 +331,19 @@ class FilesReportPluginTest extends \Test\TestCase { ->willReturn('/sub/node2'); $node2->method('getFileInfo')->willReturn($fileInfo); - $config = $this->getMockBuilder(IConfig::class) - ->disableOriginalConstructor() - ->getMock(); + $config = $this->createMock(IConfig::class); + $validator = $this->createMock(IFilenameValidator::class); + $accountManager = $this->createMock(IAccountManager::class); $this->server->addPlugin( - new \OCA\DAV\Connector\Sabre\FilesPlugin( + new FilesPlugin( $this->tree, $config, $this->createMock(IRequest::class), $this->previewManager, - $this->createMock(IUserSession::class) + $this->createMock(IUserSession::class), + $validator, + $accountManager, ) ); $this->plugin->initialize($this->server); @@ -419,9 +351,6 @@ class FilesReportPluginTest extends \Test\TestCase { $this->assertCount(2, $responses); - $this->assertEquals(200, $responses[0]->getHttpStatus()); - $this->assertEquals(200, $responses[1]->getHttpStatus()); - $this->assertEquals('http://example.com/owncloud/remote.php/dav/files/username/node1', $responses[0]->getHref()); $this->assertEquals('http://example.com/owncloud/remote.php/dav/files/username/sub/node2', $responses[1]->getHref()); @@ -439,41 +368,97 @@ class FilesReportPluginTest extends \Test\TestCase { $this->assertCount(0, $props2[200]['{DAV:}resourcetype']->getValue()); } - public function testProcessFilterRulesSingle() { + public function testProcessFilterRulesSingle(): void { $this->groupManager->expects($this->any()) ->method('isAdmin') ->willReturn(true); - $this->tagMapper->expects($this->exactly(1)) - ->method('getObjectIdsForTags') - ->withConsecutive( - ['123', 'files'] - ) - ->willReturnMap([ - ['123', 'files', 0, '', ['111', '222']], - ]); - $rules = [ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], ]; - $this->assertEquals(['111', '222'], $this->invokePrivate($this->plugin, 'processFilterRules', [$rules])); + $filesNode1 = $this->createMock(File::class); + $filesNode1->expects($this->any()) + ->method('getSize') + ->willReturn(12); + $filesNode2 = $this->createMock(Folder::class); + $filesNode2->expects($this->any()) + ->method('getSize') + ->willReturn(10); + + $tag123 = $this->createMock(ISystemTag::class); + $tag123->expects($this->any()) + ->method('getName') + ->willReturn('OneTwoThree'); + $tag123->expects($this->any()) + ->method('isUserVisible') + ->willReturn(true); + + $this->tagManager->expects($this->once()) + ->method('getTagsByIds') + ->with(['123']) + ->willReturn([$tag123]); + + $this->userFolder->expects($this->once()) + ->method('searchBySystemTag') + ->with('OneTwoThree') + ->willReturn([$filesNode1, $filesNode2]); + + $this->assertEquals([$filesNode1, $filesNode2], self::invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, 0, 0])); } - public function testProcessFilterRulesAndCondition() { + public function testProcessFilterRulesAndCondition(): void { $this->groupManager->expects($this->any()) ->method('isAdmin') ->willReturn(true); - $this->tagMapper->expects($this->exactly(2)) - ->method('getObjectIdsForTags') - ->withConsecutive( - ['123', 'files'], - ['456', 'files'] - ) + $filesNode1 = $this->createMock(File::class); + $filesNode1->expects($this->any()) + ->method('getSize') + ->willReturn(12); + $filesNode1->expects($this->any()) + ->method('getId') + ->willReturn(111); + $filesNode2 = $this->createMock(Folder::class); + $filesNode2->expects($this->any()) + ->method('getSize') + ->willReturn(10); + $filesNode2->expects($this->any()) + ->method('getId') + ->willReturn(222); + $filesNode3 = $this->createMock(File::class); + $filesNode3->expects($this->any()) + ->method('getSize') + ->willReturn(14); + $filesNode3->expects($this->any()) + ->method('getId') + ->willReturn(333); + + $tag123 = $this->createMock(ISystemTag::class); + $tag123->expects($this->any()) + ->method('getName') + ->willReturn('OneTwoThree'); + $tag123->expects($this->any()) + ->method('isUserVisible') + ->willReturn(true); + $tag456 = $this->createMock(ISystemTag::class); + $tag456->expects($this->any()) + ->method('getName') + ->willReturn('FourFiveSix'); + $tag456->expects($this->any()) + ->method('isUserVisible') + ->willReturn(true); + + $this->tagManager->expects($this->once()) + ->method('getTagsByIds') + ->with(['123', '456']) + ->willReturn([$tag123, $tag456]); + + $this->userFolder->expects($this->exactly(2)) + ->method('searchBySystemTag') ->willReturnMap([ - ['123', 'files', 0, '', ['111', '222']], - ['456', 'files', 0, '', ['222', '333']], + ['OneTwoThree', 'testuser', 0, 0, [$filesNode1, $filesNode2]], + ['FourFiveSix', 'testuser', 0, 0, [$filesNode2, $filesNode3]], ]); $rules = [ @@ -481,23 +466,54 @@ class FilesReportPluginTest extends \Test\TestCase { ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], ]; - $this->assertEquals(['222'], array_values($this->invokePrivate($this->plugin, 'processFilterRules', [$rules]))); + $this->assertEquals([$filesNode2], array_values(self::invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null]))); } - public function testProcessFilterRulesAndConditionWithOneEmptyResult() { + public function testProcessFilterRulesAndConditionWithOneEmptyResult(): void { $this->groupManager->expects($this->any()) ->method('isAdmin') ->willReturn(true); - $this->tagMapper->expects($this->exactly(2)) - ->method('getObjectIdsForTags') - ->withConsecutive( - ['123', 'files'], - ['456', 'files'] - ) + $filesNode1 = $this->createMock(File::class); + $filesNode1->expects($this->any()) + ->method('getSize') + ->willReturn(12); + $filesNode1->expects($this->any()) + ->method('getId') + ->willReturn(111); + $filesNode2 = $this->createMock(Folder::class); + $filesNode2->expects($this->any()) + ->method('getSize') + ->willReturn(10); + $filesNode2->expects($this->any()) + ->method('getId') + ->willReturn(222); + + $tag123 = $this->createMock(ISystemTag::class); + $tag123->expects($this->any()) + ->method('getName') + ->willReturn('OneTwoThree'); + $tag123->expects($this->any()) + ->method('isUserVisible') + ->willReturn(true); + $tag456 = $this->createMock(ISystemTag::class); + $tag456->expects($this->any()) + ->method('getName') + ->willReturn('FourFiveSix'); + $tag456->expects($this->any()) + ->method('isUserVisible') + ->willReturn(true); + + $this->tagManager->expects($this->once()) + ->method('getTagsByIds') + ->with(['123', '456']) + ->willReturn([$tag123, $tag456]); + + $this->userFolder->expects($this->exactly(2)) + ->method('searchBySystemTag') ->willReturnMap([ - ['123', 'files', 0, '', ['111', '222']], - ['456', 'files', 0, '', []], + ['OneTwoThree', 'testuser', 0, 0, [$filesNode1, $filesNode2]], + ['FourFiveSix', 'testuser', 0, 0, []], ]); $rules = [ @@ -505,23 +521,53 @@ class FilesReportPluginTest extends \Test\TestCase { ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], ]; - $this->assertEquals([], array_values($this->invokePrivate($this->plugin, 'processFilterRules', [$rules]))); + $this->assertEquals([], self::invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null])); } - public function testProcessFilterRulesAndConditionWithFirstEmptyResult() { + public function testProcessFilterRulesAndConditionWithFirstEmptyResult(): void { $this->groupManager->expects($this->any()) ->method('isAdmin') ->willReturn(true); - $this->tagMapper->expects($this->exactly(1)) - ->method('getObjectIdsForTags') - ->withConsecutive( - ['123', 'files'], - ['456', 'files'] - ) + $filesNode1 = $this->createMock(File::class); + $filesNode1->expects($this->any()) + ->method('getSize') + ->willReturn(12); + $filesNode1->expects($this->any()) + ->method('getId') + ->willReturn(111); + $filesNode2 = $this->createMock(Folder::class); + $filesNode2->expects($this->any()) + ->method('getSize') + ->willReturn(10); + $filesNode2->expects($this->any()) + ->method('getId') + ->willReturn(222); + + $tag123 = $this->createMock(ISystemTag::class); + $tag123->expects($this->any()) + ->method('getName') + ->willReturn('OneTwoThree'); + $tag123->expects($this->any()) + ->method('isUserVisible') + ->willReturn(true); + $tag456 = $this->createMock(ISystemTag::class); + $tag456->expects($this->any()) + ->method('getName') + ->willReturn('FourFiveSix'); + $tag456->expects($this->any()) + ->method('isUserVisible') + ->willReturn(true); + + $this->tagManager->expects($this->once()) + ->method('getTagsByIds') + ->with(['123', '456']) + ->willReturn([$tag123, $tag456]); + + $this->userFolder->expects($this->once()) + ->method('searchBySystemTag') ->willReturnMap([ - ['123', 'files', 0, '', []], - ['456', 'files', 0, '', ['111', '222']], + ['OneTwoThree', 'testuser', 0, 0, []], ]); $rules = [ @@ -529,25 +575,68 @@ class FilesReportPluginTest extends \Test\TestCase { ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], ]; - $this->assertEquals([], array_values($this->invokePrivate($this->plugin, 'processFilterRules', [$rules]))); + $this->assertEquals([], self::invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null])); } - public function testProcessFilterRulesAndConditionWithEmptyMidResult() { + public function testProcessFilterRulesAndConditionWithEmptyMidResult(): void { $this->groupManager->expects($this->any()) ->method('isAdmin') ->willReturn(true); - $this->tagMapper->expects($this->exactly(2)) - ->method('getObjectIdsForTags') - ->withConsecutive( - ['123', 'files'], - ['456', 'files'], - ['789', 'files'] - ) + $filesNode1 = $this->createMock(File::class); + $filesNode1->expects($this->any()) + ->method('getSize') + ->willReturn(12); + $filesNode1->expects($this->any()) + ->method('getId') + ->willReturn(111); + $filesNode2 = $this->createMock(Folder::class); + $filesNode2->expects($this->any()) + ->method('getSize') + ->willReturn(10); + $filesNode2->expects($this->any()) + ->method('getId') + ->willReturn(222); + $filesNode3 = $this->createMock(Folder::class); + $filesNode3->expects($this->any()) + ->method('getSize') + ->willReturn(13); + $filesNode3->expects($this->any()) + ->method('getId') + ->willReturn(333); + + $tag123 = $this->createMock(ISystemTag::class); + $tag123->expects($this->any()) + ->method('getName') + ->willReturn('OneTwoThree'); + $tag123->expects($this->any()) + ->method('isUserVisible') + ->willReturn(true); + $tag456 = $this->createMock(ISystemTag::class); + $tag456->expects($this->any()) + ->method('getName') + ->willReturn('FourFiveSix'); + $tag456->expects($this->any()) + ->method('isUserVisible') + ->willReturn(true); + $tag789 = $this->createMock(ISystemTag::class); + $tag789->expects($this->any()) + ->method('getName') + ->willReturn('SevenEightNine'); + $tag789->expects($this->any()) + ->method('isUserVisible') + ->willReturn(true); + + $this->tagManager->expects($this->once()) + ->method('getTagsByIds') + ->with(['123', '456', '789']) + ->willReturn([$tag123, $tag456, $tag789]); + + $this->userFolder->expects($this->exactly(2)) + ->method('searchBySystemTag') ->willReturnMap([ - ['123', 'files', 0, '', ['111', '222']], - ['456', 'files', 0, '', ['333']], - ['789', 'files', 0, '', ['111', '222']], + ['OneTwoThree', 'testuser', 0, 0, [$filesNode1, $filesNode2]], + ['FourFiveSix', 'testuser', 0, 0, [$filesNode3]], ]); $rules = [ @@ -556,144 +645,186 @@ class FilesReportPluginTest extends \Test\TestCase { ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '789'], ]; - $this->assertEquals([], array_values($this->invokePrivate($this->plugin, 'processFilterRules', [$rules]))); + $this->assertEquals([], array_values(self::invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null]))); } - public function testProcessFilterRulesInvisibleTagAsAdmin() { + public function testProcessFilterRulesInvisibleTagAsAdmin(): void { $this->groupManager->expects($this->any()) ->method('isAdmin') ->willReturn(true); - $tag1 = $this->getMockBuilder(ISystemTag::class) - ->disableOriginalConstructor() - ->getMock(); - $tag1->expects($this->any()) + $filesNode1 = $this->createMock(File::class); + $filesNode1->expects($this->any()) + ->method('getSize') + ->willReturn(12); + $filesNode1->expects($this->any()) ->method('getId') - ->willReturn('123'); - $tag1->expects($this->any()) + ->willReturn(111); + $filesNode2 = $this->createMock(Folder::class); + $filesNode2->expects($this->any()) + ->method('getSize') + ->willReturn(10); + $filesNode2->expects($this->any()) + ->method('getId') + ->willReturn(222); + $filesNode3 = $this->createMock(Folder::class); + $filesNode3->expects($this->any()) + ->method('getSize') + ->willReturn(13); + $filesNode3->expects($this->any()) + ->method('getId') + ->willReturn(333); + + $tag123 = $this->createMock(ISystemTag::class); + $tag123->expects($this->any()) + ->method('getName') + ->willReturn('OneTwoThree'); + $tag123->expects($this->any()) ->method('isUserVisible') ->willReturn(true); - - $tag2 = $this->getMockBuilder(ISystemTag::class) - ->disableOriginalConstructor() - ->getMock(); - $tag2->expects($this->any()) - ->method('getId') - ->willReturn('123'); - $tag2->expects($this->any()) + $tag456 = $this->createMock(ISystemTag::class); + $tag456->expects($this->any()) + ->method('getName') + ->willReturn('FourFiveSix'); + $tag456->expects($this->any()) ->method('isUserVisible') ->willReturn(false); - // no need to fetch tags to check permissions - $this->tagManager->expects($this->never()) - ->method('getTagsByIds'); + $this->tagManager->expects($this->once()) + ->method('getTagsByIds') + ->with(['123', '456']) + ->willReturn([$tag123, $tag456]); - $this->tagMapper->expects($this->at(0)) - ->method('getObjectIdsForTags') - ->with('123') - ->willReturn(['111', '222']); - $this->tagMapper->expects($this->at(1)) - ->method('getObjectIdsForTags') - ->with('456') - ->willReturn(['222', '333']); + $this->userFolder->expects($this->exactly(2)) + ->method('searchBySystemTag') + ->willReturnMap([ + ['OneTwoThree', 'testuser', 0, 0, [$filesNode1, $filesNode2]], + ['FourFiveSix', 'testuser', 0, 0, [$filesNode2, $filesNode3]], + ]); $rules = [ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], ]; - $this->assertEquals(['222'], array_values($this->invokePrivate($this->plugin, 'processFilterRules', [$rules]))); + $this->assertEquals([$filesNode2], array_values(self::invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null]))); } - public function testProcessFilterRulesInvisibleTagAsUser() { - $this->expectException(\OCP\SystemTag\TagNotFoundException::class); + public function testProcessFilterRulesInvisibleTagAsUser(): void { + $this->expectException(TagNotFoundException::class); $this->groupManager->expects($this->any()) ->method('isAdmin') ->willReturn(false); - $tag1 = $this->getMockBuilder(ISystemTag::class) - ->disableOriginalConstructor() - ->getMock(); - $tag1->expects($this->any()) - ->method('getId') - ->willReturn('123'); - $tag1->expects($this->any()) + $tag123 = $this->createMock(ISystemTag::class); + $tag123->expects($this->any()) + ->method('getName') + ->willReturn('OneTwoThree'); + $tag123->expects($this->any()) ->method('isUserVisible') ->willReturn(true); - - $tag2 = $this->getMockBuilder(ISystemTag::class) - ->disableOriginalConstructor() - ->getMock(); - $tag2->expects($this->any()) - ->method('getId') - ->willReturn('123'); - $tag2->expects($this->any()) + $tag456 = $this->createMock(ISystemTag::class); + $tag456->expects($this->any()) + ->method('getName') + ->willReturn('FourFiveSix'); + $tag456->expects($this->any()) ->method('isUserVisible') - ->willReturn(false); // invisible + ->willReturn(false); $this->tagManager->expects($this->once()) ->method('getTagsByIds') ->with(['123', '456']) - ->willReturn([$tag1, $tag2]); + ->willThrowException(new TagNotFoundException()); + + $this->userFolder->expects($this->never()) + ->method('searchBySystemTag'); $rules = [ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], ]; - $this->invokePrivate($this->plugin, 'processFilterRules', [$rules]); + self::invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null]); } - public function testProcessFilterRulesVisibleTagAsUser() { + public function testProcessFilterRulesVisibleTagAsUser(): void { $this->groupManager->expects($this->any()) ->method('isAdmin') ->willReturn(false); - $tag1 = $this->getMockBuilder(ISystemTag::class) - ->disableOriginalConstructor() - ->getMock(); + $tag1 = $this->createMock(ISystemTag::class); $tag1->expects($this->any()) ->method('getId') ->willReturn('123'); $tag1->expects($this->any()) ->method('isUserVisible') ->willReturn(true); + $tag1->expects($this->any()) + ->method('getName') + ->willReturn('OneTwoThree'); - $tag2 = $this->getMockBuilder(ISystemTag::class) - ->disableOriginalConstructor() - ->getMock(); + $tag2 = $this->createMock(ISystemTag::class); $tag2->expects($this->any()) ->method('getId') ->willReturn('123'); $tag2->expects($this->any()) ->method('isUserVisible') ->willReturn(true); + $tag2->expects($this->any()) + ->method('getName') + ->willReturn('FourFiveSix'); + + $this->tagManager->expects($this->once()) + ->method('getTagsByIds') + ->with(['123', '456']) + ->willReturn([$tag1, $tag2]); + + $filesNode1 = $this->createMock(File::class); + $filesNode1->expects($this->any()) + ->method('getId') + ->willReturn(111); + $filesNode1->expects($this->any()) + ->method('getSize') + ->willReturn(12); + $filesNode2 = $this->createMock(Folder::class); + $filesNode2->expects($this->any()) + ->method('getId') + ->willReturn(222); + $filesNode2->expects($this->any()) + ->method('getSize') + ->willReturn(10); + $filesNode3 = $this->createMock(Folder::class); + $filesNode3->expects($this->any()) + ->method('getId') + ->willReturn(333); + $filesNode3->expects($this->any()) + ->method('getSize') + ->willReturn(33); $this->tagManager->expects($this->once()) ->method('getTagsByIds') ->with(['123', '456']) ->willReturn([$tag1, $tag2]); - $this->tagMapper->expects($this->at(0)) - ->method('getObjectIdsForTags') - ->with('123') - ->willReturn(['111', '222']); - $this->tagMapper->expects($this->at(1)) - ->method('getObjectIdsForTags') - ->with('456') - ->willReturn(['222', '333']); + // main assertion: only user visible tags are being passed through. + $this->userFolder->expects($this->exactly(2)) + ->method('searchBySystemTag') + ->willReturnMap([ + ['OneTwoThree', 'testuser', 0, 0, [$filesNode1, $filesNode2]], + ['FourFiveSix', 'testuser', 0, 0, [$filesNode2, $filesNode3]], + ]); $rules = [ ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], ]; - $this->assertEquals(['222'], array_values($this->invokePrivate($this->plugin, 'processFilterRules', [$rules]))); + $this->assertEquals([$filesNode2], array_values(self::invokePrivate($this->plugin, 'processFilterRulesForFileNodes', [$rules, null, null]))); } - public function testProcessFavoriteFilter() { + public function testProcessFavoriteFilter(): void { $rules = [ ['name' => '{http://owncloud.org/ns}favorite', 'value' => '1'], ]; @@ -702,10 +833,10 @@ class FilesReportPluginTest extends \Test\TestCase { ->method('getFavorites') ->willReturn(['456', '789']); - $this->assertEquals(['456', '789'], array_values($this->invokePrivate($this->plugin, 'processFilterRules', [$rules]))); + $this->assertEquals(['456', '789'], array_values(self::invokePrivate($this->plugin, 'processFilterRulesForFileIDs', [$rules]))); } - public function filesBaseUriProvider() { + public static function filesBaseUriProvider(): array { return [ ['', '', ''], ['files/username', '', '/files/username'], @@ -715,10 +846,8 @@ class FilesReportPluginTest extends \Test\TestCase { ]; } - /** - * @dataProvider filesBaseUriProvider - */ - public function testFilesBaseUri($uri, $reportPath, $expectedUri) { - $this->assertEquals($expectedUri, $this->invokePrivate($this->plugin, 'getFilesBaseUri', [$uri, $reportPath])); + #[\PHPUnit\Framework\Attributes\DataProvider('filesBaseUriProvider')] + public function testFilesBaseUri(string $uri, string $reportPath, string $expectedUri): void { + $this->assertEquals($expectedUri, self::invokePrivate($this->plugin, 'getFilesBaseUri', [$uri, $reportPath])); } } diff --git a/apps/dav/tests/unit/Connector/Sabre/MaintenancePluginTest.php b/apps/dav/tests/unit/Connector/Sabre/MaintenancePluginTest.php index 3f38008559c..bc1d50ac41f 100644 --- a/apps/dav/tests/unit/Connector/Sabre/MaintenancePluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/MaintenancePluginTest.php @@ -1,34 +1,17 @@ <?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 Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Valdnet <47037905+Valdnet@users.noreply.github.com> - * - * @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\Connector\Sabre; use OCA\DAV\Connector\Sabre\MaintenancePlugin; use OCP\IConfig; use OCP\IL10N; +use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; /** @@ -37,23 +20,20 @@ use Test\TestCase; * @package OCA\DAV\Tests\unit\Connector\Sabre */ class MaintenancePluginTest extends TestCase { - /** @var IConfig */ - private $config; - /** @var \PHPUnit\Framework\MockObject\Builder\InvocationMocker|\PHPUnit_Framework_MockObject_Builder_InvocationMocker|IL10N */ - private $l10n; - /** @var MaintenancePlugin */ - private $maintenancePlugin; + private IConfig&MockObject $config; + private IL10N&MockObject $l10n; + private MaintenancePlugin $maintenancePlugin; protected function setUp(): void { parent::setUp(); - $this->config = $this->getMockBuilder(IConfig::class)->getMock(); - $this->l10n = $this->getMockBuilder(IL10N::class)->getMock(); + $this->config = $this->createMock(IConfig::class); + $this->l10n = $this->createMock(IL10N::class); $this->maintenancePlugin = new MaintenancePlugin($this->config, $this->l10n); } - public function testMaintenanceMode() { + public function testMaintenanceMode(): void { $this->expectException(\Sabre\DAV\Exception\ServiceUnavailable::class); $this->expectExceptionMessage('System is in maintenance mode.'); diff --git a/apps/dav/tests/unit/Connector/Sabre/NodeTest.php b/apps/dav/tests/unit/Connector/Sabre/NodeTest.php index 00fd0ebd8aa..11970769a1e 100644 --- a/apps/dav/tests/unit/Connector/Sabre/NodeTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/NodeTest.php @@ -1,38 +1,30 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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> - * @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\Connector\Sabre; use OC\Files\FileInfo; +use OC\Files\Mount\MountPoint; +use OC\Files\Node\Folder; use OC\Files\View; +use OC\Share20\ShareAttributes; +use OCA\DAV\Connector\Sabre\File; +use OCA\Files_Sharing\SharedMount; +use OCA\Files_Sharing\SharedStorage; +use OCP\Constants; +use OCP\Files\Cache\ICacheEntry; use OCP\Files\Mount\IMountPoint; -use OCP\Files\Storage; +use OCP\Files\Storage\IStorage; +use OCP\ICache; use OCP\Share\IManager; use OCP\Share\IShare; +use PHPUnit\Framework\MockObject\MockObject; /** * Class NodeTest @@ -41,51 +33,73 @@ use OCP\Share\IShare; * @package OCA\DAV\Tests\unit\Connector\Sabre */ class NodeTest extends \Test\TestCase { - public function davPermissionsProvider() { + public static function davPermissionsProvider(): array { return [ - [\OCP\Constants::PERMISSION_ALL, 'file', false, false, 'RGDNVW'], - [\OCP\Constants::PERMISSION_ALL, 'dir', false, false, 'RGDNVCK'], - [\OCP\Constants::PERMISSION_ALL, 'file', true, false, 'SRGDNVW'], - [\OCP\Constants::PERMISSION_ALL, 'file', true, true, 'SRMGDNVW'], - [\OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_SHARE, 'file', true, false, 'SGDNVW'], - [\OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_UPDATE, 'file', false, false, 'RGD'], - [\OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_DELETE, 'file', false, false, 'RGNVW'], - [\OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE, 'file', false, false, 'RGDNVW'], - [\OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_READ, 'file', false, false, 'RDNVW'], - [\OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE, 'dir', false, false, 'RGDNV'], - [\OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_READ, 'dir', false, false, 'RDNVCK'], + [Constants::PERMISSION_ALL, 'file', false, Constants::PERMISSION_ALL, false, 'test', 'RGDNVW'], + [Constants::PERMISSION_ALL, 'dir', false, Constants::PERMISSION_ALL, false, 'test', 'RGDNVCK'], + [Constants::PERMISSION_ALL, 'file', true, Constants::PERMISSION_ALL, false, 'test', 'SRGDNVW'], + [Constants::PERMISSION_ALL, 'file', true, Constants::PERMISSION_ALL, true, 'test', 'SRMGDNVW'], + [Constants::PERMISSION_ALL, 'file', true, Constants::PERMISSION_ALL, true, '' , 'SRMGDNVW'], + [Constants::PERMISSION_ALL, 'file', true, Constants::PERMISSION_ALL - Constants::PERMISSION_UPDATE, true, '' , 'SRMGDNV'], + [Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE, 'file', true, Constants::PERMISSION_ALL, false, 'test', 'SGDNVW'], + [Constants::PERMISSION_ALL - Constants::PERMISSION_UPDATE, 'file', false, Constants::PERMISSION_ALL, false, 'test', 'RGD'], + [Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE, 'file', false, Constants::PERMISSION_ALL, false, 'test', 'RGNVW'], + [Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE, 'file', false, Constants::PERMISSION_ALL, false, 'test', 'RGDNVW'], + [Constants::PERMISSION_ALL - Constants::PERMISSION_READ, 'file', false, Constants::PERMISSION_ALL, false, 'test', 'RDNVW'], + [Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE, 'dir', false, Constants::PERMISSION_ALL, false, 'test', 'RGDNV'], + [Constants::PERMISSION_ALL - Constants::PERMISSION_READ, 'dir', false, Constants::PERMISSION_ALL, false, 'test', 'RDNVCK'], ]; } - /** - * @dataProvider davPermissionsProvider - */ - public function testDavPermissions($permissions, $type, $shared, $mounted, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('davPermissionsProvider')] + public function testDavPermissions(int $permissions, string $type, bool $shared, int $shareRootPermissions, bool $mounted, string $internalPath, string $expected): void { $info = $this->getMockBuilder(FileInfo::class) ->disableOriginalConstructor() - ->setMethods(['getPermissions', 'isShared', 'isMounted', 'getType']) + ->onlyMethods(['getPermissions', 'isShared', 'isMounted', 'getType', 'getInternalPath', 'getStorage', 'getMountPoint']) ->getMock(); - $info->expects($this->any()) - ->method('getPermissions') + $info->method('getPermissions') ->willReturn($permissions); - $info->expects($this->any()) - ->method('isShared') + $info->method('isShared') ->willReturn($shared); - $info->expects($this->any()) - ->method('isMounted') + $info->method('isMounted') ->willReturn($mounted); - $info->expects($this->any()) - ->method('getType') + $info->method('getType') ->willReturn($type); - $view = $this->getMockBuilder(View::class) - ->disableOriginalConstructor() - ->getMock(); + $info->method('getInternalPath') + ->willReturn($internalPath); + $info->method('getMountPoint') + ->willReturnCallback(function () use ($shared) { + if ($shared) { + return $this->createMock(SharedMount::class); + } else { + return $this->createMock(MountPoint::class); + } + }); + $storage = $this->createMock(IStorage::class); + if ($shared) { + $storage->method('instanceOfStorage') + ->willReturn(true); + $cache = $this->createMock(ICache::class); + $storage->method('getCache') + ->willReturn($cache); + $shareRootEntry = $this->createMock(ICacheEntry::class); + $cache->method('get') + ->willReturn($shareRootEntry); + $shareRootEntry->method('getPermissions') + ->willReturn($shareRootPermissions); + } else { + $storage->method('instanceOfStorage') + ->willReturn(false); + } + $info->method('getStorage') + ->willReturn($storage); + $view = $this->createMock(View::class); - $node = new \OCA\DAV\Connector\Sabre\File($view, $info); + $node = new File($view, $info); $this->assertEquals($expected, $node->getDavPermissions()); } - public function sharePermissionsProvider() { + public static function sharePermissionsProvider(): array { return [ [\OCP\Files\FileInfo::TYPE_FILE, null, 1, 1], [\OCP\Files\FileInfo::TYPE_FILE, null, 3, 3], @@ -125,21 +139,15 @@ class NodeTest extends \Test\TestCase { ]; } - /** - * @dataProvider sharePermissionsProvider - */ - public function testSharePermissions($type, $user, $permissions, $expected) { - $storage = $this->getMockBuilder(Storage::class) - ->disableOriginalConstructor() - ->getMock(); + #[\PHPUnit\Framework\Attributes\DataProvider('sharePermissionsProvider')] + public function testSharePermissions(string $type, ?string $user, int $permissions, int $expected): void { + $storage = $this->createMock(IStorage::class); $storage->method('getPermissions')->willReturn($permissions); - $mountpoint = $this->getMockBuilder(IMountPoint::class) - ->disableOriginalConstructor() - ->getMock(); + $mountpoint = $this->createMock(IMountPoint::class); $mountpoint->method('getMountPoint')->willReturn('myPath'); - $shareManager = $this->getMockBuilder(IManager::class)->disableOriginalConstructor()->getMock(); - $share = $this->getMockBuilder(IShare::class)->disableOriginalConstructor()->getMock(); + $shareManager = $this->createMock(IManager::class); + $share = $this->createMock(IShare::class); if ($user === null) { $shareManager->expects($this->never())->method('getShareByToken'); @@ -152,7 +160,7 @@ class NodeTest extends \Test\TestCase { $info = $this->getMockBuilder(FileInfo::class) ->disableOriginalConstructor() - ->setMethods(['getStorage', 'getType', 'getMountPoint', 'getPermissions']) + ->onlyMethods(['getStorage', 'getType', 'getMountPoint', 'getPermissions']) ->getMock(); $info->method('getStorage')->willReturn($storage); @@ -160,26 +168,78 @@ class NodeTest extends \Test\TestCase { $info->method('getMountPoint')->willReturn($mountpoint); $info->method('getPermissions')->willReturn($permissions); - $view = $this->getMockBuilder(View::class) + $view = $this->createMock(View::class); + + $node = new File($view, $info); + $this->invokePrivate($node, 'shareManager', [$shareManager]); + $this->assertEquals($expected, $node->getSharePermissions($user)); + } + + public function testShareAttributes(): void { + $storage = $this->getMockBuilder(SharedStorage::class) ->disableOriginalConstructor() + ->onlyMethods(['getShare']) ->getMock(); - $node = new \OCA\DAV\Connector\Sabre\File($view, $info); + $shareManager = $this->createMock(IManager::class); + $share = $this->createMock(IShare::class); + + $storage->expects($this->once()) + ->method('getShare') + ->willReturn($share); + + $attributes = new ShareAttributes(); + $attributes->setAttribute('permissions', 'download', false); + + $share->expects($this->once())->method('getAttributes')->willReturn($attributes); + + /** @var Folder&MockObject $info */ + $info = $this->getMockBuilder(Folder::class) + ->disableOriginalConstructor() + ->onlyMethods(['getStorage', 'getType']) + ->getMock(); + + $info->method('getStorage')->willReturn($storage); + $info->method('getType')->willReturn(FileInfo::TYPE_FOLDER); + + /** @var View&MockObject $view */ + $view = $this->createMock(View::class); + + $node = new File($view, $info); $this->invokePrivate($node, 'shareManager', [$shareManager]); - $this->assertEquals($expected, $node->getSharePermissions($user)); + $this->assertEquals($attributes->toArray(), $node->getShareAttributes()); } - public function sanitizeMtimeProvider() { + public function testShareAttributesNonShare(): void { + $storage = $this->createMock(IStorage::class); + $shareManager = $this->createMock(IManager::class); + + /** @var Folder&MockObject */ + $info = $this->getMockBuilder(Folder::class) + ->disableOriginalConstructor() + ->onlyMethods(['getStorage', 'getType']) + ->getMock(); + + $info->method('getStorage')->willReturn($storage); + $info->method('getType')->willReturn(FileInfo::TYPE_FOLDER); + + /** @var View&MockObject */ + $view = $this->createMock(View::class); + + $node = new File($view, $info); + $this->invokePrivate($node, 'shareManager', [$shareManager]); + $this->assertEquals([], $node->getShareAttributes()); + } + + public static function sanitizeMtimeProvider(): array { return [ [123456789, 123456789], ['987654321', 987654321], ]; } - /** - * @dataProvider sanitizeMtimeProvider - */ - public function testSanitizeMtime($mtime, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('sanitizeMtimeProvider')] + public function testSanitizeMtime(string|int $mtime, int $expected): void { $view = $this->getMockBuilder(View::class) ->disableOriginalConstructor() ->getMock(); @@ -187,31 +247,25 @@ class NodeTest extends \Test\TestCase { ->disableOriginalConstructor() ->getMock(); - $node = new \OCA\DAV\Connector\Sabre\File($view, $info); + $node = new File($view, $info); $result = $this->invokePrivate($node, 'sanitizeMtime', [$mtime]); $this->assertEquals($expected, $result); } - public function invalidSanitizeMtimeProvider() { + public static function invalidSanitizeMtimeProvider(): array { return [ - [-1337], [0], ['abcdef'], ['-1337'], ['0'], [12321], [24 * 60 * 60 - 1] + [-1337], [0], ['abcdef'], ['-1337'], ['0'], [12321], [24 * 60 * 60 - 1], ]; } - /** - * @dataProvider invalidSanitizeMtimeProvider - */ - public function testInvalidSanitizeMtime($mtime) { + #[\PHPUnit\Framework\Attributes\DataProvider('invalidSanitizeMtimeProvider')] + public function testInvalidSanitizeMtime(int|string $mtime): void { $this->expectException(\InvalidArgumentException::class); - $view = $this->getMockBuilder(View::class) - ->disableOriginalConstructor() - ->getMock(); - $info = $this->getMockBuilder(FileInfo::class) - ->disableOriginalConstructor() - ->getMock(); + $view = $this->createMock(View::class); + $info = $this->createMock(FileInfo::class); - $node = new \OCA\DAV\Connector\Sabre\File($view, $info); - $result = $this->invokePrivate($node, 'sanitizeMtime', [$mtime]); + $node = new File($view, $info); + self::invokePrivate($node, 'sanitizeMtime', [$mtime]); } } diff --git a/apps/dav/tests/unit/Connector/Sabre/ObjectTreeTest.php b/apps/dav/tests/unit/Connector/Sabre/ObjectTreeTest.php index 5f516cec113..b07778e4fbd 100644 --- a/apps/dav/tests/unit/Connector/Sabre/ObjectTreeTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/ObjectTreeTest.php @@ -1,38 +1,22 @@ <?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 Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @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\Connector\Sabre; use OC\Files\FileInfo; use OC\Files\Filesystem; use OC\Files\Mount\Manager; +use OC\Files\Storage\Common; use OC\Files\Storage\Temporary; use OC\Files\View; use OCA\DAV\Connector\Sabre\Directory; +use OCA\DAV\Connector\Sabre\Exception\InvalidPath; +use OCA\DAV\Connector\Sabre\File; use OCA\DAV\Connector\Sabre\ObjectTree; use OCP\Files\Mount\IMountManager; @@ -44,7 +28,7 @@ use OCP\Files\Mount\IMountManager; * @package OCA\DAV\Tests\Unit\Connector\Sabre */ class ObjectTreeTest extends \Test\TestCase { - public function copyDataProvider() { + public static function copyDataProvider(): array { return [ // copy into same dir ['a', 'b', ''], @@ -55,15 +39,12 @@ class ObjectTreeTest extends \Test\TestCase { ]; } - /** - * @dataProvider copyDataProvider - */ - public function testCopy($sourcePath, $targetPath, $targetParent) { + #[\PHPUnit\Framework\Attributes\DataProvider('copyDataProvider')] + public function testCopy(string $sourcePath, string $targetPath, string $targetParent): void { $view = $this->createMock(View::class); $view->expects($this->once()) ->method('verifyPath') - ->with($targetParent) - ->willReturn(true); + ->with($targetParent); $view->expects($this->once()) ->method('file_exists') ->with($targetPath) @@ -85,7 +66,7 @@ class ObjectTreeTest extends \Test\TestCase { $rootDir = new Directory($view, $info); $objectTree = $this->getMockBuilder(ObjectTree::class) - ->setMethods(['nodeExists', 'getNodeForPath']) + ->onlyMethods(['nodeExists', 'getNodeForPath']) ->setConstructorArgs([$rootDir, $view]) ->getMock(); @@ -94,16 +75,14 @@ class ObjectTreeTest extends \Test\TestCase { ->with($this->identicalTo($sourcePath)) ->willReturn(false); - /** @var $objectTree \OCA\DAV\Connector\Sabre\ObjectTree */ + /** @var ObjectTree $objectTree */ $mountManager = Filesystem::getMountManager(); $objectTree->init($rootDir, $view, $mountManager); $objectTree->copy($sourcePath, $targetPath); } - /** - * @dataProvider copyDataProvider - */ - public function testCopyFailNotCreatable($sourcePath, $targetPath, $targetParent) { + #[\PHPUnit\Framework\Attributes\DataProvider('copyDataProvider')] + public function testCopyFailNotCreatable($sourcePath, $targetPath, $targetParent): void { $this->expectException(\Sabre\DAV\Exception\Forbidden::class); $view = $this->createMock(View::class); @@ -128,57 +107,42 @@ class ObjectTreeTest extends \Test\TestCase { $rootDir = new Directory($view, $info); $objectTree = $this->getMockBuilder(ObjectTree::class) - ->setMethods(['nodeExists', 'getNodeForPath']) + ->onlyMethods(['nodeExists', 'getNodeForPath']) ->setConstructorArgs([$rootDir, $view]) ->getMock(); $objectTree->expects($this->never()) ->method('getNodeForPath'); - /** @var $objectTree \OCA\DAV\Connector\Sabre\ObjectTree */ + /** @var ObjectTree $objectTree */ $mountManager = Filesystem::getMountManager(); $objectTree->init($rootDir, $view, $mountManager); $objectTree->copy($sourcePath, $targetPath); } - /** - * @dataProvider nodeForPathProvider - */ + #[\PHPUnit\Framework\Attributes\DataProvider('nodeForPathProvider')] public function testGetNodeForPath( - $inputFileName, - $fileInfoQueryPath, - $outputFileName, - $type, - $enableChunkingHeader - ) { - if ($enableChunkingHeader) { - $_SERVER['HTTP_OC_CHUNKED'] = true; - } - - $rootNode = $this->getMockBuilder(Directory::class) - ->disableOriginalConstructor() - ->getMock(); - $mountManager = $this->getMockBuilder(Manager::class) - ->disableOriginalConstructor() - ->getMock(); - $view = $this->getMockBuilder(View::class) - ->disableOriginalConstructor() - ->getMock(); - $fileInfo = $this->getMockBuilder(FileInfo::class) - ->disableOriginalConstructor() - ->getMock(); + string $inputFileName, + string $fileInfoQueryPath, + string $outputFileName, + string $type, + ): void { + $rootNode = $this->createMock(Directory::class); + $mountManager = $this->createMock(Manager::class); + $view = $this->createMock(View::class); + $fileInfo = $this->createMock(FileInfo::class); $fileInfo->method('getType') ->willReturn($type); $fileInfo->method('getName') ->willReturn($outputFileName); $fileInfo->method('getStorage') - ->willReturn($this->createMock(\OC\Files\Storage\Common::class)); + ->willReturn($this->createMock(Common::class)); $view->method('getFileInfo') ->with($fileInfoQueryPath) ->willReturn($fileInfo); - $tree = new \OCA\DAV\Connector\Sabre\ObjectTree(); + $tree = new ObjectTree(); $tree->init($rootNode, $view, $mountManager); $node = $tree->getNodeForPath($inputFileName); @@ -187,15 +151,13 @@ class ObjectTreeTest extends \Test\TestCase { $this->assertEquals($outputFileName, $node->getName()); if ($type === 'file') { - $this->assertTrue($node instanceof \OCA\DAV\Connector\Sabre\File); + $this->assertInstanceOf(File::class, $node); } else { - $this->assertTrue($node instanceof \OCA\DAV\Connector\Sabre\Directory); + $this->assertInstanceOf(Directory::class, $node); } - - unset($_SERVER['HTTP_OC_CHUNKED']); } - public function nodeForPathProvider() { + public static function nodeForPathProvider(): array { return [ // regular file [ @@ -203,7 +165,6 @@ class ObjectTreeTest extends \Test\TestCase { 'regularfile.txt', 'regularfile.txt', 'file', - false ], // regular directory [ @@ -211,31 +172,6 @@ class ObjectTreeTest extends \Test\TestCase { 'regulardir', 'regulardir', 'dir', - false - ], - // regular file with chunking - [ - 'regularfile.txt', - 'regularfile.txt', - 'regularfile.txt', - 'file', - true - ], - // regular directory with chunking - [ - 'regulardir', - 'regulardir', - 'regulardir', - 'dir', - true - ], - // file with chunky file name - [ - 'regularfile.txt-chunking-123566789-10-1', - 'regularfile.txt', - 'regularfile.txt', - 'file', - true ], // regular file in subdir [ @@ -243,7 +179,6 @@ class ObjectTreeTest extends \Test\TestCase { 'subdir/regularfile.txt', 'regularfile.txt', 'file', - false ], // regular directory in subdir [ @@ -251,22 +186,13 @@ class ObjectTreeTest extends \Test\TestCase { 'subdir/regulardir', 'regulardir', 'dir', - false - ], - // file with chunky file name in subdir - [ - 'subdir/regularfile.txt-chunking-123566789-10-1', - 'subdir/regularfile.txt', - 'regularfile.txt', - 'file', - true ], ]; } - public function testGetNodeForPathInvalidPath() { - $this->expectException(\OCA\DAV\Connector\Sabre\Exception\InvalidPath::class); + public function testGetNodeForPathInvalidPath(): void { + $this->expectException(InvalidPath::class); $path = '/foo\bar'; @@ -274,7 +200,7 @@ class ObjectTreeTest extends \Test\TestCase { $storage = new Temporary([]); $view = $this->getMockBuilder(View::class) - ->setMethods(['resolvePath']) + ->onlyMethods(['resolvePath']) ->getMock(); $view->expects($this->once()) ->method('resolvePath') @@ -282,25 +208,23 @@ class ObjectTreeTest extends \Test\TestCase { return [$storage, ltrim($path, '/')]; }); - $rootNode = $this->getMockBuilder(Directory::class) - ->disableOriginalConstructor() - ->getMock(); + $rootNode = $this->createMock(Directory::class); $mountManager = $this->createMock(IMountManager::class); - $tree = new \OCA\DAV\Connector\Sabre\ObjectTree(); + $tree = new ObjectTree(); $tree->init($rootNode, $view, $mountManager); $tree->getNodeForPath($path); } - public function testGetNodeForPathRoot() { + public function testGetNodeForPathRoot(): void { $path = '/'; $storage = new Temporary([]); $view = $this->getMockBuilder(View::class) - ->setMethods(['resolvePath']) + ->onlyMethods(['resolvePath']) ->getMock(); $view->expects($this->any()) ->method('resolvePath') @@ -308,12 +232,10 @@ class ObjectTreeTest extends \Test\TestCase { return [$storage, ltrim($path, '/')]; }); - $rootNode = $this->getMockBuilder(Directory::class) - ->disableOriginalConstructor() - ->getMock(); + $rootNode = $this->createMock(Directory::class); $mountManager = $this->createMock(IMountManager::class); - $tree = new \OCA\DAV\Connector\Sabre\ObjectTree(); + $tree = new ObjectTree(); $tree->init($rootNode, $view, $mountManager); $this->assertInstanceOf('\Sabre\DAV\INode', $tree->getNodeForPath($path)); diff --git a/apps/dav/tests/unit/Connector/Sabre/PrincipalTest.php b/apps/dav/tests/unit/Connector/Sabre/PrincipalTest.php index d7c074c9e3b..e32d2671063 100644 --- a/apps/dav/tests/unit/Connector/Sabre/PrincipalTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/PrincipalTest.php @@ -1,31 +1,10 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @copyright Copyright (c) 2018, Georg Ehrke - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @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: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OCA\DAV\Tests\unit\Connector\Sabre; @@ -34,6 +13,10 @@ use OC\User\User; use OCA\DAV\CalDAV\Proxy\Proxy; use OCA\DAV\CalDAV\Proxy\ProxyMapper; use OCA\DAV\Connector\Sabre\Principal; +use OCP\Accounts\IAccount; +use OCP\Accounts\IAccountManager; +use OCP\Accounts\IAccountProperty; +use OCP\Accounts\IAccountPropertyCollection; use OCP\App\IAppManager; use OCP\IConfig; use OCP\IGroup; @@ -49,38 +32,24 @@ use Sabre\DAV\PropPatch; use Test\TestCase; class PrincipalTest extends TestCase { - - /** @var IUserManager | MockObject */ - private $userManager; - - /** @var Principal */ - private $connector; - - /** @var IGroupManager | MockObject */ - private $groupManager; - - /** @var IManager | MockObject */ - private $shareManager; - - /** @var IUserSession | MockObject */ - private $userSession; - - /** @var IAppManager | MockObject */ - private $appManager; - - /** @var ProxyMapper | MockObject */ - private $proxyMapper; - - /** @var KnownUserService|MockObject */ - private $knownUserService; - /** @var IConfig | MockObject */ - private $config; - /** @var IFactory|MockObject */ - private $languageFactory; + private IUserManager&MockObject $userManager; + private IGroupManager&MockObject $groupManager; + private IAccountManager&MockObject $accountManager; + private IManager&MockObject $shareManager; + private IUserSession&MockObject $userSession; + private IAppManager&MockObject $appManager; + private ProxyMapper&MockObject $proxyMapper; + private KnownUserService&MockObject $knownUserService; + private IConfig&MockObject $config; + private IFactory&MockObject $languageFactory; + private Principal $connector; protected function setUp(): void { + parent::setUp(); + $this->userManager = $this->createMock(IUserManager::class); $this->groupManager = $this->createMock(IGroupManager::class); + $this->accountManager = $this->createMock(IAccountManager::class); $this->shareManager = $this->createMock(IManager::class); $this->userSession = $this->createMock(IUserSession::class); $this->appManager = $this->createMock(IAppManager::class); @@ -92,6 +61,7 @@ class PrincipalTest extends TestCase { $this->connector = new Principal( $this->userManager, $this->groupManager, + $this->accountManager, $this->shareManager, $this->userSession, $this->appManager, @@ -100,7 +70,6 @@ class PrincipalTest extends TestCase { $this->config, $this->languageFactory ); - parent::setUp(); } public function testGetPrincipalsByPrefixWithoutPrefix(): void { @@ -111,26 +80,26 @@ class PrincipalTest extends TestCase { public function testGetPrincipalsByPrefixWithUsers(): void { $fooUser = $this->createMock(User::class); $fooUser - ->expects($this->once()) - ->method('getUID') - ->willReturn('foo'); + ->expects($this->once()) + ->method('getUID') + ->willReturn('foo'); $fooUser - ->expects($this->once()) - ->method('getDisplayName') - ->willReturn('Dr. Foo-Bar'); + ->expects($this->once()) + ->method('getDisplayName') + ->willReturn('Dr. Foo-Bar'); $fooUser - ->expects($this->once()) - ->method('getSystemEMailAddress') - ->willReturn(''); + ->expects($this->once()) + ->method('getSystemEMailAddress') + ->willReturn(''); $barUser = $this->createMock(User::class); $barUser ->expects($this->once()) ->method('getUID') ->willReturn('bar'); $barUser - ->expects($this->once()) - ->method('getSystemEMailAddress') - ->willReturn('bar@nextcloud.com'); + ->expects($this->once()) + ->method('getSystemEMailAddress') + ->willReturn('bar@nextcloud.com'); $this->userManager ->expects($this->once()) ->method('search') @@ -140,8 +109,47 @@ class PrincipalTest extends TestCase { $this->languageFactory ->expects($this->exactly(2)) ->method('getUserLanguage') - ->withConsecutive([$fooUser], [$barUser]) - ->willReturnOnConsecutiveCalls('de', 'en'); + ->willReturnMap([ + [$fooUser, 'de'], + [$barUser, 'en'], + ]); + + $fooAccountPropertyCollection = $this->createMock(IAccountPropertyCollection::class); + $fooAccountPropertyCollection->expects($this->once()) + ->method('getProperties') + ->willReturn([]); + $fooAccount = $this->createMock(IAccount::class); + $fooAccount->expects($this->once()) + ->method('getPropertyCollection') + ->with(IAccountManager::COLLECTION_EMAIL) + ->willReturn($fooAccountPropertyCollection); + + $emailPropertyOne = $this->createMock(IAccountProperty::class); + $emailPropertyOne->expects($this->once()) + ->method('getValue') + ->willReturn('alias@nextcloud.com'); + $emailPropertyTwo = $this->createMock(IAccountProperty::class); + $emailPropertyTwo->expects($this->once()) + ->method('getValue') + ->willReturn('alias2@nextcloud.com'); + + $barAccountPropertyCollection = $this->createMock(IAccountPropertyCollection::class); + $barAccountPropertyCollection->expects($this->once()) + ->method('getProperties') + ->willReturn([$emailPropertyOne, $emailPropertyTwo]); + $barAccount = $this->createMock(IAccount::class); + $barAccount->expects($this->once()) + ->method('getPropertyCollection') + ->with(IAccountManager::COLLECTION_EMAIL) + ->willReturn($barAccountPropertyCollection); + + $this->accountManager + ->expects($this->exactly(2)) + ->method('getAccount') + ->willReturnMap([ + [$fooUser, $fooAccount], + [$barUser, $barAccount], + ]); $expectedResponse = [ 0 => [ @@ -156,6 +164,7 @@ class PrincipalTest extends TestCase { '{urn:ietf:params:xml:ns:caldav}calendar-user-type' => 'INDIVIDUAL', '{http://nextcloud.com/ns}language' => 'en', '{http://sabredav.org/ns}email-address' => 'bar@nextcloud.com', + '{DAV:}alternate-URI-set' => ['mailto:alias@nextcloud.com', 'mailto:alias2@nextcloud.com'] ] ]; $response = $this->connector->getPrincipalsByPrefix('principals/users'); @@ -204,13 +213,13 @@ class PrincipalTest extends TestCase { public function testGetPrincipalsByPathWithMail(): void { $fooUser = $this->createMock(User::class); $fooUser - ->expects($this->once()) - ->method('getSystemEMailAddress') - ->willReturn('foo@nextcloud.com'); + ->expects($this->once()) + ->method('getSystemEMailAddress') + ->willReturn('foo@nextcloud.com'); $fooUser - ->expects($this->once()) - ->method('getUID') - ->willReturn('foo'); + ->expects($this->once()) + ->method('getUID') + ->willReturn('foo'); $this->userManager ->expects($this->once()) ->method('get') @@ -409,22 +418,19 @@ class PrincipalTest extends TestCase { ->method('getUID') ->willReturn('bar'); $this->userManager - ->expects($this->at(0)) - ->method('get') - ->with('foo') - ->willReturn($fooUser); - $this->userManager - ->expects($this->at(1)) + ->expects($this->exactly(2)) ->method('get') - ->with('bar') - ->willReturn($barUser); + ->willReturnMap([ + ['foo', $fooUser], + ['bar', $barUser], + ]); - $this->proxyMapper->expects($this->at(0)) + $this->proxyMapper->expects($this->once()) ->method('getProxiesOf') ->with('principals/users/foo') ->willReturn([]); - $this->proxyMapper->expects($this->at(1)) + $this->proxyMapper->expects($this->once()) ->method('insert') ->with($this->callback(function ($proxy) { /** @var Proxy $proxy */ @@ -457,14 +463,14 @@ class PrincipalTest extends TestCase { ['{http://sabredav.org/ns}email-address' => 'foo'])); } - /** - * @dataProvider searchPrincipalsDataProvider - */ - public function testSearchPrincipals($sharingEnabled, $groupsOnly, $test, $result): void { + #[\PHPUnit\Framework\Attributes\DataProvider('searchPrincipalsDataProvider')] + public function testSearchPrincipals(bool $sharingEnabled, bool $groupsOnly, string $test, array $result): void { $this->shareManager->expects($this->once()) ->method('shareAPIEnabled') ->willReturn($sharingEnabled); + $getUserGroupIdsReturnMap = []; + if ($sharingEnabled) { $this->shareManager->expects($this->once()) ->method('allowEnumeration') @@ -480,10 +486,7 @@ class PrincipalTest extends TestCase { ->method('getUser') ->willReturn($user); - $this->groupManager->expects($this->at(0)) - ->method('getUserGroupIds') - ->with($user) - ->willReturn(['group1', 'group2', 'group5']); + $getUserGroupIdsReturnMap[] = [$user, ['group1', 'group2', 'group5']]; } } else { $this->config->expects($this->never()) @@ -502,12 +505,12 @@ class PrincipalTest extends TestCase { $user4->method('getUID')->willReturn('user4'); if ($sharingEnabled) { - $this->userManager->expects($this->at(0)) + $this->userManager->expects($this->once()) ->method('getByEmail') ->with('user@example.com') ->willReturn([$user2, $user3]); - $this->userManager->expects($this->at(1)) + $this->userManager->expects($this->once()) ->method('searchDisplayName') ->with('User 12') ->willReturn([$user3, $user4]); @@ -520,31 +523,22 @@ class PrincipalTest extends TestCase { } if ($sharingEnabled && $groupsOnly) { - $this->groupManager->expects($this->at(1)) - ->method('getUserGroupIds') - ->with($user2) - ->willReturn(['group1', 'group3']); - $this->groupManager->expects($this->at(2)) - ->method('getUserGroupIds') - ->with($user3) - ->willReturn(['group3', 'group4']); - $this->groupManager->expects($this->at(3)) - ->method('getUserGroupIds') - ->with($user3) - ->willReturn(['group3', 'group4']); - $this->groupManager->expects($this->at(4)) - ->method('getUserGroupIds') - ->with($user4) - ->willReturn(['group4', 'group5']); + $getUserGroupIdsReturnMap[] = [$user2, ['group1', 'group3']]; + $getUserGroupIdsReturnMap[] = [$user3, ['group3', 'group4']]; + $getUserGroupIdsReturnMap[] = [$user4, ['group4', 'group5']]; } + $this->groupManager->expects($this->any()) + ->method('getUserGroupIds') + ->willReturnMap($getUserGroupIdsReturnMap); + $this->assertEquals($result, $this->connector->searchPrincipals('principals/users', ['{http://sabredav.org/ns}email-address' => 'user@example.com', '{DAV:}displayname' => 'User 12'], $test)); } - public function searchPrincipalsDataProvider(): array { + public static function searchPrincipalsDataProvider(): array { return [ [true, false, 'allof', ['principals/users/user3']], [true, false, 'anyof', ['principals/users/user2', 'principals/users/user3', 'principals/users/user4']], @@ -573,7 +567,7 @@ class PrincipalTest extends TestCase { $user3 = $this->createMock(IUser::class); $user3->method('getUID')->willReturn('user3'); - $this->userManager->expects($this->at(0)) + $this->userManager->expects($this->once()) ->method('getByEmail') ->with('user@example.com') ->willReturn([$user2, $user3]); @@ -608,14 +602,14 @@ class PrincipalTest extends TestCase { $user2->method('getSystemEMailAddress')->willReturn('user2@foo.bar'); $user3 = $this->createMock(IUser::class); $user3->method('getUID')->willReturn('user3'); - $user2->method('getDisplayName')->willReturn('User 22'); - $user2->method('getSystemEMailAddress')->willReturn('user2@foo.bar123'); + $user3->method('getDisplayName')->willReturn('User 22'); + $user3->method('getSystemEMailAddress')->willReturn('user2@foo.bar123'); $user4 = $this->createMock(IUser::class); $user4->method('getUID')->willReturn('user4'); - $user2->method('getDisplayName')->willReturn('User 222'); - $user2->method('getSystemEMailAddress')->willReturn('user2@foo.bar456'); + $user4->method('getDisplayName')->willReturn('User 222'); + $user4->method('getSystemEMailAddress')->willReturn('user2@foo.bar456'); - $this->userManager->expects($this->at(0)) + $this->userManager->expects($this->once()) ->method('searchDisplayName') ->with('User 2') ->willReturn([$user2, $user3, $user4]); @@ -662,6 +656,10 @@ class PrincipalTest extends TestCase { ->method('allowEnumerationFullMatch') ->willReturn(true); + $this->shareManager->expects($this->once()) + ->method('matchEmail') + ->willReturn(true); + $user2 = $this->createMock(IUser::class); $user2->method('getUID')->willReturn('user2'); $user2->method('getDisplayName')->willReturn('User 2'); @@ -707,15 +705,15 @@ class PrincipalTest extends TestCase { } public function testSearchPrincipalWithEnumerationLimitedDisplayname(): void { - $this->shareManager->expects($this->at(0)) + $this->shareManager->expects($this->once()) ->method('shareAPIEnabled') ->willReturn(true); - $this->shareManager->expects($this->at(1)) + $this->shareManager->expects($this->once()) ->method('allowEnumeration') ->willReturn(true); - $this->shareManager->expects($this->at(2)) + $this->shareManager->expects($this->once()) ->method('limitEnumerationToGroups') ->willReturn(true); @@ -737,24 +735,19 @@ class PrincipalTest extends TestCase { $user4->method('getSystemEMailAddress')->willReturn('user2@foo.bar456'); - $this->userSession->expects($this->at(0)) + $this->userSession->expects($this->once()) ->method('getUser') ->willReturn($user2); - $this->groupManager->expects($this->at(0)) - ->method('getUserGroupIds') - ->willReturn(['group1']); - $this->groupManager->expects($this->at(1)) - ->method('getUserGroupIds') - ->willReturn(['group1']); - $this->groupManager->expects($this->at(2)) + $this->groupManager->expects($this->exactly(4)) ->method('getUserGroupIds') - ->willReturn(['group1']); - $this->groupManager->expects($this->at(3)) - ->method('getUserGroupIds') - ->willReturn(['group2']); + ->willReturnMap([ + [$user2, ['group1']], + [$user3, ['group1']], + [$user4, ['group2']], + ]); - $this->userManager->expects($this->at(0)) + $this->userManager->expects($this->once()) ->method('searchDisplayName') ->with('User') ->willReturn([$user2, $user3, $user4]); @@ -768,15 +761,15 @@ class PrincipalTest extends TestCase { } public function testSearchPrincipalWithEnumerationLimitedMail(): void { - $this->shareManager->expects($this->at(0)) + $this->shareManager->expects($this->once()) ->method('shareAPIEnabled') ->willReturn(true); - $this->shareManager->expects($this->at(1)) + $this->shareManager->expects($this->once()) ->method('allowEnumeration') ->willReturn(true); - $this->shareManager->expects($this->at(2)) + $this->shareManager->expects($this->once()) ->method('limitEnumerationToGroups') ->willReturn(true); @@ -798,24 +791,19 @@ class PrincipalTest extends TestCase { $user4->method('getSystemEMailAddress')->willReturn('user2@foo.bar456'); - $this->userSession->expects($this->at(0)) + $this->userSession->expects($this->once()) ->method('getUser') ->willReturn($user2); - $this->groupManager->expects($this->at(0)) - ->method('getUserGroupIds') - ->willReturn(['group1']); - $this->groupManager->expects($this->at(1)) - ->method('getUserGroupIds') - ->willReturn(['group1']); - $this->groupManager->expects($this->at(2)) + $this->groupManager->expects($this->exactly(4)) ->method('getUserGroupIds') - ->willReturn(['group1']); - $this->groupManager->expects($this->at(3)) - ->method('getUserGroupIds') - ->willReturn(['group2']); + ->willReturnMap([ + [$user2, ['group1']], + [$user3, ['group1']], + [$user4, ['group2']], + ]); - $this->userManager->expects($this->at(0)) + $this->userManager->expects($this->once()) ->method('getByEmail') ->with('user') ->willReturn([$user2, $user3, $user4]); @@ -836,27 +824,20 @@ class PrincipalTest extends TestCase { $this->assertEquals(null, $this->connector->findByUri('mailto:user@foo.com', 'principals/users')); } - /** - * @dataProvider findByUriWithGroupRestrictionDataProvider - */ - public function testFindByUriWithGroupRestriction($uri, $email, $expects): void { + #[\PHPUnit\Framework\Attributes\DataProvider('findByUriWithGroupRestrictionDataProvider')] + public function testFindByUriWithGroupRestriction(string $uri, string $email, ?string $expects): void { $this->shareManager->expects($this->once()) ->method('shareApiEnabled') ->willReturn(true); $this->shareManager->expects($this->once()) - ->method('shareWithGroupMembersOnly') - ->willReturn(true); + ->method('shareWithGroupMembersOnly') + ->willReturn(true); $user = $this->createMock(IUser::class); $this->userSession->expects($this->once()) - ->method('getUser') - ->willReturn($user); - - $this->groupManager->expects($this->at(0)) - ->method('getUserGroupIds') - ->with($user) - ->willReturn(['group1', 'group2']); + ->method('getUser') + ->willReturn($user); $user2 = $this->createMock(IUser::class); $user2->method('getUID')->willReturn('user2'); @@ -864,43 +845,45 @@ class PrincipalTest extends TestCase { $user3->method('getUID')->willReturn('user3'); $this->userManager->expects($this->once()) - ->method('getByEmail') - ->with($email) - ->willReturn([$email === 'user2@foo.bar' ? $user2 : $user3]); + ->method('getByEmail') + ->with($email) + ->willReturn([$email === 'user2@foo.bar' ? $user2 : $user3]); if ($email === 'user2@foo.bar') { - $this->groupManager->expects($this->at(1)) - ->method('getUserGroupIds') - ->with($user2) - ->willReturn(['group1', 'group3']); + $this->groupManager->expects($this->exactly(2)) + ->method('getUserGroupIds') + ->willReturnMap([ + [$user, ['group1', 'group2']], + [$user2, ['group1', 'group3']], + ]); } else { - $this->groupManager->expects($this->at(1)) - ->method('getUserGroupIds') - ->with($user3) - ->willReturn(['group3', 'group3']); + $this->groupManager->expects($this->exactly(2)) + ->method('getUserGroupIds') + ->willReturnMap([ + [$user, ['group1', 'group2']], + [$user3, ['group3', 'group3']], + ]); } $this->assertEquals($expects, $this->connector->findByUri($uri, 'principals/users')); } - public function findByUriWithGroupRestrictionDataProvider(): array { + public static function findByUriWithGroupRestrictionDataProvider(): array { return [ ['mailto:user2@foo.bar', 'user2@foo.bar', 'principals/users/user2'], ['mailto:user3@foo.bar', 'user3@foo.bar', null], ]; } - /** - * @dataProvider findByUriWithoutGroupRestrictionDataProvider - */ - public function testFindByUriWithoutGroupRestriction($uri, $email, $expects): void { + #[\PHPUnit\Framework\Attributes\DataProvider('findByUriWithoutGroupRestrictionDataProvider')] + public function testFindByUriWithoutGroupRestriction(string $uri, string $email, string $expects): void { $this->shareManager->expects($this->once()) ->method('shareApiEnabled') ->willReturn(true); $this->shareManager->expects($this->once()) - ->method('shareWithGroupMembersOnly') - ->willReturn(false); + ->method('shareWithGroupMembersOnly') + ->willReturn(false); $user2 = $this->createMock(IUser::class); $user2->method('getUID')->willReturn('user2'); @@ -908,17 +891,47 @@ class PrincipalTest extends TestCase { $user3->method('getUID')->willReturn('user3'); $this->userManager->expects($this->once()) - ->method('getByEmail') - ->with($email) - ->willReturn([$email === 'user2@foo.bar' ? $user2 : $user3]); + ->method('getByEmail') + ->with($email) + ->willReturn([$email === 'user2@foo.bar' ? $user2 : $user3]); $this->assertEquals($expects, $this->connector->findByUri($uri, 'principals/users')); } - public function findByUriWithoutGroupRestrictionDataProvider(): array { + public static function findByUriWithoutGroupRestrictionDataProvider(): array { return [ ['mailto:user2@foo.bar', 'user2@foo.bar', 'principals/users/user2'], ['mailto:user3@foo.bar', 'user3@foo.bar', 'principals/users/user3'], ]; } + + public function testGetEmailAddressesOfPrincipal(): void { + $principal = [ + '{http://sabredav.org/ns}email-address' => 'bar@company.org', + '{DAV:}alternate-URI-set' => [ + '/some/url', + 'mailto:foo@bar.com', + 'mailto:duplicate@example.com', + ], + '{urn:ietf:params:xml:ns:caldav}calendar-user-address-set' => [ + 'mailto:bernard@example.com', + 'mailto:bernard.desruisseaux@example.com', + ], + '{http://calendarserver.org/ns/}email-address-set' => [ + 'mailto:duplicate@example.com', + 'mailto:user@some.org', + ], + ]; + + $expected = [ + 'bar@company.org', + 'foo@bar.com', + 'duplicate@example.com', + 'bernard@example.com', + 'bernard.desruisseaux@example.com', + 'user@some.org', + ]; + $actual = $this->connector->getEmailAddressesOfPrincipal($principal); + $this->assertEquals($expected, $actual); + } } diff --git a/apps/dav/tests/unit/Connector/Sabre/PropFindMonitorPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/PropFindMonitorPluginTest.php new file mode 100644 index 00000000000..b528c3d731c --- /dev/null +++ b/apps/dav/tests/unit/Connector/Sabre/PropFindMonitorPluginTest.php @@ -0,0 +1,123 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace unit\Connector\Sabre; + +use OCA\DAV\Connector\Sabre\PropFindMonitorPlugin; +use OCA\DAV\Connector\Sabre\Server; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use Sabre\HTTP\Request; +use Sabre\HTTP\Response; +use Test\TestCase; + +class PropFindMonitorPluginTest extends TestCase { + + private PropFindMonitorPlugin $plugin; + private Server&MockObject $server; + private LoggerInterface&MockObject $logger; + private Request&MockObject $request; + private Response&MockObject $response; + + public static function dataTest(): array { + $minQueriesTrigger = PropFindMonitorPlugin::THRESHOLD_QUERY_FACTOR + * PropFindMonitorPlugin::THRESHOLD_NODES; + return [ + 'No queries logged' => [[], 0], + 'Plugins with queries in less than threshold nodes should not be logged' => [ + [ + [ + 'PluginName' => ['queries' => 100, 'nodes' + => PropFindMonitorPlugin::THRESHOLD_NODES - 1] + ], + [], + ], + 0 + ], + 'Plugins with query-to-node ratio less than threshold should not be logged' => [ + [ + [ + 'PluginName' => [ + 'queries' => $minQueriesTrigger - 1, + 'nodes' => PropFindMonitorPlugin::THRESHOLD_NODES ], + ], + [], + ], + 0 + ], + 'Plugins with more nodes scanned than queries executed should not be logged' => [ + [ + [ + 'PluginName' => [ + 'queries' => $minQueriesTrigger, + 'nodes' => PropFindMonitorPlugin::THRESHOLD_NODES * 2], + ], + [], + ], + 0 + ], + 'Plugins with queries only in highest depth level should not be logged' => [ + [ + [ + 'PluginName' => [ + 'queries' => $minQueriesTrigger, + 'nodes' => PropFindMonitorPlugin::THRESHOLD_NODES - 1 + ] + ], + [ + 'PluginName' => [ + 'queries' => $minQueriesTrigger * 2, + 'nodes' => PropFindMonitorPlugin::THRESHOLD_NODES + ] + ] + ], + 0 + ], + 'Plugins with too many queries should be logged' => [ + [ + [ + 'FirstPlugin' => [ + 'queries' => $minQueriesTrigger, + 'nodes' => PropFindMonitorPlugin::THRESHOLD_NODES, + ], + 'SecondPlugin' => [ + 'queries' => $minQueriesTrigger, + 'nodes' => PropFindMonitorPlugin::THRESHOLD_NODES, + ] + ], + [] + ], + 2 + ] + ]; + } + + /** + * @dataProvider dataTest + */ + public function test(array $queries, $expectedLogCalls): void { + $this->plugin->initialize($this->server); + $this->server->expects($this->once())->method('getPluginQueries') + ->willReturn($queries); + + $this->server->expects(empty($queries) ? $this->never() : $this->once()) + ->method('getLogger') + ->willReturn($this->logger); + + $this->logger->expects($this->exactly($expectedLogCalls))->method('error'); + $this->plugin->afterResponse($this->request, $this->response); + } + + protected function setUp(): void { + parent::setUp(); + + $this->plugin = new PropFindMonitorPlugin(); + $this->server = $this->createMock(Server::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->request = $this->createMock(Request::class); + $this->response = $this->createMock(Response::class); + } +} diff --git a/apps/dav/tests/unit/Connector/Sabre/PropfindCompressionPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/PropfindCompressionPluginTest.php index e048190e633..e6f696ed160 100644 --- a/apps/dav/tests/unit/Connector/Sabre/PropfindCompressionPluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/PropfindCompressionPluginTest.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.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: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\DAV\Tests\unit\Connector\Sabre; @@ -31,8 +14,7 @@ use Sabre\HTTP\Response; use Test\TestCase; class PropfindCompressionPluginTest extends TestCase { - /** @var PropfindCompressionPlugin */ - private $plugin; + private PropfindCompressionPlugin $plugin; protected function setUp(): void { parent::setUp(); @@ -40,7 +22,7 @@ class PropfindCompressionPluginTest extends TestCase { $this->plugin = new PropfindCompressionPlugin(); } - public function testNoHeader() { + public function testNoHeader(): void { $request = $this->createMock(Request::class); $response = $this->createMock(Response::class); @@ -55,7 +37,7 @@ class PropfindCompressionPluginTest extends TestCase { $this->assertSame($response, $result); } - public function testHeaderButNoGzip() { + public function testHeaderButNoGzip(): void { $request = $this->createMock(Request::class); $response = $this->createMock(Response::class); @@ -70,7 +52,7 @@ class PropfindCompressionPluginTest extends TestCase { $this->assertSame($response, $result); } - public function testHeaderGzipButNoStringBody() { + public function testHeaderGzipButNoStringBody(): void { $request = $this->createMock(Request::class); $response = $this->createMock(Response::class); @@ -86,7 +68,7 @@ class PropfindCompressionPluginTest extends TestCase { } - public function testProperGzip() { + public function testProperGzip(): void { $request = $this->createMock(Request::class); $response = $this->createMock(Response::class); diff --git a/apps/dav/tests/unit/Connector/Sabre/PublicAuthTest.php b/apps/dav/tests/unit/Connector/Sabre/PublicAuthTest.php new file mode 100644 index 00000000000..fef62b51c67 --- /dev/null +++ b/apps/dav/tests/unit/Connector/Sabre/PublicAuthTest.php @@ -0,0 +1,384 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2022-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OCA\DAV\Tests\unit\Connector; + +use OCA\DAV\Connector\Sabre\PublicAuth; +use OCP\IRequest; +use OCP\ISession; +use OCP\IURLGenerator; +use OCP\Security\Bruteforce\IThrottler; +use OCP\Share\Exceptions\ShareNotFound; +use OCP\Share\IManager; +use OCP\Share\IShare; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; + +/** + * Class PublicAuthTest + * + * @group DB + * + * @package OCA\DAV\Tests\unit\Connector + */ +class PublicAuthTest extends \Test\TestCase { + + private ISession&MockObject $session; + private IRequest&MockObject $request; + private IManager&MockObject $shareManager; + private IThrottler&MockObject $throttler; + private LoggerInterface&MockObject $logger; + private IURLGenerator&MockObject $urlGenerator; + private PublicAuth $auth; + + private bool|string $oldUser; + + protected function setUp(): void { + parent::setUp(); + + $this->session = $this->createMock(ISession::class); + $this->request = $this->createMock(IRequest::class); + $this->shareManager = $this->createMock(IManager::class); + $this->throttler = $this->createMock(IThrottler::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->urlGenerator = $this->createMock(IURLGenerator::class); + + $this->auth = new PublicAuth( + $this->request, + $this->shareManager, + $this->session, + $this->throttler, + $this->logger, + $this->urlGenerator, + ); + + // Store current user + $this->oldUser = \OC_User::getUser(); + } + + protected function tearDown(): void { + \OC_User::setIncognitoMode(false); + + // Set old user + \OC_User::setUserId($this->oldUser); + if ($this->oldUser !== false) { + \OC_Util::setupFS($this->oldUser); + } + + parent::tearDown(); + } + + public function testGetToken(): void { + $this->request->method('getPathInfo') + ->willReturn('/dav/files/GX9HSGQrGE'); + + $result = self::invokePrivate($this->auth, 'getToken'); + + $this->assertSame('GX9HSGQrGE', $result); + } + + public function testGetTokenInvalid(): void { + $this->request->method('getPathInfo') + ->willReturn('/dav/files'); + + $this->expectException(\Sabre\DAV\Exception\NotFound::class); + self::invokePrivate($this->auth, 'getToken'); + } + + public function testCheckTokenValidShare(): void { + $this->request->method('getPathInfo') + ->willReturn('/dav/files/GX9HSGQrGE'); + + $share = $this->createMock(IShare::class); + $share->method('getPassword')->willReturn(null); + + $this->shareManager->expects($this->once()) + ->method('getShareByToken') + ->with('GX9HSGQrGE') + ->willReturn($share); + + $result = self::invokePrivate($this->auth, 'checkToken'); + $this->assertSame([true, 'principals/GX9HSGQrGE'], $result); + } + + public function testCheckTokenInvalidShare(): void { + $this->request->method('getPathInfo') + ->willReturn('/dav/files/GX9HSGQrGE'); + + $this->shareManager + ->expects($this->once()) + ->method('getShareByToken') + ->with('GX9HSGQrGE') + ->willThrowException(new ShareNotFound()); + + $this->expectException(\Sabre\DAV\Exception\NotFound::class); + self::invokePrivate($this->auth, 'checkToken'); + } + + public function testCheckTokenAlreadyAuthenticated(): void { + $this->request->method('getPathInfo') + ->willReturn('/dav/files/GX9HSGQrGE'); + + $share = $this->createMock(IShare::class); + $share->method('getShareType')->willReturn(42); + + $this->shareManager->expects($this->once()) + ->method('getShareByToken') + ->with('GX9HSGQrGE') + ->willReturn($share); + + $this->session->method('exists')->with('public_link_authenticated')->willReturn(true); + $this->session->method('get')->with('public_link_authenticated')->willReturn('42'); + + $result = self::invokePrivate($this->auth, 'checkToken'); + $this->assertSame([true, 'principals/GX9HSGQrGE'], $result); + } + + public function testCheckTokenPasswordNotAuthenticated(): void { + $this->request->method('getPathInfo') + ->willReturn('/dav/files/GX9HSGQrGE'); + + $share = $this->createMock(IShare::class); + $share->method('getPassword')->willReturn('password'); + $share->method('getShareType')->willReturn(42); + + $this->shareManager->expects($this->once()) + ->method('getShareByToken') + ->with('GX9HSGQrGE') + ->willReturn($share); + + $this->session->method('exists')->with('public_link_authenticated')->willReturn(false); + + $this->expectException(\Sabre\DAV\Exception\NotAuthenticated::class); + self::invokePrivate($this->auth, 'checkToken'); + } + + public function testCheckTokenPasswordAuthenticatedWrongShare(): void { + $this->request->method('getPathInfo') + ->willReturn('/dav/files/GX9HSGQrGE'); + + $share = $this->createMock(IShare::class); + $share->method('getPassword')->willReturn('password'); + $share->method('getShareType')->willReturn(42); + + $this->shareManager->expects($this->once()) + ->method('getShareByToken') + ->with('GX9HSGQrGE') + ->willReturn($share); + + $this->session->method('exists')->with('public_link_authenticated')->willReturn(false); + $this->session->method('get')->with('public_link_authenticated')->willReturn('43'); + + $this->expectException(\Sabre\DAV\Exception\NotAuthenticated::class); + self::invokePrivate($this->auth, 'checkToken'); + } + + public function testNoShare(): void { + $this->request->method('getPathInfo') + ->willReturn('/dav/files/GX9HSGQrGE'); + + $this->shareManager->expects($this->once()) + ->method('getShareByToken') + ->with('GX9HSGQrGE') + ->willThrowException(new ShareNotFound()); + + $result = self::invokePrivate($this->auth, 'validateUserPass', ['username', 'password']); + + $this->assertFalse($result); + } + + public function testShareNoPassword(): void { + $this->request->method('getPathInfo') + ->willReturn('/dav/files/GX9HSGQrGE'); + + $share = $this->createMock(IShare::class); + $share->method('getPassword')->willReturn(null); + + $this->shareManager->expects($this->once()) + ->method('getShareByToken') + ->with('GX9HSGQrGE') + ->willReturn($share); + + $result = self::invokePrivate($this->auth, 'validateUserPass', ['username', 'password']); + + $this->assertTrue($result); + } + + public function testSharePasswordFancyShareType(): void { + $this->request->method('getPathInfo') + ->willReturn('/dav/files/GX9HSGQrGE'); + + $share = $this->createMock(IShare::class); + $share->method('getPassword')->willReturn('password'); + $share->method('getShareType')->willReturn(42); + + $this->shareManager->expects($this->once()) + ->method('getShareByToken') + ->with('GX9HSGQrGE') + ->willReturn($share); + + $result = self::invokePrivate($this->auth, 'validateUserPass', ['username', 'password']); + + $this->assertFalse($result); + } + + + public function testSharePasswordRemote(): void { + $this->request->method('getPathInfo') + ->willReturn('/dav/files/GX9HSGQrGE'); + + $share = $this->createMock(IShare::class); + $share->method('getPassword')->willReturn('password'); + $share->method('getShareType')->willReturn(IShare::TYPE_REMOTE); + + $this->shareManager->expects($this->once()) + ->method('getShareByToken') + ->with('GX9HSGQrGE') + ->willReturn($share); + + $result = self::invokePrivate($this->auth, 'validateUserPass', ['username', 'password']); + + $this->assertTrue($result); + } + + public function testSharePasswordLinkValidPassword(): void { + $this->request->method('getPathInfo') + ->willReturn('/dav/files/GX9HSGQrGE'); + + $share = $this->createMock(IShare::class); + $share->method('getPassword')->willReturn('password'); + $share->method('getShareType')->willReturn(IShare::TYPE_LINK); + + $this->shareManager->expects($this->once()) + ->method('getShareByToken') + ->with('GX9HSGQrGE') + ->willReturn($share); + + $this->shareManager->expects($this->once()) + ->method('checkPassword')->with( + $this->equalTo($share), + $this->equalTo('password') + )->willReturn(true); + + $result = self::invokePrivate($this->auth, 'validateUserPass', ['username', 'password']); + + $this->assertTrue($result); + } + + public function testSharePasswordMailValidPassword(): void { + $this->request->method('getPathInfo') + ->willReturn('/dav/files/GX9HSGQrGE'); + + $share = $this->createMock(IShare::class); + $share->method('getPassword')->willReturn('password'); + $share->method('getShareType')->willReturn(IShare::TYPE_EMAIL); + + $this->shareManager->expects($this->once()) + ->method('getShareByToken') + ->with('GX9HSGQrGE') + ->willReturn($share); + + $this->shareManager->expects($this->once()) + ->method('checkPassword')->with( + $this->equalTo($share), + $this->equalTo('password') + )->willReturn(true); + + $result = self::invokePrivate($this->auth, 'validateUserPass', ['username', 'password']); + + $this->assertTrue($result); + } + + public function testInvalidSharePasswordLinkValidSession(): void { + $this->request->method('getPathInfo') + ->willReturn('/dav/files/GX9HSGQrGE'); + + $share = $this->createMock(IShare::class); + $share->method('getPassword')->willReturn('password'); + $share->method('getShareType')->willReturn(IShare::TYPE_LINK); + $share->method('getId')->willReturn('42'); + + $this->shareManager->expects($this->once()) + ->method('getShareByToken') + ->with('GX9HSGQrGE') + ->willReturn($share); + + $this->shareManager->expects($this->once()) + ->method('checkPassword') + ->with( + $this->equalTo($share), + $this->equalTo('password') + )->willReturn(false); + + $this->session->method('exists')->with('public_link_authenticated')->willReturn(true); + $this->session->method('get')->with('public_link_authenticated')->willReturn('42'); + + $result = self::invokePrivate($this->auth, 'validateUserPass', ['username', 'password']); + + $this->assertTrue($result); + } + + public function testSharePasswordLinkInvalidSession(): void { + $this->request->method('getPathInfo') + ->willReturn('/dav/files/GX9HSGQrGE'); + + $share = $this->createMock(IShare::class); + $share->method('getPassword')->willReturn('password'); + $share->method('getShareType')->willReturn(IShare::TYPE_LINK); + $share->method('getId')->willReturn('42'); + + $this->shareManager->expects($this->once()) + ->method('getShareByToken') + ->with('GX9HSGQrGE') + ->willReturn($share); + + $this->shareManager->expects($this->once()) + ->method('checkPassword') + ->with( + $this->equalTo($share), + $this->equalTo('password') + )->willReturn(false); + + $this->session->method('exists')->with('public_link_authenticated')->willReturn(true); + $this->session->method('get')->with('public_link_authenticated')->willReturn('43'); + + $result = self::invokePrivate($this->auth, 'validateUserPass', ['username', 'password']); + + $this->assertFalse($result); + } + + + public function testSharePasswordMailInvalidSession(): void { + $this->request->method('getPathInfo') + ->willReturn('/dav/files/GX9HSGQrGE'); + + $share = $this->createMock(IShare::class); + $share->method('getPassword')->willReturn('password'); + $share->method('getShareType')->willReturn(IShare::TYPE_EMAIL); + $share->method('getId')->willReturn('42'); + + $this->shareManager->expects($this->once()) + ->method('getShareByToken') + ->with('GX9HSGQrGE') + ->willReturn($share); + + $this->shareManager->expects($this->once()) + ->method('checkPassword') + ->with( + $this->equalTo($share), + $this->equalTo('password') + )->willReturn(false); + + $this->session->method('exists')->with('public_link_authenticated')->willReturn(true); + $this->session->method('get')->with('public_link_authenticated')->willReturn('43'); + + $result = self::invokePrivate($this->auth, 'validateUserPass', ['username', 'password']); + + $this->assertFalse($result); + } +} diff --git a/apps/dav/tests/unit/Connector/Sabre/QuotaPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/QuotaPluginTest.php index 69b2140e640..6fe2d6ccabe 100644 --- a/apps/dav/tests/unit/Connector/Sabre/QuotaPluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/QuotaPluginTest.php @@ -1,32 +1,10 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * Copyright (c) 2013 Thomas Müller <thomas.mueller@tmit.eu> - * Copyright (c) 2013 Thomas Müller <thomas.mueller@tmit.eu> - * - * @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> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @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: 2013-2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OCA\DAV\Tests\unit\Connector\Sabre; @@ -35,83 +13,56 @@ use OCA\DAV\Connector\Sabre\QuotaPlugin; use OCP\Files\FileInfo; use Test\TestCase; -/** - * Copyright (c) 2013 Thomas Müller <thomas.mueller@tmit.eu> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ class QuotaPluginTest extends TestCase { + private \Sabre\DAV\Server $server; - /** @var \Sabre\DAV\Server | \PHPUnit\Framework\MockObject\MockObject */ - private $server; - - /** @var \OCA\DAV\Connector\Sabre\QuotaPlugin | \PHPUnit\Framework\MockObject\MockObject */ - private $plugin; + private QuotaPlugin $plugin; - private function init($quota, $checkedPath = '') { - $view = $this->buildFileViewMock($quota, $checkedPath); + private function init(int $quota, string $checkedPath = ''): void { + $view = $this->buildFileViewMock((string)$quota, $checkedPath); $this->server = new \Sabre\DAV\Server(); - $this->plugin = $this->getMockBuilder(QuotaPlugin::class) - ->setConstructorArgs([$view]) - ->setMethods(['getFileChunking']) - ->getMock(); + $this->plugin = new QuotaPlugin($view); $this->plugin->initialize($this->server); } - /** - * @dataProvider lengthProvider - */ - public function testLength($expected, $headers) { + #[\PHPUnit\Framework\Attributes\DataProvider('lengthProvider')] + public function testLength(?int $expected, array $headers): void { $this->init(0); - $this->plugin->expects($this->never()) - ->method('getFileChunking'); + $this->server->httpRequest = new \Sabre\HTTP\Request('POST', 'dummy.file', $headers); $length = $this->plugin->getLength(); $this->assertEquals($expected, $length); } - /** - * @dataProvider quotaOkayProvider - */ - public function testCheckQuota($quota, $headers) { + #[\PHPUnit\Framework\Attributes\DataProvider('quotaOkayProvider')] + public function testCheckQuota(int $quota, array $headers): void { $this->init($quota); - $this->plugin->expects($this->never()) - ->method('getFileChunking'); $this->server->httpRequest = new \Sabre\HTTP\Request('POST', 'dummy.file', $headers); $result = $this->plugin->checkQuota(''); $this->assertTrue($result); } - /** - * @dataProvider quotaExceededProvider - */ - public function testCheckExceededQuota($quota, $headers) { + #[\PHPUnit\Framework\Attributes\DataProvider('quotaExceededProvider')] + public function testCheckExceededQuota(int $quota, array $headers): void { $this->expectException(\Sabre\DAV\Exception\InsufficientStorage::class); $this->init($quota); - $this->plugin->expects($this->never()) - ->method('getFileChunking'); $this->server->httpRequest = new \Sabre\HTTP\Request('POST', 'dummy.file', $headers); $this->plugin->checkQuota(''); } - /** - * @dataProvider quotaOkayProvider - */ - public function testCheckQuotaOnPath($quota, $headers) { + #[\PHPUnit\Framework\Attributes\DataProvider('quotaOkayProvider')] + public function testCheckQuotaOnPath(int $quota, array $headers): void { $this->init($quota, 'sub/test.txt'); - $this->plugin->expects($this->never()) - ->method('getFileChunking'); $this->server->httpRequest = new \Sabre\HTTP\Request('POST', 'dummy.file', $headers); $result = $this->plugin->checkQuota('/sub/test.txt'); $this->assertTrue($result); } - public function quotaOkayProvider() { + public static function quotaOkayProvider(): array { return [ [1024, []], [1024, ['X-EXPECTED-ENTITY-LENGTH' => '1024']], @@ -130,7 +81,7 @@ class QuotaPluginTest extends TestCase { ]; } - public function quotaExceededProvider() { + public static function quotaExceededProvider(): array { return [ [1023, ['X-EXPECTED-ENTITY-LENGTH' => '1024']], [511, ['CONTENT-LENGTH' => '512']], @@ -138,7 +89,7 @@ class QuotaPluginTest extends TestCase { ]; } - public function lengthProvider() { + public static function lengthProvider(): array { return [ [null, []], [1024, ['X-EXPECTED-ENTITY-LENGTH' => '1024']], @@ -149,12 +100,12 @@ class QuotaPluginTest extends TestCase { [null, ['CONTENT-LENGTH' => 'A']], [1024, ['OC-TOTAL-LENGTH' => 'A', 'CONTENT-LENGTH' => '1024']], [1024, ['OC-TOTAL-LENGTH' => 'A', 'X-EXPECTED-ENTITY-LENGTH' => '1024']], - [null, ['OC-TOTAL-LENGTH' => '2048', 'X-EXPECTED-ENTITY-LENGTH' => 'A']], - [null, ['OC-TOTAL-LENGTH' => '2048', 'CONTENT-LENGTH' => 'A']], + [2048, ['OC-TOTAL-LENGTH' => '2048', 'X-EXPECTED-ENTITY-LENGTH' => 'A']], + [2048, ['OC-TOTAL-LENGTH' => '2048', 'CONTENT-LENGTH' => 'A']], ]; } - public function quotaChunkedOkProvider() { + public static function quotaChunkedOkProvider(): array { return [ [1024, 0, ['X-EXPECTED-ENTITY-LENGTH' => '1024']], [1024, 0, ['CONTENT-LENGTH' => '512']], @@ -173,30 +124,7 @@ class QuotaPluginTest extends TestCase { ]; } - /** - * @dataProvider quotaChunkedOkProvider - */ - public function testCheckQuotaChunkedOk($quota, $chunkTotalSize, $headers) { - $this->init($quota, 'sub/test.txt'); - - $mockChunking = $this->getMockBuilder(\OC_FileChunking::class) - ->disableOriginalConstructor() - ->getMock(); - $mockChunking->expects($this->once()) - ->method('getCurrentSize') - ->willReturn($chunkTotalSize); - - $this->plugin->expects($this->once()) - ->method('getFileChunking') - ->willReturn($mockChunking); - - $headers['OC-CHUNKED'] = 1; - $this->server->httpRequest = new \Sabre\HTTP\Request('POST', 'dummy.file', $headers); - $result = $this->plugin->checkQuota('/sub/test.txt-chunking-12345-3-1'); - $this->assertTrue($result); - } - - public function quotaChunkedFailProvider() { + public static function quotaChunkedFailProvider(): array { return [ [400, 0, ['X-EXPECTED-ENTITY-LENGTH' => '1024']], [400, 0, ['CONTENT-LENGTH' => '512']], @@ -208,39 +136,15 @@ class QuotaPluginTest extends TestCase { ]; } - /** - * @dataProvider quotaChunkedFailProvider - */ - public function testCheckQuotaChunkedFail($quota, $chunkTotalSize, $headers) { - $this->expectException(\Sabre\DAV\Exception\InsufficientStorage::class); - - $this->init($quota, 'sub/test.txt'); - - $mockChunking = $this->getMockBuilder(\OC_FileChunking::class) - ->disableOriginalConstructor() - ->getMock(); - $mockChunking->expects($this->once()) - ->method('getCurrentSize') - ->willReturn($chunkTotalSize); - - $this->plugin->expects($this->once()) - ->method('getFileChunking') - ->willReturn($mockChunking); - - $headers['OC-CHUNKED'] = 1; - $this->server->httpRequest = new \Sabre\HTTP\Request('POST', 'dummy.file', $headers); - $this->plugin->checkQuota('/sub/test.txt-chunking-12345-3-1'); - } - - private function buildFileViewMock($quota, $checkedPath) { - // mock filesysten + private function buildFileViewMock(string $quota, string $checkedPath): View { + // mock filesystem $view = $this->getMockBuilder(View::class) - ->setMethods(['free_space']) + ->onlyMethods(['free_space']) ->disableOriginalConstructor() ->getMock(); $view->expects($this->any()) ->method('free_space') - ->with($this->identicalTo($checkedPath)) + ->with($checkedPath) ->willReturn($quota); return $view; diff --git a/apps/dav/tests/unit/Connector/Sabre/RequestTest/Auth.php b/apps/dav/tests/unit/Connector/Sabre/RequestTest/Auth.php index 917c63038cb..b01807d5bbb 100644 --- a/apps/dav/tests/unit/Connector/Sabre/RequestTest/Auth.php +++ b/apps/dav/tests/unit/Connector/Sabre/RequestTest/Auth.php @@ -1,53 +1,30 @@ <?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 Robin Appelman <robin@icewind.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\Connector\Sabre\RequestTest; +use OCP\IUserSession; +use OCP\Server; use Sabre\DAV\Auth\Backend\BackendInterface; use Sabre\HTTP\RequestInterface; use Sabre\HTTP\ResponseInterface; class Auth implements BackendInterface { /** - * @var string - */ - private $user; - - /** - * @var string - */ - private $password; - - /** * Auth constructor. * * @param string $user * @param string $password */ - public function __construct($user, $password) { - $this->user = $user; - $this->password = $password; + public function __construct( + private $user, + private $password, + ) { } /** @@ -79,7 +56,7 @@ class Auth implements BackendInterface { * @return array */ public function check(RequestInterface $request, ResponseInterface $response) { - $userSession = \OC::$server->getUserSession(); + $userSession = Server::get(IUserSession::class); $result = $userSession->login($this->user, $this->password); if ($result) { //we need to pass the user name, which may differ from login name @@ -89,7 +66,7 @@ class Auth implements BackendInterface { \OC::$server->getUserFolder($user); return [true, "principals/$user"]; } - return [false, "login failed"]; + return [false, 'login failed']; } /** @@ -113,7 +90,7 @@ class Auth implements BackendInterface { * @param ResponseInterface $response * @return void */ - public function challenge(RequestInterface $request, ResponseInterface $response) { + public function challenge(RequestInterface $request, ResponseInterface $response): void { // TODO: Implement challenge() method. } } diff --git a/apps/dav/tests/unit/Connector/Sabre/RequestTest/DeleteTest.php b/apps/dav/tests/unit/Connector/Sabre/RequestTest/DeleteTest.php index b76564e59d4..7d3488e6b5a 100644 --- a/apps/dav/tests/unit/Connector/Sabre/RequestTest/DeleteTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/RequestTest/DeleteTest.php @@ -1,28 +1,15 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Robin Appelman <robin@icewind.nl> - * - * @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\Connector\Sabre\RequestTest; use OCP\AppFramework\Http; +use OCP\Files\FileInfo; /** * Class DeleteTest @@ -32,8 +19,8 @@ use OCP\AppFramework\Http; * @package OCA\DAV\Tests\unit\Connector\Sabre\RequestTest */ class DeleteTest extends RequestTestCase { - public function testBasicUpload() { - $user = $this->getUniqueID(); + public function testBasicUpload(): void { + $user = self::getUniqueID(); $view = $this->setupUser($user, 'pass'); $view->file_put_contents('foo.txt', 'asd'); @@ -44,7 +31,7 @@ class DeleteTest extends RequestTestCase { $mount->getStorage()->unlink($mount->getInternalPath($internalPath)); // cache entry still exists - $this->assertInstanceOf('\OCP\Files\FileInfo', $view->getFileInfo('foo.txt')); + $this->assertInstanceOf(FileInfo::class, $view->getFileInfo('foo.txt')); $response = $this->request($view, $user, 'pass', 'DELETE', '/foo.txt'); diff --git a/apps/dav/tests/unit/Connector/Sabre/RequestTest/DownloadTest.php b/apps/dav/tests/unit/Connector/Sabre/RequestTest/DownloadTest.php index ceae6fadf28..34171963ef0 100644 --- a/apps/dav/tests/unit/Connector/Sabre/RequestTest/DownloadTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/RequestTest/DownloadTest.php @@ -1,26 +1,10 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Robin Appelman <robin@icewind.nl> - * @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\Connector\Sabre\RequestTest; @@ -35,8 +19,8 @@ use OCP\Lock\ILockingProvider; * @package OCA\DAV\Tests\unit\Connector\Sabre\RequestTest */ class DownloadTest extends RequestTestCase { - public function testDownload() { - $user = $this->getUniqueID(); + public function testDownload(): void { + $user = self::getUniqueID(); $view = $this->setupUser($user, 'pass'); $view->file_put_contents('foo.txt', 'bar'); @@ -46,8 +30,8 @@ class DownloadTest extends RequestTestCase { $this->assertEquals(stream_get_contents($response->getBody()), 'bar'); } - public function testDownloadWriteLocked() { - $user = $this->getUniqueID(); + public function testDownloadWriteLocked(): void { + $user = self::getUniqueID(); $view = $this->setupUser($user, 'pass'); $view->file_put_contents('foo.txt', 'bar'); @@ -58,8 +42,8 @@ class DownloadTest extends RequestTestCase { $this->assertEquals(Http::STATUS_LOCKED, $result->getStatus()); } - public function testDownloadReadLocked() { - $user = $this->getUniqueID(); + public function testDownloadReadLocked(): void { + $user = self::getUniqueID(); $view = $this->setupUser($user, 'pass'); $view->file_put_contents('foo.txt', 'bar'); diff --git a/apps/dav/tests/unit/Connector/Sabre/RequestTest/EncryptionMasterKeyUploadTest.php b/apps/dav/tests/unit/Connector/Sabre/RequestTest/EncryptionMasterKeyUploadTest.php index f0fd9e5a833..615490ddc92 100644 --- a/apps/dav/tests/unit/Connector/Sabre/RequestTest/EncryptionMasterKeyUploadTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/RequestTest/EncryptionMasterKeyUploadTest.php @@ -1,27 +1,17 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * - * @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: 2017-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OCA\DAV\Tests\unit\Connector\Sabre\RequestTest; use OC\Files\View; +use OCP\IConfig; +use OCP\ITempManager; +use OCP\Server; use Test\Traits\EncryptionTrait; /** @@ -34,12 +24,12 @@ use Test\Traits\EncryptionTrait; class EncryptionMasterKeyUploadTest extends UploadTest { use EncryptionTrait; - protected function setupUser($name, $password) { + protected function setupUser($name, $password): View { $this->createUser($name, $password); - $tmpFolder = \OC::$server->getTempManager()->getTemporaryFolder(); + $tmpFolder = Server::get(ITempManager::class)->getTemporaryFolder(); $this->registerMount($name, '\OC\Files\Storage\Local', '/' . $name, ['datadir' => $tmpFolder]); // we use the master key - \OC::$server->getConfig()->setAppValue('encryption', 'useMasterKey', '1'); + Server::get(IConfig::class)->setAppValue('encryption', 'useMasterKey', '1'); $this->setupForUser($name, $password); $this->loginWithEncryption($name); return new View('/' . $name . '/files'); diff --git a/apps/dav/tests/unit/Connector/Sabre/RequestTest/EncryptionUploadTest.php b/apps/dav/tests/unit/Connector/Sabre/RequestTest/EncryptionUploadTest.php index da7c35560cb..efa7bb54cf8 100644 --- a/apps/dav/tests/unit/Connector/Sabre/RequestTest/EncryptionUploadTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/RequestTest/EncryptionUploadTest.php @@ -1,30 +1,17 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Joas Schilling <coding@schilljs.com> - * @author Robin Appelman <robin@icewind.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\Connector\Sabre\RequestTest; use OC\Files\View; +use OCP\IConfig; +use OCP\ITempManager; +use OCP\Server; use Test\Traits\EncryptionTrait; /** @@ -37,12 +24,12 @@ use Test\Traits\EncryptionTrait; class EncryptionUploadTest extends UploadTest { use EncryptionTrait; - protected function setupUser($name, $password) { + protected function setupUser($name, $password): View { $this->createUser($name, $password); - $tmpFolder = \OC::$server->getTempManager()->getTemporaryFolder(); + $tmpFolder = Server::get(ITempManager::class)->getTemporaryFolder(); $this->registerMount($name, '\OC\Files\Storage\Local', '/' . $name, ['datadir' => $tmpFolder]); // we use per-user keys - \OC::$server->getConfig()->setAppValue('encryption', 'useMasterKey', '0'); + Server::get(IConfig::class)->setAppValue('encryption', 'useMasterKey', '0'); $this->setupForUser($name, $password); $this->loginWithEncryption($name); return new View('/' . $name . '/files'); diff --git a/apps/dav/tests/unit/Connector/Sabre/RequestTest/ExceptionPlugin.php b/apps/dav/tests/unit/Connector/Sabre/RequestTest/ExceptionPlugin.php index eb459912bf4..0c53e4b1009 100644 --- a/apps/dav/tests/unit/Connector/Sabre/RequestTest/ExceptionPlugin.php +++ b/apps/dav/tests/unit/Connector/Sabre/RequestTest/ExceptionPlugin.php @@ -1,35 +1,22 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Robin Appelman <robin@icewind.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\Connector\Sabre\RequestTest; -class ExceptionPlugin extends \OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin { +use OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin; + +class ExceptionPlugin extends ExceptionLoggerPlugin { /** * @var \Throwable[] */ protected $exceptions = []; - public function logException(\Throwable $ex) { + public function logException(\Throwable $ex): void { $exceptionClass = get_class($ex); if (!isset($this->nonFatalExceptions[$exceptionClass])) { $this->exceptions[] = $ex; diff --git a/apps/dav/tests/unit/Connector/Sabre/RequestTest/PartFileInRootUploadTest.php b/apps/dav/tests/unit/Connector/Sabre/RequestTest/PartFileInRootUploadTest.php index 88ec848ad89..e6fa489fb24 100644 --- a/apps/dav/tests/unit/Connector/Sabre/RequestTest/PartFileInRootUploadTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/RequestTest/PartFileInRootUploadTest.php @@ -1,31 +1,15 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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 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\Connector\Sabre\RequestTest; +use OC\AllConfig; use OCP\IConfig; +use OCP\Server; /** * Class PartFileInRootUploadTest @@ -36,10 +20,8 @@ use OCP\IConfig; */ class PartFileInRootUploadTest extends UploadTest { protected function setUp(): void { - $config = \OC::$server->getConfig(); - $mockConfig = $this->getMockBuilder(IConfig::class) - ->disableOriginalConstructor() - ->getMock(); + $config = Server::get(IConfig::class); + $mockConfig = $this->createMock(IConfig::class); $mockConfig->expects($this->any()) ->method('getSystemValue') ->willReturnCallback(function ($key, $default) use ($config) { @@ -49,7 +31,7 @@ class PartFileInRootUploadTest extends UploadTest { return $config->getSystemValue($key, $default); } }); - $this->overwriteService('AllConfig', $mockConfig); + $this->overwriteService(AllConfig::class, $mockConfig); parent::setUp(); } diff --git a/apps/dav/tests/unit/Connector/Sabre/RequestTest/RequestTestCase.php b/apps/dav/tests/unit/Connector/Sabre/RequestTest/RequestTestCase.php index 5adfece42e8..404dc7fa5d7 100644 --- a/apps/dav/tests/unit/Connector/Sabre/RequestTest/RequestTestCase.php +++ b/apps/dav/tests/unit/Connector/Sabre/RequestTest/RequestTestCase.php @@ -1,37 +1,28 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @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 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\Connector\Sabre\RequestTest; use OC\Files\View; use OCA\DAV\Connector\Sabre\Server; use OCA\DAV\Connector\Sabre\ServerFactory; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\Mount\IMountManager; +use OCP\IConfig; +use OCP\IDBConnection; +use OCP\IPreview; use OCP\IRequest; +use OCP\IRequestId; +use OCP\ITagManager; +use OCP\ITempManager; +use OCP\IUserSession; +use OCP\L10N\IFactory; +use Psr\Log\LoggerInterface; use Sabre\HTTP\Request; use Test\TestCase; use Test\Traits\MountProviderTrait; @@ -40,11 +31,7 @@ use Test\Traits\UserTrait; abstract class RequestTestCase extends TestCase { use UserTrait; use MountProviderTrait; - - /** - * @var \OCA\DAV\Connector\Sabre\ServerFactory - */ - protected $serverFactory; + protected ServerFactory $serverFactory; protected function getStream($string) { $stream = fopen('php://temp', 'r+'); @@ -56,34 +43,30 @@ abstract class RequestTestCase extends TestCase { protected function setUp(): void { parent::setUp(); - unset($_SERVER['HTTP_OC_CHUNKED']); - $this->serverFactory = new ServerFactory( - \OC::$server->getConfig(), - \OC::$server->getLogger(), - \OC::$server->getDatabaseConnection(), - \OC::$server->getUserSession(), - \OC::$server->getMountManager(), - \OC::$server->getTagManager(), - $this->getMockBuilder(IRequest::class) - ->disableOriginalConstructor() - ->getMock(), - \OC::$server->getPreviewManager(), - \OC::$server->getEventDispatcher(), - \OC::$server->getL10N('dav') + \OCP\Server::get(IConfig::class), + \OCP\Server::get(LoggerInterface::class), + \OCP\Server::get(IDBConnection::class), + \OCP\Server::get(IUserSession::class), + \OCP\Server::get(IMountManager::class), + \OCP\Server::get(ITagManager::class), + $this->createMock(IRequest::class), + \OCP\Server::get(IPreview::class), + \OCP\Server::get(IEventDispatcher::class), + \OCP\Server::get(IFactory::class)->get('dav'), ); } - protected function setupUser($name, $password) { + protected function setupUser($name, $password): View { $this->createUser($name, $password); - $tmpFolder = \OC::$server->getTempManager()->getTemporaryFolder(); + $tmpFolder = \OCP\Server::get(ITempManager::class)->getTemporaryFolder(); $this->registerMount($name, '\OC\Files\Storage\Local', '/' . $name, ['datadir' => $tmpFolder]); - $this->loginAsUser($name); + self::loginAsUser($name); return new View('/' . $name . '/files'); } /** - * @param \OC\Files\View $view the view to run the webdav server against + * @param View $view the view to run the webdav server against * @param string $user * @param string $password * @param string $method @@ -98,26 +81,31 @@ abstract class RequestTestCase extends TestCase { $body = $this->getStream($body); } $this->logout(); - $exceptionPlugin = new ExceptionPlugin('webdav', null); + $exceptionPlugin = new ExceptionPlugin('webdav', \OCP\Server::get(LoggerInterface::class)); $server = $this->getSabreServer($view, $user, $password, $exceptionPlugin); $request = new Request($method, $url, $headers, $body); // since sabre catches all exceptions we need to save them and throw them from outside the sabre server - $originalServer = $_SERVER; - + $serverParams = []; if (is_array($headers)) { foreach ($headers as $header => $value) { - $_SERVER['HTTP_' . strtoupper(str_replace('-', '_', $header))] = $value; + $serverParams['HTTP_' . strtoupper(str_replace('-', '_', $header))] = $value; } } + $ncRequest = new \OC\AppFramework\Http\Request([ + 'server' => $serverParams + ], $this->createMock(IRequestId::class), $this->createMock(IConfig::class), null); + + $this->overwriteService(IRequest::class, $ncRequest); $result = $this->makeRequest($server, $request); + $this->restoreService(IRequest::class); + foreach ($exceptionPlugin->getExceptions() as $exception) { throw $exception; } - $_SERVER = $originalServer; return $result; } @@ -145,7 +133,7 @@ abstract class RequestTestCase extends TestCase { $authBackend = new Auth($user, $password); $authPlugin = new \Sabre\DAV\Auth\Plugin($authBackend); - $server = $this->serverFactory->createServer('/', 'dummy', $authPlugin, function () use ($view) { + $server = $this->serverFactory->createServer(false, '/', 'dummy', $authPlugin, function () use ($view) { return $view; }); $server->addPlugin($exceptionPlugin); diff --git a/apps/dav/tests/unit/Connector/Sabre/RequestTest/Sapi.php b/apps/dav/tests/unit/Connector/Sabre/RequestTest/Sapi.php index 4a2e025f018..08d774e56b8 100644 --- a/apps/dav/tests/unit/Connector/Sabre/RequestTest/Sapi.php +++ b/apps/dav/tests/unit/Connector/Sabre/RequestTest/Sapi.php @@ -1,25 +1,10 @@ <?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 Robin Appelman <robin@icewind.nl> - * - * @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\Connector\Sabre\RequestTest; @@ -28,11 +13,6 @@ use Sabre\HTTP\Response; class Sapi { /** - * @var \Sabre\HTTP\Request - */ - private $request; - - /** * @var \Sabre\HTTP\Response */ private $response; @@ -47,15 +27,16 @@ class Sapi { return $this->request; } - public function __construct(Request $request) { - $this->request = $request; + public function __construct( + private Request $request, + ) { } /** * @param \Sabre\HTTP\Response $response * @return void */ - public function sendResponse(Response $response) { + public function sendResponse(Response $response): void { // we need to copy the body since we close the source stream $copyStream = fopen('php://temp', 'r+'); if (is_string($response->getBody())) { diff --git a/apps/dav/tests/unit/Connector/Sabre/RequestTest/UploadTest.php b/apps/dav/tests/unit/Connector/Sabre/RequestTest/UploadTest.php index 9f7d381ad14..5c6d0f03334 100644 --- a/apps/dav/tests/unit/Connector/Sabre/RequestTest/UploadTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/RequestTest/UploadTest.php @@ -1,26 +1,10 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Robin Appelman <robin@icewind.nl> - * @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\Connector\Sabre\RequestTest; @@ -35,8 +19,8 @@ use OCP\Lock\ILockingProvider; * @package OCA\DAV\Tests\unit\Connector\Sabre\RequestTest */ class UploadTest extends RequestTestCase { - public function testBasicUpload() { - $user = $this->getUniqueID(); + public function testBasicUpload(): void { + $user = self::getUniqueID(); $view = $this->setupUser($user, 'pass'); $this->assertFalse($view->file_exists('foo.txt')); @@ -51,8 +35,8 @@ class UploadTest extends RequestTestCase { $this->assertEquals(3, $info->getSize()); } - public function testUploadOverWrite() { - $user = $this->getUniqueID(); + public function testUploadOverWrite(): void { + $user = self::getUniqueID(); $view = $this->setupUser($user, 'pass'); $view->file_put_contents('foo.txt', 'foobar'); @@ -67,8 +51,8 @@ class UploadTest extends RequestTestCase { $this->assertEquals(3, $info->getSize()); } - public function testUploadOverWriteReadLocked() { - $user = $this->getUniqueID(); + public function testUploadOverWriteReadLocked(): void { + $user = self::getUniqueID(); $view = $this->setupUser($user, 'pass'); $view->file_put_contents('foo.txt', 'bar'); @@ -79,8 +63,8 @@ class UploadTest extends RequestTestCase { $this->assertEquals(Http::STATUS_LOCKED, $result->getStatus()); } - public function testUploadOverWriteWriteLocked() { - $user = $this->getUniqueID(); + public function testUploadOverWriteWriteLocked(): void { + $user = self::getUniqueID(); $view = $this->setupUser($user, 'pass'); $this->loginAsUser($user); @@ -91,115 +75,4 @@ class UploadTest extends RequestTestCase { $result = $this->request($view, $user, 'pass', 'PUT', '/foo.txt', 'asd'); $this->assertEquals(Http::STATUS_LOCKED, $result->getStatus()); } - - public function testChunkedUpload() { - $user = $this->getUniqueID(); - $view = $this->setupUser($user, 'pass'); - - $this->assertFalse($view->file_exists('foo.txt')); - $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-0', 'asd', ['OC-Chunked' => '1']); - - $this->assertEquals(201, $response->getStatus()); - $this->assertFalse($view->file_exists('foo.txt')); - - $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-1', 'bar', ['OC-Chunked' => '1']); - - $this->assertEquals(Http::STATUS_CREATED, $response->getStatus()); - $this->assertTrue($view->file_exists('foo.txt')); - - $this->assertEquals('asdbar', $view->file_get_contents('foo.txt')); - - $info = $view->getFileInfo('foo.txt'); - $this->assertInstanceOf('\OC\Files\FileInfo', $info); - $this->assertEquals(6, $info->getSize()); - } - - public function testChunkedUploadOverWrite() { - $user = $this->getUniqueID(); - $view = $this->setupUser($user, 'pass'); - - $view->file_put_contents('foo.txt', 'bar'); - $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-0', 'asd', ['OC-Chunked' => '1']); - - $this->assertEquals(Http::STATUS_CREATED, $response->getStatus()); - $this->assertEquals('bar', $view->file_get_contents('foo.txt')); - - $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-1', 'bar', ['OC-Chunked' => '1']); - - $this->assertEquals(Http::STATUS_CREATED, $response->getStatus()); - - $this->assertEquals('asdbar', $view->file_get_contents('foo.txt')); - - $info = $view->getFileInfo('foo.txt'); - $this->assertInstanceOf('\OC\Files\FileInfo', $info); - $this->assertEquals(6, $info->getSize()); - } - - public function testChunkedUploadOutOfOrder() { - $user = $this->getUniqueID(); - $view = $this->setupUser($user, 'pass'); - - $this->assertFalse($view->file_exists('foo.txt')); - $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-1', 'bar', ['OC-Chunked' => '1']); - - $this->assertEquals(Http::STATUS_CREATED, $response->getStatus()); - $this->assertFalse($view->file_exists('foo.txt')); - - $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-0', 'asd', ['OC-Chunked' => '1']); - - $this->assertEquals(201, $response->getStatus()); - $this->assertTrue($view->file_exists('foo.txt')); - - $this->assertEquals('asdbar', $view->file_get_contents('foo.txt')); - - $info = $view->getFileInfo('foo.txt'); - $this->assertInstanceOf('\OC\Files\FileInfo', $info); - $this->assertEquals(6, $info->getSize()); - } - - public function testChunkedUploadOutOfOrderReadLocked() { - $user = $this->getUniqueID(); - $view = $this->setupUser($user, 'pass'); - - $this->assertFalse($view->file_exists('foo.txt')); - - $view->lockFile('/foo.txt', ILockingProvider::LOCK_SHARED); - - try { - $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-1', 'bar', ['OC-Chunked' => '1']); - } catch (\OCA\DAV\Connector\Sabre\Exception\FileLocked $e) { - $this->fail('Didn\'t expect locked error for the first chunk on read lock'); - return; - } - - $this->assertEquals(Http::STATUS_CREATED, $response->getStatus()); - $this->assertFalse($view->file_exists('foo.txt')); - - // last chunk should trigger the locked error since it tries to assemble - $result = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-0', 'asd', ['OC-Chunked' => '1']); - $this->assertEquals(Http::STATUS_LOCKED, $result->getStatus()); - } - - public function testChunkedUploadOutOfOrderWriteLocked() { - $user = $this->getUniqueID(); - $view = $this->setupUser($user, 'pass'); - - $this->assertFalse($view->file_exists('foo.txt')); - - $view->lockFile('/foo.txt', ILockingProvider::LOCK_EXCLUSIVE); - - try { - $response = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-1', 'bar', ['OC-Chunked' => '1']); - } catch (\OCA\DAV\Connector\Sabre\Exception\FileLocked $e) { - $this->fail('Didn\'t expect locked error for the first chunk on write lock'); // maybe forbid this in the future for write locks only? - return; - } - - $this->assertEquals(Http::STATUS_CREATED, $response->getStatus()); - $this->assertFalse($view->file_exists('foo.txt')); - - // last chunk should trigger the locked error since it tries to assemble - $result = $this->request($view, $user, 'pass', 'PUT', '/foo.txt-chunking-123-2-0', 'asd', ['OC-Chunked' => '1']); - $this->assertEquals(Http::STATUS_LOCKED, $result->getStatus()); - } } diff --git a/apps/dav/tests/unit/Connector/Sabre/SharesPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/SharesPluginTest.php index a81226ccb5e..1c8e29dab38 100644 --- a/apps/dav/tests/unit/Connector/Sabre/SharesPluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/SharesPluginTest.php @@ -1,72 +1,34 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Maxence Lange <maxence@nextcloud.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @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\Connector\Sabre; use OCA\DAV\Connector\Sabre\Directory; use OCA\DAV\Connector\Sabre\File; use OCA\DAV\Connector\Sabre\Node; +use OCA\DAV\Connector\Sabre\SharesPlugin; use OCA\DAV\Upload\UploadFile; use OCP\Files\Folder; use OCP\IUser; use OCP\IUserSession; use OCP\Share\IManager; use OCP\Share\IShare; +use PHPUnit\Framework\MockObject\MockObject; use Sabre\DAV\Tree; class SharesPluginTest extends \Test\TestCase { - public const SHARETYPES_PROPERTYNAME = \OCA\DAV\Connector\Sabre\SharesPlugin::SHARETYPES_PROPERTYNAME; - - /** - * @var \Sabre\DAV\Server - */ - private $server; - - /** - * @var \Sabre\DAV\Tree - */ - private $tree; + public const SHARETYPES_PROPERTYNAME = SharesPlugin::SHARETYPES_PROPERTYNAME; - /** - * @var \OCP\Share\IManager - */ - private $shareManager; - - /** - * @var \OCP\Files\Folder - */ - private $userFolder; - - /** - * @var \OCA\DAV\Connector\Sabre\SharesPlugin - */ - private $plugin; + private \Sabre\DAV\Server $server; + private \Sabre\DAV\Tree&MockObject $tree; + private \OCP\Share\IManager&MockObject $shareManager; + private Folder&MockObject $userFolder; + private SharesPlugin $plugin; protected function setUp(): void { parent::setUp(); @@ -83,7 +45,7 @@ class SharesPluginTest extends \Test\TestCase { ->willReturn($user); $this->userFolder = $this->createMock(Folder::class); - $this->plugin = new \OCA\DAV\Connector\Sabre\SharesPlugin( + $this->plugin = new SharesPlugin( $this->tree, $userSession, $this->userFolder, @@ -92,13 +54,9 @@ class SharesPluginTest extends \Test\TestCase { $this->plugin->initialize($this->server); } - /** - * @dataProvider sharesGetPropertiesDataProvider - */ - public function testGetProperties($shareTypes) { - $sabreNode = $this->getMockBuilder(Node::class) - ->disableOriginalConstructor() - ->getMock(); + #[\PHPUnit\Framework\Attributes\DataProvider('sharesGetPropertiesDataProvider')] + public function testGetProperties(array $shareTypes): void { + $sabreNode = $this->createMock(Node::class); $sabreNode->expects($this->any()) ->method('getId') ->willReturn(123); @@ -107,9 +65,7 @@ class SharesPluginTest extends \Test\TestCase { ->willReturn('/subdir'); // node API nodes - $node = $this->getMockBuilder(Folder::class) - ->disableOriginalConstructor() - ->getMock(); + $node = $this->createMock(Folder::class); $sabreNode->method('getNode') ->willReturn($node); @@ -119,7 +75,7 @@ class SharesPluginTest extends \Test\TestCase { ->with( $this->equalTo('user1'), $this->anything(), - $this->anything(), + $this->equalTo($node), $this->equalTo(false), $this->equalTo(-1) ) @@ -133,6 +89,16 @@ class SharesPluginTest extends \Test\TestCase { return []; }); + $this->shareManager->expects($this->any()) + ->method('getSharedWith') + ->with( + $this->equalTo('user1'), + $this->anything(), + $this->equalTo($node), + $this->equalTo(-1) + ) + ->willReturn([]); + $propFind = new \Sabre\DAV\PropFind( '/dummyPath', [self::SHARETYPES_PROPERTYNAME], @@ -151,10 +117,8 @@ class SharesPluginTest extends \Test\TestCase { $this->assertEquals($shareTypes, $result[200][self::SHARETYPES_PROPERTYNAME]->getShareTypes()); } - /** - * @dataProvider sharesGetPropertiesDataProvider - */ - public function testPreloadThenGetProperties($shareTypes) { + #[\PHPUnit\Framework\Attributes\DataProvider('sharesGetPropertiesDataProvider')] + public function testPreloadThenGetProperties(array $shareTypes): void { $sabreNode1 = $this->createMock(File::class); $sabreNode1->method('getId') ->willReturn(111); @@ -193,7 +157,7 @@ class SharesPluginTest extends \Test\TestCase { ->willReturn($node2); $dummyShares = array_map(function ($type) { - $share = $this->getMockBuilder(IShare::class)->getMock(); + $share = $this->createMock(IShare::class); $share->expects($this->any()) ->method('getShareType') ->willReturn($type); @@ -222,6 +186,16 @@ class SharesPluginTest extends \Test\TestCase { }); $this->shareManager->expects($this->any()) + ->method('getSharedWith') + ->with( + $this->equalTo('user1'), + $this->anything(), + $this->equalTo($node), + $this->equalTo(-1) + ) + ->willReturn([]); + + $this->shareManager->expects($this->any()) ->method('getSharesInFolder') ->with( $this->equalTo('user1'), @@ -269,7 +243,7 @@ class SharesPluginTest extends \Test\TestCase { $this->assertEquals($shareTypes, $result[200][self::SHARETYPES_PROPERTYNAME]->getShareTypes()); } - public function sharesGetPropertiesDataProvider() { + public static function sharesGetPropertiesDataProvider(): array { return [ [[]], [[IShare::TYPE_USER]], @@ -278,6 +252,7 @@ class SharesPluginTest extends \Test\TestCase { [[IShare::TYPE_REMOTE]], [[IShare::TYPE_ROOM]], [[IShare::TYPE_DECK]], + [[IShare::TYPE_SCIENCEMESH]], [[IShare::TYPE_USER, IShare::TYPE_GROUP]], [[IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_LINK]], [[IShare::TYPE_USER, IShare::TYPE_LINK]], @@ -287,9 +262,7 @@ class SharesPluginTest extends \Test\TestCase { } public function testGetPropertiesSkipChunks(): void { - $sabreNode = $this->getMockBuilder(UploadFile::class) - ->disableOriginalConstructor() - ->getMock(); + $sabreNode = $this->createMock(UploadFile::class); $propFind = new \Sabre\DAV\PropFind( '/dummyPath', diff --git a/apps/dav/tests/unit/Connector/Sabre/TagsPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/TagsPluginTest.php index 995342db003..5003280bfdc 100644 --- a/apps/dav/tests/unit/Connector/Sabre/TagsPluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/TagsPluginTest.php @@ -1,105 +1,66 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * Copyright (c) 2014 Vincent Petry <pvince81@owncloud.com> - * Copyright (c) 2014 Vincent Petry <pvince81@owncloud.com> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @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> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @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: 2014-2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OCA\DAV\Tests\unit\Connector\Sabre; use OCA\DAV\Connector\Sabre\Directory; use OCA\DAV\Connector\Sabre\File; use OCA\DAV\Connector\Sabre\Node; +use OCA\DAV\Connector\Sabre\TagList; +use OCA\DAV\Connector\Sabre\TagsPlugin; use OCA\DAV\Upload\UploadFile; +use OCP\EventDispatcher\IEventDispatcher; use OCP\ITagManager; use OCP\ITags; +use OCP\IUser; +use OCP\IUserSession; +use PHPUnit\Framework\MockObject\MockObject; use Sabre\DAV\Tree; -/** - * Copyright (c) 2014 Vincent Petry <pvince81@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ class TagsPluginTest extends \Test\TestCase { - public const TAGS_PROPERTYNAME = \OCA\DAV\Connector\Sabre\TagsPlugin::TAGS_PROPERTYNAME; - public const FAVORITE_PROPERTYNAME = \OCA\DAV\Connector\Sabre\TagsPlugin::FAVORITE_PROPERTYNAME; - public const TAG_FAVORITE = \OCA\DAV\Connector\Sabre\TagsPlugin::TAG_FAVORITE; - - /** - * @var \Sabre\DAV\Server - */ - private $server; - - /** - * @var Tree - */ - private $tree; - - /** - * @var \OCP\ITagManager - */ - private $tagManager; - - /** - * @var \OCP\ITags - */ - private $tagger; - - /** - * @var \OCA\DAV\Connector\Sabre\TagsPlugin - */ - private $plugin; + public const TAGS_PROPERTYNAME = TagsPlugin::TAGS_PROPERTYNAME; + public const FAVORITE_PROPERTYNAME = TagsPlugin::FAVORITE_PROPERTYNAME; + public const TAG_FAVORITE = TagsPlugin::TAG_FAVORITE; + + private \Sabre\DAV\Server $server; + private Tree&MockObject $tree; + private ITagManager&MockObject $tagManager; + private ITags&MockObject $tagger; + private IEventDispatcher&MockObject $eventDispatcher; + private IUserSession&MockObject $userSession; + private TagsPlugin $plugin; protected function setUp(): void { parent::setUp(); + $this->server = new \Sabre\DAV\Server(); - $this->tree = $this->getMockBuilder(Tree::class) - ->disableOriginalConstructor() - ->getMock(); - $this->tagger = $this->getMockBuilder(ITags::class) - ->disableOriginalConstructor() - ->getMock(); - $this->tagManager = $this->getMockBuilder(ITagManager::class) - ->disableOriginalConstructor() - ->getMock(); + $this->tree = $this->createMock(Tree::class); + $this->tagger = $this->createMock(ITags::class); + $this->tagManager = $this->createMock(ITagManager::class); + $this->eventDispatcher = $this->createMock(IEventDispatcher::class); + $user = $this->createMock(IUser::class); + + $this->userSession = $this->createMock(IUserSession::class); + $this->userSession->expects($this->any()) + ->method('getUser') + ->withAnyParameters() + ->willReturn($user); $this->tagManager->expects($this->any()) ->method('load') ->with('files') ->willReturn($this->tagger); - $this->plugin = new \OCA\DAV\Connector\Sabre\TagsPlugin($this->tree, $this->tagManager); + $this->plugin = new TagsPlugin($this->tree, $this->tagManager, $this->eventDispatcher, $this->userSession); $this->plugin->initialize($this->server); } - /** - * @dataProvider tagsGetPropertiesDataProvider - */ - public function testGetProperties($tags, $requestedProperties, $expectedProperties) { - $node = $this->getMockBuilder(Node::class) - ->disableOriginalConstructor() - ->getMock(); + #[\PHPUnit\Framework\Attributes\DataProvider('tagsGetPropertiesDataProvider')] + public function testGetProperties(array $tags, array $requestedProperties, array $expectedProperties): void { + $node = $this->createMock(Node::class); $node->expects($this->any()) ->method('getId') ->willReturn(123); @@ -132,19 +93,13 @@ class TagsPluginTest extends \Test\TestCase { $this->assertEquals($expectedProperties, $result); } - /** - * @dataProvider tagsGetPropertiesDataProvider - */ - public function testPreloadThenGetProperties($tags, $requestedProperties, $expectedProperties) { - $node1 = $this->getMockBuilder(File::class) - ->disableOriginalConstructor() - ->getMock(); + #[\PHPUnit\Framework\Attributes\DataProvider('tagsGetPropertiesDataProvider')] + public function testPreloadThenGetProperties(array $tags, array $requestedProperties, array $expectedProperties): void { + $node1 = $this->createMock(File::class); $node1->expects($this->any()) ->method('getId') ->willReturn(111); - $node2 = $this->getMockBuilder(File::class) - ->disableOriginalConstructor() - ->getMock(); + $node2 = $this->createMock(File::class); $node2->expects($this->any()) ->method('getId') ->willReturn(222); @@ -157,9 +112,7 @@ class TagsPluginTest extends \Test\TestCase { $expectedCallCount = 1; } - $node = $this->getMockBuilder(Directory::class) - ->disableOriginalConstructor() - ->getMock(); + $node = $this->createMock(Directory::class); $node->expects($this->any()) ->method('getId') ->willReturn(123); @@ -214,7 +167,7 @@ class TagsPluginTest extends \Test\TestCase { $this->assertEquals($expectedProperties, $result); } - public function tagsGetPropertiesDataProvider() { + public static function tagsGetPropertiesDataProvider(): array { return [ // request both, receive both [ @@ -222,7 +175,7 @@ class TagsPluginTest extends \Test\TestCase { [self::TAGS_PROPERTYNAME, self::FAVORITE_PROPERTYNAME], [ 200 => [ - self::TAGS_PROPERTYNAME => new \OCA\DAV\Connector\Sabre\TagList(['tag1', 'tag2']), + self::TAGS_PROPERTYNAME => new TagList(['tag1', 'tag2']), self::FAVORITE_PROPERTYNAME => true, ] ] @@ -233,7 +186,7 @@ class TagsPluginTest extends \Test\TestCase { [self::TAGS_PROPERTYNAME], [ 200 => [ - self::TAGS_PROPERTYNAME => new \OCA\DAV\Connector\Sabre\TagList(['tag1', 'tag2']), + self::TAGS_PROPERTYNAME => new TagList(['tag1', 'tag2']), ] ] ], @@ -261,7 +214,7 @@ class TagsPluginTest extends \Test\TestCase { [self::TAGS_PROPERTYNAME, self::FAVORITE_PROPERTYNAME], [ 200 => [ - self::TAGS_PROPERTYNAME => new \OCA\DAV\Connector\Sabre\TagList([]), + self::TAGS_PROPERTYNAME => new TagList([]), self::FAVORITE_PROPERTYNAME => false, ] ] @@ -270,9 +223,7 @@ class TagsPluginTest extends \Test\TestCase { } public function testGetPropertiesSkipChunks(): void { - $sabreNode = $this->getMockBuilder(UploadFile::class) - ->disableOriginalConstructor() - ->getMock(); + $sabreNode = $this->createMock(UploadFile::class); $propFind = new \Sabre\DAV\PropFind( '/dummyPath', @@ -289,12 +240,10 @@ class TagsPluginTest extends \Test\TestCase { $this->assertCount(2, $result[404]); } - public function testUpdateTags() { + public function testUpdateTags(): void { // this test will replace the existing tags "tagremove" with "tag1" and "tag2" // and keep "tagkeep" - $node = $this->getMockBuilder(Node::class) - ->disableOriginalConstructor() - ->getMock(); + $node = $this->createMock(Node::class); $node->expects($this->any()) ->method('getId') ->willReturn(123); @@ -304,27 +253,31 @@ class TagsPluginTest extends \Test\TestCase { ->with('/dummypath') ->willReturn($node); - $this->tagger->expects($this->at(0)) + $this->tagger->expects($this->once()) ->method('getTagsForObjects') ->with($this->equalTo([123])) ->willReturn([123 => ['tagkeep', 'tagremove', self::TAG_FAVORITE]]); // then tag as tag1 and tag2 - $this->tagger->expects($this->at(1)) - ->method('tagAs') - ->with(123, 'tag1'); - $this->tagger->expects($this->at(2)) + $calls = [ + [123, 'tag1'], + [123, 'tag2'], + ]; + $this->tagger->expects($this->exactly(count($calls))) ->method('tagAs') - ->with(123, 'tag2'); + ->willReturnCallback(function () use (&$calls): void { + $expected = array_shift($calls); + $this->assertEquals($expected, func_get_args()); + }); // it will untag tag3 - $this->tagger->expects($this->at(3)) + $this->tagger->expects($this->once()) ->method('unTag') ->with(123, 'tagremove'); // properties to set $propPatch = new \Sabre\DAV\PropPatch([ - self::TAGS_PROPERTYNAME => new \OCA\DAV\Connector\Sabre\TagList(['tag1', 'tag2', 'tagkeep']) + self::TAGS_PROPERTYNAME => new TagList(['tag1', 'tag2', 'tagkeep']) ]); $this->plugin->handleUpdateProperties( @@ -339,13 +292,11 @@ class TagsPluginTest extends \Test\TestCase { $result = $propPatch->getResult(); $this->assertEquals(200, $result[self::TAGS_PROPERTYNAME]); - $this->assertFalse(isset($result[self::FAVORITE_PROPERTYNAME])); + $this->assertArrayNotHasKey(self::FAVORITE_PROPERTYNAME, $result); } - public function testUpdateTagsFromScratch() { - $node = $this->getMockBuilder(Node::class) - ->disableOriginalConstructor() - ->getMock(); + public function testUpdateTagsFromScratch(): void { + $node = $this->createMock(Node::class); $node->expects($this->any()) ->method('getId') ->willReturn(123); @@ -355,22 +306,26 @@ class TagsPluginTest extends \Test\TestCase { ->with('/dummypath') ->willReturn($node); - $this->tagger->expects($this->at(0)) + $this->tagger->expects($this->once()) ->method('getTagsForObjects') ->with($this->equalTo([123])) ->willReturn([]); // then tag as tag1 and tag2 - $this->tagger->expects($this->at(1)) - ->method('tagAs') - ->with(123, 'tag1'); - $this->tagger->expects($this->at(2)) + $calls = [ + [123, 'tag1'], + [123, 'tag2'], + ]; + $this->tagger->expects($this->exactly(count($calls))) ->method('tagAs') - ->with(123, 'tag2'); + ->willReturnCallback(function () use (&$calls): void { + $expected = array_shift($calls); + $this->assertEquals($expected, func_get_args()); + }); // properties to set $propPatch = new \Sabre\DAV\PropPatch([ - self::TAGS_PROPERTYNAME => new \OCA\DAV\Connector\Sabre\TagList(['tag1', 'tag2', 'tagkeep']) + self::TAGS_PROPERTYNAME => new TagList(['tag1', 'tag2']) ]); $this->plugin->handleUpdateProperties( @@ -385,15 +340,13 @@ class TagsPluginTest extends \Test\TestCase { $result = $propPatch->getResult(); $this->assertEquals(200, $result[self::TAGS_PROPERTYNAME]); - $this->assertFalse(false, isset($result[self::FAVORITE_PROPERTYNAME])); + $this->assertArrayNotHasKey(self::FAVORITE_PROPERTYNAME, $result); } - public function testUpdateFav() { + public function testUpdateFav(): void { // this test will replace the existing tags "tagremove" with "tag1" and "tag2" // and keep "tagkeep" - $node = $this->getMockBuilder(Node::class) - ->disableOriginalConstructor() - ->getMock(); + $node = $this->createMock(Node::class); $node->expects($this->any()) ->method('getId') ->willReturn(123); @@ -424,8 +377,8 @@ class TagsPluginTest extends \Test\TestCase { $this->assertEmpty($propPatch->getRemainingMutations()); $result = $propPatch->getResult(); - $this->assertFalse(false, isset($result[self::TAGS_PROPERTYNAME])); - $this->assertEquals(200, isset($result[self::FAVORITE_PROPERTYNAME])); + $this->assertArrayNotHasKey(self::TAGS_PROPERTYNAME, $result); + $this->assertEquals(200, $result[self::FAVORITE_PROPERTYNAME]); // unfavorite now // set favorite tag @@ -449,7 +402,7 @@ class TagsPluginTest extends \Test\TestCase { $this->assertEmpty($propPatch->getRemainingMutations()); $result = $propPatch->getResult(); - $this->assertFalse(false, isset($result[self::TAGS_PROPERTYNAME])); - $this->assertEquals(200, isset($result[self::FAVORITE_PROPERTYNAME])); + $this->assertArrayNotHasKey(self::TAGS_PROPERTYNAME, $result); + $this->assertEquals(200, $result[self::FAVORITE_PROPERTYNAME]); } } |