diff options
author | John Molakvoæ <skjnldsv@users.noreply.github.com> | 2024-03-15 13:01:10 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-03-15 13:01:10 +0100 |
commit | 3f560ae940a5648572104c1287929b66b196fa4c (patch) | |
tree | 0d6a645ef3bc08bd94a48d0fb64954f733a0f10b /tests | |
parent | 40edb426a3a6857598ff3f447a85e83e32ff0c32 (diff) | |
parent | df1cd1ba7e6e1f6e66a2b3229b5c082f1b81162e (diff) | |
download | nextcloud-server-3f560ae940a5648572104c1287929b66b196fa4c.tar.gz nextcloud-server-3f560ae940a5648572104c1287929b66b196fa4c.zip |
Merge branch 'master' into refactor/OC-Server-getRootFolder
Signed-off-by: John Molakvoæ <skjnldsv@users.noreply.github.com>
Diffstat (limited to 'tests')
123 files changed, 5288 insertions, 1651 deletions
diff --git a/tests/Core/Command/Config/App/GetConfigTest.php b/tests/Core/Command/Config/App/GetConfigTest.php index 521ecfbfb40..5988d8d867f 100644 --- a/tests/Core/Command/Config/App/GetConfigTest.php +++ b/tests/Core/Command/Config/App/GetConfigTest.php @@ -21,8 +21,9 @@ namespace Tests\Core\Command\Config\App; +use OC\AppConfig; use OC\Core\Command\Config\App\GetConfig; -use OCP\IConfig; +use OCP\Exceptions\AppConfigUnknownKeyException; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Test\TestCase; @@ -42,13 +43,13 @@ class GetConfigTest extends TestCase { protected function setUp(): void { parent::setUp(); - $config = $this->config = $this->getMockBuilder(IConfig::class) + $config = $this->config = $this->getMockBuilder(AppConfig::class) ->disableOriginalConstructor() ->getMock(); $this->consoleInput = $this->getMockBuilder(InputInterface::class)->getMock(); $this->consoleOutput = $this->getMockBuilder(OutputInterface::class)->getMock(); - /** @var \OCP\IConfig $config */ + /** @var \OCP\IAppConfig $config */ $this->command = new GetConfig($config); } @@ -108,20 +109,22 @@ class GetConfigTest extends TestCase { * @param string $expectedMessage */ public function testGet($configName, $value, $configExists, $defaultValue, $hasDefault, $outputFormat, $expectedReturn, $expectedMessage) { - $this->config->expects($this->atLeastOnce()) - ->method('getAppKeys') - ->with('app-name') - ->willReturn($configExists ? [$configName] : []); - if (!$expectedReturn) { if ($configExists) { $this->config->expects($this->once()) - ->method('getAppValue') + ->method('getDetails') ->with('app-name', $configName) - ->willReturn($value); + ->willReturn(['value' => $value]); } } + if (!$configExists) { + $this->config->expects($this->once()) + ->method('getDetails') + ->with('app-name', $configName) + ->willThrowException(new AppConfigUnknownKeyException()); + } + $this->consoleInput->expects($this->exactly(2)) ->method('getArgument') ->willReturnMap([ diff --git a/tests/Core/Command/Config/App/SetConfigTest.php b/tests/Core/Command/Config/App/SetConfigTest.php index 88053f8c189..4918053048a 100644 --- a/tests/Core/Command/Config/App/SetConfigTest.php +++ b/tests/Core/Command/Config/App/SetConfigTest.php @@ -21,8 +21,10 @@ namespace Tests\Core\Command\Config\App; +use OC\AppConfig; use OC\Core\Command\Config\App\SetConfig; -use OCP\IConfig; +use OCP\Exceptions\AppConfigUnknownKeyException; +use OCP\IAppConfig; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Test\TestCase; @@ -42,13 +44,13 @@ class SetConfigTest extends TestCase { protected function setUp(): void { parent::setUp(); - $config = $this->config = $this->getMockBuilder(IConfig::class) + $config = $this->config = $this->getMockBuilder(AppConfig::class) ->disableOriginalConstructor() ->getMock(); $this->consoleInput = $this->getMockBuilder(InputInterface::class)->getMock(); $this->consoleOutput = $this->getMockBuilder(OutputInterface::class)->getMock(); - /** @var \OCP\IConfig $config */ + /** @var \OCP\IAppConfig $config */ $this->command = new SetConfig($config); } @@ -85,14 +87,24 @@ class SetConfigTest extends TestCase { * @param string $expectedMessage */ public function testSet($configName, $newValue, $configExists, $updateOnly, $updated, $expectedMessage) { - $this->config->expects($this->once()) - ->method('getAppKeys') - ->with('app-name') - ->willReturn($configExists ? [$configName] : []); + $this->config->expects($this->any()) + ->method('hasKey') + ->with('app-name', $configName) + ->willReturn($configExists); + + if (!$configExists) { + $this->config->expects($this->any()) + ->method('getValueType') + ->willThrowException(new AppConfigUnknownKeyException()); + } else { + $this->config->expects($this->any()) + ->method('getValueType') + ->willReturn(IAppConfig::VALUE_MIXED); + } if ($updated) { $this->config->expects($this->once()) - ->method('setAppValue') + ->method('setValueMixed') ->with('app-name', $configName, $newValue); } @@ -104,13 +116,19 @@ class SetConfigTest extends TestCase { ]); $this->consoleInput->expects($this->any()) ->method('getOption') - ->with('value') - ->willReturn($newValue); + ->willReturnMap([ + ['value', $newValue], + ['lazy', null], + ['sensitive', null], + ['no-interaction', true], + ]); $this->consoleInput->expects($this->any()) ->method('hasParameterOption') - ->with('--update-only') - ->willReturn($updateOnly); - + ->willReturnMap([ + ['--type', false, false], + ['--value', false, true], + ['--update-only', false, $updateOnly] + ]); $this->consoleOutput->expects($this->any()) ->method('writeln') ->with($this->stringContains($expectedMessage)); diff --git a/tests/Core/Command/Preview/RepairTest.php b/tests/Core/Command/Preview/RepairTest.php index fd4af615340..550dad1ef2f 100644 --- a/tests/Core/Command/Preview/RepairTest.php +++ b/tests/Core/Command/Preview/RepairTest.php @@ -10,11 +10,11 @@ use OCP\Files\Node; use OCP\IConfig; use OCP\Lock\ILockingProvider; use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; use Symfony\Component\Console\Formatter\OutputFormatterInterface; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Test\TestCase; -use Psr\Log\LoggerInterface; class RepairTest extends TestCase { /** @var IConfig|MockObject */ diff --git a/tests/Core/Command/User/AddTest.php b/tests/Core/Command/User/AddTest.php new file mode 100644 index 00000000000..3ca6b635a94 --- /dev/null +++ b/tests/Core/Command/User/AddTest.php @@ -0,0 +1,170 @@ +<?php +/** + * @copyright Copyright (c) 2021, Philip Gatzka (philip.gatzka@mailbox.org) + * + * @author Philip Gatzka <philip.gatzka@mailbox.org> + * + * @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/>. + * + */ + +declare(strict_types=1); + +namespace Core\Command\User; + +use OC\Core\Command\User\Add; +use OCA\Settings\Mailer\NewUserMailHelper; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\IAppConfig; +use OCP\IGroupManager; +use OCP\IUser; +use OCP\IUserManager; +use OCP\Mail\IEMailTemplate; +use OCP\mail\IMailer; +use OCP\Security\ISecureRandom; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Test\TestCase; + +class AddTest extends TestCase { + /** @var IUserManager|\PHPUnit\Framework\MockObject\MockObject */ + private $userManager; + + /** @var IGroupManager|\PHPUnit\Framework\MockObject\MockObject */ + private $groupManager; + + /** @var IMailer|\PHPUnit\Framework\MockObject\MockObject */ + private $mailer; + + /** @var IAppConfig|\PHPUnit\Framework\MockObject\MockObject */ + private $appConfig; + + /** @var NewUserMailHelper|\PHPUnit\Framework\MockObject\MockObject */ + private $mailHelper; + + /** @var IEventDispatcher|\PHPUnit\Framework\MockObject\MockObject */ + private $eventDispatcher; + + /** @var ISecureRandom|\PHPUnit\Framework\MockObject\MockObject */ + private $secureRandom; + + /** @var IUser|\PHPUnit\Framework\MockObject\MockObject */ + private $user; + + /** @var InputInterface|\PHPUnit\Framework\MockObject\MockObject */ + private $consoleInput; + + /** @var OutputInterface|\PHPUnit\Framework\MockObject\MockObject */ + private $consoleOutput; + + /** @var Add */ + private $addCommand; + + public function setUp(): void { + parent::setUp(); + + $this->userManager = static::createMock(IUserManager::class); + $this->groupManager = static::createStub(IGroupManager::class); + $this->mailer = static::createMock(IMailer::class); + $this->appConfig = static::createMock(IAppConfig::class); + $this->mailHelper = static::createMock(NewUserMailHelper::class); + $this->eventDispatcher = static::createStub(IEventDispatcher::class); + $this->secureRandom = static::createStub(ISecureRandom::class); + + $this->user = static::createMock(IUser::class); + + $this->consoleInput = static::createMock(InputInterface::class); + $this->consoleOutput = static::createMock(OutputInterface::class); + + $this->addCommand = new Add( + $this->userManager, + $this->groupManager, + $this->mailer, + $this->appConfig, + $this->mailHelper, + $this->eventDispatcher, + $this->secureRandom + ); + } + + /** + * @dataProvider addEmailDataProvider + */ + public function testAddEmail( + ?string $email, + bool $isEmailValid, + bool $shouldSendEmail, + ): void { + $this->user->expects($isEmailValid ? static::once() : static::never()) + ->method('setSystemEMailAddress') + ->with(static::equalTo($email)); + + $this->userManager->method('createUser') + ->willReturn($this->user); + + $this->appConfig->method('getValueString') + ->willReturn($shouldSendEmail ? 'yes' : 'no'); + + $this->mailer->method('validateMailAddress') + ->willReturn($isEmailValid); + + $this->mailHelper->method('generateTemplate') + ->willReturn(static::createMock(IEMailTemplate::class)); + + $this->mailHelper->expects($isEmailValid && $shouldSendEmail ? static::once() : static::never()) + ->method('sendMail'); + + $this->consoleInput->method('getOption') + ->will(static::returnValueMap([ + ['generate-password', 'true'], + ['email', $email], + ['group', []], + ])); + + $this->invokePrivate($this->addCommand, 'execute', [ + $this->consoleInput, + $this->consoleOutput + ]); + } + + /** + * @return array + */ + public function addEmailDataProvider(): array { + return [ + 'Valid E-Mail' => [ + 'info@example.com', + true, + true, + ], + 'Invalid E-Mail' => [ + 'info@@example.com', + false, + false, + ], + 'No E-Mail' => [ + '', + false, + false, + ], + 'Valid E-Mail, but no mail should be sent' => [ + 'info@example.com', + true, + false, + ], + ]; + } +} diff --git a/tests/Core/Command/User/AuthTokens/DeleteTest.php b/tests/Core/Command/User/AuthTokens/DeleteTest.php index 528d4c6869b..e11bf938dc9 100644 --- a/tests/Core/Command/User/AuthTokens/DeleteTest.php +++ b/tests/Core/Command/User/AuthTokens/DeleteTest.php @@ -22,8 +22,8 @@ */ namespace Tests\Core\Command\User\AuthTokens; -use OC\Core\Command\User\AuthTokens\Delete; use OC\Authentication\Token\IProvider; +use OC\Core\Command\User\AuthTokens\Delete; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Exception\RuntimeException; use Symfony\Component\Console\Input\InputInterface; diff --git a/tests/Core/Controller/AppPasswordControllerTest.php b/tests/Core/Controller/AppPasswordControllerTest.php index 47220fcf5ab..5f257d163ed 100644 --- a/tests/Core/Controller/AppPasswordControllerTest.php +++ b/tests/Core/Controller/AppPasswordControllerTest.php @@ -29,6 +29,7 @@ use OC\Authentication\Exceptions\InvalidTokenException; use OC\Authentication\Token\IProvider; use OC\Authentication\Token\IToken; use OC\Core\Controller\AppPasswordController; +use OC\User\Session; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCS\OCSForbiddenException; use OCP\Authentication\Exceptions\CredentialsUnavailableException; @@ -38,6 +39,8 @@ use OCP\Authentication\LoginCredentials\IStore; use OCP\EventDispatcher\IEventDispatcher; use OCP\IRequest; use OCP\ISession; +use OCP\IUserManager; +use OCP\Security\Bruteforce\IThrottler; use OCP\Security\ISecureRandom; use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; @@ -61,6 +64,15 @@ class AppPasswordControllerTest extends TestCase { /** @var IEventDispatcher|\PHPUnit\Framework\MockObject\MockObject */ private $eventDispatcher; + /** @var Session|MockObject */ + private $userSession; + + /** @var IUserManager|MockObject */ + private $userManager; + + /** @var IThrottler|MockObject */ + private $throttler; + /** @var AppPasswordController */ private $controller; @@ -73,6 +85,9 @@ class AppPasswordControllerTest extends TestCase { $this->credentialStore = $this->createMock(IStore::class); $this->request = $this->createMock(IRequest::class); $this->eventDispatcher = $this->createMock(IEventDispatcher::class); + $this->userSession = $this->createMock(Session::class); + $this->userManager = $this->createMock(IUserManager::class); + $this->throttler = $this->createMock(IThrottler::class); $this->controller = new AppPasswordController( 'core', @@ -81,7 +96,10 @@ class AppPasswordControllerTest extends TestCase { $this->random, $this->tokenProvider, $this->credentialStore, - $this->eventDispatcher + $this->eventDispatcher, + $this->userSession, + $this->userManager, + $this->throttler ); } diff --git a/tests/Core/Controller/LoginControllerTest.php b/tests/Core/Controller/LoginControllerTest.php index 333baf0aa86..005b7d713e5 100644 --- a/tests/Core/Controller/LoginControllerTest.php +++ b/tests/Core/Controller/LoginControllerTest.php @@ -30,6 +30,7 @@ use OC\Authentication\Login\LoginResult; use OC\Authentication\TwoFactorAuth\Manager; use OC\Core\Controller\LoginController; use OC\User\Session; +use OCP\App\IAppManager; use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Http\TemplateResponse; use OCP\Defaults; @@ -89,6 +90,9 @@ class LoginControllerTest extends TestCase { /** @var IL10N|MockObject */ private $l; + /** @var IAppManager|MockObject */ + private $appManager; + protected function setUp(): void { parent::setUp(); $this->request = $this->createMock(IRequest::class); @@ -104,6 +108,8 @@ class LoginControllerTest extends TestCase { $this->webAuthnManager = $this->createMock(\OC\Authentication\WebAuthn\Manager::class); $this->notificationManager = $this->createMock(IManager::class); $this->l = $this->createMock(IL10N::class); + $this->appManager = $this->createMock(IAppManager::class); + $this->l->expects($this->any()) ->method('t') ->willReturnCallback(function ($text, $parameters = []) { @@ -132,7 +138,8 @@ class LoginControllerTest extends TestCase { $this->initialStateService, $this->webAuthnManager, $this->notificationManager, - $this->l + $this->l, + $this->appManager, ); } @@ -145,6 +152,10 @@ class LoginControllerTest extends TestCase { $this->request ->method('getServerProtocol') ->willReturn('https'); + $this->request + ->expects($this->once()) + ->method('isUserAgent') + ->willReturn(false); $this->config ->expects($this->never()) ->method('deleteUserValue'); @@ -159,6 +170,29 @@ class LoginControllerTest extends TestCase { $this->assertEquals($expected, $this->loginController->logout()); } + public function testLogoutNoClearSiteData() { + $this->request + ->expects($this->once()) + ->method('getCookie') + ->with('nc_token') + ->willReturn(null); + $this->request + ->method('getServerProtocol') + ->willReturn('https'); + $this->request + ->expects($this->once()) + ->method('isUserAgent') + ->willReturn(true); + $this->urlGenerator + ->expects($this->once()) + ->method('linkToRouteAbsolute') + ->with('core.login.showLoginForm') + ->willReturn('/login'); + + $expected = new RedirectResponse('/login'); + $this->assertEquals($expected, $this->loginController->logout()); + } + public function testLogoutWithToken() { $this->request ->expects($this->once()) @@ -166,9 +200,12 @@ class LoginControllerTest extends TestCase { ->with('nc_token') ->willReturn('MyLoginToken'); $this->request - ->expects($this->once()) ->method('getServerProtocol') ->willReturn('https'); + $this->request + ->expects($this->once()) + ->method('isUserAgent') + ->willReturn(false); $user = $this->createMock(IUser::class); $user ->expects($this->once()) @@ -228,7 +265,7 @@ class LoginControllerTest extends TestCase { ], ] ); - $this->initialStateService->expects($this->exactly(11)) + $this->initialStateService->expects($this->exactly(12)) ->method('provideInitialState') ->withConsecutive([ 'core', @@ -270,7 +307,7 @@ class LoginControllerTest extends TestCase { ->expects($this->once()) ->method('isLoggedIn') ->willReturn(false); - $this->initialStateService->expects($this->exactly(12)) + $this->initialStateService->expects($this->exactly(13)) ->method('provideInitialState') ->withConsecutive([], [], [], [ 'core', @@ -314,7 +351,7 @@ class LoginControllerTest extends TestCase { * @dataProvider passwordResetDataProvider */ public function testShowLoginFormWithPasswordResetOption($canChangePassword, - $expectedResult) { + $expectedResult) { $this->userSession ->expects($this->once()) ->method('isLoggedIn') @@ -341,7 +378,7 @@ class LoginControllerTest extends TestCase { ->method('get') ->with('LdapUser') ->willReturn($user); - $this->initialStateService->expects($this->exactly(11)) + $this->initialStateService->expects($this->exactly(12)) ->method('provideInitialState') ->withConsecutive([], [], [ 'core', @@ -391,7 +428,7 @@ class LoginControllerTest extends TestCase { ->method('get') ->with('0') ->willReturn($user); - $this->initialStateService->expects($this->exactly(11)) + $this->initialStateService->expects($this->exactly(12)) ->method('provideInitialState') ->withConsecutive([], [], [], [ 'core', diff --git a/tests/Core/Controller/OCSControllerTest.php b/tests/Core/Controller/OCSControllerTest.php index 61ed4a50d62..a10455e482a 100644 --- a/tests/Core/Controller/OCSControllerTest.php +++ b/tests/Core/Controller/OCSControllerTest.php @@ -196,7 +196,7 @@ class OCSControllerTest extends TestCase { ->with('NotExistingUser') ->willReturn(null); - $expected = new DataResponse(['User not found'], 404); + $expected = new DataResponse(['Account not found'], 404); $this->assertEquals($expected, $this->controller->getIdentityProof('NotExistingUser')); } diff --git a/tests/Core/Service/LoginFlowV2ServiceUnitTest.php b/tests/Core/Service/LoginFlowV2ServiceUnitTest.php index 57443ca1328..7ca389946d8 100644 --- a/tests/Core/Service/LoginFlowV2ServiceUnitTest.php +++ b/tests/Core/Service/LoginFlowV2ServiceUnitTest.php @@ -27,8 +27,8 @@ use OC\Authentication\Token\IProvider; use OC\Authentication\Token\IToken; use OC\Core\Data\LoginFlowV2Credentials; use OC\Core\Data\LoginFlowV2Tokens; -use OC\Core\Db\LoginFlowV2Mapper; use OC\Core\Db\LoginFlowV2; +use OC\Core\Db\LoginFlowV2Mapper; use OC\Core\Exception\LoginFlowV2NotFoundException; use OC\Core\Service\LoginFlowV2Service; use OCP\AppFramework\Db\DoesNotExistException; diff --git a/tests/data/integritycheck/mimetypeListModified/core/js/mimetypelist.js b/tests/data/integritycheck/mimetypeListModified/core/js/mimetypelist.js index a8e98e786f5..ffcf5e1fb1c 100644 --- a/tests/data/integritycheck/mimetypeListModified/core/js/mimetypelist.js +++ b/tests/data/integritycheck/mimetypeListModified/core/js/mimetypelist.js @@ -115,6 +115,7 @@ OC.MimeTypeList={ "image/targa": "image/tga", "application/vnd.openxmlformats-officedocument.wordprocessingml.document.oform": "x-office/form", "application/vnd.openxmlformats-officedocument.wordprocessingml.document.docxf": "x-office/form-template", + "image/x-emf": "image/emf", "my-custom/mimetype": "custom" }, files: [ diff --git a/tests/data/integritycheck/mimetypeListModified/core/signature.json b/tests/data/integritycheck/mimetypeListModified/core/signature.json index 43f489f6d3f..4ee844b87f8 100644 --- a/tests/data/integritycheck/mimetypeListModified/core/signature.json +++ b/tests/data/integritycheck/mimetypeListModified/core/signature.json @@ -1,7 +1,7 @@ { "hashes": { - "core\/js\/mimetypelist.js": "2a6ab9af5353d464c0dd3bd651cfc13ba35009463949bc45bd8f7073828fa34fbdac7a81ba2b188e7e0c97fc63c2167825b069f61341e507ca33213b1e6e4593" + "core\/js\/mimetypelist.js": "e9e515c5eeb08b02971e5943640bef89b6d1c2d746d5883bf974c6ac753f810685c7a20bf0ed4d32d8c046cd2cef6c045682c7d89c6a3b7b6c33122f5fd2088f" }, - "signature": "A5JmjvGiWC9iDaDL0\/wzIBx1ovV6xW5pL134ZtzcFh6dCRoYVZFZvInb98zz4Js3Y4R+uAv4XYKFgclG9BXjoa+q7SmN4qV95o870OsX3MEAaPRHEJv7V6P19hinwC0\/c6XUtMUlTn2IqdoacmELV3v+vmXiU0bYpNfqkYDjt1mCfl3EWB+uMCn+W849k1hoRc\/nI3JuDzl3VtP4G6zzJ3NexsXJOHOb6\/GyGVnkOltN3Ep5wBqXtr28LLLWWYbgxUEQ5ZNBd98PCpRm3r\/3eGreREccUzh+Kfo1XK+Rbnf8U6z3DXOwZK4cP\/CJBAhtlUDyw+TY58jcOSLxF2I61jqYoa8En2ukcQdXUvyTcsN+RzAKo+yhAPw0CJvDzb9zuS7gJUpev6nZnJKQ6dNQApQQILtwgz9dDlVKToxgyyhV1giTqEEZDvH1t2MSjz8fbGlm1YY60YJSs2SA\/cAff+sQVmoGCpRtdHriCDAET+5gTuz0wEXnvn6Jvxqxta9IZ0fisKCjfRH5FFdjfBM8Cgk6HOhSAfHoSH+ZFUGy8+NICZXe7CGr40iIjFLSIS0RgclQZSjYK8bfjA43XFpXeJNGjIvxHTr4tzm8gJ3YbVqCyN45HBcxS3q7yJCie3brqCZvXfXyhoGY6WhPAkBLQ+8nNP\/qeWlV8DMX+ZUYxso=", + "signature": "iKEOaoY+lowIZrDjozpCqDFtag8qtANZ4AqnwZG1HrzuP3Yv7uaCUZbpsyr4FklKyyZFbh4w5K3x5bacKq\/h7tFVu5A56sunSZIMDjO\/ToGFYtZC59hTi0mKlmR+rIbAwmlm2Qad0uSD+\/4bkihL\/haPAtV8IbHXqxwjcYjkPmyi0W3rN1sOycgbH8Hmu7UlkdZORGTVVHdMpQuIljaBGBonQUTnqUb2BVsZ7YKW3Ls1AKMBam\/OGrB8rAJOht5b86qIE1jzzU\/BI7Qs+r8C+sh84LpLgz\/33njaBNANwfnvbrcb4f\/95BZCL4DcMGfwJ\/VNRVJrBjQSweYb+ypq5WMMOUvHHEg4CovoH\/XbdCAbRVet34vRZnZe5F4bXQOZXp0eqbqoY+STwQ5Ku2O7YUWwfppjxWMMfs1hDUrvvMBFRCd5mla\/aktV7ugishcZdKUFyDsyOEtT292Cb5f\/62RqnMniD9a+TOBE1qWH5DXYQqRO9TUdVtGQ3ITbLxEAzlfUmwYoXp+wgKbzOXC4KFzpxJnxHM+vuURkO5lUza68gqiG8\/uhNcPQufDT5CjasQVBTK5tdoL64UnXqATgU3rrD\/MByOXWlZvMsAS+NjPkF30UnvqgApEwytOlTZ27+ntZjfwhM3DlXNKE3mzUx+tvVfwBDmhEpBK\/Qpk6HLc=", "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----" }
\ No newline at end of file diff --git a/tests/data/svg/settings-admin-red.svg b/tests/data/svg/settings-admin-red.svg index 54d7d3a9b15..4f69e5a16ca 100644 --- a/tests/data/svg/settings-admin-red.svg +++ b/tests/data/svg/settings-admin-red.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 16 16" height="16" width="16" version="1.1"><path color="#000" d="m1 1v4h4v-4h-4zm5 1v2h8v-2h-8zm-5 4v4h4v-4h-4zm5 1v2h8v-2h-8zm-5 4v4h4v-4h-4zm1 1h2v2h-2v-2zm4 0v2h8v-2h-8z" fill="#f00"/></svg> +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" height="16" width="16" version="1.1"><path color="#000" d="m1 1v4h4v-4h-4zm5 1v2h8v-2h-8zm-5 4v4h4v-4h-4zm5 1v2h8v-2h-8zm-5 4v4h4v-4h-4zm1 1h2v2h-2v-2zm4 0v2h8v-2h-8z" fill="#f00"/></svg> diff --git a/tests/lib/App/AppManagerTest.php b/tests/lib/App/AppManagerTest.php index 104b0941644..dfbaedff957 100644 --- a/tests/lib/App/AppManagerTest.php +++ b/tests/lib/App/AppManagerTest.php @@ -23,6 +23,7 @@ use OCP\ICacheFactory; use OCP\IConfig; use OCP\IGroup; use OCP\IGroupManager; +use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserSession; use PHPUnit\Framework\MockObject\MockObject; @@ -98,6 +99,8 @@ class AppManagerTest extends TestCase { /** @var LoggerInterface|MockObject */ protected $logger; + protected IURLGenerator|MockObject $urlGenerator; + /** @var IAppManager */ protected $manager; @@ -112,6 +115,7 @@ class AppManagerTest extends TestCase { $this->cache = $this->createMock(ICache::class); $this->eventDispatcher = $this->createMock(IEventDispatcher::class); $this->logger = $this->createMock(LoggerInterface::class); + $this->urlGenerator = $this->createMock(IURLGenerator::class); $this->cacheFactory->expects($this->any()) ->method('createDistributed') ->with('settings') @@ -123,10 +127,101 @@ class AppManagerTest extends TestCase { $this->groupManager, $this->cacheFactory, $this->eventDispatcher, - $this->logger + $this->logger, + $this->urlGenerator, ); } + /** + * @dataProvider dataGetAppIcon + */ + public function testGetAppIcon($callback, ?bool $dark, string|null $expected) { + $this->urlGenerator->expects($this->atLeastOnce()) + ->method('imagePath') + ->willReturnCallback($callback); + + if ($dark !== null) { + $this->assertEquals($expected, $this->manager->getAppIcon('test', $dark)); + } else { + $this->assertEquals($expected, $this->manager->getAppIcon('test')); + } + } + + public function dataGetAppIcon(): array { + $nothing = function ($appId) { + $this->assertEquals('test', $appId); + throw new \RuntimeException(); + }; + + $createCallback = function ($workingIcons) { + return function ($appId, $icon) use ($workingIcons) { + $this->assertEquals('test', $appId); + if (in_array($icon, $workingIcons)) { + return '/path/' . $icon; + } + throw new \RuntimeException(); + }; + }; + + return [ + 'does not find anything' => [ + $nothing, + false, + null, + ], + 'nothing if request dark but only bright available' => [ + $createCallback(['app.svg']), + true, + null, + ], + 'nothing if request bright but only dark available' => [ + $createCallback(['app-dark.svg']), + false, + null, + ], + 'bright and only app.svg' => [ + $createCallback(['app.svg']), + false, + '/path/app.svg', + ], + 'dark and only app-dark.svg' => [ + $createCallback(['app-dark.svg']), + true, + '/path/app-dark.svg', + ], + 'dark only appname -dark.svg' => [ + $createCallback(['test-dark.svg']), + true, + '/path/test-dark.svg', + ], + 'bright and only appname.svg' => [ + $createCallback(['test.svg']), + false, + '/path/test.svg', + ], + 'priotize custom over default' => [ + $createCallback(['app.svg', 'test.svg']), + false, + '/path/test.svg', + ], + 'defaults to bright' => [ + $createCallback(['test-dark.svg', 'test.svg']), + null, + '/path/test.svg', + ], + 'no dark icon on default' => [ + $createCallback(['test-dark.svg', 'test.svg', 'app-dark.svg', 'app.svg']), + false, + '/path/test.svg', + ], + 'no bright icon on dark' => [ + $createCallback(['test-dark.svg', 'test.svg', 'app-dark.svg', 'app.svg']), + true, + '/path/test-dark.svg', + ], + ]; + } + public function testEnableApp() { // making sure "files_trashbin" is disabled if ($this->manager->isEnabledForUser('files_trashbin')) { @@ -170,9 +265,16 @@ class AppManagerTest extends TestCase { /** @var AppManager|MockObject $manager */ $manager = $this->getMockBuilder(AppManager::class) ->setConstructorArgs([ - $this->userSession, $this->config, $this->appConfig, $this->groupManager, $this->cacheFactory, $this->eventDispatcher, $this->logger + $this->userSession, + $this->config, + $this->appConfig, + $this->groupManager, + $this->cacheFactory, + $this->eventDispatcher, + $this->logger, + $this->urlGenerator, ]) - ->setMethods([ + ->onlyMethods([ 'getAppPath', ]) ->getMock(); @@ -218,9 +320,16 @@ class AppManagerTest extends TestCase { /** @var AppManager|MockObject $manager */ $manager = $this->getMockBuilder(AppManager::class) ->setConstructorArgs([ - $this->userSession, $this->config, $this->appConfig, $this->groupManager, $this->cacheFactory, $this->eventDispatcher, $this->logger + $this->userSession, + $this->config, + $this->appConfig, + $this->groupManager, + $this->cacheFactory, + $this->eventDispatcher, + $this->logger, + $this->urlGenerator, ]) - ->setMethods([ + ->onlyMethods([ 'getAppPath', 'getAppInfo', ]) @@ -274,9 +383,16 @@ class AppManagerTest extends TestCase { /** @var AppManager|MockObject $manager */ $manager = $this->getMockBuilder(AppManager::class) ->setConstructorArgs([ - $this->userSession, $this->config, $this->appConfig, $this->groupManager, $this->cacheFactory, $this->eventDispatcher, $this->logger + $this->userSession, + $this->config, + $this->appConfig, + $this->groupManager, + $this->cacheFactory, + $this->eventDispatcher, + $this->logger, + $this->urlGenerator, ]) - ->setMethods([ + ->onlyMethods([ 'getAppPath', 'getAppInfo', ]) @@ -470,8 +586,17 @@ class AppManagerTest extends TestCase { public function testGetAppsNeedingUpgrade() { /** @var AppManager|MockObject $manager */ $manager = $this->getMockBuilder(AppManager::class) - ->setConstructorArgs([$this->userSession, $this->config, $this->appConfig, $this->groupManager, $this->cacheFactory, $this->eventDispatcher, $this->logger]) - ->setMethods(['getAppInfo']) + ->setConstructorArgs([ + $this->userSession, + $this->config, + $this->appConfig, + $this->groupManager, + $this->cacheFactory, + $this->eventDispatcher, + $this->logger, + $this->urlGenerator, + ]) + ->onlyMethods(['getAppInfo']) ->getMock(); $appInfos = [ @@ -521,8 +646,17 @@ class AppManagerTest extends TestCase { public function testGetIncompatibleApps() { /** @var AppManager|MockObject $manager */ $manager = $this->getMockBuilder(AppManager::class) - ->setConstructorArgs([$this->userSession, $this->config, $this->appConfig, $this->groupManager, $this->cacheFactory, $this->eventDispatcher, $this->logger]) - ->setMethods(['getAppInfo']) + ->setConstructorArgs([ + $this->userSession, + $this->config, + $this->appConfig, + $this->groupManager, + $this->cacheFactory, + $this->eventDispatcher, + $this->logger, + $this->urlGenerator, + ]) + ->onlyMethods(['getAppInfo']) ->getMock(); $appInfos = [ @@ -660,6 +794,17 @@ class AppManagerTest extends TestCase { true, 'settings', ], + // system default app and user apporder + [ + // system default is settings + 'unexist,settings', + '', + // apporder says default app is files (order is lower) + '{"files_id":{"app":"files","order":1},"settings_id":{"app":"settings","order":2}}', + true, + // system default should override apporder + 'settings' + ], // user-customized defaultapp [ '', @@ -680,7 +825,7 @@ class AppManagerTest extends TestCase { [ 'unexist,settings', 'files', - '{"settings":[1],"files":[2]}', + '{"settings_id":{"app":"settings","order":1},"files_id":{"app":"files","order":2}}', true, 'files', ], @@ -688,18 +833,34 @@ class AppManagerTest extends TestCase { [ '', '', - '{"settings":[1],"files":[2]}', + '{"settings_id":{"app":"settings","order":1},"files":{"app":"files","order":2}}', true, 'settings', ], + // user-customized apporder fallback with missing app key (entries added by closures does not always have an app key set (Nextcloud 27 spreed app for example)) + [ + '', + '', + '{"spreed":{"order":1},"files":{"app":"files","order":2}}', + true, + 'files', + ], // user-customized apporder, but called without fallback [ '', '', - '{"settings":[1],"files":[2]}', + '{"settings":{"app":"settings","order":1},"files":{"app":"files","order":2}}', false, '', ], + // user-customized apporder with an app that has multiple routes + [ + '', + '', + '{"settings_id":{"app":"settings","order":1},"settings_id_2":{"app":"settings","order":3},"id_files":{"app":"files","order":2}}', + true, + 'settings', + ], ]; } diff --git a/tests/lib/App/AppStore/Bundles/BundleFetcherTest.php b/tests/lib/App/AppStore/Bundles/BundleFetcherTest.php index c1fe5ef328a..86ddd12f1e7 100644 --- a/tests/lib/App/AppStore/Bundles/BundleFetcherTest.php +++ b/tests/lib/App/AppStore/Bundles/BundleFetcherTest.php @@ -26,6 +26,7 @@ use OC\App\AppStore\Bundles\EducationBundle; use OC\App\AppStore\Bundles\EnterpriseBundle; use OC\App\AppStore\Bundles\GroupwareBundle; use OC\App\AppStore\Bundles\HubBundle; +use OC\App\AppStore\Bundles\PublicSectorBundle; use OC\App\AppStore\Bundles\SocialSharingBundle; use OCP\IL10N; use Test\TestCase; @@ -53,6 +54,7 @@ class BundleFetcherTest extends TestCase { new GroupwareBundle($this->l10n), new SocialSharingBundle($this->l10n), new EducationBundle($this->l10n), + new PublicSectorBundle($this->l10n), ]; $this->assertEquals($expected, $this->bundleFetcher->getBundles()); } diff --git a/tests/lib/App/AppStore/Fetcher/AppFetcherTest.php b/tests/lib/App/AppStore/Fetcher/AppFetcherTest.php index 39b0a699092..0f279c6f884 100644 --- a/tests/lib/App/AppStore/Fetcher/AppFetcherTest.php +++ b/tests/lib/App/AppStore/Fetcher/AppFetcherTest.php @@ -2098,6 +2098,95 @@ EJL3BaQAQaASSsvFrcozYxrQG4VzEg== ->willReturnCallback(function ($key, $default) { if ($key === 'version') { return '11.0.0.2'; + } else { + return $default; + } + }); + $this->config->method('getSystemValue') + ->willReturnCallback(function ($key, $default) { + if ($key === 'appsallowlist') { + return ['contacts']; + } + return $default; + }); + $this->config->method('getAppValue') + ->willReturnCallback(function ($app, $key, $default) { + if ($app === 'support' && $key === 'subscription_key') { + return 'subscription-key'; + } + return $default; + }); + $this->config + ->method('getSystemValueBool') + ->willReturnArgument(1); + + $file = $this->createMock(ISimpleFile::class); + $folder = $this->createMock(ISimpleFolder::class); + $folder + ->expects($this->once()) + ->method('getFile') + ->with('apps.json') + ->willThrowException(new NotFoundException()); + $folder + ->expects($this->once()) + ->method('newFile') + ->with('apps.json') + ->willReturn($file); + $this->appData + ->expects($this->once()) + ->method('getFolder') + ->with('/') + ->willReturn($folder); + $client = $this->createMock(IClient::class); + $this->clientService + ->expects($this->once()) + ->method('newClient') + ->willReturn($client); + $response = $this->createMock(IResponse::class); + $client + ->expects($this->once()) + ->method('get') + ->with('https://apps.nextcloud.com/api/v1/apps.json', [ + 'timeout' => 60, + 'headers' => [ + 'X-NC-Subscription-Key' => 'subscription-key', + ], + ]) + ->willReturn($response); + $response + ->expects($this->once()) + ->method('getBody') + ->willReturn(self::$responseJson); + $response->method('getHeader') + ->with($this->equalTo('ETag')) + ->willReturn('"myETag"'); + $this->timeFactory + ->expects($this->once()) + ->method('getTime') + ->willReturn(1234); + + $this->registry + ->expects($this->exactly(2)) + ->method('delegateHasValidSubscription') + ->willReturn(true); + + $file + ->expects($this->once()) + ->method('putContent'); + $file + ->method('getContent') + ->willReturn(json_encode(self::$expectedResponse)); + + $apps = array_values($this->fetcher->get()); + $this->assertEquals(count($apps), 1); + $this->assertEquals($apps[0]['id'], 'contacts'); + } + + public function testGetAppsAllowlistCustomAppstore(): void { + $this->config->method('getSystemValueString') + ->willReturnCallback(function ($key, $default) { + if ($key === 'version') { + return '11.0.0.2'; } elseif ($key === 'appstoreurl' && $default === 'https://apps.nextcloud.com/api/v1') { return 'https://custom.appsstore.endpoint/api/v1'; } else { @@ -2142,7 +2231,9 @@ EJL3BaQAQaASSsvFrcozYxrQG4VzEg== $client ->expects($this->once()) ->method('get') - ->with('https://custom.appsstore.endpoint/api/v1/apps.json') + ->with('https://custom.appsstore.endpoint/api/v1/apps.json', [ + 'timeout' => 60, + ]) ->willReturn($response); $response ->expects($this->once()) @@ -2157,7 +2248,7 @@ EJL3BaQAQaASSsvFrcozYxrQG4VzEg== ->willReturn(1234); $this->registry - ->expects($this->exactly(2)) + ->expects($this->exactly(1)) ->method('delegateHasValidSubscription') ->willReturn(true); diff --git a/tests/lib/App/AppStore/Fetcher/CategoryFetcherTest.php b/tests/lib/App/AppStore/Fetcher/CategoryFetcherTest.php index 874a58fc6ba..dbfca97f999 100644 --- a/tests/lib/App/AppStore/Fetcher/CategoryFetcherTest.php +++ b/tests/lib/App/AppStore/Fetcher/CategoryFetcherTest.php @@ -64,6 +64,11 @@ class CategoryFetcherTest extends FetcherBase { } return $default; }); + $this->config + ->method('getSystemValueString') + ->willReturnCallback(function ($var, $default) { + return $default; + }); $this->appData ->expects($this->never()) ->method('getFolder'); diff --git a/tests/lib/App/AppStore/Fetcher/FetcherBase.php b/tests/lib/App/AppStore/Fetcher/FetcherBase.php index dbc3f2a687c..7645cbef599 100644 --- a/tests/lib/App/AppStore/Fetcher/FetcherBase.php +++ b/tests/lib/App/AppStore/Fetcher/FetcherBase.php @@ -76,10 +76,13 @@ abstract class FetcherBase extends TestCase { public function testGetWithAlreadyExistingFileAndUpToDateTimestampAndVersion() { $this->config - ->expects($this->exactly(1)) ->method('getSystemValueString') - ->with($this->equalTo('version'), $this->anything()) - ->willReturn('11.0.0.2'); + ->willReturnCallback(function ($var, $default) { + if ($var === 'version') { + return '11.0.0.2'; + } + return $default; + }); $this->config->method('getSystemValueBool') ->willReturnArgument(1); diff --git a/tests/lib/App/AppStore/Version/VersionParserTest.php b/tests/lib/App/AppStore/Version/VersionParserTest.php index edb48dc4130..a81dbecf32c 100644 --- a/tests/lib/App/AppStore/Version/VersionParserTest.php +++ b/tests/lib/App/AppStore/Version/VersionParserTest.php @@ -77,7 +77,7 @@ class VersionParserTest extends TestCase { * @param Version $expected */ public function testGetVersion($input, - Version $expected) { + Version $expected) { $this->assertEquals($expected, $this->versionParser->getVersion($input)); } diff --git a/tests/lib/App/InfoParserTest.php b/tests/lib/App/InfoParserTest.php index bc561611501..c34d2775faf 100644 --- a/tests/lib/App/InfoParserTest.php +++ b/tests/lib/App/InfoParserTest.php @@ -10,8 +10,8 @@ namespace Test\App; use OC; use OC\App\InfoParser; -use Test\TestCase; use OCP\Cache\CappedMemoryCache; +use Test\TestCase; class InfoParserTest extends TestCase { /** @var OCP\Cache\CappedMemoryCache */ diff --git a/tests/lib/AppConfigTest.php b/tests/lib/AppConfigTest.php index d4ae66cb2f1..86bd339bc7e 100644 --- a/tests/lib/AppConfigTest.php +++ b/tests/lib/AppConfigTest.php @@ -1,17 +1,36 @@ <?php + +declare(strict_types=1); /** - * Copyright (c) 2013 Christopher Schäpers <christopher@schaepers.it> - * Copyright (c) 2013 Bart Visscher <bartv@thisnet.nl> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * @copyright Copyright (c) 2024, Maxence Lange <maxence@artificial-owl.com> + * + * @author Maxence Lange <maxence@artificial-owl.com> + * + * @license AGPL-3.0 or later + * + * 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 Test; +use InvalidArgumentException; use OC\AppConfig; -use OC\DB\Connection; -use OCP\IConfig; +use OCP\Exceptions\AppConfigTypeConflictException; +use OCP\Exceptions\AppConfigUnknownKeyException; +use OCP\IAppConfig; +use OCP\IDBConnection; +use OCP\Security\ICrypto; +use Psr\Log\LoggerInterface; /** * Class AppConfigTest @@ -21,348 +40,1425 @@ use OCP\IConfig; * @package Test */ class AppConfigTest extends TestCase { - /** @var \OCP\IAppConfig */ - protected $appConfig; - - /** @var Connection */ - protected $connection; + protected IAppConfig $appConfig; + protected IDBConnection $connection; + private LoggerInterface $logger; + private ICrypto $crypto; + private array $originalConfig; - protected $originalConfig; + /** + * @var array<string, array<array<string, string, int, bool, bool>>> + * [appId => [configKey, configValue, valueType, lazy, sensitive]] + */ + private array $baseStruct = + [ + 'testapp' => [ + 'enabled' => ['enabled', 'true'], + 'installed_version' => ['installed_version', '1.2.3'], + 'depends_on' => ['depends_on', 'someapp'], + 'deletethis' => ['deletethis', 'deletethis'], + 'key' => ['key', 'value'] + ], + 'someapp' => [ + 'key' => ['key', 'value'], + 'otherkey' => ['otherkey', 'othervalue'] + ], + '123456' => [ + 'enabled' => ['enabled', 'true'], + 'key' => ['key', 'value'] + ], + 'anotherapp' => [ + 'enabled' => ['enabled', 'false'], + 'key' => ['key', 'value'] + ], + 'non-sensitive-app' => [ + 'lazy-key' => ['lazy-key', 'value', IAppConfig::VALUE_STRING, true, false], + 'non-lazy-key' => ['non-lazy-key', 'value', IAppConfig::VALUE_STRING, false, false], + ], + 'sensitive-app' => [ + 'lazy-key' => ['lazy-key', 'value', IAppConfig::VALUE_STRING, true, true], + 'non-lazy-key' => ['non-lazy-key', 'value', IAppConfig::VALUE_STRING, false, true], + ], + 'only-lazy' => [ + 'lazy-key' => ['lazy-key', 'value', IAppConfig::VALUE_STRING, true] + ], + 'typed' => [ + 'mixed' => ['mixed', 'mix', IAppConfig::VALUE_MIXED], + 'string' => ['string', 'value', IAppConfig::VALUE_STRING], + 'int' => ['int', '42', IAppConfig::VALUE_INT], + 'float' => ['float', '3.14', IAppConfig::VALUE_FLOAT], + 'bool' => ['bool', '1', IAppConfig::VALUE_BOOL], + 'array' => ['array', '{"test": 1}', IAppConfig::VALUE_ARRAY], + ], + 'prefix-app' => [ + 'key1' => ['key1', 'value'], + 'prefix1' => ['prefix1', 'value'], + 'prefix-2' => ['prefix-2', 'value'], + 'key-2' => ['key-2', 'value'], + ] + ]; protected function setUp(): void { parent::setUp(); - $this->connection = \OC::$server->get(Connection::class); + $this->connection = \OCP\Server::get(IDBConnection::class); + $this->logger = \OCP\Server::get(LoggerInterface::class); + $this->crypto = \OCP\Server::get(ICrypto::class); + + // storing current config and emptying the data table $sql = $this->connection->getQueryBuilder(); $sql->select('*') ->from('appconfig'); - $result = $sql->execute(); + $result = $sql->executeQuery(); $this->originalConfig = $result->fetchAll(); $result->closeCursor(); $sql = $this->connection->getQueryBuilder(); $sql->delete('appconfig'); - $sql->execute(); - - $this->overwriteService(AppConfig::class, new \OC\AppConfig($this->connection)); + $sql->executeStatement(); $sql = $this->connection->getQueryBuilder(); $sql->insert('appconfig') - ->values([ - 'appid' => $sql->createParameter('appid'), - 'configkey' => $sql->createParameter('configkey'), - 'configvalue' => $sql->createParameter('configvalue'), - ]); - - $sql->setParameters([ - 'appid' => 'testapp', - 'configkey' => 'enabled', - 'configvalue' => 'true', - ])->execute(); - $sql->setParameters([ - 'appid' => 'testapp', - 'configkey' => 'installed_version', - 'configvalue' => '1.2.3', - ])->execute(); - $sql->setParameters([ - 'appid' => 'testapp', - 'configkey' => 'depends_on', - 'configvalue' => 'someapp', - ])->execute(); - $sql->setParameters([ - 'appid' => 'testapp', - 'configkey' => 'deletethis', - 'configvalue' => 'deletethis', - ])->execute(); - $sql->setParameters([ - 'appid' => 'testapp', - 'configkey' => 'key', - 'configvalue' => 'value', - ])->execute(); - - $sql->setParameters([ - 'appid' => 'someapp', - 'configkey' => 'key', - 'configvalue' => 'value', - ])->execute(); - $sql->setParameters([ - 'appid' => 'someapp', - 'configkey' => 'otherkey', - 'configvalue' => 'othervalue', - ])->execute(); - - $sql->setParameters([ - 'appid' => '123456', - 'configkey' => 'key', - 'configvalue' => 'value', - ])->execute(); - $sql->setParameters([ - 'appid' => '123456', - 'configkey' => 'enabled', - 'configvalue' => 'false', - ])->execute(); - - $sql->setParameters([ - 'appid' => 'anotherapp', - 'configkey' => 'key', - 'configvalue' => 'value', - ])->execute(); - $sql->setParameters([ - 'appid' => 'anotherapp', - 'configkey' => 'enabled', - 'configvalue' => 'false', - ])->execute(); + ->values( + [ + 'appid' => $sql->createParameter('appid'), + 'configkey' => $sql->createParameter('configkey'), + 'configvalue' => $sql->createParameter('configvalue'), + 'type' => $sql->createParameter('type'), + 'lazy' => $sql->createParameter('lazy') + ] + ); + + foreach ($this->baseStruct as $appId => $appData) { + foreach ($appData as $key => $row) { + $value = $row[1]; + $type = $row[2] ?? IAppConfig::VALUE_MIXED; + if (($row[4] ?? false) === true) { + $type |= IAppConfig::VALUE_SENSITIVE; + $value = self::invokePrivate(AppConfig::class, 'ENCRYPTION_PREFIX') . $this->crypto->encrypt($value); + $this->baseStruct[$appId][$key]['encrypted'] = $value; + } + + $sql->setParameters( + [ + 'appid' => $appId, + 'configkey' => $row[0], + 'configvalue' => $value, + 'type' => $type, + 'lazy' => (($row[3] ?? false) === true) ? 1 : 0 + ] + )->executeStatement(); + } + } } protected function tearDown(): void { $sql = $this->connection->getQueryBuilder(); $sql->delete('appconfig'); - $sql->execute(); + $sql->executeStatement(); $sql = $this->connection->getQueryBuilder(); $sql->insert('appconfig') - ->values([ - 'appid' => $sql->createParameter('appid'), - 'configkey' => $sql->createParameter('configkey'), - 'configvalue' => $sql->createParameter('configvalue'), - ]); - - foreach ($this->originalConfig as $configs) { + ->values( + [ + 'appid' => $sql->createParameter('appid'), + 'configkey' => $sql->createParameter('configkey'), + 'configvalue' => $sql->createParameter('configvalue'), + 'lazy' => $sql->createParameter('lazy'), + 'type' => $sql->createParameter('type'), + ] + ); + + foreach ($this->originalConfig as $key => $configs) { $sql->setParameter('appid', $configs['appid']) ->setParameter('configkey', $configs['configkey']) - ->setParameter('configvalue', $configs['configvalue']); - $sql->execute(); + ->setParameter('configvalue', $configs['configvalue']) + ->setParameter('lazy', ($configs['lazy'] === '1') ? '1' : '0') + ->setParameter('type', $configs['type']); + $sql->executeStatement(); } - $this->restoreService(AppConfig::class); + // $this->restoreService(AppConfig::class); parent::tearDown(); } - public function testGetApps() { - $config = new \OC\AppConfig(\OC::$server->get(Connection::class)); + /** + * @param bool $preLoading TRUE will preload the 'fast' cache, which is the normal behavior of usual + * IAppConfig + * + * @return IAppConfig + */ + private function generateAppConfig(bool $preLoading = true): IAppConfig { + /** @var AppConfig $config */ + $config = new \OC\AppConfig( + $this->connection, + $this->logger, + $this->crypto, + ); + $msg = ' generateAppConfig() failed to confirm cache status'; + + // confirm cache status + $status = $config->statusCache(); + $this->assertSame(false, $status['fastLoaded'], $msg); + $this->assertSame(false, $status['lazyLoaded'], $msg); + $this->assertSame([], $status['fastCache'], $msg); + $this->assertSame([], $status['lazyCache'], $msg); + if ($preLoading) { + // simple way to initiate the load of non-lazy config values in cache + $config->getValueString('core', 'preload', ''); + + // confirm cache status + $status = $config->statusCache(); + $this->assertSame(true, $status['fastLoaded'], $msg); + $this->assertSame(false, $status['lazyLoaded'], $msg); + + $apps = array_values(array_diff(array_keys($this->baseStruct), ['only-lazy'])); + $this->assertEqualsCanonicalizing($apps, array_keys($status['fastCache']), $msg); + $this->assertSame([], array_keys($status['lazyCache']), $msg); + } + + return $config; + } + + public function testGetApps(): void { + $config = $this->generateAppConfig(false); + + $this->assertEqualsCanonicalizing(array_keys($this->baseStruct), $config->getApps()); + } + + /** + * returns list of app and their keys + * + * @return array<string, string[]> ['appId' => ['key1', 'key2', ]] + * @see testGetKeys + */ + public function providerGetAppKeys(): array { + $appKeys = []; + foreach ($this->baseStruct as $appId => $appData) { + $keys = []; + foreach ($appData as $row) { + $keys[] = $row[0]; + } + $appKeys[] = [(string)$appId, $keys]; + } - $this->assertEqualsCanonicalizing([ - 'anotherapp', - 'someapp', - 'testapp', - 123456, - ], $config->getApps()); + return $appKeys; } - public function testGetKeys() { - $config = new \OC\AppConfig(\OC::$server->get(Connection::class)); + /** + * returns list of config keys + * + * @return array<string, string, string, int, bool, bool> [appId, key, value, type, lazy, sensitive] + * @see testIsSensitive + * @see testIsLazy + * @see testGetKeys + */ + public function providerGetKeys(): array { + $appKeys = []; + foreach ($this->baseStruct as $appId => $appData) { + foreach ($appData as $row) { + $appKeys[] = [ + (string)$appId, $row[0], $row[1], $row[2] ?? IAppConfig::VALUE_MIXED, $row[3] ?? false, + $row[4] ?? false + ]; + } + } - $keys = $config->getKeys('testapp'); - $this->assertEqualsCanonicalizing([ - 'deletethis', - 'depends_on', - 'enabled', - 'installed_version', - 'key', - ], $keys); + return $appKeys; } - public function testGetValue() { - $config = new \OC\AppConfig(\OC::$server->get(Connection::class)); + /** + * @dataProvider providerGetAppKeys + * + * @param string $appId + * @param array $expectedKeys + */ + public function testGetKeys(string $appId, array $expectedKeys): void { + $config = $this->generateAppConfig(); + $this->assertEqualsCanonicalizing($expectedKeys, $config->getKeys($appId)); + } - $value = $config->getValue('testapp', 'installed_version'); - $this->assertConfigKey('testapp', 'installed_version', $value); + public function testGetKeysOnUnknownAppShouldReturnsEmptyArray(): void { + $config = $this->generateAppConfig(); + $this->assertEqualsCanonicalizing([], $config->getKeys('unknown-app')); + } - $value = $config->getValue('testapp', 'nonexistant'); - $this->assertNull($value); + /** + * @dataProvider providerGetKeys + * + * @param string $appId + * @param string $configKey + * @param string $value + * @param bool $lazy + */ + public function testHasKey(string $appId, string $configKey, string $value, int $type, bool $lazy): void { + $config = $this->generateAppConfig(); + $this->assertEquals(true, $config->hasKey($appId, $configKey, $lazy)); + } - $value = $config->getValue('testapp', 'nonexistant', 'default'); - $this->assertEquals('default', $value); + public function testHasKeyOnNonExistentKeyReturnsFalse(): void { + $config = $this->generateAppConfig(); + $this->assertEquals(false, $config->hasKey(array_keys($this->baseStruct)[0], 'inexistant-key')); } - public function testHasKey() { - $config = new \OC\AppConfig(\OC::$server->get(Connection::class)); + public function testHasKeyOnUnknownAppReturnsFalse(): void { + $config = $this->generateAppConfig(); + $this->assertEquals(false, $config->hasKey('inexistant-app', 'inexistant-key')); + } - $this->assertTrue($config->hasKey('testapp', 'installed_version')); - $this->assertFalse($config->hasKey('testapp', 'nonexistant')); - $this->assertFalse($config->hasKey('nonexistant', 'nonexistant')); + public function testHasKeyOnMistypedAsLazyReturnsFalse(): void { + $config = $this->generateAppConfig(); + $this->assertSame(false, $config->hasKey('non-sensitive-app', 'non-lazy-key', true)); } - public function testSetValueUpdate() { - $config = new \OC\AppConfig(\OC::$server->get(Connection::class)); + public function testHasKeyOnMistypeAsNonLazyReturnsFalse(): void { + $config = $this->generateAppConfig(); + $this->assertSame(false, $config->hasKey('non-sensitive-app', 'lazy-key', false)); + } - $this->assertEquals('1.2.3', $config->getValue('testapp', 'installed_version')); - $this->assertConfigKey('testapp', 'installed_version', '1.2.3'); + public function testHasKeyOnMistypeAsNonLazyReturnsTrueWithLazyArgumentIsNull(): void { + $config = $this->generateAppConfig(); + $this->assertSame(true, $config->hasKey('non-sensitive-app', 'lazy-key', null)); + } - $wasModified = $config->setValue('testapp', 'installed_version', '1.2.3'); - if (!(\OC::$server->get(Connection::class) instanceof \OC\DB\OracleConnection)) { - $this->assertFalse($wasModified); - } + /** + * @dataProvider providerGetKeys + */ + public function testIsSensitive( + string $appId, string $configKey, string $configValue, int $type, bool $lazy, bool $sensitive + ): void { + $config = $this->generateAppConfig(); + $this->assertEquals($sensitive, $config->isSensitive($appId, $configKey, $lazy)); + } - $this->assertEquals('1.2.3', $config->getValue('testapp', 'installed_version')); - $this->assertConfigKey('testapp', 'installed_version', '1.2.3'); + public function testIsSensitiveOnNonExistentKeyThrowsException(): void { + $config = $this->generateAppConfig(); + $this->expectException(AppConfigUnknownKeyException::class); + $config->isSensitive(array_keys($this->baseStruct)[0], 'inexistant-key'); + } - $this->assertTrue($config->setValue('testapp', 'installed_version', '1.33.7')); + public function testIsSensitiveOnUnknownAppThrowsException(): void { + $config = $this->generateAppConfig(); + $this->expectException(AppConfigUnknownKeyException::class); + $config->isSensitive('unknown-app', 'inexistant-key'); + } + public function testIsSensitiveOnSensitiveMistypedAsLazy(): void { + $config = $this->generateAppConfig(); + $this->assertSame(true, $config->isSensitive('sensitive-app', 'non-lazy-key', true)); + } - $this->assertEquals('1.33.7', $config->getValue('testapp', 'installed_version')); - $this->assertConfigKey('testapp', 'installed_version', '1.33.7'); + public function testIsSensitiveOnNonSensitiveMistypedAsLazy(): void { + $config = $this->generateAppConfig(); + $this->assertSame(false, $config->isSensitive('non-sensitive-app', 'non-lazy-key', true)); + } - $config->setValue('someapp', 'somekey', 'somevalue'); - $this->assertConfigKey('someapp', 'somekey', 'somevalue'); + public function testIsSensitiveOnSensitiveMistypedAsNonLazyThrowsException(): void { + $config = $this->generateAppConfig(); + $this->expectException(AppConfigUnknownKeyException::class); + $config->isSensitive('sensitive-app', 'lazy-key', false); } - public function testSetValueInsert() { - $config = new \OC\AppConfig(\OC::$server->get(Connection::class)); + public function testIsSensitiveOnNonSensitiveMistypedAsNonLazyThrowsException(): void { + $config = $this->generateAppConfig(); + $this->expectException(AppConfigUnknownKeyException::class); + $config->isSensitive('non-sensitive-app', 'lazy-key', false); + } - $this->assertFalse($config->hasKey('someapp', 'somekey')); - $this->assertNull($config->getValue('someapp', 'somekey')); + /** + * @dataProvider providerGetKeys + */ + public function testIsLazy(string $appId, string $configKey, string $configValue, int $type, bool $lazy + ): void { + $config = $this->generateAppConfig(); + $this->assertEquals($lazy, $config->isLazy($appId, $configKey)); + } - $this->assertTrue($config->setValue('someapp', 'somekey', 'somevalue')); + public function testIsLazyOnNonExistentKeyThrowsException(): void { + $config = $this->generateAppConfig(); + $this->expectException(AppConfigUnknownKeyException::class); + $config->isLazy(array_keys($this->baseStruct)[0], 'inexistant-key'); + } - $this->assertTrue($config->hasKey('someapp', 'somekey')); - $this->assertEquals('somevalue', $config->getValue('someapp', 'somekey')); - $this->assertConfigKey('someapp', 'somekey', 'somevalue'); + public function testIsLazyOnUnknownAppThrowsException(): void { + $config = $this->generateAppConfig(); + $this->expectException(AppConfigUnknownKeyException::class); + $config->isLazy('unknown-app', 'inexistant-key'); + } - $wasInserted = $config->setValue('someapp', 'somekey', 'somevalue'); - if (!(\OC::$server->get(Connection::class) instanceof \OC\DB\OracleConnection)) { - $this->assertFalse($wasInserted); - } + public function testGetAllValuesWithEmptyApp(): void { + $config = $this->generateAppConfig(); + $this->expectException(InvalidArgumentException::class); + $config->getAllValues(''); } - public function testDeleteKey() { - $config = new \OC\AppConfig(\OC::$server->get(Connection::class)); + /** + * @dataProvider providerGetAppKeys + * + * @param string $appId + * @param array $keys + */ + public function testGetAllValuesWithEmptyKey(string $appId, array $keys): void { + $config = $this->generateAppConfig(); + $this->assertEqualsCanonicalizing($keys, array_keys($config->getAllValues($appId, ''))); + } - $this->assertTrue($config->hasKey('testapp', 'deletethis')); + public function testGetAllValuesWithPrefix(): void { + $config = $this->generateAppConfig(); + $this->assertEqualsCanonicalizing(['prefix1', 'prefix-2'], array_keys($config->getAllValues('prefix-app', 'prefix'))); + } - $config->deleteKey('testapp', 'deletethis'); + public function testSearchValues(): void { + $config = $this->generateAppConfig(); + $this->assertEqualsCanonicalizing(['testapp' => 'true', '123456' => 'true', 'anotherapp' => 'false'], $config->searchValues('enabled')); + } - $this->assertFalse($config->hasKey('testapp', 'deletethis')); + public function testGetValueString(): void { + $config = $this->generateAppConfig(); + $this->assertSame('value', $config->getValueString('typed', 'string', '')); + } - $sql = \OC::$server->getDatabaseConnection()->getQueryBuilder(); - $sql->select('configvalue') - ->from('appconfig') - ->where($sql->expr()->eq('appid', $sql->createParameter('appid'))) - ->andWhere($sql->expr()->eq('configkey', $sql->createParameter('configkey'))) - ->setParameter('appid', 'testapp') - ->setParameter('configkey', 'deletethis'); - $query = $sql->execute(); - $result = $query->fetch(); - $query->closeCursor(); - $this->assertFalse($result); + public function testGetValueStringOnUnknownAppReturnsDefault(): void { + $config = $this->generateAppConfig(); + $this->assertSame('default-1', $config->getValueString('typed-1', 'string', 'default-1')); } - public function testDeleteApp() { - $config = new \OC\AppConfig(\OC::$server->get(Connection::class)); + public function testGetValueStringOnNonExistentKeyReturnsDefault(): void { + $config = $this->generateAppConfig(); + $this->assertSame('default-2', $config->getValueString('typed', 'string-2', 'default-2')); + } - $this->assertTrue($config->hasKey('someapp', 'otherkey')); + public function testGetValueStringOnWrongType(): void { + $config = $this->generateAppConfig(); + $this->expectException(AppConfigTypeConflictException::class); + $config->getValueString('typed', 'int'); + } - $config->deleteApp('someapp'); + public function testGetNonLazyValueStringAsLazy(): void { + $config = $this->generateAppConfig(); + $this->assertSame('value', $config->getValueString('non-sensitive-app', 'non-lazy-key', 'default', lazy: true)); + } - $this->assertFalse($config->hasKey('someapp', 'otherkey')); + public function testGetValueInt() { + $config = $this->generateAppConfig(); + $this->assertSame(42, $config->getValueInt('typed', 'int', 0)); + } - $sql = \OC::$server->getDatabaseConnection()->getQueryBuilder(); - $sql->select('configvalue') - ->from('appconfig') - ->where($sql->expr()->eq('appid', $sql->createParameter('appid'))) - ->setParameter('appid', 'someapp'); - $query = $sql->execute(); - $result = $query->fetch(); - $query->closeCursor(); - $this->assertFalse($result); + public function testGetValueIntOnUnknownAppReturnsDefault(): void { + $config = $this->generateAppConfig(); + $this->assertSame(1, $config->getValueInt('typed-1', 'int', 1)); } - public function testGetValuesNotAllowed() { - $config = new \OC\AppConfig(\OC::$server->get(Connection::class)); + public function testGetValueIntOnNonExistentKeyReturnsDefault() { + $config = $this->generateAppConfig(); + $this->assertSame(2, $config->getValueInt('typed', 'int-2', 2)); + } + + public function testGetValueIntOnWrongType(): void { + $config = $this->generateAppConfig(); + $this->expectException(AppConfigTypeConflictException::class); + $config->getValueInt('typed', 'float'); + } - $this->assertFalse($config->getValues('testapp', 'enabled')); + public function testGetValueFloat() { + $config = $this->generateAppConfig(); + $this->assertSame(3.14, $config->getValueFloat('typed', 'float', 0)); + } - $this->assertFalse($config->getValues(false, false)); + public function testGetValueFloatOnNonUnknownAppReturnsDefault(): void { + $config = $this->generateAppConfig(); + $this->assertSame(1.11, $config->getValueFloat('typed-1', 'float', 1.11)); } - public function testGetValues() { - $config = new \OC\AppConfig(\OC::$server->get(Connection::class)); + public function testGetValueFloatOnNonExistentKeyReturnsDefault() { + $config = $this->generateAppConfig(); + $this->assertSame(2.22, $config->getValueFloat('typed', 'float-2', 2.22)); + } - $sql = \OC::$server->getDatabaseConnection()->getQueryBuilder(); - $sql->select(['configkey', 'configvalue']) - ->from('appconfig') - ->where($sql->expr()->eq('appid', $sql->createParameter('appid'))) - ->setParameter('appid', 'testapp'); - $query = $sql->execute(); - $expected = []; - while ($row = $query->fetch()) { - $expected[$row['configkey']] = $row['configvalue']; - } - $query->closeCursor(); + public function testGetValueFloatOnWrongType(): void { + $config = $this->generateAppConfig(); + $this->expectException(AppConfigTypeConflictException::class); + $config->getValueFloat('typed', 'bool'); + } - $values = $config->getValues('testapp', false); - $this->assertEquals($expected, $values); + public function testGetValueBool(): void { + $config = $this->generateAppConfig(); + $this->assertSame(true, $config->getValueBool('typed', 'bool')); + } - $sql = \OC::$server->getDatabaseConnection()->getQueryBuilder(); - $sql->select(['appid', 'configvalue']) - ->from('appconfig') - ->where($sql->expr()->eq('configkey', $sql->createParameter('configkey'))) - ->setParameter('configkey', 'enabled'); - $query = $sql->execute(); - $expected = []; - while ($row = $query->fetch()) { - $expected[$row['appid']] = $row['configvalue']; - } - $query->closeCursor(); + public function testGetValueBoolOnUnknownAppReturnsDefault(): void { + $config = $this->generateAppConfig(); + $this->assertSame(false, $config->getValueBool('typed-1', 'bool', false)); + } + + public function testGetValueBoolOnNonExistentKeyReturnsDefault(): void { + $config = $this->generateAppConfig(); + $this->assertSame(false, $config->getValueBool('typed', 'bool-2')); + } - $values = $config->getValues(false, 'enabled'); - $this->assertEquals($expected, $values); + public function testGetValueBoolOnWrongType(): void { + $config = $this->generateAppConfig(); + $this->expectException(AppConfigTypeConflictException::class); + $config->getValueBool('typed', 'array'); } - public function testGetFilteredValues() { - /** @var \OC\AppConfig|\PHPUnit\Framework\MockObject\MockObject $config */ - $config = $this->getMockBuilder(\OC\AppConfig::class) - ->setConstructorArgs([\OC::$server->get(Connection::class)]) - ->setMethods(['getValues']) - ->getMock(); + public function testGetValueArray(): void { + $config = $this->generateAppConfig(); + $this->assertEqualsCanonicalizing(['test' => 1], $config->getValueArray('typed', 'array', [])); + } - $config->expects($this->once()) - ->method('getValues') - ->with('user_ldap', false) - ->willReturn([ - 'ldap_agent_password' => 'secret', - 's42ldap_agent_password' => 'secret', - 'ldap_dn' => 'dn', - ]); + public function testGetValueArrayOnUnknownAppReturnsDefault(): void { + $config = $this->generateAppConfig(); + $this->assertSame([1], $config->getValueArray('typed-1', 'array', [1])); + } - $values = $config->getFilteredValues('user_ldap'); - $this->assertEquals([ - 'ldap_agent_password' => IConfig::SENSITIVE_VALUE, - 's42ldap_agent_password' => IConfig::SENSITIVE_VALUE, - 'ldap_dn' => 'dn', - ], $values); + public function testGetValueArrayOnNonExistentKeyReturnsDefault(): void { + $config = $this->generateAppConfig(); + $this->assertSame([1, 2], $config->getValueArray('typed', 'array-2', [1, 2])); } - public function testSettingConfigParallel() { - $appConfig1 = new \OC\AppConfig(\OC::$server->get(Connection::class)); - $appConfig2 = new \OC\AppConfig(\OC::$server->get(Connection::class)); - $appConfig1->getValue('testapp', 'foo', 'v1'); - $appConfig2->getValue('testapp', 'foo', 'v1'); + public function testGetValueArrayOnWrongType(): void { + $config = $this->generateAppConfig(); + $this->expectException(AppConfigTypeConflictException::class); + $config->getValueArray('typed', 'string'); + } - $appConfig1->setValue('testapp', 'foo', 'v1'); - $this->assertConfigKey('testapp', 'foo', 'v1'); - $appConfig2->setValue('testapp', 'foo', 'v2'); - $this->assertConfigKey('testapp', 'foo', 'v2'); + /** + * @return array + * @see testGetValueType + * + * @see testGetValueMixed + */ + public function providerGetValueMixed(): array { + return [ + // key, value, type + ['mixed', 'mix', IAppConfig::VALUE_MIXED], + ['string', 'value', IAppConfig::VALUE_STRING], + ['int', '42', IAppConfig::VALUE_INT], + ['float', '3.14', IAppConfig::VALUE_FLOAT], + ['bool', '1', IAppConfig::VALUE_BOOL], + ['array', '{"test": 1}', IAppConfig::VALUE_ARRAY], + ]; + } + + /** + * @dataProvider providerGetValueMixed + * + * @param string $key + * @param string $value + */ + public function testGetValueMixed(string $key, string $value): void { + $config = $this->generateAppConfig(); + $this->assertSame($value, $config->getValueMixed('typed', $key)); } /** - * @param string $app + * @dataProvider providerGetValueMixed + * * @param string $key - * @param string $expected + * @param string $value + * @param int $type */ - protected function assertConfigKey($app, $key, $expected) { - $sql = \OC::$server->getDatabaseConnection()->getQueryBuilder(); + public function testGetValueType(string $key, string $value, int $type): void { + $config = $this->generateAppConfig(); + $this->assertSame($type, $config->getValueType('typed', $key)); + } + + public function testGetValueTypeOnUnknownApp(): void { + $config = $this->generateAppConfig(); + $this->expectException(AppConfigUnknownKeyException::class); + $config->getValueType('typed-1', 'string'); + } + + public function testGetValueTypeOnNonExistentKey(): void { + $config = $this->generateAppConfig(); + $this->expectException(AppConfigUnknownKeyException::class); + $config->getValueType('typed', 'string-2'); + } + + public function testSetValueString(): void { + $config = $this->generateAppConfig(); + $config->setValueString('feed', 'string', 'value-1'); + $this->assertSame('value-1', $config->getValueString('feed', 'string', '')); + } + + public function testSetValueStringCache(): void { + $config = $this->generateAppConfig(); + $config->setValueString('feed', 'string', 'value-1'); + $status = $config->statusCache(); + $this->assertSame('value-1', $status['fastCache']['feed']['string']); + } + + public function testSetValueStringDatabase(): void { + $config = $this->generateAppConfig(); + $config->setValueString('feed', 'string', 'value-1'); + $config->clearCache(); + $this->assertSame('value-1', $config->getValueString('feed', 'string', '')); + } + + public function testSetValueStringIsUpdated(): void { + $config = $this->generateAppConfig(); + $config->setValueString('feed', 'string', 'value-1'); + $this->assertSame(true, $config->setValueString('feed', 'string', 'value-2')); + } + + public function testSetValueStringIsNotUpdated(): void { + $config = $this->generateAppConfig(); + $config->setValueString('feed', 'string', 'value-1'); + $this->assertSame(false, $config->setValueString('feed', 'string', 'value-1')); + } + + public function testSetValueStringIsUpdatedCache(): void { + $config = $this->generateAppConfig(); + $config->setValueString('feed', 'string', 'value-1'); + $config->setValueString('feed', 'string', 'value-2'); + $status = $config->statusCache(); + $this->assertSame('value-2', $status['fastCache']['feed']['string']); + } + + public function testSetValueStringIsUpdatedDatabase(): void { + $config = $this->generateAppConfig(); + $config->setValueString('feed', 'string', 'value-1'); + $config->setValueString('feed', 'string', 'value-2'); + $config->clearCache(); + $this->assertSame('value-2', $config->getValueString('feed', 'string', '')); + } + + public function testSetValueInt(): void { + $config = $this->generateAppConfig(); + $config->setValueInt('feed', 'int', 42); + $this->assertSame(42, $config->getValueInt('feed', 'int', 0)); + } + + public function testSetValueIntCache(): void { + $config = $this->generateAppConfig(); + $config->setValueInt('feed', 'int', 42); + $status = $config->statusCache(); + $this->assertSame('42', $status['fastCache']['feed']['int']); + } + + public function testSetValueIntDatabase(): void { + $config = $this->generateAppConfig(); + $config->setValueInt('feed', 'int', 42); + $config->clearCache(); + $this->assertSame(42, $config->getValueInt('feed', 'int', 0)); + } + + public function testSetValueIntIsUpdated(): void { + $config = $this->generateAppConfig(); + $config->setValueInt('feed', 'int', 42); + $this->assertSame(true, $config->setValueInt('feed', 'int', 17)); + } + + public function testSetValueIntIsNotUpdated(): void { + $config = $this->generateAppConfig(); + $config->setValueInt('feed', 'int', 42); + $this->assertSame(false, $config->setValueInt('feed', 'int', 42)); + } + + public function testSetValueIntIsUpdatedCache(): void { + $config = $this->generateAppConfig(); + $config->setValueInt('feed', 'int', 42); + $config->setValueInt('feed', 'int', 17); + $status = $config->statusCache(); + $this->assertSame('17', $status['fastCache']['feed']['int']); + } + + public function testSetValueIntIsUpdatedDatabase(): void { + $config = $this->generateAppConfig(); + $config->setValueInt('feed', 'int', 42); + $config->setValueInt('feed', 'int', 17); + $config->clearCache(); + $this->assertSame(17, $config->getValueInt('feed', 'int', 0)); + } + + public function testSetValueFloat(): void { + $config = $this->generateAppConfig(); + $config->setValueFloat('feed', 'float', 3.14); + $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0)); + } + + public function testSetValueFloatCache(): void { + $config = $this->generateAppConfig(); + $config->setValueFloat('feed', 'float', 3.14); + $status = $config->statusCache(); + $this->assertSame('3.14', $status['fastCache']['feed']['float']); + } + + public function testSetValueFloatDatabase(): void { + $config = $this->generateAppConfig(); + $config->setValueFloat('feed', 'float', 3.14); + $config->clearCache(); + $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0)); + } + + public function testSetValueFloatIsUpdated(): void { + $config = $this->generateAppConfig(); + $config->setValueFloat('feed', 'float', 3.14); + $this->assertSame(true, $config->setValueFloat('feed', 'float', 1.23)); + } + + public function testSetValueFloatIsNotUpdated(): void { + $config = $this->generateAppConfig(); + $config->setValueFloat('feed', 'float', 3.14); + $this->assertSame(false, $config->setValueFloat('feed', 'float', 3.14)); + } + + public function testSetValueFloatIsUpdatedCache(): void { + $config = $this->generateAppConfig(); + $config->setValueFloat('feed', 'float', 3.14); + $config->setValueFloat('feed', 'float', 1.23); + $status = $config->statusCache(); + $this->assertSame('1.23', $status['fastCache']['feed']['float']); + } + + public function testSetValueFloatIsUpdatedDatabase(): void { + $config = $this->generateAppConfig(); + $config->setValueFloat('feed', 'float', 3.14); + $config->setValueFloat('feed', 'float', 1.23); + $config->clearCache(); + $this->assertSame(1.23, $config->getValueFloat('feed', 'float', 0)); + } + + public function testSetValueBool(): void { + $config = $this->generateAppConfig(); + $config->setValueBool('feed', 'bool', true); + $this->assertSame(true, $config->getValueBool('feed', 'bool', false)); + } + + public function testSetValueBoolCache(): void { + $config = $this->generateAppConfig(); + $config->setValueBool('feed', 'bool', true); + $status = $config->statusCache(); + $this->assertSame('1', $status['fastCache']['feed']['bool']); + } + + public function testSetValueBoolDatabase(): void { + $config = $this->generateAppConfig(); + $config->setValueBool('feed', 'bool', true); + $config->clearCache(); + $this->assertSame(true, $config->getValueBool('feed', 'bool', false)); + } + + public function testSetValueBoolIsUpdated(): void { + $config = $this->generateAppConfig(); + $config->setValueBool('feed', 'bool', true); + $this->assertSame(true, $config->setValueBool('feed', 'bool', false)); + } + + public function testSetValueBoolIsNotUpdated(): void { + $config = $this->generateAppConfig(); + $config->setValueBool('feed', 'bool', true); + $this->assertSame(false, $config->setValueBool('feed', 'bool', true)); + } + + public function testSetValueBoolIsUpdatedCache(): void { + $config = $this->generateAppConfig(); + $config->setValueBool('feed', 'bool', true); + $config->setValueBool('feed', 'bool', false); + $status = $config->statusCache(); + $this->assertSame('0', $status['fastCache']['feed']['bool']); + } + + public function testSetValueBoolIsUpdatedDatabase(): void { + $config = $this->generateAppConfig(); + $config->setValueBool('feed', 'bool', true); + $config->setValueBool('feed', 'bool', false); + $config->clearCache(); + $this->assertSame(false, $config->getValueBool('feed', 'bool', true)); + } + + + public function testSetValueArray(): void { + $config = $this->generateAppConfig(); + $config->setValueArray('feed', 'array', ['test' => 1]); + $this->assertSame(['test' => 1], $config->getValueArray('feed', 'array', [])); + } + + public function testSetValueArrayCache(): void { + $config = $this->generateAppConfig(); + $config->setValueArray('feed', 'array', ['test' => 1]); + $status = $config->statusCache(); + $this->assertSame('{"test":1}', $status['fastCache']['feed']['array']); + } + + public function testSetValueArrayDatabase(): void { + $config = $this->generateAppConfig(); + $config->setValueArray('feed', 'array', ['test' => 1]); + $config->clearCache(); + $this->assertSame(['test' => 1], $config->getValueArray('feed', 'array', [])); + } + + public function testSetValueArrayIsUpdated(): void { + $config = $this->generateAppConfig(); + $config->setValueArray('feed', 'array', ['test' => 1]); + $this->assertSame(true, $config->setValueArray('feed', 'array', ['test' => 2])); + } + + public function testSetValueArrayIsNotUpdated(): void { + $config = $this->generateAppConfig(); + $config->setValueArray('feed', 'array', ['test' => 1]); + $this->assertSame(false, $config->setValueArray('feed', 'array', ['test' => 1])); + } + + public function testSetValueArrayIsUpdatedCache(): void { + $config = $this->generateAppConfig(); + $config->setValueArray('feed', 'array', ['test' => 1]); + $config->setValueArray('feed', 'array', ['test' => 2]); + $status = $config->statusCache(); + $this->assertSame('{"test":2}', $status['fastCache']['feed']['array']); + } + + public function testSetValueArrayIsUpdatedDatabase(): void { + $config = $this->generateAppConfig(); + $config->setValueArray('feed', 'array', ['test' => 1]); + $config->setValueArray('feed', 'array', ['test' => 2]); + $config->clearCache(); + $this->assertSame(['test' => 2], $config->getValueArray('feed', 'array', [])); + } + + public function testSetLazyValueString(): void { + $config = $this->generateAppConfig(); + $config->setValueString('feed', 'string', 'value-1', true); + $this->assertSame('value-1', $config->getValueString('feed', 'string', '', true)); + } + + public function testSetLazyValueStringCache(): void { + $config = $this->generateAppConfig(); + $config->setValueString('feed', 'string', 'value-1', true); + $status = $config->statusCache(); + $this->assertSame('value-1', $status['lazyCache']['feed']['string']); + } + + public function testSetLazyValueStringDatabase(): void { + $config = $this->generateAppConfig(); + $config->setValueString('feed', 'string', 'value-1', true); + $config->clearCache(); + $this->assertSame('value-1', $config->getValueString('feed', 'string', '', true)); + } + + public function testSetLazyValueStringAsNonLazy(): void { + $config = $this->generateAppConfig(); + $config->setValueString('feed', 'string', 'value-1', true); + $config->setValueString('feed', 'string', 'value-1', false); + $this->assertSame('value-1', $config->getValueString('feed', 'string', '')); + } + + public function testSetNonLazyValueStringAsLazy(): void { + $config = $this->generateAppConfig(); + $config->setValueString('feed', 'string', 'value-1', false); + $config->setValueString('feed', 'string', 'value-1', true); + $this->assertSame('value-1', $config->getValueString('feed', 'string', '', true)); + } + + public function testSetSensitiveValueString(): void { + $config = $this->generateAppConfig(); + $config->setValueString('feed', 'string', 'value-1', sensitive: true); + $this->assertSame('value-1', $config->getValueString('feed', 'string', '')); + } + + public function testSetSensitiveValueStringCache(): void { + $config = $this->generateAppConfig(); + $config->setValueString('feed', 'string', 'value-1', sensitive: true); + $status = $config->statusCache(); + $this->assertStringStartsWith(self::invokePrivate(AppConfig::class, 'ENCRYPTION_PREFIX'), $status['fastCache']['feed']['string']); + } + + public function testSetSensitiveValueStringDatabase(): void { + $config = $this->generateAppConfig(); + $config->setValueString('feed', 'string', 'value-1', sensitive: true); + $config->clearCache(); + $this->assertSame('value-1', $config->getValueString('feed', 'string', '')); + } + + public function testSetNonSensitiveValueStringAsSensitive(): void { + $config = $this->generateAppConfig(); + $config->setValueString('feed', 'string', 'value-1', sensitive: false); + $config->setValueString('feed', 'string', 'value-1', sensitive: true); + $this->assertSame(true, $config->isSensitive('feed', 'string')); + + $this->assertConfigValueNotEquals('feed', 'string', 'value-1'); + $this->assertConfigValueNotEquals('feed', 'string', 'value-2'); + } + + public function testSetSensitiveValueStringAsNonSensitiveStaysSensitive(): void { + $config = $this->generateAppConfig(); + $config->setValueString('feed', 'string', 'value-1', sensitive: true); + $config->setValueString('feed', 'string', 'value-2', sensitive: false); + $this->assertSame(true, $config->isSensitive('feed', 'string')); + + $this->assertConfigValueNotEquals('feed', 'string', 'value-1'); + $this->assertConfigValueNotEquals('feed', 'string', 'value-2'); + } + + public function testSetSensitiveValueStringAsNonSensitiveAreStillUpdated(): void { + $config = $this->generateAppConfig(); + $config->setValueString('feed', 'string', 'value-1', sensitive: true); + $config->setValueString('feed', 'string', 'value-2', sensitive: false); + $this->assertSame('value-2', $config->getValueString('feed', 'string', '')); + + $this->assertConfigValueNotEquals('feed', 'string', 'value-1'); + $this->assertConfigValueNotEquals('feed', 'string', 'value-2'); + } + + public function testSetLazyValueInt(): void { + $config = $this->generateAppConfig(); + $config->setValueInt('feed', 'int', 42, true); + $this->assertSame(42, $config->getValueInt('feed', 'int', 0, true)); + } + + public function testSetLazyValueIntCache(): void { + $config = $this->generateAppConfig(); + $config->setValueInt('feed', 'int', 42, true); + $status = $config->statusCache(); + $this->assertSame('42', $status['lazyCache']['feed']['int']); + } + + public function testSetLazyValueIntDatabase(): void { + $config = $this->generateAppConfig(); + $config->setValueInt('feed', 'int', 42, true); + $config->clearCache(); + $this->assertSame(42, $config->getValueInt('feed', 'int', 0, true)); + } + + public function testSetLazyValueIntAsNonLazy(): void { + $config = $this->generateAppConfig(); + $config->setValueInt('feed', 'int', 42, true); + $config->setValueInt('feed', 'int', 42, false); + $this->assertSame(42, $config->getValueInt('feed', 'int', 0)); + } + + public function testSetNonLazyValueIntAsLazy(): void { + $config = $this->generateAppConfig(); + $config->setValueInt('feed', 'int', 42, false); + $config->setValueInt('feed', 'int', 42, true); + $this->assertSame(42, $config->getValueInt('feed', 'int', 0, true)); + } + + public function testSetSensitiveValueInt(): void { + $config = $this->generateAppConfig(); + $config->setValueInt('feed', 'int', 42, sensitive: true); + $this->assertSame(42, $config->getValueInt('feed', 'int', 0)); + } + + public function testSetSensitiveValueIntCache(): void { + $config = $this->generateAppConfig(); + $config->setValueInt('feed', 'int', 42, sensitive: true); + $status = $config->statusCache(); + $this->assertStringStartsWith(self::invokePrivate(AppConfig::class, 'ENCRYPTION_PREFIX'), $status['fastCache']['feed']['int']); + } + + public function testSetSensitiveValueIntDatabase(): void { + $config = $this->generateAppConfig(); + $config->setValueInt('feed', 'int', 42, sensitive: true); + $config->clearCache(); + $this->assertSame(42, $config->getValueInt('feed', 'int', 0)); + } + + public function testSetNonSensitiveValueIntAsSensitive(): void { + $config = $this->generateAppConfig(); + $config->setValueInt('feed', 'int', 42); + $config->setValueInt('feed', 'int', 42, sensitive: true); + $this->assertSame(true, $config->isSensitive('feed', 'int')); + } + + public function testSetSensitiveValueIntAsNonSensitiveStaysSensitive(): void { + $config = $this->generateAppConfig(); + $config->setValueInt('feed', 'int', 42, sensitive: true); + $config->setValueInt('feed', 'int', 17); + $this->assertSame(true, $config->isSensitive('feed', 'int')); + } + + public function testSetSensitiveValueIntAsNonSensitiveAreStillUpdated(): void { + $config = $this->generateAppConfig(); + $config->setValueInt('feed', 'int', 42, sensitive: true); + $config->setValueInt('feed', 'int', 17); + $this->assertSame(17, $config->getValueInt('feed', 'int', 0)); + } + + public function testSetLazyValueFloat(): void { + $config = $this->generateAppConfig(); + $config->setValueFloat('feed', 'float', 3.14, true); + $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0, true)); + } + + public function testSetLazyValueFloatCache(): void { + $config = $this->generateAppConfig(); + $config->setValueFloat('feed', 'float', 3.14, true); + $status = $config->statusCache(); + $this->assertSame('3.14', $status['lazyCache']['feed']['float']); + } + + public function testSetLazyValueFloatDatabase(): void { + $config = $this->generateAppConfig(); + $config->setValueFloat('feed', 'float', 3.14, true); + $config->clearCache(); + $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0, true)); + } + + public function testSetLazyValueFloatAsNonLazy(): void { + $config = $this->generateAppConfig(); + $config->setValueFloat('feed', 'float', 3.14, true); + $config->setValueFloat('feed', 'float', 3.14, false); + $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0)); + } + + public function testSetNonLazyValueFloatAsLazy(): void { + $config = $this->generateAppConfig(); + $config->setValueFloat('feed', 'float', 3.14, false); + $config->setValueFloat('feed', 'float', 3.14, true); + $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0, true)); + } + + public function testSetSensitiveValueFloat(): void { + $config = $this->generateAppConfig(); + $config->setValueFloat('feed', 'float', 3.14, sensitive: true); + $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0)); + } + + public function testSetSensitiveValueFloatCache(): void { + $config = $this->generateAppConfig(); + $config->setValueFloat('feed', 'float', 3.14, sensitive: true); + $status = $config->statusCache(); + $this->assertStringStartsWith(self::invokePrivate(AppConfig::class, 'ENCRYPTION_PREFIX'), $status['fastCache']['feed']['float']); + } + + public function testSetSensitiveValueFloatDatabase(): void { + $config = $this->generateAppConfig(); + $config->setValueFloat('feed', 'float', 3.14, sensitive: true); + $config->clearCache(); + $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0)); + } + + public function testSetNonSensitiveValueFloatAsSensitive(): void { + $config = $this->generateAppConfig(); + $config->setValueFloat('feed', 'float', 3.14); + $config->setValueFloat('feed', 'float', 3.14, sensitive: true); + $this->assertSame(true, $config->isSensitive('feed', 'float')); + } + + public function testSetSensitiveValueFloatAsNonSensitiveStaysSensitive(): void { + $config = $this->generateAppConfig(); + $config->setValueFloat('feed', 'float', 3.14, sensitive: true); + $config->setValueFloat('feed', 'float', 1.23); + $this->assertSame(true, $config->isSensitive('feed', 'float')); + } + + public function testSetSensitiveValueFloatAsNonSensitiveAreStillUpdated(): void { + $config = $this->generateAppConfig(); + $config->setValueFloat('feed', 'float', 3.14, sensitive: true); + $config->setValueFloat('feed', 'float', 1.23); + $this->assertSame(1.23, $config->getValueFloat('feed', 'float', 0)); + } + + public function testSetLazyValueBool(): void { + $config = $this->generateAppConfig(); + $config->setValueBool('feed', 'bool', true, true); + $this->assertSame(true, $config->getValueBool('feed', 'bool', false, true)); + } + + public function testSetLazyValueBoolCache(): void { + $config = $this->generateAppConfig(); + $config->setValueBool('feed', 'bool', true, true); + $status = $config->statusCache(); + $this->assertSame('1', $status['lazyCache']['feed']['bool']); + } + + public function testSetLazyValueBoolDatabase(): void { + $config = $this->generateAppConfig(); + $config->setValueBool('feed', 'bool', true, true); + $config->clearCache(); + $this->assertSame(true, $config->getValueBool('feed', 'bool', false, true)); + } + + public function testSetLazyValueBoolAsNonLazy(): void { + $config = $this->generateAppConfig(); + $config->setValueBool('feed', 'bool', true, true); + $config->setValueBool('feed', 'bool', true, false); + $this->assertSame(true, $config->getValueBool('feed', 'bool', false)); + } + + public function testSetNonLazyValueBoolAsLazy(): void { + $config = $this->generateAppConfig(); + $config->setValueBool('feed', 'bool', true, false); + $config->setValueBool('feed', 'bool', true, true); + $this->assertSame(true, $config->getValueBool('feed', 'bool', false, true)); + } + + public function testSetLazyValueArray(): void { + $config = $this->generateAppConfig(); + $config->setValueArray('feed', 'array', ['test' => 1], true); + $this->assertSame(['test' => 1], $config->getValueArray('feed', 'array', [], true)); + } + + public function testSetLazyValueArrayCache(): void { + $config = $this->generateAppConfig(); + $config->setValueArray('feed', 'array', ['test' => 1], true); + $status = $config->statusCache(); + $this->assertSame('{"test":1}', $status['lazyCache']['feed']['array']); + } + + public function testSetLazyValueArrayDatabase(): void { + $config = $this->generateAppConfig(); + $config->setValueArray('feed', 'array', ['test' => 1], true); + $config->clearCache(); + $this->assertSame(['test' => 1], $config->getValueArray('feed', 'array', [], true)); + } + + public function testSetLazyValueArrayAsNonLazy(): void { + $config = $this->generateAppConfig(); + $config->setValueArray('feed', 'array', ['test' => 1], true); + $config->setValueArray('feed', 'array', ['test' => 1], false); + $this->assertSame(['test' => 1], $config->getValueArray('feed', 'array', [])); + } + + public function testSetNonLazyValueArrayAsLazy(): void { + $config = $this->generateAppConfig(); + $config->setValueArray('feed', 'array', ['test' => 1], false); + $config->setValueArray('feed', 'array', ['test' => 1], true); + $this->assertSame(['test' => 1], $config->getValueArray('feed', 'array', [], true)); + } + + + public function testSetSensitiveValueArray(): void { + $config = $this->generateAppConfig(); + $config->setValueArray('feed', 'array', ['test' => 1], sensitive: true); + $this->assertEqualsCanonicalizing(['test' => 1], $config->getValueArray('feed', 'array', [])); + } + + public function testSetSensitiveValueArrayCache(): void { + $config = $this->generateAppConfig(); + $config->setValueArray('feed', 'array', ['test' => 1], sensitive: true); + $status = $config->statusCache(); + $this->assertStringStartsWith(self::invokePrivate(AppConfig::class, 'ENCRYPTION_PREFIX'), $status['fastCache']['feed']['array']); + } + + public function testSetSensitiveValueArrayDatabase(): void { + $config = $this->generateAppConfig(); + $config->setValueArray('feed', 'array', ['test' => 1], sensitive: true); + $config->clearCache(); + $this->assertEqualsCanonicalizing(['test' => 1], $config->getValueArray('feed', 'array', [])); + } + + public function testSetNonSensitiveValueArrayAsSensitive(): void { + $config = $this->generateAppConfig(); + $config->setValueArray('feed', 'array', ['test' => 1]); + $config->setValueArray('feed', 'array', ['test' => 1], sensitive: true); + $this->assertSame(true, $config->isSensitive('feed', 'array')); + } + + public function testSetSensitiveValueArrayAsNonSensitiveStaysSensitive(): void { + $config = $this->generateAppConfig(); + $config->setValueArray('feed', 'array', ['test' => 1], sensitive: true); + $config->setValueArray('feed', 'array', ['test' => 2]); + $this->assertSame(true, $config->isSensitive('feed', 'array')); + } + + public function testSetSensitiveValueArrayAsNonSensitiveAreStillUpdated(): void { + $config = $this->generateAppConfig(); + $config->setValueArray('feed', 'array', ['test' => 1], sensitive: true); + $config->setValueArray('feed', 'array', ['test' => 2]); + $this->assertEqualsCanonicalizing(['test' => 2], $config->getValueArray('feed', 'array', [])); + } + + public function testUpdateNotSensitiveToSensitive(): void { + $config = $this->generateAppConfig(); + $config->updateSensitive('non-sensitive-app', 'lazy-key', true); + $this->assertSame(true, $config->isSensitive('non-sensitive-app', 'lazy-key', true)); + } + + public function testUpdateSensitiveToNotSensitive(): void { + $config = $this->generateAppConfig(); + $config->updateSensitive('sensitive-app', 'lazy-key', false); + $this->assertSame(false, $config->isSensitive('sensitive-app', 'lazy-key', true)); + } + + public function testUpdateSensitiveToSensitiveReturnsFalse(): void { + $config = $this->generateAppConfig(); + $this->assertSame(false, $config->updateSensitive('sensitive-app', 'lazy-key', true)); + } + + public function testUpdateNotSensitiveToNotSensitiveReturnsFalse(): void { + $config = $this->generateAppConfig(); + $this->assertSame(false, $config->updateSensitive('non-sensitive-app', 'lazy-key', false)); + } + + public function testUpdateSensitiveOnUnknownKeyReturnsFalse(): void { + $config = $this->generateAppConfig(); + $this->assertSame(false, $config->updateSensitive('non-sensitive-app', 'unknown-key', true)); + } + + public function testUpdateNotLazyToLazy(): void { + $config = $this->generateAppConfig(); + $config->updateLazy('non-sensitive-app', 'non-lazy-key', true); + $this->assertSame(true, $config->isLazy('non-sensitive-app', 'non-lazy-key')); + } + + public function testUpdateLazyToNotLazy(): void { + $config = $this->generateAppConfig(); + $config->updateLazy('non-sensitive-app', 'lazy-key', false); + $this->assertSame(false, $config->isLazy('non-sensitive-app', 'lazy-key')); + } + + public function testUpdateLazyToLazyReturnsFalse(): void { + $config = $this->generateAppConfig(); + $this->assertSame(false, $config->updateLazy('non-sensitive-app', 'lazy-key', true)); + } + + public function testUpdateNotLazyToNotLazyReturnsFalse(): void { + $config = $this->generateAppConfig(); + $this->assertSame(false, $config->updateLazy('non-sensitive-app', 'non-lazy-key', false)); + } + + public function testUpdateLazyOnUnknownKeyReturnsFalse(): void { + $config = $this->generateAppConfig(); + $this->assertSame(false, $config->updateLazy('non-sensitive-app', 'unknown-key', true)); + } + + public function testGetDetails(): void { + $config = $this->generateAppConfig(); + $this->assertEquals( + [ + 'app' => 'non-sensitive-app', + 'key' => 'lazy-key', + 'value' => 'value', + 'type' => 4, + 'lazy' => true, + 'typeString' => 'string', + 'sensitive' => false, + ], + $config->getDetails('non-sensitive-app', 'lazy-key') + ); + } + + public function testGetDetailsSensitive(): void { + $config = $this->generateAppConfig(); + $this->assertEquals( + [ + 'app' => 'sensitive-app', + 'key' => 'lazy-key', + 'value' => $this->baseStruct['sensitive-app']['lazy-key']['encrypted'], + 'type' => 4, + 'lazy' => true, + 'typeString' => 'string', + 'sensitive' => true, + ], + $config->getDetails('sensitive-app', 'lazy-key') + ); + } + + public function testGetDetailsInt(): void { + $config = $this->generateAppConfig(); + $this->assertEquals( + [ + 'app' => 'typed', + 'key' => 'int', + 'value' => '42', + 'type' => 8, + 'lazy' => false, + 'typeString' => 'integer', + 'sensitive' => false + ], + $config->getDetails('typed', 'int') + ); + } + + public function testGetDetailsFloat(): void { + $config = $this->generateAppConfig(); + $this->assertEquals( + [ + 'app' => 'typed', + 'key' => 'float', + 'value' => '3.14', + 'type' => 16, + 'lazy' => false, + 'typeString' => 'float', + 'sensitive' => false + ], + $config->getDetails('typed', 'float') + ); + } + + public function testGetDetailsBool(): void { + $config = $this->generateAppConfig(); + $this->assertEquals( + [ + 'app' => 'typed', + 'key' => 'bool', + 'value' => '1', + 'type' => 32, + 'lazy' => false, + 'typeString' => 'boolean', + 'sensitive' => false + ], + $config->getDetails('typed', 'bool') + ); + } + + public function testGetDetailsArray(): void { + $config = $this->generateAppConfig(); + $this->assertEquals( + [ + 'app' => 'typed', + 'key' => 'array', + 'value' => '{"test": 1}', + 'type' => 64, + 'lazy' => false, + 'typeString' => 'array', + 'sensitive' => false + ], + $config->getDetails('typed', 'array') + ); + } + + public function testDeleteKey(): void { + $config = $this->generateAppConfig(); + $config->deleteKey('anotherapp', 'key'); + $this->assertSame('default', $config->getValueString('anotherapp', 'key', 'default')); + } + + public function testDeleteKeyCache(): void { + $config = $this->generateAppConfig(); + $config->deleteKey('anotherapp', 'key'); + $status = $config->statusCache(); + $this->assertEqualsCanonicalizing(['enabled' => 'false'], $status['fastCache']['anotherapp']); + } + + public function testDeleteKeyDatabase(): void { + $config = $this->generateAppConfig(); + $config->deleteKey('anotherapp', 'key'); + $config->clearCache(); + $this->assertSame('default', $config->getValueString('anotherapp', 'key', 'default')); + } + + public function testDeleteApp(): void { + $config = $this->generateAppConfig(); + $config->deleteApp('anotherapp'); + $this->assertSame('default', $config->getValueString('anotherapp', 'key', 'default')); + $this->assertSame('default', $config->getValueString('anotherapp', 'enabled', 'default')); + } + + public function testDeleteAppCache(): void { + $config = $this->generateAppConfig(); + $status = $config->statusCache(); + $this->assertSame(true, isset($status['fastCache']['anotherapp'])); + $config->deleteApp('anotherapp'); + $status = $config->statusCache(); + $this->assertSame(false, isset($status['fastCache']['anotherapp'])); + } + + public function testDeleteAppDatabase(): void { + $config = $this->generateAppConfig(); + $config->deleteApp('anotherapp'); + $config->clearCache(); + $this->assertSame('default', $config->getValueString('anotherapp', 'key', 'default')); + $this->assertSame('default', $config->getValueString('anotherapp', 'enabled', 'default')); + } + + public function testClearCache(): void { + $config = $this->generateAppConfig(); + $config->setValueString('feed', 'string', '123454'); + $config->clearCache(); + $status = $config->statusCache(); + $this->assertSame([], $status['fastCache']); + } + + public function testSensitiveValuesAreEncrypted(): void { + $key = self::getUniqueID('secret'); + + $appConfig = $this->generateAppConfig(); + $secret = md5((string) time()); + $appConfig->setValueString('testapp', $key, $secret, sensitive: true); + + $this->assertConfigValueNotEquals('testapp', $key, $secret); + + // Can get in same run + $actualSecret = $appConfig->getValueString('testapp', $key); + $this->assertEquals($secret, $actualSecret); + + // Can get freshly decrypted from DB + $newAppConfig = $this->generateAppConfig(); + $actualSecret = $newAppConfig->getValueString('testapp', $key); + $this->assertEquals($secret, $actualSecret); + } + + public function testMigratingNonSensitiveValueToSensitiveWithSetValue(): void { + $key = self::getUniqueID('secret'); + $appConfig = $this->generateAppConfig(); + $secret = sha1((string) time()); + + // Unencrypted + $appConfig->setValueString('testapp', $key, $secret); + $this->assertConfigKey('testapp', $key, $secret); + + // Can get freshly decrypted from DB + $newAppConfig = $this->generateAppConfig(); + $actualSecret = $newAppConfig->getValueString('testapp', $key); + $this->assertEquals($secret, $actualSecret); + + // Encrypting on change + $appConfig->setValueString('testapp', $key, $secret, sensitive: true); + $this->assertConfigValueNotEquals('testapp', $key, $secret); + + // Can get in same run + $actualSecret = $appConfig->getValueString('testapp', $key); + $this->assertEquals($secret, $actualSecret); + + // Can get freshly decrypted from DB + $newAppConfig = $this->generateAppConfig(); + $actualSecret = $newAppConfig->getValueString('testapp', $key); + $this->assertEquals($secret, $actualSecret); + } + + public function testUpdateSensitiveValueToNonSensitiveWithUpdateSensitive(): void { + $key = self::getUniqueID('secret'); + $appConfig = $this->generateAppConfig(); + $secret = sha1((string) time()); + + // Encrypted + $appConfig->setValueString('testapp', $key, $secret, sensitive: true); + $this->assertConfigValueNotEquals('testapp', $key, $secret); + + // Migrate to non-sensitive / non-encrypted + $appConfig->updateSensitive('testapp', $key, false); + $this->assertConfigKey('testapp', $key, $secret); + } + + public function testUpdateNonSensitiveValueToSensitiveWithUpdateSensitive(): void { + $key = self::getUniqueID('secret'); + $appConfig = $this->generateAppConfig(); + $secret = sha1((string) time()); + + // Unencrypted + $appConfig->setValueString('testapp', $key, $secret); + $this->assertConfigKey('testapp', $key, $secret); + + // Migrate to sensitive / encrypted + $appConfig->updateSensitive('testapp', $key, true); + $this->assertConfigValueNotEquals('testapp', $key, $secret); + } + + protected function loadConfigValueFromDatabase(string $app, string $key): string|false { + $sql = $this->connection->getQueryBuilder(); $sql->select('configvalue') ->from('appconfig') ->where($sql->expr()->eq('appid', $sql->createParameter('appid'))) ->andWhere($sql->expr()->eq('configkey', $sql->createParameter('configkey'))) ->setParameter('appid', $app) ->setParameter('configkey', $key); - $query = $sql->execute(); - $actual = $query->fetch(); + $query = $sql->executeQuery(); + $actual = $query->fetchOne(); $query->closeCursor(); - $this->assertEquals($expected, $actual['configvalue']); + return $actual; + } + + protected function assertConfigKey(string $app, string $key, string|false $expected): void { + $this->assertEquals($expected, $this->loadConfigValueFromDatabase($app, $key)); + } + + protected function assertConfigValueNotEquals(string $app, string $key, string|false $expected): void { + $this->assertNotEquals($expected, $this->loadConfigValueFromDatabase($app, $key)); } } diff --git a/tests/lib/AppFramework/Controller/ControllerTest.php b/tests/lib/AppFramework/Controller/ControllerTest.php index cb6fc777dee..e435bbc44e3 100644 --- a/tests/lib/AppFramework/Controller/ControllerTest.php +++ b/tests/lib/AppFramework/Controller/ControllerTest.php @@ -23,6 +23,7 @@ namespace Test\AppFramework\Controller; +use OC\AppFramework\DependencyInjection\DIContainer; use OC\AppFramework\Http\Request; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\DataResponse; @@ -30,7 +31,6 @@ use OCP\AppFramework\Http\JSONResponse; use OCP\IConfig; use OCP\IRequest; use OCP\IRequestId; -use OC\AppFramework\DependencyInjection\DIContainer; class ChildController extends Controller { public function __construct($appName, $request) { diff --git a/tests/lib/AppFramework/Http/ContentSecurityPolicyTest.php b/tests/lib/AppFramework/Http/ContentSecurityPolicyTest.php index 8e6ac32b416..1f92410888c 100644 --- a/tests/lib/AppFramework/Http/ContentSecurityPolicyTest.php +++ b/tests/lib/AppFramework/Http/ContentSecurityPolicyTest.php @@ -468,6 +468,15 @@ class ContentSecurityPolicyTest extends \Test\TestCase { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'nonce-".base64_encode($nonce) . "';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; $this->contentSecurityPolicy->useJsNonce($nonce); + $this->contentSecurityPolicy->useStrictDynamicOnScripts(false); + $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); + } + + public function testGetPolicyNonceDefault() { + $nonce = 'my-nonce'; + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'nonce-".base64_encode($nonce) . "';script-src-elem 'strict-dynamic' 'nonce-".base64_encode($nonce) . "';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; + + $this->contentSecurityPolicy->useJsNonce($nonce); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } @@ -477,6 +486,31 @@ class ContentSecurityPolicyTest extends \Test\TestCase { $this->contentSecurityPolicy->useJsNonce($nonce); $this->contentSecurityPolicy->useStrictDynamic(true); + $this->contentSecurityPolicy->useStrictDynamicOnScripts(false); + $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); + } + + public function testGetPolicyNonceStrictDynamicDefault() { + $nonce = 'my-nonce'; + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'strict-dynamic' 'nonce-".base64_encode($nonce) . "';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; + + $this->contentSecurityPolicy->useJsNonce($nonce); + $this->contentSecurityPolicy->useStrictDynamic(true); + $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); + } + + public function testGetPolicyStrictDynamicOnScriptsOff() { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; + + $this->contentSecurityPolicy->useStrictDynamicOnScripts(false); + $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); + } + + public function testGetPolicyStrictDynamicAndStrictDynamicOnScripts() { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; + + $this->contentSecurityPolicy->useStrictDynamic(true); + $this->contentSecurityPolicy->useStrictDynamicOnScripts(true); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } } diff --git a/tests/lib/AppFramework/Http/DispatcherTest.php b/tests/lib/AppFramework/Http/DispatcherTest.php index 7f81701a8b3..aa74fe5c6ea 100644 --- a/tests/lib/AppFramework/Http/DispatcherTest.php +++ b/tests/lib/AppFramework/Http/DispatcherTest.php @@ -31,14 +31,15 @@ use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\JSONResponse; +use OCP\AppFramework\Http\ParameterOutOfRangeException; use OCP\AppFramework\Http\Response; use OCP\Diagnostics\IEventLogger; use OCP\IConfig; use OCP\IRequest; +use OCP\IRequestId; use PHPUnit\Framework\MockObject\MockObject; use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; -use OCP\IRequestId; class TestController extends Controller { /** @@ -522,4 +523,51 @@ class DispatcherTest extends \Test\TestCase { $this->assertEquals('{"text":[3,true,4,1]}', $response[3]); } + + + public function rangeDataProvider(): array { + return [ + [PHP_INT_MIN, PHP_INT_MAX, 42, false], + [0, 12, -5, true], + [-12, 0, 5, true], + [7, 14, 5, true], + [7, 14, 10, false], + [-14, -7, -10, false], + ]; + } + + /** + * @dataProvider rangeDataProvider + */ + public function testEnsureParameterValueSatisfiesRange(int $min, int $max, int $input, bool $throw): void { + $this->reflector = $this->createMock(ControllerMethodReflector::class); + $this->reflector->expects($this->any()) + ->method('getRange') + ->willReturn([ + 'min' => $min, + 'max' => $max, + ]); + + $this->dispatcher = new Dispatcher( + $this->http, + $this->middlewareDispatcher, + $this->reflector, + $this->request, + $this->config, + \OC::$server->getDatabaseConnection(), + $this->logger, + $this->eventLogger, + $this->container, + ); + + if ($throw) { + $this->expectException(ParameterOutOfRangeException::class); + } + + $this->invokePrivate($this->dispatcher, 'ensureParameterValueSatisfiesRange', ['myArgument', $input]); + if (!$throw) { + // do not mark this test risky + $this->assertTrue(true); + } + } } diff --git a/tests/lib/AppFramework/Http/EmptyContentSecurityPolicyTest.php b/tests/lib/AppFramework/Http/EmptyContentSecurityPolicyTest.php index 328e464f981..8f548b7bb91 100644 --- a/tests/lib/AppFramework/Http/EmptyContentSecurityPolicyTest.php +++ b/tests/lib/AppFramework/Http/EmptyContentSecurityPolicyTest.php @@ -425,6 +425,42 @@ class EmptyContentSecurityPolicyTest extends \Test\TestCase { $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } + public function testGetPolicyWithJsNonceAndStrictDynamic() { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'strict-dynamic' 'nonce-TXlKc05vbmNl' www.nextcloud.com;frame-ancestors 'none'"; + + $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->useStrictDynamic(true); + $this->contentSecurityPolicy->useJsNonce('MyJsNonce'); + $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); + } + + public function testGetPolicyWithJsNonceAndStrictDynamicAndStrictDynamicOnScripts() { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'strict-dynamic' 'nonce-TXlKc05vbmNl' www.nextcloud.com;frame-ancestors 'none'"; + + $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->useStrictDynamic(true); + $this->contentSecurityPolicy->useStrictDynamicOnScripts(true); + $this->contentSecurityPolicy->useJsNonce('MyJsNonce'); + // Should be same as `testGetPolicyWithJsNonceAndStrictDynamic` because of fallback + $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); + } + + public function testGetPolicyWithJsNonceAndStrictDynamicOnScripts() { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'nonce-TXlKc05vbmNl' www.nextcloud.com;script-src-elem 'strict-dynamic' 'nonce-TXlKc05vbmNl' www.nextcloud.com;frame-ancestors 'none'"; + + $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->useStrictDynamicOnScripts(true); + $this->contentSecurityPolicy->useJsNonce('MyJsNonce'); + $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); + } + + public function testGetPolicyWithStrictDynamicOnScripts() { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'"; + + $this->contentSecurityPolicy->useStrictDynamicOnScripts(true); + $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); + } + public function testGetPolicyWithJsNonceAndSelfScriptDomain() { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'nonce-TXlKc05vbmNl';frame-ancestors 'none'"; diff --git a/tests/lib/AppFramework/Http/RequestTest.php b/tests/lib/AppFramework/Http/RequestTest.php index 0ce2e283bb5..2af5d3ef18a 100644 --- a/tests/lib/AppFramework/Http/RequestTest.php +++ b/tests/lib/AppFramework/Http/RequestTest.php @@ -549,331 +549,188 @@ class RequestTest extends \Test\TestCase { $this->assertEquals('3', $request->getParams()['id']); } - public function testGetRemoteAddressWithoutTrustedRemote() { - $this->config - ->expects($this->once()) - ->method('getSystemValue') - ->with('trusted_proxies') - ->willReturn([]); - - $request = new Request( - [ - 'server' => [ + public function dataGetRemoteAddress(): array { + return [ + 'IPv4 without trusted remote' => [ + [ 'REMOTE_ADDR' => '10.0.0.2', 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4', - 'HTTP_X_FORWARDED_FOR' => '192.168.0.233' + 'HTTP_X_FORWARDED_FOR' => '192.168.0.233', ], + [], + [], + '10.0.0.2', ], - $this->requestId, - $this->config, - $this->csrfTokenManager, - $this->stream - ); - - $this->assertSame('10.0.0.2', $request->getRemoteAddress()); - } - - public function testGetRemoteAddressWithNoTrustedHeader() { - $this->config - ->expects($this->exactly(2)) - ->method('getSystemValue') - ->withConsecutive( - ['trusted_proxies'], - ['forwarded_for_headers'], - )->willReturnOnConsecutiveCalls( - ['10.0.0.2'], - [] - ); - - $request = new Request( - [ - 'server' => [ + 'IPv4 without trusted headers' => [ + [ 'REMOTE_ADDR' => '10.0.0.2', 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4', - 'HTTP_X_FORWARDED_FOR' => '192.168.0.233' + 'HTTP_X_FORWARDED_FOR' => '192.168.0.233', ], - ], - $this->requestId, - $this->config, - $this->csrfTokenManager, - $this->stream - ); - - $this->assertSame('10.0.0.2', $request->getRemoteAddress()); - } - - public function testGetRemoteAddressWithSingleTrustedRemote() { - $this->config - ->expects($this->exactly(2)) - ->method('getSystemValue') - ->withConsecutive( - ['trusted_proxies'], - ['forwarded_for_headers'], - )-> willReturnOnConsecutiveCalls( ['10.0.0.2'], - ['HTTP_X_FORWARDED'], - ); - - $request = new Request( - [ - 'server' => [ + [], + '10.0.0.2', + ], + 'IPv4 with single trusted remote' => [ + [ 'REMOTE_ADDR' => '10.0.0.2', 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4', - 'HTTP_X_FORWARDED_FOR' => '192.168.0.233' + 'HTTP_X_FORWARDED_FOR' => '192.168.0.233', ], - ], - $this->requestId, - $this->config, - $this->csrfTokenManager, - $this->stream - ); - - $this->assertSame('10.4.0.5', $request->getRemoteAddress()); - } - - public function testGetRemoteAddressIPv6WithSingleTrustedRemote() { - $this->config - ->expects($this->exactly(2)) - ->method('getSystemValue') - ->withConsecutive( - ['trusted_proxies'], - ['forwarded_for_headers'], - )-> willReturnOnConsecutiveCalls( - ['2001:db8:85a3:8d3:1319:8a2e:370:7348'], + ['10.0.0.2'], ['HTTP_X_FORWARDED'], - ); - - $request = new Request( - [ - 'server' => [ + '10.4.0.4', + ], + 'IPv6 with single trusted remote' => [ + [ 'REMOTE_ADDR' => '2001:db8:85a3:8d3:1319:8a2e:370:7348', 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4', - 'HTTP_X_FORWARDED_FOR' => '192.168.0.233' + 'HTTP_X_FORWARDED_FOR' => '192.168.0.233', ], + ['2001:db8:85a3:8d3:1319:8a2e:370:7348'], + ['HTTP_X_FORWARDED'], + '10.4.0.4', ], - $this->requestId, - $this->config, - $this->csrfTokenManager, - $this->stream - ); - - $this->assertSame('10.4.0.5', $request->getRemoteAddress()); - } - - public function testGetRemoteAddressVerifyPriorityHeader() { - $this->config - ->expects($this->exactly(2)) - ->method('getSystemValue') - ->withConsecutive( - ['trusted_proxies'], - ['forwarded_for_headers'], - )-> willReturnOnConsecutiveCalls( + 'IPv4 with multiple trusted remotes' => [ + [ + 'REMOTE_ADDR' => '10.0.0.2', + 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4, ::1', + 'HTTP_X_FORWARDED_FOR' => '192.168.0.233', + ], + ['10.0.0.2', '::1'], + ['HTTP_X_FORWARDED'], + '10.4.0.4', + ], + 'IPv4 order of forwarded-for headers' => [ + [ + 'REMOTE_ADDR' => '10.0.0.2', + 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4', + 'HTTP_X_FORWARDED_FOR' => '192.168.0.233', + ], ['10.0.0.2'], [ - 'HTTP_CLIENT_IP', - 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', + 'HTTP_X_FORWARDED_FOR', + 'HTTP_CLIENT_IP', ], - ); - - $request = new Request( - [ - 'server' => [ + '192.168.0.233', + ], + 'IPv4 order of forwarded-for headers (reversed)' => [ + [ 'REMOTE_ADDR' => '10.0.0.2', 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4', - 'HTTP_X_FORWARDED_FOR' => '192.168.0.233' + 'HTTP_X_FORWARDED_FOR' => '192.168.0.233', ], - ], - $this->requestId, - $this->config, - $this->csrfTokenManager, - $this->stream - ); - - $this->assertSame('192.168.0.233', $request->getRemoteAddress()); - } - - public function testGetRemoteAddressIPv6VerifyPriorityHeader() { - $this->config - ->expects($this->exactly(2)) - ->method('getSystemValue') - ->withConsecutive( - ['trusted_proxies'], - ['forwarded_for_headers'], - )-> willReturnOnConsecutiveCalls( - ['2001:db8:85a3:8d3:1319:8a2e:370:7348'], + ['10.0.0.2'], [ 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', - 'HTTP_X_FORWARDED' + 'HTTP_X_FORWARDED', ], - ); - - $request = new Request( - [ - 'server' => [ + '10.4.0.4', + ], + 'IPv6 order of forwarded-for headers' => [ + [ 'REMOTE_ADDR' => '2001:db8:85a3:8d3:1319:8a2e:370:7348', 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4', - 'HTTP_X_FORWARDED_FOR' => '192.168.0.233' + 'HTTP_X_FORWARDED_FOR' => '192.168.0.233', ], - ], - $this->requestId, - $this->config, - $this->csrfTokenManager, - $this->stream - ); - - $this->assertSame('192.168.0.233', $request->getRemoteAddress()); - } - - public function testGetRemoteAddressWithMatchingCidrTrustedRemote() { - $this->config - ->expects($this->exactly(2)) - ->method('getSystemValue') - ->withConsecutive( - ['trusted_proxies'], - ['forwarded_for_headers'], - )-> willReturnOnConsecutiveCalls( - ['192.168.2.0/24'], - ['HTTP_X_FORWARDED_FOR'], - ); - - $request = new Request( - [ - 'server' => [ - 'REMOTE_ADDR' => '192.168.2.99', - 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4', - 'HTTP_X_FORWARDED_FOR' => '192.168.0.233' + ['2001:db8:85a3:8d3:1319:8a2e:370:7348'], + [ + 'HTTP_X_FORWARDED', + 'HTTP_X_FORWARDED_FOR', + 'HTTP_CLIENT_IP', ], + '192.168.0.233', ], - $this->requestId, - $this->config, - $this->csrfTokenManager, - $this->stream - ); - - $this->assertSame('192.168.0.233', $request->getRemoteAddress()); - } - - public function testGetRemoteAddressWithNotMatchingCidrTrustedRemote() { - $this->config - ->expects($this->once()) - ->method('getSystemValue') - ->with('trusted_proxies') - ->willReturn(['192.168.2.0/24']); - - $request = new Request( - [ - 'server' => [ + 'IPv4 matching CIDR of trusted proxy' => [ + [ 'REMOTE_ADDR' => '192.168.3.99', 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4', - 'HTTP_X_FORWARDED_FOR' => '192.168.0.233' + 'HTTP_X_FORWARDED_FOR' => '192.168.0.233', ], + ['192.168.2.0/24'], + ['HTTP_X_FORWARDED_FOR'], + '192.168.3.99', ], - $this->requestId, - $this->config, - $this->csrfTokenManager, - $this->stream - ); - - $this->assertSame('192.168.3.99', $request->getRemoteAddress()); - } - - public function testGetRemoteIpv6AddressWithMatchingIpv6CidrTrustedRemote() { - $this->config - ->expects($this->exactly(2)) - ->method('getSystemValue') - ->withConsecutive( - ['trusted_proxies'], - ['forwarded_for_headers'] - )->willReturnOnConsecutiveCalls( - ['2001:db8:85a3:8d3:1319:8a20::/95'], - ['HTTP_X_FORWARDED_FOR'] - ); - - $request = new Request( - [ - 'server' => [ + 'IPv6 matching CIDR of trusted proxy' => [ + [ 'REMOTE_ADDR' => '2001:db8:85a3:8d3:1319:8a21:370:7348', 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4', - 'HTTP_X_FORWARDED_FOR' => '192.168.0.233' + 'HTTP_X_FORWARDED_FOR' => '192.168.0.233', ], + ['2001:db8:85a3:8d3:1319:8a20::/95'], + ['HTTP_X_FORWARDED_FOR'], + '192.168.0.233', ], - $this->requestId, - $this->config, - $this->csrfTokenManager, - $this->stream - ); - - $this->assertSame('192.168.0.233', $request->getRemoteAddress()); - } - - public function testGetRemoteAddressIpv6WithNotMatchingCidrTrustedRemote() { - $this->config - ->expects($this->once()) - ->method('getSystemValue') - ->with('trusted_proxies') - ->willReturn(['fd::/8']); - - $request = new Request( - [ - 'server' => [ + 'IPv6 not matching CIDR of trusted proxy' => [ + [ 'REMOTE_ADDR' => '2001:db8:85a3:8d3:1319:8a2e:370:7348', 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4', - 'HTTP_X_FORWARDED_FOR' => '192.168.0.233' + 'HTTP_X_FORWARDED_FOR' => '192.168.0.233', ], + ['fd::/8'], + [], + '2001:db8:85a3:8d3:1319:8a2e:370:7348', ], - $this->requestId, - $this->config, - $this->csrfTokenManager, - $this->stream - ); - - $this->assertSame('2001:db8:85a3:8d3:1319:8a2e:370:7348', $request->getRemoteAddress()); - } - - public function testGetRemoteAddressIpv6WithInvalidTrustedProxy() { - $this->config - ->expects($this->once()) - ->method('getSystemValue') - ->with('trusted_proxies') - ->willReturn(['fx::/8']); - - $request = new Request( - [ - 'server' => [ + 'IPv6 with invalid trusted proxy' => [ + [ 'REMOTE_ADDR' => '2001:db8:85a3:8d3:1319:8a2e:370:7348', 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4', - 'HTTP_X_FORWARDED_FOR' => '192.168.0.233' + 'HTTP_X_FORWARDED_FOR' => '192.168.0.233', ], + ['fx::/8'], + [], + '2001:db8:85a3:8d3:1319:8a2e:370:7348', ], - $this->requestId, - $this->config, - $this->csrfTokenManager, - $this->stream - ); - - $this->assertSame('2001:db8:85a3:8d3:1319:8a2e:370:7348', $request->getRemoteAddress()); + 'IPv4 forwarded for IPv6' => [ + [ + 'REMOTE_ADDR' => '192.168.2.99', + 'HTTP_X_FORWARDED_FOR' => '[2001:db8:85a3:8d3:1319:8a2e:370:7348]', + ], + ['192.168.2.0/24'], + ['HTTP_X_FORWARDED_FOR'], + '2001:db8:85a3:8d3:1319:8a2e:370:7348', + ], + 'IPv4 with port' => [ + [ + 'REMOTE_ADDR' => '2001:db8:85a3:8d3:1319:8a2e:370:7348', + 'HTTP_X_FORWARDED_FOR' => '192.168.2.99:8080', + ], + ['2001:db8::/8'], + ['HTTP_X_FORWARDED_FOR'], + '192.168.2.99', + ], + 'IPv6 with port' => [ + [ + 'REMOTE_ADDR' => '192.168.2.99', + 'HTTP_X_FORWARDED_FOR' => '[2001:db8:85a3:8d3:1319:8a2e:370:7348]:8080', + ], + ['192.168.2.0/24'], + ['HTTP_X_FORWARDED_FOR'], + '2001:db8:85a3:8d3:1319:8a2e:370:7348', + ], + ]; } - public function testGetRemoteAddressWithXForwardedForIPv6() { + /** + * @dataProvider dataGetRemoteAddress + */ + public function testGetRemoteAddress(array $headers, array $trustedProxies, array $forwardedForHeaders, string $expected): void { $this->config - ->expects($this->exactly(2)) ->method('getSystemValue') ->withConsecutive( ['trusted_proxies'], ['forwarded_for_headers'], - )-> willReturnOnConsecutiveCalls( - ['192.168.2.0/24'], - ['HTTP_X_FORWARDED_FOR'], + ) + ->willReturnOnConsecutiveCalls( + $trustedProxies, + $forwardedForHeaders, ); $request = new Request( [ - 'server' => [ - 'REMOTE_ADDR' => '192.168.2.99', - 'HTTP_X_FORWARDED_FOR' => '[2001:db8:85a3:8d3:1319:8a2e:370:7348]', - ], + 'server' => $headers, ], $this->requestId, $this->config, @@ -881,7 +738,7 @@ class RequestTest extends \Test\TestCase { $this->stream ); - $this->assertSame('2001:db8:85a3:8d3:1319:8a2e:370:7348', $request->getRemoteAddress()); + $this->assertSame($expected, $request->getRemoteAddress()); } /** @@ -1796,14 +1653,14 @@ class RequestTest extends \Test\TestCase { public function providesGetRequestUriWithOverwriteData() { return [ ['/scriptname.php/some/PathInfo', '/owncloud/', ''], - ['/scriptname.php/some/PathInfo', '/owncloud/', '123'], + ['/scriptname.php/some/PathInfo', '/owncloud/', '123', '123.123.123.123'], ]; } /** * @dataProvider providesGetRequestUriWithOverwriteData */ - public function testGetRequestUriWithOverwrite($expectedUri, $overwriteWebRoot, $overwriteCondAddr) { + public function testGetRequestUriWithOverwrite($expectedUri, $overwriteWebRoot, $overwriteCondAddr, $remoteAddr = '') { $this->config ->expects($this->exactly(2)) ->method('getSystemValueString') @@ -1812,13 +1669,14 @@ class RequestTest extends \Test\TestCase { ['overwritecondaddr', '', $overwriteCondAddr], ]); - $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') + $request = $this->getMockBuilder(Request::class) ->setMethods(['getScriptName']) ->setConstructorArgs([ [ 'server' => [ 'REQUEST_URI' => '/test.php/some/PathInfo', 'SCRIPT_NAME' => '/test.php', + 'REMOTE_ADDR' => $remoteAddr ] ], $this->requestId, diff --git a/tests/lib/AppFramework/Middleware/MiddlewareTest.php b/tests/lib/AppFramework/Middleware/MiddlewareTest.php index f9e775269d4..e4980009b09 100644 --- a/tests/lib/AppFramework/Middleware/MiddlewareTest.php +++ b/tests/lib/AppFramework/Middleware/MiddlewareTest.php @@ -23,13 +23,13 @@ namespace Test\AppFramework\Middleware; +use OC\AppFramework\DependencyInjection\DIContainer; use OC\AppFramework\Http\Request; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Middleware; use OCP\IConfig; use OCP\IRequestId; -use OC\AppFramework\DependencyInjection\DIContainer; class ChildMiddleware extends Middleware { }; diff --git a/tests/lib/AppFramework/OCS/BaseResponseTest.php b/tests/lib/AppFramework/OCS/BaseResponseTest.php index a1e0c620574..4857b573802 100644 --- a/tests/lib/AppFramework/OCS/BaseResponseTest.php +++ b/tests/lib/AppFramework/OCS/BaseResponseTest.php @@ -28,6 +28,17 @@ namespace Test\AppFramework\Middleware; use OC\AppFramework\OCS\BaseResponse; +class ArrayValue implements \JsonSerializable { + private $array; + public function __construct(array $array) { + $this->array = $array; + } + + public function jsonSerialize(): mixed { + return $this->array; + } +} + class BaseResponseTest extends \Test\TestCase { public function testToXml(): void { /** @var BaseResponse $response */ @@ -56,4 +67,32 @@ class BaseResponseTest extends \Test\TestCase { $writer->outputMemory(true) ); } + + public function testToXmlJsonSerializable(): void { + /** @var BaseResponse $response */ + $response = $this->createMock(BaseResponse::class); + + $writer = new \XMLWriter(); + $writer->openMemory(); + $writer->setIndent(false); + $writer->startDocument(); + + $data = [ + 'hello' => 'hello', + 'information' => new ArrayValue([ + '@test' => 'some data', + 'someElement' => 'withAttribute', + ]), + 'value without key', + 'object' => new \stdClass(), + ]; + + $this->invokePrivate($response, 'toXml', [$data, $writer]); + $writer->endDocument(); + + $this->assertEquals( + "<?xml version=\"1.0\"?>\n<hello>hello</hello><information test=\"some data\"><someElement>withAttribute</someElement></information><element>value without key</element><object/>\n", + $writer->outputMemory(true) + ); + } } diff --git a/tests/lib/AppFramework/Routing/RoutingTest.php b/tests/lib/AppFramework/Routing/RoutingTest.php index d7fde02dbcb..c9812a5dfb7 100644 --- a/tests/lib/AppFramework/Routing/RoutingTest.php +++ b/tests/lib/AppFramework/Routing/RoutingTest.php @@ -267,14 +267,14 @@ class RoutingTest extends \Test\TestCase { * @param string $postfix */ private function assertSimpleOCSRoute($routes, - $name, - $verb, - $url, - $controllerName, - $actionName, - array $requirements = [], - array $defaults = [], - $postfix = '') { + $name, + $verb, + $url, + $controllerName, + $actionName, + array $requirements = [], + array $defaults = [], + $postfix = '') { if ($postfix) { $name .= $postfix; } diff --git a/tests/lib/AppFramework/Services/AppConfigTest.php b/tests/lib/AppFramework/Services/AppConfigTest.php new file mode 100644 index 00000000000..a7d14d5001c --- /dev/null +++ b/tests/lib/AppFramework/Services/AppConfigTest.php @@ -0,0 +1,691 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2024, Maxence Lange <maxence@artificial-owl.com + * + * @author Maxence Lange <maxence@artificial-owl.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 Test\AppFramework\Services; + +use OC\AppConfig as AppConfigCore; +use OC\AppFramework\Services\AppConfig; +use OCP\Exceptions\AppConfigTypeConflictException; +use OCP\Exceptions\AppConfigUnknownKeyException; +use OCP\IAppConfig as IAppConfigCore; +use OCP\IConfig; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class AppConfigTest extends TestCase { + private IConfig|MockObject $config; + private IAppConfigCore|MockObject $appConfigCore; + private AppConfig $appConfig; + + private const TEST_APPID = 'appconfig-test'; + + protected function setUp(): void { + parent::setUp(); + $this->config = $this->createMock(IConfig::class); + $this->appConfigCore = $this->createMock(AppConfigCore::class); + + $this->appConfig = new AppConfig($this->config, $this->appConfigCore, self::TEST_APPID); + } + + public function testGetAppKeys(): void { + $expected = ['key1', 'key2', 'key3', 'key4', 'key5', 'key6', 'key7', 'test8']; + $this->appConfigCore->expects($this->once()) + ->method('getKeys') + ->with(self::TEST_APPID) + ->willReturn($expected); + $this->assertSame($expected, $this->appConfig->getAppKeys()); + } + + + /** + * @return array + * @see testHasAppKey + */ + public function providerHasAppKey(): array { + return [ + // lazy, expected + [false, true], + [true, true], + [false, false], + [true, false], + ]; + } + + /** + * @dataProvider providerHasAppKey + * + * @param bool $lazy + * @param bool $expected + */ + public function testHasAppKey(bool $lazy, bool $expected): void { + $key = 'key'; + $this->appConfigCore->expects($this->once()) + ->method('hasKey') + ->with(self::TEST_APPID, $key, $lazy) + ->willReturn($expected); + $this->assertSame($expected, $this->appConfig->hasAppKey($key, $lazy)); + } + + + /** + * @return array + * @see testIsSensitive + */ + public function providerIsSensitive(): array { + return [ + // lazy, expected + [false, true], + [true, true], + [false, false], + [true, false], + ]; + } + + /** + * @dataProvider providerIsSensitive + * + * @param bool $lazy + * @param bool $expected + */ + public function testIsSensitive(bool $lazy, bool $expected): void { + $key = 'key'; + $this->appConfigCore->expects($this->once()) + ->method('isSensitive') + ->with(self::TEST_APPID, $key, $lazy) + ->willReturn($expected); + + $this->assertSame($expected, $this->appConfig->isSensitive($key, $lazy)); + } + + /** + * @dataProvider providerIsSensitive + * + * @param bool $lazy + * @param bool $expected + */ + public function testIsSensitiveException(bool $lazy, bool $expected): void { + $key = 'unknown-key'; + $this->appConfigCore->expects($this->once()) + ->method('isSensitive') + ->with(self::TEST_APPID, $key, $lazy) + ->willThrowException(new AppConfigUnknownKeyException()); + + $this->expectException(AppConfigUnknownKeyException::class); + $this->appConfig->isSensitive($key, $lazy); + } + + /** + * @return array + * @see testIsLazy + */ + public function providerIsLazy(): array { + return [ + // expected + [true], + [false], + ]; + } + + /** + * @dataProvider providerIsLazy + * + * @param bool $expected + */ + public function testIsLazy(bool $expected): void { + $key = 'key'; + $this->appConfigCore->expects($this->once()) + ->method('isLazy') + ->with(self::TEST_APPID, $key) + ->willReturn($expected); + + $this->assertSame($expected, $this->appConfig->isLazy($key)); + } + + public function testIsLazyException(): void { + $key = 'unknown-key'; + $this->appConfigCore->expects($this->once()) + ->method('isLazy') + ->with(self::TEST_APPID, $key) + ->willThrowException(new AppConfigUnknownKeyException()); + + $this->expectException(AppConfigUnknownKeyException::class); + $this->appConfig->isLazy($key); + } + + /** + * @return array + * @see testGetAllAppValues + */ + public function providerGetAllAppValues(): array { + return [ + // key, filtered + ['', false], + ['', true], + ['key', false], + ['key', true], + ]; + } + + /** + * @dataProvider providerGetAllAppValues + * + * @param string $key + * @param bool $filtered + */ + public function testGetAllAppValues(string $key, bool $filtered): void { + $expected = [ + 'key1' => 'value1', + 'key2' => 3, + 'key3' => 3.14, + 'key4' => true + ]; + + $this->appConfigCore->expects($this->once()) + ->method('getAllValues') + ->with(self::TEST_APPID, $key, $filtered) + ->willReturn($expected); + + $this->assertSame($expected, $this->appConfig->getAllAppValues($key, $filtered)); + } + + public function testSetAppValue(): void { + $key = 'key'; + $value = 'value'; + $this->appConfigCore->expects($this->once()) + ->method('setValueMixed') + ->with(self::TEST_APPID, $key, $value); + + $this->appConfig->setAppValue($key, $value); + } + + /** + * @return array + * @see testSetAppValueString + * @see testSetAppValueStringException + * @see testSetAppValueInt + * @see testSetAppValueIntException + * @see testSetAppValueFloat + * @see testSetAppValueFloatException + * @see testSetAppValueArray + * @see testSetAppValueArrayException + */ + public function providerSetAppValue(): array { + return [ + // lazy, sensitive, expected + [false, false, true], + [false, true, true], + [true, true, true], + [true, false, true], + [false, false, false], + [false, true, false], + [true, true, false], + [true, false, false], + ]; + } + + /** + * @dataProvider providerSetAppValue + * + * @param bool $lazy + * @param bool $sensitive + * @param bool $expected + */ + public function testSetAppValueString(bool $lazy, bool $sensitive, bool $expected): void { + $key = 'key'; + $value = 'valueString'; + $this->appConfigCore->expects($this->once()) + ->method('setValueString') + ->with(self::TEST_APPID, $key, $value, $lazy, $sensitive) + ->willReturn($expected); + + $this->assertSame($expected, $this->appConfig->setAppValueString($key, $value, $lazy, $sensitive)); + } + + /** + * @dataProvider providerSetAppValue + * + * @param bool $lazy + * @param bool $sensitive + */ + public function testSetAppValueStringException(bool $lazy, bool $sensitive): void { + $key = 'key'; + $value = 'valueString'; + $this->appConfigCore->expects($this->once()) + ->method('setValueString') + ->with(self::TEST_APPID, $key, $value, $lazy, $sensitive) + ->willThrowException(new AppConfigTypeConflictException()); + + $this->expectException(AppConfigTypeConflictException::class); + $this->appConfig->setAppValueString($key, $value, $lazy, $sensitive); + } + + /** + * @dataProvider providerSetAppValue + * + * @param bool $lazy + * @param bool $sensitive + * @param bool $expected + */ + public function testSetAppValueInt(bool $lazy, bool $sensitive, bool $expected): void { + $key = 'key'; + $value = 42; + $this->appConfigCore->expects($this->once()) + ->method('setValueInt') + ->with(self::TEST_APPID, $key, $value, $lazy, $sensitive) + ->willReturn($expected); + + $this->assertSame($expected, $this->appConfig->setAppValueInt($key, $value, $lazy, $sensitive)); + } + + /** + * @dataProvider providerSetAppValue + * + * @param bool $lazy + * @param bool $sensitive + */ + public function testSetAppValueIntException(bool $lazy, bool $sensitive): void { + $key = 'key'; + $value = 42; + $this->appConfigCore->expects($this->once()) + ->method('setValueInt') + ->with(self::TEST_APPID, $key, $value, $lazy, $sensitive) + ->willThrowException(new AppConfigTypeConflictException()); + + $this->expectException(AppConfigTypeConflictException::class); + $this->appConfig->setAppValueInt($key, $value, $lazy, $sensitive); + } + + /** + * @dataProvider providerSetAppValue + * + * @param bool $lazy + * @param bool $sensitive + * @param bool $expected + */ + public function testSetAppValueFloat(bool $lazy, bool $sensitive, bool $expected): void { + $key = 'key'; + $value = 3.14; + $this->appConfigCore->expects($this->once()) + ->method('setValueFloat') + ->with(self::TEST_APPID, $key, $value, $lazy, $sensitive) + ->willReturn($expected); + + $this->assertSame($expected, $this->appConfig->setAppValueFloat($key, $value, $lazy, $sensitive)); + } + + /** + * @dataProvider providerSetAppValue + * + * @param bool $lazy + * @param bool $sensitive + */ + public function testSetAppValueFloatException(bool $lazy, bool $sensitive): void { + $key = 'key'; + $value = 3.14; + $this->appConfigCore->expects($this->once()) + ->method('setValueFloat') + ->with(self::TEST_APPID, $key, $value, $lazy, $sensitive) + ->willThrowException(new AppConfigTypeConflictException()); + + $this->expectException(AppConfigTypeConflictException::class); + $this->appConfig->setAppValueFloat($key, $value, $lazy, $sensitive); + } + + /** + * @return array + * @see testSetAppValueBool + */ + public function providerSetAppValueBool(): array { + return [ + // lazy, expected + [false, true], + [false, false], + [true, true], + [true, false], + ]; + } + + /** + * @dataProvider providerSetAppValueBool + * + * @param bool $lazy + * @param bool $expected + */ + public function testSetAppValueBool(bool $lazy, bool $expected): void { + $key = 'key'; + $value = true; + $this->appConfigCore->expects($this->once()) + ->method('setValueBool') + ->with(self::TEST_APPID, $key, $value, $lazy) + ->willReturn($expected); + + $this->assertSame($expected, $this->appConfig->setAppValueBool($key, $value, $lazy)); + } + + /** + * @dataProvider providerSetAppValueBool + * + * @param bool $lazy + */ + public function testSetAppValueBoolException(bool $lazy): void { + $key = 'key'; + $value = true; + $this->appConfigCore->expects($this->once()) + ->method('setValueBool') + ->with(self::TEST_APPID, $key, $value, $lazy) + ->willThrowException(new AppConfigTypeConflictException()); + + $this->expectException(AppConfigTypeConflictException::class); + $this->appConfig->setAppValueBool($key, $value, $lazy); + } + + /** + * @dataProvider providerSetAppValue + * + * @param bool $lazy + * @param bool $sensitive + * @param bool $expected + */ + public function testSetAppValueArray(bool $lazy, bool $sensitive, bool $expected): void { + $key = 'key'; + $value = ['item' => true]; + $this->appConfigCore->expects($this->once()) + ->method('setValueArray') + ->with(self::TEST_APPID, $key, $value, $lazy, $sensitive) + ->willReturn($expected); + + $this->assertSame($expected, $this->appConfig->setAppValueArray($key, $value, $lazy, $sensitive)); + } + + /** + * @dataProvider providerSetAppValue + * + * @param bool $lazy + * @param bool $sensitive + */ + public function testSetAppValueArrayException(bool $lazy, bool $sensitive): void { + $key = 'key'; + $value = ['item' => true]; + $this->appConfigCore->expects($this->once()) + ->method('setValueArray') + ->with(self::TEST_APPID, $key, $value, $lazy, $sensitive) + ->willThrowException(new AppConfigTypeConflictException()); + + $this->expectException(AppConfigTypeConflictException::class); + $this->appConfig->setAppValueArray($key, $value, $lazy, $sensitive); + } + + public function testGetAppValue(): void { + $key = 'key'; + $value = 'value'; + $default = 'default'; + $this->appConfigCore->expects($this->once()) + ->method('getValueMixed') + ->with(self::TEST_APPID, $key, $default) + ->willReturn($value); + + $this->assertSame($value, $this->appConfig->getAppValue($key, $default)); + } + + public function testGetAppValueDefault(): void { + $key = 'key'; + $default = 'default'; + $this->appConfigCore->expects($this->once()) + ->method('getValueMixed') + ->with(self::TEST_APPID, $key, $default) + ->willReturn($default); + + $this->assertSame($default, $this->appConfig->getAppValue($key, $default)); + } + + /** + * @return array + * @see testGetAppValueString + * @see testGetAppValueStringException + * @see testGetAppValueInt + * @see testGetAppValueIntException + * @see testGetAppValueFloat + * @see testGetAppValueFloatException + * @see testGetAppValueBool + * @see testGetAppValueBoolException + * @see testGetAppValueArray + * @see testGetAppValueArrayException + */ + public function providerGetAppValue(): array { + return [ + // lazy, exist + [false, false], + [false, true], + [true, true], + [true, false] + ]; + } + + /** + * @dataProvider providerGetAppValue + * + * @param bool $lazy + * @param bool $exist + */ + public function testGetAppValueString(bool $lazy, bool $exist): void { + $key = 'key'; + $value = 'valueString'; + $default = 'default'; + + $expected = ($exist) ? $value : $default; + $this->appConfigCore->expects($this->once()) + ->method('getValueString') + ->with(self::TEST_APPID, $key, $default, $lazy) + ->willReturn($expected); + + $this->assertSame($expected, $this->appConfig->getAppValueString($key, $default, $lazy)); + } + + /** + * @dataProvider providerGetAppValue + * + * @param bool $lazy + */ + public function testGetAppValueStringException(bool $lazy): void { + $key = 'key'; + $default = 'default'; + + $this->appConfigCore->expects($this->once()) + ->method('getValueString') + ->with(self::TEST_APPID, $key, $default, $lazy) + ->willThrowException(new AppConfigTypeConflictException()); + + $this->expectException(AppConfigTypeConflictException::class); + $this->appConfig->getAppValueString($key, $default, $lazy); + } + + /** + * @dataProvider providerGetAppValue + * + * @param bool $lazy + * @param bool $exist + */ + public function testGetAppValueInt(bool $lazy, bool $exist): void { + $key = 'key'; + $value = 42; + $default = 17; + + $expected = ($exist) ? $value : $default; + $this->appConfigCore->expects($this->once()) + ->method('getValueInt') + ->with(self::TEST_APPID, $key, $default, $lazy) + ->willReturn($expected); + + $this->assertSame($expected, $this->appConfig->getAppValueInt($key, $default, $lazy)); + } + + /** + * @dataProvider providerGetAppValue + * + * @param bool $lazy + */ + public function testGetAppValueIntException(bool $lazy): void { + $key = 'key'; + $default = 17; + + $this->appConfigCore->expects($this->once()) + ->method('getValueInt') + ->with(self::TEST_APPID, $key, $default, $lazy) + ->willThrowException(new AppConfigTypeConflictException()); + + $this->expectException(AppConfigTypeConflictException::class); + $this->appConfig->getAppValueInt($key, $default, $lazy); + } + + /** + * @dataProvider providerGetAppValue + * + * @param bool $lazy + * @param bool $exist + */ + public function testGetAppValueFloat(bool $lazy, bool $exist): void { + $key = 'key'; + $value = 3.14; + $default = 17.04; + + $expected = ($exist) ? $value : $default; + $this->appConfigCore->expects($this->once()) + ->method('getValueFloat') + ->with(self::TEST_APPID, $key, $default, $lazy) + ->willReturn($expected); + + $this->assertSame($expected, $this->appConfig->getAppValueFloat($key, $default, $lazy)); + } + + /** + * @dataProvider providerGetAppValue + * + * @param bool $lazy + */ + public function testGetAppValueFloatException(bool $lazy): void { + $key = 'key'; + $default = 17.04; + + $this->appConfigCore->expects($this->once()) + ->method('getValueFloat') + ->with(self::TEST_APPID, $key, $default, $lazy) + ->willThrowException(new AppConfigTypeConflictException()); + + $this->expectException(AppConfigTypeConflictException::class); + $this->appConfig->getAppValueFloat($key, $default, $lazy); + } + + /** + * @dataProvider providerGetAppValue + * + * @param bool $lazy + * @param bool $exist + */ + public function testGetAppValueBool(bool $lazy, bool $exist): void { + $key = 'key'; + $value = true; + $default = false; + + $expected = ($exist) ? $value : $default; // yes, it can be simplified + $this->appConfigCore->expects($this->once()) + ->method('getValueBool') + ->with(self::TEST_APPID, $key, $default, $lazy) + ->willReturn($expected); + + $this->assertSame($expected, $this->appConfig->getAppValueBool($key, $default, $lazy)); + } + + /** + * @dataProvider providerGetAppValue + * + * @param bool $lazy + */ + public function testGetAppValueBoolException(bool $lazy): void { + $key = 'key'; + $default = false; + + $this->appConfigCore->expects($this->once()) + ->method('getValueBool') + ->with(self::TEST_APPID, $key, $default, $lazy) + ->willThrowException(new AppConfigTypeConflictException()); + + $this->expectException(AppConfigTypeConflictException::class); + $this->appConfig->getAppValueBool($key, $default, $lazy); + } + + /** + * @dataProvider providerGetAppValue + * + * @param bool $lazy + * @param bool $exist + */ + public function testGetAppValueArray(bool $lazy, bool $exist): void { + $key = 'key'; + $value = ['item' => true]; + $default = []; + + $expected = ($exist) ? $value : $default; + $this->appConfigCore->expects($this->once()) + ->method('getValueArray') + ->with(self::TEST_APPID, $key, $default, $lazy) + ->willReturn($expected); + + $this->assertSame($expected, $this->appConfig->getAppValueArray($key, $default, $lazy)); + } + + /** + * @dataProvider providerGetAppValue + * + * @param bool $lazy + */ + public function testGetAppValueArrayException(bool $lazy): void { + $key = 'key'; + $default = []; + + $this->appConfigCore->expects($this->once()) + ->method('getValueArray') + ->with(self::TEST_APPID, $key, $default, $lazy) + ->willThrowException(new AppConfigTypeConflictException()); + + $this->expectException(AppConfigTypeConflictException::class); + $this->appConfig->getAppValueArray($key, $default, $lazy); + } + + public function testDeleteAppValue(): void { + $key = 'key'; + $this->appConfigCore->expects($this->once()) + ->method('deleteKey') + ->with(self::TEST_APPID, $key); + + $this->appConfig->deleteAppValue($key); + } + + public function testDeleteAppValues(): void { + $this->appConfigCore->expects($this->once()) + ->method('deleteApp') + ->with(self::TEST_APPID); + + $this->appConfig->deleteAppValues(); + } +} diff --git a/tests/lib/AppFramework/Utility/ControllerMethodReflectorTest.php b/tests/lib/AppFramework/Utility/ControllerMethodReflectorTest.php index 5452fb853b9..2ba2f34425b 100644 --- a/tests/lib/AppFramework/Utility/ControllerMethodReflectorTest.php +++ b/tests/lib/AppFramework/Utility/ControllerMethodReflectorTest.php @@ -54,6 +54,14 @@ class MiddleController extends BaseController { public function test3() { } + + /** + * @psalm-param int<-4, 42> $rangedOne + * @psalm-param int<min, max> $rangedTwo + * @return void + */ + public function test4(int $rangedOne, int $rangedTwo) { + } } class EndController extends MiddleController { @@ -234,4 +242,17 @@ class ControllerMethodReflectorTest extends \Test\TestCase { $this->assertFalse($reader->hasAnnotation('Annotation')); } + + public function testRangeDetection() { + $reader = new ControllerMethodReflector(); + $reader->reflect('Test\AppFramework\Utility\EndController', 'test4'); + + $rangeInfo1 = $reader->getRange('rangedOne'); + $this->assertSame(-4, $rangeInfo1['min']); + $this->assertSame(42, $rangeInfo1['max']); + + $rangeInfo2 = $reader->getRange('rangedTwo'); + $this->assertSame(PHP_INT_MIN, $rangeInfo2['min']); + $this->assertSame(PHP_INT_MAX, $rangeInfo2['max']); + } } diff --git a/tests/lib/AppFramework/Utility/TimeFactoryTest.php b/tests/lib/AppFramework/Utility/TimeFactoryTest.php index 5811a2cf86a..91740ee6088 100644 --- a/tests/lib/AppFramework/Utility/TimeFactoryTest.php +++ b/tests/lib/AppFramework/Utility/TimeFactoryTest.php @@ -46,4 +46,21 @@ class TimeFactoryTest extends \Test\TestCase { $now = $withTimeZone->now(); self::assertSame('Europe/Berlin', $now->getTimezone()->getName()); } + + public function testGetTimeZone(): void { + $expected = new \DateTimeZone('Europe/Berlin'); + $actual = $this->timeFactory->getTimeZone('Europe/Berlin'); + self::assertEquals($expected, $actual); + } + + public function testGetTimeZoneUTC(): void { + $expected = new \DateTimeZone('UTC'); + $actual = $this->timeFactory->getTimeZone(); + self::assertEquals($expected, $actual); + } + + public function testGetTimeZoneInvalid(): void { + $this->expectException(\Exception::class); + $this->timeFactory->getTimeZone('blubblub'); + } } diff --git a/tests/lib/AppTest.php b/tests/lib/AppTest.php index 12fbdb011d9..4e723e5d2f1 100644 --- a/tests/lib/AppTest.php +++ b/tests/lib/AppTest.php @@ -14,6 +14,8 @@ use OC\App\InfoParser; use OC\AppConfig; use OCP\EventDispatcher\IEventDispatcher; use OCP\IAppConfig; +use OCP\IURLGenerator; +use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; /** @@ -537,6 +539,7 @@ class AppTest extends \Test\TestCase { private function setupAppConfigMock() { + /** @var AppConfig|MockObject */ $appConfig = $this->getMockBuilder(AppConfig::class) ->setMethods(['getValues']) ->setConstructorArgs([\OC::$server->getDatabaseConnection()]) @@ -561,7 +564,8 @@ class AppTest extends \Test\TestCase { \OC::$server->getGroupManager(), \OC::$server->getMemCacheFactory(), \OC::$server->get(IEventDispatcher::class), - \OC::$server->get(LoggerInterface::class) + \OC::$server->get(LoggerInterface::class), + \OC::$server->get(IURLGenerator::class), )); } diff --git a/tests/lib/Authentication/Token/PublicKeyTokenMapperTest.php b/tests/lib/Authentication/Token/PublicKeyTokenMapperTest.php index 68962b26931..08ae62f3a05 100644 --- a/tests/lib/Authentication/Token/PublicKeyTokenMapperTest.php +++ b/tests/lib/Authentication/Token/PublicKeyTokenMapperTest.php @@ -26,9 +26,9 @@ declare(strict_types=1); namespace Test\Authentication\Token; use OC; -use OC\Authentication\Token\IToken; use OC\Authentication\Token\PublicKeyToken; use OC\Authentication\Token\PublicKeyTokenMapper; +use OCP\Authentication\Token\IToken; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\IUser; @@ -249,7 +249,7 @@ class PublicKeyTokenMapperTest extends TestCase { $this->assertCount(0, $this->mapper->getTokenByUser('user1000')); } - public function testDeleteById() { + public function testGetById() { /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */ $user = $this->createMock(IUser::class); $qb = $this->dbConnection->getQueryBuilder(); @@ -259,17 +259,8 @@ class PublicKeyTokenMapperTest extends TestCase { $result = $qb->execute(); $id = $result->fetch()['id']; - $this->mapper->deleteById('user1', (int)$id); - $this->assertEquals(4, $this->getNumberOfTokens()); - } - - public function testDeleteByIdWrongUser() { - /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */ - $user = $this->createMock(IUser::class); - $id = 33; - - $this->mapper->deleteById('user1000', $id); - $this->assertEquals(5, $this->getNumberOfTokens()); + $token = $this->mapper->getTokenById((int)$id); + $this->assertEquals('user1', $token->getUID()); } public function testDeleteByName() { diff --git a/tests/lib/Authentication/Token/PublicKeyTokenProviderTest.php b/tests/lib/Authentication/Token/PublicKeyTokenProviderTest.php index b3f5241877e..69894f14855 100644 --- a/tests/lib/Authentication/Token/PublicKeyTokenProviderTest.php +++ b/tests/lib/Authentication/Token/PublicKeyTokenProviderTest.php @@ -29,12 +29,13 @@ namespace Test\Authentication\Token; use OC\Authentication\Exceptions\ExpiredTokenException; use OC\Authentication\Exceptions\InvalidTokenException; use OC\Authentication\Exceptions\PasswordlessTokenException; -use OC\Authentication\Token\IToken; use OC\Authentication\Token\PublicKeyToken; use OC\Authentication\Token\PublicKeyTokenMapper; use OC\Authentication\Token\PublicKeyTokenProvider; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Authentication\Token\IToken; +use OCP\ICacheFactory; use OCP\IConfig; use OCP\IDBConnection; use OCP\Security\ICrypto; @@ -60,6 +61,8 @@ class PublicKeyTokenProviderTest extends TestCase { private $logger; /** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject */ private $timeFactory; + /** @var ICacheFactory|\PHPUnit\Framework\MockObject\MockObject */ + private $cacheFactory; /** @var int */ private $time; @@ -90,6 +93,7 @@ class PublicKeyTokenProviderTest extends TestCase { $this->time = 1313131; $this->timeFactory->method('getTime') ->willReturn($this->time); + $this->cacheFactory = $this->createMock(ICacheFactory::class); $this->tokenProvider = new PublicKeyTokenProvider( $this->mapper, @@ -99,6 +103,7 @@ class PublicKeyTokenProviderTest extends TestCase { $this->logger, $this->timeFactory, $this->hasher, + $this->cacheFactory, ); } @@ -332,12 +337,12 @@ class PublicKeyTokenProviderTest extends TestCase { $this->tokenProvider->invalidateToken('token7'); } - public function testInvaildateTokenById() { + public function testInvalidateTokenById() { $id = 123; $this->mapper->expects($this->once()) - ->method('deleteById') - ->with('uid', $id); + ->method('getTokenById') + ->with($id); $this->tokenProvider->invalidateTokenById('uid', $id); } diff --git a/tests/lib/Authentication/TwoFactorAuth/ManagerTest.php b/tests/lib/Authentication/TwoFactorAuth/ManagerTest.php index 7647e3bda7d..a2655f58649 100644 --- a/tests/lib/Authentication/TwoFactorAuth/ManagerTest.php +++ b/tests/lib/Authentication/TwoFactorAuth/ManagerTest.php @@ -39,8 +39,8 @@ use OCP\ISession; use OCP\IUser; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; -use function reset; use Test\TestCase; +use function reset; class ManagerTest extends TestCase { /** @var IUser|MockObject */ @@ -629,13 +629,26 @@ class ManagerTest extends TestCase { return false; } elseif ($var === 'app_password') { return false; + } elseif ($var === 'app_api') { + return false; } return true; }); + $this->session->method('get') + ->willReturnCallback(function ($var) { + if ($var === Manager::SESSION_UID_KEY) { + return 'user'; + } elseif ($var === 'app_api') { + return true; + } + return null; + }); $this->session->expects($this->once()) ->method('get') - ->with(Manager::SESSION_UID_DONE) - ->willReturn('user'); + ->willReturnMap([ + [Manager::SESSION_UID_DONE, 'user'], + ['app_api', true] + ]); $this->assertFalse($this->manager->needsSecondFactor($user)); } @@ -695,8 +708,10 @@ class ManagerTest extends TestCase { public function testNeedsSecondFactorAppPassword() { $user = $this->createMock(IUser::class); $this->session->method('exists') - ->with('app_password') - ->willReturn(true); + ->willReturnMap([ + ['app_password', true], + ['app_api', true] + ]); $this->assertFalse($this->manager->needsSecondFactor($user)); } diff --git a/tests/lib/Avatar/AvatarManagerTest.php b/tests/lib/Avatar/AvatarManagerTest.php index 06ff4086f72..b35fb2f23a8 100644 --- a/tests/lib/Avatar/AvatarManagerTest.php +++ b/tests/lib/Avatar/AvatarManagerTest.php @@ -29,6 +29,7 @@ use OC\Avatar\PlaceholderAvatar; use OC\Avatar\UserAvatar; use OC\KnownUser\KnownUserService; use OC\User\Manager; +use OC\User\User; use OCP\Accounts\IAccount; use OCP\Accounts\IAccountManager; use OCP\Accounts\IAccountProperty; @@ -37,7 +38,6 @@ use OCP\Files\SimpleFS\ISimpleFolder; use OCP\IConfig; use OCP\IL10N; use OCP\IUser; -use OC\User\User; use OCP\IUserSession; use Psr\Log\LoggerInterface; diff --git a/tests/lib/BackgroundJob/DummyJobList.php b/tests/lib/BackgroundJob/DummyJobList.php index 7886e8f877c..05a9e5928c2 100644 --- a/tests/lib/BackgroundJob/DummyJobList.php +++ b/tests/lib/BackgroundJob/DummyJobList.php @@ -116,7 +116,7 @@ class DummyJobList extends \OC\BackgroundJob\JobList { /** * set the job that was last ran * - * @param \OC\BackgroundJob\Job $job + * @param \OCP\BackgroundJob\Job $job */ public function setLastJob(IJob $job): void { $i = array_search($job, $this->jobs); diff --git a/tests/lib/BackgroundJob/JobTest.php b/tests/lib/BackgroundJob/JobTest.php index a4e0dcf4fd6..c3a4a7d0552 100644 --- a/tests/lib/BackgroundJob/JobTest.php +++ b/tests/lib/BackgroundJob/JobTest.php @@ -9,16 +9,20 @@ namespace Test\BackgroundJob; use OCP\AppFramework\Utility\ITimeFactory; -use OCP\ILogger; +use Psr\Log\LoggerInterface; class JobTest extends \Test\TestCase { private $run = false; private ITimeFactory $timeFactory; + private LoggerInterface $logger; protected function setUp(): void { parent::setUp(); $this->run = false; - $this->timeFactory = \OC::$server->get(ITimeFactory::class); + $this->timeFactory = \OCP\Server::get(ITimeFactory::class); + $this->logger = $this->createMock(LoggerInterface::class); + + \OC::$server->registerService(LoggerInterface::class, fn ($c) => $this->logger); } public function testRemoveAfterException() { @@ -29,14 +33,11 @@ class JobTest extends \Test\TestCase { }); $jobList->add($job); - $logger = $this->getMockBuilder(ILogger::class) - ->disableOriginalConstructor() - ->getMock(); - $logger->expects($this->once()) + $this->logger->expects($this->once()) ->method('error'); $this->assertCount(1, $jobList->getAll()); - $job->execute($jobList, $logger); + $job->start($jobList); $this->assertTrue($this->run); $this->assertCount(1, $jobList->getAll()); } @@ -49,14 +50,11 @@ class JobTest extends \Test\TestCase { }); $jobList->add($job); - $logger = $this->getMockBuilder(ILogger::class) - ->disableOriginalConstructor() - ->getMock(); - $logger->expects($this->once()) + $this->logger->expects($this->once()) ->method('error'); $this->assertCount(1, $jobList->getAll()); - $job->execute($jobList, $logger); + $job->start($jobList); $this->assertTrue($this->run); $this->assertCount(1, $jobList->getAll()); } diff --git a/tests/lib/BackgroundJob/QueuedJobTest.php b/tests/lib/BackgroundJob/QueuedJobTest.php index 9378816ce61..aaf24957f09 100644 --- a/tests/lib/BackgroundJob/QueuedJobTest.php +++ b/tests/lib/BackgroundJob/QueuedJobTest.php @@ -9,20 +9,10 @@ namespace Test\BackgroundJob; use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\QueuedJob; -class TestQueuedJob extends \OC\BackgroundJob\QueuedJob { - public $ran = false; - - - public function run($argument) { - $this->ran = true; - } -} - - -class TestQueuedJobNew extends \OCP\BackgroundJob\QueuedJob { - public $ran = false; - +class TestQueuedJobNew extends QueuedJob { + public bool $ran = false; public function run($argument) { $this->ran = true; @@ -30,10 +20,7 @@ class TestQueuedJobNew extends \OCP\BackgroundJob\QueuedJob { } class QueuedJobTest extends \Test\TestCase { - /** - * @var DummyJobList $jobList - */ - private $jobList; + private DummyJobList $jobList; protected function setUp(): void { parent::setUp(); @@ -41,22 +28,13 @@ class QueuedJobTest extends \Test\TestCase { $this->jobList = new DummyJobList(); } - public function testJobShouldBeRemoved() { - $job = new TestQueuedJob(); - $this->jobList->add($job); - - $this->assertTrue($this->jobList->has($job, null)); - $job->execute($this->jobList); - $this->assertTrue($job->ran); - } - public function testJobShouldBeRemovedNew() { - $job = new TestQueuedJobNew(\OC::$server->query(ITimeFactory::class)); + $job = new TestQueuedJobNew(\OCP\Server::get(ITimeFactory::class)); $job->setId(42); $this->jobList->add($job); $this->assertTrue($this->jobList->has($job, null)); - $job->execute($this->jobList); + $job->start($this->jobList); $this->assertTrue($job->ran); } } diff --git a/tests/lib/BackgroundJob/TestJob.php b/tests/lib/BackgroundJob/TestJob.php index cc7a4651c4b..54b0ec7d9ea 100644 --- a/tests/lib/BackgroundJob/TestJob.php +++ b/tests/lib/BackgroundJob/TestJob.php @@ -23,7 +23,7 @@ class TestJob extends \OCP\BackgroundJob\Job { * @param callable $callback */ public function __construct(ITimeFactory $time = null, $testCase = null, $callback = null) { - parent::__construct($time ?? \OC::$server->get(ITimeFactory::class)); + parent::__construct($time ?? \OCP\Server::get(ITimeFactory::class)); $this->testCase = $testCase; $this->callback = $callback; } diff --git a/tests/lib/BackgroundJob/TimedJobTest.php b/tests/lib/BackgroundJob/TimedJobTest.php index 12f1c43adde..d0dd794a77c 100644 --- a/tests/lib/BackgroundJob/TimedJobTest.php +++ b/tests/lib/BackgroundJob/TimedJobTest.php @@ -9,23 +9,10 @@ namespace Test\BackgroundJob; use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\TimedJob; -class TestTimedJob extends \OC\BackgroundJob\TimedJob { - /** @var bool */ - public $ran = false; - - public function __construct() { - $this->setInterval(10); - } - - public function run($argument) { - $this->ran = true; - } -} - -class TestTimedJobNew extends \OCP\BackgroundJob\TimedJob { - /** @var bool */ - public $ran = false; +class TestTimedJobNew extends TimedJob { + public bool $ran = false; public function __construct(ITimeFactory $timeFactory) { parent::__construct($timeFactory); @@ -38,57 +25,23 @@ class TestTimedJobNew extends \OCP\BackgroundJob\TimedJob { } class TimedJobTest extends \Test\TestCase { - /** @var DummyJobList $jobList */ - private $jobList; - - /** @var ITimeFactory */ - private $time; + private DummyJobList $jobList; + private ITimeFactory $time; protected function setUp(): void { parent::setUp(); $this->jobList = new DummyJobList(); - $this->time = \OC::$server->query(ITimeFactory::class); - } - - public function testShouldRunAfterInterval() { - $job = new TestTimedJob(); - $this->jobList->add($job); - - $job->setLastRun(time() - 12); - $job->execute($this->jobList); - $this->assertTrue($job->ran); + $this->time = \OCP\Server::get(ITimeFactory::class); } - public function testShouldNotRunWithinInterval() { - $job = new TestTimedJob(); - $this->jobList->add($job); - - $job->setLastRun(time() - 5); - $job->execute($this->jobList); - $this->assertFalse($job->ran); - } - - public function testShouldNotTwice() { - $job = new TestTimedJob(); - $this->jobList->add($job); - - $job->setLastRun(time() - 15); - $job->execute($this->jobList); - $this->assertTrue($job->ran); - $job->ran = false; - $job->execute($this->jobList); - $this->assertFalse($job->ran); - } - - public function testShouldRunAfterIntervalNew() { $job = new TestTimedJobNew($this->time); $job->setId(42); $this->jobList->add($job); $job->setLastRun(time() - 12); - $job->execute($this->jobList); + $job->start($this->jobList); $this->assertTrue($job->ran); } @@ -98,7 +51,7 @@ class TimedJobTest extends \Test\TestCase { $this->jobList->add($job); $job->setLastRun(time() - 5); - $job->execute($this->jobList); + $job->start($this->jobList); $this->assertFalse($job->ran); } @@ -108,10 +61,10 @@ class TimedJobTest extends \Test\TestCase { $this->jobList->add($job); $job->setLastRun(time() - 15); - $job->execute($this->jobList); + $job->start($this->jobList); $this->assertTrue($job->ran); $job->ran = false; - $job->execute($this->jobList); + $job->start($this->jobList); $this->assertFalse($job->ran); } } diff --git a/tests/lib/Collaboration/Collaborators/MailPluginTest.php b/tests/lib/Collaboration/Collaborators/MailPluginTest.php index 3cf76c562a1..804e5385a9d 100644 --- a/tests/lib/Collaboration/Collaborators/MailPluginTest.php +++ b/tests/lib/Collaboration/Collaborators/MailPluginTest.php @@ -38,8 +38,8 @@ use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserManager; use OCP\IUserSession; -use OCP\Share\IShare; use OCP\Mail\IMailer; +use OCP\Share\IShare; use Test\TestCase; class MailPluginTest extends TestCase { diff --git a/tests/lib/Collaboration/Collaborators/UserPluginTest.php b/tests/lib/Collaboration/Collaborators/UserPluginTest.php index 0d9d89c7f8b..6a8cf17111c 100644 --- a/tests/lib/Collaboration/Collaborators/UserPluginTest.php +++ b/tests/lib/Collaboration/Collaborators/UserPluginTest.php @@ -23,6 +23,9 @@ namespace Test\Collaboration\Collaborators; +use OC\Collaboration\Collaborators\SearchResult; +use OC\Collaboration\Collaborators\UserPlugin; +use OC\KnownUser\KnownUserService; use OCP\Collaboration\Collaborators\ISearchResult; use OCP\IConfig; use OCP\IGroup; @@ -32,9 +35,6 @@ use OCP\IUserManager; use OCP\IUserSession; use OCP\Share\IShare; use OCP\UserStatus\IManager as IUserStatusManager; -use OC\Collaboration\Collaborators\SearchResult; -use OC\Collaboration\Collaborators\UserPlugin; -use OC\KnownUser\KnownUserService; use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; diff --git a/tests/lib/Command/CronBusTest.php b/tests/lib/Command/CronBusTest.php index ea610a135d8..4b3c7dca95a 100644 --- a/tests/lib/Command/CronBusTest.php +++ b/tests/lib/Command/CronBusTest.php @@ -44,7 +44,7 @@ class CronBusTest extends AsyncBusTest { protected function runJobs() { $jobs = $this->jobList->getAll(); foreach ($jobs as $job) { - $job->execute($this->jobList); + $job->start($this->jobList); } } } diff --git a/tests/lib/Comments/CommentTest.php b/tests/lib/Comments/CommentTest.php index 328e6fd5447..ad4c5ef8832 100644 --- a/tests/lib/Comments/CommentTest.php +++ b/tests/lib/Comments/CommentTest.php @@ -23,6 +23,8 @@ class CommentTest extends TestCase { $creationDT = new \DateTime(); $latestChildDT = new \DateTime('yesterday'); $object = ['type' => 'files', 'id' => 'file64']; + $referenceId = sha1('referenceId'); + $metaData = ['last_edit_actor_id' => 'admin']; $comment ->setId($id) @@ -34,7 +36,9 @@ class CommentTest extends TestCase { ->setActor($actor['type'], $actor['id']) ->setCreationDateTime($creationDT) ->setLatestChildDateTime($latestChildDT) - ->setObject($object['type'], $object['id']); + ->setObject($object['type'], $object['id']) + ->setReferenceId($referenceId) + ->setMetaData($metaData); $this->assertSame($id, $comment->getId()); $this->assertSame($parentId, $comment->getParentId()); @@ -48,6 +52,8 @@ class CommentTest extends TestCase { $this->assertSame($latestChildDT, $comment->getLatestChildDateTime()); $this->assertSame($object['type'], $comment->getObjectType()); $this->assertSame($object['id'], $comment->getObjectId()); + $this->assertSame($referenceId, $comment->getReferenceId()); + $this->assertSame($metaData, $comment->getMetaData()); } @@ -128,19 +134,24 @@ class CommentTest extends TestCase { $comment->setMessage($msg); } - public function mentionsProvider() { + public function mentionsProvider(): array { return [ [ - '@alice @bob look look, a cook!', ['alice', 'bob'] + '@alice @bob look look, a cook!', + [['type' => 'user', 'id' => 'alice'], ['type' => 'user', 'id' => 'bob']], ], [ - 'no mentions in this message', [] + 'no mentions in this message', + [] ], [ - '@alice @bob look look, a duplication @alice test @bob!', ['alice', 'bob'] + '@alice @bob look look, a duplication @alice test @bob!', + [['type' => 'user', 'id' => 'alice'], ['type' => 'user', 'id' => 'bob']], ], [ - '@alice is the author, notify @bob, nevertheless mention her!', ['alice', 'bob'], 'alice' + '@alice is the author, notify @bob, nevertheless mention her!', + [['type' => 'user', 'id' => 'alice'], ['type' => 'user', 'id' => 'bob']], + /* author: */ 'alice' ], [ '@foobar and @barfoo you should know, @foo@bar.com is valid' . @@ -148,19 +159,38 @@ class CommentTest extends TestCase { ' cc @23452-4333-54353-2342 @yolo!' . ' however the most important thing to know is that www.croissant.com/@oil is not valid' . ' and won\'t match anything at all', - ['bar@foo.org@foobar.io', '23452-4333-54353-2342', 'foo@bar.com', 'foobar', 'barfoo', 'yolo'] + [ + ['type' => 'user', 'id' => 'bar@foo.org@foobar.io'], + ['type' => 'user', 'id' => '23452-4333-54353-2342'], + ['type' => 'user', 'id' => 'foo@bar.com'], + ['type' => 'user', 'id' => 'foobar'], + ['type' => 'user', 'id' => 'barfoo'], + ['type' => 'user', 'id' => 'yolo'], + ], ], [ - '@@chef is also a valid mention, no matter how strange it looks', ['@chef'] + '@@chef is also a valid mention, no matter how strange it looks', + [['type' => 'user', 'id' => '@chef']], ], [ - 'Also @"user with spaces" are now supported', ['user with spaces'] + 'Also @"user with spaces" are now supported', + [['type' => 'user', 'id' => 'user with spaces']], ], [ - 'Also @"guest/0123456789abcdef" are now supported', [], null, ['guest/0123456789abcdef'] + 'Also @"guest/0123456789abcdef" are now supported', + [['type' => 'guest', 'id' => 'guest/0123456789abcdef']], ], [ - 'Also @"group/My Group ID 321" are now supported', [], null, [], ['My Group ID 321'] + 'Also @"group/My Group ID 321" are now supported', + [['type' => 'group', 'id' => 'My Group ID 321']], + ], + [ + 'Welcome federation @"federated_group/My Group ID 321" @"federated_team/Former Cirle" @"federated_user/cloudId@http://example.tld:8080/nextcloud"! Now freshly supported', + [ + ['type' => 'federated_user', 'id' => 'cloudId@http://example.tld:8080/nextcloud'], + ['type' => 'federated_group', 'id' => 'My Group ID 321'], + ['type' => 'federated_team', 'id' => 'Former Cirle'], + ], ], ]; } @@ -169,31 +199,16 @@ class CommentTest extends TestCase { * @dataProvider mentionsProvider * * @param string $message - * @param array $expectedUids - * @param string|null $author - * @param array $expectedGuests + * @param array $expectedMentions + * @param ?string $author */ - public function testMentions(string $message, array $expectedUids, ?string $author = null, array $expectedGuests = [], array $expectedGroups = []): void { + public function testMentions(string $message, array $expectedMentions, ?string $author = null): void { $comment = new Comment(); $comment->setMessage($message); if (!is_null($author)) { $comment->setActor('user', $author); } $mentions = $comment->getMentions(); - while ($mention = array_shift($mentions)) { - if ($mention['type'] === 'user') { - $id = array_shift($expectedUids); - } elseif ($mention['type'] === 'guest') { - $id = array_shift($expectedGuests); - } elseif ($mention['type'] === 'group') { - $id = array_shift($expectedGroups); - } else { - $this->fail('Unexpected mention type'); - continue; - } - $this->assertSame($id, $mention['id']); - } - $this->assertEmpty($mentions); - $this->assertEmpty($expectedUids); + $this->assertSame($expectedMentions, $mentions); } } diff --git a/tests/lib/Comments/ManagerTest.php b/tests/lib/Comments/ManagerTest.php index 5fa1beee374..9dcc24d12a1 100644 --- a/tests/lib/Comments/ManagerTest.php +++ b/tests/lib/Comments/ManagerTest.php @@ -10,6 +10,8 @@ use OCP\Comments\IComment; use OCP\Comments\ICommentsEventHandler; use OCP\Comments\ICommentsManager; use OCP\Comments\NotFoundException; +use OCP\Files\Folder; +use OCP\Files\IRootFolder; use OCP\IConfig; use OCP\IDBConnection; use OCP\IInitialStateService; @@ -26,11 +28,14 @@ use Test\TestCase; class ManagerTest extends TestCase { /** @var IDBConnection */ private $connection; + /** @var \PHPUnit\Framework\MockObject\MockObject|IRootFolder */ + private $rootFolder; protected function setUp(): void { parent::setUp(); $this->connection = \OC::$server->getDatabaseConnection(); + $this->rootFolder = $this->createMock(IRootFolder::class); $sql = $this->connection->getDatabasePlatform()->getTruncateTableSQL('`*PREFIX*comments`'); $this->connection->prepare($sql)->execute(); @@ -65,6 +70,8 @@ class ManagerTest extends TestCase { 'object_type' => $qb->createNamedParameter('files'), 'object_id' => $qb->createNamedParameter($objectId), 'expire_date' => $qb->createNamedParameter($expireDate, 'datetime'), + 'reference_id' => $qb->createNamedParameter('referenceId'), + 'meta_data' => $qb->createNamedParameter(json_encode(['last_edit_actor_id' => 'admin'])), ]) ->execute(); @@ -78,7 +85,8 @@ class ManagerTest extends TestCase { $this->createMock(IConfig::class), $this->createMock(ITimeFactory::class), new EmojiHelper($this->connection), - $this->createMock(IInitialStateService::class) + $this->createMock(IInitialStateService::class), + $this->rootFolder, ); } @@ -119,6 +127,8 @@ class ManagerTest extends TestCase { 'latest_child_timestamp' => $qb->createNamedParameter($latestChildDT, 'datetime'), 'object_type' => $qb->createNamedParameter('files'), 'object_id' => $qb->createNamedParameter('file64'), + 'reference_id' => $qb->createNamedParameter('referenceId'), + 'meta_data' => $qb->createNamedParameter(json_encode(['last_edit_actor_id' => 'admin'])), ]) ->execute(); @@ -138,6 +148,8 @@ class ManagerTest extends TestCase { $this->assertSame($comment->getObjectId(), 'file64'); $this->assertEquals($comment->getCreationDateTime()->getTimestamp(), $creationDT->getTimestamp()); $this->assertEquals($comment->getLatestChildDateTime(), $latestChildDT); + $this->assertEquals($comment->getReferenceId(), 'referenceId'); + $this->assertEquals($comment->getMetaData(), ['last_edit_actor_id' => 'admin']); } @@ -323,23 +335,19 @@ class ManagerTest extends TestCase { } public function testGetNumberOfUnreadCommentsForFolder() { - $query = $this->connection->getQueryBuilder(); - $query->insert('filecache') - ->values([ - 'parent' => $query->createNamedParameter(1000), - 'size' => $query->createNamedParameter(10), - 'mtime' => $query->createNamedParameter(10), - 'storage_mtime' => $query->createNamedParameter(10), - 'path' => $query->createParameter('path'), - 'path_hash' => $query->createParameter('path'), - ]); - - $fileIds = []; - for ($i = 0; $i < 4; $i++) { - $query->setParameter('path', 'path_' . $i); - $query->execute(); - $fileIds[] = $query->getLastInsertId(); - } + $folder = $this->createMock(Folder::class); + $fileIds = range(1111, 1114); + $children = array_map(function (int $id) { + $file = $this->createMock(Folder::class); + $file->method('getId') + ->willReturn($id); + return $file; + }, $fileIds); + $folder->method('getId')->willReturn(1000); + $folder->method('getDirectoryListing')->willReturn($children); + $this->rootFolder->method('getFirstNodeById') + ->with($folder->getId()) + ->willReturn($folder); // 2 comment for 1111 with 1 before read marker // 2 comments for 1112 with no read marker @@ -361,7 +369,7 @@ class ManagerTest extends TestCase { $manager->setReadMark('files', (string) $fileIds[0], (new \DateTime())->modify('-1 days'), $user); $manager->setReadMark('files', (string) $fileIds[2], (new \DateTime()), $user); - $amount = $manager->getNumberOfUnreadCommentsForFolder(1000, $user); + $amount = $manager->getNumberOfUnreadCommentsForFolder($folder->getId(), $user); $this->assertEquals([ $fileIds[0] => 1, $fileIds[1] => 2, @@ -744,7 +752,8 @@ class ManagerTest extends TestCase { $this->createMock(IConfig::class), Server::get(ITimeFactory::class), new EmojiHelper($this->connection), - $this->createMock(IInitialStateService::class) + $this->createMock(IInitialStateService::class), + $this->rootFolder, ); // just to make sure they are really set, with correct actor data @@ -789,7 +798,8 @@ class ManagerTest extends TestCase { $this->createMock(IConfig::class), Server::get(ITimeFactory::class), new EmojiHelper($this->connection), - $this->createMock(IInitialStateService::class) + $this->createMock(IInitialStateService::class), + $this->rootFolder, ); $deleted = $manager->deleteCommentsExpiredAtObject('files'); diff --git a/tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php b/tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php index e3d6ea99226..69805bf81f2 100644 --- a/tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php +++ b/tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php @@ -217,6 +217,7 @@ class ContactsStoreTest extends TestCase { ['core', 'shareapi_exclude_groups', 'no', 'yes'], ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'], ['core', 'shareapi_exclude_groups_list', '', '["group1", "group5", "group6"]'], + ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'], ]); /** @var IUser|MockObject $currentUser */ @@ -260,6 +261,7 @@ class ContactsStoreTest extends TestCase { ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'], ['core', 'shareapi_exclude_groups', 'no', 'no'], ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'], + ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'], ]); /** @var IUser|MockObject $currentUser */ @@ -334,6 +336,7 @@ class ContactsStoreTest extends TestCase { ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'], ['core', 'shareapi_exclude_groups', 'no', 'no'], ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'], + ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'], ]); /** @var IUser|MockObject $currentUser */ @@ -466,6 +469,7 @@ class ContactsStoreTest extends TestCase { ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'yes'], ['core', 'shareapi_exclude_groups', 'no', 'no'], ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'], + ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'], ]); /** @var IUser|MockObject $currentUser */ @@ -619,6 +623,7 @@ class ContactsStoreTest extends TestCase { ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'yes'], ['core', 'shareapi_exclude_groups', 'no', 'no'], ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'], + ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'], ]); /** @var IUser|MockObject $currentUser */ diff --git a/tests/lib/DB/QueryBuilder/ExpressionBuilderDBTest.php b/tests/lib/DB/QueryBuilder/ExpressionBuilderDBTest.php index 35d8b4faa34..33b5824a0dd 100644 --- a/tests/lib/DB/QueryBuilder/ExpressionBuilderDBTest.php +++ b/tests/lib/DB/QueryBuilder/ExpressionBuilderDBTest.php @@ -21,8 +21,12 @@ namespace Test\DB\QueryBuilder; +use Doctrine\DBAL\Schema\SchemaException; +use Doctrine\DBAL\Types\Types; use OC\DB\QueryBuilder\Literal; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IConfig; +use OCP\Server; use Test\TestCase; /** @@ -31,11 +35,13 @@ use Test\TestCase; class ExpressionBuilderDBTest extends TestCase { /** @var \Doctrine\DBAL\Connection|\OCP\IDBConnection */ protected $connection; + protected $schemaSetup = false; protected function setUp(): void { parent::setUp(); $this->connection = \OC::$server->getDatabaseConnection(); + $this->prepareTestingTable(); } public function likeProvider() { @@ -150,6 +156,59 @@ class ExpressionBuilderDBTest extends TestCase { self::assertEquals('myvalue', $entries[0]['configvalue']); } + public function testDateTimeEquals() { + $dateTime = new \DateTime('2023-01-01'); + $insert = $this->connection->getQueryBuilder(); + $insert->insert('testing') + ->values(['datetime' => $insert->createNamedParameter($dateTime, IQueryBuilder::PARAM_DATE)]) + ->executeStatement(); + + $query = $this->connection->getQueryBuilder(); + $result = $query->select('*') + ->from('testing') + ->where($query->expr()->eq('datetime', $query->createNamedParameter($dateTime, IQueryBuilder::PARAM_DATE))) + ->executeQuery(); + $entries = $result->fetchAll(); + $result->closeCursor(); + self::assertCount(1, $entries); + } + + public function testDateTimeLess() { + $dateTime = new \DateTime('2022-01-01'); + $dateTimeCompare = new \DateTime('2022-01-02'); + $insert = $this->connection->getQueryBuilder(); + $insert->insert('testing') + ->values(['datetime' => $insert->createNamedParameter($dateTime, IQueryBuilder::PARAM_DATE)]) + ->executeStatement(); + + $query = $this->connection->getQueryBuilder(); + $result = $query->select('*') + ->from('testing') + ->where($query->expr()->lt('datetime', $query->createNamedParameter($dateTimeCompare, IQueryBuilder::PARAM_DATE))) + ->executeQuery(); + $entries = $result->fetchAll(); + $result->closeCursor(); + self::assertCount(1, $entries); + } + + public function testDateTimeGreater() { + $dateTime = new \DateTime('2023-01-02'); + $dateTimeCompare = new \DateTime('2023-01-01'); + $insert = $this->connection->getQueryBuilder(); + $insert->insert('testing') + ->values(['datetime' => $insert->createNamedParameter($dateTime, IQueryBuilder::PARAM_DATE)]) + ->executeStatement(); + + $query = $this->connection->getQueryBuilder(); + $result = $query->select('*') + ->from('testing') + ->where($query->expr()->gt('datetime', $query->createNamedParameter($dateTimeCompare, IQueryBuilder::PARAM_DATE))) + ->executeQuery(); + $entries = $result->fetchAll(); + $result->closeCursor(); + self::assertCount(1, $entries); + } + protected function createConfig($appId, $key, $value) { $query = $this->connection->getQueryBuilder(); $query->insert('appconfig') @@ -160,4 +219,31 @@ class ExpressionBuilderDBTest extends TestCase { ]) ->execute(); } + + protected function prepareTestingTable(): void { + if ($this->schemaSetup) { + $this->connection->getQueryBuilder()->delete('testing')->executeStatement(); + } + + $prefix = Server::get(IConfig::class)->getSystemValueString('dbtableprefix', 'oc_'); + $schema = $this->connection->createSchema(); + try { + $schema->getTable($prefix . 'testing'); + $this->connection->getQueryBuilder()->delete('testing')->executeStatement(); + } catch (SchemaException $e) { + $this->schemaSetup = true; + $table = $schema->createTable($prefix . 'testing'); + $table->addColumn('id', Types::BIGINT, [ + 'autoincrement' => true, + 'notnull' => true, + ]); + + $table->addColumn('datetime', Types::DATETIME_MUTABLE, [ + 'notnull' => false, + ]); + + $table->setPrimaryKey(['id']); + $this->connection->migrateToSchema($schema); + } + } } diff --git a/tests/lib/DB/QueryBuilder/FunctionBuilderTest.php b/tests/lib/DB/QueryBuilder/FunctionBuilderTest.php index 08392b09d8d..430c7d24950 100644 --- a/tests/lib/DB/QueryBuilder/FunctionBuilderTest.php +++ b/tests/lib/DB/QueryBuilder/FunctionBuilderTest.php @@ -50,6 +50,7 @@ class FunctionBuilderTest extends TestCase { if ($real) { $this->addDummyData(); $query->where($query->expr()->eq('appid', $query->createNamedParameter('group_concat'))); + $query->orderBy('configkey', 'asc'); } $query->select($query->func()->concat(...$arguments)); diff --git a/tests/lib/DB/QueryBuilder/QueryBuilderTest.php b/tests/lib/DB/QueryBuilder/QueryBuilderTest.php index 6e9a7a84925..f92b7665532 100644 --- a/tests/lib/DB/QueryBuilder/QueryBuilderTest.php +++ b/tests/lib/DB/QueryBuilder/QueryBuilderTest.php @@ -326,7 +326,7 @@ class QueryBuilderTest extends \Test\TestCase { 'appid', $this->queryBuilder->expr()->literal('testFirstResult1') )) - ->orderBy('appid', 'DESC'); + ->orderBy('configkey', 'ASC'); $query = $this->queryBuilder->execute(); $rows = $query->fetchAll(); diff --git a/tests/lib/DateTimeFormatterTest.php b/tests/lib/DateTimeFormatterTest.php index 71d98ba7581..3409c0d9207 100644 --- a/tests/lib/DateTimeFormatterTest.php +++ b/tests/lib/DateTimeFormatterTest.php @@ -34,7 +34,7 @@ class DateTimeFormatterTest extends TestCase { protected function setUp(): void { parent::setUp(); - $this->formatter = new \OC\DateTimeFormatter(new \DateTimeZone('UTC'), \OC::$server->getL10N('lib', 'en')); + $this->formatter = new \OC\DateTimeFormatter(new \DateTimeZone('UTC'), \OCP\Util::getL10N('lib', 'en')); } protected function getTimestampAgo($time, $seconds = 0, $minutes = 0, $hours = 0, $days = 0, $years = 0) { @@ -43,7 +43,7 @@ class DateTimeFormatterTest extends TestCase { public function formatTimeSpanData() { $time = 1416916800; // Use a fixed timestamp so we don't switch days/years with the getTimestampAgo - $deL10N = \OC::$server->getL10N('lib', 'de'); + $deL10N = \OCP\Util::getL10N('lib', 'de'); return [ ['seconds ago', $time, $time], ['in a few seconds', $time + 5 , $time], @@ -84,7 +84,7 @@ class DateTimeFormatterTest extends TestCase { public function formatDateSpanData() { $time = 1416916800; // Use a fixed timestamp so we don't switch days/years with the getTimestampAgo - $deL10N = \OC::$server->getL10N('lib', 'de'); + $deL10N = \OCP\Util::getL10N('lib', 'de'); return [ // Normal testing ['today', $this->getTimestampAgo($time, 30, 15), $time], diff --git a/tests/lib/Diagnostics/EventLoggerTest.php b/tests/lib/Diagnostics/EventLoggerTest.php index 18cd3a91b1a..006d0b8b3ea 100644 --- a/tests/lib/Diagnostics/EventLoggerTest.php +++ b/tests/lib/Diagnostics/EventLoggerTest.php @@ -21,10 +21,10 @@ namespace Test\Diagnostics; -use Psr\Log\LoggerInterface; use OC\Diagnostics\EventLogger; use OC\Log; use OC\SystemConfig; +use Psr\Log\LoggerInterface; use Test\TestCase; class EventLoggerTest extends TestCase { diff --git a/tests/lib/Encryption/Keys/StorageTest.php b/tests/lib/Encryption/Keys/StorageTest.php index a47edb3fdd6..eaef7fbaa54 100644 --- a/tests/lib/Encryption/Keys/StorageTest.php +++ b/tests/lib/Encryption/Keys/StorageTest.php @@ -53,6 +53,7 @@ class StorageTest extends TestCase { $this->util = $this->getMockBuilder('OC\Encryption\Util') ->disableOriginalConstructor() + ->setMethodsExcept(['getFileKeyDir']) ->getMock(); $this->view = $this->getMockBuilder(View::class) @@ -583,39 +584,6 @@ class StorageTest extends TestCase { $this->assertSame($expected, $args[0]); } - /** - * @dataProvider dataTestGetFileKeyDir - * - * @param bool $isSystemWideMountPoint - * @param string $storageRoot - * @param string $expected - */ - public function testGetFileKeyDir($isSystemWideMountPoint, $storageRoot, $expected) { - $path = '/user1/files/foo/bar.txt'; - $owner = 'user1'; - $relativePath = '/foo/bar.txt'; - - $this->invokePrivate($this->storage, 'root_dir', [$storageRoot]); - - $this->util->expects($this->once())->method('isSystemWideMountPoint') - ->willReturn($isSystemWideMountPoint); - $this->util->expects($this->once())->method('getUidAndFilename') - ->with($path)->willReturn([$owner, $relativePath]); - - $this->assertSame($expected, - $this->invokePrivate($this->storage, 'getFileKeyDir', ['OC_DEFAULT_MODULE', $path]) - ); - } - - public function dataTestGetFileKeyDir() { - return [ - [false, '', '/user1/files_encryption/keys/foo/bar.txt/OC_DEFAULT_MODULE/'], - [true, '', '/files_encryption/keys/foo/bar.txt/OC_DEFAULT_MODULE/'], - [false, 'newStorageRoot', '/newStorageRoot/user1/files_encryption/keys/foo/bar.txt/OC_DEFAULT_MODULE/'], - [true, 'newStorageRoot', '/newStorageRoot/files_encryption/keys/foo/bar.txt/OC_DEFAULT_MODULE/'], - ]; - } - /** * @dataProvider dataTestBackupUserKeys diff --git a/tests/lib/Encryption/ManagerTest.php b/tests/lib/Encryption/ManagerTest.php index 4e354ad518c..5d8cf02f94d 100644 --- a/tests/lib/Encryption/ManagerTest.php +++ b/tests/lib/Encryption/ManagerTest.php @@ -193,63 +193,63 @@ class ManagerTest extends TestCase { $this->assertEquals('ID1', $this->manager->getDefaultEncryptionModuleId()); } -// /** -// * @expectedException \OC\Encryption\Exceptions\ModuleAlreadyExistsException -// * @expectedExceptionMessage Id "0" already used by encryption module "TestDummyModule0" -// */ -// public function testModuleRegistration() { -// $config = $this->createMock(IConfig::class); -// $config->expects($this->any())->method('getSystemValueBool')->willReturn(true); -// $em = $this->createMock(IEncryptionModule::class); -// $em->expects($this->any())->method('getId')->willReturn(0); -// $em->expects($this->any())->method('getDisplayName')->willReturn('TestDummyModule0'); -// $m = new Manager($config); -// $m->registerEncryptionModule($em); -// $this->assertTrue($m->isEnabled()); -// $m->registerEncryptionModule($em); -// } -// -// public function testModuleUnRegistration() { -// $config = $this->createMock(IConfig::class); -// $config->expects($this->any())->method('getSystemValueBool')->willReturn(true); -// $em = $this->createMock(IEncryptionModule::class); -// $em->expects($this->any())->method('getId')->willReturn(0); -// $em->expects($this->any())->method('getDisplayName')->willReturn('TestDummyModule0'); -// $m = new Manager($config); -// $m->registerEncryptionModule($em); -// $this->assertTrue($m->isEnabled()); -// $m->unregisterEncryptionModule($em); -// $this->assertFalse($m->isEnabled()); -// } -// -// /** -// * @expectedException \OC\Encryption\Exceptions\ModuleDoesNotExistsException -// * @expectedExceptionMessage Module with ID: unknown does not exist. -// */ -// public function testGetEncryptionModuleUnknown() { -// $config = $this->createMock(IConfig::class); -// $config->expects($this->any())->method('getSystemValueBool')->willReturn(true); -// $em = $this->createMock(IEncryptionModule::class); -// $em->expects($this->any())->method('getId')->willReturn(0); -// $em->expects($this->any())->method('getDisplayName')->willReturn('TestDummyModule0'); -// $m = new Manager($config); -// $m->registerEncryptionModule($em); -// $this->assertTrue($m->isEnabled()); -// $m->getEncryptionModule('unknown'); -// } -// -// public function testGetEncryptionModule() { -// $config = $this->createMock(IConfig::class); -// $config->expects($this->any())->method('getSystemValueBool')->willReturn(true); -// $em = $this->createMock(IEncryptionModule::class); -// $em->expects($this->any())->method('getId')->willReturn(0); -// $em->expects($this->any())->method('getDisplayName')->willReturn('TestDummyModule0'); -// $m = new Manager($config); -// $m->registerEncryptionModule($em); -// $this->assertTrue($m->isEnabled()); -// $en0 = $m->getEncryptionModule(0); -// $this->assertEquals(0, $en0->getId()); -// } + // /** + // * @expectedException \OC\Encryption\Exceptions\ModuleAlreadyExistsException + // * @expectedExceptionMessage Id "0" already used by encryption module "TestDummyModule0" + // */ + // public function testModuleRegistration() { + // $config = $this->createMock(IConfig::class); + // $config->expects($this->any())->method('getSystemValueBool')->willReturn(true); + // $em = $this->createMock(IEncryptionModule::class); + // $em->expects($this->any())->method('getId')->willReturn(0); + // $em->expects($this->any())->method('getDisplayName')->willReturn('TestDummyModule0'); + // $m = new Manager($config); + // $m->registerEncryptionModule($em); + // $this->assertTrue($m->isEnabled()); + // $m->registerEncryptionModule($em); + // } + // + // public function testModuleUnRegistration() { + // $config = $this->createMock(IConfig::class); + // $config->expects($this->any())->method('getSystemValueBool')->willReturn(true); + // $em = $this->createMock(IEncryptionModule::class); + // $em->expects($this->any())->method('getId')->willReturn(0); + // $em->expects($this->any())->method('getDisplayName')->willReturn('TestDummyModule0'); + // $m = new Manager($config); + // $m->registerEncryptionModule($em); + // $this->assertTrue($m->isEnabled()); + // $m->unregisterEncryptionModule($em); + // $this->assertFalse($m->isEnabled()); + // } + // + // /** + // * @expectedException \OC\Encryption\Exceptions\ModuleDoesNotExistsException + // * @expectedExceptionMessage Module with ID: unknown does not exist. + // */ + // public function testGetEncryptionModuleUnknown() { + // $config = $this->createMock(IConfig::class); + // $config->expects($this->any())->method('getSystemValueBool')->willReturn(true); + // $em = $this->createMock(IEncryptionModule::class); + // $em->expects($this->any())->method('getId')->willReturn(0); + // $em->expects($this->any())->method('getDisplayName')->willReturn('TestDummyModule0'); + // $m = new Manager($config); + // $m->registerEncryptionModule($em); + // $this->assertTrue($m->isEnabled()); + // $m->getEncryptionModule('unknown'); + // } + // + // public function testGetEncryptionModule() { + // $config = $this->createMock(IConfig::class); + // $config->expects($this->any())->method('getSystemValueBool')->willReturn(true); + // $em = $this->createMock(IEncryptionModule::class); + // $em->expects($this->any())->method('getId')->willReturn(0); + // $em->expects($this->any())->method('getDisplayName')->willReturn('TestDummyModule0'); + // $m = new Manager($config); + // $m->registerEncryptionModule($em); + // $this->assertTrue($m->isEnabled()); + // $en0 = $m->getEncryptionModule(0); + // $this->assertEquals(0, $en0->getId()); + // } protected function addNewEncryptionModule(Manager $manager, $id) { $encryptionModule = $this->createMock(IEncryptionModule::class); diff --git a/tests/lib/Encryption/UpdateTest.php b/tests/lib/Encryption/UpdateTest.php index df7d137f0b6..209acbc7d47 100644 --- a/tests/lib/Encryption/UpdateTest.php +++ b/tests/lib/Encryption/UpdateTest.php @@ -21,14 +21,14 @@ namespace Test\Encryption; +use OC\Encryption\File; use OC\Encryption\Update; use OC\Encryption\Util; use OC\Files\Mount\Manager; use OC\Files\View; +use OCP\Encryption\IEncryptionModule; use Psr\Log\LoggerInterface; use Test\TestCase; -use OC\Encryption\File; -use OCP\Encryption\IEncryptionModule; class UpdateTest extends TestCase { /** @var \OC\Encryption\Update */ diff --git a/tests/lib/Encryption/UtilTest.php b/tests/lib/Encryption/UtilTest.php index e236ef54c26..7f5b05d6967 100644 --- a/tests/lib/Encryption/UtilTest.php +++ b/tests/lib/Encryption/UtilTest.php @@ -13,6 +13,7 @@ use Test\TestCase; class UtilTest extends TestCase { /** * block size will always be 8192 for a PHP stream + * * @see https://bugs.php.net/bug.php?id=21641 */ protected int $headerSize = 8192; @@ -205,4 +206,47 @@ class UtilTest extends TestCase { , []], ]; } + + /** + * @dataProvider dataTestGetFileKeyDir + * + * @param bool $isSystemWideMountPoint + * @param string $storageRoot + * @param string $expected + */ + public function testGetFileKeyDir($isSystemWideMountPoint, $storageRoot, $expected) { + $path = '/user1/files/foo/bar.txt'; + $owner = 'user1'; + $relativePath = '/foo/bar.txt'; + + $util = $this->getMockBuilder(Util::class) + ->onlyMethods(['isSystemWideMountPoint', 'getUidAndFilename', 'getKeyStorageRoot']) + ->setConstructorArgs([ + $this->view, + $this->userManager, + $this->groupManager, + $this->config + ]) + ->getMock(); + + $util->expects($this->once())->method('getKeyStorageRoot') + ->willReturn($storageRoot); + $util->expects($this->once())->method('isSystemWideMountPoint') + ->willReturn($isSystemWideMountPoint); + $util->expects($this->once())->method('getUidAndFilename') + ->with($path)->willReturn([$owner, $relativePath]); + + $this->assertSame($expected, + $util->getFileKeyDir('OC_DEFAULT_MODULE', $path) + ); + } + + public function dataTestGetFileKeyDir() { + return [ + [false, '', '/user1/files_encryption/keys/foo/bar.txt/OC_DEFAULT_MODULE/'], + [true, '', '/files_encryption/keys/foo/bar.txt/OC_DEFAULT_MODULE/'], + [false, 'newStorageRoot', '/newStorageRoot/user1/files_encryption/keys/foo/bar.txt/OC_DEFAULT_MODULE/'], + [true, 'newStorageRoot', '/newStorageRoot/files_encryption/keys/foo/bar.txt/OC_DEFAULT_MODULE/'], + ]; + } } diff --git a/tests/lib/Files/Cache/ScannerTest.php b/tests/lib/Files/Cache/ScannerTest.php index e4c052f6025..22d458a4a9b 100644 --- a/tests/lib/Files/Cache/ScannerTest.php +++ b/tests/lib/Files/Cache/ScannerTest.php @@ -404,4 +404,48 @@ class ScannerTest extends TestCase { ['/sub/folder/foo.txt', false], ]; } + + public function testNoETagUnscannedFolder() { + $this->fillTestFolders(); + + $this->scanner->scan(''); + + $oldFolderEntry = $this->cache->get('folder'); + // create a new file in a folder by keeping the mtime unchanged, but mark the folder as unscanned + $this->storage->file_put_contents('folder/new.txt', 'foo'); + $this->storage->touch('folder', $oldFolderEntry->getMTime()); + $this->cache->update($oldFolderEntry->getId(), ['size' => -1]); + + $this->scanner->scan(''); + + $this->cache->inCache('folder/new.txt'); + + $newFolderEntry = $this->cache->get('folder'); + $this->assertNotEquals($newFolderEntry->getEtag(), $oldFolderEntry->getEtag()); + } + + public function testNoETagUnscannedSubFolder() { + $this->fillTestFolders(); + $this->storage->mkdir('folder/sub'); + + $this->scanner->scan(''); + + $oldFolderEntry1 = $this->cache->get('folder'); + $oldFolderEntry2 = $this->cache->get('folder/sub'); + // create a new file in a folder by keeping the mtime unchanged, but mark the folder as unscanned + $this->storage->file_put_contents('folder/sub/new.txt', 'foo'); + $this->storage->touch('folder/sub', $oldFolderEntry1->getMTime()); + + // we only mark the direct parent as unscanned, which is the current "notify" behavior + $this->cache->update($oldFolderEntry2->getId(), ['size' => -1]); + + $this->scanner->scan(''); + + $this->cache->inCache('folder/new.txt'); + + $newFolderEntry1 = $this->cache->get('folder'); + $this->assertNotEquals($newFolderEntry1->getEtag(), $oldFolderEntry1->getEtag()); + $newFolderEntry2 = $this->cache->get('folder/sub'); + $this->assertNotEquals($newFolderEntry2->getEtag(), $oldFolderEntry2->getEtag()); + } } diff --git a/tests/lib/Files/Cache/SearchBuilderTest.php b/tests/lib/Files/Cache/SearchBuilderTest.php index 5eb1a0252f0..45fa17bd227 100644 --- a/tests/lib/Files/Cache/SearchBuilderTest.php +++ b/tests/lib/Files/Cache/SearchBuilderTest.php @@ -154,6 +154,7 @@ class SearchBuilderTest extends TestCase { [new SearchComparison(ISearchComparison::COMPARE_LIKE, 'name', 'foo%'), [0, 1]], [new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', 'image/jpg'), [0]], [new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mimetype', 'image/%'), [0, 1]], + [new SearchComparison(ISearchComparison::COMPARE_IN, 'mimetype', ['image/jpg', 'image/png']), [0, 1]], [new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'size', 50), new SearchComparison(ISearchComparison::COMPARE_LESS_THAN, 'mtime', 125) diff --git a/tests/lib/Files/Cache/UpdaterLegacyTest.php b/tests/lib/Files/Cache/UpdaterLegacyTest.php index be0390db15e..ad6e7cff123 100644 --- a/tests/lib/Files/Cache/UpdaterLegacyTest.php +++ b/tests/lib/Files/Cache/UpdaterLegacyTest.php @@ -262,14 +262,14 @@ class UpdaterLegacyTest extends \Test\TestCase { $this->assertIsString($cachedData['etag']); $this->assertNotSame($oldEtag, $cachedData['etag']); // rename can cause mtime change - invalid assert -// $this->assertEquals($mtime, $cachedData['mtime']); + // $this->assertEquals($mtime, $cachedData['mtime']); $cachedData = $view->getFileInfo('folder'); $this->assertIsString($folderCachedData['etag']); $this->assertIsString($cachedData['etag']); $this->assertNotSame($oldEtag, $cachedData['etag']); // rename can cause mtime change - invalid assert -// $this->assertEquals($mtime, $cachedData['mtime']); + // $this->assertEquals($mtime, $cachedData['mtime']); } public function testTouch() { diff --git a/tests/lib/Files/Config/UserMountCacheTest.php b/tests/lib/Files/Config/UserMountCacheTest.php index ccad4671ae9..9e910f4f47f 100644 --- a/tests/lib/Files/Config/UserMountCacheTest.php +++ b/tests/lib/Files/Config/UserMountCacheTest.php @@ -11,8 +11,8 @@ namespace Test\Files\Config; use OC\DB\QueryBuilder\Literal; use OC\Files\Mount\MountPoint; use OC\Files\Storage\Storage; -use OCP\Cache\CappedMemoryCache; use OC\User\Manager; +use OCP\Cache\CappedMemoryCache; use OCP\Diagnostics\IEventLogger; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Config\ICachedMountInfo; @@ -84,8 +84,8 @@ class UserMountCacheTest extends TestCase { } } - private function getStorage($storageId) { - $rootId = $this->createCacheEntry('', $storageId); + private function getStorage($storageId, $rootInternalPath = '') { + $rootId = $this->createCacheEntry($rootInternalPath, $storageId); $storageCache = $this->getMockBuilder('\OC\Files\Cache\Storage') ->disableOriginalConstructor() @@ -118,6 +118,10 @@ class UserMountCacheTest extends TestCase { $this->invokePrivate($this->cache, 'mountsForUsers', [new CappedMemoryCache()]); } + private function keyForMount(MountPoint $mount): string { + return $mount->getStorageRootId().'::'.$mount->getMountPoint(); + } + public function testNewMounts() { $user = $this->userManager->get('u1'); @@ -131,9 +135,9 @@ class UserMountCacheTest extends TestCase { $cachedMounts = $this->cache->getMountsForUser($user); $this->assertCount(1, $cachedMounts); - $cachedMount = $cachedMounts[0]; + $cachedMount = $cachedMounts[$this->keyForMount($mount)]; $this->assertEquals('/asd/', $cachedMount->getMountPoint()); - $this->assertEquals($user, $cachedMount->getUser()); + $this->assertEquals($user->getUID(), $cachedMount->getUser()->getUID()); $this->assertEquals($storage->getCache()->getId(''), $cachedMount->getRootId()); $this->assertEquals($storage->getStorageCache()->getNumericId(), $cachedMount->getStorageId()); } @@ -155,9 +159,9 @@ class UserMountCacheTest extends TestCase { $cachedMounts = $this->cache->getMountsForUser($user); $this->assertCount(1, $cachedMounts); - $cachedMount = $cachedMounts[0]; + $cachedMount = $cachedMounts[$this->keyForMount($mount)]; $this->assertEquals('/asd/', $cachedMount->getMountPoint()); - $this->assertEquals($user, $cachedMount->getUser()); + $this->assertEquals($user->getUID(), $cachedMount->getUser()->getUID()); $this->assertEquals($storage->getCache()->getId(''), $cachedMount->getRootId()); $this->assertEquals($storage->getStorageCache()->getNumericId(), $cachedMount->getStorageId()); } @@ -200,7 +204,7 @@ class UserMountCacheTest extends TestCase { $cachedMounts = $this->cache->getMountsForUser($user); $this->assertCount(1, $cachedMounts); - $cachedMount = $cachedMounts[0]; + $cachedMount = $cachedMounts[$this->keyForMount($mount)]; $this->assertEquals('/foo/', $cachedMount->getMountPoint()); } @@ -223,7 +227,7 @@ class UserMountCacheTest extends TestCase { $cachedMounts = $this->cache->getMountsForUser($user); $this->assertCount(1, $cachedMounts); - $cachedMount = $cachedMounts[0]; + $cachedMount = $cachedMounts[$this->keyForMount($mount)]; $this->assertEquals(1, $cachedMount->getMountId()); } @@ -233,7 +237,7 @@ class UserMountCacheTest extends TestCase { $user3 = $this->userManager->get('u3'); [$storage1, $id1] = $this->getStorage(1); - [$storage2, $id2] = $this->getStorage(2); + [$storage2, $id2] = $this->getStorage(2, 'foo/bar'); $mount1 = new MountPoint($storage1, '/foo/'); $mount2 = new MountPoint($storage2, '/bar/'); @@ -248,15 +252,17 @@ class UserMountCacheTest extends TestCase { $cachedMounts = $this->cache->getMountsForUser($user1); $this->assertCount(2, $cachedMounts); - $this->assertEquals('/foo/', $cachedMounts[0]->getMountPoint()); - $this->assertEquals($user1, $cachedMounts[0]->getUser()); - $this->assertEquals($id1, $cachedMounts[0]->getRootId()); - $this->assertEquals(1, $cachedMounts[0]->getStorageId()); - - $this->assertEquals('/bar/', $cachedMounts[1]->getMountPoint()); - $this->assertEquals($user1, $cachedMounts[1]->getUser()); - $this->assertEquals($id2, $cachedMounts[1]->getRootId()); - $this->assertEquals(2, $cachedMounts[1]->getStorageId()); + $this->assertEquals('/foo/', $cachedMounts[$this->keyForMount($mount1)]->getMountPoint()); + $this->assertEquals($user1->getUID(), $cachedMounts[$this->keyForMount($mount1)]->getUser()->getUID()); + $this->assertEquals($id1, $cachedMounts[$this->keyForMount($mount1)]->getRootId()); + $this->assertEquals(1, $cachedMounts[$this->keyForMount($mount1)]->getStorageId()); + $this->assertEquals('', $cachedMounts[$this->keyForMount($mount1)]->getRootInternalPath()); + + $this->assertEquals('/bar/', $cachedMounts[$this->keyForMount($mount2)]->getMountPoint()); + $this->assertEquals($user1->getUID(), $cachedMounts[$this->keyForMount($mount2)]->getUser()->getUID()); + $this->assertEquals($id2, $cachedMounts[$this->keyForMount($mount2)]->getRootId()); + $this->assertEquals(2, $cachedMounts[$this->keyForMount($mount2)]->getStorageId()); + $this->assertEquals('foo/bar', $cachedMounts[$this->keyForMount($mount2)]->getRootInternalPath()); $cachedMounts = $this->cache->getMountsForUser($user3); $this->assertEmpty($cachedMounts); @@ -282,12 +288,12 @@ class UserMountCacheTest extends TestCase { $this->assertCount(2, $cachedMounts); $this->assertEquals('/bar/', $cachedMounts[0]->getMountPoint()); - $this->assertEquals($user1, $cachedMounts[0]->getUser()); + $this->assertEquals($user1->getUID(), $cachedMounts[0]->getUser()->getUID()); $this->assertEquals($id2, $cachedMounts[0]->getRootId()); $this->assertEquals(2, $cachedMounts[0]->getStorageId()); $this->assertEquals('/bar/', $cachedMounts[1]->getMountPoint()); - $this->assertEquals($user2, $cachedMounts[1]->getUser()); + $this->assertEquals($user2->getUID(), $cachedMounts[1]->getUser()->getUID()); $this->assertEquals($id2, $cachedMounts[1]->getRootId()); $this->assertEquals(2, $cachedMounts[1]->getStorageId()); } @@ -312,12 +318,12 @@ class UserMountCacheTest extends TestCase { $this->assertCount(2, $cachedMounts); $this->assertEquals('/bar/', $cachedMounts[0]->getMountPoint()); - $this->assertEquals($user1, $cachedMounts[0]->getUser()); + $this->assertEquals($user1->getUID(), $cachedMounts[0]->getUser()->getUID()); $this->assertEquals($id2, $cachedMounts[0]->getRootId()); $this->assertEquals(2, $cachedMounts[0]->getStorageId()); $this->assertEquals('/bar/', $cachedMounts[1]->getMountPoint()); - $this->assertEquals($user2, $cachedMounts[1]->getUser()); + $this->assertEquals($user2->getUID(), $cachedMounts[1]->getUser()->getUID()); $this->assertEquals($id2, $cachedMounts[1]->getRootId()); $this->assertEquals(2, $cachedMounts[1]->getStorageId()); } @@ -372,7 +378,7 @@ class UserMountCacheTest extends TestCase { $this->assertCount(1, $cachedMounts); $this->assertEquals('/foo/', $cachedMounts[0]->getMountPoint()); - $this->assertEquals($user1, $cachedMounts[0]->getUser()); + $this->assertEquals($user1->getUID(), $cachedMounts[0]->getUser()->getUID()); $this->assertEquals($rootId, $cachedMounts[0]->getRootId()); $this->assertEquals(2, $cachedMounts[0]->getStorageId()); } @@ -394,7 +400,7 @@ class UserMountCacheTest extends TestCase { $this->assertCount(1, $cachedMounts); $this->assertEquals('/foo/', $cachedMounts[0]->getMountPoint()); - $this->assertEquals($user1, $cachedMounts[0]->getUser()); + $this->assertEquals($user1->getUID(), $cachedMounts[0]->getUser()->getUID()); $this->assertEquals($rootId, $cachedMounts[0]->getRootId()); $this->assertEquals(2, $cachedMounts[0]->getStorageId()); $this->assertEquals('foo/bar', $cachedMounts[0]->getInternalPath()); @@ -427,7 +433,7 @@ class UserMountCacheTest extends TestCase { $this->assertCount(1, $cachedMounts); $this->assertEquals('/', $cachedMounts[0]->getMountPoint()); - $this->assertEquals($user1, $cachedMounts[0]->getUser()); + $this->assertEquals($user1->getUID(), $cachedMounts[0]->getUser()->getUID()); $this->assertEquals($folderId, $cachedMounts[0]->getRootId()); $this->assertEquals(2, $cachedMounts[0]->getStorageId()); $this->assertEquals('foo', $cachedMounts[0]->getRootInternalPath()); @@ -521,7 +527,7 @@ class UserMountCacheTest extends TestCase { $cachedMounts = $this->cache->getMountsForUser($user1); $this->assertCount(1, $cachedMounts); - $this->assertEquals('', $cachedMounts[0]->getMountProvider()); + $this->assertEquals('', $cachedMounts[$this->keyForMount($mount1)]->getMountProvider()); $mount1 = new MountPoint($storage1, '/foo/', null, null, null, null, 'dummy'); $this->cache->registerMounts($user1, [$mount1], ['dummy']); @@ -530,6 +536,6 @@ class UserMountCacheTest extends TestCase { $cachedMounts = $this->cache->getMountsForUser($user1); $this->assertCount(1, $cachedMounts); - $this->assertEquals('dummy', $cachedMounts[0]->getMountProvider()); + $this->assertEquals('dummy', $cachedMounts[$this->keyForMount($mount1)]->getMountProvider()); } } diff --git a/tests/lib/Files/EtagTest.php b/tests/lib/Files/EtagTest.php index b9583a6ac7c..a71bfa1e572 100644 --- a/tests/lib/Files/EtagTest.php +++ b/tests/lib/Files/EtagTest.php @@ -9,8 +9,8 @@ namespace Test\Files; use OC\Files\Filesystem; -use OCP\EventDispatcher\IEventDispatcher; use OCA\Files_Sharing\AppInfo\Application; +use OCP\EventDispatcher\IEventDispatcher; use Psr\Log\LoggerInterface; /** diff --git a/tests/lib/Files/FilesystemTest.php b/tests/lib/Files/FilesystemTest.php index 96fcab77474..5473f164cef 100644 --- a/tests/lib/Files/FilesystemTest.php +++ b/tests/lib/Files/FilesystemTest.php @@ -325,14 +325,14 @@ class FilesystemTest extends \Test\TestCase { $rootView->mkdir('/' . $user); $rootView->mkdir('/' . $user . '/files'); -// \OC\Files\Filesystem::file_put_contents('/foo', 'foo'); + // \OC\Files\Filesystem::file_put_contents('/foo', 'foo'); \OC\Files\Filesystem::mkdir('/bar'); -// \OC\Files\Filesystem::file_put_contents('/bar//foo', 'foo'); + // \OC\Files\Filesystem::file_put_contents('/bar//foo', 'foo'); $tmpFile = \OC::$server->getTempManager()->getTemporaryFile(); file_put_contents($tmpFile, 'foo'); $fh = fopen($tmpFile, 'r'); -// \OC\Files\Filesystem::file_put_contents('/bar//foo', $fh); + // \OC\Files\Filesystem::file_put_contents('/bar//foo', $fh); } /** diff --git a/tests/lib/Files/Node/FileTest.php b/tests/lib/Files/Node/FileTest.php index 218e2531727..b245f5e2db5 100644 --- a/tests/lib/Files/Node/FileTest.php +++ b/tests/lib/Files/Node/FileTest.php @@ -39,7 +39,7 @@ class FileTest extends NodeTest { public function testGetContent() { /** @var \OC\Files\Node\Root|\PHPUnit\Framework\MockObject\MockObject $root */ $root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $hook = function ($file) { @@ -69,7 +69,7 @@ class FileTest extends NodeTest { /** @var \OC\Files\Node\Root|\PHPUnit\Framework\MockObject\MockObject $root */ $root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $root->expects($this->any()) @@ -88,7 +88,7 @@ class FileTest extends NodeTest { public function testPutContent() { /** @var \OC\Files\Node\Root|\PHPUnit\Framework\MockObject\MockObject $root */ $root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $root->expects($this->any()) @@ -115,7 +115,7 @@ class FileTest extends NodeTest { /** @var \OC\Files\Node\Root|\PHPUnit\Framework\MockObject\MockObject $root */ $root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $this->view->expects($this->once()) @@ -130,7 +130,7 @@ class FileTest extends NodeTest { public function testGetMimeType() { /** @var \OC\Files\Node\Root|\PHPUnit\Framework\MockObject\MockObject $root */ $root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $this->view->expects($this->once()) @@ -154,7 +154,8 @@ class FileTest extends NodeTest { $this->userMountCache, $this->logger, $this->userManager, - $this->eventDispatcher + $this->eventDispatcher, + $this->cacheFactory, ); $hook = function ($file) { @@ -190,7 +191,8 @@ class FileTest extends NodeTest { $this->userMountCache, $this->logger, $this->userManager, - $this->eventDispatcher + $this->eventDispatcher, + $this->cacheFactory, ); $hooksCalled = 0; $hook = function ($file) use (&$hooksCalled) { @@ -230,7 +232,8 @@ class FileTest extends NodeTest { $this->userMountCache, $this->logger, $this->userManager, - $this->eventDispatcher + $this->eventDispatcher, + $this->cacheFactory, ); $hook = function ($file) { throw new \Exception('Hooks are not supposed to be called'); @@ -256,7 +259,8 @@ class FileTest extends NodeTest { $this->userMountCache, $this->logger, $this->userManager, - $this->eventDispatcher + $this->eventDispatcher, + $this->cacheFactory, ); $hook = function () { throw new \Exception('Hooks are not supposed to be called'); @@ -282,7 +286,8 @@ class FileTest extends NodeTest { $this->userMountCache, $this->logger, $this->userManager, - $this->eventDispatcher + $this->eventDispatcher, + $this->cacheFactory, ); $hook = function () { throw new \Exception('Hooks are not supposed to be called'); diff --git a/tests/lib/Files/Node/FolderTest.php b/tests/lib/Files/Node/FolderTest.php index 0bcf69c5c13..a219222d008 100644 --- a/tests/lib/Files/Node/FolderTest.php +++ b/tests/lib/Files/Node/FolderTest.php @@ -68,7 +68,7 @@ class FolderTest extends NodeTest { * @var \OC\Files\View | \PHPUnit\Framework\MockObject\MockObject $view */ $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $root->expects($this->any()) ->method('getUser') @@ -101,7 +101,7 @@ class FolderTest extends NodeTest { $manager = $this->createMock(Manager::class); $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $root->expects($this->any()) ->method('getUser') @@ -120,7 +120,7 @@ class FolderTest extends NodeTest { $manager = $this->createMock(Manager::class); $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $root->expects($this->any()) ->method('getUser') @@ -140,7 +140,7 @@ class FolderTest extends NodeTest { $manager = $this->createMock(Manager::class); $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $root->expects($this->any()) ->method('getUser') @@ -158,7 +158,7 @@ class FolderTest extends NodeTest { $manager = $this->createMock(Manager::class); $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $root->expects($this->any()) ->method('getUser') @@ -182,7 +182,7 @@ class FolderTest extends NodeTest { $manager = $this->createMock(Manager::class); $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $root->expects($this->any()) ->method('getUser') @@ -209,7 +209,7 @@ class FolderTest extends NodeTest { $manager = $this->createMock(Manager::class); $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $root->method('getUser') ->willReturn($this->user); @@ -226,7 +226,7 @@ class FolderTest extends NodeTest { $manager = $this->createMock(Manager::class); $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $root->expects($this->any()) ->method('getUser') @@ -253,7 +253,7 @@ class FolderTest extends NodeTest { $manager = $this->createMock(Manager::class); $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $root->method('getUser') ->willReturn($this->user); @@ -270,7 +270,7 @@ class FolderTest extends NodeTest { $manager = $this->createMock(Manager::class); $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $root->method('getUser') ->willReturn($this->user); @@ -287,7 +287,7 @@ class FolderTest extends NodeTest { $manager = $this->createMock(Manager::class); $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $root->method('getUser') ->willReturn($this->user); @@ -328,7 +328,7 @@ class FolderTest extends NodeTest { $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) ->setMethods(['getUser', 'getMountsIn', 'getMount']) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $root->expects($this->any()) ->method('getUser') @@ -368,7 +368,7 @@ class FolderTest extends NodeTest { $manager = $this->createMock(Manager::class); $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $root->method('getUser') ->willReturn($this->user); @@ -408,7 +408,7 @@ class FolderTest extends NodeTest { $manager = $this->createMock(Manager::class); $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $root->expects($this->any()) ->method('getUser') @@ -478,7 +478,7 @@ class FolderTest extends NodeTest { $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) ->setMethods(['getMountsIn', 'getMount']) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $storage = $this->createMock(\OC\Files\Storage\Storage::class); $mount = new MountPoint($storage, '/bar'); @@ -525,7 +525,7 @@ class FolderTest extends NodeTest { $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) ->setMethods(['getMountsIn', 'getMount']) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $storage = $this->createMock(\OC\Files\Storage\Storage::class); $mount = new MountPoint($storage, '/bar'); @@ -568,7 +568,7 @@ class FolderTest extends NodeTest { $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) ->setMethods(['getMountsIn', 'getMount']) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $storage = $this->createMock(\OC\Files\Storage\Storage::class); $mount = new MountPoint($storage, '/bar'); @@ -610,7 +610,7 @@ class FolderTest extends NodeTest { $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) ->setMethods(['getMountsIn', 'getMount']) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $storage = $this->createMock(\OC\Files\Storage\Storage::class); $mount1 = new MountPoint($storage, '/bar'); @@ -672,7 +672,7 @@ class FolderTest extends NodeTest { $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) ->setMethods(['getUser', 'getMountsIn', 'getMount']) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $view->expects($this->any()) @@ -697,7 +697,7 @@ class FolderTest extends NodeTest { /** @var \PHPUnit\Framework\MockObject\MockObject|\OC\Files\Node\Root $root */ $root = $this->getMockBuilder(Root::class) ->setMethods(['getUser', 'getMountsIn', 'getMount']) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); /** @var \PHPUnit\Framework\MockObject\MockObject|\OC\Files\FileInfo $folderInfo */ $folderInfo = $this->getMockBuilder(FileInfo::class) @@ -762,7 +762,7 @@ class FolderTest extends NodeTest { /** @var \PHPUnit\Framework\MockObject\MockObject|\OC\Files\Node\Root $root */ $root = $this->getMockBuilder(Root::class) ->setMethods(['getUser', 'getMountsIn', 'getMount']) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); /** @var \PHPUnit\Framework\MockObject\MockObject|\OC\Files\FileInfo $folderInfo */ $folderInfo = $this->getMockBuilder(FileInfo::class) @@ -826,7 +826,7 @@ class FolderTest extends NodeTest { /** @var \PHPUnit\Framework\MockObject\MockObject|\OC\Files\Node\Root $root */ $root = $this->getMockBuilder(Root::class) ->setMethods(['getUser', 'getMountsIn', 'getMount']) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); /** @var \PHPUnit\Framework\MockObject\MockObject|\OC\Files\FileInfo $folderInfo */ $folderInfo = $this->getMockBuilder(FileInfo::class) @@ -910,7 +910,7 @@ class FolderTest extends NodeTest { $manager = $this->createMock(Manager::class); $view = $this->getRootViewMock(); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); $root->expects($this->any()) ->method('getUser') diff --git a/tests/lib/Files/Node/HookConnectorTest.php b/tests/lib/Files/Node/HookConnectorTest.php index 0501c175a5f..c64052ad172 100644 --- a/tests/lib/Files/Node/HookConnectorTest.php +++ b/tests/lib/Files/Node/HookConnectorTest.php @@ -13,6 +13,7 @@ use OC\Files\Node\HookConnector; use OC\Files\Node\Root; use OC\Files\Storage\Temporary; use OC\Files\View; +use OC\Memcache\ArrayCache; use OCP\EventDispatcher\GenericEvent as APIGenericEvent; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Events\Node\AbstractNodeEvent; @@ -30,6 +31,7 @@ use OCP\Files\Events\Node\NodeRenamedEvent; use OCP\Files\Events\Node\NodeTouchedEvent; use OCP\Files\Events\Node\NodeWrittenEvent; use OCP\Files\Node; +use OCP\ICacheFactory; use OCP\IUserManager; use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\GenericEvent; @@ -51,6 +53,8 @@ class HookConnectorTest extends TestCase { /** @var IEventDispatcher */ protected $eventDispatcher; + private LoggerInterface $logger; + /** @var View */ private $view; @@ -67,6 +71,11 @@ class HookConnectorTest extends TestCase { // this will setup the FS $this->loginAsUser($this->userId); $this->registerMount($this->userId, new Temporary(), '/' . $this->userId . '/files/'); + $cacheFactory = $this->createMock(ICacheFactory::class); + $cacheFactory->method('createLocal') + ->willReturnCallback(function () { + return new ArrayCache(); + }); $this->view = new View(); $this->root = new Root( Filesystem::getMountManager(), @@ -75,9 +84,11 @@ class HookConnectorTest extends TestCase { \OC::$server->getUserMountCache(), $this->createMock(LoggerInterface::class), $this->createMock(IUserManager::class), - $this->createMock(IEventDispatcher::class) + $this->createMock(IEventDispatcher::class), + $cacheFactory, ); $this->eventDispatcher = \OC::$server->query(IEventDispatcher::class); + $this->logger = \OC::$server->query(LoggerInterface::class); } protected function tearDown(): void { @@ -143,7 +154,7 @@ class HookConnectorTest extends TestCase { * @dataProvider viewToNodeProvider */ public function testViewToNode(callable $operation, $expectedHook, $expectedLegacyEvent, $expectedEvent) { - $connector = new HookConnector($this->root, $this->view, $this->eventDispatcher); + $connector = new HookConnector($this->root, $this->view, $this->eventDispatcher, $this->logger); $connector->viewToNode(); $hookCalled = false; /** @var Node $hookNode */ @@ -212,7 +223,7 @@ class HookConnectorTest extends TestCase { * @dataProvider viewToNodeProviderCopyRename */ public function testViewToNodeCopyRename(callable $operation, $expectedHook, $expectedLegacyEvent, $expectedEvent) { - $connector = new HookConnector($this->root, $this->view, $this->eventDispatcher); + $connector = new HookConnector($this->root, $this->view, $this->eventDispatcher, $this->logger); $connector->viewToNode(); $hookCalled = false; /** @var Node $hookSourceNode */ @@ -267,7 +278,7 @@ class HookConnectorTest extends TestCase { } public function testPostDeleteMeta() { - $connector = new HookConnector($this->root, $this->view, $this->eventDispatcher); + $connector = new HookConnector($this->root, $this->view, $this->eventDispatcher, $this->logger); $connector->viewToNode(); $hookCalled = false; /** @var Node $hookNode */ diff --git a/tests/lib/Files/Node/IntegrationTest.php b/tests/lib/Files/Node/IntegrationTest.php index 5ef5a134e1b..dacc65ad4d1 100644 --- a/tests/lib/Files/Node/IntegrationTest.php +++ b/tests/lib/Files/Node/IntegrationTest.php @@ -11,8 +11,10 @@ namespace Test\Files\Node; use OC\Files\Node\Root; use OC\Files\Storage\Temporary; use OC\Files\View; +use OC\Memcache\ArrayCache; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Mount\IMountManager; +use OCP\ICacheFactory; use OCP\IUserManager; use Psr\Log\LoggerInterface; use Test\Traits\UserTrait; @@ -52,6 +54,11 @@ class IntegrationTest extends \Test\TestCase { $user = $this->createUser($this->getUniqueID('user'), ''); $this->loginAsUser($user->getUID()); + $cacheFactory = $this->createMock(ICacheFactory::class); + $cacheFactory->method('createLocal') + ->willReturnCallback(function () { + return new ArrayCache(); + }); $this->view = new View(); $this->root = new Root( @@ -61,7 +68,8 @@ class IntegrationTest extends \Test\TestCase { \OC::$server->getUserMountCache(), $this->createMock(LoggerInterface::class), $this->createMock(IUserManager::class), - $this->createMock(IEventDispatcher::class) + $this->createMock(IEventDispatcher::class), + $cacheFactory, ); $storage = new Temporary([]); $subStorage = new Temporary([]); diff --git a/tests/lib/Files/Node/NodeTest.php b/tests/lib/Files/Node/NodeTest.php index 4f782f28a36..85a7f8a82f3 100644 --- a/tests/lib/Files/Node/NodeTest.php +++ b/tests/lib/Files/Node/NodeTest.php @@ -11,12 +11,14 @@ namespace Test\Files\Node; use OC\Files\FileInfo; use OC\Files\Mount\Manager; use OC\Files\View; +use OC\Memcache\ArrayCache; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\IRootFolder; use OCP\Files\Mount\IMountPoint; use OCP\Files\Node; use OCP\Files\NotFoundException; use OCP\Files\Storage; +use OCP\ICacheFactory; use OCP\IUser; use OCP\IUserManager; use Psr\Log\LoggerInterface; @@ -43,6 +45,8 @@ abstract class NodeTest extends \Test\TestCase { protected $userManager; /** @var IEventDispatcher|\PHPUnit\Framework\MockObject\MockObject */ protected $eventDispatcher; + /** @var ICacheFactory|\PHPUnit\Framework\MockObject\MockObject */ + protected $cacheFactory; protected function setUp(): void { parent::setUp(); @@ -63,8 +67,13 @@ abstract class NodeTest extends \Test\TestCase { $this->logger = $this->createMock(LoggerInterface::class); $this->userManager = $this->createMock(IUserManager::class); $this->eventDispatcher = $this->createMock(IEventDispatcher::class); + $this->cacheFactory = $this->createMock(ICacheFactory::class); + $this->cacheFactory->method('createLocal') + ->willReturnCallback(function () { + return new ArrayCache(); + }); $this->root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->eventDispatcher]) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->getMock(); } @@ -174,7 +183,8 @@ abstract class NodeTest extends \Test\TestCase { $this->userMountCache, $this->logger, $this->userManager, - $this->eventDispatcher + $this->eventDispatcher, + $this->cacheFactory, ); $root->listen('\OC\Files', 'preDelete', $preListener); @@ -422,7 +432,8 @@ abstract class NodeTest extends \Test\TestCase { $this->userMountCache, $this->logger, $this->userManager, - $this->eventDispatcher + $this->eventDispatcher, + $this->cacheFactory, ); $root->listen('\OC\Files', 'preTouch', $preListener); $root->listen('\OC\Files', 'postTouch', $postListener); @@ -599,7 +610,7 @@ abstract class NodeTest extends \Test\TestCase { public function testMoveCopyHooks($operationMethod, $viewMethod, $preHookName, $postHookName) { /** @var IRootFolder|\PHPUnit\Framework\MockObject\MockObject $root */ $root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher]) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory]) ->setMethods(['get']) ->getMock(); diff --git a/tests/lib/Files/Node/RootTest.php b/tests/lib/Files/Node/RootTest.php index aab658c3c36..d9abe2955b1 100644 --- a/tests/lib/Files/Node/RootTest.php +++ b/tests/lib/Files/Node/RootTest.php @@ -8,12 +8,14 @@ namespace Test\Files\Node; -use OCP\Cache\CappedMemoryCache; use OC\Files\FileInfo; use OC\Files\Mount\Manager; use OC\Files\Node\Folder; use OC\Files\View; +use OC\Memcache\ArrayCache; +use OCP\Cache\CappedMemoryCache; use OCP\EventDispatcher\IEventDispatcher; +use OCP\ICacheFactory; use OCP\IUser; use OCP\IUserManager; use Psr\Log\LoggerInterface; @@ -36,6 +38,8 @@ class RootTest extends \Test\TestCase { private $userManager; /** @var IEventDispatcher|\PHPUnit\Framework\MockObject\MockObject */ private $eventDispatcher; + /** @var ICacheFactory|\PHPUnit\Framework\MockObject\MockObject */ + protected $cacheFactory; protected function setUp(): void { parent::setUp(); @@ -50,6 +54,11 @@ class RootTest extends \Test\TestCase { $this->logger = $this->createMock(LoggerInterface::class); $this->userManager = $this->createMock(IUserManager::class); $this->eventDispatcher = $this->createMock(IEventDispatcher::class); + $this->cacheFactory = $this->createMock(ICacheFactory::class); + $this->cacheFactory->method('createLocal') + ->willReturnCallback(function () { + return new ArrayCache(); + }); } /** @@ -82,7 +91,8 @@ class RootTest extends \Test\TestCase { $this->userMountCache, $this->logger, $this->userManager, - $this->eventDispatcher + $this->eventDispatcher, + $this->cacheFactory, ); $view->expects($this->once()) @@ -114,7 +124,8 @@ class RootTest extends \Test\TestCase { $this->userMountCache, $this->logger, $this->userManager, - $this->eventDispatcher + $this->eventDispatcher, + $this->cacheFactory, ); $view->expects($this->once()) @@ -138,7 +149,8 @@ class RootTest extends \Test\TestCase { $this->userMountCache, $this->logger, $this->userManager, - $this->eventDispatcher + $this->eventDispatcher, + $this->cacheFactory, ); $root->get('/../foo'); @@ -156,7 +168,8 @@ class RootTest extends \Test\TestCase { $this->userMountCache, $this->logger, $this->userManager, - $this->eventDispatcher + $this->eventDispatcher, + $this->cacheFactory, ); $root->get('/bar/foo'); @@ -170,7 +183,8 @@ class RootTest extends \Test\TestCase { $this->userMountCache, $this->logger, $this->userManager, - $this->eventDispatcher + $this->eventDispatcher, + $this->cacheFactory, ); $user = $this->createMock(IUser::class); $user @@ -211,7 +225,8 @@ class RootTest extends \Test\TestCase { $this->userMountCache, $this->logger, $this->userManager, - $this->eventDispatcher + $this->eventDispatcher, + $this->cacheFactory, ); $this->userManager ->expects($this->once()) diff --git a/tests/lib/Files/ObjectStore/ObjectStoreStorageOverwrite.php b/tests/lib/Files/ObjectStore/ObjectStoreStorageOverwrite.php index b85f6289c94..9c398131535 100644 --- a/tests/lib/Files/ObjectStore/ObjectStoreStorageOverwrite.php +++ b/tests/lib/Files/ObjectStore/ObjectStoreStorageOverwrite.php @@ -30,7 +30,7 @@ use OCP\Files\ObjectStore\IObjectStore; * Allow overwriting the object store instance for test purposes */ class ObjectStoreStorageOverwrite extends ObjectStoreStorage { - public function setObjectStore(IObjectStore $objectStore) { + public function setObjectStore(IObjectStore $objectStore): void { $this->objectStore = $objectStore; } @@ -38,7 +38,7 @@ class ObjectStoreStorageOverwrite extends ObjectStoreStorage { return $this->objectStore; } - public function setValidateWrites(bool $validate) { + public function setValidateWrites(bool $validate): void { $this->validateWrites = $validate; } } diff --git a/tests/lib/Files/ObjectStore/ObjectStoreStorageTest.php b/tests/lib/Files/ObjectStore/ObjectStoreStorageTest.php index 1bebaf6c4ba..2f835747077 100644 --- a/tests/lib/Files/ObjectStore/ObjectStoreStorageTest.php +++ b/tests/lib/Files/ObjectStore/ObjectStoreStorageTest.php @@ -237,4 +237,42 @@ class ObjectStoreStorageTest extends Storage { $this->assertEquals('2', $this->instance->file_get_contents('b/target/sub/2.txt')); $this->assertEquals('3', $this->instance->file_get_contents('b/target/sub/3.txt')); } + + public function testCopyPreservesPermissions() { + $cache = $this->instance->getCache(); + + $this->instance->file_put_contents('test.txt', 'foo'); + $this->assertTrue($cache->inCache('test.txt')); + + $cache->update($cache->getId('test.txt'), ['permissions' => \OCP\Constants::PERMISSION_READ]); + $this->assertEquals(\OCP\Constants::PERMISSION_READ, $this->instance->getPermissions('test.txt')); + + $this->assertTrue($this->instance->copy('test.txt', 'new.txt')); + + $this->assertTrue($cache->inCache('new.txt')); + $this->assertEquals(\OCP\Constants::PERMISSION_READ, $this->instance->getPermissions('new.txt')); + } + + /** + * Test that copying files will drop permissions like local storage does + * TODO: Drop this and fix local storage + */ + public function testCopyGrantsPermissions() { + $config['objectstore'] = $this->objectStorage; + $config['handleCopiesAsOwned'] = true; + $instance = new ObjectStoreStorageOverwrite($config); + + $cache = $instance->getCache(); + + $instance->file_put_contents('test.txt', 'foo'); + $this->assertTrue($cache->inCache('test.txt')); + + $cache->update($cache->getId('test.txt'), ['permissions' => \OCP\Constants::PERMISSION_READ]); + $this->assertEquals(\OCP\Constants::PERMISSION_READ, $instance->getPermissions('test.txt')); + + $this->assertTrue($instance->copy('test.txt', 'new.txt')); + + $this->assertTrue($cache->inCache('new.txt')); + $this->assertEquals(\OCP\Constants::PERMISSION_ALL, $instance->getPermissions('new.txt')); + } } diff --git a/tests/lib/Files/ObjectStore/S3Test.php b/tests/lib/Files/ObjectStore/S3Test.php index fd451dc3c01..c8333ca1ea3 100644 --- a/tests/lib/Files/ObjectStore/S3Test.php +++ b/tests/lib/Files/ObjectStore/S3Test.php @@ -106,29 +106,30 @@ class S3Test extends ObjectStoreTest { } public function assertNoUpload($objectUrn) { + /** @var \OC\Files\ObjectStore\S3 */ $s3 = $this->getInstance(); $s3client = $s3->getConnection(); $uploads = $s3client->listMultipartUploads([ 'Bucket' => $s3->getBucket(), 'Prefix' => $objectUrn, ]); - $this->assertArrayNotHasKey('Uploads', $uploads); + $this->assertArrayNotHasKey('Uploads', $uploads, 'Assert is not uploaded'); } public function testEmptyUpload() { $s3 = $this->getInstance(); $emptyStream = fopen("php://memory", "r"); - fwrite($emptyStream, null); + fwrite($emptyStream, ''); $s3->writeObject('emptystream', $emptyStream); $this->assertNoUpload('emptystream'); - $this->assertTrue($s3->objectExists('emptystream')); + $this->assertTrue($s3->objectExists('emptystream'), 'Object exists on S3'); $thrown = false; try { - self::assertFalse($s3->readObject('emptystream')); + self::assertFalse($s3->readObject('emptystream'), 'Reading empty stream object should return false'); } catch (\Exception $e) { // An exception is expected here since 0 byte files are wrapped // to be read from an empty memory stream in the ObjectStoreStorage @@ -163,20 +164,20 @@ class S3Test extends ObjectStoreTest { $s3->writeObject('testfilesizes', $sourceStream); $this->assertNoUpload('testfilesizes'); - self::assertTrue($s3->objectExists('testfilesizes')); + self::assertTrue($s3->objectExists('testfilesizes'), 'Object exists on S3'); $result = $s3->readObject('testfilesizes'); // compare first 100 bytes - self::assertEquals(str_repeat('A', 100), fread($result, 100)); + self::assertEquals(str_repeat('A', 100), fread($result, 100), 'Compare first 100 bytes'); - // compare 100 bytes + // compare last 100 bytes fseek($result, $size - 100); - self::assertEquals(str_repeat('A', 100), fread($result, 100)); + self::assertEquals(str_repeat('A', 100), fread($result, 100), 'Compare last 100 bytes'); // end of file reached fseek($result, $size); - self::assertTrue(feof($result)); + self::assertTrue(feof($result), 'End of file reached'); $this->assertNoUpload('testfilesizes'); } diff --git a/tests/lib/Files/ObjectStore/SwiftTest.php b/tests/lib/Files/ObjectStore/SwiftTest.php index 1ea55a84628..bebfba0c8a4 100644 --- a/tests/lib/Files/ObjectStore/SwiftTest.php +++ b/tests/lib/Files/ObjectStore/SwiftTest.php @@ -38,4 +38,8 @@ class SwiftTest extends ObjectStoreTest { return new Swift($config['arguments']); } + + public function testFseekSize() { + $this->markTestSkipped('Swift does not support seeking at the moment'); + } } diff --git a/tests/lib/Files/Search/QueryOptimizer/CombinedTests.php b/tests/lib/Files/Search/QueryOptimizer/CombinedTests.php new file mode 100644 index 00000000000..c5abd5603da --- /dev/null +++ b/tests/lib/Files/Search/QueryOptimizer/CombinedTests.php @@ -0,0 +1,191 @@ +<?php + +namespace Test\Files\Search\QueryOptimizer; + +use OC\Files\Search\QueryOptimizer\QueryOptimizer; +use OC\Files\Search\SearchBinaryOperator; +use OC\Files\Search\SearchComparison; +use OCP\Files\Search\ISearchBinaryOperator; +use OCP\Files\Search\ISearchComparison; +use Test\TestCase; + +class CombinedTests extends TestCase { + private QueryOptimizer $optimizer; + + protected function setUp(): void { + parent::setUp(); + + $this->optimizer = new QueryOptimizer(); + } + + public function testBasicOrOfAnds() { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "foo"), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "bar"), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "asd"), + ]) + ] + ); + $this->assertEquals('((storage eq 1 and path eq "foo") or (storage eq 1 and path eq "bar") or (storage eq 1 and path eq "asd"))', $operator->__toString()); + + $this->optimizer->processOperator($operator); + + $this->assertEquals('(storage eq 1 and path in ["foo","bar","asd"])', $operator->__toString()); + } + + public function testComplexSearchPattern1() { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 1), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 2), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "201"), + new SearchComparison(ISearchComparison::COMPARE_LIKE, "path", "201/%"), + ]), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 3), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "301"), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 4), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "401"), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 3), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "302"), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 4), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "402"), + ]), + ] + ); + $this->assertEquals('((storage eq 1) or (storage eq 2 and (path eq "201" or path like "201\/%")) or (storage eq 3 and path eq "301") or (storage eq 4 and path eq "401") or (storage eq 3 and path eq "302") or (storage eq 4 and path eq "402"))', $operator->__toString()); + + $this->optimizer->processOperator($operator); + + $this->assertEquals('(storage eq 1 or (storage eq 2 and (path eq "201" or path like "201\/%")) or (storage eq 3 and path in ["301","302"]) or (storage eq 4 and path in ["401","402"]))', $operator->__toString()); + } + + public function testComplexSearchPattern2() { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 1), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 2), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "201"), + new SearchComparison(ISearchComparison::COMPARE_LIKE, "path", "201/%"), + ]), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 3), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "301"), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 4), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "401"), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 3), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "302"), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 4), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "402"), + ]), + ] + ); + $this->assertEquals('(storage eq 1 or (storage eq 2 and (path eq "201" or path like "201\/%")) or (storage eq 3 and path eq "301") or (storage eq 4 and path eq "401") or (storage eq 3 and path eq "302") or (storage eq 4 and path eq "402"))', $operator->__toString()); + + $this->optimizer->processOperator($operator); + + $this->assertEquals('(storage eq 1 or (storage eq 2 and (path eq "201" or path like "201\/%")) or (storage eq 3 and path in ["301","302"]) or (storage eq 4 and path in ["401","402"]))', $operator->__toString()); + } + + public function testApplySearchConstraints1() { + $operator = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "mimetype", "image/png"), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "mimetype", "image/jpeg"), + ]), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [ + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 1), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "files"), + new SearchComparison(ISearchComparison::COMPARE_LIKE, "path", "files/%"), + ]), + ]), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 2), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 3), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "files/301"), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 3), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "files/302"), + ]), + ]), + ]); + $this->assertEquals('(((mimetype eq "image\/png" or mimetype eq "image\/jpeg")) and ((storage eq 1 and (path eq "files" or path like "files\/%")) or storage eq 2 or (storage eq 3 and path eq "files\/301") or (storage eq 3 and path eq "files\/302")))', $operator->__toString()); + + $this->optimizer->processOperator($operator); + + $this->assertEquals('(mimetype in ["image\/png","image\/jpeg"] and ((storage eq 1 and (path eq "files" or path like "files\/%")) or storage eq 2 or (storage eq 3 and path in ["files\/301","files\/302"])))', $operator->__toString()); + } + + public function testApplySearchConstraints2() { + $operator = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "mimetype", "image/png"), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "mimetype", "image/jpeg"), + ]), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [ + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 1), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "files"), + new SearchComparison(ISearchComparison::COMPARE_LIKE, "path", "files/%"), + ]), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 2), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 3), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "files/301"), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 3), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "files/302"), + ]), + ]), + ]); + $this->assertEquals('(((mimetype eq "image\/png" or mimetype eq "image\/jpeg")) and ((storage eq 1 and (path eq "files" or path like "files\/%")) or (storage eq 2) or (storage eq 3 and path eq "files\/301") or (storage eq 3 and path eq "files\/302")))', $operator->__toString()); + + $this->optimizer->processOperator($operator); + + $this->assertEquals('(mimetype in ["image\/png","image\/jpeg"] and ((storage eq 1 and (path eq "files" or path like "files\/%")) or storage eq 2 or (storage eq 3 and path in ["files\/301","files\/302"])))', $operator->__toString()); + } +} diff --git a/tests/lib/Files/Search/QueryOptimizer/FlattenNestedBoolTest.php b/tests/lib/Files/Search/QueryOptimizer/FlattenNestedBoolTest.php new file mode 100644 index 00000000000..a21f2a19b90 --- /dev/null +++ b/tests/lib/Files/Search/QueryOptimizer/FlattenNestedBoolTest.php @@ -0,0 +1,42 @@ +<?php + +namespace Test\Files\Search\QueryOptimizer; + +use OC\Files\Search\QueryOptimizer\FlattenNestedBool; +use OC\Files\Search\QueryOptimizer\FlattenSingleArgumentBinaryOperation; +use OC\Files\Search\SearchBinaryOperator; +use OC\Files\Search\SearchComparison; +use OCP\Files\Search\ISearchBinaryOperator; +use OCP\Files\Search\ISearchComparison; +use Test\TestCase; + +class FlattenNestedBoolTest extends TestCase { + private $optimizer; + private $simplifier; + + protected function setUp(): void { + parent::setUp(); + + $this->optimizer = new FlattenNestedBool(); + $this->simplifier = new FlattenSingleArgumentBinaryOperation(); + } + + public function testOrs() { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "foo"), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "bar"), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "asd"), + ]) + ] + ); + $this->assertEquals('(path eq "foo" or (path eq "bar" or path eq "asd"))', $operator->__toString()); + + $this->optimizer->processOperator($operator); + $this->simplifier->processOperator($operator); + + $this->assertEquals('(path eq "foo" or path eq "bar" or path eq "asd")', $operator->__toString()); + } +} diff --git a/tests/lib/Files/Search/QueryOptimizer/MergeDistributiveOperationsTest.php b/tests/lib/Files/Search/QueryOptimizer/MergeDistributiveOperationsTest.php new file mode 100644 index 00000000000..4c7ecc9d46f --- /dev/null +++ b/tests/lib/Files/Search/QueryOptimizer/MergeDistributiveOperationsTest.php @@ -0,0 +1,160 @@ +<?php + +namespace Test\Files\Search\QueryOptimizer; + +use OC\Files\Search\QueryOptimizer\FlattenSingleArgumentBinaryOperation; +use OC\Files\Search\QueryOptimizer\MergeDistributiveOperations; +use OC\Files\Search\SearchBinaryOperator; +use OC\Files\Search\SearchComparison; +use OCP\Files\Search\ISearchBinaryOperator; +use OCP\Files\Search\ISearchComparison; +use Test\TestCase; + +class MergeDistributiveOperationsTest extends TestCase { + private $optimizer; + private $simplifier; + + protected function setUp(): void { + parent::setUp(); + + $this->optimizer = new MergeDistributiveOperations(); + $this->simplifier = new FlattenSingleArgumentBinaryOperation(); + } + + public function testBasicOrOfAnds() { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "foo"), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "bar"), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "asd"), + ]) + ] + ); + $this->assertEquals('((storage eq 1 and path eq "foo") or (storage eq 1 and path eq "bar") or (storage eq 1 and path eq "asd"))', $operator->__toString()); + + $this->optimizer->processOperator($operator); + $this->simplifier->processOperator($operator); + + $this->assertEquals('(storage eq 1 and (path eq "foo" or path eq "bar" or path eq "asd"))', $operator->__toString()); + } + + public function testDontTouchIfNotSame() { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "foo"), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 2), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "bar"), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 3), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "asd"), + ]) + ] + ); + $this->assertEquals('((storage eq 1 and path eq "foo") or (storage eq 2 and path eq "bar") or (storage eq 3 and path eq "asd"))', $operator->__toString()); + + $this->optimizer->processOperator($operator); + $this->simplifier->processOperator($operator); + + $this->assertEquals('((storage eq 1 and path eq "foo") or (storage eq 2 and path eq "bar") or (storage eq 3 and path eq "asd"))', $operator->__toString()); + } + + public function testMergePartial() { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "foo"), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "bar"), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 2), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "asd"), + ]) + ] + ); + $this->assertEquals('((storage eq 1 and path eq "foo") or (storage eq 1 and path eq "bar") or (storage eq 2 and path eq "asd"))', $operator->__toString()); + + $this->optimizer->processOperator($operator); + $this->simplifier->processOperator($operator); + + $this->assertEquals('((storage eq 1 and (path eq "foo" or path eq "bar")) or (storage eq 2 and path eq "asd"))', $operator->__toString()); + } + + public function testOptimizeInside() { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_AND, + [ + new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "foo"), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "bar"), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "asd"), + ]) + ] + ), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "mimetype", "text") + ] + ); + $this->assertEquals('(((storage eq 1 and path eq "foo") or (storage eq 1 and path eq "bar") or (storage eq 1 and path eq "asd")) and mimetype eq "text")', $operator->__toString()); + + $this->optimizer->processOperator($operator); + $this->simplifier->processOperator($operator); + + $this->assertEquals('((storage eq 1 and (path eq "foo" or path eq "bar" or path eq "asd")) and mimetype eq "text")', $operator->__toString()); + } + + public function testMoveInnerOperations() { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "foo"), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "bar"), + ]), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "storage", 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "asd"), + new SearchComparison(ISearchComparison::COMPARE_GREATER_THAN, "size", "100"), + ]) + ] + ); + $this->assertEquals('((storage eq 1 and path eq "foo") or (storage eq 1 and path eq "bar") or (storage eq 1 and path eq "asd" and size gt "100"))', $operator->__toString()); + + $this->optimizer->processOperator($operator); + $this->simplifier->processOperator($operator); + + $this->assertEquals('(storage eq 1 and (path eq "foo" or path eq "bar" or (path eq "asd" and size gt "100")))', $operator->__toString()); + } +} diff --git a/tests/lib/Files/Search/QueryOptimizer/OrEqualsToInTest.php b/tests/lib/Files/Search/QueryOptimizer/OrEqualsToInTest.php new file mode 100644 index 00000000000..3d3160079cd --- /dev/null +++ b/tests/lib/Files/Search/QueryOptimizer/OrEqualsToInTest.php @@ -0,0 +1,120 @@ +<?php + +namespace Test\Files\Search\QueryOptimizer; + +use OC\Files\Search\QueryOptimizer\FlattenSingleArgumentBinaryOperation; +use OC\Files\Search\QueryOptimizer\OrEqualsToIn; +use OC\Files\Search\SearchBinaryOperator; +use OC\Files\Search\SearchComparison; +use OCP\Files\Search\ISearchBinaryOperator; +use OCP\Files\Search\ISearchComparison; +use Test\TestCase; + +class OrEqualsToInTest extends TestCase { + private $optimizer; + private $simplifier; + + protected function setUp(): void { + parent::setUp(); + + $this->optimizer = new OrEqualsToIn(); + $this->simplifier = new FlattenSingleArgumentBinaryOperation(); + } + + public function testOrs() { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "foo"), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "bar"), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "asd"), + ] + ); + $this->assertEquals('(path eq "foo" or path eq "bar" or path eq "asd")', $operator->__toString()); + + $this->optimizer->processOperator($operator); + $this->simplifier->processOperator($operator); + + $this->assertEquals('path in ["foo","bar","asd"]', $operator->__toString()); + } + + public function testOrsMultipleFields() { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "foo"), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "bar"), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "fileid", 1), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "fileid", 2), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "mimetype", "asd"), + ] + ); + $this->assertEquals('(path eq "foo" or path eq "bar" or fileid eq 1 or fileid eq 2 or mimetype eq "asd")', $operator->__toString()); + + $this->optimizer->processOperator($operator); + $this->simplifier->processOperator($operator); + + $this->assertEquals('(path in ["foo","bar"] or fileid in [1,2] or mimetype eq "asd")', $operator->__toString()); + } + + public function testPreserveHints() { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "foo"), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "bar"), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "asd"), + ] + ); + foreach ($operator->getArguments() as $argument) { + $argument->setQueryHint(ISearchComparison::HINT_PATH_EQ_HASH, false); + } + $this->assertEquals('(path eq "foo" or path eq "bar" or path eq "asd")', $operator->__toString()); + + $this->optimizer->processOperator($operator); + $this->simplifier->processOperator($operator); + + $this->assertEquals('path in ["foo","bar","asd"]', $operator->__toString()); + $this->assertEquals(false, $operator->getQueryHint(ISearchComparison::HINT_PATH_EQ_HASH, true)); + } + + public function testOrSomeEq() { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "foo"), + new SearchComparison(ISearchComparison::COMPARE_LIKE, "path", "foo%"), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "bar"), + ] + ); + $this->assertEquals('(path eq "foo" or path like "foo%" or path eq "bar")', $operator->__toString()); + + $this->optimizer->processOperator($operator); + $this->simplifier->processOperator($operator); + + $this->assertEquals('(path in ["foo","bar"] or path like "foo%")', $operator->__toString()); + } + + public function testOrsInside() { + $operator = new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_AND, + [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "mimetype", "text"), + new SearchBinaryOperator( + ISearchBinaryOperator::OPERATOR_OR, + [ + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "foo"), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "bar"), + new SearchComparison(ISearchComparison::COMPARE_EQUAL, "path", "asd"), + ] + ) + ] + ); + $this->assertEquals('(mimetype eq "text" and (path eq "foo" or path eq "bar" or path eq "asd"))', $operator->__toString()); + + $this->optimizer->processOperator($operator); + $this->simplifier->processOperator($operator); + + $this->assertEquals('(mimetype eq "text" and path in ["foo","bar","asd"])', $operator->__toString()); + } +} diff --git a/tests/lib/Files/Search/SearchIntegrationTest.php b/tests/lib/Files/Search/SearchIntegrationTest.php new file mode 100644 index 00000000000..74018a597d9 --- /dev/null +++ b/tests/lib/Files/Search/SearchIntegrationTest.php @@ -0,0 +1,44 @@ +<?php + +namespace Test\Files\Search; + +use OC\Files\Search\SearchBinaryOperator; +use OC\Files\Search\SearchComparison; +use OC\Files\Search\SearchQuery; +use OC\Files\Storage\Temporary; +use OCP\Files\Search\ISearchBinaryOperator; +use OCP\Files\Search\ISearchComparison; +use Test\TestCase; + +/** + * @group DB + */ +class SearchIntegrationTest extends TestCase { + private $cache; + private $storage; + + protected function setUp(): void { + parent::setUp(); + + $this->storage = new Temporary([]); + $this->cache = $this->storage->getCache(); + $this->storage->getScanner()->scan(''); + } + + + public function testThousandAndOneFilters() { + $id = $this->cache->put("file10", ['size' => 1, 'mtime' => 50, 'mimetype' => 'foo/folder']); + + $comparisons = []; + for($i = 1; $i <= 1001; $i++) { + $comparisons[] = new SearchComparison(ISearchComparison::COMPARE_EQUAL, "name", "file$i"); + } + $operator = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, $comparisons); + $query = new SearchQuery($operator, 10, 0, []); + + $results = $this->cache->searchQuery($query); + + $this->assertCount(1, $results); + $this->assertEquals($id, $results[0]->getId()); + } +} diff --git a/tests/lib/Files/Storage/CommonTest.php b/tests/lib/Files/Storage/CommonTest.php index 0900765c510..3740289df95 100644 --- a/tests/lib/Files/Storage/CommonTest.php +++ b/tests/lib/Files/Storage/CommonTest.php @@ -24,6 +24,7 @@ namespace Test\Files\Storage; use OC\Files\Storage\Wrapper\Jail; use OC\Files\Storage\Wrapper\Wrapper; +use OCP\Files\InvalidPathException; use PHPUnit\Framework\MockObject\MockObject; /** @@ -39,22 +40,66 @@ class CommonTest extends Storage { */ private $tmpDir; + private array $invalidCharsBackup; + protected function setUp(): void { parent::setUp(); $this->tmpDir = \OC::$server->getTempManager()->getTemporaryFolder(); $this->instance = new \OC\Files\Storage\CommonTest(['datadir' => $this->tmpDir]); + $this->invalidCharsBackup = \OC::$server->getConfig()->getSystemValue('forbidden_chars', []); } protected function tearDown(): void { \OC_Helper::rmdirr($this->tmpDir); + \OC::$server->getConfig()->setSystemValue('forbidden_chars', $this->invalidCharsBackup); parent::tearDown(); } + /** + * @dataProvider dataVerifyPath + */ + public function testVerifyPath(string $filename, array $additionalChars, bool $throws) { + /** @var \OC\Files\Storage\CommonTest|MockObject $instance */ + $instance = $this->getMockBuilder(\OC\Files\Storage\CommonTest::class) + ->onlyMethods(['copyFromStorage', 'rmdir', 'unlink']) + ->setConstructorArgs([['datadir' => $this->tmpDir]]) + ->getMock(); + $instance->method('copyFromStorage') + ->willThrowException(new \Exception('copy')); + + \OC::$server->getConfig()->setSystemValue('forbidden_chars', $additionalChars); + + if ($throws) { + $this->expectException(InvalidPathException::class); + } else { + $this->expectNotToPerformAssertions(); + } + $instance->verifyPath('/', $filename); + } + + public function dataVerifyPath(): array { + return [ + // slash is always forbidden + 'invalid slash' => ['a/b.txt', [], true], + // backslash is also forbidden + 'invalid backslash' => ['a\\b.txt', [], true], + // by default colon is not forbidden + 'valid name' => ['a: b.txt', [], false], + // colon can be added to the list of forbidden character + 'invalid custom character' => ['a: b.txt', [':'], true], + // make sure to not split the list entries as they migh contain Unicode sequences + // in this example the "face in clouds" emoji contains the clouds emoji so only having clouds is ok + 'valid unicode sequence' => ['🌫️.txt', ['😶🌫️'], false], + // This is the reverse: clouds are forbidden -> so is also the face in the clouds emoji + 'valid unicode sequence' => ['😶🌫️.txt', ['🌫️'], true], + ]; + } + public function testMoveFromStorageWrapped() { /** @var \OC\Files\Storage\CommonTest|MockObject $instance */ $instance = $this->getMockBuilder(\OC\Files\Storage\CommonTest::class) - ->setMethods(['copyFromStorage', 'rmdir', 'unlink']) + ->onlyMethods(['copyFromStorage', 'rmdir', 'unlink']) ->setConstructorArgs([['datadir' => $this->tmpDir]]) ->getMock(); $instance->method('copyFromStorage') @@ -72,7 +117,7 @@ class CommonTest extends Storage { public function testMoveFromStorageJailed() { /** @var \OC\Files\Storage\CommonTest|MockObject $instance */ $instance = $this->getMockBuilder(\OC\Files\Storage\CommonTest::class) - ->setMethods(['copyFromStorage', 'rmdir', 'unlink']) + ->onlyMethods(['copyFromStorage', 'rmdir', 'unlink']) ->setConstructorArgs([['datadir' => $this->tmpDir]]) ->getMock(); $instance->method('copyFromStorage') @@ -95,7 +140,7 @@ class CommonTest extends Storage { public function testMoveFromStorageNestedJail() { /** @var \OC\Files\Storage\CommonTest|MockObject $instance */ $instance = $this->getMockBuilder(\OC\Files\Storage\CommonTest::class) - ->setMethods(['copyFromStorage', 'rmdir', 'unlink']) + ->onlyMethods(['copyFromStorage', 'rmdir', 'unlink']) ->setConstructorArgs([['datadir' => $this->tmpDir]]) ->getMock(); $instance->method('copyFromStorage') diff --git a/tests/lib/Files/Storage/Storage.php b/tests/lib/Files/Storage/Storage.php index a646fd5fd0b..63b3dfc5a98 100644 --- a/tests/lib/Files/Storage/Storage.php +++ b/tests/lib/Files/Storage/Storage.php @@ -94,12 +94,13 @@ abstract class Storage extends \Test\TestCase { $dirEntry = $content[0]; unset($dirEntry['scan_permissions']); unset($dirEntry['etag']); + $this->assertLessThanOrEqual(1, abs($dirEntry['mtime'] - $this->instance->filemtime($directory))); + unset($dirEntry['mtime']); + unset($dirEntry['storage_mtime']); $this->assertEquals([ 'name' => $directory, 'mimetype' => $this->instance->getMimeType($directory), - 'mtime' => $this->instance->filemtime($directory), 'size' => -1, - 'storage_mtime' => $this->instance->filemtime($directory), 'permissions' => $this->instance->getPermissions($directory), ], $dirEntry); diff --git a/tests/lib/Files/Storage/Wrapper/EncryptionTest.php b/tests/lib/Files/Storage/Wrapper/EncryptionTest.php index ec815950d3c..be5f191c6eb 100644 --- a/tests/lib/Files/Storage/Wrapper/EncryptionTest.php +++ b/tests/lib/Files/Storage/Wrapper/EncryptionTest.php @@ -422,9 +422,9 @@ class EncryptionTest extends Storage { * @param boolean $renameKeysReturn */ public function testRename($source, - $target, - $encryptionEnabled, - $renameKeysReturn) { + $target, + $encryptionEnabled, + $renameKeysReturn) { if ($encryptionEnabled) { $this->keyStore ->expects($this->once()) @@ -790,10 +790,10 @@ class EncryptionTest extends Storage { ->method('isEnabled') ->willReturn($encryptionEnabled); // FIXME can not overwrite the return after definition -// $this->mount->expects($this->at(0)) -// ->method('getOption') -// ->with('encrypt', true) -// ->willReturn($mountPointEncryptionEnabled); + // $this->mount->expects($this->at(0)) + // ->method('getOption') + // ->with('encrypt', true) + // ->willReturn($mountPointEncryptionEnabled); global $mockedMountPointEncryptionEnabled; $mockedMountPointEncryptionEnabled = $mountPointEncryptionEnabled; diff --git a/tests/lib/Files/Stream/EncryptionTest.php b/tests/lib/Files/Stream/EncryptionTest.php index 8cdb379512a..3d45ff2fc3c 100644 --- a/tests/lib/Files/Stream/EncryptionTest.php +++ b/tests/lib/Files/Stream/EncryptionTest.php @@ -78,13 +78,13 @@ class EncryptionTest extends \Test\TestCase { * @dataProvider dataProviderStreamOpen() */ public function testStreamOpen($isMasterKeyUsed, - $mode, - $fullPath, - $fileExists, - $expectedSharePath, - $expectedSize, - $expectedUnencryptedSize, - $expectedReadOnly) { + $mode, + $fullPath, + $fileExists, + $expectedSharePath, + $expectedSize, + $expectedUnencryptedSize, + $expectedReadOnly) { // build mocks $encryptionModuleMock = $this->getMockBuilder('\OCP\Encryption\IEncryptionModule') ->disableOriginalConstructor()->getMock(); diff --git a/tests/lib/Files/Type/LoaderTest.php b/tests/lib/Files/Type/LoaderTest.php index fd3ec552dd2..ea016f9eded 100644 --- a/tests/lib/Files/Type/LoaderTest.php +++ b/tests/lib/Files/Type/LoaderTest.php @@ -23,15 +23,14 @@ namespace Test\Files\Type; use OC\Files\Type\Loader; use OCP\IDBConnection; +use Test\TestCase; -class LoaderTest extends \Test\TestCase { - /** @var IDBConnection */ - protected $db; - /** @var Loader */ - protected $loader; +class LoaderTest extends TestCase { + protected IDBConnection $db; + protected Loader $loader; protected function setUp(): void { - $this->db = \OC::$server->getDatabaseConnection(); + $this->db = \OC::$server->get(IDBConnection::class); $this->loader = new Loader($this->db); } diff --git a/tests/lib/Files/ViewTest.php b/tests/lib/Files/ViewTest.php index 18629f69a13..b9dd49d71fe 100644 --- a/tests/lib/Files/ViewTest.php +++ b/tests/lib/Files/ViewTest.php @@ -7,8 +7,6 @@ namespace Test\Files; -use OC\Share20\ShareDisableChecker; -use OCP\Cache\CappedMemoryCache; use OC\Files\Cache\Watcher; use OC\Files\Filesystem; use OC\Files\Mount\MountPoint; @@ -17,6 +15,8 @@ use OC\Files\Storage\Common; use OC\Files\Storage\Storage; use OC\Files\Storage\Temporary; use OC\Files\View; +use OC\Share20\ShareDisableChecker; +use OCP\Cache\CappedMemoryCache; use OCP\Constants; use OCP\Files\Config\IMountProvider; use OCP\Files\FileInfo; diff --git a/tests/lib/Group/ManagerTest.php b/tests/lib/Group/ManagerTest.php index 06875f86536..142532a5f4f 100644 --- a/tests/lib/Group/ManagerTest.php +++ b/tests/lib/Group/ManagerTest.php @@ -24,11 +24,11 @@ namespace Test\Group; use OC\Group\Database; -use OC\User\User; use OC\User\Manager; +use OC\User\User; use OCP\EventDispatcher\IEventDispatcher; -use OCP\GroupInterface; use OCP\Group\Backend\ISearchableGroupBackend; +use OCP\GroupInterface; use OCP\ICacheFactory; use OCP\IUser; use PHPUnit\Framework\MockObject\MockObject; diff --git a/tests/lib/Http/Client/ClientTest.php b/tests/lib/Http/Client/ClientTest.php index 3cef9d75986..0e6e265584e 100644 --- a/tests/lib/Http/Client/ClientTest.php +++ b/tests/lib/Http/Client/ClientTest.php @@ -149,6 +149,7 @@ class ClientTest extends \Test\TestCase { ['https://service.localhost'], ['!@#$', true], // test invalid url ['https://normal.host.com'], + ['https://com.one-.nextcloud-one.com'], ]; } diff --git a/tests/lib/Http/Client/DnsPinMiddlewareTest.php b/tests/lib/Http/Client/DnsPinMiddlewareTest.php index 2fa868e6963..54071f37b1a 100644 --- a/tests/lib/Http/Client/DnsPinMiddlewareTest.php +++ b/tests/lib/Http/Client/DnsPinMiddlewareTest.php @@ -287,7 +287,7 @@ class DnsPinMiddlewareTest extends TestCase { public function testRejectIPv4() { $this->expectException(LocalServerException::class); - $this->expectExceptionMessage('Host violates local access rules'); + $this->expectExceptionMessage('violates local access rules'); $mockHandler = new MockHandler([ static function (RequestInterface $request, array $options) { @@ -334,7 +334,7 @@ class DnsPinMiddlewareTest extends TestCase { public function testRejectIPv6() { $this->expectException(LocalServerException::class); - $this->expectExceptionMessage('Host violates local access rules'); + $this->expectExceptionMessage('violates local access rules'); $mockHandler = new MockHandler([ static function (RequestInterface $request, array $options) { @@ -381,7 +381,7 @@ class DnsPinMiddlewareTest extends TestCase { public function testRejectCanonicalName() { $this->expectException(LocalServerException::class); - $this->expectExceptionMessage('Host violates local access rules'); + $this->expectExceptionMessage('violates local access rules'); $mockHandler = new MockHandler([ static function (RequestInterface $request, array $options) { diff --git a/tests/lib/InitialStateServiceTest.php b/tests/lib/InitialStateServiceTest.php index 554478e123f..5af09afb4c0 100644 --- a/tests/lib/InitialStateServiceTest.php +++ b/tests/lib/InitialStateServiceTest.php @@ -25,14 +25,14 @@ declare(strict_types=1); namespace Test; +use JsonSerializable; use OC\AppFramework\Bootstrap\Coordinator; +use OC\InitialStateService; use OCP\IServerContainer; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; -use function json_encode; -use JsonSerializable; -use OC\InitialStateService; use stdClass; +use function json_encode; class InitialStateServiceTest extends TestCase { /** @var InitialStateService */ diff --git a/tests/lib/InstallerTest.php b/tests/lib/InstallerTest.php index 4e622dca6d1..5df3e2eafa9 100644 --- a/tests/lib/InstallerTest.php +++ b/tests/lib/InstallerTest.php @@ -51,7 +51,7 @@ class InstallerTest extends TestCase { $this->appstore = $config->setSystemValue('appstoreenabled', true); $config->setSystemValue('appstoreenabled', true); $installer = new Installer( - \OC::$server->getAppFetcher(), + \OC::$server->get(AppFetcher::class), \OC::$server->getHTTPClientService(), \OC::$server->getTempManager(), \OC::$server->get(LoggerInterface::class), @@ -74,7 +74,7 @@ class InstallerTest extends TestCase { protected function tearDown(): void { $installer = new Installer( - \OC::$server->getAppFetcher(), + \OC::$server->get(AppFetcher::class), \OC::$server->getHTTPClientService(), \OC::$server->getTempManager(), \OC::$server->get(LoggerInterface::class), @@ -98,7 +98,7 @@ class InstallerTest extends TestCase { // Install app $installer = new Installer( - \OC::$server->getAppFetcher(), + \OC::$server->get(AppFetcher::class), \OC::$server->getHTTPClientService(), \OC::$server->getTempManager(), \OC::$server->get(LoggerInterface::class), diff --git a/tests/lib/IntegrityCheck/CheckerTest.php b/tests/lib/IntegrityCheck/CheckerTest.php index 203e7e97227..1cfbe9907a9 100644 --- a/tests/lib/IntegrityCheck/CheckerTest.php +++ b/tests/lib/IntegrityCheck/CheckerTest.php @@ -28,6 +28,7 @@ use OC\IntegrityCheck\Helpers\EnvironmentHelper; use OC\IntegrityCheck\Helpers\FileAccessHelper; use OC\Memcache\NullCache; use OCP\App\IAppManager; +use OCP\IAppConfig; use OCP\ICacheFactory; use OCP\IConfig; use phpseclib\Crypt\RSA; @@ -45,6 +46,8 @@ class CheckerTest extends TestCase { private $fileAccessHelper; /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */ private $config; + /** @var IAppConfig|\PHPUnit\Framework\MockObject\MockObject */ + private $appConfig; /** @var ICacheFactory|\PHPUnit\Framework\MockObject\MockObject */ private $cacheFactory; /** @var IAppManager|\PHPUnit\Framework\MockObject\MockObject */ @@ -58,6 +61,7 @@ class CheckerTest extends TestCase { $this->fileAccessHelper = $this->createMock(FileAccessHelper::class); $this->appLocator = $this->createMock(AppLocator::class); $this->config = $this->createMock(IConfig::class); + $this->appConfig = $this->createMock(IAppConfig::class); $this->cacheFactory = $this->createMock(ICacheFactory::class); $this->appManager = $this->createMock(IAppManager::class); $this->mimeTypeDetector = $this->createMock(\OC\Files\Type\Detection::class); @@ -76,6 +80,7 @@ class CheckerTest extends TestCase { $this->fileAccessHelper, $this->appLocator, $this->config, + $this->appConfig, $this->cacheFactory, $this->appManager, $this->mimeTypeDetector @@ -1025,6 +1030,7 @@ class CheckerTest extends TestCase { $this->fileAccessHelper, $this->appLocator, $this->config, + $this->appConfig, $this->cacheFactory, $this->appManager, $this->mimeTypeDetector, @@ -1089,9 +1095,9 @@ class CheckerTest extends TestCase { true, false, ); - $this->config + $this->appConfig ->expects($this->once()) - ->method('deleteAppValue') + ->method('deleteKey') ->with('core', 'oc.integritycheck.checker'); $this->checker->runInstanceVerification(); diff --git a/tests/lib/L10N/L10nTest.php b/tests/lib/L10N/L10nTest.php index bd1fce29547..9817f8b0141 100644 --- a/tests/lib/L10N/L10nTest.php +++ b/tests/lib/L10N/L10nTest.php @@ -201,12 +201,12 @@ class L10nTest extends TestCase { } public function testServiceGetLanguageCode() { - $l = \OC::$server->getL10N('lib', 'de'); + $l = \OCP\Util::getL10N('lib', 'de'); $this->assertEquals('de', $l->getLanguageCode()); } public function testWeekdayName() { - $l = \OC::$server->getL10N('lib', 'de'); + $l = \OCP\Util::getL10N('lib', 'de'); $this->assertEquals('Mo.', $l->l('weekdayName', new \DateTime('2017-11-6'), ['width' => 'abbreviated'])); } diff --git a/tests/lib/Lock/MemcacheLockingProviderTest.php b/tests/lib/Lock/MemcacheLockingProviderTest.php index b67be799d59..95001ec03a3 100644 --- a/tests/lib/Lock/MemcacheLockingProviderTest.php +++ b/tests/lib/Lock/MemcacheLockingProviderTest.php @@ -22,6 +22,7 @@ namespace Test\Lock; use OC\Memcache\ArrayCache; +use OCP\AppFramework\Utility\ITimeFactory; class MemcacheLockingProviderTest extends LockingProvider { /** @@ -34,7 +35,8 @@ class MemcacheLockingProviderTest extends LockingProvider { */ protected function getInstance() { $this->memcache = new ArrayCache(); - return new \OC\Lock\MemcacheLockingProvider($this->memcache); + $timeProvider = \OC::$server->get(ITimeFactory::class); + return new \OC\Lock\MemcacheLockingProvider($this->memcache, $timeProvider); } protected function tearDown(): void { diff --git a/tests/lib/Mail/MailerTest.php b/tests/lib/Mail/MailerTest.php index 5ffba939284..0eee7db126a 100644 --- a/tests/lib/Mail/MailerTest.php +++ b/tests/lib/Mail/MailerTest.php @@ -21,13 +21,13 @@ use OCP\IL10N; use OCP\IURLGenerator; use OCP\L10N\IFactory; use OCP\Mail\Events\BeforeMessageSent; -use Psr\Log\LoggerInterface; -use Test\TestCase; use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; use Symfony\Component\Mailer\Mailer as SymfonyMailer; -use Symfony\Component\Mime\Email; -use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport; use Symfony\Component\Mailer\Transport\SendmailTransport; +use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport; +use Symfony\Component\Mime\Email; +use Test\TestCase; class MailerTest extends TestCase { /** @var IConfig|MockObject */ @@ -72,7 +72,7 @@ class MailerTest extends TestCase { public function sendmailModeProvider(): array { return [ 'smtp' => ['smtp', ' -bs'], - 'pipe' => ['pipe', ' -t'], + 'pipe' => ['pipe', ' -t -i'], ]; } diff --git a/tests/lib/Mail/MessageTest.php b/tests/lib/Mail/MessageTest.php index 2becc4d2081..1d309ca742d 100644 --- a/tests/lib/Mail/MessageTest.php +++ b/tests/lib/Mail/MessageTest.php @@ -11,13 +11,13 @@ namespace Test\Mail; use OC\Mail\Message; use OCP\Mail\Headers\AutoSubmitted; use OCP\Mail\IEMailTemplate; +use PHPUnit\Framework\MockObject\MockObject; use Symfony\Component\Mime\Address; use Symfony\Component\Mime\Email; use Symfony\Component\Mime\Exception\RfcComplianceException; use Symfony\Component\Mime\Header\HeaderInterface; use Symfony\Component\Mime\Header\Headers; use Test\TestCase; -use PHPUnit\Framework\MockObject\MockObject; class MessageTest extends TestCase { /** @var Email */ diff --git a/tests/lib/Memcache/FactoryTest.php b/tests/lib/Memcache/FactoryTest.php index 5f13a94eacd..e8bf519e1a9 100644 --- a/tests/lib/Memcache/FactoryTest.php +++ b/tests/lib/Memcache/FactoryTest.php @@ -22,8 +22,8 @@ namespace Test\Memcache; use OC\Memcache\NullCache; -use Psr\Log\LoggerInterface; use OCP\Profiler\IProfiler; +use Psr\Log\LoggerInterface; class Test_Factory_Available_Cache1 extends NullCache { public function __construct($prefix = '') { diff --git a/tests/lib/Memcache/RedisTest.php b/tests/lib/Memcache/RedisTest.php index b94b69a5e6a..27c6fc11ee8 100644 --- a/tests/lib/Memcache/RedisTest.php +++ b/tests/lib/Memcache/RedisTest.php @@ -9,11 +9,18 @@ namespace Test\Memcache; +use OC\Memcache\Redis; + /** * @group Memcache * @group Redis */ class RedisTest extends Cache { + /** + * @var Redis cache; + */ + protected $instance; + public static function setUpBeforeClass(): void { parent::setUpBeforeClass(); @@ -62,4 +69,18 @@ class RedisTest extends Cache { $this->assertEquals(sha1($script[0]), $script[1]); } } + + public function testCasTtlNotChanged() { + $this->instance->set('foo', 'bar', 50); + $this->assertTrue($this->instance->compareSetTTL('foo', 'bar', 100)); + // allow for 1s of inaccuracy due to time moving forward + $this->assertLessThan(1, 100 - $this->instance->getTTL('foo')); + } + + public function testCasTtlChanged() { + $this->instance->set('foo', 'bar1', 50); + $this->assertFalse($this->instance->compareSetTTL('foo', 'bar', 100)); + // allow for 1s of inaccuracy due to time moving forward + $this->assertLessThan(1, 50 - $this->instance->getTTL('foo')); + } } diff --git a/tests/lib/Migration/BackgroundRepairTest.php b/tests/lib/Migration/BackgroundRepairTest.php index 2e68924a96d..95cd5419f30 100644 --- a/tests/lib/Migration/BackgroundRepairTest.php +++ b/tests/lib/Migration/BackgroundRepairTest.php @@ -21,15 +21,15 @@ namespace Test\Migration; -use OCP\AppFramework\Utility\ITimeFactory; -use OCP\EventDispatcher\IEventDispatcher; -use OCP\Migration\IOutput; -use OCP\Migration\IRepairStep; use OC\BackgroundJob\JobList; use OC\Migration\BackgroundRepair; use OC\NeedsUpdateException; +use OC\Repair; use OC\Repair\Events\RepairStepEvent; -use PHPUnit\Framework\MockObject\MockObject; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; use Psr\Log\LoggerInterface; use Test\TestCase; @@ -57,20 +57,12 @@ class TestRepairStep implements IRepairStep { } class BackgroundRepairTest extends TestCase { - /** @var JobList|MockObject */ - private $jobList; - - /** @var BackgroundRepair|MockObject */ - private $job; - - /** @var LoggerInterface|MockObject */ - private $logger; - - /** @var IEventDispatcher|MockObject $dispatcher */ - private $dispatcher; - - /** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject $dispatcher */ - private $time; + private JobList $jobList; + private BackgroundRepair $job; + private LoggerInterface $logger; + private IEventDispatcher $dispatcher; + private ITimeFactory $time; + private Repair $repair; protected function setUp(): void { parent::setUp(); @@ -85,8 +77,9 @@ class BackgroundRepairTest extends TestCase { $this->time = $this->createMock(ITimeFactory::class); $this->time->method('getTime') ->willReturn(999999); + $this->repair = new Repair($this->dispatcher, $this->logger); $this->job = $this->getMockBuilder(BackgroundRepair::class) - ->setConstructorArgs([$this->dispatcher, $this->time, $this->logger, $this->jobList]) + ->setConstructorArgs([$this->repair, $this->time, $this->logger, $this->jobList]) ->setMethods(['loadApp']) ->getMock(); } diff --git a/tests/lib/NavigationManagerTest.php b/tests/lib/NavigationManagerTest.php index 8edf7ecb9cc..beefc2353d6 100644 --- a/tests/lib/NavigationManagerTest.php +++ b/tests/lib/NavigationManagerTest.php @@ -224,19 +224,19 @@ class NavigationManagerTest extends TestCase { ->method('isEnabledForUser') ->with('theming') ->willReturn(true); - $this->appManager->expects($this->once())->method('getAppInfo')->with('test')->willReturn($navigation); - /* + $this->appManager->expects($this->once()) + ->method('getAppInfo') + ->with('test') + ->willReturn($navigation); + $this->urlGenerator->expects($this->any()) + ->method('imagePath') + ->willReturnCallback(function ($appName, $file) { + return "/apps/$appName/img/$file"; + }); $this->appManager->expects($this->any()) - ->method('getAppInfo') - ->will($this->returnValueMap([ - ['test', null, null, $navigation], - ['theming', null, null, null], - ])); - */ + ->method('getAppIcon') + ->willReturnCallback(fn (string $appName) => "/apps/$appName/img/app.svg"); $this->l10nFac->expects($this->any())->method('get')->willReturn($l); - $this->urlGenerator->expects($this->any())->method('imagePath')->willReturnCallback(function ($appName, $file) { - return "/apps/$appName/img/$file"; - }); $this->urlGenerator->expects($this->any())->method('linkToRoute')->willReturnCallback(function ($route) { if ($route === 'core.login.logout') { return 'https://example.com/logout'; @@ -365,7 +365,6 @@ class NavigationManagerTest extends TestCase { 'unread' => 0, 'default' => true, 'app' => 'test', - 'key' => 0, ]], ['logout' => $defaults['logout']] ), @@ -416,7 +415,6 @@ class NavigationManagerTest extends TestCase { 'unread' => 0, 'default' => false, 'app' => 'test', - 'key' => 0, ], 'test1' => [ 'id' => 'test1', @@ -430,7 +428,6 @@ class NavigationManagerTest extends TestCase { 'unread' => 0, 'default' => true, // because of order 'app' => 'test', - 'key' => 1, ]], ['logout' => $defaults['logout']] ), @@ -458,7 +455,6 @@ class NavigationManagerTest extends TestCase { 'unread' => 0, 'default' => true, 'app' => 'test', - 'key' => 0, ]], ['logout' => $defaults['logout']] ), @@ -514,7 +510,6 @@ class NavigationManagerTest extends TestCase { 'unread' => 0, 'default' => true, 'app' => 'test', - 'key' => 0, ], ]; $navigation = ['navigations' => [ @@ -528,7 +523,7 @@ class NavigationManagerTest extends TestCase { function (string $userId, string $appName, string $key, mixed $default = '') use ($testOrder) { $this->assertEquals('user001', $userId); if ($key === 'apporder') { - return json_encode(['test' => [$testOrder]]); + return json_encode(['test' => ['app' => 'test', 'order' => $testOrder]]); } return $default; } @@ -539,6 +534,7 @@ class NavigationManagerTest extends TestCase { ->with('theming') ->willReturn(true); $this->appManager->expects($this->once())->method('getAppInfo')->with('test')->willReturn($navigation); + $this->appManager->expects($this->once())->method('getAppIcon')->with('test')->willReturn('/apps/test/img/app.svg'); $this->l10nFac->expects($this->any())->method('get')->willReturn($l); $this->urlGenerator->expects($this->any())->method('imagePath')->willReturnCallback(function ($appName, $file) { return "/apps/$appName/img/$file"; diff --git a/tests/lib/Repair/RepairDavSharesTest.php b/tests/lib/Repair/RepairDavSharesTest.php index 394fc985469..0e9fdca7cdd 100644 --- a/tests/lib/Repair/RepairDavSharesTest.php +++ b/tests/lib/Repair/RepairDavSharesTest.php @@ -32,9 +32,9 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IConfig; use OCP\IDBConnection; use OCP\IGroupManager; +use OCP\Migration\IOutput; use Psr\Log\LoggerInterface; use Test\TestCase; -use OCP\Migration\IOutput; use function in_array; class RepairDavSharesTest extends TestCase { diff --git a/tests/lib/RepairTest.php b/tests/lib/RepairTest.php index 1a2fd620e49..0708f6dc046 100644 --- a/tests/lib/RepairTest.php +++ b/tests/lib/RepairTest.php @@ -8,13 +8,13 @@ namespace Test; -use OCP\EventDispatcher\IEventDispatcher; -use OCP\Migration\IRepairStep; use OC\Repair; +use OC\Repair\Events\RepairErrorEvent; use OC\Repair\Events\RepairInfoEvent; use OC\Repair\Events\RepairStepEvent; use OC\Repair\Events\RepairWarningEvent; -use OC\Repair\Events\RepairErrorEvent; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\Migration\IRepairStep; use Psr\Log\LoggerInterface; class TestRepairStep implements IRepairStep { @@ -46,7 +46,7 @@ class RepairTest extends TestCase { protected function setUp(): void { parent::setUp(); $dispatcher = \OC::$server->get(IEventDispatcher::class); - $this->repair = new \OC\Repair([], $dispatcher, $this->createMock(LoggerInterface::class)); + $this->repair = new Repair($dispatcher, $this->createMock(LoggerInterface::class)); $dispatcher->addListener(RepairWarningEvent::class, function (RepairWarningEvent $event) { $this->outputArray[] = 'warning: ' . $event->getMessage(); diff --git a/tests/lib/Security/Bruteforce/ThrottlerTest.php b/tests/lib/Security/Bruteforce/ThrottlerTest.php index e368a0912b1..473aa374d39 100644 --- a/tests/lib/Security/Bruteforce/ThrottlerTest.php +++ b/tests/lib/Security/Bruteforce/ThrottlerTest.php @@ -160,9 +160,9 @@ class ThrottlerTest extends TestCase { * @param bool $enabled */ private function isIpWhiteListedHelper($ip, - $whitelists, - $isWhiteListed, - $enabled) { + $whitelists, + $isWhiteListed, + $enabled) { $this->config->method('getAppKeys') ->with($this->equalTo('bruteForce')) ->willReturn(array_keys($whitelists)); @@ -197,8 +197,8 @@ class ThrottlerTest extends TestCase { * @param bool $isWhiteListed */ public function testIsIpWhiteListedWithEnabledProtection($ip, - $whitelists, - $isWhiteListed) { + $whitelists, + $isWhiteListed) { $this->isIpWhiteListedHelper( $ip, $whitelists, @@ -215,8 +215,8 @@ class ThrottlerTest extends TestCase { * @param bool $isWhiteListed */ public function testIsIpWhiteListedWithDisabledProtection($ip, - $whitelists, - $isWhiteListed) { + $whitelists, + $isWhiteListed) { $this->isIpWhiteListedHelper( $ip, $whitelists, diff --git a/tests/lib/Security/CertificateManagerTest.php b/tests/lib/Security/CertificateManagerTest.php index 6ad8231a61d..08cbfa9e221 100644 --- a/tests/lib/Security/CertificateManagerTest.php +++ b/tests/lib/Security/CertificateManagerTest.php @@ -146,9 +146,9 @@ class CertificateManagerTest extends \Test\TestCase { * @param bool $expected */ public function testNeedRebundling($CaBundleMtime, - $targetBundleMtime, - $targetBundleExists, - $expected + $targetBundleMtime, + $targetBundleExists, + $expected ) { $view = $this->getMockBuilder(View::class) ->disableOriginalConstructor()->getMock(); diff --git a/tests/lib/Security/RemoteHostValidatorTest.php b/tests/lib/Security/RemoteHostValidatorTest.php index 030a75b1e79..b1371d9343c 100644 --- a/tests/lib/Security/RemoteHostValidatorTest.php +++ b/tests/lib/Security/RemoteHostValidatorTest.php @@ -60,8 +60,17 @@ class RemoteHostValidatorTest extends TestCase { ); } - public function testValid(): void { - $host = 'nextcloud.com'; + public function dataValid(): array { + return [ + ['nextcloud.com', true], + ['com.one-.nextcloud-one.com', false], + ]; + } + + /** + * @dataProvider dataValid + */ + public function testValid(string $host, bool $expected): void { $this->hostnameClassifier ->method('isLocalHostname') ->with($host) @@ -73,7 +82,7 @@ class RemoteHostValidatorTest extends TestCase { $valid = $this->validator->isValid($host); - self::assertTrue($valid); + self::assertSame($expected, $valid); } public function testLocalHostname(): void { diff --git a/tests/lib/Security/VerificationToken/VerificationTokenTest.php b/tests/lib/Security/VerificationToken/VerificationTokenTest.php index a71d2b09f71..182bd533f84 100644 --- a/tests/lib/Security/VerificationToken/VerificationTokenTest.php +++ b/tests/lib/Security/VerificationToken/VerificationTokenTest.php @@ -34,8 +34,8 @@ use OCP\IUser; use OCP\Security\ICrypto; use OCP\Security\ISecureRandom; use OCP\Security\VerificationToken\InvalidTokenException; -use Test\TestCase; use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; class VerificationTokenTest extends TestCase { /** @var VerificationToken */ diff --git a/tests/lib/Settings/DeclarativeManagerTest.php b/tests/lib/Settings/DeclarativeManagerTest.php new file mode 100644 index 00000000000..b6f3ce44c15 --- /dev/null +++ b/tests/lib/Settings/DeclarativeManagerTest.php @@ -0,0 +1,538 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Andrey Borysenko <andrey.borysenko@nextcloud.com> + * + * @author Andrey Borysenko <andrey.borysenko@nextcloud.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace Test\Settings; + +use OC\AppFramework\Bootstrap\Coordinator; +use OC\Settings\DeclarativeManager; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\IAppConfig; +use OCP\IConfig; +use OCP\IGroupManager; +use OCP\IUser; +use OCP\Settings\DeclarativeSettingsTypes; +use OCP\Settings\Events\DeclarativeSettingsSetValueEvent; +use OCP\Settings\IDeclarativeManager; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use Test\TestCase; + +class DeclarativeManagerTest extends TestCase { + + /** @var IDeclarativeManager|MockObject */ + private $declarativeManager; + + /** @var IEventDispatcher|MockObject */ + private $eventDispatcher; + + /** @var IGroupManager|MockObject */ + private $groupManager; + + /** @var Coordinator|MockObject */ + private $coordinator; + + /** @var IConfig|MockObject */ + private $config; + + /** @var IAppConfig|MockObject */ + private $appConfig; + + /** @var LoggerInterface|MockObject */ + private $logger; + + /** @var IUser|MockObject */ + private $user; + + /** @var IUser|MockObject */ + private $adminUser; + + public const validSchemaAllFields = [ + 'id' => 'test_form_1', + 'priority' => 10, + 'section_type' => DeclarativeSettingsTypes::SECTION_TYPE_ADMIN, // admin, personal + 'section_id' => 'additional', + 'storage_type' => DeclarativeSettingsTypes::STORAGE_TYPE_INTERNAL, // external, internal (handled by core to store in appconfig and preferences) + 'title' => 'Test declarative settings', // NcSettingsSection name + 'description' => 'These fields are rendered dynamically from declarative schema', // NcSettingsSection description + 'doc_url' => '', // NcSettingsSection doc_url for documentation or help page, empty string if not needed + 'fields' => [ + [ + 'id' => 'test_field_7', // configkey + 'title' => 'Multi-selection', // name or label + 'description' => 'Select some option setting', // hint + 'type' => DeclarativeSettingsTypes::MULTI_SELECT, + 'options' => ['foo', 'bar', 'baz'], // simple options for select, radio, multi-select + 'placeholder' => 'Select some multiple options', // input placeholder + 'default' => ['foo', 'bar'], + ], + [ + 'id' => 'some_real_setting', + 'title' => 'Select single option', + 'description' => 'Single option radio buttons', + 'type' => DeclarativeSettingsTypes::RADIO, // radio (NcCheckboxRadioSwitch type radio) + 'placeholder' => 'Select single option, test interval', + 'default' => '40m', + 'options' => [ + [ + 'name' => 'Each 40 minutes', // NcCheckboxRadioSwitch display name + 'value' => '40m' // NcCheckboxRadioSwitch value + ], + [ + 'name' => 'Each 60 minutes', + 'value' => '60m' + ], + [ + 'name' => 'Each 120 minutes', + 'value' => '120m' + ], + [ + 'name' => 'Each day', + 'value' => 60 * 24 . 'm' + ], + ], + ], + [ + 'id' => 'test_field_1', // configkey + 'title' => 'Default text field', // label + 'description' => 'Set some simple text setting', // hint + 'type' => DeclarativeSettingsTypes::TEXT, + 'placeholder' => 'Enter text setting', // placeholder + 'default' => 'foo', + ], + [ + 'id' => 'test_field_1_1', + 'title' => 'Email field', + 'description' => 'Set email config', + 'type' => DeclarativeSettingsTypes::EMAIL, + 'placeholder' => 'Enter email', + 'default' => '', + ], + [ + 'id' => 'test_field_1_2', + 'title' => 'Tel field', + 'description' => 'Set tel config', + 'type' => DeclarativeSettingsTypes::TEL, + 'placeholder' => 'Enter your tel', + 'default' => '', + ], + [ + 'id' => 'test_field_1_3', + 'title' => 'Url (website) field', + 'description' => 'Set url config', + 'type' => 'url', + 'placeholder' => 'Enter url', + 'default' => '', + ], + [ + 'id' => 'test_field_1_4', + 'title' => 'Number field', + 'description' => 'Set number config', + 'type' => DeclarativeSettingsTypes::NUMBER, + 'placeholder' => 'Enter number value', + 'default' => 0, + ], + [ + 'id' => 'test_field_2', + 'title' => 'Password', + 'description' => 'Set some secure value setting', + 'type' => 'password', + 'placeholder' => 'Set secure value', + 'default' => '', + ], + [ + 'id' => 'test_field_3', + 'title' => 'Selection', + 'description' => 'Select some option setting', + 'type' => DeclarativeSettingsTypes::SELECT, + 'options' => ['foo', 'bar', 'baz'], + 'placeholder' => 'Select some option setting', + 'default' => 'foo', + ], + [ + 'id' => 'test_field_4', + 'title' => 'Toggle something', + 'description' => 'Select checkbox option setting', + 'type' => DeclarativeSettingsTypes::CHECKBOX, + 'label' => 'Verify something if enabled', + 'default' => false, + ], + [ + 'id' => 'test_field_5', + 'title' => 'Multiple checkbox toggles, describing one setting, checked options are saved as an JSON object {foo: true, bar: false}', + 'description' => 'Select checkbox option setting', + 'type' => DeclarativeSettingsTypes::MULTI_CHECKBOX, + 'default' => ['foo' => true, 'bar' => true], + 'options' => [ + [ + 'name' => 'Foo', + 'value' => 'foo', // multiple-checkbox configkey + ], + [ + 'name' => 'Bar', + 'value' => 'bar', + ], + [ + 'name' => 'Baz', + 'value' => 'baz', + ], + [ + 'name' => 'Qux', + 'value' => 'qux', + ], + ], + ], + [ + 'id' => 'test_field_6', + 'title' => 'Radio toggles, describing one setting like single select', + 'description' => 'Select radio option setting', + 'type' => DeclarativeSettingsTypes::RADIO, // radio (NcCheckboxRadioSwitch type radio) + 'label' => 'Select single toggle', + 'default' => 'foo', + 'options' => [ + [ + 'name' => 'First radio', // NcCheckboxRadioSwitch display name + 'value' => 'foo' // NcCheckboxRadioSwitch value + ], + [ + 'name' => 'Second radio', + 'value' => 'bar' + ], + [ + 'name' => 'Second radio', + 'value' => 'baz' + ], + ], + ], + ], + ]; + + public static bool $testSetInternalValueAfterChange = false; + + protected function setUp(): void { + parent::setUp(); + + $this->eventDispatcher = $this->createMock(IEventDispatcher::class); + $this->groupManager = $this->createMock(IGroupManager::class); + $this->coordinator = $this->createMock(Coordinator::class); + $this->config = $this->createMock(IConfig::class); + $this->appConfig = $this->createMock(IAppConfig::class); + $this->logger = $this->createMock(LoggerInterface::class); + + $this->declarativeManager = new DeclarativeManager( + $this->eventDispatcher, + $this->groupManager, + $this->coordinator, + $this->config, + $this->appConfig, + $this->logger + ); + + $this->user = $this->createMock(IUser::class); + $this->user->expects($this->any()) + ->method('getUID') + ->willReturn('test_user'); + + $this->adminUser = $this->createMock(IUser::class); + $this->adminUser->expects($this->any()) + ->method('getUID') + ->willReturn('admin_test_user'); + + $this->groupManager->expects($this->any()) + ->method('isAdmin') + ->willReturnCallback(function ($userId) { + return $userId === 'admin_test_user'; + }); + } + + public function testRegisterSchema(): void { + $app = 'testing'; + $schema = self::validSchemaAllFields; + $this->declarativeManager->registerSchema($app, $schema); + $formIds = $this->declarativeManager->getFormIDs($this->adminUser, $schema['section_type'], $schema['section_id']); + $this->assertTrue(isset($formIds[$app]) && in_array($schema['id'], $formIds[$app])); + } + + /** + * Simple test to verify that exception is thrown when trying to register schema with duplicate id + */ + public function testRegisterDuplicateSchema(): void { + $this->declarativeManager->registerSchema('testing', self::validSchemaAllFields); + $this->expectException(\Exception::class); + $this->declarativeManager->registerSchema('testing', self::validSchemaAllFields); + } + + /** + * It's not allowed to register schema with duplicate fields ids for the same app + */ + public function testRegisterSchemaWithDuplicateFields(): void { + // Register first valid schema + $this->declarativeManager->registerSchema('testing', self::validSchemaAllFields); + // Register second schema with duplicate fields, but different schema id + $this->expectException(\Exception::class); + $schema = self::validSchemaAllFields; + $schema['id'] = 'test_form_2'; + $this->declarativeManager->registerSchema('testing', $schema); + } + + public function testRegisterMultipleSchemasAndDuplicate(): void { + $app = 'testing'; + $schema = self::validSchemaAllFields; + $this->declarativeManager->registerSchema($app, $schema); + $formIds = $this->declarativeManager->getFormIDs($this->adminUser, $schema['section_type'], $schema['section_id']); + // 1. Check that form is registered for the app + $this->assertTrue(isset($formIds[$app]) && in_array($schema['id'], $formIds[$app])); + $app = 'testing2'; + $this->declarativeManager->registerSchema($app, $schema); + $formIds = $this->declarativeManager->getFormIDs($this->adminUser, $schema['section_type'], $schema['section_id']); + // 2. Check that form is registered for the second app + $this->assertTrue(isset($formIds[$app]) && in_array($schema['id'], $formIds[$app])); + $app = 'testing'; + $this->expectException(\Exception::class); // expecting duplicate form id and duplicate fields ids exception + $this->declarativeManager->registerSchema($app, $schema); + $schemaDuplicateFields = self::validSchemaAllFields; + $schemaDuplicateFields['id'] = 'test_form_2'; // change form id to test duplicate fields + $this->declarativeManager->registerSchema($app, $schemaDuplicateFields); + // 3. Check that not valid form with duplicate fields is not registered + $formIds = $this->declarativeManager->getFormIDs($this->adminUser, $schemaDuplicateFields['section_type'], $schemaDuplicateFields['section_id']); + $this->assertFalse(isset($formIds[$app]) && in_array($schemaDuplicateFields['id'], $formIds[$app])); + } + + /** + * @dataProvider dataValidateSchema + */ + public function testValidateSchema(bool $expected, bool $expectException, string $app, array $schema): void { + if ($expectException) { + $this->expectException(\Exception::class); + } + $this->declarativeManager->registerSchema($app, $schema); + $formIds = $this->declarativeManager->getFormIDs($this->adminUser, $schema['section_type'], $schema['section_id']); + $this->assertEquals($expected, isset($formIds[$app]) && in_array($schema['id'], $formIds[$app])); + } + + public static function dataValidateSchema(): array { + return [ + 'valid schema with all supported fields' => [ + true, + false, + 'testing', + self::validSchemaAllFields, + ], + 'invalid schema with missing id' => [ + false, + true, + 'testing', + [ + 'priority' => 10, + 'section_type' => DeclarativeSettingsTypes::SECTION_TYPE_ADMIN, + 'section_id' => 'additional', + 'storage_type' => DeclarativeSettingsTypes::STORAGE_TYPE_INTERNAL, + 'title' => 'Test declarative settings', + 'description' => 'These fields are rendered dynamically from declarative schema', + 'doc_url' => '', + 'fields' => [ + [ + 'id' => 'test_field_7', + 'title' => 'Multi-selection', + 'description' => 'Select some option setting', + 'type' => DeclarativeSettingsTypes::MULTI_SELECT, + 'options' => ['foo', 'bar', 'baz'], + 'placeholder' => 'Select some multiple options', + 'default' => ['foo', 'bar'], + ], + ], + ], + ], + 'invalid schema with invalid field' => [ + false, + true, + 'testing', + [ + 'id' => 'test_form_1', + 'priority' => 10, + 'section_type' => DeclarativeSettingsTypes::SECTION_TYPE_ADMIN, + 'section_id' => 'additional', + 'storage_type' => DeclarativeSettingsTypes::STORAGE_TYPE_INTERNAL, + 'title' => 'Test declarative settings', + 'description' => 'These fields are rendered dynamically from declarative schema', + 'doc_url' => '', + 'fields' => [ + [ + 'id' => 'test_invalid_field', + 'title' => 'Invalid field', + 'description' => 'Some invalid setting description', + 'type' => 'some_invalid_type', + 'placeholder' => 'Some invalid field placeholder', + 'default' => null, + ], + ], + ], + ], + ]; + } + + public function testGetFormIDs(): void { + $app = 'testing'; + $schema = self::validSchemaAllFields; + $this->declarativeManager->registerSchema($app, $schema); + $formIds = $this->declarativeManager->getFormIDs($this->adminUser, $schema['section_type'], $schema['section_id']); + $this->assertTrue(isset($formIds[$app]) && in_array($schema['id'], $formIds[$app])); + $app = 'testing2'; + $this->declarativeManager->registerSchema($app, $schema); + $formIds = $this->declarativeManager->getFormIDs($this->adminUser, $schema['section_type'], $schema['section_id']); + $this->assertTrue(isset($formIds[$app]) && in_array($schema['id'], $formIds[$app])); + } + + /** + * Check that form with default values is returned with internal storage_type + */ + public function testGetFormsWithDefaultValues(): void { + $app = 'testing'; + $schema = self::validSchemaAllFields; + $this->declarativeManager->registerSchema($app, $schema); + + $this->config->expects($this->any()) + ->method('getAppValue') + ->willReturnCallback(fn ($app, $configkey, $default) => $default); + + $forms = $this->declarativeManager->getFormsWithValues($this->adminUser, $schema['section_type'], $schema['section_id']); + $this->assertNotEmpty($forms); + $this->assertTrue(array_search($schema['id'], array_column($forms, 'id')) !== false); + // Check some_real_setting field default value + $someRealSettingField = array_values(array_filter(array_filter($forms, fn ($form) => $form['id'] === $schema['id'])[0]['fields'], fn ($field) => $field['id'] === 'some_real_setting'))[0]; + $schemaSomeRealSettingField = array_values(array_filter($schema['fields'], fn ($field) => $field['id'] === 'some_real_setting'))[0]; + $this->assertEquals($schemaSomeRealSettingField['default'], $someRealSettingField['default']); + } + + /** + * Check values in json format to ensure that they are properly encoded + */ + public function testGetFormsWithDefaultValuesJson(): void { + $app = 'testing'; + $schema = [ + 'id' => 'test_form_1', + 'priority' => 10, + 'section_type' => DeclarativeSettingsTypes::SECTION_TYPE_PERSONAL, + 'section_id' => 'additional', + 'storage_type' => DeclarativeSettingsTypes::STORAGE_TYPE_INTERNAL, + 'title' => 'Test declarative settings', + 'description' => 'These fields are rendered dynamically from declarative schema', + 'doc_url' => '', + 'fields' => [ + [ + 'id' => 'test_field_json', + 'title' => 'Multi-selection', + 'description' => 'Select some option setting', + 'type' => DeclarativeSettingsTypes::MULTI_SELECT, + 'options' => ['foo', 'bar', 'baz'], + 'placeholder' => 'Select some multiple options', + 'default' => ['foo', 'bar'], + ], + ], + ]; + $this->declarativeManager->registerSchema($app, $schema); + + // config->getUserValue() should be called with json encoded default value + $this->config->expects($this->once()) + ->method('getUserValue') + ->with($this->adminUser->getUID(), $app, 'test_field_json', json_encode($schema['fields'][0]['default'])) + ->willReturn(json_encode($schema['fields'][0]['default'])); + + $forms = $this->declarativeManager->getFormsWithValues($this->adminUser, $schema['section_type'], $schema['section_id']); + $this->assertNotEmpty($forms); + $this->assertTrue(array_search($schema['id'], array_column($forms, 'id')) !== false); + $testFieldJson = array_values(array_filter(array_filter($forms, fn ($form) => $form['id'] === $schema['id'])[0]['fields'], fn ($field) => $field['id'] === 'test_field_json'))[0]; + $this->assertEquals(json_encode($schema['fields'][0]['default']), $testFieldJson['value']); + } + + /** + * Check that saving value for field with internal storage_type is handled by core + */ + public function testSetInternalValue(): void { + $app = 'testing'; + $schema = self::validSchemaAllFields; + $this->declarativeManager->registerSchema($app, $schema); + self::$testSetInternalValueAfterChange = false; + + $this->config->expects($this->any()) + ->method('getAppValue') + ->willReturnCallback(function ($app, $configkey, $default) { + if ($configkey === 'some_real_setting' && self::$testSetInternalValueAfterChange) { + return '120m'; + } + return $default; + }); + + $this->appConfig->expects($this->once()) + ->method('setValueString') + ->with($app, 'some_real_setting', '120m'); + + $forms = $this->declarativeManager->getFormsWithValues($this->adminUser, $schema['section_type'], $schema['section_id']); + $someRealSettingField = array_values(array_filter(array_filter($forms, fn ($form) => $form['id'] === $schema['id'])[0]['fields'], fn ($field) => $field['id'] === 'some_real_setting'))[0]; + $this->assertEquals('40m', $someRealSettingField['value']); // first check that default value (40m) is returned + + // Set new value for some_real_setting field + $this->declarativeManager->setValue($this->adminUser, $app, $schema['id'], 'some_real_setting', '120m'); + self::$testSetInternalValueAfterChange = true; + + $forms = $this->declarativeManager->getFormsWithValues($this->adminUser, $schema['section_type'], $schema['section_id']); + $this->assertNotEmpty($forms); + $this->assertTrue(array_search($schema['id'], array_column($forms, 'id')) !== false); + // Check some_real_setting field default value + $someRealSettingField = array_values(array_filter(array_filter($forms, fn ($form) => $form['id'] === $schema['id'])[0]['fields'], fn ($field) => $field['id'] === 'some_real_setting'))[0]; + $this->assertEquals('120m', $someRealSettingField['value']); + } + + public function testSetExternalValue(): void { + $app = 'testing'; + $schema = self::validSchemaAllFields; + // Change storage_type to external and section_type to personal + $schema['storage_type'] = DeclarativeSettingsTypes::STORAGE_TYPE_EXTERNAL; + $schema['section_type'] = DeclarativeSettingsTypes::SECTION_TYPE_PERSONAL; + $this->declarativeManager->registerSchema($app, $schema); + + $setDeclarativeSettingsValueEvent = new DeclarativeSettingsSetValueEvent( + $this->adminUser, + $app, + $schema['id'], + 'some_real_setting', + '120m' + ); + + $this->eventDispatcher->expects($this->once()) + ->method('dispatchTyped') + ->with($setDeclarativeSettingsValueEvent); + $this->declarativeManager->setValue($this->adminUser, $app, $schema['id'], 'some_real_setting', '120m'); + } + + public function testAdminFormUserUnauthorized(): void { + $app = 'testing'; + $schema = self::validSchemaAllFields; + $this->declarativeManager->registerSchema($app, $schema); + + $this->expectException(\Exception::class); + $this->declarativeManager->getFormsWithValues($this->user, $schema['section_type'], $schema['section_id']); + } +} diff --git a/tests/lib/Settings/ManagerTest.php b/tests/lib/Settings/ManagerTest.php index cc13479b1d0..93d57c37806 100644 --- a/tests/lib/Settings/ManagerTest.php +++ b/tests/lib/Settings/ManagerTest.php @@ -34,9 +34,9 @@ use OCP\IURLGenerator; use OCP\L10N\IFactory; use OCP\Settings\ISettings; use OCP\Settings\ISubAdminSettings; +use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; use Test\TestCase; -use PHPUnit\Framework\MockObject\MockObject; class ManagerTest extends TestCase { /** @var Manager|MockObject */ diff --git a/tests/lib/SetupTest.php b/tests/lib/SetupTest.php index eacc165650c..542e8a08feb 100644 --- a/tests/lib/SetupTest.php +++ b/tests/lib/SetupTest.php @@ -14,27 +14,20 @@ use OC\Setup; use OC\SystemConfig; use OCP\Defaults; use OCP\IL10N; +use OCP\L10N\IFactory as IL10NFactory; use OCP\Security\ISecureRandom; -use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; class SetupTest extends \Test\TestCase { - /** @var SystemConfig|MockObject */ - protected $config; - /** @var \bantu\IniGetWrapper\IniGetWrapper|MockObject */ - private $iniWrapper; - /** @var \OCP\IL10N|MockObject */ - private $l10n; - /** @var Defaults|MockObject */ - private $defaults; - /** @var \OC\Setup|MockObject */ - protected $setupClass; - /** @var LoggerInterface|MockObject */ - protected $logger; - /** @var \OCP\Security\ISecureRandom|MockObject */ - protected $random; - /** @var Installer|MockObject */ - protected $installer; + protected SystemConfig $config; + private IniGetWrapper $iniWrapper; + private IL10N $l10n; + private IL10NFactory $l10nFactory; + private Defaults $defaults; + protected Setup $setupClass; + protected LoggerInterface $logger; + protected ISecureRandom $random; + protected Installer $installer; protected function setUp(): void { parent::setUp(); @@ -42,13 +35,16 @@ class SetupTest extends \Test\TestCase { $this->config = $this->createMock(SystemConfig::class); $this->iniWrapper = $this->createMock(IniGetWrapper::class); $this->l10n = $this->createMock(IL10N::class); + $this->l10nFactory = $this->createMock(IL10NFactory::class); + $this->l10nFactory->method('get') + ->willReturn($this->l10n); $this->defaults = $this->createMock(Defaults::class); $this->logger = $this->createMock(LoggerInterface::class); $this->random = $this->createMock(ISecureRandom::class); $this->installer = $this->createMock(Installer::class); $this->setupClass = $this->getMockBuilder(Setup::class) ->setMethods(['class_exists', 'is_callable', 'getAvailableDbDriversForPdo']) - ->setConstructorArgs([$this->config, $this->iniWrapper, $this->l10n, $this->defaults, $this->logger, $this->random, $this->installer]) + ->setConstructorArgs([$this->config, $this->iniWrapper, $this->l10nFactory, $this->defaults, $this->logger, $this->random, $this->installer]) ->getMock(); } diff --git a/tests/lib/Share/Backend.php b/tests/lib/Share/Backend.php index 18443a4e247..f383d804971 100644 --- a/tests/lib/Share/Backend.php +++ b/tests/lib/Share/Backend.php @@ -21,6 +21,10 @@ namespace Test\Share; +use OC\Share20\Manager; +use OCP\Server; +use OCP\Share\IShare; + class Backend implements \OCP\Share_Backend { public const FORMAT_SOURCE = 0; public const FORMAT_TARGET = 1; @@ -46,7 +50,11 @@ class Backend implements \OCP\Share_Backend { } - $shares = \OC\Share\Share::getItemsSharedWithUser('test', $shareWith); + $shareManager = Server::get(Manager::class); + $shares = array_merge( + $shareManager->getSharedWith($shareWith, IShare::TYPE_USER), + $shareManager->getSharedWith($shareWith, IShare::TYPE_GROUP), + ); $knownTargets = []; foreach ($shares as $share) { diff --git a/tests/lib/Share/ShareTest.php b/tests/lib/Share/ShareTest.php index 35dc00739f6..121f337e4ab 100644 --- a/tests/lib/Share/ShareTest.php +++ b/tests/lib/Share/ShareTest.php @@ -22,13 +22,11 @@ namespace Test\Share; use OC\Share\Share; -use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\IGroup; use OCP\IGroupManager; use OCP\IUser; use OCP\IUserManager; -use OCP\Share\IShare; /** * Class Test_Share @@ -127,110 +125,6 @@ class ShareTest extends \Test\TestCase { parent::tearDown(); } - public function testGetItemSharedWithUser() { - \OC_User::setUserId($this->user1->getUID()); - - // add dummy values to the share table - $query = $this->connection->getQueryBuilder(); - $query->insert('share') - ->values([ - 'item_type' => $query->createParameter('itemType'), - 'item_source' => $query->createParameter('itemSource'), - 'item_target' => $query->createParameter('itemTarget'), - 'share_type' => $query->createParameter('shareType'), - 'share_with' => $query->createParameter('shareWith'), - 'uid_owner' => $query->createParameter('uidOwner') - ]); - $args = [ - ['test', 99, 'target1', IShare::TYPE_USER, $this->user2->getUID(), $this->user1->getUID()], - ['test', 99, 'target2', IShare::TYPE_USER, $this->user4->getUID(), $this->user1->getUID()], - ['test', 99, 'target3', IShare::TYPE_USER, $this->user3->getUID(), $this->user2->getUID()], - ['test', 99, 'target4', IShare::TYPE_USER, $this->user3->getUID(), $this->user4->getUID()], - ['test', 99, 'target4', IShare::TYPE_USER, $this->user6->getUID(), $this->user4->getUID()], - ]; - foreach ($args as $row) { - $query->setParameter('itemType', $row[0]); - $query->setParameter('itemSource', $row[1], IQueryBuilder::PARAM_INT); - $query->setParameter('itemTarget', $row[2]); - $query->setParameter('shareType', $row[3], IQueryBuilder::PARAM_INT); - $query->setParameter('shareWith', $row[4]); - $query->setParameter('uidOwner', $row[5]); - $query->executeStatement(); - } - - $result1 = Share::getItemSharedWithUser('test', 99, $this->user2->getUID(), $this->user1->getUID()); - $this->assertSame(1, count($result1)); - $this->verifyResult($result1, ['target1']); - - $result2 = Share::getItemSharedWithUser('test', 99, null, $this->user1->getUID()); - $this->assertSame(2, count($result2)); - $this->verifyResult($result2, ['target1', 'target2']); - - $result3 = Share::getItemSharedWithUser('test', 99, $this->user3->getUID()); - $this->assertSame(2, count($result3)); - $this->verifyResult($result3, ['target3', 'target4']); - - $result4 = Share::getItemSharedWithUser('test', 99, null, null); - $this->assertSame(5, count($result4)); // 5 because target4 appears twice - $this->verifyResult($result4, ['target1', 'target2', 'target3', 'target4']); - - $result6 = Share::getItemSharedWithUser('test', 99, $this->user6->getUID(), null); - $this->assertSame(1, count($result6)); - $this->verifyResult($result6, ['target4']); - } - - public function testGetItemSharedWithUserFromGroupShare() { - \OC_User::setUserId($this->user1->getUID()); - - // add dummy values to the share table - $query = $this->connection->getQueryBuilder(); - $query->insert('share') - ->values([ - 'item_type' => $query->createParameter('itemType'), - 'item_source' => $query->createParameter('itemSource'), - 'item_target' => $query->createParameter('itemTarget'), - 'share_type' => $query->createParameter('shareType'), - 'share_with' => $query->createParameter('shareWith'), - 'uid_owner' => $query->createParameter('uidOwner') - ]); - $args = [ - ['test', 99, 'target1', IShare::TYPE_GROUP, $this->group1->getGID(), $this->user1->getUID()], - ['test', 99, 'target2', IShare::TYPE_GROUP, $this->group2->getGID(), $this->user1->getUID()], - ['test', 99, 'target3', IShare::TYPE_GROUP, $this->group1->getGID(), $this->user2->getUID()], - ['test', 99, 'target4', IShare::TYPE_GROUP, $this->group1->getGID(), $this->user4->getUID()], - ]; - foreach ($args as $row) { - $query->setParameter('itemType', $row[0]); - $query->setParameter('itemSource', $row[1], IQueryBuilder::PARAM_INT); - $query->setParameter('itemTarget', $row[2]); - $query->setParameter('shareType', $row[3], IQueryBuilder::PARAM_INT); - $query->setParameter('shareWith', $row[4]); - $query->setParameter('uidOwner', $row[5]); - $query->executeStatement(); - } - - // user2 is in group1 and group2 - $result1 = Share::getItemSharedWithUser('test', 99, $this->user2->getUID(), $this->user1->getUID()); - $this->assertSame(2, count($result1)); - $this->verifyResult($result1, ['target1', 'target2']); - - $result2 = Share::getItemSharedWithUser('test', 99, null, $this->user1->getUID()); - $this->assertSame(2, count($result2)); - $this->verifyResult($result2, ['target1', 'target2']); - - // user3 is in group1 and group2 - $result3 = Share::getItemSharedWithUser('test', 99, $this->user3->getUID()); - $this->assertSame(3, count($result3)); - $this->verifyResult($result3, ['target1', 'target3', 'target4']); - - $result4 = Share::getItemSharedWithUser('test', 99, null, null); - $this->assertSame(4, count($result4)); - $this->verifyResult($result4, ['target1', 'target2', 'target3', 'target4']); - - $result6 = Share::getItemSharedWithUser('test', 99, $this->user6->getUID(), null); - $this->assertSame(0, count($result6)); - } - public function verifyResult($result, $expected) { foreach ($result as $r) { if (in_array($r['item_target'], $expected)) { diff --git a/tests/lib/Share20/DefaultShareProviderTest.php b/tests/lib/Share20/DefaultShareProviderTest.php index afa428014f8..e1cdb8530c3 100644 --- a/tests/lib/Share20/DefaultShareProviderTest.php +++ b/tests/lib/Share20/DefaultShareProviderTest.php @@ -133,8 +133,8 @@ class DefaultShareProviderTest extends \Test\TestCase { * @return int */ private function addShareToDB($shareType, $sharedWith, $sharedBy, $shareOwner, - $itemType, $fileSource, $fileTarget, $permissions, $token, $expiration, - $parent = null) { + $itemType, $fileSource, $fileTarget, $permissions, $token, $expiration, + $parent = null) { $qb = $this->dbConn->getQueryBuilder(); $qb->insert('share'); @@ -210,7 +210,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $ownerPath = $this->createMock(File::class); $shareOwnerFolder = $this->createMock(Folder::class); - $shareOwnerFolder->method('getById')->with(42)->willReturn([$ownerPath]); + $shareOwnerFolder->method('getFirstNodeById')->with(42)->willReturn($ownerPath); $this->rootFolder ->method('getUserFolder') @@ -288,7 +288,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $ownerPath = $this->createMock(File::class); $shareOwnerFolder = $this->createMock(Folder::class); - $shareOwnerFolder->method('getById')->with(42)->willReturn([$ownerPath]); + $shareOwnerFolder->method('getFirstNodeById')->with(42)->willReturn($ownerPath); $this->rootFolder ->method('getUserFolder') @@ -332,7 +332,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $ownerPath = $this->createMock(Folder::class); $shareOwnerFolder = $this->createMock(Folder::class); - $shareOwnerFolder->method('getById')->with(42)->willReturn([$ownerPath]); + $shareOwnerFolder->method('getFirstNodeById')->with(42)->willReturn($ownerPath); $this->rootFolder ->method('getUserFolder') @@ -371,7 +371,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $node->method('getId')->willReturn(42); $this->rootFolder->method('getUserFolder')->with('user0')->willReturnSelf(); - $this->rootFolder->method('getById')->willReturn([$node]); + $this->rootFolder->method('getFirstNodeById')->willReturn($node); $this->userManager->method('get')->willReturnMap([ ['user0', $user0], @@ -416,7 +416,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $ownerPath = $this->createMock(Folder::class); $shareOwnerFolder = $this->createMock(Folder::class); - $shareOwnerFolder->method('getById')->with(42)->willReturn([$ownerPath]); + $shareOwnerFolder->method('getFirstNodeById')->with(42)->willReturn($ownerPath); $this->rootFolder ->method('getUserFolder') @@ -633,7 +633,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $ownerPath = $this->createMock(Folder::class); $ownerFolder = $this->createMock(Folder::class); - $ownerFolder->method('getById')->willReturn([$ownerPath]); + $ownerFolder->method('getFirstNodeById')->willReturn($ownerPath); $this->rootFolder ->method('getUserFolder') @@ -690,12 +690,12 @@ class DefaultShareProviderTest extends \Test\TestCase { ['shareOwner', $ownerFolder], ]); - $userFolder->method('getById') + $userFolder->method('getFirstNodeById') ->with(100) - ->willReturn([$path]); - $ownerFolder->method('getById') + ->willReturn($path); + $ownerFolder->method('getFirstNodeById') ->with(100) - ->willReturn([$path]); + ->willReturn($path); $share->setShareType(IShare::TYPE_USER); $share->setSharedWith('sharedWith'); @@ -762,12 +762,12 @@ class DefaultShareProviderTest extends \Test\TestCase { ['shareOwner', $ownerFolder], ]); - $userFolder->method('getById') + $userFolder->method('getFirstNodeById') ->with(100) - ->willReturn([$path]); - $ownerFolder->method('getById') + ->willReturn($path); + $ownerFolder->method('getFirstNodeById') ->with(100) - ->willReturn([$path]); + ->willReturn($path); $share->setShareType(IShare::TYPE_GROUP); $share->setSharedWith('sharedWith'); @@ -832,12 +832,12 @@ class DefaultShareProviderTest extends \Test\TestCase { ['shareOwner', $ownerFolder], ]); - $userFolder->method('getById') + $userFolder->method('getFirstNodeById') ->with(100) - ->willReturn([$path]); - $ownerFolder->method('getById') + ->willReturn($path); + $ownerFolder->method('getFirstNodeById') ->with(100) - ->willReturn([$path]); + ->willReturn($path); $share->setShareType(IShare::TYPE_LINK); $share->setSharedBy('sharedBy'); @@ -890,7 +890,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $file = $this->createMock(File::class); $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturnSelf(); - $this->rootFolder->method('getById')->with(42)->willReturn([$file]); + $this->rootFolder->method('getFirstNodeById')->with(42)->willReturn($file); $share = $this->provider->getShareByToken('secrettoken'); $this->assertEquals($id, $share->getId()); @@ -981,7 +981,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $file = $this->createMock(File::class); $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturnSelf(); - $this->rootFolder->method('getById')->with($fileId)->willReturn([$file]); + $this->rootFolder->method('getFirstNodeById')->with($fileId)->willReturn($file); $share = $this->provider->getSharedWith('sharedWith', IShare::TYPE_USER, null, 1, 0); $this->assertCount(1, $share); @@ -1053,7 +1053,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $file = $this->createMock(File::class); $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturnSelf(); - $this->rootFolder->method('getById')->with($fileId)->willReturn([$file]); + $this->rootFolder->method('getFirstNodeById')->with($fileId)->willReturn($file); $share = $this->provider->getSharedWith('sharedWith', IShare::TYPE_GROUP, null, 20, 1); $this->assertCount(1, $share); @@ -1141,7 +1141,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $file = $this->createMock(File::class); $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturnSelf(); - $this->rootFolder->method('getById')->with($fileId)->willReturn([$file]); + $this->rootFolder->method('getFirstNodeById')->with($fileId)->willReturn($file); $share = $this->provider->getSharedWith('user', IShare::TYPE_GROUP, null, -1, 0); $this->assertCount(1, $share); @@ -1184,7 +1184,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $file->method('getId')->willReturn($fileId2); $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf(); - $this->rootFolder->method('getById')->with($fileId2)->willReturn([$file]); + $this->rootFolder->method('getFirstNodeById')->with($fileId2)->willReturn($file); $share = $this->provider->getSharedWith('user0', IShare::TYPE_USER, $file, -1, 0); $this->assertCount(1, $share); @@ -1225,7 +1225,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $node = $this->createMock(Folder::class); $node->method('getId')->willReturn($fileId2); $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf(); - $this->rootFolder->method('getById')->with($fileId2)->willReturn([$node]); + $this->rootFolder->method('getFirstNodeById')->with($fileId2)->willReturn($node); $share = $this->provider->getSharedWith('user0', IShare::TYPE_GROUP, $node, -1, 0); $this->assertCount(1, $share); @@ -1276,7 +1276,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $file = $this->createMock(File::class); $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturnSelf(); - $this->rootFolder->method('getById')->with($deletedFileId)->willReturn([$file]); + $this->rootFolder->method('getFirstNodeById')->with($deletedFileId)->willReturn($file); $groups = []; foreach (range(0, 100) as $i) { @@ -1336,7 +1336,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $file = $this->createMock(File::class); $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturnSelf(); - $this->rootFolder->method('getById')->with(42)->willReturn([$file]); + $this->rootFolder->method('getFirstNodeById')->with(42)->willReturn($file); $share = $this->provider->getSharesBy('sharedBy', IShare::TYPE_USER, null, false, 1, 0); $this->assertCount(1, $share); @@ -1386,7 +1386,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $file = $this->createMock(File::class); $file->method('getId')->willReturn(42); $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturnSelf(); - $this->rootFolder->method('getById')->with(42)->willReturn([$file]); + $this->rootFolder->method('getFirstNodeById')->with(42)->willReturn($file); $share = $this->provider->getSharesBy('sharedBy', IShare::TYPE_USER, $file, false, 1, 0); $this->assertCount(1, $share); @@ -1436,7 +1436,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $file = $this->createMock(File::class); $file->method('getId')->willReturn(42); $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturnSelf(); - $this->rootFolder->method('getById')->with(42)->willReturn([$file]); + $this->rootFolder->method('getFirstNodeById')->with(42)->willReturn($file); $shares = $this->provider->getSharesBy('shareOwner', IShare::TYPE_USER, null, true, -1, 0); $this->assertCount(2, $shares); @@ -1496,7 +1496,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $file->method('getId')->willReturn(1); $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf(); - $this->rootFolder->method('getById')->with(1)->willReturn([$file]); + $this->rootFolder->method('getFirstNodeById')->with(1)->willReturn($file); $share = $this->provider->getShareById($id); @@ -1568,7 +1568,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $file->method('getId')->willReturn(1); $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf(); - $this->rootFolder->method('getById')->with(1)->willReturn([$file]); + $this->rootFolder->method('getFirstNodeById')->with(1)->willReturn($file); $share = $this->provider->getShareById($id); @@ -1626,7 +1626,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $file->method('getId')->willReturn(1); $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf(); - $this->rootFolder->method('getById')->with(1)->willReturn([$file]); + $this->rootFolder->method('getFirstNodeById')->with(1)->willReturn($file); $share = $this->provider->getShareById($id); @@ -1668,7 +1668,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $file->method('getId')->willReturn(1); $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf(); - $this->rootFolder->method('getById')->with(1)->willReturn([$file]); + $this->rootFolder->method('getFirstNodeById')->with(1)->willReturn($file); $share = $this->provider->getShareById($id); @@ -1706,7 +1706,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $file->method('getId')->willReturn(1); $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf(); - $this->rootFolder->method('getById')->with(1)->willReturn([$file]); + $this->rootFolder->method('getFirstNodeById')->with(1)->willReturn($file); $share = $this->provider->getShareById($id); @@ -1760,7 +1760,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $file->method('getId')->willReturn(1); $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf(); - $this->rootFolder->method('getById')->with(1)->willReturn([$file]); + $this->rootFolder->method('getFirstNodeById')->with(1)->willReturn($file); $share = $this->provider->getShareById($id); @@ -1797,7 +1797,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $file->method('getId')->willReturn(1); $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf(); - $this->rootFolder->method('getById')->with(1)->willReturn([$file]); + $this->rootFolder->method('getFirstNodeById')->with(1)->willReturn($file); $share = $this->provider->getShareById($id); @@ -1828,9 +1828,9 @@ class DefaultShareProviderTest extends \Test\TestCase { $file2->method('getId')->willReturn(43); $folder1 = $this->createMock(Folder::class); - $folder1->method('getById')->with(42)->willReturn([$file1]); + $folder1->method('getFirstNodeById')->with(42)->willReturn($file1); $folder2 = $this->createMock(Folder::class); - $folder2->method('getById')->with(43)->willReturn([$file2]); + $folder2->method('getFirstNodeById')->with(43)->willReturn($file2); $this->rootFolder->method('getUserFolder')->willReturnMap([ ['user2', $folder1], @@ -1885,9 +1885,9 @@ class DefaultShareProviderTest extends \Test\TestCase { $file2->method('getId')->willReturn(43); $folder1 = $this->createMock(Folder::class); - $folder1->method('getById')->with(42)->willReturn([$file1]); + $folder1->method('getFirstNodeById')->with(42)->willReturn($file1); $folder2 = $this->createMock(Folder::class); - $folder2->method('getById')->with(43)->willReturn([$file2]); + $folder2->method('getFirstNodeById')->with(43)->willReturn($file2); $this->rootFolder->method('getUserFolder')->willReturnMap([ ['user2', $folder1], @@ -1951,9 +1951,9 @@ class DefaultShareProviderTest extends \Test\TestCase { $file2->method('getId')->willReturn(43); $folder1 = $this->createMock(Folder::class); - $folder1->method('getById')->with(42)->willReturn([$file1]); + $folder1->method('getFirstNodeById')->with(42)->willReturn($file1); $folder2 = $this->createMock(Folder::class); - $folder2->method('getById')->with(43)->willReturn([$file2]); + $folder2->method('getFirstNodeById')->with(43)->willReturn($file2); $this->rootFolder->method('getUserFolder')->willReturnMap([ ['user2', $folder1], @@ -2022,9 +2022,9 @@ class DefaultShareProviderTest extends \Test\TestCase { $file2->method('getId')->willReturn(43); $folder1 = $this->createMock(Folder::class); - $folder1->method('getById')->with(42)->willReturn([$file1]); + $folder1->method('getFirstNodeById')->with(42)->willReturn($file1); $folder2 = $this->createMock(Folder::class); - $folder2->method('getById')->with(43)->willReturn([$file2]); + $folder2->method('getFirstNodeById')->with(43)->willReturn($file2); $this->rootFolder->method('getUserFolder')->willReturnMap([ ['user2', $folder1], @@ -2101,9 +2101,9 @@ class DefaultShareProviderTest extends \Test\TestCase { $file2->method('getId')->willReturn(43); $folder1 = $this->createMock(Folder::class); - $folder1->method('getById')->with(42)->willReturn([$file1]); + $folder1->method('getFirstNodeById')->with(42)->willReturn($file1); $folder2 = $this->createMock(Folder::class); - $folder2->method('getById')->with(43)->willReturn([$file2]); + $folder2->method('getFirstNodeById')->with(43)->willReturn($file2); $this->rootFolder->method('getUserFolder')->willReturnMap([ ['user2', $folder1], @@ -2179,7 +2179,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $file->method('getId')->willReturn(42); $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf(); - $this->rootFolder->method('getById')->willReturn([$file]); + $this->rootFolder->method('getFirstNodeById')->willReturn($file); $share = $this->provider->getShareById($id, null); @@ -2215,7 +2215,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $folder->method('getId')->willReturn(42); $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf(); - $this->rootFolder->method('getById')->willReturn([$folder]); + $this->rootFolder->method('getFirstNodeById')->willReturn($folder); $share = $this->provider->getShareById($id, 'user0'); @@ -2881,23 +2881,23 @@ class DefaultShareProviderTest extends \Test\TestCase { $ownerPath1 = $this->createMock(File::class); $shareOwner1Folder = $this->createMock(Folder::class); - $shareOwner1Folder->method('getById')->willReturn([$ownerPath1]); + $shareOwner1Folder->method('getFirstNodeById')->willReturn($ownerPath1); $ownerPath2 = $this->createMock(File::class); $shareOwner2Folder = $this->createMock(Folder::class); - $shareOwner2Folder->method('getById')->willReturn([$ownerPath2]); + $shareOwner2Folder->method('getFirstNodeById')->willReturn($ownerPath2); $ownerPath3 = $this->createMock(File::class); $shareOwner3Folder = $this->createMock(Folder::class); - $shareOwner3Folder->method('getById')->willReturn([$ownerPath3]); + $shareOwner3Folder->method('getFirstNodeById')->willReturn($ownerPath3); $ownerPath4 = $this->createMock(File::class); $shareOwner4Folder = $this->createMock(Folder::class); - $shareOwner4Folder->method('getById')->willReturn([$ownerPath4]); + $shareOwner4Folder->method('getFirstNodeById')->willReturn($ownerPath4); $ownerPath5 = $this->createMock(File::class); $shareOwner5Folder = $this->createMock(Folder::class); - $shareOwner5Folder->method('getById')->willReturn([$ownerPath5]); + $shareOwner5Folder->method('getFirstNodeById')->willReturn($ownerPath5); $this->rootFolder ->method('getUserFolder') diff --git a/tests/lib/Share20/ManagerTest.php b/tests/lib/Share20/ManagerTest.php index c770181799f..d787556eb64 100644 --- a/tests/lib/Share20/ManagerTest.php +++ b/tests/lib/Share20/ManagerTest.php @@ -21,6 +21,7 @@ namespace Test\Share20; +use DateTimeZone; use OC\Files\Mount\MoveableMount; use OC\KnownUser\KnownUserService; use OC\Share20\DefaultShareProvider; @@ -39,6 +40,7 @@ use OCP\Files\Node; use OCP\Files\Storage; use OCP\HintException; use OCP\IConfig; +use OCP\IDateTimeZone; use OCP\IGroup; use OCP\IGroupManager; use OCP\IL10N; @@ -114,6 +116,9 @@ class ManagerTest extends \Test\TestCase { protected $knownUserService; /** @var ShareDisableChecker|MockObject */ protected $shareDisabledChecker; + private DateTimeZone $timezone; + /** @var IDateTimeZone|MockObject */ + protected $dateTimeZone; protected function setUp(): void { $this->logger = $this->createMock(LoggerInterface::class); @@ -132,6 +137,9 @@ class ManagerTest extends \Test\TestCase { $this->knownUserService = $this->createMock(KnownUserService::class); $this->shareDisabledChecker = new ShareDisableChecker($this->config, $this->userManager, $this->groupManager); + $this->dateTimeZone = $this->createMock(IDateTimeZone::class); + $this->timezone = new \DateTimeZone('Pacific/Auckland'); + $this->dateTimeZone->method('getTimeZone')->willReturnCallback(fn () => $this->timezone); $this->l10nFactory = $this->createMock(IFactory::class); $this->l = $this->createMock(IL10N::class); @@ -143,19 +151,27 @@ class ManagerTest extends \Test\TestCase { ->willReturnCallback(function ($singular, $plural, $count, $parameters = []) { return vsprintf(str_replace('%n', $count, ($count === 1) ? $singular : $plural), $parameters); }); + $this->l10nFactory->method('get')->willReturn($this->l); $this->factory = new DummyFactory(\OC::$server); - $this->manager = new Manager( + $this->manager = $this->createManager($this->factory); + + $this->defaultProvider = $this->createMock(DefaultShareProvider::class); + $this->defaultProvider->method('identifier')->willReturn('default'); + $this->factory->setProvider($this->defaultProvider); + } + + private function createManager(IProviderFactory $factory): Manager { + return new Manager( $this->logger, $this->config, $this->secureRandom, $this->hasher, $this->mountManager, $this->groupManager, - $this->l, $this->l10nFactory, - $this->factory, + $factory, $this->userManager, $this->rootFolder, $this->mailer, @@ -164,12 +180,9 @@ class ManagerTest extends \Test\TestCase { $this->dispatcher, $this->userSession, $this->knownUserService, - $this->shareDisabledChecker + $this->shareDisabledChecker, + $this->dateTimeZone, ); - - $this->defaultProvider = $this->createMock(DefaultShareProvider::class); - $this->defaultProvider->method('identifier')->willReturn('default'); - $this->factory->setProvider($this->defaultProvider); } /** @@ -184,7 +197,6 @@ class ManagerTest extends \Test\TestCase { $this->hasher, $this->mountManager, $this->groupManager, - $this->l, $this->l10nFactory, $this->factory, $this->userManager, @@ -196,6 +208,7 @@ class ManagerTest extends \Test\TestCase { $this->userSession, $this->knownUserService, $this->shareDisabledChecker, + $this->dateTimeZone, ]); } @@ -785,9 +798,9 @@ class ManagerTest extends \Test\TestCase { ->willReturn(42); // Id 108 is used in the data to refer to the node of the share. $userFolder->expects($this->any()) - ->method('getById') + ->method('getFirstNodeById') ->with(108) - ->willReturn([$share->getNode()]); + ->willReturn($share->getNode()); $userFolder->expects($this->any()) ->method('getRelativePath') ->willReturnArgument(0); @@ -933,7 +946,7 @@ class ManagerTest extends \Test\TestCase { ]); } - $expected = new \DateTime(); + $expected = new \DateTime('now', $this->timezone); $expected->setTime(0, 0, 0); $expected->add(new \DateInterval('P3D')); @@ -968,7 +981,7 @@ class ManagerTest extends \Test\TestCase { ]); } - $expected = new \DateTime(); + $expected = new \DateTime('now', $this->timezone); $expected->setTime(0, 0, 0); $expected->add(new \DateInterval('P1D')); @@ -1015,7 +1028,7 @@ class ManagerTest extends \Test\TestCase { * @dataProvider validateExpirationDateInternalProvider */ public function testValidateExpirationDateInternalEnforceValid($shareType) { - $future = new \DateTime(); + $future = new \DateTime('now', $this->dateTimeZone->getTimeZone()); $future->add(new \DateInterval('P2D')); $future->setTime(1, 2, 3); @@ -1057,7 +1070,7 @@ class ManagerTest extends \Test\TestCase { * @dataProvider validateExpirationDateInternalProvider */ public function testValidateExpirationDateInternalNoDefault($shareType) { - $date = new \DateTime(); + $date = new \DateTime('now', $this->dateTimeZone->getTimeZone()); $date->add(new \DateInterval('P5D')); $date->setTime(1, 2, 3); @@ -1105,9 +1118,10 @@ class ManagerTest extends \Test\TestCase { $share = $this->manager->newShare(); $share->setShareType($shareType); - $expected = new \DateTime(); + $expected = new \DateTime('now', $this->timezone); + $expected->setTime(0, 0); $expected->add(new \DateInterval('P3D')); - $expected->setTime(0, 0, 0); + $expected->setTimezone(new \DateTimeZone(date_default_timezone_get())); if ($shareType === IShare::TYPE_USER) { $this->config->method('getAppValue') @@ -1140,12 +1154,12 @@ class ManagerTest extends \Test\TestCase { * @dataProvider validateExpirationDateInternalProvider */ public function testValidateExpirationDateInternalDefault($shareType) { - $future = new \DateTime(); + $future = new \DateTime('now', $this->timezone); $future->add(new \DateInterval('P5D')); $future->setTime(1, 2, 3); $expected = clone $future; - $expected->setTime(0, 0, 0); + $expected->setTime(0, 0); $share = $this->manager->newShare(); $share->setShareType($shareType); @@ -1182,7 +1196,7 @@ class ManagerTest extends \Test\TestCase { * @dataProvider validateExpirationDateInternalProvider */ public function testValidateExpirationDateInternalHookModification($shareType) { - $nextWeek = new \DateTime(); + $nextWeek = new \DateTime('now', $this->timezone); $nextWeek->add(new \DateInterval('P7D')); $nextWeek->setTime(0, 0, 0); @@ -1311,7 +1325,7 @@ class ManagerTest extends \Test\TestCase { ['core', 'link_defaultExpDays', '3', '3'], ]); - $expected = new \DateTime(); + $expected = new \DateTime('now', $this->timezone); $expected->setTime(0, 0, 0); $expected->add(new \DateInterval('P3D')); @@ -1332,7 +1346,7 @@ class ManagerTest extends \Test\TestCase { ['core', 'link_defaultExpDays', '3', '1'], ]); - $expected = new \DateTime(); + $expected = new \DateTime('now', $this->timezone); $expected->setTime(0, 0, 0); $expected->add(new \DateInterval('P1D')); @@ -1363,7 +1377,7 @@ class ManagerTest extends \Test\TestCase { } public function testValidateExpirationDateEnforceValid() { - $future = new \DateTime(); + $future = new \DateTime('now', $this->timezone); $future->add(new \DateInterval('P2D')); $future->setTime(1, 2, 3); @@ -1392,12 +1406,13 @@ class ManagerTest extends \Test\TestCase { } public function testValidateExpirationDateNoDefault() { - $date = new \DateTime(); + $date = new \DateTime('now', $this->timezone); $date->add(new \DateInterval('P5D')); $date->setTime(1, 2, 3); $expected = clone $date; - $expected->setTime(0, 0, 0); + $expected->setTime(0, 0); + $expected->setTimezone(new \DateTimeZone(date_default_timezone_get())); $share = $this->manager->newShare(); $share->setExpirationDate($date); @@ -1431,9 +1446,10 @@ class ManagerTest extends \Test\TestCase { public function testValidateExpirationDateNoDateDefault() { $share = $this->manager->newShare(); - $expected = new \DateTime(); + $expected = new \DateTime('now', $this->timezone); $expected->add(new \DateInterval('P3D')); - $expected->setTime(0, 0, 0); + $expected->setTime(0, 0); + $expected->setTimezone(new \DateTimeZone(date_default_timezone_get())); $this->config->method('getAppValue') ->willReturnMap([ @@ -1454,12 +1470,44 @@ class ManagerTest extends \Test\TestCase { } public function testValidateExpirationDateDefault() { - $future = new \DateTime(); + $future = new \DateTime('now', $this->timezone); $future->add(new \DateInterval('P5D')); $future->setTime(1, 2, 3); $expected = clone $future; - $expected->setTime(0, 0, 0); + $expected->setTime(0, 0); + $expected->setTimezone(new \DateTimeZone(date_default_timezone_get())); + + $share = $this->manager->newShare(); + $share->setExpirationDate($future); + + $this->config->method('getAppValue') + ->willReturnMap([ + ['core', 'shareapi_default_expire_date', 'no', 'yes'], + ['core', 'shareapi_expire_after_n_days', '7', '3'], + ['core', 'link_defaultExpDays', '3', '1'], + ]); + + $hookListener = $this->getMockBuilder('Dummy')->setMethods(['listener'])->getMock(); + \OCP\Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener'); + $hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($expected) { + return $data['expirationDate'] == $expected; + })); + + self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]); + + $this->assertEquals($expected, $share->getExpirationDate()); + } + + public function testValidateExpirationNegativeOffsetTimezone() { + $this->timezone = new \DateTimeZone('Pacific/Tahiti'); + $future = new \DateTime(); + $future->add(new \DateInterval('P5D')); + + $expected = clone $future; + $expected->setTimezone($this->timezone); + $expected->setTime(0, 0); + $expected->setTimezone(new \DateTimeZone(date_default_timezone_get())); $share = $this->manager->newShare(); $share->setExpirationDate($future); @@ -1483,11 +1531,12 @@ class ManagerTest extends \Test\TestCase { } public function testValidateExpirationDateHookModification() { - $nextWeek = new \DateTime(); + $nextWeek = new \DateTime('now', $this->timezone); $nextWeek->add(new \DateInterval('P7D')); - $nextWeek->setTime(0, 0, 0); $save = clone $nextWeek; + $save->setTime(0, 0); + $save->setTimezone(new \DateTimeZone(date_default_timezone_get())); $hookListener = $this->getMockBuilder('Dummy')->setMethods(['listener'])->getMock(); \OCP\Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener'); @@ -1569,6 +1618,7 @@ class ManagerTest extends \Test\TestCase { ->method('getAppValue') ->willReturnMap([ ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'], + ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'], ]); self::invokePrivate($this->manager, 'userCreateChecks', [$share]); @@ -1602,6 +1652,7 @@ class ManagerTest extends \Test\TestCase { ->method('getAppValue') ->willReturnMap([ ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'], + ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'], ]); $this->defaultProvider @@ -1616,7 +1667,7 @@ class ManagerTest extends \Test\TestCase { public function testUserCreateChecksIdenticalShareExists() { $this->expectException(AlreadySharedException::class); - $this->expectExceptionMessage('Sharing name.txt failed, because this item is already shared with user user'); + $this->expectExceptionMessage('Sharing name.txt failed, because this item is already shared with the account user'); $share = $this->manager->newShare(); $share->setSharedWithDisplayName('user'); @@ -1645,7 +1696,7 @@ class ManagerTest extends \Test\TestCase { public function testUserCreateChecksIdenticalPathSharedViaGroup() { $this->expectException(AlreadySharedException::class); - $this->expectExceptionMessage('Sharing name2.txt failed, because this item is already shared with user userName'); + $this->expectExceptionMessage('Sharing name2.txt failed, because this item is already shared with the account userName'); $share = $this->manager->newShare(); @@ -1794,6 +1845,7 @@ class ManagerTest extends \Test\TestCase { ->willReturnMap([ ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'], ['core', 'shareapi_allow_group_sharing', 'yes', 'yes'], + ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'], ]); self::invokePrivate($this->manager, 'groupCreateChecks', [$share]); @@ -1817,6 +1869,7 @@ class ManagerTest extends \Test\TestCase { ->willReturnMap([ ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'], ['core', 'shareapi_allow_group_sharing', 'yes', 'yes'], + ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'], ]); $this->assertNull($this->invokePrivate($this->manager, 'groupCreateChecks', [$share])); @@ -1846,6 +1899,7 @@ class ManagerTest extends \Test\TestCase { ->willReturnMap([ ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'], ['core', 'shareapi_allow_group_sharing', 'yes', 'yes'], + ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'], ]); self::invokePrivate($this->manager, 'groupCreateChecks', [$share]); @@ -2745,26 +2799,7 @@ class ManagerTest extends \Test\TestCase { $factory = $this->createMock(IProviderFactory::class); - $manager = new Manager( - $this->logger, - $this->config, - $this->secureRandom, - $this->hasher, - $this->mountManager, - $this->groupManager, - $this->l, - $this->l10nFactory, - $factory, - $this->userManager, - $this->rootFolder, - $this->mailer, - $this->urlGenerator, - $this->defaults, - $this->dispatcher, - $this->userSession, - $this->knownUserService, - $this->shareDisabledChecker, - ); + $manager = $this->createManager($factory); $share = $this->createMock(IShare::class); @@ -2793,26 +2828,7 @@ class ManagerTest extends \Test\TestCase { $factory = $this->createMock(IProviderFactory::class); - $manager = new Manager( - $this->logger, - $this->config, - $this->secureRandom, - $this->hasher, - $this->mountManager, - $this->groupManager, - $this->l, - $this->l10nFactory, - $factory, - $this->userManager, - $this->rootFolder, - $this->mailer, - $this->urlGenerator, - $this->defaults, - $this->dispatcher, - $this->userSession, - $this->knownUserService, - $this->shareDisabledChecker, - ); + $manager = $this->createManager($factory); $share = $this->createMock(IShare::class); @@ -2848,26 +2864,7 @@ class ManagerTest extends \Test\TestCase { $factory = $this->createMock(IProviderFactory::class); - $manager = new Manager( - $this->logger, - $this->config, - $this->secureRandom, - $this->hasher, - $this->mountManager, - $this->groupManager, - $this->l, - $this->l10nFactory, - $factory, - $this->userManager, - $this->rootFolder, - $this->mailer, - $this->urlGenerator, - $this->defaults, - $this->dispatcher, - $this->userSession, - $this->knownUserService, - $this->shareDisabledChecker, - ); + $manager = $this->createManager($factory); $share = $this->createMock(IShare::class); @@ -4248,26 +4245,7 @@ class ManagerTest extends \Test\TestCase { throw new Exception\ProviderException(); }); - $manager = new Manager( - $this->logger, - $this->config, - $this->secureRandom, - $this->hasher, - $this->mountManager, - $this->groupManager, - $this->l, - $this->l10nFactory, - $factory, - $this->userManager, - $this->rootFolder, - $this->mailer, - $this->urlGenerator, - $this->defaults, - $this->dispatcher, - $this->userSession, - $this->knownUserService, - $this->shareDisabledChecker, - ); + $manager = $this->createManager($factory); $this->assertSame($expected, $manager->shareProviderExists($shareType) ); @@ -4283,26 +4261,7 @@ class ManagerTest extends \Test\TestCase { public function testGetSharesInFolder() { $factory = new DummyFactory2($this->createMock(IServerContainer::class)); - $manager = new Manager( - $this->logger, - $this->config, - $this->secureRandom, - $this->hasher, - $this->mountManager, - $this->groupManager, - $this->l, - $this->l10nFactory, - $factory, - $this->userManager, - $this->rootFolder, - $this->mailer, - $this->urlGenerator, - $this->defaults, - $this->dispatcher, - $this->userSession, - $this->knownUserService, - $this->shareDisabledChecker, - ); + $manager = $this->createManager($factory); $factory->setProvider($this->defaultProvider); $extraProvider = $this->createMock(IShareProvider::class); @@ -4349,26 +4308,7 @@ class ManagerTest extends \Test\TestCase { public function testGetAccessList() { $factory = new DummyFactory2($this->createMock(IServerContainer::class)); - $manager = new Manager( - $this->logger, - $this->config, - $this->secureRandom, - $this->hasher, - $this->mountManager, - $this->groupManager, - $this->l, - $this->l10nFactory, - $factory, - $this->userManager, - $this->rootFolder, - $this->mailer, - $this->urlGenerator, - $this->defaults, - $this->dispatcher, - $this->userSession, - $this->knownUserService, - $this->shareDisabledChecker, - ); + $manager = $this->createManager($factory); $factory->setProvider($this->defaultProvider); $extraProvider = $this->createMock(IShareProvider::class); @@ -4407,9 +4347,9 @@ class ManagerTest extends \Test\TestCase { ->willReturn($userFolder); $folder->method('getPath') ->willReturn('/owner/files/folder'); - $userFolder->method('getById') + $userFolder->method('getFirstNodeById') ->with($this->equalTo(42)) - ->willReturn([12 => $file]); + ->willReturn($file); $userFolder->method('getPath') ->willReturn('/user1/files'); @@ -4467,26 +4407,7 @@ class ManagerTest extends \Test\TestCase { public function testGetAccessListWithCurrentAccess() { $factory = new DummyFactory2($this->createMock(IServerContainer::class)); - $manager = new Manager( - $this->logger, - $this->config, - $this->secureRandom, - $this->hasher, - $this->mountManager, - $this->groupManager, - $this->l, - $this->l10nFactory, - $factory, - $this->userManager, - $this->rootFolder, - $this->mailer, - $this->urlGenerator, - $this->defaults, - $this->dispatcher, - $this->userSession, - $this->knownUserService, - $this->shareDisabledChecker, - ); + $manager = $this->createManager($factory); $factory->setProvider($this->defaultProvider); $extraProvider = $this->createMock(IShareProvider::class); @@ -4525,9 +4446,9 @@ class ManagerTest extends \Test\TestCase { ->willReturn($userFolder); $folder->method('getPath') ->willReturn('/owner/files/folder'); - $userFolder->method('getById') + $userFolder->method('getFirstNodeById') ->with($this->equalTo(42)) - ->willReturn([42 => $file]); + ->willReturn($file); $userFolder->method('getPath') ->willReturn('/user1/files'); @@ -4594,26 +4515,7 @@ class ManagerTest extends \Test\TestCase { public function testGetAllShares() { $factory = new DummyFactory2($this->createMock(IServerContainer::class)); - $manager = new Manager( - $this->logger, - $this->config, - $this->secureRandom, - $this->hasher, - $this->mountManager, - $this->groupManager, - $this->l, - $this->l10nFactory, - $factory, - $this->userManager, - $this->rootFolder, - $this->mailer, - $this->urlGenerator, - $this->defaults, - $this->dispatcher, - $this->userSession, - $this->knownUserService, - $this->shareDisabledChecker - ); + $manager = $this->createManager($factory); $factory->setProvider($this->defaultProvider); $extraProvider = $this->createMock(IShareProvider::class); diff --git a/tests/lib/Template/JSResourceLocatorTest.php b/tests/lib/Template/JSResourceLocatorTest.php index f5af138060a..ae104d4d6ea 100644 --- a/tests/lib/Template/JSResourceLocatorTest.php +++ b/tests/lib/Template/JSResourceLocatorTest.php @@ -127,9 +127,9 @@ class JSResourceLocatorTest extends \Test\TestCase { $webRoot = $resource[1]; $file = $resource[2]; - $expectedRoot = $new_apps_path . '/test-js-app'; - $expectedWebRoot = \OC::$WEBROOT . '/js-apps-test/test-js-app'; - $expectedFile = 'test-file.js'; + $expectedRoot = $new_apps_path; + $expectedWebRoot = \OC::$WEBROOT . '/js-apps-test'; + $expectedFile = $appName . '/test-file.js'; $this->assertEquals($expectedRoot, $root, 'Ensure the app path symlink is resolved into the real path'); @@ -145,7 +145,7 @@ class JSResourceLocatorTest extends \Test\TestCase { ->method('getAppPath') ->with('core') ->willThrowException(new AppPathNotFoundException()); - $this->appManager->expects($this->once()) + $this->appManager->expects($this->atMost(1)) ->method('getAppWebPath') ->with('core') ->willThrowException(new AppPathNotFoundException()); diff --git a/tests/lib/TestCase.php b/tests/lib/TestCase.php index 14f1961ef2e..db124bd6823 100644 --- a/tests/lib/TestCase.php +++ b/tests/lib/TestCase.php @@ -263,6 +263,8 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase { } return $property->getValue(); + } elseif ($reflection->hasConstant($methodName)) { + return $reflection->getConstant($methodName); } return false; diff --git a/tests/lib/TextProcessing/TextProcessingTest.php b/tests/lib/TextProcessing/TextProcessingTest.php index 15f36cb2452..193c5d4c39d 100644 --- a/tests/lib/TextProcessing/TextProcessingTest.php +++ b/tests/lib/TextProcessing/TextProcessingTest.php @@ -24,13 +24,13 @@ use OCP\Common\Exception\NotFoundException; use OCP\EventDispatcher\IEventDispatcher; use OCP\IConfig; use OCP\IServerContainer; +use OCP\PreConditionNotMetException; use OCP\TextProcessing\Events\TaskFailedEvent; use OCP\TextProcessing\Events\TaskSuccessfulEvent; use OCP\TextProcessing\FreePromptTaskType; use OCP\TextProcessing\IManager; use OCP\TextProcessing\IProvider; use OCP\TextProcessing\SummaryTaskType; -use OCP\PreConditionNotMetException; use OCP\TextProcessing\Task; use OCP\TextProcessing\TopicsTaskType; use PHPUnit\Framework\Constraint\IsInstanceOf; diff --git a/tests/lib/Updater/ChangesCheckTest.php b/tests/lib/Updater/ChangesCheckTest.php index e96406622f4..4afb9f05a5b 100644 --- a/tests/lib/Updater/ChangesCheckTest.php +++ b/tests/lib/Updater/ChangesCheckTest.php @@ -26,15 +26,15 @@ declare(strict_types=1); namespace Test\Updater; +use OC\Updater\Changes; use OC\Updater\ChangesCheck; use OC\Updater\ChangesMapper; -use OC\Updater\Changes; use OCP\AppFramework\Db\DoesNotExistException; use OCP\Http\Client\IClient; use OCP\Http\Client\IClientService; use OCP\Http\Client\IResponse; -use Test\TestCase; use Psr\Log\LoggerInterface; +use Test\TestCase; class ChangesCheckTest extends TestCase { /** @var IClientService|\PHPUnit\Framework\MockObject\MockObject */ diff --git a/tests/lib/Updater/VersionCheckTest.php b/tests/lib/Updater/VersionCheckTest.php index 0f073abc3ce..ed04975fc54 100644 --- a/tests/lib/Updater/VersionCheckTest.php +++ b/tests/lib/Updater/VersionCheckTest.php @@ -24,6 +24,7 @@ namespace Test\Updater; use OC\Updater\VersionCheck; use OCP\Http\Client\IClientService; +use OCP\IAppConfig; use OCP\IConfig; use OCP\IUserManager; use OCP\Support\Subscription\IRegistry; @@ -33,6 +34,8 @@ use Psr\Log\LoggerInterface; class VersionCheckTest extends \Test\TestCase { /** @var IConfig| \PHPUnit\Framework\MockObject\MockObject */ private $config; + /** @var IAppConfig| \PHPUnit\Framework\MockObject\MockObject */ + private $appConfig; /** @var VersionCheck | \PHPUnit\Framework\MockObject\MockObject*/ private $updater; /** @var IRegistry | \PHPUnit\Framework\Mo2ckObject\MockObject*/ @@ -45,6 +48,9 @@ class VersionCheckTest extends \Test\TestCase { $this->config = $this->getMockBuilder(IConfig::class) ->disableOriginalConstructor() ->getMock(); + $this->appConfig = $this->getMockBuilder(IAppConfig::class) + ->disableOriginalConstructor() + ->getMock(); $clientService = $this->getMockBuilder(IClientService::class) ->disableOriginalConstructor() ->getMock(); @@ -59,6 +65,7 @@ class VersionCheckTest extends \Test\TestCase { ->setConstructorArgs([ $clientService, $this->config, + $this->appConfig, $this->createMock(IUserManager::class), $this->registry, $this->logger, @@ -71,7 +78,7 @@ class VersionCheckTest extends \Test\TestCase { * @return string */ private function buildUpdateUrl($baseUrl) { - return $baseUrl . '?version='.implode('x', Util::getVersion()).'xinstalledatxlastupdatedatx'.\OC_Util::getChannel().'xxx'.PHP_MAJOR_VERSION.'x'.PHP_MINOR_VERSION.'x'.PHP_RELEASE_VERSION.'x0x0'; + return $baseUrl . '?version='.implode('x', Util::getVersion()).'xinstalledatx' . time() . 'x'.\OC_Util::getChannel().'xxx'.PHP_MAJOR_VERSION.'x'.PHP_MINOR_VERSION.'x'.PHP_RELEASE_VERSION.'x0x0'; } public function testCheckInCache() { @@ -88,17 +95,16 @@ class VersionCheckTest extends \Test\TestCase { ->method('getSystemValueBool') ->with('has_internet_connection', true) ->willReturn(true); + $this->appConfig + ->expects($this->once()) + ->method('getValueInt') + ->with('core', 'lastupdatedat') + ->willReturn(time()); $this->config - ->expects($this->exactly(2)) + ->expects($this->once()) ->method('getAppValue') - ->withConsecutive( - ['core', 'lastupdatedat'], - ['core', 'lastupdateResult'] - ) - ->willReturnOnConsecutiveCalls( - time(), - json_encode($expectedResult) - ); + ->with('core', 'lastupdateResult') + ->willReturn(json_encode($expectedResult)); $this->assertSame($expectedResult, $this->updater->check()); } @@ -119,33 +125,32 @@ class VersionCheckTest extends \Test\TestCase { ->method('getSystemValueBool') ->with('has_internet_connection', true) ->willReturn(true); - $this->config - ->expects($this->exactly(4)) - ->method('getAppValue') - ->withConsecutive( - ['core', 'lastupdatedat'], - ['core', 'installedat'], - ['core', 'installedat'], - ['core', 'lastupdatedat'], - ) + $this->appConfig + ->expects($this->exactly(2)) + ->method('getValueInt') + ->with('core', 'lastupdatedat') ->willReturnOnConsecutiveCalls( - '0', - 'installedat', - 'installedat', - 'lastupdatedat', + 0, + time(), ); $this->config + ->expects($this->exactly(2)) + ->method('getAppValue') + ->with('core', 'installedat') + ->willReturn('installedat'); + $this->config ->expects($this->once()) ->method('getSystemValueString') ->with('updater.server.url', 'https://updates.nextcloud.com/updater_server/') ->willReturnArgument(1); + $this->appConfig + ->expects($this->once()) + ->method('setValueInt') + ->with('core', 'lastupdatedat', time()); $this->config - ->expects($this->exactly(2)) + ->expects($this->once()) ->method('setAppValue') - ->withConsecutive( - ['core', 'lastupdatedat', $this->isType('string')], - ['core', 'lastupdateResult', json_encode($expectedResult)] - ); + ->with('core', 'lastupdateResult', json_encode($expectedResult)); $updateXml = '<?xml version="1.0"?> <owncloud> @@ -171,33 +176,32 @@ class VersionCheckTest extends \Test\TestCase { ->method('getSystemValueBool') ->with('has_internet_connection', true) ->willReturn(true); - $this->config - ->expects($this->exactly(4)) - ->method('getAppValue') - ->withConsecutive( - ['core', 'lastupdatedat'], - ['core', 'installedat'], - ['core', 'installedat'], - ['core', 'lastupdatedat'], - ) + $this->appConfig + ->expects($this->exactly(2)) + ->method('getValueInt') + ->with('core', 'lastupdatedat') ->willReturnOnConsecutiveCalls( - '0', - 'installedat', - 'installedat', - 'lastupdatedat', + 0, + time(), ); $this->config + ->expects($this->exactly(2)) + ->method('getAppValue') + ->with('core', 'installedat') + ->willReturn('installedat'); + $this->config ->expects($this->once()) ->method('getSystemValueString') ->with('updater.server.url', 'https://updates.nextcloud.com/updater_server/') ->willReturnArgument(1); + $this->appConfig + ->expects($this->once()) + ->method('setValueInt') + ->with('core', 'lastupdatedat', time()); $this->config - ->expects($this->exactly(2)) + ->expects($this->once()) ->method('setAppValue') - ->withConsecutive( - ['core', 'lastupdatedat', $this->isType('string')], - ['core', 'lastupdateResult', '[]'] - ); + ->with('core', 'lastupdateResult', $this->isType('string')); $updateXml = 'Invalid XML Response!'; $this->updater @@ -225,33 +229,32 @@ class VersionCheckTest extends \Test\TestCase { ->method('getSystemValueBool') ->with('has_internet_connection', true) ->willReturn(true); - $this->config - ->expects($this->exactly(4)) - ->method('getAppValue') - ->withConsecutive( - ['core', 'lastupdatedat'], - ['core', 'installedat'], - ['core', 'installedat'], - ['core', 'lastupdatedat'], - ) + $this->appConfig + ->expects($this->exactly(2)) + ->method('getValueInt') + ->with('core', 'lastupdatedat') ->willReturnOnConsecutiveCalls( - '0', - 'installedat', - 'installedat', - 'lastupdatedat', + 0, + time(), ); $this->config + ->expects($this->exactly(2)) + ->method('getAppValue') + ->with('core', 'installedat') + ->willReturn('installedat'); + $this->config ->expects($this->once()) ->method('getSystemValueString') ->with('updater.server.url', 'https://updates.nextcloud.com/updater_server/') ->willReturnArgument(1); + $this->appConfig + ->expects($this->once()) + ->method('setValueInt') + ->with('core', 'lastupdatedat', time()); $this->config - ->expects($this->exactly(2)) + ->expects($this->once()) ->method('setAppValue') - ->withConsecutive( - ['core', 'lastupdatedat', $this->isType('string')], - ['core', 'lastupdateResult', $this->isType('string')] - ); + ->with('core', 'lastupdateResult', $this->isType('string')); $updateXml = '<?xml version="1.0"?> <owncloud> @@ -278,33 +281,32 @@ class VersionCheckTest extends \Test\TestCase { ->method('getSystemValueBool') ->with('has_internet_connection', true) ->willReturn(true); - $this->config - ->expects($this->exactly(4)) - ->method('getAppValue') - ->withConsecutive( - ['core', 'lastupdatedat'], - ['core', 'installedat'], - ['core', 'installedat'], - ['core', 'lastupdatedat'], - ) + $this->appConfig + ->expects($this->exactly(2)) + ->method('getValueInt') + ->with('core', 'lastupdatedat') ->willReturnOnConsecutiveCalls( - '0', - 'installedat', - 'installedat', - 'lastupdatedat', + 0, + time(), ); $this->config + ->expects($this->exactly(2)) + ->method('getAppValue') + ->with('core', 'installedat') + ->willReturn('installedat'); + $this->config ->expects($this->once()) ->method('getSystemValueString') ->with('updater.server.url', 'https://updates.nextcloud.com/updater_server/') ->willReturnArgument(1); + $this->appConfig + ->expects($this->once()) + ->method('setValueInt') + ->with('core', 'lastupdatedat', time()); $this->config - ->expects($this->exactly(2)) + ->expects($this->once()) ->method('setAppValue') - ->withConsecutive( - ['core', 'lastupdatedat', $this->isType('string')], - ['core', 'lastupdateResult', json_encode($expectedResult)] - ); + ->with('core', 'lastupdateResult', $this->isType('string')); $updateXml = ''; $this->updater @@ -332,33 +334,32 @@ class VersionCheckTest extends \Test\TestCase { ->method('getSystemValueBool') ->with('has_internet_connection', true) ->willReturn(true); - $this->config - ->expects($this->exactly(4)) - ->method('getAppValue') - ->withConsecutive( - ['core', 'lastupdatedat'], - ['core', 'installedat'], - ['core', 'installedat'], - ['core', 'lastupdatedat'], - ) + $this->appConfig + ->expects($this->exactly(2)) + ->method('getValueInt') + ->with('core', 'lastupdatedat') ->willReturnOnConsecutiveCalls( - '0', - 'installedat', - 'installedat', - 'lastupdatedat', + 0, + time(), ); $this->config + ->expects($this->exactly(2)) + ->method('getAppValue') + ->with('core', 'installedat') + ->willReturn('installedat'); + $this->config ->expects($this->once()) ->method('getSystemValueString') ->with('updater.server.url', 'https://updates.nextcloud.com/updater_server/') ->willReturnArgument(1); + $this->appConfig + ->expects($this->once()) + ->method('setValueInt') + ->with('core', 'lastupdatedat', time()); $this->config - ->expects($this->exactly(2)) + ->expects($this->once()) ->method('setAppValue') - ->withConsecutive( - ['core', 'lastupdatedat', $this->isType('string')], - ['core', 'lastupdateResult', $this->isType('string')] - ); + ->with('core', 'lastupdateResult', $this->isType('string')); // missing autoupdater element should still not fail $updateXml = '<?xml version="1.0"?> diff --git a/tests/lib/UpdaterTest.php b/tests/lib/UpdaterTest.php index 579761208db..bff50de47e2 100644 --- a/tests/lib/UpdaterTest.php +++ b/tests/lib/UpdaterTest.php @@ -22,16 +22,19 @@ namespace Test; -use PHPUnit\Framework\MockObject\MockObject; -use Psr\Log\LoggerInterface; use OC\Installer; use OC\IntegrityCheck\Checker; use OC\Updater; +use OCP\IAppConfig; use OCP\IConfig; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; class UpdaterTest extends TestCase { /** @var IConfig|MockObject */ private $config; + /** @var IAppConfig|MockObject */ + private $appConfig; /** @var LoggerInterface|MockObject */ private $logger; /** @var Updater */ @@ -46,6 +49,9 @@ class UpdaterTest extends TestCase { $this->config = $this->getMockBuilder(IConfig::class) ->disableOriginalConstructor() ->getMock(); + $this->appConfig = $this->getMockBuilder(IAppConfig::class) + ->disableOriginalConstructor() + ->getMock(); $this->logger = $this->getMockBuilder(LoggerInterface::class) ->disableOriginalConstructor() ->getMock(); @@ -58,6 +64,7 @@ class UpdaterTest extends TestCase { $this->updater = new Updater( $this->config, + $this->appConfig, $this->checker, $this->logger, $this->installer diff --git a/tests/lib/UrlGeneratorTest.php b/tests/lib/UrlGeneratorTest.php index dac15798a98..51653d74364 100644 --- a/tests/lib/UrlGeneratorTest.php +++ b/tests/lib/UrlGeneratorTest.php @@ -192,26 +192,34 @@ class UrlGeneratorTest extends \Test\TestCase { /** * @dataProvider provideOCSRoutes */ - public function testLinkToOCSRouteAbsolute(string $route, string $expected) { + public function testLinkToOCSRouteAbsolute(string $route, bool $ignoreFrontController, string $expected): void { $this->mockBaseUrl(); \OC::$WEBROOT = '/nextcloud'; $this->router->expects($this->once()) ->method('generate') - ->willReturnCallback(function ($routeName, $parameters) { + ->willReturnCallback(function (string $routeName, array $parameters) use ($ignoreFrontController) { if ($routeName === 'ocs.core.OCS.getCapabilities') { - return '/index.php/ocsapp/cloud/capabilities'; + if (!$ignoreFrontController) { + return '/nextcloud/index.php/ocsapp/cloud/capabilities'; + } + return '/nextcloud/ocsapp/cloud/capabilities'; } elseif ($routeName === 'ocs.core.WhatsNew.dismiss') { - return '/index.php/ocsapp/core/whatsnew'; + if (!$ignoreFrontController) { + return '/nextcloud/index.php/ocsapp/core/whatsnew'; + } + return '/nextcloud/ocsapp/core/whatsnew'; } }); $result = $this->urlGenerator->linkToOCSRouteAbsolute($route); $this->assertEquals($expected, $result); } - public function provideOCSRoutes() { + public function provideOCSRoutes(): array { return [ - ['core.OCS.getCapabilities', 'http://localhost/nextcloud/ocs/v2.php/cloud/capabilities'], - ['core.WhatsNew.dismiss', 'http://localhost/nextcloud/ocs/v2.php/core/whatsnew'], + ['core.OCS.getCapabilities', false, 'http://localhost/nextcloud/ocs/v2.php/cloud/capabilities'], + ['core.OCS.getCapabilities', true, 'http://localhost/nextcloud/ocs/v2.php/cloud/capabilities'], + ['core.WhatsNew.dismiss', false, 'http://localhost/nextcloud/ocs/v2.php/core/whatsnew'], + ['core.WhatsNew.dismiss', true, 'http://localhost/nextcloud/ocs/v2.php/core/whatsnew'], ]; } diff --git a/tests/lib/User/AvailabilityCoordinatorTest.php b/tests/lib/User/AvailabilityCoordinatorTest.php index 8e847f7e5d5..b41b1fbac2a 100644 --- a/tests/lib/User/AvailabilityCoordinatorTest.php +++ b/tests/lib/User/AvailabilityCoordinatorTest.php @@ -28,11 +28,14 @@ namespace Test\User; use OC\User\AvailabilityCoordinator; use OC\User\OutOfOfficeData; +use OCA\DAV\CalDAV\TimezoneService; use OCA\DAV\Db\Absence; -use OCA\DAV\Db\AbsenceMapper; +use OCA\DAV\Service\AbsenceService; use OCP\ICache; use OCP\ICacheFactory; +use OCP\IConfig; use OCP\IUser; +use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; use Test\TestCase; @@ -40,16 +43,20 @@ class AvailabilityCoordinatorTest extends TestCase { private AvailabilityCoordinator $availabilityCoordinator; private ICacheFactory $cacheFactory; private ICache $cache; - private AbsenceMapper $absenceMapper; + private IConfig|MockObject $config; + private AbsenceService $absenceService; private LoggerInterface $logger; + private MockObject|TimezoneService $timezoneService; protected function setUp(): void { parent::setUp(); $this->cacheFactory = $this->createMock(ICacheFactory::class); $this->cache = $this->createMock(ICache::class); - $this->absenceMapper = $this->createMock(AbsenceMapper::class); + $this->absenceService = $this->createMock(AbsenceService::class); + $this->config = $this->createMock(IConfig::class); $this->logger = $this->createMock(LoggerInterface::class); + $this->timezoneService = $this->createMock(TimezoneService::class); $this->cacheFactory->expects(self::once()) ->method('createLocal') @@ -57,12 +64,25 @@ class AvailabilityCoordinatorTest extends TestCase { $this->availabilityCoordinator = new AvailabilityCoordinator( $this->cacheFactory, - $this->absenceMapper, + $this->config, + $this->absenceService, $this->logger, + $this->timezoneService, ); } - public function testGetOutOfOfficeData(): void { + public function testIsEnabled(): void { + $this->config->expects(self::once()) + ->method('getAppValue') + ->with('dav', 'hide_absence_settings', 'no') + ->willReturn('no'); + + $isEnabled = $this->availabilityCoordinator->isEnabled(); + + self::assertTrue($isEnabled); + } + + public function testGetOutOfOfficeDataInEffect(): void { $absence = new Absence(); $absence->setId(420); $absence->setUserId('user'); @@ -70,28 +90,29 @@ class AvailabilityCoordinatorTest extends TestCase { $absence->setLastDay('2023-10-08'); $absence->setStatus('Vacation'); $absence->setMessage('On vacation'); + $this->timezoneService->method('getUserTimezone')->with('user')->willReturn('Europe/Berlin'); $user = $this->createMock(IUser::class); $user->method('getUID') ->willReturn('user'); - $this->cache->expects(self::once()) + $this->cache->expects(self::exactly(2)) ->method('get') - ->with('user') - ->willReturn(null); - $this->absenceMapper->expects(self::once()) - ->method('findByUserId') - ->with('user') + ->willReturnOnConsecutiveCalls(null, null); + $this->absenceService->expects(self::once()) + ->method('getAbsence') + ->with($user->getUID()) ->willReturn($absence); - $this->cache->expects(self::once()) + $this->cache->expects(self::exactly(2)) ->method('set') - ->with('user', '{"id":"420","startDate":1696118400,"endDate":1696723200,"shortMessage":"Vacation","message":"On vacation"}', 300); + ->withConsecutive([$user->getUID() . '_timezone', 'Europe/Berlin', 3600], + [$user->getUID(), '{"id":"420","startDate":1696111200,"endDate":1696802340,"shortMessage":"Vacation","message":"On vacation"}', 300]); $expected = new OutOfOfficeData( '420', $user, - 1696118400, - 1696723200, + 1696111200, + 1696802340, 'Vacation', 'On vacation', ); @@ -99,25 +120,32 @@ class AvailabilityCoordinatorTest extends TestCase { self::assertEquals($expected, $actual); } - public function testGetOutOfOfficeDataWithCachedData(): void { + public function testGetOutOfOfficeDataCachedAll(): void { + $absence = new Absence(); + $absence->setId(420); + $absence->setUserId('user'); + $absence->setFirstDay('2023-10-01'); + $absence->setLastDay('2023-10-08'); + $absence->setStatus('Vacation'); + $absence->setMessage('On vacation'); + $user = $this->createMock(IUser::class); $user->method('getUID') ->willReturn('user'); - $this->cache->expects(self::once()) + $this->cache->expects(self::exactly(2)) ->method('get') - ->with('user') - ->willReturn('{"id":"420","startDate":1696118400,"endDate":1696723200,"shortMessage":"Vacation","message":"On vacation"}'); - $this->absenceMapper->expects(self::never()) - ->method('findByUserId'); - $this->cache->expects(self::never()) + ->willReturnOnConsecutiveCalls('UTC', '{"id":"420","startDate":1696118400,"endDate":1696809540,"shortMessage":"Vacation","message":"On vacation"}'); + $this->absenceService->expects(self::never()) + ->method('getAbsence'); + $this->cache->expects(self::exactly(1)) ->method('set'); $expected = new OutOfOfficeData( '420', $user, 1696118400, - 1696723200, + 1696809540, 'Vacation', 'On vacation', ); @@ -125,6 +153,32 @@ class AvailabilityCoordinatorTest extends TestCase { self::assertEquals($expected, $actual); } + public function testGetOutOfOfficeDataNoData(): void { + $absence = new Absence(); + $absence->setId(420); + $absence->setUserId('user'); + $absence->setFirstDay('2023-10-01'); + $absence->setLastDay('2023-10-08'); + $absence->setStatus('Vacation'); + $absence->setMessage('On vacation'); + + $user = $this->createMock(IUser::class); + $user->method('getUID') + ->willReturn('user'); + + $this->cache->expects(self::exactly(2)) + ->method('get') + ->willReturnOnConsecutiveCalls('UTC', null); + $this->absenceService->expects(self::once()) + ->method('getAbsence') + ->willReturn(null); + $this->cache->expects(self::never()) + ->method('set'); + + $actual = $this->availabilityCoordinator->getCurrentOutOfOfficeData($user); + self::assertNull($actual); + } + public function testGetOutOfOfficeDataWithInvalidCachedData(): void { $absence = new Absence(); $absence->setId(420); @@ -133,28 +187,28 @@ class AvailabilityCoordinatorTest extends TestCase { $absence->setLastDay('2023-10-08'); $absence->setStatus('Vacation'); $absence->setMessage('On vacation'); + $this->timezoneService->method('getUserTimezone')->with('user')->willReturn('Europe/Berlin'); $user = $this->createMock(IUser::class); $user->method('getUID') ->willReturn('user'); - $this->cache->expects(self::once()) + $this->cache->expects(self::exactly(2)) ->method('get') - ->with('user') - ->willReturn('{"id":"420",}'); - $this->absenceMapper->expects(self::once()) - ->method('findByUserId') + ->willReturnOnConsecutiveCalls('UTC', '{"id":"420",}'); + $this->absenceService->expects(self::once()) + ->method('getAbsence') ->with('user') ->willReturn($absence); $this->cache->expects(self::once()) ->method('set') - ->with('user', '{"id":"420","startDate":1696118400,"endDate":1696723200,"shortMessage":"Vacation","message":"On vacation"}', 300); + ->with('user', '{"id":"420","startDate":1696118400,"endDate":1696809540,"shortMessage":"Vacation","message":"On vacation"}', 300); $expected = new OutOfOfficeData( '420', $user, 1696118400, - 1696723200, + 1696809540, 'Vacation', 'On vacation', ); diff --git a/tests/lib/User/ManagerTest.php b/tests/lib/User/ManagerTest.php index aa597f758b0..5e8c2ed7131 100644 --- a/tests/lib/User/ManagerTest.php +++ b/tests/lib/User/ManagerTest.php @@ -434,7 +434,7 @@ class ManagerTest extends TestCase { public function testCreateUserFromBackendWithBackendError() { $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('Could not create user'); + $this->expectExceptionMessage('Could not create account'); /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject $config */ $config = $this->createMock(IConfig::class); diff --git a/tests/lib/User/SessionTest.php b/tests/lib/User/SessionTest.php index b6ac7a69fed..50adda64afd 100644 --- a/tests/lib/User/SessionTest.php +++ b/tests/lib/User/SessionTest.php @@ -14,6 +14,7 @@ use OC\Authentication\Exceptions\InvalidTokenException; use OC\Authentication\Exceptions\PasswordLoginForbiddenException; use OC\Authentication\Token\IProvider; use OC\Authentication\Token\IToken; +use OC\Security\CSRF\CsrfTokenManager; use OC\Session\Memory; use OC\User\LoginException; use OC\User\Manager; @@ -34,7 +35,6 @@ use OCP\Security\ISecureRandom; use OCP\User\Events\PostLoginEvent; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; -use OC\Security\CSRF\CsrfTokenManager; /** * @group DB @@ -479,6 +479,56 @@ class SessionTest extends \Test\TestCase { $userSession->logClientIn('john', 'doe', $request, $this->throttler); } + public function testTryTokenLoginNoHeaderNoSessionCookie(): void { + $request = $this->createMock(IRequest::class); + $this->config->expects(self::once()) + ->method('getSystemValueString') + ->with('instanceid') + ->willReturn('abc123'); + $request->method('getHeader')->with('Authorization')->willReturn(''); + $request->method('getCookie')->with('abc123')->willReturn(null); + $this->tokenProvider->expects(self::never()) + ->method('getToken'); + + $loginResult = $this->userSession->tryTokenLogin($request); + + self::assertFalse($loginResult); + } + + public function testTryTokenLoginAuthorizationHeaderTokenNotFound(): void { + $request = $this->createMock(IRequest::class); + $request->method('getHeader')->with('Authorization')->willReturn('Bearer abcde-12345'); + $this->tokenProvider->expects(self::once()) + ->method('getToken') + ->with('abcde-12345') + ->willThrowException(new InvalidTokenException()); + + $loginResult = $this->userSession->tryTokenLogin($request); + + self::assertFalse($loginResult); + } + + public function testTryTokenLoginSessionIdTokenNotFound(): void { + $request = $this->createMock(IRequest::class); + $this->config->expects(self::once()) + ->method('getSystemValueString') + ->with('instanceid') + ->willReturn('abc123'); + $request->method('getHeader')->with('Authorization')->willReturn(''); + $request->method('getCookie')->with('abc123')->willReturn('abcde12345'); + $this->session->expects(self::once()) + ->method('getId') + ->willReturn('abcde12345'); + $this->tokenProvider->expects(self::once()) + ->method('getToken') + ->with('abcde12345') + ->willThrowException(new InvalidTokenException()); + + $loginResult = $this->userSession->tryTokenLogin($request); + + self::assertFalse($loginResult); + } + public function testRememberLoginValidToken() { $session = $this->getMockBuilder(Memory::class)->setConstructorArgs([''])->getMock(); $managerMethods = get_class_methods(Manager::class); @@ -1110,7 +1160,7 @@ class SessionTest extends \Test\TestCase { $userSession->expects($this->once()) ->method('isTokenPassword') - ->willReturn(true); + ->willReturn(false); $userSession->expects($this->once()) ->method('login') ->with('john@foo.bar', 'I-AM-AN-PASSWORD') diff --git a/tests/lib/Util/Group/Dummy.php b/tests/lib/Util/Group/Dummy.php index a864c8ce9d9..fd784e4fa46 100644 --- a/tests/lib/Util/Group/Dummy.php +++ b/tests/lib/Util/Group/Dummy.php @@ -29,13 +29,13 @@ namespace Test\Util\Group; -use Test\Util\User\Dummy as DummyUser; use OCP\Group\Backend\ABackend; -use OCP\Group\Backend\IDeleteGroupBackend; use OCP\Group\Backend\IAddToGroupBackend; -use OCP\Group\Backend\IRemoveFromGroupBackend; -use OCP\Group\Backend\ICreateGroupBackend; use OCP\Group\Backend\ICountUsersBackend; +use OCP\Group\Backend\ICreateGroupBackend; +use OCP\Group\Backend\IDeleteGroupBackend; +use OCP\Group\Backend\IRemoveFromGroupBackend; +use Test\Util\User\Dummy as DummyUser; /** * Dummy group backend, does not keep state, only for testing use diff --git a/tests/preseed-config.php b/tests/preseed-config.php index 16aea87c8a7..c62b4471280 100644 --- a/tests/preseed-config.php +++ b/tests/preseed-config.php @@ -25,71 +25,52 @@ if (getenv('OBJECT_STORE') === 's3') { 'arguments' => [ 'bucket' => 'nextcloud', 'autocreate' => true, - 'key' => 'nextcloud', - 'secret' => 'nextcloud', - 'hostname' => getenv('DRONE') === 'true' ? 'minio' : 'localhost', + 'key' => getenv('OBJECT_STORE_KEY') ?: 'nextcloud', + 'secret' => getenv('OBJECT_STORE_SECRET') ?: 'nextcloud', + 'hostname' => getenv('OBJECT_STORE_HOST') ?: 'localhost', 'port' => 9000, 'use_ssl' => false, // required for some non amazon s3 implementations 'use_path_style' => true ] ]; -} -if (getenv('OBJECT_STORE') === 'swift') { - $swiftHost = getenv('DRONE') === 'true' ? 'dockswift' : 'localhost'; - - if (getenv('SWIFT-AUTH') === 'v2.0') { - $CONFIG['objectstore'] = [ - 'class' => 'OC\\Files\\ObjectStore\\Swift', - 'arguments' => [ - 'autocreate' => true, - 'username' => 'swift', - 'tenantName' => 'service', - 'password' => 'swift', - 'serviceName' => 'swift', - 'region' => 'regionOne', - 'url' => "http://$swiftHost:5000/v2.0", - 'bucket' => 'nextcloud' - ] - ]; - } else { - $CONFIG['objectstore'] = [ - 'class' => 'OC\\Files\\ObjectStore\\Swift', - 'arguments' => [ - 'autocreate' => true, - 'user' => [ - 'name' => 'swift', - 'password' => 'swift', - 'domain' => [ - 'name' => 'default', - ] - ], - 'scope' => [ - 'project' => [ - 'name' => 'service', - 'domain' => [ - 'name' => 'default', - ], - ], - ], - 'tenantName' => 'service', - 'serviceName' => 'swift', - 'region' => 'regionOne', - 'url' => "http://$swiftHost:5000/v3", - 'bucket' => 'nextcloud' - ] - ]; - } -} -if (getenv('OBJECT_STORE') === 'azure') { +} elseif (getenv('OBJECT_STORE') === 'azure') { $CONFIG['objectstore'] = [ 'class' => 'OC\\Files\\ObjectStore\\Azure', 'arguments' => [ 'container' => 'test', - 'account_name' => 'devstoreaccount1', - 'account_key' => 'Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==', - 'endpoint' => 'http://' . (getenv('DRONE') === 'true' ? 'azurite' : 'localhost') . ':10000/devstoreaccount1', + 'account_name' => getenv('OBJECT_STORE_KEY') ?: 'devstoreaccount1', + 'account_key' => getenv('OBJECT_STORE_SECRET') ?: 'Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==', + 'endpoint' => 'http://' . (getenv('OBJECT_STORE_HOST') ?: 'localhost') . ':10000/' . (getenv('OBJECT_STORE_KEY') ?: 'devstoreaccount1'), 'autocreate' => true ] ]; +} elseif (getenv('OBJECT_STORE') === 'swift') { + $swiftHost = getenv('OBJECT_STORE_HOST') ?: 'localhost:5000'; + + $CONFIG['objectstore'] = [ + 'class' => 'OC\\Files\\ObjectStore\\Swift', + 'arguments' => [ + 'autocreate' => true, + 'user' => [ + 'name' => getenv('OBJECT_STORE_KEY') ?: 'swift', + 'password' => getenv('OBJECT_STORE_SECRET') ?: 'swift', + 'domain' => [ + 'name' => 'Default', + ], + ], + 'scope' => [ + 'project' => [ + 'name' => 'service', + 'domain' => [ + 'name' => 'Default', + ], + ], + ], + 'serviceName' => 'service', + 'region' => 'RegionOne', + 'url' => "http://$swiftHost/v3", + 'bucket' => 'nextcloud', + ] + ]; } diff --git a/tests/redis.config.php b/tests/redis.config.php index 9d3f1eca63e..2ff46ec6728 100644 --- a/tests/redis.config.php +++ b/tests/redis.config.php @@ -5,7 +5,7 @@ $CONFIG = [ 'memcache.distributed' => '\\OC\\Memcache\\Redis', 'memcache.locking' => '\\OC\\Memcache\\Redis', 'redis' => [ - 'host' => 'cache', + 'host' => 'localhost', 'port' => 6379, 'timeout' => 0, ], |