diff options
author | John Molakvoæ <skjnldsv@users.noreply.github.com> | 2024-03-15 13:03:34 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-03-15 13:03:34 +0100 |
commit | 9338ef36ded767f2c35b7ec575b351859420ed09 (patch) | |
tree | 65c53c6a36f300859dc22b2d423275bcf2911367 /tests/lib | |
parent | 6b09a79227a5dc98aa4620c6e5e15b610a06c806 (diff) | |
parent | df1cd1ba7e6e1f6e66a2b3229b5c082f1b81162e (diff) | |
download | nextcloud-server-9338ef36ded767f2c35b7ec575b351859420ed09.tar.gz nextcloud-server-9338ef36ded767f2c35b7ec575b351859420ed09.zip |
Merge branch 'master' into refactor/OC-Server-getShareManager
Signed-off-by: John Molakvoæ <skjnldsv@users.noreply.github.com>
Diffstat (limited to 'tests/lib')
126 files changed, 6485 insertions, 1840 deletions
diff --git a/tests/lib/Accounts/AccountManagerTest.php b/tests/lib/Accounts/AccountManagerTest.php index d12dfbfacea..3d0bee5902f 100644 --- a/tests/lib/Accounts/AccountManagerTest.php +++ b/tests/lib/Accounts/AccountManagerTest.php @@ -26,6 +26,7 @@ namespace Test\Accounts; use OC\Accounts\Account; use OC\Accounts\AccountManager; +use OC\PhoneNumberUtil; use OCA\Settings\BackgroundJobs\VerifyUserData; use OCP\Accounts\IAccountManager; use OCP\Accounts\UserUpdatedEvent; @@ -34,6 +35,7 @@ use OCP\Defaults; use OCP\EventDispatcher\IEventDispatcher; use OCP\IConfig; use OCP\IDBConnection; +use OCP\IPhoneNumberUtil; use OCP\IURLGenerator; use OCP\IUser; use OCP\L10N\IFactory; @@ -75,6 +77,8 @@ class AccountManagerTest extends TestCase { /** @var IJobList|MockObject */ private $jobList; + /** @var IPhoneNumberUtil */ + private $phoneNumberUtil; /** accounts table name */ private string $table = 'accounts'; @@ -97,6 +101,7 @@ class AccountManagerTest extends TestCase { $this->l10nFactory = $this->createMock(IFactory::class); $this->urlGenerator = $this->createMock(IURLGenerator::class); $this->crypto = $this->createMock(ICrypto::class); + $this->phoneNumberUtil = new PhoneNumberUtil(); $this->accountManager = new AccountManager( $this->connection, @@ -109,7 +114,8 @@ class AccountManagerTest extends TestCase { $this->defaults, $this->l10nFactory, $this->urlGenerator, - $this->crypto + $this->crypto, + $this->phoneNumberUtil, ); } @@ -473,7 +479,8 @@ class AccountManagerTest extends TestCase { $this->defaults, $this->l10nFactory, $this->urlGenerator, - $this->crypto + $this->crypto, + $this->phoneNumberUtil, ]) ->onlyMethods($mockedMethods) ->getMock(); diff --git a/tests/lib/App/AppManagerTest.php b/tests/lib/App/AppManagerTest.php index 3bf2195499f..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 = [ @@ -607,21 +741,124 @@ class AppManagerTest extends TestCase { // none specified, default to files [ '', + '', + '{}', + true, 'files', ], + // none specified, without fallback + [ + '', + '', + '{}', + false, + '', + ], // unexisting or inaccessible app specified, default to files [ 'unexist', + '', + '{}', + true, 'files', ], + // unexisting or inaccessible app specified, without fallbacks + [ + 'unexist', + '', + '{}', + false, + '', + ], // non-standard app [ 'settings', + '', + '{}', + true, + 'settings', + ], + // non-standard app, without fallback + [ + 'settings', + '', + '{}', + false, 'settings', ], // non-standard app with fallback [ 'unexist,settings', + '', + '{}', + 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 + [ + '', + 'files', + '', + true, + 'files', + ], + // user-customized defaultapp with systemwide + [ + 'unexist,settings', + 'files', + '', + true, + 'files', + ], + // user-customized defaultapp with system wide and apporder + [ + 'unexist,settings', + 'files', + '{"settings_id":{"app":"settings","order":1},"files_id":{"app":"files","order":2}}', + true, + 'files', + ], + // user-customized apporder fallback + [ + '', + '', + '{"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":{"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', ], ]; @@ -630,7 +867,7 @@ class AppManagerTest extends TestCase { /** * @dataProvider provideDefaultApps */ - public function testGetDefaultAppForUser($defaultApps, $expectedApp) { + public function testGetDefaultAppForUser($defaultApps, $userDefaultApps, $userApporder, $withFallbacks, $expectedApp) { $user = $this->newUser('user1'); $this->userSession->expects($this->once()) @@ -642,11 +879,13 @@ class AppManagerTest extends TestCase { ->with('defaultapp', $this->anything()) ->willReturn($defaultApps); - $this->config->expects($this->once()) + $this->config->expects($this->atLeastOnce()) ->method('getUserValue') - ->with('user1', 'core', 'defaultapp') - ->willReturn(''); + ->willReturnMap([ + ['user1', 'core', 'defaultapp', '', $userDefaultApps], + ['user1', 'core', 'apporder', '[]', $userApporder], + ]); - $this->assertEquals($expectedApp, $this->manager->getDefaultAppForUser()); + $this->assertEquals($expectedApp, $this->manager->getDefaultAppForUser(null, $withFallbacks)); } } 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/Http/ResponseTest.php b/tests/lib/AppFramework/Http/ResponseTest.php index c1c122e789e..6fb7a0155b4 100644 --- a/tests/lib/AppFramework/Http/ResponseTest.php +++ b/tests/lib/AppFramework/Http/ResponseTest.php @@ -229,7 +229,6 @@ class ResponseTest extends \Test\TestCase { $headers = $this->childResponse->getHeaders(); $this->assertEquals('no-cache, no-store, must-revalidate', $headers['Cache-Control']); - $this->assertFalse(isset($headers['Pragma'])); $this->assertFalse(isset($headers['Expires'])); } @@ -245,7 +244,6 @@ class ResponseTest extends \Test\TestCase { $headers = $this->childResponse->getHeaders(); $this->assertEquals('private, max-age=33, must-revalidate', $headers['Cache-Control']); - $this->assertEquals('private', $headers['Pragma']); $this->assertEquals('Thu, 15 Jan 1970 06:56:40 +0000', $headers['Expires']); } 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/SimpleContainerTest.php b/tests/lib/AppFramework/Utility/SimpleContainerTest.php index 61b3299671b..054012bdd5d 100644 --- a/tests/lib/AppFramework/Utility/SimpleContainerTest.php +++ b/tests/lib/AppFramework/Utility/SimpleContainerTest.php @@ -50,6 +50,17 @@ class ClassComplexConstructor { } } +class ClassNullableUntypedConstructorArg { + public function __construct($class) { + } +} +class ClassNullableTypedConstructorArg { + public $class; + public function __construct(?\Some\Class $class) { + $this->class = $class; + } +} + interface IInterfaceConstructor { } class ClassInterfaceConstructor { @@ -243,4 +254,17 @@ class SimpleContainerTest extends \Test\TestCase { $this->assertNotSame( $this->container->query('test'), $this->container->query('test1')); } + + public function testQueryUntypedNullable(): void { + $this->expectException(\OCP\AppFramework\QueryException::class); + + $this->container->query(ClassNullableUntypedConstructorArg::class); + } + + public function testQueryTypedNullable(): void { + /** @var ClassNullableTypedConstructorArg $service */ + $service = $this->container->query(ClassNullableTypedConstructorArg::class); + + self::assertNull($service->class); + } } 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 8574f462ca7..05a9e5928c2 100644 --- a/tests/lib/BackgroundJob/DummyJobList.php +++ b/tests/lib/BackgroundJob/DummyJobList.php @@ -35,7 +35,7 @@ class DummyJobList extends \OC\BackgroundJob\JobList { * @param IJob|class-string<IJob> $job * @param mixed $argument */ - public function add($job, $argument = null): void { + public function add($job, $argument = null, int $firstCheck = null): void { if (is_string($job)) { /** @var IJob $job */ $job = \OCP\Server::get($job); @@ -46,6 +46,10 @@ class DummyJobList extends \OC\BackgroundJob\JobList { } } + public function scheduleAfter(string $job, int $runAfter, $argument = null): void { + $this->add($job, $argument, $runAfter); + } + /** * @param IJob|string $job * @param mixed $argument @@ -112,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 100de0a861c..4b3c7dca95a 100644 --- a/tests/lib/Command/CronBusTest.php +++ b/tests/lib/Command/CronBusTest.php @@ -44,14 +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); } } - - public function testClosureFromPreviousVersion() { - $serializedClosure = 'C:32:"Opis\\Closure\\SerializableClosure":217:{a:5:{s:3:"use";a:0:{}s:8:"function";s:64:"function () {\\Test\\Command\\AsyncBusTest::$lastCommand = \'opis\';}";s:5:"scope";s:24:"Test\\Command\\CronBusTest";s:4:"this";N;s:4:"self";s:32:"0000000027dcfe2f00000000407fa805";}}'; - $this->jobList->add('OC\Command\ClosureJob', $serializedClosure); - $this->runJobs(); - $this->assertEquals('opis', AsyncBusTest::$lastCommand); - } } 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 ccb888a6c3d..69805bf81f2 100644 --- a/tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php +++ b/tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php @@ -1,4 +1,7 @@ <?php + +declare(strict_types=1); + /** * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at> * @copyright 2017 Lukas Reschke <lukas@statuscode.ch> @@ -28,6 +31,8 @@ namespace Tests\Contacts\ContactsMenu; use OC\Contacts\ContactsMenu\ContactsStore; use OC\KnownUser\KnownUserService; use OC\Profile\ProfileManager; +use OCA\UserStatus\Db\UserStatus; +use OCA\UserStatus\Service\StatusService; use OCP\Contacts\IManager; use OCP\IConfig; use OCP\IGroupManager; @@ -40,6 +45,7 @@ use Test\TestCase; class ContactsStoreTest extends TestCase { private ContactsStore $contactsStore; + private StatusService|MockObject $statusService; /** @var IManager|MockObject */ private $contactsManager; /** @var ProfileManager */ @@ -61,6 +67,7 @@ class ContactsStoreTest extends TestCase { parent::setUp(); $this->contactsManager = $this->createMock(IManager::class); + $this->statusService = $this->createMock(StatusService::class); $this->userManager = $this->createMock(IUserManager::class); $this->profileManager = $this->createMock(ProfileManager::class); $this->urlGenerator = $this->createMock(IURLGenerator::class); @@ -70,13 +77,14 @@ class ContactsStoreTest extends TestCase { $this->l10nFactory = $this->createMock(IL10NFactory::class); $this->contactsStore = new ContactsStore( $this->contactsManager, + $this->statusService, $this->config, $this->profileManager, $this->userManager, $this->urlGenerator, $this->groupManager, $this->knownUserService, - $this->l10nFactory + $this->l10nFactory, ); } @@ -209,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 */ @@ -252,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 */ @@ -326,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 */ @@ -458,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 */ @@ -611,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 */ @@ -964,4 +977,118 @@ class ContactsStoreTest extends TestCase { $this->assertEquals(null, $entry); } + + public function testGetRecentStatusFirst(): void { + $user = $this->createMock(IUser::class); + $status1 = new UserStatus(); + $status1->setUserId('user1'); + $status2 = new UserStatus(); + $status2->setUserId('user2'); + $this->statusService->expects(self::once()) + ->method('findAllRecentStatusChanges') + ->willReturn([ + $status1, + $status2, + ]); + $user1 = $this->createMock(IUser::class); + $user1->method('getCloudId')->willReturn('user1@localcloud'); + $user2 = $this->createMock(IUser::class); + $user2->method('getCloudId')->willReturn('user2@localcloud'); + $this->userManager->expects(self::exactly(2)) + ->method('get') + ->willReturnCallback(function ($uid) use ($user1, $user2) { + return match ($uid) { + 'user1' => $user1, + 'user2' => $user2, + }; + }); + $this->contactsManager + ->expects(self::exactly(3)) + ->method('search') + ->willReturnCallback(function ($uid, $searchProps, $options) { + return match ([$uid, $options['limit'] ?? null]) { + ['user1@localcloud', 1] => [ + [ + 'UID' => 'user1', + 'URI' => 'user1.vcf', + ], + ], + ['user2@localcloud' => [], 1], // Simulate not found + ['', 4] => [ + [ + 'UID' => 'contact1', + 'URI' => 'contact1.vcf', + ], + [ + 'UID' => 'contact2', + 'URI' => 'contact2.vcf', + ], + ], + default => [], + }; + }); + + $contacts = $this->contactsStore->getContacts( + $user, + null, + 5, + ); + + self::assertCount(3, $contacts); + self::assertEquals('user1', $contacts[0]->getProperty('UID')); + self::assertEquals('contact1', $contacts[1]->getProperty('UID')); + self::assertEquals('contact2', $contacts[2]->getProperty('UID')); + } + + public function testPaginateRecentStatus(): void { + $user = $this->createMock(IUser::class); + $status1 = new UserStatus(); + $status1->setUserId('user1'); + $status2 = new UserStatus(); + $status2->setUserId('user2'); + $status3 = new UserStatus(); + $status3->setUserId('user3'); + $this->statusService->expects(self::never()) + ->method('findAllRecentStatusChanges'); + $this->contactsManager + ->expects(self::exactly(2)) + ->method('search') + ->willReturnCallback(function ($uid, $searchProps, $options) { + return match ([$uid, $options['limit'] ?? null, $options['offset'] ?? null]) { + ['', 2, 0] => [ + [ + 'UID' => 'contact1', + 'URI' => 'contact1.vcf', + ], + [ + 'UID' => 'contact2', + 'URI' => 'contact2.vcf', + ], + ], + ['', 2, 3] => [ + [ + 'UID' => 'contact3', + 'URI' => 'contact3.vcf', + ], + ], + default => [], + }; + }); + + $page1 = $this->contactsStore->getContacts( + $user, + null, + 2, + 0, + ); + $page2 = $this->contactsStore->getContacts( + $user, + null, + 2, + 3, + ); + + self::assertCount(2, $page1); + self::assertCount(1, $page2); + } } diff --git a/tests/lib/Contacts/ContactsMenu/EntryTest.php b/tests/lib/Contacts/ContactsMenu/EntryTest.php index 684edd9f25e..253ec321365 100644 --- a/tests/lib/Contacts/ContactsMenu/EntryTest.php +++ b/tests/lib/Contacts/ContactsMenu/EntryTest.php @@ -103,6 +103,12 @@ class EntryTest extends TestCase { 'emailAddresses' => ['user@example.com'], 'profileTitle' => null, 'profileUrl' => null, + 'status' => null, + 'statusMessage' => null, + 'statusMessageTimestamp' => null, + 'statusIcon' => null, + 'isUser' => false, + 'uid' => null, ]; $this->entry->setId(123); diff --git a/tests/lib/Contacts/ContactsMenu/ManagerTest.php b/tests/lib/Contacts/ContactsMenu/ManagerTest.php index eb776a6e39d..2ea3966ad4f 100644 --- a/tests/lib/Contacts/ContactsMenu/ManagerTest.php +++ b/tests/lib/Contacts/ContactsMenu/ManagerTest.php @@ -26,10 +26,10 @@ namespace Tests\Contacts\ContactsMenu; use OC\Contacts\ContactsMenu\ActionProviderStore; use OC\Contacts\ContactsMenu\ContactsStore; +use OC\Contacts\ContactsMenu\Entry; use OC\Contacts\ContactsMenu\Manager; use OCP\App\IAppManager; use OCP\Constants; -use OCP\Contacts\ContactsMenu\IEntry; use OCP\Contacts\ContactsMenu\IProvider; use OCP\IConfig; use OCP\IUser; @@ -65,7 +65,7 @@ class ManagerTest extends TestCase { private function generateTestEntries(): array { $entries = []; foreach (range('Z', 'A') as $char) { - $entry = $this->createMock(IEntry::class); + $entry = $this->createMock(Entry::class); $entry->expects($this->any()) ->method('getFullName') ->willReturn('Contact ' . $char); diff --git a/tests/lib/ContactsManagerTest.php b/tests/lib/ContactsManagerTest.php index d15cd74bef8..63491214eb7 100644 --- a/tests/lib/ContactsManagerTest.php +++ b/tests/lib/ContactsManagerTest.php @@ -109,6 +109,9 @@ class ContactsManagerTest extends \Test\TestCase { ->method('delete') ->willReturn('returnMe'); + $addressbook->expects($this->any()) + ->method('getKey') + ->willReturn('addressbookKey'); $this->cm->registerAddressBook($addressbook); $result = $this->cm->delete(1, $addressbook->getKey()); @@ -128,6 +131,10 @@ class ContactsManagerTest extends \Test\TestCase { $addressbook->expects($this->never()) ->method('delete'); + $addressbook->expects($this->any()) + ->method('getKey') + ->willReturn('addressbookKey'); + $this->cm->registerAddressBook($addressbook); $result = $this->cm->delete(1, $addressbook->getKey()); $this->assertEquals($result, null); @@ -142,6 +149,10 @@ class ContactsManagerTest extends \Test\TestCase { $addressbook->expects($this->never()) ->method('delete'); + $addressbook->expects($this->any()) + ->method('getKey') + ->willReturn('addressbookKey'); + $this->cm->registerAddressBook($addressbook); $result = $this->cm->delete(1, 'noaddressbook'); $this->assertEquals($result, null); @@ -161,6 +172,10 @@ class ContactsManagerTest extends \Test\TestCase { ->method('createOrUpdate') ->willReturn('returnMe'); + $addressbook->expects($this->any()) + ->method('getKey') + ->willReturn('addressbookKey'); + $this->cm->registerAddressBook($addressbook); $result = $this->cm->createOrUpdate([], $addressbook->getKey()); $this->assertEquals($result, 'returnMe'); @@ -179,6 +194,10 @@ class ContactsManagerTest extends \Test\TestCase { $addressbook->expects($this->never()) ->method('createOrUpdate'); + $addressbook->expects($this->any()) + ->method('getKey') + ->willReturn('addressbookKey'); + $this->cm->registerAddressBook($addressbook); $result = $this->cm->createOrUpdate([], $addressbook->getKey()); $this->assertEquals($result, null); @@ -193,6 +212,10 @@ class ContactsManagerTest extends \Test\TestCase { $addressbook->expects($this->never()) ->method('createOrUpdate'); + $addressbook->expects($this->any()) + ->method('getKey') + ->willReturn('addressbookKey'); + $this->cm->registerAddressBook($addressbook); $result = $this->cm->createOrUpdate([], 'noaddressbook'); $this->assertEquals($result, null); @@ -209,6 +232,10 @@ class ContactsManagerTest extends \Test\TestCase { ->disableOriginalConstructor() ->getMock(); + $addressbook->expects($this->any()) + ->method('getKey') + ->willReturn('addressbookKey'); + $this->cm->registerAddressBook($addressbook); $result = $this->cm->isEnabled(); $this->assertTrue($result); diff --git a/tests/lib/DB/MigratorTest.php b/tests/lib/DB/MigratorTest.php index 4d7d9cab19f..8447e0931e7 100644 --- a/tests/lib/DB/MigratorTest.php +++ b/tests/lib/DB/MigratorTest.php @@ -11,16 +11,12 @@ namespace Test\DB; use Doctrine\DBAL\Exception; use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Platforms\OraclePlatform; -use Doctrine\DBAL\Platforms\PostgreSQL94Platform; use Doctrine\DBAL\Platforms\SqlitePlatform; use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\SchemaConfig; use OC\DB\Migrator; -use OC\DB\MySQLMigrator; use OC\DB\OracleMigrator; -use OC\DB\PostgreSqlMigrator; use OC\DB\SQLiteMigrator; use OCP\DB\Types; use OCP\IConfig; @@ -67,10 +63,6 @@ class MigratorTest extends \Test\TestCase { return new SQLiteMigrator($this->connection, $this->config, $dispatcher); } elseif ($platform instanceof OraclePlatform) { return new OracleMigrator($this->connection, $this->config, $dispatcher); - } elseif ($platform instanceof MySQLPlatform) { - return new MySQLMigrator($this->connection, $this->config, $dispatcher); - } elseif ($platform instanceof PostgreSQL94Platform) { - return new PostgreSqlMigrator($this->connection, $this->config, $dispatcher); } return new Migrator($this->connection, $this->config, $dispatcher); } @@ -138,14 +130,6 @@ class MigratorTest extends \Test\TestCase { return $config; } - private function isSQLite() { - return $this->connection->getDatabasePlatform() instanceof SqlitePlatform; - } - - private function isMySQL() { - return $this->connection->getDatabasePlatform() instanceof MySQLPlatform; - } - public function testUpgrade() { [$startSchema, $endSchema] = $this->getDuplicateKeySchemas(); $migrator = $this->getMigrator(); 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 8d800cf6f34..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; @@ -178,4 +179,74 @@ class UtilTest extends TestCase { ['/foo/test.txt.ocTransferId7567.part', '/foo/test.txt'], ]; } + + /** + * @dataProvider dataTestParseRawHeader + */ + public function testParseRawHeader($rawHeader, $expected) { + $result = $this->util->parseRawHeader($rawHeader); + $this->assertSameSize($expected, $result); + foreach ($result as $key => $value) { + $this->assertArrayHasKey($key, $expected); + $this->assertSame($expected[$key], $value); + } + } + + public function dataTestParseRawHeader() { + return [ + [str_pad('HBEGIN:oc_encryption_module:0:HEND', $this->headerSize, '-', STR_PAD_RIGHT) + , [Util::HEADER_ENCRYPTION_MODULE_KEY => '0']], + [str_pad('HBEGIN:oc_encryption_module:0:custom_header:foo:HEND', $this->headerSize, '-', STR_PAD_RIGHT) + , ['custom_header' => 'foo', Util::HEADER_ENCRYPTION_MODULE_KEY => '0']], + [str_pad('HelloWorld', $this->headerSize, '-', STR_PAD_RIGHT), []], + ['', []], + [str_pad('HBEGIN:oc_encryption_module:0', $this->headerSize, '-', STR_PAD_RIGHT) + , []], + [str_pad('oc_encryption_module:0:HEND', $this->headerSize, '-', STR_PAD_RIGHT) + , []], + ]; + } + + /** + * @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/FileInfoTest.php b/tests/lib/Files/FileInfoTest.php index fd2b506beb9..98f51aed67d 100644 --- a/tests/lib/Files/FileInfoTest.php +++ b/tests/lib/Files/FileInfoTest.php @@ -9,6 +9,8 @@ namespace Test\Files; use OC\Files\FileInfo; +use OC\Files\Mount\HomeMountPoint; +use OC\Files\Mount\MountPoint; use OC\Files\Storage\Home; use OC\Files\Storage\Temporary; use OCP\IConfig; @@ -33,19 +35,27 @@ class FileInfoTest extends TestCase { ->willReturn('foo'); $user->method('getHome') ->willReturn('foo'); + $storage = new Home(['user' => $user]); $fileInfo = new FileInfo( '', - new Home(['user' => $user]), - '', [], null); + $storage, + '', + [], + new HomeMountPoint($user, $storage, '/foo/files') + ); $this->assertFalse($fileInfo->isMounted()); } public function testIsMountedNonHomeStorage() { + $storage = new Temporary(); $fileInfo = new FileInfo( '', - new Temporary(), - '', [], null); + $storage, + '', + [], + new MountPoint($storage, '/foo/files/bar') + ); $this->assertTrue($fileInfo->isMounted()); } } 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/Mount/CacheMountProviderTest.php b/tests/lib/Files/Mount/CacheMountProviderTest.php new file mode 100644 index 00000000000..d142e5fc3c2 --- /dev/null +++ b/tests/lib/Files/Mount/CacheMountProviderTest.php @@ -0,0 +1,108 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Daniel Kesselberg <mail@danielkesselberg.de> + * + * @author Daniel Kesselberg <mail@danielkesselberg.de> + * + * @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\Files\Mount; + +use OC\Files\Mount\CacheMountProvider; +use OC\Files\Storage\StorageFactory; +use OCP\Files\Storage\IStorageFactory; +use OCP\IConfig; +use OCP\IUser; +use Test\TestCase; + +class CacheMountProviderTestStream { + public static $statCounter = 0; + public static $mkdirCounter = 0; + + public $context; + + public function mkdir(string $path, int $mode, int $options): bool { + self::$mkdirCounter++; + return true; + } + + public function url_stat(string $path, int $flags): array|false { + self::$statCounter++; + return false; + } +} + +class CacheMountProviderTest extends TestCase { + private IConfig $config; + private IUser $user; + private IStorageFactory $storageFactory; + + protected function setUp(): void { + $this->config = $this->createMock(IConfig::class); + $this->user = $this->createMock(IUser::class); + $this->storageFactory = new StorageFactory(); + stream_wrapper_register('cachemountprovidertest', CacheMountProviderTestStream::class); + } + + protected function tearDown(): void { + stream_wrapper_unregister('cachemountprovidertest'); + } + + public function testGetMountsForUser(): void { + $provider = new CacheMountProvider($this->config); + + $this->assertCount(0, $provider->getMountsForUser($this->user, $this->storageFactory)); + } + + public function testGetMountsForUserCacheDir(): void { + $this->config->expects($this->exactly(1)) + ->method('getSystemValueString') + ->willReturnMap([ + ['cache_path', '', 'cachemountprovidertest:////cache_path'], + ]); + $this->user->method('getUID') + ->willReturn('bob'); + + $provider = new CacheMountProvider($this->config); + $mounts = $provider->getMountsForUser($this->user, $this->storageFactory); + + $this->assertCount(2, $mounts); + $this->assertEquals(1, CacheMountProviderTestStream::$statCounter); + $this->assertEquals(2, CacheMountProviderTestStream::$mkdirCounter); + + $cacheMountProvider = $mounts[0]; + $this->assertEquals('/bob/cache/', $cacheMountProvider->getMountPoint()); + + $cacheStorage = $cacheMountProvider->getStorage(); + $this->assertEquals('local::cachemountprovidertest://cache_path/bob/', $cacheStorage->getId()); + + $uploadsMountProvider = $mounts[1]; + $this->assertEquals('/bob/uploads/', $uploadsMountProvider->getMountPoint()); + + $uploadsStorage = $uploadsMountProvider->getStorage(); + $this->assertEquals('local::cachemountprovidertest://cache_path/bob/uploads/', $uploadsStorage->getId()); + + $cacheStorage->mkdir('foobar'); + $this->assertEquals(3, CacheMountProviderTestStream::$mkdirCounter); + + $uploadsStorage->mkdir('foobar'); + $this->assertEquals(4, CacheMountProviderTestStream::$mkdirCounter); + } +} 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 b63d287a191..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); @@ -481,8 +492,7 @@ abstract class NodeTest extends \Test\TestCase { $parentNode = new \OC\Files\Node\Folder($this->root, $this->view, '/bar'); $newNode = $this->createTestNode($this->root, $this->view, '/bar/asd'); - $this->root->expects($this->exactly(2)) - ->method('get') + $this->root->method('get') ->willReturnMap([ ['/bar/asd', $newNode], ['/bar', $parentNode] @@ -600,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/LocalTest.php b/tests/lib/Files/Storage/LocalTest.php index e324d2b28db..1190a2b2da0 100644 --- a/tests/lib/Files/Storage/LocalTest.php +++ b/tests/lib/Files/Storage/LocalTest.php @@ -139,4 +139,15 @@ class LocalTest extends Storage { umask($oldMask); $this->assertTrue($this->instance->isUpdatable('test.txt')); } + + public function testUnavailableExternal() { + $this->expectException(\OCP\Files\StorageNotAvailableException::class); + $this->instance = new \OC\Files\Storage\Local(['datadir' => $this->tmpDir . '/unexist', 'isExternal' => true]); + } + + public function testUnavailableNonExternal() { + $this->instance = new \OC\Files\Storage\Local(['datadir' => $this->tmpDir . '/unexist']); + // no exception thrown + $this->assertNotNull($this->instance); + } } 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 11be0c60fbd..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()) @@ -601,19 +601,19 @@ class EncryptionTest extends Storage { $this->encryptionManager, $util, $this->logger, $this->file, null, $this->keyStore, $this->update, $this->mountManager, $this->arrayCache ] ) - ->setMethods(['getCache','readFirstBlock', 'parseRawHeader']) + ->setMethods(['getCache', 'readFirstBlock']) ->getMock(); - $instance->expects($this->once())->method('getCache')->willReturn($cache); + $instance->method('getCache')->willReturn($cache); - $instance->expects($this->once())->method(('parseRawHeader')) + $util->method('parseRawHeader') ->willReturn([Util::HEADER_ENCRYPTION_MODULE_KEY => 'OC_DEFAULT_MODULE']); if ($strippedPathExists) { - $instance->expects($this->once())->method('readFirstBlock') + $instance->method('readFirstBlock') ->with($strippedPath)->willReturn(''); } else { - $instance->expects($this->once())->method('readFirstBlock') + $instance->method('readFirstBlock') ->with($path)->willReturn(''); } @@ -679,11 +679,13 @@ class EncryptionTest extends Storage { $this->encryptionManager, $util, $this->logger, $this->file, null, $this->keyStore, $this->update, $this->mountManager, $this->arrayCache ] ) - ->setMethods(['readFirstBlock', 'parseRawHeader', 'getCache']) + ->setMethods(['readFirstBlock', 'getCache']) ->getMock(); - $instance->expects($this->any())->method(('parseRawHeader'))->willReturn($header); - $instance->expects($this->once())->method('getCache')->willReturn($cache); + $instance->method('readFirstBlock')->willReturn(''); + + $util->method(('parseRawHeader'))->willReturn($header); + $instance->method('getCache')->willReturn($cache); $result = $this->invokePrivate($instance, 'getHeader', ['test.txt']); $this->assertSameSize($expected, $result); @@ -702,44 +704,6 @@ class EncryptionTest extends Storage { ]; } - /** - * @dataProvider dataTestParseRawHeader - */ - public function testParseRawHeader($rawHeader, $expected) { - $instance = new \OC\Files\Storage\Wrapper\Encryption( - [ - 'storage' => $this->sourceStorage, - 'root' => 'foo', - 'mountPoint' => '/', - 'mount' => $this->mount - ], - $this->encryptionManager, $this->util, $this->logger, $this->file, null, $this->keyStore, $this->update, $this->mountManager, $this->arrayCache - - ); - - $result = $this->invokePrivate($instance, 'parseRawHeader', [$rawHeader]); - $this->assertSameSize($expected, $result); - foreach ($result as $key => $value) { - $this->assertArrayHasKey($key, $expected); - $this->assertSame($expected[$key], $value); - } - } - - public function dataTestParseRawHeader() { - return [ - [str_pad('HBEGIN:oc_encryption_module:0:HEND', $this->headerSize, '-', STR_PAD_RIGHT) - , [Util::HEADER_ENCRYPTION_MODULE_KEY => '0']], - [str_pad('HBEGIN:oc_encryption_module:0:custom_header:foo:HEND', $this->headerSize, '-', STR_PAD_RIGHT) - , ['custom_header' => 'foo', Util::HEADER_ENCRYPTION_MODULE_KEY => '0']], - [str_pad('HelloWorld', $this->headerSize, '-', STR_PAD_RIGHT), []], - ['', []], - [str_pad('HBEGIN:oc_encryption_module:0', $this->headerSize, '-', STR_PAD_RIGHT) - , []], - [str_pad('oc_encryption_module:0:HEND', $this->headerSize, '-', STR_PAD_RIGHT) - , []], - ]; - } - public function dataCopyBetweenStorage() { return [ [true, true, true], @@ -826,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/Storage/Wrapper/KnownMtimeTest.php b/tests/lib/Files/Storage/Wrapper/KnownMtimeTest.php new file mode 100644 index 00000000000..0a1691f9e70 --- /dev/null +++ b/tests/lib/Files/Storage/Wrapper/KnownMtimeTest.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace lib\Files\Storage\Wrapper; + +use OC\Files\Storage\Temporary; +use OC\Files\Storage\Wrapper\KnownMtime; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Clock\ClockInterface; +use Test\Files\Storage\Storage; + +/** + * @group DB + */ +class KnownMtimeTest extends Storage { + /** @var Temporary */ + private $sourceStorage; + + /** @var ClockInterface|MockObject */ + private $clock; + private int $fakeTime = 0; + + protected function setUp(): void { + parent::setUp(); + $this->fakeTime = 0; + $this->sourceStorage = new Temporary([]); + $this->clock = $this->createMock(ClockInterface::class); + $this->clock->method('now')->willReturnCallback(function () { + if ($this->fakeTime) { + return new \DateTimeImmutable("@{$this->fakeTime}"); + } else { + return new \DateTimeImmutable(); + } + }); + $this->instance = $this->getWrappedStorage(); + } + + protected function tearDown(): void { + $this->sourceStorage->cleanUp(); + parent::tearDown(); + } + + protected function getWrappedStorage() { + return new KnownMtime([ + 'storage' => $this->sourceStorage, + 'clock' => $this->clock, + ]); + } + + public function testNewerKnownMtime() { + $future = time() + 1000; + $this->fakeTime = $future; + + $this->instance->file_put_contents('foo.txt', 'bar'); + + // fuzzy match since the clock might have ticked + $this->assertLessThan(2, abs(time() - $this->sourceStorage->filemtime('foo.txt'))); + $this->assertEquals($this->sourceStorage->filemtime('foo.txt'), $this->sourceStorage->stat('foo.txt')['mtime']); + $this->assertEquals($this->sourceStorage->filemtime('foo.txt'), $this->sourceStorage->getMetaData('foo.txt')['mtime']); + + $this->assertEquals($future, $this->instance->filemtime('foo.txt')); + $this->assertEquals($future, $this->instance->stat('foo.txt')['mtime']); + $this->assertEquals($future, $this->instance->getMetaData('foo.txt')['mtime']); + } +} 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 a0323293605..818c69ed355 100644 --- a/tests/lib/Files/ViewTest.php +++ b/tests/lib/Files/ViewTest.php @@ -7,7 +7,6 @@ namespace Test\Files; -use OCP\Cache\CappedMemoryCache; use OC\Files\Cache\Watcher; use OC\Files\Filesystem; use OC\Files\Mount\MountPoint; @@ -16,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; @@ -297,7 +298,7 @@ class ViewTest extends \Test\TestCase { */ public function testRemoveSharePermissionWhenSharingDisabledForUser($excludeGroups, $excludeGroupsList, $expectedShareable) { // Reset sharing disabled for users cache - self::invokePrivate(\OC::$server->get(IShareManager::class), 'sharingDisabledForUsersCache', [new CappedMemoryCache()]); + self::invokePrivate(\OC::$server->get(ShareDisableChecker::class), 'sharingDisabledForUsersCache', [new CappedMemoryCache()]); $config = \OC::$server->getConfig(); $oldExcludeGroupsFlag = $config->getAppValue('core', 'shareapi_exclude_groups', 'no'); @@ -322,7 +323,7 @@ class ViewTest extends \Test\TestCase { $config->setAppValue('core', 'shareapi_exclude_groups_list', $oldExcludeGroupsList); // Reset sharing disabled for users cache - self::invokePrivate(\OC::$server->get(IShareManager::class), 'sharingDisabledForUsersCache', [new CappedMemoryCache()]); + self::invokePrivate(\OC::$server->get(ShareDisableChecker::class), 'sharingDisabledForUsersCache', [new CappedMemoryCache()]); } public function testCacheIncompleteFolder() { @@ -1690,8 +1691,6 @@ class ViewTest extends \Test\TestCase { ->setSharedBy($this->user) ->setShareType(IShare::TYPE_USER) ->setPermissions(\OCP\Constants::PERMISSION_READ) - ->setId(42) - ->setProviderId('foo') ->setNode($shareDir); $shareManager->createShare($share); diff --git a/tests/lib/Group/ManagerTest.php b/tests/lib/Group/ManagerTest.php index 2887d14acaa..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; @@ -92,6 +92,7 @@ class ManagerTest extends TestCase { 'inGroup', 'getGroups', 'groupExists', + 'groupsExists', 'usersInGroup', 'createGroup', 'addToGroup', @@ -361,10 +362,12 @@ class ManagerTest extends TestCase { ->method('getGroups') ->with('1') ->willReturn(['group1']); + $backend->expects($this->never()) + ->method('groupExists'); $backend->expects($this->once()) - ->method('groupExists') - ->with('group1') - ->willReturn(false); + ->method('getGroupsDetails') + ->with(['group1']) + ->willReturn([]); /** @var \OC\User\Manager $userManager */ $userManager = $this->createMock(Manager::class); diff --git a/tests/lib/Http/Client/ClientServiceTest.php b/tests/lib/Http/Client/ClientServiceTest.php index 40da0a2111c..3aae7ceae25 100644 --- a/tests/lib/Http/Client/ClientServiceTest.php +++ b/tests/lib/Http/Client/ClientServiceTest.php @@ -12,8 +12,8 @@ declare(strict_types=1); namespace Test\Http\Client; use GuzzleHttp\Client as GuzzleClient; -use GuzzleHttp\HandlerStack; use GuzzleHttp\Handler\CurlHandler; +use GuzzleHttp\HandlerStack; use GuzzleHttp\Middleware; use OC\Http\Client\Client; use OC\Http\Client\ClientService; @@ -32,6 +32,9 @@ class ClientServiceTest extends \Test\TestCase { public function testNewClient(): void { /** @var IConfig $config */ $config = $this->createMock(IConfig::class); + $config->method('getSystemValueBool') + ->with('dns_pinning', true) + ->willReturn(true); /** @var ICertificateManager $certificateManager */ $certificateManager = $this->createMock(ICertificateManager::class); $dnsPinMiddleware = $this->createMock(DnsPinMiddleware::class); @@ -74,4 +77,52 @@ class ClientServiceTest extends \Test\TestCase { $clientService->newClient() ); } + + public function testDisableDnsPinning(): void { + /** @var IConfig $config */ + $config = $this->createMock(IConfig::class); + $config->method('getSystemValueBool') + ->with('dns_pinning', true) + ->willReturn(false); + /** @var ICertificateManager $certificateManager */ + $certificateManager = $this->createMock(ICertificateManager::class); + $dnsPinMiddleware = $this->createMock(DnsPinMiddleware::class); + $dnsPinMiddleware + ->expects($this->never()) + ->method('addDnsPinning') + ->willReturn(function () { + }); + $remoteHostValidator = $this->createMock(IRemoteHostValidator::class); + $eventLogger = $this->createMock(IEventLogger::class); + $logger = $this->createMock(LoggerInterface::class); + + $clientService = new ClientService( + $config, + $certificateManager, + $dnsPinMiddleware, + $remoteHostValidator, + $eventLogger, + $logger, + ); + + $handler = new CurlHandler(); + $stack = HandlerStack::create($handler); + $stack->push(Middleware::tap(function (RequestInterface $request) use ($eventLogger) { + $eventLogger->start('http:request', $request->getMethod() . " request to " . $request->getRequestTarget()); + }, function () use ($eventLogger) { + $eventLogger->end('http:request'); + }), 'event logger'); + $guzzleClient = new GuzzleClient(['handler' => $stack]); + + $this->assertEquals( + new Client( + $config, + $certificateManager, + $guzzleClient, + $remoteHostValidator, + $logger, + ), + $clientService->newClient() + ); + } } 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 new file mode 100644 index 00000000000..54071f37b1a --- /dev/null +++ b/tests/lib/Http/Client/DnsPinMiddlewareTest.php @@ -0,0 +1,563 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Daniel Kesselberg <mail@danielkesselberg.de> + * + * @author Daniel Kesselberg <mail@danielkesselberg.de> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace lib\Http\Client; + +use GuzzleHttp\Handler\MockHandler; +use GuzzleHttp\HandlerStack; +use GuzzleHttp\Psr7\Request; +use GuzzleHttp\Psr7\Response; +use OC\Http\Client\DnsPinMiddleware; +use OC\Http\Client\NegativeDnsCache; +use OC\Memcache\NullCache; +use OC\Net\IpAddressClassifier; +use OCP\Http\Client\LocalServerException; +use OCP\ICacheFactory; +use Psr\Http\Message\RequestInterface; +use Test\TestCase; + +class DnsPinMiddlewareTest extends TestCase { + private DnsPinMiddleware $dnsPinMiddleware; + + protected function setUp(): void { + parent::setUp(); + + $cacheFactory = $this->createMock(ICacheFactory::class); + $cacheFactory + ->method('createLocal') + ->willReturn(new NullCache()); + + $ipAddressClassifier = new IpAddressClassifier(); + $negativeDnsCache = new NegativeDnsCache($cacheFactory); + + $this->dnsPinMiddleware = $this->getMockBuilder(DnsPinMiddleware::class) + ->setConstructorArgs([$negativeDnsCache, $ipAddressClassifier]) + ->onlyMethods(['dnsGetRecord']) + ->getMock(); + } + + public function testPopulateDnsCacheIPv4() { + $mockHandler = new MockHandler([ + static function (RequestInterface $request, array $options) { + self::arrayHasKey('curl', $options); + self::arrayHasKey(CURLOPT_RESOLVE, $options['curl']); + self::assertEquals([ + 'www.example.com:80:1.1.1.1', + 'www.example.com:443:1.1.1.1' + ], $options['curl'][CURLOPT_RESOLVE]); + return new Response(200); + }, + ]); + + $this->dnsPinMiddleware + ->method('dnsGetRecord') + ->willReturnCallback(function (string $hostname, int $type) { + // example.com SOA + if ($hostname === 'example.com') { + return match ($type) { + DNS_SOA => [ + [ + 'host' => 'example.com', + 'class' => 'IN', + 'ttl' => 7079, + 'type' => 'SOA', + 'minimum-ttl' => 3600, + ] + ], + }; + } + + // example.com A, AAAA, CNAME + if ($hostname === 'www.example.com') { + return match ($type) { + DNS_A => [], + DNS_AAAA => [], + DNS_CNAME => [ + [ + 'host' => 'www.example.com', + 'class' => 'IN', + 'ttl' => 1800, + 'type' => 'A', + 'target' => 'www.example.net' + ] + ], + }; + } + + // example.net SOA + if ($hostname === 'example.net') { + return match ($type) { + DNS_SOA => [ + [ + 'host' => 'example.net', + 'class' => 'IN', + 'ttl' => 7079, + 'type' => 'SOA', + 'minimum-ttl' => 3600, + ] + ], + }; + } + + // example.net A, AAAA, CNAME + if ($hostname === 'www.example.net') { + return match ($type) { + DNS_A => [ + [ + 'host' => 'www.example.net', + 'class' => 'IN', + 'ttl' => 1800, + 'type' => 'A', + 'ip' => '1.1.1.1' + ] + ], + DNS_AAAA => [], + DNS_CNAME => [], + }; + } + + return false; + }); + + $stack = new HandlerStack($mockHandler); + $stack->push($this->dnsPinMiddleware->addDnsPinning()); + $handler = $stack->resolve(); + + $handler( + new Request('GET', 'https://www.example.com'), + ['nextcloud' => ['allow_local_address' => false]] + ); + } + + public function testPopulateDnsCacheIPv6() { + $mockHandler = new MockHandler([ + static function (RequestInterface $request, array $options) { + self::arrayHasKey('curl', $options); + self::arrayHasKey(CURLOPT_RESOLVE, $options['curl']); + self::assertEquals([ + 'www.example.com:80:1.1.1.1,1.0.0.1,2606:4700:4700::1111,2606:4700:4700::1001', + 'www.example.com:443:1.1.1.1,1.0.0.1,2606:4700:4700::1111,2606:4700:4700::1001' + ], $options['curl'][CURLOPT_RESOLVE]); + return new Response(200); + }, + ]); + + $this->dnsPinMiddleware + ->method('dnsGetRecord') + ->willReturnCallback(function (string $hostname, int $type) { + // example.com SOA + if ($hostname === 'example.com') { + return match ($type) { + DNS_SOA => [ + [ + 'host' => 'example.com', + 'class' => 'IN', + 'ttl' => 7079, + 'type' => 'SOA', + 'minimum-ttl' => 3600, + ] + ], + }; + } + + // example.com A, AAAA, CNAME + if ($hostname === 'www.example.com') { + return match ($type) { + DNS_A => [], + DNS_AAAA => [], + DNS_CNAME => [ + [ + 'host' => 'www.example.com', + 'class' => 'IN', + 'ttl' => 1800, + 'type' => 'A', + 'target' => 'www.example.net' + ] + ], + }; + } + + // example.net SOA + if ($hostname === 'example.net') { + return match ($type) { + DNS_SOA => [ + [ + 'host' => 'example.net', + 'class' => 'IN', + 'ttl' => 7079, + 'type' => 'SOA', + 'minimum-ttl' => 3600, + ] + ], + }; + } + + // example.net A, AAAA, CNAME + if ($hostname === 'www.example.net') { + return match ($type) { + DNS_A => [ + [ + 'host' => 'www.example.net', + 'class' => 'IN', + 'ttl' => 1800, + 'type' => 'A', + 'ip' => '1.1.1.1' + ], + [ + 'host' => 'www.example.net', + 'class' => 'IN', + 'ttl' => 1800, + 'type' => 'A', + 'ip' => '1.0.0.1' + ], + ], + DNS_AAAA => [ + [ + 'host' => 'www.example.net', + 'class' => 'IN', + 'ttl' => 1800, + 'type' => 'AAAA', + 'ip' => '2606:4700:4700::1111' + ], + [ + 'host' => 'www.example.net', + 'class' => 'IN', + 'ttl' => 1800, + 'type' => 'AAAA', + 'ip' => '2606:4700:4700::1001' + ], + ], + DNS_CNAME => [], + }; + } + + return false; + }); + + $stack = new HandlerStack($mockHandler); + $stack->push($this->dnsPinMiddleware->addDnsPinning()); + $handler = $stack->resolve(); + + $handler( + new Request('GET', 'https://www.example.com'), + ['nextcloud' => ['allow_local_address' => false]] + ); + } + + public function testAllowLocalAddress() { + $mockHandler = new MockHandler([ + static function (RequestInterface $request, array $options) { + self::assertArrayNotHasKey('curl', $options); + return new Response(200); + }, + ]); + + $stack = new HandlerStack($mockHandler); + $stack->push($this->dnsPinMiddleware->addDnsPinning()); + $handler = $stack->resolve(); + + $handler( + new Request('GET', 'https://www.example.com'), + ['nextcloud' => ['allow_local_address' => true]] + ); + } + + public function testRejectIPv4() { + $this->expectException(LocalServerException::class); + $this->expectExceptionMessage('violates local access rules'); + + $mockHandler = new MockHandler([ + static function (RequestInterface $request, array $options) { + // The handler should not be called + }, + ]); + + $this->dnsPinMiddleware + ->method('dnsGetRecord') + ->willReturnCallback(function (string $hostname, int $type) { + return match ($type) { + DNS_SOA => [ + [ + 'host' => 'example.com', + 'class' => 'IN', + 'ttl' => 7079, + 'type' => 'SOA', + 'minimum-ttl' => 3600, + ] + ], + DNS_A => [ + [ + 'host' => 'example.com', + 'class' => 'IN', + 'ttl' => 1800, + 'type' => 'A', + 'ip' => '192.168.0.1' + ] + ], + DNS_AAAA => [], + DNS_CNAME => [], + }; + }); + + $stack = new HandlerStack($mockHandler); + $stack->push($this->dnsPinMiddleware->addDnsPinning()); + $handler = $stack->resolve(); + + $handler( + new Request('GET', 'https://www.example.com'), + ['nextcloud' => ['allow_local_address' => false]] + ); + } + + public function testRejectIPv6() { + $this->expectException(LocalServerException::class); + $this->expectExceptionMessage('violates local access rules'); + + $mockHandler = new MockHandler([ + static function (RequestInterface $request, array $options) { + // The handler should not be called + }, + ]); + + $this->dnsPinMiddleware + ->method('dnsGetRecord') + ->willReturnCallback(function (string $hostname, int $type) { + return match ($type) { + DNS_SOA => [ + [ + 'host' => 'example.com', + 'class' => 'IN', + 'ttl' => 7079, + 'type' => 'SOA', + 'minimum-ttl' => 3600, + ] + ], + DNS_A => [], + DNS_AAAA => [ + [ + 'host' => 'ipv6.example.com', + 'class' => 'IN', + 'ttl' => 1800, + 'type' => 'AAAA', + 'ipv6' => 'fd12:3456:789a:1::1' + ] + ], + DNS_CNAME => [], + }; + }); + + $stack = new HandlerStack($mockHandler); + $stack->push($this->dnsPinMiddleware->addDnsPinning()); + $handler = $stack->resolve(); + + $handler( + new Request('GET', 'https://ipv6.example.com'), + ['nextcloud' => ['allow_local_address' => false]] + ); + } + + public function testRejectCanonicalName() { + $this->expectException(LocalServerException::class); + $this->expectExceptionMessage('violates local access rules'); + + $mockHandler = new MockHandler([ + static function (RequestInterface $request, array $options) { + // The handler should not be called + }, + ]); + + $this->dnsPinMiddleware + ->method('dnsGetRecord') + ->willReturnCallback(function (string $hostname, int $type) { + // example.com SOA + if ($hostname === 'example.com') { + return match ($type) { + DNS_SOA => [ + [ + 'host' => 'example.com', + 'class' => 'IN', + 'ttl' => 7079, + 'type' => 'SOA', + 'minimum-ttl' => 3600, + ] + ], + }; + } + + // example.com A, AAAA, CNAME + if ($hostname === 'www.example.com') { + return match ($type) { + DNS_A => [], + DNS_AAAA => [], + DNS_CNAME => [ + [ + 'host' => 'www.example.com', + 'class' => 'IN', + 'ttl' => 1800, + 'type' => 'A', + 'target' => 'www.example.net' + ] + ], + }; + } + + // example.net SOA + if ($hostname === 'example.net') { + return match ($type) { + DNS_SOA => [ + [ + 'host' => 'example.net', + 'class' => 'IN', + 'ttl' => 7079, + 'type' => 'SOA', + 'minimum-ttl' => 3600, + ] + ], + }; + } + + // example.net A, AAAA, CNAME + if ($hostname === 'www.example.net') { + return match ($type) { + DNS_A => [ + [ + 'host' => 'www.example.net', + 'class' => 'IN', + 'ttl' => 1800, + 'type' => 'A', + 'ip' => '192.168.0.2' + ] + ], + DNS_AAAA => [], + DNS_CNAME => [], + }; + } + + return false; + }); + + $stack = new HandlerStack($mockHandler); + $stack->push($this->dnsPinMiddleware->addDnsPinning()); + $handler = $stack->resolve(); + + $handler( + new Request('GET', 'https://www.example.com'), + ['nextcloud' => ['allow_local_address' => false]] + ); + } + + public function testRejectFaultyResponse() { + $this->expectException(LocalServerException::class); + $this->expectExceptionMessage('No DNS record found for www.example.com'); + + $mockHandler = new MockHandler([ + static function (RequestInterface $request, array $options) { + // The handler should not be called + }, + ]); + + $this->dnsPinMiddleware + ->method('dnsGetRecord') + ->willReturnCallback(function (string $hostname, int $type) { + return false; + }); + + $stack = new HandlerStack($mockHandler); + $stack->push($this->dnsPinMiddleware->addDnsPinning()); + $handler = $stack->resolve(); + + $handler( + new Request('GET', 'https://www.example.com'), + ['nextcloud' => ['allow_local_address' => false]] + ); + } + + public function testIgnoreSubdomainForSoaQuery() { + $mockHandler = new MockHandler([ + static function (RequestInterface $request, array $options) { + // The handler should not be called + }, + ]); + + $dnsQueries = []; + + $this->dnsPinMiddleware + ->method('dnsGetRecord') + ->willReturnCallback(function (string $hostname, int $type) use (&$dnsQueries) { + // log query + $dnsQueries[] = $hostname . $type; + + // example.com SOA + if ($hostname === 'example.com') { + return match ($type) { + DNS_SOA => [ + [ + 'host' => 'example.com', + 'class' => 'IN', + 'ttl' => 7079, + 'type' => 'SOA', + 'minimum-ttl' => 3600, + ] + ], + }; + } + + // example.net A, AAAA, CNAME + if ($hostname === 'subsubdomain.subdomain.example.com') { + return match ($type) { + DNS_A => [ + [ + 'host' => 'subsubdomain.subdomain.example.com', + 'class' => 'IN', + 'ttl' => 1800, + 'type' => 'A', + 'ip' => '1.1.1.1' + ] + ], + DNS_AAAA => [], + DNS_CNAME => [], + }; + } + + return false; + }); + + $stack = new HandlerStack($mockHandler); + $stack->push($this->dnsPinMiddleware->addDnsPinning()); + $handler = $stack->resolve(); + + $handler( + new Request('GET', 'https://subsubdomain.subdomain.example.com'), + ['nextcloud' => ['allow_local_address' => false]] + ); + + $this->assertCount(4, $dnsQueries); + $this->assertContains('example.com' . DNS_SOA, $dnsQueries); + $this->assertContains('subsubdomain.subdomain.example.com' . DNS_A, $dnsQueries); + $this->assertContains('subsubdomain.subdomain.example.com' . DNS_AAAA, $dnsQueries); + $this->assertContains('subsubdomain.subdomain.example.com' . DNS_CNAME, $dnsQueries); + } +} 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 60c8ac1cc94..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), @@ -576,30 +576,30 @@ MPLX6f5V9tCJtlH6ztmEcDROfvuVc0U3rEhqx2hphoyo+MZrPFpdcJL8KkIdMKbY ], ]; $this->appFetcher - ->expects($this->atLeastOnce()) + ->expects($this->once()) ->method('get') - ->willReturnOnConsecutiveCalls($appArray); + ->willReturn($appArray); $realTmpFile = \OC::$server->getTempManager()->getTemporaryFile('.tar.gz'); copy(__DIR__ . '/../data/testapp.tar.gz', $realTmpFile); $this->tempManager - ->expects($this->atLeastOnce()) + ->expects($this->once()) ->method('getTemporaryFile') ->with('.tar.gz') - ->willReturnOnConsecutiveCalls($realTmpFile); + ->willReturn($realTmpFile); $realTmpFolder = \OC::$server->getTempManager()->getTemporaryFolder(); $this->tempManager - ->expects($this->atLeastOnce()) + ->expects($this->once()) ->method('getTemporaryFolder') - ->willReturnOnConsecutiveCalls($realTmpFolder); + ->willReturn($realTmpFolder); $client = $this->createMock(IClient::class); $client ->expects($this->once()) ->method('get') ->with('https://example.com', ['sink' => $realTmpFile, 'timeout' => 120]); $this->clientService - ->expects($this->atLeastOnce()) + ->expects($this->once()) ->method('newClient') - ->willReturnOnConsecutiveCalls($client); + ->willReturn($client); $installer = $this->getInstaller(); $installer->downloadApp('testapp'); @@ -610,6 +610,14 @@ MPLX6f5V9tCJtlH6ztmEcDROfvuVc0U3rEhqx2hphoyo+MZrPFpdcJL8KkIdMKbY public function testDownloadAppWithDowngrade() { + // Use previous test to download the application in version 0.9 + $this->testDownloadAppSuccessful(); + + // Reset mocks + $this->appFetcher = $this->createMock(AppFetcher::class); + $this->clientService = $this->createMock(IClientService::class); + $this->tempManager = $this->createMock(ITempManager::class); + $this->expectException(\Exception::class); $this->expectExceptionMessage('App for id testapp has version 0.9 and tried to update to lower version 0.8'); @@ -662,19 +670,19 @@ JXhrdaWDZ8fzpUjugrtC3qslsqL0dzgU37anS3HwrT8=', ], ]; $this->appFetcher - ->expects($this->at(1)) + ->expects($this->once()) ->method('get') ->willReturn($appArray); $realTmpFile = \OC::$server->getTempManager()->getTemporaryFile('.tar.gz'); copy(__DIR__ . '/../data/testapp.0.8.tar.gz', $realTmpFile); $this->tempManager - ->expects($this->at(2)) + ->expects($this->once()) ->method('getTemporaryFile') ->with('.tar.gz') ->willReturn($realTmpFile); $realTmpFolder = \OC::$server->getTempManager()->getTemporaryFolder(); $this->tempManager - ->expects($this->at(3)) + ->expects($this->once()) ->method('getTemporaryFolder') ->willReturn($realTmpFolder); $client = $this->createMock(IClient::class); @@ -683,10 +691,9 @@ JXhrdaWDZ8fzpUjugrtC3qslsqL0dzgU37anS3HwrT8=', ->method('get') ->with('https://example.com', ['sink' => $realTmpFile, 'timeout' => 120]); $this->clientService - ->expects($this->at(1)) + ->expects($this->once()) ->method('newClient') ->willReturn($client); - $this->testDownloadAppSuccessful(); $this->assertTrue(file_exists(__DIR__ . '/../../apps/testapp/appinfo/info.xml')); $this->assertEquals('0.9', \OC_App::getAppVersionByPath(__DIR__ . '/../../apps/testapp/')); 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 9cdd7058ffa..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 = '') { @@ -140,4 +140,15 @@ class FactoryTest extends \Test\TestCase { $profiler = $this->getMockBuilder(IProfiler::class)->getMock(); new \OC\Memcache\Factory('abc', $logger, $profiler, $localCache, $distributedCache); } + + public function testCreateInMemory(): void { + $logger = $this->getMockBuilder(LoggerInterface::class)->getMock(); + $profiler = $this->getMockBuilder(IProfiler::class)->getMock(); + $factory = new \OC\Memcache\Factory('abc', $logger, $profiler, null, null, null); + + $cache = $factory->createInMemory(); + $cache->set('test', 48); + + self::assertSame(48, $cache->get('test')); + } } 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/Metadata/FileMetadataMapperTest.php b/tests/lib/Metadata/FileMetadataMapperTest.php deleted file mode 100644 index 4f7708ab9a9..00000000000 --- a/tests/lib/Metadata/FileMetadataMapperTest.php +++ /dev/null @@ -1,87 +0,0 @@ -<?php - -declare(strict_types=1); - -/** - * @copyright 2022 Carl Schwan <carl@carlschwan.eu> - * @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\Metadata; - -use OC\Metadata\FileMetadataMapper; -use OC\Metadata\FileMetadata; -use PHPUnit\Framework\MockObject\MockObject; - -/** - * @group DB - * @package Test\DB\QueryBuilder - */ -class FileMetadataMapperTest extends \Test\TestCase { - /** @var IDBConnection */ - protected $connection; - - /** @var SystemConfig|MockObject */ - protected $config; - - /** @var FileMetadataMapper|MockObject */ - protected $mapper; - - protected function setUp(): void { - parent::setUp(); - - $this->connection = \OC::$server->getDatabaseConnection(); - $this->mapper = new FileMetadataMapper($this->connection); - } - - public function testFindForGroupForFiles() { - $file1 = new FileMetadata(); - $file1->setId(1); - $file1->setGroupName('size'); - $file1->setArrayAsValue([]); - - $file2 = new FileMetadata(); - $file2->setId(2); - $file2->setGroupName('size'); - $file2->setArrayAsValue(['width' => 293, 'height' => 23]); - - // not added, it's the default - $file3 = new FileMetadata(); - $file3->setId(3); - $file3->setGroupName('size'); - $file3->setArrayAsValue([]); - - $file4 = new FileMetadata(); - $file4->setId(4); - $file4->setGroupName('size'); - $file4->setArrayAsValue(['complex' => ["yes", "maybe" => 34.0]]); - - $this->mapper->insert($file1); - $this->mapper->insert($file2); - $this->mapper->insert($file4); - - $files = $this->mapper->findForGroupForFiles([1, 2, 3, 4], 'size'); - - $this->assertEquals($files[1]->getValue(), $file1->getValue()); - $this->assertEquals($files[2]->getValue(), $file2->getValue()); - $this->assertEquals($files[3]->getDecodedValue(), $file3->getDecodedValue()); - $this->assertEquals($files[4]->getValue(), $file4->getValue()); - - $this->mapper->clear(1); - $this->mapper->clear(2); - $this->mapper->clear(4); - } -} 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 d5c827fe1cb..beefc2353d6 100644 --- a/tests/lib/NavigationManagerTest.php +++ b/tests/lib/NavigationManagerTest.php @@ -95,7 +95,7 @@ class NavigationManagerTest extends TestCase { //'icon' => 'optional', 'href' => 'url', 'active' => true, - 'unread' => 0 + 'unread' => 0, ], 'entry id2' => [ 'id' => 'entry id', @@ -106,7 +106,8 @@ class NavigationManagerTest extends TestCase { 'active' => false, 'type' => 'link', 'classes' => '', - 'unread' => 0 + 'unread' => 0, + 'default' => true, ] ] ]; @@ -215,23 +216,27 @@ class NavigationManagerTest extends TestCase { return vsprintf($text, $parameters); }); + /* Return default value */ + $this->config->method('getUserValue') + ->willReturnArgument(3); + $this->appManager->expects($this->any()) ->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'; @@ -271,6 +276,17 @@ class NavigationManagerTest extends TestCase { ] ]; $defaults = [ + 'profile' => [ + 'type' => 'settings', + 'id' => 'profile', + 'order' => 1, + 'href' => '/apps/test/', + 'name' => 'View profile', + 'icon' => '', + 'active' => false, + 'classes' => '', + 'unread' => 0, + ], 'accessibility_settings' => [ 'type' => 'settings', 'id' => 'accessibility_settings', @@ -334,6 +350,7 @@ class NavigationManagerTest extends TestCase { return [ 'minimalistic' => [ array_merge( + ['profile' => $defaults['profile']], ['accessibility_settings' => $defaults['accessibility_settings']], ['settings' => $defaults['settings']], ['test' => [ @@ -345,7 +362,9 @@ class NavigationManagerTest extends TestCase { 'active' => false, 'type' => 'link', 'classes' => '', - 'unread' => 0 + 'unread' => 0, + 'default' => true, + 'app' => 'test', ]], ['logout' => $defaults['logout']] ), @@ -357,6 +376,7 @@ class NavigationManagerTest extends TestCase { ], 'minimalistic-settings' => [ array_merge( + ['profile' => $defaults['profile']], ['accessibility_settings' => $defaults['accessibility_settings']], ['settings' => $defaults['settings']], ['test' => [ @@ -368,7 +388,7 @@ class NavigationManagerTest extends TestCase { 'active' => false, 'type' => 'settings', 'classes' => '', - 'unread' => 0 + 'unread' => 0, ]], ['logout' => $defaults['logout']] ), @@ -378,8 +398,49 @@ class NavigationManagerTest extends TestCase { ], ]] ], + 'with-multiple' => [ + array_merge( + ['profile' => $defaults['profile']], + ['accessibility_settings' => $defaults['accessibility_settings']], + ['settings' => $defaults['settings']], + ['test' => [ + 'id' => 'test', + 'order' => 100, + 'href' => '/apps/test/', + 'icon' => '/apps/test/img/app.svg', + 'name' => 'Test', + 'active' => false, + 'type' => 'link', + 'classes' => '', + 'unread' => 0, + 'default' => false, + 'app' => 'test', + ], + 'test1' => [ + 'id' => 'test1', + 'order' => 50, + 'href' => '/apps/test/', + 'icon' => '/apps/test/img/app.svg', + 'name' => 'Other test', + 'active' => false, + 'type' => 'link', + 'classes' => '', + 'unread' => 0, + 'default' => true, // because of order + 'app' => 'test', + ]], + ['logout' => $defaults['logout']] + ), + ['navigations' => [ + 'navigation' => [ + ['route' => 'test.page.index', 'name' => 'Test'], + ['route' => 'test.page.index', 'name' => 'Other test', 'order' => 50], + ] + ]] + ], 'admin' => [ array_merge( + ['profile' => $defaults['profile']], $adminSettings, $apps, ['test' => [ @@ -391,7 +452,9 @@ class NavigationManagerTest extends TestCase { 'active' => false, 'type' => 'link', 'classes' => '', - 'unread' => 0 + 'unread' => 0, + 'default' => true, + 'app' => 'test', ]], ['logout' => $defaults['logout']] ), @@ -404,6 +467,7 @@ class NavigationManagerTest extends TestCase { ], 'no name' => [ array_merge( + ['profile' => $defaults['profile']], $adminSettings, $apps, ['logout' => $defaults['logout']] @@ -417,12 +481,85 @@ class NavigationManagerTest extends TestCase { ], 'no admin' => [ $defaults, - ['navigations' => [[ - '@attributes' => ['role' => 'admin'], - 'route' => 'test.page.index', - 'name' => 'Test' - ]]] + ['navigations' => [ + 'navigation' => [ + ['@attributes' => ['role' => 'admin'], 'route' => 'test.page.index', 'name' => 'Test'] + ], + ]], ] ]; } + + public function testWithAppManagerAndApporder() { + $l = $this->createMock(IL10N::class); + $l->expects($this->any())->method('t')->willReturnCallback(function ($text, $parameters = []) { + return vsprintf($text, $parameters); + }); + + $testOrder = 12; + $expected = [ + 'test' => [ + 'type' => 'link', + 'id' => 'test', + 'order' => $testOrder, + 'href' => '/apps/test/', + 'name' => 'Test', + 'icon' => '/apps/test/img/app.svg', + 'active' => false, + 'classes' => '', + 'unread' => 0, + 'default' => true, + 'app' => 'test', + ], + ]; + $navigation = ['navigations' => [ + 'navigation' => [ + ['route' => 'test.page.index', 'name' => 'Test'] + ], + ]]; + + $this->config->method('getUserValue') + ->willReturnCallback( + function (string $userId, string $appName, string $key, mixed $default = '') use ($testOrder) { + $this->assertEquals('user001', $userId); + if ($key === 'apporder') { + return json_encode(['test' => ['app' => 'test', 'order' => $testOrder]]); + } + return $default; + } + ); + + $this->appManager->expects($this->any()) + ->method('isEnabledForUser') + ->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"; + }); + $this->urlGenerator->expects($this->any())->method('linkToRoute')->willReturnCallback(function ($route) { + if ($route === 'core.login.logout') { + return 'https://example.com/logout'; + } + return '/apps/test/'; + }); + $user = $this->createMock(IUser::class); + $user->expects($this->any())->method('getUID')->willReturn('user001'); + $this->userSession->expects($this->any())->method('getUser')->willReturn($user); + $this->userSession->expects($this->any())->method('isLoggedIn')->willReturn(true); + $this->appManager->expects($this->any()) + ->method('getEnabledAppsForUser') + ->with($user) + ->willReturn(['test']); + $this->groupManager->expects($this->any())->method('isAdmin')->willReturn(false); + $subadmin = $this->createMock(SubAdmin::class); + $subadmin->expects($this->any())->method('isSubAdmin')->with($user)->willReturn(false); + $this->groupManager->expects($this->any())->method('getSubAdmin')->willReturn($subadmin); + + $this->navigationManager->clear(); + $entries = $this->navigationManager->getAll(); + $this->assertEquals($expected, $entries); + } } 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/Repair/RepairSqliteAutoincrementTest.php b/tests/lib/Repair/RepairSqliteAutoincrementTest.php deleted file mode 100644 index b4be47d0157..00000000000 --- a/tests/lib/Repair/RepairSqliteAutoincrementTest.php +++ /dev/null @@ -1,90 +0,0 @@ -<?php -/** - * Copyright (c) 2015 Vincent Petry <pvince81@owncloud.com> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - -namespace Test\Repair; - -use OC\DB\Connection; -use OCP\Migration\IOutput; - -/** - * Tests for fixing the SQLite id recycling - * - * @group DB - */ -class RepairSqliteAutoincrementTest extends \Test\TestCase { - /** - * @var \OC\Repair\SqliteAutoincrement - */ - private $repair; - - /** - * @var Connection - */ - private $connection; - - /** - * @var string - */ - private $tableName; - - /** - * @var \OCP\IConfig - */ - private $config; - - protected function setUp(): void { - parent::setUp(); - - $this->connection = \OC::$server->get(\OC\DB\Connection::class); - $this->config = \OC::$server->getConfig(); - if (!$this->connection->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\SqlitePlatform) { - $this->markTestSkipped("Test only relevant on Sqlite"); - } - - $dbPrefix = $this->config->getSystemValueString('dbtableprefix', 'oc_'); - $this->tableName = $this->getUniqueID($dbPrefix . 'autoinc_test'); - $this->connection->prepare('CREATE TABLE ' . $this->tableName . '("someid" INTEGER NOT NULL, "text" VARCHAR(16), PRIMARY KEY("someid"))')->execute(); - - $this->repair = new \OC\Repair\SqliteAutoincrement($this->connection); - } - - protected function tearDown(): void { - $this->connection->getSchemaManager()->dropTable($this->tableName); - parent::tearDown(); - } - - /** - * Tests whether autoincrement works - * - * @return boolean true if autoincrement works, false otherwise - */ - protected function checkAutoincrement() { - $this->connection->executeUpdate('INSERT INTO ' . $this->tableName . ' ("text") VALUES ("test")'); - $insertId = $this->connection->lastInsertId(); - $this->connection->executeUpdate('DELETE FROM ' . $this->tableName . ' WHERE "someid" = ?', [$insertId]); - - // insert again - $this->connection->executeUpdate('INSERT INTO ' . $this->tableName . ' ("text") VALUES ("test2")'); - $newInsertId = $this->connection->lastInsertId(); - - return ($insertId !== $newInsertId); - } - - public function testConvertIdColumn() { - $this->assertFalse($this->checkAutoincrement()); - - /** @var IOutput | \PHPUnit\Framework\MockObject\MockObject $outputMock */ - $outputMock = $this->getMockBuilder('\OCP\Migration\IOutput') - ->disableOriginalConstructor() - ->getMock(); - - $this->repair->run($outputMock); - - $this->assertTrue($this->checkAutoincrement()); - } -} 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 08e7d1aa274..0433da35edf 100644 --- a/tests/lib/Share20/DefaultShareProviderTest.php +++ b/tests/lib/Share20/DefaultShareProviderTest.php @@ -24,12 +24,12 @@ namespace Test\Share20; use OC\Share20\DefaultShareProvider; use OC\Share20\ShareAttributes; +use OCP\AppFramework\Utility\ITimeFactory; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Defaults; use OCP\Files\File; use OCP\Files\Folder; use OCP\Files\IRootFolder; -use OCP\IConfig; use OCP\IDBConnection; use OCP\IGroup; use OCP\IGroupManager; @@ -80,8 +80,8 @@ class DefaultShareProviderTest extends \Test\TestCase { /** @var \PHPUnit\Framework\MockObject\MockObject|IURLGenerator */ protected $urlGenerator; - /** @var IConfig|MockObject */ - protected $config; + /** @var ITimeFactory|MockObject */ + protected $timeFactory; protected function setUp(): void { $this->dbConn = \OC::$server->getDatabaseConnection(); @@ -93,9 +93,10 @@ class DefaultShareProviderTest extends \Test\TestCase { $this->l10n = $this->createMock(IL10N::class); $this->defaults = $this->getMockBuilder(Defaults::class)->disableOriginalConstructor()->getMock(); $this->urlGenerator = $this->createMock(IURLGenerator::class); - $this->config = $this->createMock(IConfig::class); + $this->timeFactory = $this->createMock(ITimeFactory::class); $this->userManager->expects($this->any())->method('userExists')->willReturn(true); + $this->timeFactory->expects($this->any())->method('now')->willReturn(new \DateTimeImmutable("2023-05-04 00:00 Europe/Berlin")); //Empty share table $this->dbConn->getQueryBuilder()->delete('share')->execute(); @@ -109,7 +110,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $this->defaults, $this->l10nFactory, $this->urlGenerator, - $this->config + $this->timeFactory ); } @@ -133,8 +134,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 +211,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 +289,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 +333,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 +372,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 +417,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') @@ -470,7 +471,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $this->defaults, $this->l10nFactory, $this->urlGenerator, - $this->config + $this->timeFactory ]) ->setMethods(['getShareById']) ->getMock(); @@ -565,7 +566,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $this->defaults, $this->l10nFactory, $this->urlGenerator, - $this->config + $this->timeFactory ]) ->setMethods(['getShareById']) ->getMock(); @@ -633,7 +634,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 +691,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'); @@ -725,11 +726,11 @@ class DefaultShareProviderTest extends \Test\TestCase { $this->assertLessThanOrEqual(new \DateTime(), $share2->getShareTime()); $this->assertSame($path, $share2->getNode()); - // nothing from setSharedWithDisplayName/setSharedWithAvatar is saved in DB + // Data is kept after creation $this->assertSame('Displayed Name', $share->getSharedWithDisplayName()); $this->assertSame('/path/to/image.svg', $share->getSharedWithAvatar()); - $this->assertSame(null, $share2->getSharedWithDisplayName()); - $this->assertSame(null, $share2->getSharedWithAvatar()); + $this->assertSame('Displayed Name', $share2->getSharedWithDisplayName()); + $this->assertSame('/path/to/image.svg', $share2->getSharedWithAvatar()); $this->assertSame( [ @@ -762,12 +763,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'); @@ -795,11 +796,11 @@ class DefaultShareProviderTest extends \Test\TestCase { $this->assertLessThanOrEqual(new \DateTime(), $share2->getShareTime()); $this->assertSame($path, $share2->getNode()); - // nothing from setSharedWithDisplayName/setSharedWithAvatar is saved in DB + // Data is kept after creation $this->assertSame('Displayed Name', $share->getSharedWithDisplayName()); $this->assertSame('/path/to/image.svg', $share->getSharedWithAvatar()); - $this->assertSame(null, $share2->getSharedWithDisplayName()); - $this->assertSame(null, $share2->getSharedWithAvatar()); + $this->assertSame('Displayed Name', $share2->getSharedWithDisplayName()); + $this->assertSame('/path/to/image.svg', $share2->getSharedWithAvatar()); $this->assertSame( [ @@ -832,12 +833,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 +891,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 +982,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 +1054,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 +1142,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 +1185,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 +1226,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 +1277,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 +1337,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 +1387,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 +1437,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 +1497,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 +1569,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 +1627,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 +1669,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 +1707,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 +1761,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 +1798,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 +1829,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 +1886,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 +1952,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 +2023,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 +2102,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 +2180,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 +2216,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'); @@ -2525,7 +2526,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $this->defaults, $this->l10nFactory, $this->urlGenerator, - $this->config + $this->timeFactory ); $password = md5(time()); @@ -2623,7 +2624,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $this->defaults, $this->l10nFactory, $this->urlGenerator, - $this->config + $this->timeFactory ); $u1 = $userManager->createUser('testShare1', 'test'); @@ -2719,7 +2720,7 @@ class DefaultShareProviderTest extends \Test\TestCase { $this->defaults, $this->l10nFactory, $this->urlGenerator, - $this->config + $this->timeFactory ); $u1 = $userManager->createUser('testShare1', 'test'); @@ -2881,23 +2882,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 1f3bcd405a1..d787556eb64 100644 --- a/tests/lib/Share20/ManagerTest.php +++ b/tests/lib/Share20/ManagerTest.php @@ -21,12 +21,14 @@ namespace Test\Share20; +use DateTimeZone; use OC\Files\Mount\MoveableMount; use OC\KnownUser\KnownUserService; use OC\Share20\DefaultShareProvider; use OC\Share20\Exception; use OC\Share20\Manager; use OC\Share20\Share; +use OC\Share20\ShareDisableChecker; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\File; @@ -38,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; @@ -111,6 +114,11 @@ class ManagerTest extends \Test\TestCase { protected $userSession; /** @var KnownUserService|MockObject */ 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); @@ -128,6 +136,11 @@ class ManagerTest extends \Test\TestCase { $this->userSession = $this->createMock(IUserSession::class); $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); $this->l->method('t') @@ -138,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, @@ -158,12 +179,10 @@ class ManagerTest extends \Test\TestCase { $this->defaults, $this->dispatcher, $this->userSession, - $this->knownUserService + $this->knownUserService, + $this->shareDisabledChecker, + $this->dateTimeZone, ); - - $this->defaultProvider = $this->createMock(DefaultShareProvider::class); - $this->defaultProvider->method('identifier')->willReturn('default'); - $this->factory->setProvider($this->defaultProvider); } /** @@ -178,7 +197,6 @@ class ManagerTest extends \Test\TestCase { $this->hasher, $this->mountManager, $this->groupManager, - $this->l, $this->l10nFactory, $this->factory, $this->userManager, @@ -188,7 +206,9 @@ class ManagerTest extends \Test\TestCase { $this->defaults, $this->dispatcher, $this->userSession, - $this->knownUserService + $this->knownUserService, + $this->shareDisabledChecker, + $this->dateTimeZone, ]); } @@ -778,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); @@ -926,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')); @@ -961,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')); @@ -1008,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); @@ -1050,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); @@ -1098,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') @@ -1133,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); @@ -1175,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); @@ -1304,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')); @@ -1325,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')); @@ -1356,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); @@ -1385,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); @@ -1424,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([ @@ -1447,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); @@ -1476,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'); @@ -1562,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]); @@ -1595,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 @@ -1609,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'); @@ -1638,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(); @@ -1787,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]); @@ -1810,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])); @@ -1839,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]); @@ -2738,25 +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 - ); + $manager = $this->createManager($factory); $share = $this->createMock(IShare::class); @@ -2785,25 +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 - ); + $manager = $this->createManager($factory); $share = $this->createMock(IShare::class); @@ -2839,25 +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 - ); + $manager = $this->createManager($factory); $share = $this->createMock(IShare::class); @@ -4238,25 +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 - ); + $manager = $this->createManager($factory); $this->assertSame($expected, $manager->shareProviderExists($shareType) ); @@ -4272,25 +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 - ); + $manager = $this->createManager($factory); $factory->setProvider($this->defaultProvider); $extraProvider = $this->createMock(IShareProvider::class); @@ -4337,25 +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 - ); + $manager = $this->createManager($factory); $factory->setProvider($this->defaultProvider); $extraProvider = $this->createMock(IShareProvider::class); @@ -4394,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'); @@ -4454,25 +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 - ); + $manager = $this->createManager($factory); $factory->setProvider($this->defaultProvider); $extraProvider = $this->createMock(IShareProvider::class); @@ -4511,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'); @@ -4580,25 +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 - ); + $manager = $this->createManager($factory); $factory->setProvider($this->defaultProvider); $extraProvider = $this->createMock(IShareProvider::class); diff --git a/tests/lib/Share20/ShareByMailProviderTest.php b/tests/lib/Share20/ShareByMailProviderTest.php index 5116dfe9e58..0cdd13fe17c 100644 --- a/tests/lib/Share20/ShareByMailProviderTest.php +++ b/tests/lib/Share20/ShareByMailProviderTest.php @@ -35,7 +35,6 @@ use OCP\Files\IRootFolder; use OCP\IConfig; use OCP\IDBConnection; use OCP\IL10N; -use OCP\ILogger; use OCP\IURLGenerator; use OCP\IUserManager; use OCP\Mail\IMailer; @@ -43,6 +42,7 @@ use OCP\Security\IHasher; use OCP\Security\ISecureRandom; use OCP\Share\IShare; use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; use Test\TestCase; /** @@ -79,7 +79,7 @@ class ShareByMailProviderTest extends TestCase { /** @var IConfig|MockObject */ protected $config; - /** @var ILogger|MockObject */ + /** @var LoggerInterface|MockObject */ private $logger; /** @var IHasher|MockObject */ @@ -108,7 +108,7 @@ class ShareByMailProviderTest extends TestCase { $this->l10n = $this->createMock(IL10N::class); $this->defaults = $this->getMockBuilder(Defaults::class)->disableOriginalConstructor()->getMock(); $this->urlGenerator = $this->createMock(IURLGenerator::class); - $this->logger = $this->createMock(ILogger::class); + $this->logger = $this->createMock(LoggerInterface::class); $this->activityManager = $this->createMock(\OCP\Activity\IManager::class); $this->settingsManager = $this->createMock(SettingsManager::class); $this->hasher = $this->createMock(IHasher::class); @@ -162,8 +162,8 @@ class ShareByMailProviderTest extends TestCase { * @throws \OCP\DB\Exception */ private function addShareToDB($shareType, $sharedWith, $sharedBy, $shareOwner, - $itemType, $fileSource, $fileTarget, $permissions, $token, $expiration, - $parent) { + $itemType, $fileSource, $fileTarget, $permissions, $token, $expiration, + $parent) { $qb = $this->dbConn->getQueryBuilder(); $qb->insert('share'); 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 f5fc9a6e8f2..db124bd6823 100644 --- a/tests/lib/TestCase.php +++ b/tests/lib/TestCase.php @@ -41,6 +41,38 @@ use OCP\IL10N; use OCP\Security\ISecureRandom; use Psr\Log\LoggerInterface; +if (version_compare(\PHPUnit\Runner\Version::id(), 10, '>=')) { + trait OnNotSuccessfulTestTrait { + protected function onNotSuccessfulTest(\Throwable $t): never { + $this->restoreAllServices(); + + // restore database connection + if (!$this->IsDatabaseAccessAllowed()) { + \OC::$server->registerService(IDBConnection::class, function () { + return self::$realDatabase; + }); + } + + parent::onNotSuccessfulTest($t); + } + } +} else { + trait OnNotSuccessfulTestTrait { + protected function onNotSuccessfulTest(\Throwable $t): void { + $this->restoreAllServices(); + + // restore database connection + if (!$this->IsDatabaseAccessAllowed()) { + \OC::$server->registerService(IDBConnection::class, function () { + return self::$realDatabase; + }); + } + + parent::onNotSuccessfulTest($t); + } + } +} + abstract class TestCase extends \PHPUnit\Framework\TestCase { /** @var \OC\Command\QueueBus */ private $commandBus; @@ -54,6 +86,8 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase { /** @var array */ protected $services = []; + use OnNotSuccessfulTestTrait; + /** * @param string $name * @param mixed $newService @@ -150,19 +184,6 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase { } } - protected function onNotSuccessfulTest(\Throwable $t): void { - $this->restoreAllServices(); - - // restore database connection - if (!$this->IsDatabaseAccessAllowed()) { - \OC::$server->registerService(IDBConnection::class, function () { - return self::$realDatabase; - }); - } - - parent::onNotSuccessfulTest($t); - } - protected function tearDown(): void { $this->restoreAllServices(); @@ -242,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 be847253035..ed04975fc54 100644 --- a/tests/lib/Updater/VersionCheckTest.php +++ b/tests/lib/Updater/VersionCheckTest.php @@ -24,24 +24,33 @@ 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; use OCP\Util; +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*/ private $registry; + /** @var LoggerInterface | \PHPUnit\Framework\Mo2ckObject\MockObject*/ + private $logger; protected function setUp(): void { parent::setUp(); $this->config = $this->getMockBuilder(IConfig::class) ->disableOriginalConstructor() ->getMock(); + $this->appConfig = $this->getMockBuilder(IAppConfig::class) + ->disableOriginalConstructor() + ->getMock(); $clientService = $this->getMockBuilder(IClientService::class) ->disableOriginalConstructor() ->getMock(); @@ -50,13 +59,16 @@ class VersionCheckTest extends \Test\TestCase { $this->registry ->method('delegateHasValidSubscription') ->willReturn(false); + $this->logger = $this->createMock(LoggerInterface::class); $this->updater = $this->getMockBuilder(VersionCheck::class) ->setMethods(['getUrlContent']) ->setConstructorArgs([ $clientService, $this->config, + $this->appConfig, $this->createMock(IUserManager::class), $this->registry, + $this->logger, ]) ->getMock(); } @@ -66,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() { @@ -83,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()); } @@ -114,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> @@ -166,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 @@ -220,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> @@ -273,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 @@ -327,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 new file mode 100644 index 00000000000..b41b1fbac2a --- /dev/null +++ b/tests/lib/User/AvailabilityCoordinatorTest.php @@ -0,0 +1,218 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Richard Steinmetz <richard@steinmetz.cloud> + * + * @author Richard Steinmetz <richard@steinmetz.cloud> + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace Test\User; + +use OC\User\AvailabilityCoordinator; +use OC\User\OutOfOfficeData; +use OCA\DAV\CalDAV\TimezoneService; +use OCA\DAV\Db\Absence; +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; + +class AvailabilityCoordinatorTest extends TestCase { + private AvailabilityCoordinator $availabilityCoordinator; + private ICacheFactory $cacheFactory; + private ICache $cache; + 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->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') + ->willReturn($this->cache); + + $this->availabilityCoordinator = new AvailabilityCoordinator( + $this->cacheFactory, + $this->config, + $this->absenceService, + $this->logger, + $this->timezoneService, + ); + } + + 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'); + $absence->setFirstDay('2023-10-01'); + $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::exactly(2)) + ->method('get') + ->willReturnOnConsecutiveCalls(null, null); + $this->absenceService->expects(self::once()) + ->method('getAbsence') + ->with($user->getUID()) + ->willReturn($absence); + $this->cache->expects(self::exactly(2)) + ->method('set') + ->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, + 1696111200, + 1696802340, + 'Vacation', + 'On vacation', + ); + $actual = $this->availabilityCoordinator->getCurrentOutOfOfficeData($user); + self::assertEquals($expected, $actual); + } + + 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::exactly(2)) + ->method('get') + ->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, + 1696809540, + 'Vacation', + 'On vacation', + ); + $actual = $this->availabilityCoordinator->getCurrentOutOfOfficeData($user); + 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); + $absence->setUserId('user'); + $absence->setFirstDay('2023-10-01'); + $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::exactly(2)) + ->method('get') + ->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":1696809540,"shortMessage":"Vacation","message":"On vacation"}', 300); + + $expected = new OutOfOfficeData( + '420', + $user, + 1696118400, + 1696809540, + 'Vacation', + 'On vacation', + ); + $actual = $this->availabilityCoordinator->getCurrentOutOfOfficeData($user); + self::assertEquals($expected, $actual); + } +} 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/lib/Util/User/Dummy.php b/tests/lib/Util/User/Dummy.php index 478b7599701..7106d879256 100644 --- a/tests/lib/Util/User/Dummy.php +++ b/tests/lib/Util/User/Dummy.php @@ -168,7 +168,7 @@ class Dummy extends Backend implements \OCP\IUserBackend { } public function getDisplayName($uid) { - return isset($this->displayNames[$uid])? $this->displayNames[$uid]: $uid; + return $this->displayNames[$uid] ?? $uid; } /** diff --git a/tests/lib/UtilCheckServerTest.php b/tests/lib/UtilCheckServerTest.php index 9ddb1f8e45f..7e47734ede7 100644 --- a/tests/lib/UtilCheckServerTest.php +++ b/tests/lib/UtilCheckServerTest.php @@ -30,7 +30,7 @@ class UtilCheckServerTest extends \Test\TestCase { $config->expects($this->any()) ->method('getValue') ->willReturnCallback(function ($key, $default) use ($systemOptions) { - return isset($systemOptions[$key]) ? $systemOptions[$key] : $default; + return $systemOptions[$key] ?? $default; }); return $config; } |