diff options
Diffstat (limited to 'apps/settings/tests')
39 files changed, 2344 insertions, 2410 deletions
diff --git a/apps/settings/tests/Activity/SecurityFilterTest.php b/apps/settings/tests/Activity/SecurityFilterTest.php index 6903cf5f3c2..b07c1e825b4 100644 --- a/apps/settings/tests/Activity/SecurityFilterTest.php +++ b/apps/settings/tests/Activity/SecurityFilterTest.php @@ -1,25 +1,8 @@ <?php + /** - * @copyright Copyright (c) 2016 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\Settings\Tests; @@ -30,15 +13,9 @@ use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; class SecurityFilterTest extends TestCase { - - /** @var IURLGenerator|MockObject */ - private $urlGenerator; - - /** @var IL10N|MockObject */ - private $l10n; - - /** @var SecurityFilter */ - private $filter; + private IURLGenerator&MockObject $urlGenerator; + private IL10N&MockObject $l10n; + private SecurityFilter $filter; protected function setUp(): void { parent::setUp(); @@ -49,15 +26,15 @@ class SecurityFilterTest extends TestCase { $this->filter = new SecurityFilter($this->urlGenerator, $this->l10n); } - public function testAllowedApps() { + public function testAllowedApps(): void { $this->assertEquals([], $this->filter->allowedApps()); } - public function testFilterTypes() { + public function testFilterTypes(): void { $this->assertEquals(['security'], $this->filter->filterTypes(['comments', 'security'])); } - public function testGetIcon() { + public function testGetIcon(): void { $this->urlGenerator->expects($this->once()) ->method('imagePath') ->with('core', 'actions/password.svg') @@ -69,11 +46,11 @@ class SecurityFilterTest extends TestCase { $this->assertEquals('abs/path/to/icon.svg', $this->filter->getIcon()); } - public function testGetIdentifier() { + public function testGetIdentifier(): void { $this->assertEquals('security', $this->filter->getIdentifier()); } - public function testGetName() { + public function testGetName(): void { $this->l10n->expects($this->once()) ->method('t') ->with('Security') @@ -81,7 +58,7 @@ class SecurityFilterTest extends TestCase { $this->assertEquals('translated', $this->filter->getName()); } - public function testGetPriority() { + public function testGetPriority(): void { $this->assertEquals(30, $this->filter->getPriority()); } } diff --git a/apps/settings/tests/Activity/SecurityProviderTest.php b/apps/settings/tests/Activity/SecurityProviderTest.php index 4499e470c2b..ed9de362a87 100644 --- a/apps/settings/tests/Activity/SecurityProviderTest.php +++ b/apps/settings/tests/Activity/SecurityProviderTest.php @@ -1,31 +1,13 @@ <?php + /** - * @copyright Copyright (c) 2016 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\Settings\Tests; -use InvalidArgumentException; use OCA\Settings\Activity\SecurityProvider; +use OCP\Activity\Exceptions\UnknownActivityException; use OCP\Activity\IEvent; use OCP\Activity\IManager; use OCP\IL10N; @@ -35,51 +17,41 @@ use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; class SecurityProviderTest extends TestCase { - - /** @var IFactory|MockObject */ - private $l10n; - - /** @var IURLGenerator|MockObject */ - private $urlGenerator; - - /** @var IManager|MockObject */ - private $activityManager; - - /** @var SecurityProvider */ - private $provider; + private IFactory&MockObject $l10nFactory; + private IURLGenerator&MockObject $urlGenerator; + private IManager&MockObject $activityManager; + private SecurityProvider $provider; protected function setUp(): void { parent::setUp(); - $this->l10n = $this->createMock(IFactory::class); + $this->l10nFactory = $this->createMock(IFactory::class); $this->urlGenerator = $this->createMock(IURLGenerator::class); $this->activityManager = $this->createMock(IManager::class); - $this->provider = new SecurityProvider($this->l10n, $this->urlGenerator, $this->activityManager); + $this->provider = new SecurityProvider($this->l10nFactory, $this->urlGenerator, $this->activityManager); } - public function testParseUnrelated() { + public function testParseUnrelated(): void { $lang = 'ru'; $event = $this->createMock(IEvent::class); $event->expects($this->once()) ->method('getType') ->willReturn('comments'); - $this->expectException(InvalidArgumentException::class); + $this->expectException(UnknownActivityException::class); $this->provider->parse($lang, $event); } - public function subjectData() { + public static function subjectData(): array { return [ ['twofactor_success'], ['twofactor_failed'], ]; } - /** - * @dataProvider subjectData - */ - public function testParse($subject) { + #[\PHPUnit\Framework\Attributes\DataProvider('subjectData')] + public function testParse(string $subject): void { $lang = 'ru'; $event = $this->createMock(IEvent::class); $l = $this->createMock(IL10N::class); @@ -87,7 +59,7 @@ class SecurityProviderTest extends TestCase { $event->expects($this->once()) ->method('getType') ->willReturn('security'); - $this->l10n->expects($this->once()) + $this->l10nFactory->expects($this->once()) ->method('get') ->with('settings', $lang) ->willReturn($l); @@ -115,7 +87,7 @@ class SecurityProviderTest extends TestCase { $this->provider->parse($lang, $event); } - public function testParseInvalidSubject() { + public function testParseInvalidSubject(): void { $lang = 'ru'; $l = $this->createMock(IL10N::class); $event = $this->createMock(IEvent::class); @@ -123,7 +95,7 @@ class SecurityProviderTest extends TestCase { $event->expects($this->once()) ->method('getType') ->willReturn('security'); - $this->l10n->expects($this->once()) + $this->l10nFactory->expects($this->once()) ->method('get') ->with('settings', $lang) ->willReturn($l); @@ -131,7 +103,7 @@ class SecurityProviderTest extends TestCase { ->method('getSubject') ->willReturn('unrelated'); - $this->expectException(InvalidArgumentException::class); + $this->expectException(UnknownActivityException::class); $this->provider->parse($lang, $event); } } diff --git a/apps/settings/tests/Activity/SecuritySettingTest.php b/apps/settings/tests/Activity/SecuritySettingTest.php index 840f228b0c1..ca11e38caa8 100644 --- a/apps/settings/tests/Activity/SecuritySettingTest.php +++ b/apps/settings/tests/Activity/SecuritySettingTest.php @@ -1,25 +1,8 @@ <?php + /** - * @copyright Copyright (c) 2016 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\Settings\Tests; @@ -30,8 +13,8 @@ use Test\TestCase; class SecuritySettingTest extends TestCase { private $l10n; - /** @var SecuritySetting */ - private $setting; + /** @var */ + private SecuritySetting $setting; protected function setUp(): void { parent::setUp(); @@ -41,19 +24,19 @@ class SecuritySettingTest extends TestCase { $this->setting = new SecuritySetting($this->l10n); } - public function testCanChangeMail() { + public function testCanChangeMail(): void { $this->assertFalse($this->setting->canChangeMail()); } - public function testCanChangeStream() { + public function testCanChangeStream(): void { $this->assertFalse($this->setting->canChangeStream()); } - public function testGetIdentifier() { + public function testGetIdentifier(): void { $this->assertEquals('security', $this->setting->getIdentifier()); } - public function testGetName() { + public function testGetName(): void { $this->l10n->expects($this->once()) ->method('t') ->with('Security') @@ -61,11 +44,11 @@ class SecuritySettingTest extends TestCase { $this->assertEquals('Sicherheit', $this->setting->getName()); } - public function testGetPriority() { + public function testGetPriority(): void { $this->assertEquals(30, $this->setting->getPriority()); } - public function testIsDefaultEnabled() { + public function testIsDefaultEnabled(): void { $this->assertTrue($this->setting->isDefaultEnabledMail()); $this->assertTrue($this->setting->isDefaultEnabledStream()); } diff --git a/apps/settings/tests/AppInfo/ApplicationTest.php b/apps/settings/tests/AppInfo/ApplicationTest.php index c9b44bda987..3e895d87b06 100644 --- a/apps/settings/tests/AppInfo/ApplicationTest.php +++ b/apps/settings/tests/AppInfo/ApplicationTest.php @@ -1,26 +1,9 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, Joas Schilling <coding@schilljs.com> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\Settings\Tests\AppInfo; @@ -45,11 +28,8 @@ use Test\TestCase; * @group DB */ class ApplicationTest extends TestCase { - /** @var Application */ - protected $app; - - /** @var IAppContainer */ - protected $container; + protected Application $app; + protected IAppContainer $container; protected function setUp(): void { parent::setUp(); @@ -57,12 +37,12 @@ class ApplicationTest extends TestCase { $this->container = $this->app->getContainer(); } - public function testContainerAppName() { + public function testContainerAppName(): void { $this->app = new Application(); - $this->assertEquals('settings', $this->container->getAppName()); + $this->assertEquals('settings', $this->container->get('appName')); } - public function dataContainerQuery() { + public static function dataContainerQuery(): array { return [ [AdminSettingsController::class, Controller::class], [AppSettingsController::class, Controller::class], @@ -76,12 +56,8 @@ class ApplicationTest extends TestCase { ]; } - /** - * @dataProvider dataContainerQuery - * @param string $service - * @param string $expected - */ - public function testContainerQuery($service, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataContainerQuery')] + public function testContainerQuery(string $service, string $expected): void { $this->assertTrue($this->container->query($service) instanceof $expected); } } diff --git a/apps/settings/tests/Controller/AdminSettingsControllerTest.php b/apps/settings/tests/Controller/AdminSettingsControllerTest.php index acdcaa136aa..fbdc506457b 100644 --- a/apps/settings/tests/Controller/AdminSettingsControllerTest.php +++ b/apps/settings/tests/Controller/AdminSettingsControllerTest.php @@ -1,40 +1,24 @@ <?php + /** - * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jan C. Borchardt <hey@jancborchardt.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\Settings\Tests\Controller; use OCA\Settings\Controller\AdminSettingsController; use OCA\Settings\Settings\Personal\ServerDevNotice; use OCP\AppFramework\Http\TemplateResponse; +use OCP\AppFramework\Services\IInitialState; use OCP\Group\ISubAdmin; use OCP\IGroupManager; use OCP\INavigationManager; use OCP\IRequest; use OCP\IUser; +use OCP\IUserManager; use OCP\IUserSession; +use OCP\Server; +use OCP\Settings\IDeclarativeManager; use OCP\Settings\IManager; use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; @@ -48,22 +32,17 @@ use Test\TestCase; */ class AdminSettingsControllerTest extends TestCase { - /** @var AdminSettingsController */ - private $adminSettingsController; - /** @var IRequest|MockObject */ - private $request; - /** @var INavigationManager|MockObject */ - private $navigationManager; - /** @var IManager|MockObject */ - private $settingsManager; - /** @var IUserSession|MockObject */ - private $userSession; - /** @var IGroupManager|MockObject */ - private $groupManager; - /** @var ISubAdmin|MockObject */ - private $subAdmin; - /** @var string */ - private $adminUid = 'lololo'; + private IRequest&MockObject $request; + private INavigationManager&MockObject $navigationManager; + private IManager&MockObject $settingsManager; + private IUserSession&MockObject $userSession; + private IGroupManager&MockObject $groupManager; + private ISubAdmin&MockObject $subAdmin; + private IDeclarativeManager&MockObject $declarativeSettingsManager; + private IInitialState&MockObject $initialState; + + private string $adminUid = 'lololo'; + private AdminSettingsController $adminSettingsController; protected function setUp(): void { parent::setUp(); @@ -74,6 +53,8 @@ class AdminSettingsControllerTest extends TestCase { $this->userSession = $this->createMock(IUserSession::class); $this->groupManager = $this->createMock(IGroupManager::class); $this->subAdmin = $this->createMock(ISubAdmin::class); + $this->declarativeSettingsManager = $this->createMock(IDeclarativeManager::class); + $this->initialState = $this->createMock(IInitialState::class); $this->adminSettingsController = new AdminSettingsController( 'settings', @@ -82,21 +63,27 @@ class AdminSettingsControllerTest extends TestCase { $this->settingsManager, $this->userSession, $this->groupManager, - $this->subAdmin + $this->subAdmin, + $this->declarativeSettingsManager, + $this->initialState, ); - $user = \OC::$server->getUserManager()->createUser($this->adminUid, 'mylongrandompassword'); + $user = Server::get(IUserManager::class)->createUser($this->adminUid, 'mylongrandompassword'); \OC_User::setUserId($user->getUID()); - \OC::$server->getGroupManager()->createGroup('admin')->addUser($user); + Server::get(IGroupManager::class)->createGroup('admin')->addUser($user); } protected function tearDown(): void { - \OC::$server->getUserManager()->get($this->adminUid)->delete(); + Server::get(IUserManager::class) + ->get($this->adminUid) + ->delete(); + \OC_User::setUserId(null); + Server::get(IUserSession::class)->setUser(null); parent::tearDown(); } - public function testIndex() { + public function testIndex(): void { $user = $this->createMock(IUser::class); $this->userSession ->method('getUser') @@ -110,6 +97,12 @@ class AdminSettingsControllerTest extends TestCase { ->method('isSubAdmin') ->with($user) ->willReturn(false); + + $form = new TemplateResponse('settings', 'settings/empty'); + $setting = $this->createMock(ServerDevNotice::class); + $setting->expects(self::any()) + ->method('getForm') + ->willReturn($form); $this->settingsManager ->expects($this->once()) ->method('getAdminSections') @@ -122,7 +115,12 @@ class AdminSettingsControllerTest extends TestCase { ->expects($this->once()) ->method('getAllowedAdminSettings') ->with('test') - ->willReturn([5 => $this->createMock(ServerDevNotice::class)]); + ->willReturn([5 => [$setting]]); + $this->declarativeSettingsManager + ->expects($this->any()) + ->method('getFormIDs') + ->with($user, 'admin', 'test') + ->willReturn([]); $idx = $this->adminSettingsController->index('test'); diff --git a/apps/settings/tests/Controller/AppSettingsControllerTest.php b/apps/settings/tests/Controller/AppSettingsControllerTest.php index 28d77dc3696..392bb7b561d 100644 --- a/apps/settings/tests/Controller/AppSettingsControllerTest.php +++ b/apps/settings/tests/Controller/AppSettingsControllerTest.php @@ -1,42 +1,25 @@ <?php + /** - * @copyright Copyright (c) 2016, Lukas Reschke <lukas@statuscode.ch> - * @copyright Copyright (c) 2015, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2015 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OCA\Settings\Tests\Controller; +use OC\App\AppManager; use OC\App\AppStore\Bundles\BundleFetcher; +use OC\App\AppStore\Fetcher\AppDiscoverFetcher; use OC\App\AppStore\Fetcher\AppFetcher; use OC\App\AppStore\Fetcher\CategoryFetcher; use OC\Installer; use OCA\Settings\Controller\AppSettingsController; -use OCP\App\IAppManager; use OCP\AppFramework\Http\ContentSecurityPolicy; use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\TemplateResponse; +use OCP\AppFramework\Services\IInitialState; +use OCP\Files\AppData\IAppDataFactory; +use OCP\Http\Client\IClientService; use OCP\IConfig; use OCP\IL10N; use OCP\INavigationManager; @@ -55,44 +38,36 @@ use Test\TestCase; * @group DB */ class AppSettingsControllerTest extends TestCase { - /** @var AppSettingsController */ - private $appSettingsController; - /** @var IRequest|MockObject */ - private $request; - /** @var IL10N|MockObject */ - private $l10n; - /** @var IConfig|MockObject */ - private $config; - /** @var INavigationManager|MockObject */ - private $navigationManager; - /** @var IAppManager|MockObject */ - private $appManager; - /** @var CategoryFetcher|MockObject */ - private $categoryFetcher; - /** @var AppFetcher|MockObject */ - private $appFetcher; - /** @var IFactory|MockObject */ - private $l10nFactory; - /** @var BundleFetcher|MockObject */ - private $bundleFetcher; - /** @var Installer|MockObject */ - private $installer; - /** @var IURLGenerator|MockObject */ - private $urlGenerator; - /** @var LoggerInterface|MockObject */ - private $logger; + private IRequest&MockObject $request; + private IL10N&MockObject $l10n; + private IConfig&MockObject $config; + private INavigationManager&MockObject $navigationManager; + private AppManager&MockObject $appManager; + private CategoryFetcher&MockObject $categoryFetcher; + private AppFetcher&MockObject $appFetcher; + private IFactory&MockObject $l10nFactory; + private BundleFetcher&MockObject $bundleFetcher; + private Installer&MockObject $installer; + private IURLGenerator&MockObject $urlGenerator; + private LoggerInterface&MockObject $logger; + private IInitialState&MockObject $initialState; + private IAppDataFactory&MockObject $appDataFactory; + private AppDiscoverFetcher&MockObject $discoverFetcher; + private IClientService&MockObject $clientService; + private AppSettingsController $appSettingsController; protected function setUp(): void { parent::setUp(); $this->request = $this->createMock(IRequest::class); + $this->appDataFactory = $this->createMock(IAppDataFactory::class); $this->l10n = $this->createMock(IL10N::class); $this->l10n->expects($this->any()) ->method('t') ->willReturnArgument(0); $this->config = $this->createMock(IConfig::class); $this->navigationManager = $this->createMock(INavigationManager::class); - $this->appManager = $this->createMock(IAppManager::class); + $this->appManager = $this->createMock(AppManager::class); $this->categoryFetcher = $this->createMock(CategoryFetcher::class); $this->appFetcher = $this->createMock(AppFetcher::class); $this->l10nFactory = $this->createMock(IFactory::class); @@ -100,10 +75,14 @@ class AppSettingsControllerTest extends TestCase { $this->installer = $this->createMock(Installer::class); $this->urlGenerator = $this->createMock(IURLGenerator::class); $this->logger = $this->createMock(LoggerInterface::class); + $this->initialState = $this->createMock(IInitialState::class); + $this->discoverFetcher = $this->createMock(AppDiscoverFetcher::class); + $this->clientService = $this->createMock(IClientService::class); $this->appSettingsController = new AppSettingsController( 'settings', $this->request, + $this->appDataFactory, $this->l10n, $this->config, $this->navigationManager, @@ -114,63 +93,56 @@ class AppSettingsControllerTest extends TestCase { $this->bundleFetcher, $this->installer, $this->urlGenerator, - $this->logger + $this->logger, + $this->initialState, + $this->discoverFetcher, + $this->clientService, ); } - public function testListCategories() { + public function testListCategories(): void { $this->installer->expects($this->any()) ->method('isUpdateAvailable') ->willReturn(false); $expected = new JSONResponse([ [ 'id' => 'auth', - 'ident' => 'auth', 'displayName' => 'Authentication & authorization', ], [ 'id' => 'customization', - 'ident' => 'customization', 'displayName' => 'Customization', ], [ 'id' => 'files', - 'ident' => 'files', 'displayName' => 'Files', ], [ 'id' => 'integration', - 'ident' => 'integration', 'displayName' => 'Integration', ], [ 'id' => 'monitoring', - 'ident' => 'monitoring', 'displayName' => 'Monitoring', ], [ 'id' => 'multimedia', - 'ident' => 'multimedia', 'displayName' => 'Multimedia', ], [ 'id' => 'office', - 'ident' => 'office', 'displayName' => 'Office & text', ], [ 'id' => 'organization', - 'ident' => 'organization', 'displayName' => 'Organization', ], [ 'id' => 'social', - 'ident' => 'social', 'displayName' => 'Social & communication', ], [ 'id' => 'tools', - 'ident' => 'tools', 'displayName' => 'Tools', ], ]); @@ -183,7 +155,7 @@ class AppSettingsControllerTest extends TestCase { $this->assertEquals($expected, $this->appSettingsController->listCategories()); } - public function testViewApps() { + public function testViewApps(): void { $this->bundleFetcher->expects($this->once())->method('getBundles')->willReturn([]); $this->installer->expects($this->any()) ->method('isUpdateAvailable') @@ -198,18 +170,17 @@ class AppSettingsControllerTest extends TestCase { ->method('setActiveEntry') ->with('core_apps'); + $this->initialState + ->expects($this->exactly(4)) + ->method('provideInitialState'); + $policy = new ContentSecurityPolicy(); $policy->addAllowedImageDomain('https://usercontent.apps.nextcloud.com'); $expected = new TemplateResponse('settings', - 'settings-vue', + 'settings/empty', [ - 'serverData' => [ - 'updateCount' => 0, - 'appstoreEnabled' => true, - 'bundles' => [], - 'developerDocumentation' => '' - ] + 'pageTitle' => 'Settings' ], 'user'); $expected->setContentSecurityPolicy($policy); @@ -217,7 +188,7 @@ class AppSettingsControllerTest extends TestCase { $this->assertEquals($expected, $this->appSettingsController->viewApps()); } - public function testViewAppsAppstoreNotEnabled() { + public function testViewAppsAppstoreNotEnabled(): void { $this->installer->expects($this->any()) ->method('isUpdateAvailable') ->willReturn(false); @@ -232,18 +203,17 @@ class AppSettingsControllerTest extends TestCase { ->method('setActiveEntry') ->with('core_apps'); + $this->initialState + ->expects($this->exactly(4)) + ->method('provideInitialState'); + $policy = new ContentSecurityPolicy(); $policy->addAllowedImageDomain('https://usercontent.apps.nextcloud.com'); $expected = new TemplateResponse('settings', - 'settings-vue', + 'settings/empty', [ - 'serverData' => [ - 'updateCount' => 0, - 'appstoreEnabled' => false, - 'bundles' => [], - 'developerDocumentation' => '' - ] + 'pageTitle' => 'Settings' ], 'user'); $expected->setContentSecurityPolicy($policy); diff --git a/apps/settings/tests/Controller/AuthSettingsControllerTest.php b/apps/settings/tests/Controller/AuthSettingsControllerTest.php index b744b942e09..d195dbf83d3 100644 --- a/apps/settings/tests/Controller/AuthSettingsControllerTest.php +++ b/apps/settings/tests/Controller/AuthSettingsControllerTest.php @@ -1,31 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Fabrizio Steiner <fabrizio.steiner@gmail.com> - * @author Greta Doci <gretadoci@gmail.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Sergej Nikolaev <kinolaev@gmail.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace Test\Settings\Controller; @@ -51,24 +29,15 @@ use Psr\Log\LoggerInterface; use Test\TestCase; class AuthSettingsControllerTest extends TestCase { - - /** @var AuthSettingsController */ - private $controller; - /** @var IRequest|MockObject */ - private $request; - /** @var IProvider|MockObject */ - private $tokenProvider; - /** @var ISession|MockObject */ - private $session; - /**@var IUserSession|MockObject */ - private $userSession; - /** @var ISecureRandom|MockObject */ - private $secureRandom; - /** @var IManager|MockObject */ - private $activityManager; - /** @var RemoteWipe|MockObject */ - private $remoteWipe; - private $uid = 'jane'; + private IRequest&MockObject $request; + private IProvider&MockObject $tokenProvider; + private ISession&MockObject $session; + private IUserSession&MockObject $userSession; + private ISecureRandom&MockObject $secureRandom; + private IManager&MockObject $activityManager; + private RemoteWipe&MockObject $remoteWipe; + private string $uid = 'jane'; + private AuthSettingsController $controller; protected function setUp(): void { parent::setUp(); @@ -80,7 +49,7 @@ class AuthSettingsControllerTest extends TestCase { $this->secureRandom = $this->createMock(ISecureRandom::class); $this->activityManager = $this->createMock(IManager::class); $this->remoteWipe = $this->createMock(RemoteWipe::class); - /** @var LoggerInterface|MockObject $logger */ + /** @var LoggerInterface&MockObject $logger */ $logger = $this->createMock(LoggerInterface::class); $this->controller = new AuthSettingsController( @@ -97,7 +66,7 @@ class AuthSettingsControllerTest extends TestCase { ); } - public function testCreate() { + public function testCreate(): void { $name = 'Nexus 4'; $sessionToken = $this->createMock(IToken::class); $deviceToken = $this->createMock(IToken::class); @@ -146,12 +115,12 @@ class AuthSettingsControllerTest extends TestCase { $this->assertEquals($expected, $response->getData()); } - public function testCreateSessionNotAvailable() { + public function testCreateSessionNotAvailable(): void { $name = 'personal phone'; $this->session->expects($this->once()) ->method('getId') - ->will($this->throwException(new SessionNotAvailableException())); + ->willThrowException(new SessionNotAvailableException()); $expected = new JSONResponse(); $expected->setStatus(Http::STATUS_SERVICE_UNAVAILABLE); @@ -159,7 +128,7 @@ class AuthSettingsControllerTest extends TestCase { $this->assertEquals($expected, $this->controller->create($name)); } - public function testCreateInvalidToken() { + public function testCreateInvalidToken(): void { $name = 'Company IPhone'; $this->session->expects($this->once()) @@ -168,7 +137,7 @@ class AuthSettingsControllerTest extends TestCase { $this->tokenProvider->expects($this->once()) ->method('getToken') ->with('sessionid') - ->will($this->throwException(new InvalidTokenException())); + ->willThrowException(new InvalidTokenException()); $expected = new JSONResponse(); $expected->setStatus(Http::STATUS_SERVICE_UNAVAILABLE); @@ -176,7 +145,7 @@ class AuthSettingsControllerTest extends TestCase { $this->assertEquals($expected, $this->controller->create($name)); } - public function testDestroy() { + public function testDestroy(): void { $tokenId = 124; $token = $this->createMock(PublicKeyToken::class); @@ -198,7 +167,7 @@ class AuthSettingsControllerTest extends TestCase { $this->assertEquals([], $this->controller->destroy($tokenId)); } - public function testDestroyExpired() { + public function testDestroyExpired(): void { $tokenId = 124; $token = $this->createMock(PublicKeyToken::class); @@ -222,7 +191,7 @@ class AuthSettingsControllerTest extends TestCase { $this->assertSame([], $this->controller->destroy($tokenId)); } - public function testDestroyWrongUser() { + public function testDestroyWrongUser(): void { $tokenId = 124; $token = $this->createMock(PublicKeyToken::class); @@ -237,19 +206,14 @@ class AuthSettingsControllerTest extends TestCase { $this->assertSame(\OCP\AppFramework\Http::STATUS_NOT_FOUND, $response->getStatus()); } - public function dataRenameToken(): array { + public static function dataRenameToken(): array { return [ 'App password => Other token name' => ['App password', 'Other token name'], 'Other token name => App password' => ['Other token name', 'App password'], ]; } - /** - * @dataProvider dataRenameToken - * - * @param string $name - * @param string $newName - */ + #[\PHPUnit\Framework\Attributes\DataProvider('dataRenameToken')] public function testUpdateRename(string $name, string $newName): void { $tokenId = 42; $token = $this->createMock(PublicKeyToken::class); @@ -267,7 +231,7 @@ class AuthSettingsControllerTest extends TestCase { $token->expects($this->once()) ->method('getScopeAsArray') - ->willReturn(['filesystem' => true]); + ->willReturn([IToken::SCOPE_FILESYSTEM => true]); $token->expects($this->once()) ->method('setName') @@ -277,22 +241,17 @@ class AuthSettingsControllerTest extends TestCase { ->method('updateToken') ->with($this->equalTo($token)); - $this->assertSame([], $this->controller->update($tokenId, ['filesystem' => true], $newName)); + $this->assertSame([], $this->controller->update($tokenId, [IToken::SCOPE_FILESYSTEM => true], $newName)); } - public function dataUpdateFilesystemScope(): array { + public static function dataUpdateFilesystemScope(): array { return [ 'Grant filesystem access' => [false, true], 'Revoke filesystem access' => [true, false], ]; } - /** - * @dataProvider dataUpdateFilesystemScope - * - * @param bool $filesystem - * @param bool $newFilesystem - */ + #[\PHPUnit\Framework\Attributes\DataProvider('dataUpdateFilesystemScope')] public function testUpdateFilesystemScope(bool $filesystem, bool $newFilesystem): void { $tokenId = 42; $token = $this->createMock(PublicKeyToken::class); @@ -310,17 +269,17 @@ class AuthSettingsControllerTest extends TestCase { $token->expects($this->once()) ->method('getScopeAsArray') - ->willReturn(['filesystem' => $filesystem]); + ->willReturn([IToken::SCOPE_FILESYSTEM => $filesystem]); $token->expects($this->once()) ->method('setScope') - ->with($this->equalTo(['filesystem' => $newFilesystem])); + ->with($this->equalTo([IToken::SCOPE_FILESYSTEM => $newFilesystem])); $this->tokenProvider->expects($this->once()) ->method('updateToken') ->with($this->equalTo($token)); - $this->assertSame([], $this->controller->update($tokenId, ['filesystem' => $newFilesystem], 'App password')); + $this->assertSame([], $this->controller->update($tokenId, [IToken::SCOPE_FILESYSTEM => $newFilesystem], 'App password')); } public function testUpdateNoChange(): void { @@ -339,7 +298,7 @@ class AuthSettingsControllerTest extends TestCase { $token->expects($this->once()) ->method('getScopeAsArray') - ->willReturn(['filesystem' => true]); + ->willReturn([IToken::SCOPE_FILESYSTEM => true]); $token->expects($this->never()) ->method('setName'); @@ -351,10 +310,10 @@ class AuthSettingsControllerTest extends TestCase { ->method('updateToken') ->with($this->equalTo($token)); - $this->assertSame([], $this->controller->update($tokenId, ['filesystem' => true], 'App password')); + $this->assertSame([], $this->controller->update($tokenId, [IToken::SCOPE_FILESYSTEM => true], 'App password')); } - public function testUpdateExpired() { + public function testUpdateExpired(): void { $tokenId = 42; $token = $this->createMock(PublicKeyToken::class); @@ -371,10 +330,10 @@ class AuthSettingsControllerTest extends TestCase { ->method('updateToken') ->with($this->equalTo($token)); - $this->assertSame([], $this->controller->update($tokenId, ['filesystem' => true], 'App password')); + $this->assertSame([], $this->controller->update($tokenId, [IToken::SCOPE_FILESYSTEM => true], 'App password')); } - public function testUpdateTokenWrongUser() { + public function testUpdateTokenWrongUser(): void { $tokenId = 42; $token = $this->createMock(PublicKeyToken::class); @@ -389,12 +348,12 @@ class AuthSettingsControllerTest extends TestCase { $this->tokenProvider->expects($this->never()) ->method('updateToken'); - $response = $this->controller->update($tokenId, ['filesystem' => true], 'App password'); + $response = $this->controller->update($tokenId, [IToken::SCOPE_FILESYSTEM => true], 'App password'); $this->assertSame([], $response->getData()); $this->assertSame(\OCP\AppFramework\Http::STATUS_NOT_FOUND, $response->getStatus()); } - public function testUpdateTokenNonExisting() { + public function testUpdateTokenNonExisting(): void { $this->tokenProvider->expects($this->once()) ->method('getTokenById') ->with($this->equalTo(42)) @@ -403,7 +362,7 @@ class AuthSettingsControllerTest extends TestCase { $this->tokenProvider->expects($this->never()) ->method('updateToken'); - $response = $this->controller->update(42, ['filesystem' => true], 'App password'); + $response = $this->controller->update(42, [IToken::SCOPE_FILESYSTEM => true], 'App password'); $this->assertSame([], $response->getData()); $this->assertSame(\OCP\AppFramework\Http::STATUS_NOT_FOUND, $response->getStatus()); } diff --git a/apps/settings/tests/Controller/CheckSetupControllerTest.php b/apps/settings/tests/Controller/CheckSetupControllerTest.php index b7dd96ae653..a8e89260573 100644 --- a/apps/settings/tests/Controller/CheckSetupControllerTest.php +++ b/apps/settings/tests/Controller/CheckSetupControllerTest.php @@ -1,67 +1,25 @@ <?php + /** - * @copyright Copyright (c) 2015, ownCloud, Inc. - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Michael Weimann <mail@michael-weimann.eu> - * @author Morris Jobke <hey@morrisjobke.de> - * @author nhirokinet <nhirokinet@nhiroki.net> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Sylvia van Os <sylvia@hackerchick.me> - * @author Timo Förster <tfoerster@webfoersterei.de> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2015 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OCA\Settings\Tests\Controller; -use bantu\IniGetWrapper\IniGetWrapper; -use Doctrine\DBAL\Platforms\SqlitePlatform; -use OC; -use OC\DB\Connection; use OC\IntegrityCheck\Checker; -use OC\MemoryInfo; -use OC\Security\SecureRandom; use OCA\Settings\Controller\CheckSetupController; -use OCP\App\IAppManager; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataDisplayResponse; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\RedirectResponse; -use OCP\Http\Client\IClientService; use OCP\IConfig; -use OCP\IDateTimeFormatter; -use OCP\IDBConnection; use OCP\IL10N; use OCP\IRequest; -use OCP\IServerContainer; -use OCP\ITempManager; use OCP\IURLGenerator; -use OCP\Lock\ILockingProvider; -use OCP\Notification\IManager; +use OCP\SetupCheck\ISetupCheckManager; use PHPUnit\Framework\MockObject\MockObject; -use Psr\Http\Message\ResponseInterface; use Psr\Log\LoggerInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Test\TestCase; /** @@ -71,514 +29,60 @@ use Test\TestCase; * @package Tests\Settings\Controller */ class CheckSetupControllerTest extends TestCase { - /** @var CheckSetupController | \PHPUnit\Framework\MockObject\MockObject */ - private $checkSetupController; - /** @var IRequest | \PHPUnit\Framework\MockObject\MockObject */ - private $request; - /** @var IConfig | \PHPUnit\Framework\MockObject\MockObject */ - private $config; - /** @var IClientService | \PHPUnit\Framework\MockObject\MockObject*/ - private $clientService; - /** @var IURLGenerator | \PHPUnit\Framework\MockObject\MockObject */ - private $urlGenerator; - /** @var IL10N | \PHPUnit\Framework\MockObject\MockObject */ - private $l10n; - /** @var LoggerInterface */ - private $logger; - /** @var Checker|\PHPUnit\Framework\MockObject\MockObject */ - private $checker; - /** @var EventDispatcherInterface|\PHPUnit\Framework\MockObject\MockObject */ - private $dispatcher; - /** @var Connection|\PHPUnit\Framework\MockObject\MockObject */ - private $db; - /** @var ILockingProvider|\PHPUnit\Framework\MockObject\MockObject */ - private $lockingProvider; - /** @var IDateTimeFormatter|\PHPUnit\Framework\MockObject\MockObject */ - private $dateTimeFormatter; - /** @var MemoryInfo|MockObject */ - private $memoryInfo; - /** @var SecureRandom|\PHPUnit\Framework\MockObject\MockObject */ - private $secureRandom; - /** @var IniGetWrapper|\PHPUnit\Framework\MockObject\MockObject */ - private $iniGetWrapper; - /** @var IDBConnection|\PHPUnit\Framework\MockObject\MockObject */ - private $connection; - /** @var ITempManager|\PHPUnit\Framework\MockObject\MockObject */ - private $tempManager; - /** @var IManager|\PHPUnit\Framework\MockObject\MockObject */ - private $notificationManager; - /** @var IAppManager|MockObject */ - private $appManager; - /** @var IServerContainer|MockObject */ - private $serverContainer; - - /** - * Holds a list of directories created during tests. - * - * @var array - */ - private $dirsToRemove = []; + private IRequest&MockObject $request; + private IConfig&MockObject $config; + private IURLGenerator&MockObject $urlGenerator; + private IL10N&MockObject $l10n; + private LoggerInterface&MockObject $logger; + private Checker&MockObject $checker; + private ISetupCheckManager&MockObject $setupCheckManager; + private CheckSetupController $checkSetupController; protected function setUp(): void { parent::setUp(); - $this->request = $this->getMockBuilder(IRequest::class) - ->disableOriginalConstructor()->getMock(); - $this->config = $this->getMockBuilder(IConfig::class) - ->disableOriginalConstructor()->getMock(); - $this->clientService = $this->getMockBuilder(IClientService::class) - ->disableOriginalConstructor()->getMock(); - $this->urlGenerator = $this->getMockBuilder(IURLGenerator::class) - ->disableOriginalConstructor()->getMock(); - $this->l10n = $this->getMockBuilder(IL10N::class) - ->disableOriginalConstructor()->getMock(); + $this->request = $this->createMock(IRequest::class); + $this->config = $this->createMock(IConfig::class); + $this->urlGenerator = $this->createMock(IURLGenerator::class); + $this->l10n = $this->createMock(IL10N::class); $this->l10n->expects($this->any()) ->method('t') ->willReturnCallback(function ($message, array $replace) { return vsprintf($message, $replace); }); - $this->dispatcher = $this->getMockBuilder(EventDispatcherInterface::class) - ->disableOriginalConstructor()->getMock(); - $this->checker = $this->getMockBuilder('\OC\IntegrityCheck\Checker') - ->disableOriginalConstructor()->getMock(); - $this->logger = $this->getMockBuilder(LoggerInterface::class)->getMock(); - $this->db = $this->getMockBuilder(Connection::class) - ->disableOriginalConstructor()->getMock(); - $this->lockingProvider = $this->getMockBuilder(ILockingProvider::class)->getMock(); - $this->dateTimeFormatter = $this->getMockBuilder(IDateTimeFormatter::class)->getMock(); - $this->memoryInfo = $this->getMockBuilder(MemoryInfo::class) - ->setMethods(['isMemoryLimitSufficient',]) - ->getMock(); - $this->secureRandom = $this->getMockBuilder(SecureRandom::class)->getMock(); - $this->iniGetWrapper = $this->getMockBuilder(IniGetWrapper::class)->getMock(); - $this->connection = $this->getMockBuilder(IDBConnection::class) - ->disableOriginalConstructor()->getMock(); - $this->tempManager = $this->getMockBuilder(ITempManager::class)->getMock(); - $this->notificationManager = $this->getMockBuilder(IManager::class)->getMock(); - $this->appManager = $this->createMock(IAppManager::class); - $this->serverContainer = $this->createMock(IServerContainer::class); - $this->checkSetupController = $this->getMockBuilder(CheckSetupController::class) - ->setConstructorArgs([ - 'settings', - $this->request, - $this->config, - $this->clientService, - $this->urlGenerator, - $this->l10n, - $this->checker, - $this->logger, - $this->dispatcher, - $this->db, - $this->lockingProvider, - $this->dateTimeFormatter, - $this->memoryInfo, - $this->secureRandom, - $this->iniGetWrapper, - $this->connection, - $this->tempManager, - $this->notificationManager, - $this->appManager, - $this->serverContainer, - ]) - ->setMethods([ - 'isReadOnlyConfig', - 'wasEmailTestSuccessful', - 'hasValidTransactionIsolationLevel', - 'hasFileinfoInstalled', - 'hasWorkingFileLocking', - 'getLastCronInfo', - 'getSuggestedOverwriteCliURL', - 'getCurlVersion', - 'isPhpOutdated', - 'getOpcacheSetupRecommendations', - 'hasFreeTypeSupport', - 'hasMissingIndexes', - 'hasMissingPrimaryKeys', - 'isSqliteUsed', - 'isPHPMailerUsed', - 'getAppDirsWithDifferentOwner', - 'isImagickEnabled', - 'areWebauthnExtensionsEnabled', - 'hasRecommendedPHPModules', - 'hasBigIntConversionPendingColumns', - 'isMysqlUsedWithoutUTF8MB4', - 'isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed', - ])->getMock(); - } - - /** - * Removes directories created during tests. - * - * @after - * @return void - */ - public function removeTestDirectories() { - foreach ($this->dirsToRemove as $dirToRemove) { - rmdir($dirToRemove); - } - $this->dirsToRemove = []; - } - - public function testIsInternetConnectionWorkingDisabledViaConfig() { - $this->config->expects($this->once()) - ->method('getSystemValue') - ->with('has_internet_connection', true) - ->willReturn(false); - - $this->assertFalse( - self::invokePrivate( - $this->checkSetupController, - 'hasInternetConnectivityProblems' - ) - ); - } - - public function testIsInternetConnectionWorkingCorrectly() { - $this->config->expects($this->at(0)) - ->method('getSystemValue') - ->with('has_internet_connection', true) - ->willReturn(true); - - $this->config->expects($this->at(1)) - ->method('getSystemValue') - ->with('connectivity_check_domains', ['www.nextcloud.com', 'www.startpage.com', 'www.eff.org', 'www.edri.org']) - ->willReturn(['www.nextcloud.com', 'www.startpage.com', 'www.eff.org', 'www.edri.org']); - - $client = $this->getMockBuilder('\OCP\Http\Client\IClient') - ->disableOriginalConstructor()->getMock(); - $client->expects($this->any()) - ->method('get'); - - $this->clientService->expects($this->once()) - ->method('newClient') - ->willReturn($client); - - - $this->assertFalse( - self::invokePrivate( - $this->checkSetupController, - 'hasInternetConnectivityProblems' - ) - ); - } - - public function testIsInternetConnectionFail() { - $this->config->expects($this->at(0)) - ->method('getSystemValue') - ->with('has_internet_connection', true) - ->willReturn(true); - - $this->config->expects($this->at(1)) - ->method('getSystemValue') - ->with('connectivity_check_domains', ['www.nextcloud.com', 'www.startpage.com', 'www.eff.org', 'www.edri.org']) - ->willReturn(['www.nextcloud.com', 'www.startpage.com', 'www.eff.org', 'www.edri.org']); - - $client = $this->getMockBuilder('\OCP\Http\Client\IClient') - ->disableOriginalConstructor()->getMock(); - $client->expects($this->any()) - ->method('get') - ->will($this->throwException(new \Exception())); - - $this->clientService->expects($this->exactly(4)) - ->method('newClient') - ->willReturn($client); - - $this->assertTrue( - self::invokePrivate( - $this->checkSetupController, - 'hasInternetConnectivityProblems' - ) - ); - } - - - public function testIsMemcacheConfiguredFalse() { - $this->config->expects($this->once()) - ->method('getSystemValue') - ->with('memcache.local', null) - ->willReturn(null); - - $this->assertFalse( - self::invokePrivate( - $this->checkSetupController, - 'isMemcacheConfigured' - ) - ); - } - - public function testIsMemcacheConfiguredTrue() { - $this->config->expects($this->once()) - ->method('getSystemValue') - ->with('memcache.local', null) - ->willReturn('SomeProvider'); - - $this->assertTrue( - self::invokePrivate( - $this->checkSetupController, - 'isMemcacheConfigured' - ) - ); - } - - public function testIsPhpSupportedFalse() { - $this->checkSetupController - ->expects($this->once()) - ->method('isPhpOutdated') - ->willReturn(true); - - $this->assertEquals( - ['eol' => true, 'version' => PHP_VERSION], - self::invokePrivate($this->checkSetupController, 'isPhpSupported') - ); - } - - public function testIsPhpSupportedTrue() { - $this->checkSetupController - ->expects($this->exactly(2)) - ->method('isPhpOutdated') - ->willReturn(false); - - $this->assertEquals( - ['eol' => false, 'version' => PHP_VERSION], - self::invokePrivate($this->checkSetupController, 'isPhpSupported') - ); - - - $this->assertEquals( - ['eol' => false, 'version' => PHP_VERSION], - self::invokePrivate($this->checkSetupController, 'isPhpSupported') - ); - } - - /** - * @dataProvider dataForwardedForHeadersWorking - * - * @param array $trustedProxies - * @param string $remoteAddrNotForwarded - * @param string $remoteAddr - * @param bool $result - */ - public function testForwardedForHeadersWorking(array $trustedProxies, string $remoteAddrNotForwarded, string $remoteAddr, bool $result): void { - $this->config->expects($this->once()) - ->method('getSystemValue') - ->with('trusted_proxies', []) - ->willReturn($trustedProxies); - $this->request->expects($this->atLeastOnce()) - ->method('getHeader') - ->willReturnMap([ - ['REMOTE_ADDR', $remoteAddrNotForwarded], - ['X-Forwarded-Host', ''] - ]); - $this->request->expects($this->any()) - ->method('getRemoteAddress') - ->willReturn($remoteAddr); - - $this->assertEquals( - $result, - self::invokePrivate($this->checkSetupController, 'forwardedForHeadersWorking') - ); - } - - public function dataForwardedForHeadersWorking(): array { - return [ - // description => trusted proxies, getHeader('REMOTE_ADDR'), getRemoteAddr, expected result - 'no trusted proxies' => [[], '2.2.2.2', '2.2.2.2', true], - 'trusted proxy, remote addr not trusted proxy' => [['1.1.1.1'], '2.2.2.2', '2.2.2.2', true], - 'trusted proxy, remote addr is trusted proxy, x-forwarded-for working' => [['1.1.1.1'], '1.1.1.1', '2.2.2.2', true], - 'trusted proxy, remote addr is trusted proxy, x-forwarded-for not set' => [['1.1.1.1'], '1.1.1.1', '1.1.1.1', false], - ]; - } - - public function testForwardedHostPresentButTrustedProxiesNotAnArray(): void { - $this->config->expects($this->once()) - ->method('getSystemValue') - ->with('trusted_proxies', []) - ->willReturn('1.1.1.1'); - $this->request->expects($this->atLeastOnce()) - ->method('getHeader') - ->willReturnMap([ - ['REMOTE_ADDR', '1.1.1.1'], - ['X-Forwarded-Host', 'nextcloud.test'] - ]); - $this->request->expects($this->any()) - ->method('getRemoteAddress') - ->willReturn('1.1.1.1'); - - $this->assertEquals( - false, - self::invokePrivate($this->checkSetupController, 'forwardedForHeadersWorking') + $this->checker = $this->createMock(Checker::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->setupCheckManager = $this->createMock(ISetupCheckManager::class); + $this->checkSetupController = new CheckSetupController( + 'settings', + $this->request, + $this->config, + $this->urlGenerator, + $this->l10n, + $this->checker, + $this->logger, + $this->setupCheckManager, ); } - public function testForwardedHostPresentButTrustedProxiesEmpty(): void { - $this->config->expects($this->once()) - ->method('getSystemValue') - ->with('trusted_proxies', []) - ->willReturn([]); - $this->request->expects($this->atLeastOnce()) - ->method('getHeader') + public function testCheck(): void { + $this->config->expects($this->any()) + ->method('getAppValue') ->willReturnMap([ - ['REMOTE_ADDR', '1.1.1.1'], - ['X-Forwarded-Host', 'nextcloud.test'] + ['files_external', 'user_certificate_scan', '', '["a", "b"]'], + ['dav', 'needs_system_address_book_sync', 'no', 'no'], ]); - $this->request->expects($this->any()) - ->method('getRemoteAddress') - ->willReturn('1.1.1.1'); - - $this->assertEquals( - false, - self::invokePrivate($this->checkSetupController, 'forwardedForHeadersWorking') - ); - } - - public function testCheck() { - $this->config->expects($this->at(0)) - ->method('getAppValue') - ->with('files_external', 'user_certificate_scan', false) - ->willReturn('["a", "b"]'); - $this->config->expects($this->at(1)) - ->method('getAppValue') - ->with('core', 'cronErrors') - ->willReturn(''); - $this->config->expects($this->at(3)) - ->method('getSystemValue') - ->with('connectivity_check_domains', ['www.nextcloud.com', 'www.startpage.com', 'www.eff.org', 'www.edri.org']) - ->willReturn(['www.nextcloud.com', 'www.startpage.com', 'www.eff.org', 'www.edri.org']); - $this->config->expects($this->at(4)) - ->method('getSystemValue') - ->with('memcache.local', null) - ->willReturn('SomeProvider'); - $this->config->expects($this->at(5)) - ->method('getSystemValue') - ->with('has_internet_connection', true) - ->willReturn(true); - $this->config->expects($this->at(6)) + $this->config->expects($this->any()) ->method('getSystemValue') - ->with('appstoreenabled', true) - ->willReturn(false); - - $this->request->expects($this->atLeastOnce()) - ->method('getHeader') ->willReturnMap([ - ['REMOTE_ADDR', '4.3.2.1'], - ['X-Forwarded-Host', ''] + ['connectivity_check_domains', ['www.nextcloud.com', 'www.startpage.com', 'www.eff.org', 'www.edri.org'], ['www.nextcloud.com', 'www.startpage.com', 'www.eff.org', 'www.edri.org']], + ['memcache.local', null, 'SomeProvider'], + ['has_internet_connection', true, true], + ['appstoreenabled', true, false], ]); - $client = $this->getMockBuilder('\OCP\Http\Client\IClient') - ->disableOriginalConstructor()->getMock(); - $client->expects($this->at(0)) - ->method('get') - ->with('http://www.nextcloud.com/', []) - ->will($this->throwException(new \Exception())); - $client->expects($this->at(1)) - ->method('get') - ->with('http://www.startpage.com/', []) - ->will($this->throwException(new \Exception())); - $client->expects($this->at(2)) - ->method('get') - ->with('http://www.eff.org/', []) - ->will($this->throwException(new \Exception())); - $client->expects($this->at(3)) - ->method('get') - ->with('http://www.edri.org/', []) - ->will($this->throwException(new \Exception())); - $this->clientService->expects($this->exactly(4)) - ->method('newClient') - ->willReturn($client); - $this->checkSetupController - ->expects($this->once()) - ->method('isPhpOutdated') - ->willReturn(true); - $this->checkSetupController - ->expects($this->once()) - ->method('getOpcacheSetupRecommendations') - ->willReturn(['recommendation1', 'recommendation2']); - $this->checkSetupController - ->method('hasFreeTypeSupport') - ->willReturn(false); - $this->checkSetupController - ->method('hasMissingIndexes') - ->willReturn([]); - $this->checkSetupController - ->method('hasMissingPrimaryKeys') - ->willReturn([]); - $this->checkSetupController - ->method('isSqliteUsed') - ->willReturn(false); - $this->checkSetupController - ->expects($this->once()) - ->method('isReadOnlyConfig') - ->willReturn(false); - $this->checkSetupController - ->expects($this->once()) - ->method('wasEmailTestSuccessful') - ->willReturn(false); - $this->checkSetupController - ->expects($this->once()) - ->method('hasValidTransactionIsolationLevel') - ->willReturn(true); - $this->checkSetupController - ->expects($this->once()) - ->method('hasFileinfoInstalled') - ->willReturn(true); - $this->checkSetupController - ->expects($this->once()) - ->method('hasWorkingFileLocking') - ->willReturn(true); - $this->checkSetupController - ->expects($this->once()) - ->method('getSuggestedOverwriteCliURL') - ->willReturn(''); - $this->checkSetupController - ->expects($this->once()) - ->method('getLastCronInfo') - ->willReturn([ - 'diffInSeconds' => 123, - 'relativeTime' => '2 hours ago', - 'backgroundJobsUrl' => 'https://example.org', - ]); - $this->checker - ->expects($this->once()) - ->method('hasPassedCheck') - ->willReturn(true); - $this->memoryInfo - ->method('isMemoryLimitSufficient') - ->willReturn(true); - - $this->checkSetupController - ->expects($this->once()) - ->method('getAppDirsWithDifferentOwner') - ->willReturn([]); - - $this->checkSetupController - ->expects($this->once()) - ->method('isImagickEnabled') - ->willReturn(false); - - $this->checkSetupController - ->expects($this->once()) - ->method('areWebauthnExtensionsEnabled') - ->willReturn(false); - - $this->checkSetupController - ->expects($this->once()) - ->method('hasRecommendedPHPModules') - ->willReturn([]); - - $this->checkSetupController - ->expects($this->once()) - ->method('hasBigIntConversionPendingColumns') - ->willReturn([]); - - $this->checkSetupController - ->expects($this->once()) - ->method('isMysqlUsedWithoutUTF8MB4') - ->willReturn(false); - - $this->checkSetupController - ->expects($this->once()) - ->method('isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed') - ->willReturn(true); + $this->request->expects($this->never()) + ->method('getHeader'); $this->urlGenerator->method('linkToDocs') ->willReturnCallback(function (string $key): string { @@ -610,365 +114,16 @@ class CheckSetupControllerTest extends TestCase { } return ''; }); - $sqlitePlatform = $this->getMockBuilder(SqlitePlatform::class)->getMock(); - $this->connection->method('getDatabasePlatform') - ->willReturn($sqlitePlatform); $expected = new DataResponse( [ - 'isGetenvServerWorking' => true, - 'isReadOnlyConfig' => false, - 'wasEmailTestSuccessful' => false, - 'hasValidTransactionIsolationLevel' => true, - 'hasFileinfoInstalled' => true, - 'hasWorkingFileLocking' => true, - 'suggestedOverwriteCliURL' => '', - 'cronInfo' => [ - 'diffInSeconds' => 123, - 'relativeTime' => '2 hours ago', - 'backgroundJobsUrl' => 'https://example.org', - ], - 'cronErrors' => [], - 'serverHasInternetConnectionProblems' => true, - 'isMemcacheConfigured' => true, - 'memcacheDocs' => 'http://docs.example.org/server/go.php?to=admin-performance', - 'isRandomnessSecure' => self::invokePrivate($this->checkSetupController, 'isRandomnessSecure'), - 'securityDocs' => 'https://docs.example.org/server/8.1/admin_manual/configuration_server/hardening.html', - 'isUsedTlsLibOutdated' => '', - 'phpSupported' => [ - 'eol' => true, - 'version' => PHP_VERSION - ], - 'forwardedForHeadersWorking' => false, - 'reverseProxyDocs' => 'reverse-proxy-doc-link', - 'isCorrectMemcachedPHPModuleInstalled' => true, - 'hasPassedCodeIntegrityCheck' => true, - 'codeIntegrityCheckerDocumentation' => 'http://docs.example.org/server/go.php?to=admin-code-integrity', - 'OpcacheSetupRecommendations' => ['recommendation1', 'recommendation2'], - 'isSettimelimitAvailable' => true, - 'hasFreeTypeSupport' => false, - 'isSqliteUsed' => false, - 'databaseConversionDocumentation' => 'http://docs.example.org/server/go.php?to=admin-db-conversion', - 'missingIndexes' => [], - 'missingPrimaryKeys' => [], - 'missingColumns' => [], - 'isMemoryLimitSufficient' => true, - 'appDirsWithDifferentOwner' => [], - 'isImagickEnabled' => false, - 'areWebauthnExtensionsEnabled' => false, - 'recommendedPHPModules' => [], - 'pendingBigIntConversionColumns' => [], - 'isMysqlUsedWithoutUTF8MB4' => false, - 'isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed' => true, - 'reverseProxyGeneratedURL' => 'https://server/index.php', - 'OCA\Settings\SetupChecks\PhpDefaultCharset' => ['pass' => true, 'description' => 'PHP configuration option default_charset should be UTF-8', 'severity' => 'warning'], - 'OCA\Settings\SetupChecks\PhpOutputBuffering' => ['pass' => true, 'description' => 'PHP configuration option output_buffering must be disabled', 'severity' => 'error'], - 'OCA\Settings\SetupChecks\LegacySSEKeyFormat' => ['pass' => true, 'description' => 'The old server-side-encryption format is enabled. We recommend disabling this.', 'severity' => 'warning', 'linkToDocumentation' => ''], - 'OCA\Settings\SetupChecks\CheckUserCertificates' => ['pass' => false, 'description' => 'There are some user imported SSL certificates present, that are not used anymore with Nextcloud 21. They can be imported on the command line via "occ security:certificates:import" command. Their paths inside the data directory are shown below.', 'severity' => 'warning', 'elements' => ['a', 'b']], - 'imageMagickLacksSVGSupport' => false, - 'isDefaultPhoneRegionSet' => false, - 'OCA\Settings\SetupChecks\SupportedDatabase' => ['pass' => true, 'description' => '', 'severity' => 'info'], - 'isFairUseOfFreePushService' => false, - 'temporaryDirectoryWritable' => false, - \OCA\Settings\SetupChecks\LdapInvalidUuids::class => ['pass' => true, 'description' => 'Invalid UUIDs of LDAP users or groups have been found. Please review your "Override UUID detection" settings in the Expert part of the LDAP configuration and use "occ ldap:update-uuid" to update them.', 'severity' => 'warning'], + 'generic' => [], ] ); $this->assertEquals($expected, $this->checkSetupController->check()); } - public function testGetCurlVersion() { - $checkSetupController = $this->getMockBuilder(CheckSetupController::class) - ->setConstructorArgs([ - 'settings', - $this->request, - $this->config, - $this->clientService, - $this->urlGenerator, - $this->l10n, - $this->checker, - $this->logger, - $this->dispatcher, - $this->db, - $this->lockingProvider, - $this->dateTimeFormatter, - $this->memoryInfo, - $this->secureRandom, - $this->iniGetWrapper, - $this->connection, - $this->tempManager, - $this->notificationManager, - $this->appManager, - $this->serverContainer - ]) - ->setMethods(null)->getMock(); - - $this->assertArrayHasKey('ssl_version', $this->invokePrivate($checkSetupController, 'getCurlVersion')); - } - - public function testIsUsedTlsLibOutdatedWithAnotherLibrary() { - $this->config->expects($this->any()) - ->method('getSystemValue') - ->willReturn(true); - $this->checkSetupController - ->expects($this->once()) - ->method('getCurlVersion') - ->willReturn(['ssl_version' => 'SSLlib']); - $this->assertSame('', $this->invokePrivate($this->checkSetupController, 'isUsedTlsLibOutdated')); - } - - public function testIsUsedTlsLibOutdatedWithMisbehavingCurl() { - $this->config->expects($this->any()) - ->method('getSystemValue') - ->willReturn(true); - $this->checkSetupController - ->expects($this->once()) - ->method('getCurlVersion') - ->willReturn([]); - $this->assertSame('', $this->invokePrivate($this->checkSetupController, 'isUsedTlsLibOutdated')); - } - - public function testIsUsedTlsLibOutdatedWithOlderOpenSsl() { - $this->config->expects($this->any()) - ->method('getSystemValue') - ->willReturn(true); - $this->checkSetupController - ->expects($this->once()) - ->method('getCurlVersion') - ->willReturn(['ssl_version' => 'OpenSSL/1.0.1c']); - $this->assertSame('cURL is using an outdated OpenSSL version (OpenSSL/1.0.1c). Please update your operating system or features such as installing and updating apps via the App Store or Federated Cloud Sharing will not work reliably.', $this->invokePrivate($this->checkSetupController, 'isUsedTlsLibOutdated')); - } - - public function testIsUsedTlsLibOutdatedWithOlderOpenSslAndWithoutAppstore() { - $this->config - ->expects($this->at(0)) - ->method('getSystemValue') - ->with('has_internet_connection', true) - ->willReturn(true); - $this->checkSetupController - ->expects($this->once()) - ->method('getCurlVersion') - ->willReturn(['ssl_version' => 'OpenSSL/1.0.1c']); - $this->assertSame('cURL is using an outdated OpenSSL version (OpenSSL/1.0.1c). Please update your operating system or features such as Federated Cloud Sharing will not work reliably.', $this->invokePrivate($this->checkSetupController, 'isUsedTlsLibOutdated')); - } - - public function testIsUsedTlsLibOutdatedWithOlderOpenSsl1() { - $this->config->expects($this->any()) - ->method('getSystemValue') - ->willReturn(true); - $this->checkSetupController - ->expects($this->once()) - ->method('getCurlVersion') - ->willReturn(['ssl_version' => 'OpenSSL/1.0.2a']); - $this->assertSame('cURL is using an outdated OpenSSL version (OpenSSL/1.0.2a). Please update your operating system or features such as installing and updating apps via the App Store or Federated Cloud Sharing will not work reliably.', $this->invokePrivate($this->checkSetupController, 'isUsedTlsLibOutdated')); - } - - public function testIsUsedTlsLibOutdatedWithMatchingOpenSslVersion() { - $this->config->expects($this->any()) - ->method('getSystemValue') - ->willReturn(true); - $this->checkSetupController - ->expects($this->once()) - ->method('getCurlVersion') - ->willReturn(['ssl_version' => 'OpenSSL/1.0.1d']); - $this->assertSame('', $this->invokePrivate($this->checkSetupController, 'isUsedTlsLibOutdated')); - } - - public function testIsUsedTlsLibOutdatedWithMatchingOpenSslVersion1() { - $this->config->expects($this->any()) - ->method('getSystemValue') - ->willReturn(true); - $this->checkSetupController - ->expects($this->once()) - ->method('getCurlVersion') - ->willReturn(['ssl_version' => 'OpenSSL/1.0.2b']); - $this->assertSame('', $this->invokePrivate($this->checkSetupController, 'isUsedTlsLibOutdated')); - } - - /** - * Setups a temp directory and some subdirectories. - * Then calls the 'getAppDirsWithDifferentOwner' method. - * The result is expected to be empty since - * there are no directories with different owners than the current user. - * - * @return void - */ - public function testAppDirectoryOwnersOk() { - $tempDir = tempnam(sys_get_temp_dir(), 'apps') . 'dir'; - mkdir($tempDir); - mkdir($tempDir . DIRECTORY_SEPARATOR . 'app1'); - mkdir($tempDir . DIRECTORY_SEPARATOR . 'app2'); - $this->dirsToRemove[] = $tempDir . DIRECTORY_SEPARATOR . 'app1'; - $this->dirsToRemove[] = $tempDir . DIRECTORY_SEPARATOR . 'app2'; - $this->dirsToRemove[] = $tempDir; - OC::$APPSROOTS = [ - [ - 'path' => $tempDir, - 'url' => '/apps', - 'writable' => true, - ], - ]; - $this->assertSame( - [], - $this->invokePrivate($this->checkSetupController, 'getAppDirsWithDifferentOwner') - ); - } - - /** - * Calls the check for a none existing app root that is marked as not writable. - * It's expected that no error happens since the check shouldn't apply. - * - * @return void - */ - public function testAppDirectoryOwnersNotWritable() { - $tempDir = tempnam(sys_get_temp_dir(), 'apps') . 'dir'; - OC::$APPSROOTS = [ - [ - 'path' => $tempDir, - 'url' => '/apps', - 'writable' => false, - ], - ]; - $this->assertSame( - [], - $this->invokePrivate($this->checkSetupController, 'getAppDirsWithDifferentOwner') - ); - } - - public function testIsBuggyNss400() { - $this->config->expects($this->any()) - ->method('getSystemValue') - ->willReturn(true); - $this->checkSetupController - ->expects($this->once()) - ->method('getCurlVersion') - ->willReturn(['ssl_version' => 'NSS/1.0.2b']); - $client = $this->getMockBuilder('\OCP\Http\Client\IClient') - ->disableOriginalConstructor()->getMock(); - $exception = $this->getMockBuilder('\GuzzleHttp\Exception\ClientException') - ->disableOriginalConstructor()->getMock(); - $response = $this->getMockBuilder(ResponseInterface::class) - ->disableOriginalConstructor()->getMock(); - $response->expects($this->once()) - ->method('getStatusCode') - ->willReturn(400); - $exception->expects($this->once()) - ->method('getResponse') - ->willReturn($response); - - $client->expects($this->at(0)) - ->method('get') - ->with('https://nextcloud.com/', []) - ->will($this->throwException($exception)); - - $this->clientService->expects($this->once()) - ->method('newClient') - ->willReturn($client); - - $this->assertSame('cURL is using an outdated NSS version (NSS/1.0.2b). Please update your operating system or features such as installing and updating apps via the App Store or Federated Cloud Sharing will not work reliably.', $this->invokePrivate($this->checkSetupController, 'isUsedTlsLibOutdated')); - } - - - public function testIsBuggyNss200() { - $this->config->expects($this->any()) - ->method('getSystemValue') - ->willReturn(true); - $this->checkSetupController - ->expects($this->once()) - ->method('getCurlVersion') - ->willReturn(['ssl_version' => 'NSS/1.0.2b']); - $client = $this->getMockBuilder('\OCP\Http\Client\IClient') - ->disableOriginalConstructor()->getMock(); - $exception = $this->getMockBuilder('\GuzzleHttp\Exception\ClientException') - ->disableOriginalConstructor()->getMock(); - $response = $this->getMockBuilder(ResponseInterface::class) - ->disableOriginalConstructor()->getMock(); - $response->expects($this->once()) - ->method('getStatusCode') - ->willReturn(200); - $exception->expects($this->once()) - ->method('getResponse') - ->willReturn($response); - - $client->expects($this->at(0)) - ->method('get') - ->with('https://nextcloud.com/', []) - ->will($this->throwException($exception)); - - $this->clientService->expects($this->once()) - ->method('newClient') - ->willReturn($client); - - $this->assertSame('', $this->invokePrivate($this->checkSetupController, 'isUsedTlsLibOutdated')); - } - - public function testIsUsedTlsLibOutdatedWithInternetDisabled() { - $this->config - ->expects($this->at(0)) - ->method('getSystemValue') - ->with('has_internet_connection', true) - ->willReturn(false); - $this->assertSame('', $this->invokePrivate($this->checkSetupController, 'isUsedTlsLibOutdated')); - } - - public function testIsUsedTlsLibOutdatedWithAppstoreDisabledAndServerToServerSharingEnabled() { - $this->config - ->expects($this->at(0)) - ->method('getSystemValue') - ->with('has_internet_connection', true) - ->willReturn(true); - $this->config - ->expects($this->at(1)) - ->method('getSystemValue') - ->with('appstoreenabled', true) - ->willReturn(false); - $this->config - ->expects($this->at(2)) - ->method('getAppValue') - ->with('files_sharing', 'outgoing_server2server_share_enabled', 'yes') - ->willReturn('no'); - $this->config - ->expects($this->at(3)) - ->method('getAppValue') - ->with('files_sharing', 'incoming_server2server_share_enabled', 'yes') - ->willReturn('yes'); - - $this->checkSetupController - ->expects($this->once()) - ->method('getCurlVersion') - ->willReturn([]); - $this->assertSame('', $this->invokePrivate($this->checkSetupController, 'isUsedTlsLibOutdated')); - } - - public function testIsUsedTlsLibOutdatedWithAppstoreDisabledAndServerToServerSharingDisabled() { - $this->config - ->expects($this->at(0)) - ->method('getSystemValue') - ->with('has_internet_connection', true) - ->willReturn(true); - $this->config - ->expects($this->at(1)) - ->method('getSystemValue') - ->with('appstoreenabled', true) - ->willReturn(false); - $this->config - ->expects($this->at(2)) - ->method('getAppValue') - ->with('files_sharing', 'outgoing_server2server_share_enabled', 'yes') - ->willReturn('no'); - $this->config - ->expects($this->at(3)) - ->method('getAppValue') - ->with('files_sharing', 'incoming_server2server_share_enabled', 'yes') - ->willReturn('no'); - - $this->checkSetupController - ->expects($this->never()) - ->method('getCurlVersion') - ->willReturn([]); - $this->assertSame('', $this->invokePrivate($this->checkSetupController, 'isUsedTlsLibOutdated')); - } - - public function testRescanFailedIntegrityCheck() { + public function testRescanFailedIntegrityCheck(): void { $this->checker ->expects($this->once()) ->method('runInstanceVerification'); @@ -982,7 +137,7 @@ class CheckSetupControllerTest extends TestCase { $this->assertEquals($expected, $this->checkSetupController->rescanFailedIntegrityCheck()); } - public function testGetFailedIntegrityCheckDisabled() { + public function testGetFailedIntegrityCheckDisabled(): void { $this->checker ->expects($this->once()) ->method('isCodeCheckEnforced') @@ -993,7 +148,7 @@ class CheckSetupControllerTest extends TestCase { } - public function testGetFailedIntegrityCheckFilesWithNoErrorsFound() { + public function testGetFailedIntegrityCheckFilesWithNoErrorsFound(): void { $this->checker ->expects($this->once()) ->method('isCodeCheckEnforced') @@ -1004,27 +159,27 @@ class CheckSetupControllerTest extends TestCase { ->willReturn([]); $expected = new DataDisplayResponse( - 'No errors have been found.', - Http::STATUS_OK, - [ - 'Content-Type' => 'text/plain', - ] + 'No errors have been found.', + Http::STATUS_OK, + [ + 'Content-Type' => 'text/plain', + ] ); $this->assertEquals($expected, $this->checkSetupController->getFailedIntegrityCheckFiles()); } - public function testGetFailedIntegrityCheckFilesWithSomeErrorsFound() { + public function testGetFailedIntegrityCheckFilesWithSomeErrorsFound(): void { $this->checker ->expects($this->once()) ->method('isCodeCheckEnforced') ->willReturn(true); $this->checker - ->expects($this->once()) - ->method('getResults') - ->willReturn([ 'core' => [ 'EXTRA_FILE' => ['/testfile' => []], 'INVALID_HASH' => [ '/.idea/workspace.xml' => [ 'expected' => 'f1c5e2630d784bc9cb02d5a28f55d6f24d06dae2a0fee685f3c2521b050955d9d452769f61454c9ddfa9c308146ade10546cfa829794448eaffbc9a04a29d216', 'current' => 'ce08bf30bcbb879a18b49239a9bec6b8702f52452f88a9d32142cad8d2494d5735e6bfa0d8642b2762c62ca5be49f9bf4ec231d4a230559d4f3e2c471d3ea094', ], '/lib/private/integritycheck/checker.php' => [ 'expected' => 'c5a03bacae8dedf8b239997901ba1fffd2fe51271d13a00cc4b34b09cca5176397a89fc27381cbb1f72855fa18b69b6f87d7d5685c3b45aee373b09be54742ea', 'current' => '88a3a92c11db91dec1ac3be0e1c87f862c95ba6ffaaaa3f2c3b8f682187c66f07af3a3b557a868342ef4a271218fe1c1e300c478e6c156c5955ed53c40d06585', ], '/settings/controller/checksetupcontroller.php' => [ 'expected' => '3e1de26ce93c7bfe0ede7c19cb6c93cadc010340225b375607a7178812e9de163179b0dc33809f451e01f491d93f6f5aaca7929685d21594cccf8bda732327c4', 'current' => '09563164f9904a837f9ca0b5f626db56c838e5098e0ccc1d8b935f68fa03a25c5ec6f6b2d9e44a868e8b85764dafd1605522b4af8db0ae269d73432e9a01e63a', ], ], ], 'bookmarks' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'dav' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'encryption' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'external' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'federation' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'files' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'files_antivirus' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'files_drop' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'files_external' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'files_pdfviewer' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'files_sharing' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'files_trashbin' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'files_versions' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'files_videoviewer' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'firstrunwizard' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'gitsmart' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'logreader' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature could not get verified.', ], ], 'password_policy' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'provisioning_api' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'sketch' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'threatblock' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'two_factor_auth' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'user_ldap' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'user_shibboleth' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], ]); + ->expects($this->once()) + ->method('getResults') + ->willReturn([ 'core' => [ 'EXTRA_FILE' => ['/testfile' => []], 'INVALID_HASH' => [ '/.idea/workspace.xml' => [ 'expected' => 'f1c5e2630d784bc9cb02d5a28f55d6f24d06dae2a0fee685f3c2521b050955d9d452769f61454c9ddfa9c308146ade10546cfa829794448eaffbc9a04a29d216', 'current' => 'ce08bf30bcbb879a18b49239a9bec6b8702f52452f88a9d32142cad8d2494d5735e6bfa0d8642b2762c62ca5be49f9bf4ec231d4a230559d4f3e2c471d3ea094', ], '/lib/private/integritycheck/checker.php' => [ 'expected' => 'c5a03bacae8dedf8b239997901ba1fffd2fe51271d13a00cc4b34b09cca5176397a89fc27381cbb1f72855fa18b69b6f87d7d5685c3b45aee373b09be54742ea', 'current' => '88a3a92c11db91dec1ac3be0e1c87f862c95ba6ffaaaa3f2c3b8f682187c66f07af3a3b557a868342ef4a271218fe1c1e300c478e6c156c5955ed53c40d06585', ], '/settings/controller/checksetupcontroller.php' => [ 'expected' => '3e1de26ce93c7bfe0ede7c19cb6c93cadc010340225b375607a7178812e9de163179b0dc33809f451e01f491d93f6f5aaca7929685d21594cccf8bda732327c4', 'current' => '09563164f9904a837f9ca0b5f626db56c838e5098e0ccc1d8b935f68fa03a25c5ec6f6b2d9e44a868e8b85764dafd1605522b4af8db0ae269d73432e9a01e63a', ], ], ], 'bookmarks' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'dav' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'encryption' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'external' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'federation' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'files' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'files_antivirus' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'files_drop' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'files_external' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'files_pdfviewer' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'files_sharing' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'files_trashbin' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'files_versions' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'files_videoviewer' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'firstrunwizard' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'gitsmart' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'logreader' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature could not get verified.', ], ], 'password_policy' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'provisioning_api' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'sketch' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'threatblock' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'two_factor_auth' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'user_ldap' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], 'user_shibboleth' => [ 'EXCEPTION' => [ 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException', 'message' => 'Signature data not found.', ], ], ]); $expected = new DataDisplayResponse( - 'Technical information + 'Technical information ===================== The following list covers which files have failed the integrity check. Please read the previous linked documentation to learn more about the errors and how to fix @@ -1416,119 +571,11 @@ Array ) ', - Http::STATUS_OK, - [ - 'Content-Type' => 'text/plain', - ] + Http::STATUS_OK, + [ + 'Content-Type' => 'text/plain', + ] ); $this->assertEquals($expected, $this->checkSetupController->getFailedIntegrityCheckFiles()); } - - public function dataForIsMysqlUsedWithoutUTF8MB4() { - return [ - ['sqlite', false, false], - ['sqlite', true, false], - ['postgres', false, false], - ['postgres', true, false], - ['oci', false, false], - ['oci', true, false], - ['mysql', false, true], - ['mysql', true, false], - ]; - } - - /** - * @dataProvider dataForIsMysqlUsedWithoutUTF8MB4 - */ - public function testIsMysqlUsedWithoutUTF8MB4(string $db, bool $useUTF8MB4, bool $expected) { - $this->config->method('getSystemValue') - ->willReturnCallback(function ($key, $default) use ($db, $useUTF8MB4) { - if ($key === 'dbtype') { - return $db; - } - if ($key === 'mysql.utf8mb4') { - return $useUTF8MB4; - } - return $default; - }); - - $checkSetupController = new CheckSetupController( - 'settings', - $this->request, - $this->config, - $this->clientService, - $this->urlGenerator, - $this->l10n, - $this->checker, - $this->logger, - $this->dispatcher, - $this->db, - $this->lockingProvider, - $this->dateTimeFormatter, - $this->memoryInfo, - $this->secureRandom, - $this->iniGetWrapper, - $this->connection, - $this->tempManager, - $this->notificationManager, - $this->appManager, - $this->serverContainer - ); - - $this->assertSame($expected, $this->invokePrivate($checkSetupController, 'isMysqlUsedWithoutUTF8MB4')); - } - - public function dataForIsEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed() { - return [ - ['singlebucket', 'OC\\Files\\ObjectStore\\Swift', true], - ['multibucket', 'OC\\Files\\ObjectStore\\Swift', true], - ['singlebucket', 'OC\\Files\\ObjectStore\\Custom', true], - ['multibucket', 'OC\Files\\ObjectStore\\Custom', true], - ['singlebucket', 'OC\Files\ObjectStore\Swift', true], - ['multibucket', 'OC\Files\ObjectStore\Swift', true], - ['singlebucket', 'OC\Files\ObjectStore\Custom', true], - ['multibucket', 'OC\Files\ObjectStore\Custom', true], - ]; - } - - /** - * @dataProvider dataForIsEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed - */ - public function testIsEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed(string $mode, string $className, bool $expected) { - $this->config->method('getSystemValue') - ->willReturnCallback(function ($key, $default) use ($mode, $className) { - if ($key === 'objectstore' && $mode === 'singlebucket') { - return ['class' => $className]; - } - if ($key === 'objectstore_multibucket' && $mode === 'multibucket') { - return ['class' => $className]; - } - return $default; - }); - - $checkSetupController = new CheckSetupController( - 'settings', - $this->request, - $this->config, - $this->clientService, - $this->urlGenerator, - $this->l10n, - $this->checker, - $this->logger, - $this->dispatcher, - $this->db, - $this->lockingProvider, - $this->dateTimeFormatter, - $this->memoryInfo, - $this->secureRandom, - $this->iniGetWrapper, - $this->connection, - $this->tempManager, - $this->notificationManager, - $this->appManager, - $this->serverContainer - ); - - $this->assertSame($expected, $this->invokePrivate($checkSetupController, 'isEnoughTempSpaceAvailableIfS3PrimaryStorageIsUsed')); - } } diff --git a/apps/settings/tests/Controller/DelegationControllerTest.php b/apps/settings/tests/Controller/DelegationControllerTest.php index 64157427fa4..c4cbe67466b 100644 --- a/apps/settings/tests/Controller/DelegationControllerTest.php +++ b/apps/settings/tests/Controller/DelegationControllerTest.php @@ -1,56 +1,33 @@ <?php + /** - * @copyright Copyright (c) 2021 Nextcloud GmbH - * - * @author Carl Schwan <carl@carlschwan.eu> - * - * @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 <https://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - - namespace OCA\Settings\Tests\Controller\Admin; use OC\Settings\AuthorizedGroup; use OCA\Settings\Controller\AuthorizedGroupController; use OCA\Settings\Service\AuthorizedGroupService; use OCP\IRequest; +use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; class DelegationControllerTest extends TestCase { - - /** @var AuthorizedGroupService */ - private $service; - - /** @var IRequest */ - private $request; - - /** @var AuthorizedGroupController */ - private $controller; + private AuthorizedGroupService&MockObject $service; + private IRequest&MockObject $request; + private AuthorizedGroupController $controller; protected function setUp(): void { parent::setUp(); - $this->request = $this->getMockBuilder(IRequest::class)->getMock(); - $this->service = $this->getMockBuilder(AuthorizedGroupService::class)->disableOriginalConstructor()->getMock(); + $this->request = $this->createMock(IRequest::class); + $this->service = $this->createMock(AuthorizedGroupService::class); $this->controller = new AuthorizedGroupController( 'settings', $this->request, $this->service ); } - public function testSaveSettings() { + public function testSaveSettings(): void { $setting = 'MySecretSetting'; $oldGroups = []; $oldGroups[] = AuthorizedGroup::fromParams(['groupId' => 'hello', 'class' => $setting]); @@ -59,7 +36,7 @@ class DelegationControllerTest extends TestCase { $this->service->expects($this->once()) ->method('findExistingGroupsForClass') ->with('MySecretSetting') - ->will($this->returnValue($oldGroups)); + ->willReturn($oldGroups); $this->service->expects($this->once()) ->method('delete') @@ -68,7 +45,7 @@ class DelegationControllerTest extends TestCase { $this->service->expects($this->once()) ->method('create') ->with('world', 'MySecretSetting') - ->will($this->returnValue(AuthorizedGroup::fromParams(['groupId' => 'world', 'class' => $setting]))); + ->willReturn(AuthorizedGroup::fromParams(['groupId' => 'world', 'class' => $setting])); $result = $this->controller->saveSettings([['gid' => 'hello'], ['gid' => 'world']], 'MySecretSetting'); diff --git a/apps/settings/tests/Controller/MailSettingsControllerTest.php b/apps/settings/tests/Controller/MailSettingsControllerTest.php index 54461201201..1bc05ca4718 100644 --- a/apps/settings/tests/Controller/MailSettingsControllerTest.php +++ b/apps/settings/tests/Controller/MailSettingsControllerTest.php @@ -1,30 +1,9 @@ <?php + +declare(strict_types=1); /** - * @copyright 2014 Lukas Reschke lukas@nextcloud.com - * @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\Settings\Tests\Controller; @@ -35,29 +14,22 @@ use OCP\AppFramework\Http; use OCP\IConfig; use OCP\IL10N; use OCP\IRequest; +use OCP\IURLGenerator; use OCP\IUserSession; use OCP\Mail\IEMailTemplate; use OCP\Mail\IMailer; -use OCP\IURLGenerator; +use PHPUnit\Framework\MockObject\MockObject; /** * @package Tests\Settings\Controller */ class MailSettingsControllerTest extends \Test\TestCase { - - /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */ - private $config; - /** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */ - private $userSession; - /** @var IMailer|\PHPUnit\Framework\MockObject\MockObject */ - private $mailer; - /** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */ - private $l; - /** @var IURLGenerator */ - private $urlGenerator; - - /** @var MailSettingsController */ - private $mailController; + private IConfig&MockObject $config; + private IUserSession&MockObject $userSession; + private IMailer&MockObject $mailer; + private IL10N&MockObject $l; + private IURLGenerator&MockObject $urlGenerator; + private MailSettingsController $mailController; protected function setUp(): void { parent::setUp(); @@ -67,7 +39,7 @@ class MailSettingsControllerTest extends \Test\TestCase { $this->userSession = $this->createMock(IUserSession::class); $this->mailer = $this->createMock(IMailer::class); $this->urlGenerator = $this->createMock(IURLGenerator::class); - /** @var IRequest|\PHPUnit\Framework\MockObject\MockObject $request */ + /** @var IRequest&MockObject $request */ $request = $this->createMock(IRequest::class); $this->mailController = new MailSettingsController( 'settings', @@ -77,39 +49,40 @@ class MailSettingsControllerTest extends \Test\TestCase { $this->userSession, $this->urlGenerator, $this->mailer, - 'no-reply@nextcloud.com' ); } - public function testSetMailSettings() { + public function testSetMailSettings(): void { + $calls = [ + [[ + 'mail_domain' => 'nextcloud.com', + 'mail_from_address' => 'demo@nextcloud.com', + 'mail_smtpmode' => 'smtp', + 'mail_smtpsecure' => 'ssl', + 'mail_smtphost' => 'mx.nextcloud.org', + 'mail_smtpauth' => 1, + 'mail_smtpport' => '25', + 'mail_sendmailmode' => 'smtp', + ]], + [[ + 'mail_domain' => 'nextcloud.com', + 'mail_from_address' => 'demo@nextcloud.com', + 'mail_smtpmode' => 'smtp', + 'mail_smtpsecure' => 'ssl', + 'mail_smtphost' => 'mx.nextcloud.org', + 'mail_smtpauth' => null, + 'mail_smtpport' => '25', + 'mail_smtpname' => null, + 'mail_smtppassword' => null, + 'mail_sendmailmode' => 'smtp', + ]], + ]; $this->config->expects($this->exactly(2)) ->method('setSystemValues') - ->withConsecutive( - [[ - 'mail_domain' => 'nextcloud.com', - 'mail_from_address' => 'demo@nextcloud.com', - 'mail_smtpmode' => 'smtp', - 'mail_smtpsecure' => 'ssl', - 'mail_smtphost' => 'mx.nextcloud.org', - 'mail_smtpauthtype' => 'NTLM', - 'mail_smtpauth' => 1, - 'mail_smtpport' => '25', - 'mail_sendmailmode' => null, - ]], - [[ - 'mail_domain' => 'nextcloud.com', - 'mail_from_address' => 'demo@nextcloud.com', - 'mail_smtpmode' => 'smtp', - 'mail_smtpsecure' => 'ssl', - 'mail_smtphost' => 'mx.nextcloud.org', - 'mail_smtpauthtype' => 'NTLM', - 'mail_smtpauth' => null, - 'mail_smtpport' => '25', - 'mail_smtpname' => null, - 'mail_smtppassword' => null, - 'mail_sendmailmode' => null, - ]] - ); + ->willReturnCallback(function () use (&$calls): void { + $expected = array_shift($calls); + $this->assertEquals($expected, func_get_args()); + }); // With authentication $response = $this->mailController->setMailSettings( @@ -118,10 +91,9 @@ class MailSettingsControllerTest extends \Test\TestCase { 'smtp', 'ssl', 'mx.nextcloud.org', - 'NTLM', - 1, + '1', '25', - null + 'smtp' ); $this->assertSame(Http::STATUS_OK, $response->getStatus()); @@ -132,15 +104,14 @@ class MailSettingsControllerTest extends \Test\TestCase { 'smtp', 'ssl', 'mx.nextcloud.org', - 'NTLM', - 0, + '0', '25', - null + 'smtp' ); $this->assertSame(Http::STATUS_OK, $response->getStatus()); } - public function testStoreCredentials() { + public function testStoreCredentials(): void { $this->config ->expects($this->once()) ->method('setSystemValues') @@ -153,7 +124,7 @@ class MailSettingsControllerTest extends \Test\TestCase { $this->assertSame(Http::STATUS_OK, $response->getStatus()); } - public function testSendTestMail() { + public function testSendTestMail(): void { $user = $this->createMock(User::class); $user->expects($this->any()) ->method('getUID') @@ -175,7 +146,7 @@ class MailSettingsControllerTest extends \Test\TestCase { // Ensure that it fails when no mail address has been specified $response = $this->mailController->sendTestMail(); $this->assertSame(Http::STATUS_BAD_REQUEST, $response->getStatus()); - $this->assertSame('You need to set your user email before being able to send test emails. Go to for that.', $response->getData()); + $this->assertSame('You need to set your account email before being able to send test emails. Go to for that.', $response->getData()); // If no exception is thrown it should work $this->config diff --git a/apps/settings/tests/Controller/TwoFactorSettingsControllerTest.php b/apps/settings/tests/Controller/TwoFactorSettingsControllerTest.php index a19db6a3380..9f8d53d4f9f 100644 --- a/apps/settings/tests/Controller/TwoFactorSettingsControllerTest.php +++ b/apps/settings/tests/Controller/TwoFactorSettingsControllerTest.php @@ -1,25 +1,9 @@ <?php + +declare(strict_types=1); /** - * @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\Settings\Tests\Controller; @@ -32,15 +16,9 @@ use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; class TwoFactorSettingsControllerTest extends TestCase { - - /** @var IRequest|MockObject */ - private $request; - - /** @var MandatoryTwoFactor|MockObject */ - private $mandatoryTwoFactor; - - /** @var TwoFactorSettingsController */ - private $controller; + private IRequest&MockObject $request; + private MandatoryTwoFactor&MockObject $mandatoryTwoFactor; + private TwoFactorSettingsController $controller; protected function setUp(): void { parent::setUp(); @@ -55,7 +33,7 @@ class TwoFactorSettingsControllerTest extends TestCase { ); } - public function testIndex() { + public function testIndex(): void { $state = new EnforcementState(true); $this->mandatoryTwoFactor->expects($this->once()) ->method('getState') @@ -67,7 +45,7 @@ class TwoFactorSettingsControllerTest extends TestCase { $this->assertEquals($expected, $resp); } - public function testUpdate() { + public function testUpdate(): void { $state = new EnforcementState(true); $this->mandatoryTwoFactor->expects($this->once()) ->method('setState') diff --git a/apps/settings/tests/Controller/UsersControllerTest.php b/apps/settings/tests/Controller/UsersControllerTest.php index 797fa1621fa..1012557bfc4 100644 --- a/apps/settings/tests/Controller/UsersControllerTest.php +++ b/apps/settings/tests/Controller/UsersControllerTest.php @@ -1,32 +1,10 @@ <?php + +declare(strict_types=1); /** - * @copyright 2014-2015 Lukas Reschke lukas@owncloud.com - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Calviño Sánchez <danxuliu@gmail.com> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vincent Petry <vincent@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/>. - * + * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2014-2015 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\Settings\Tests\Controller; @@ -35,6 +13,7 @@ use OC\Encryption\Exceptions\ModuleDoesNotExistsException; use OC\ForbiddenException; use OC\Group\Manager; use OC\KnownUser\KnownUserService; +use OC\User\Manager as UserManager; use OCA\Settings\Controller\UsersController; use OCP\Accounts\IAccount; use OCP\Accounts\IAccountManager; @@ -42,22 +21,19 @@ use OCP\Accounts\IAccountProperty; use OCP\Accounts\PropertyDoesNotExistException; use OCP\App\IAppManager; use OCP\AppFramework\Http; +use OCP\AppFramework\Services\IInitialState; use OCP\BackgroundJob\IJobList; use OCP\Encryption\IEncryptionModule; use OCP\Encryption\IManager; use OCP\EventDispatcher\IEventDispatcher; -use OCP\IAvatarManager; use OCP\IConfig; use OCP\IGroupManager; use OCP\IL10N; -use OCP\ILogger; use OCP\IRequest; use OCP\IUser; -use OCP\IUserManager; use OCP\IUserSession; use OCP\L10N\IFactory; use OCP\Mail\IMailer; -use OCP\Security\ISecureRandom; use PHPUnit\Framework\MockObject\MockObject; /** @@ -66,49 +42,27 @@ use PHPUnit\Framework\MockObject\MockObject; * @package Tests\Settings\Controller */ class UsersControllerTest extends \Test\TestCase { - /** @var IGroupManager|\PHPUnit\Framework\MockObject\MockObject */ - private $groupManager; - /** @var IUserManager|\PHPUnit\Framework\MockObject\MockObject */ - private $userManager; - /** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */ - private $userSession; - /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */ - private $config; - /** @var ILogger|\PHPUnit\Framework\MockObject\MockObject */ - private $logger; - /** @var IMailer|\PHPUnit\Framework\MockObject\MockObject */ - private $mailer; - /** @var IFactory|\PHPUnit\Framework\MockObject\MockObject */ - private $l10nFactory; - /** @var IAppManager|\PHPUnit\Framework\MockObject\MockObject */ - private $appManager; - /** @var IAvatarManager|\PHPUnit\Framework\MockObject\MockObject */ - private $avatarManager; - /** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */ - private $l; - /** @var AccountManager | \PHPUnit\Framework\MockObject\MockObject */ - private $accountManager; - /** @var ISecureRandom | \PHPUnit\Framework\MockObject\MockObject */ - private $secureRandom; - /** @var \OCA\Settings\Mailer\NewUserMailHelper|\PHPUnit\Framework\MockObject\MockObject */ - private $newUserMailHelper; - /** @var IJobList | \PHPUnit\Framework\MockObject\MockObject */ - private $jobList; - /** @var \OC\Security\IdentityProof\Manager |\PHPUnit\Framework\MockObject\MockObject */ - private $securityManager; - /** @var IManager | \PHPUnit\Framework\MockObject\MockObject */ - private $encryptionManager; - /** @var KnownUserService|\PHPUnit\Framework\MockObject\MockObject */ - private $knownUserService; - /** @var IEncryptionModule | \PHPUnit\Framework\MockObject\MockObject */ - private $encryptionModule; - /** @var IEventDispatcher|\PHPUnit\Framework\MockObject\MockObject */ - private $dispatcher; + private IGroupManager&MockObject $groupManager; + private UserManager&MockObject $userManager; + private IUserSession&MockObject $userSession; + private IConfig&MockObject $config; + private IMailer&MockObject $mailer; + private IFactory&MockObject $l10nFactory; + private IAppManager&MockObject $appManager; + private IL10N&MockObject $l; + private AccountManager&MockObject $accountManager; + private IJobList&MockObject $jobList; + private \OC\Security\IdentityProof\Manager&MockObject $securityManager; + private IManager&MockObject $encryptionManager; + private KnownUserService&MockObject $knownUserService; + private IEncryptionModule&MockObject $encryptionModule; + private IEventDispatcher&MockObject $dispatcher; + private IInitialState&MockObject $initialState; protected function setUp(): void { parent::setUp(); - $this->userManager = $this->createMock(IUserManager::class); + $this->userManager = $this->createMock(UserManager::class); $this->groupManager = $this->createMock(Manager::class); $this->userSession = $this->createMock(IUserSession::class); $this->config = $this->createMock(IConfig::class); @@ -117,11 +71,12 @@ class UsersControllerTest extends \Test\TestCase { $this->l10nFactory = $this->createMock(IFactory::class); $this->appManager = $this->createMock(IAppManager::class); $this->accountManager = $this->createMock(AccountManager::class); - $this->securityManager = $this->getMockBuilder(\OC\Security\IdentityProof\Manager::class)->disableOriginalConstructor()->getMock(); + $this->securityManager = $this->createMock(\OC\Security\IdentityProof\Manager::class); $this->jobList = $this->createMock(IJobList::class); $this->encryptionManager = $this->createMock(IManager::class); $this->knownUserService = $this->createMock(KnownUserService::class); $this->dispatcher = $this->createMock(IEventDispatcher::class); + $this->initialState = $this->createMock(IInitialState::class); $this->l->method('t') ->willReturnCallback(function ($text, $parameters = []) { @@ -137,9 +92,13 @@ class UsersControllerTest extends \Test\TestCase { /** * @param bool $isAdmin - * @return UsersController | \PHPUnit\Framework\MockObject\MockObject + * @return UsersController|MockObject */ - protected function getController($isAdmin = false, $mockedMethods = []) { + protected function getController(bool $isAdmin = false, array $mockedMethods = []) { + $this->groupManager->expects($this->any()) + ->method('isAdmin') + ->willReturn($isAdmin); + if (empty($mockedMethods)) { return new UsersController( 'settings', @@ -148,7 +107,6 @@ class UsersControllerTest extends \Test\TestCase { $this->groupManager, $this->userSession, $this->config, - $isAdmin, $this->l, $this->mailer, $this->l10nFactory, @@ -158,7 +116,8 @@ class UsersControllerTest extends \Test\TestCase { $this->jobList, $this->encryptionManager, $this->knownUserService, - $this->dispatcher + $this->dispatcher, + $this->initialState, ); } else { return $this->getMockBuilder(UsersController::class) @@ -170,7 +129,6 @@ class UsersControllerTest extends \Test\TestCase { $this->groupManager, $this->userSession, $this->config, - $isAdmin, $this->l, $this->mailer, $this->l10nFactory, @@ -180,9 +138,12 @@ class UsersControllerTest extends \Test\TestCase { $this->jobList, $this->encryptionManager, $this->knownUserService, - $this->dispatcher + $this->dispatcher, + $this->initialState, ] - )->setMethods($mockedMethods)->getMock(); + ) + ->onlyMethods($mockedMethods) + ->getMock(); } } @@ -204,7 +165,7 @@ class UsersControllerTest extends \Test\TestCase { return $property; } - protected function getDefaultAccountMock(bool $useDefaultValues = true): MockObject { + protected function getDefaultAccountMock(): MockObject { $propertyMocks = [ IAccountManager::PROPERTY_DISPLAYNAME => $this->buildPropertyMock( IAccountManager::PROPERTY_DISPLAYNAME, @@ -241,6 +202,26 @@ class UsersControllerTest extends \Test\TestCase { 'Default twitter', IAccountManager::SCOPE_LOCAL, ), + IAccountManager::PROPERTY_BLUESKY => $this->buildPropertyMock( + IAccountManager::PROPERTY_BLUESKY, + 'Default bluesky', + IAccountManager::SCOPE_LOCAL, + ), + IAccountManager::PROPERTY_FEDIVERSE => $this->buildPropertyMock( + IAccountManager::PROPERTY_FEDIVERSE, + 'Default fediverse', + IAccountManager::SCOPE_LOCAL, + ), + IAccountManager::PROPERTY_BIRTHDATE => $this->buildPropertyMock( + IAccountManager::PROPERTY_BIRTHDATE, + 'Default birthdate', + IAccountManager::SCOPE_LOCAL, + ), + IAccountManager::PROPERTY_PRONOUNS => $this->buildPropertyMock( + IAccountManager::PROPERTY_PRONOUNS, + 'Default pronouns', + IAccountManager::SCOPE_LOCAL, + ), ]; $account = $this->createMock(IAccount::class); @@ -259,14 +240,8 @@ class UsersControllerTest extends \Test\TestCase { return $account; } - /** - * @dataProvider dataTestSetUserSettings - * - * @param string $email - * @param bool $validEmail - * @param $expectedStatus - */ - public function testSetUserSettings($email, $validEmail, $expectedStatus) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestSetUserSettings')] + public function testSetUserSettings(string $email, bool $validEmail, int $expectedStatus): void { $controller = $this->getController(false, ['saveUserSettings']); $user = $this->createMock(IUser::class); $user->method('getUID')->willReturn('johndoe'); @@ -292,7 +267,7 @@ class UsersControllerTest extends \Test\TestCase { $controller->expects($this->never())->method('saveUserSettings'); } - $result = $controller->setUserSettings(// + $result = $controller->setUserSettings( AccountManager::SCOPE_FEDERATED, 'displayName', AccountManager::SCOPE_FEDERATED, @@ -305,13 +280,19 @@ class UsersControllerTest extends \Test\TestCase { 'street and city', AccountManager::SCOPE_FEDERATED, '@nextclouders', - AccountManager::SCOPE_FEDERATED + AccountManager::SCOPE_FEDERATED, + '@nextclouders', + AccountManager::SCOPE_FEDERATED, + '2020-01-01', + AccountManager::SCOPE_FEDERATED, + 'they/them', + AccountManager::SCOPE_FEDERATED, ); $this->assertSame($expectedStatus, $result->getStatus()); } - public function dataTestSetUserSettings() { + public static function dataTestSetUserSettings(): array { return [ ['', true, Http::STATUS_OK], ['', false, Http::STATUS_OK], @@ -320,7 +301,7 @@ class UsersControllerTest extends \Test\TestCase { ]; } - public function testSetUserSettingsWhenUserDisplayNameChangeNotAllowed() { + public function testSetUserSettingsWhenUserDisplayNameChangeNotAllowed(): void { $controller = $this->getController(false, ['saveUserSettings']); $avatarScope = IAccountManager::SCOPE_PUBLISHED; @@ -336,6 +317,12 @@ class UsersControllerTest extends \Test\TestCase { $addressScope = IAccountManager::SCOPE_PUBLISHED; $twitter = '@nextclouders'; $twitterScope = IAccountManager::SCOPE_PUBLISHED; + $fediverse = '@nextclouders@floss.social'; + $fediverseScope = IAccountManager::SCOPE_PUBLISHED; + $birtdate = '2020-01-01'; + $birthdateScope = IAccountManager::SCOPE_PUBLISHED; + $pronouns = 'she/her'; + $pronounsScope = IAccountManager::SCOPE_PUBLISHED; $user = $this->createMock(IUser::class); $user->method('getUID')->willReturn('johndoe'); @@ -410,11 +397,17 @@ class UsersControllerTest extends \Test\TestCase { $address, $addressScope, $twitter, - $twitterScope + $twitterScope, + $fediverse, + $fediverseScope, + $birtdate, + $birthdateScope, + $pronouns, + $pronounsScope, ); } - public function testSetUserSettingsWhenFederatedFilesharingNotEnabled() { + public function testSetUserSettingsWhenFederatedFilesharingNotEnabled(): void { $controller = $this->getController(false, ['saveUserSettings']); $user = $this->createMock(IUser::class); $user->method('getUID')->willReturn('johndoe'); @@ -447,6 +440,14 @@ class UsersControllerTest extends \Test\TestCase { $addressScope = IAccountManager::SCOPE_PUBLISHED; $twitter = '@nextclouders'; $twitterScope = IAccountManager::SCOPE_PUBLISHED; + $bluesky = 'nextclouders.net'; + $blueskyScope = IAccountManager::SCOPE_PUBLISHED; + $fediverse = '@nextclouders@floss.social'; + $fediverseScope = IAccountManager::SCOPE_PUBLISHED; + $birthdate = '2020-01-01'; + $birthdateScope = IAccountManager::SCOPE_PUBLISHED; + $pronouns = 'she/her'; + $pronounsScope = IAccountManager::SCOPE_PUBLISHED; // All settings are changed (in the past phone, website, address and // twitter were not changed). @@ -464,6 +465,14 @@ class UsersControllerTest extends \Test\TestCase { $expectedProperties[IAccountManager::PROPERTY_ADDRESS]['scope'] = $addressScope; $expectedProperties[IAccountManager::PROPERTY_TWITTER]['value'] = $twitter; $expectedProperties[IAccountManager::PROPERTY_TWITTER]['scope'] = $twitterScope; + $expectedProperties[IAccountManager::PROPERTY_BLUESKY]['value'] = $bluesky; + $expectedProperties[IAccountManager::PROPERTY_BLUESKY]['scope'] = $blueskyScope; + $expectedProperties[IAccountManager::PROPERTY_FEDIVERSE]['value'] = $fediverse; + $expectedProperties[IAccountManager::PROPERTY_FEDIVERSE]['scope'] = $fediverseScope; + $expectedProperties[IAccountManager::PROPERTY_BIRTHDATE]['value'] = $birthdate; + $expectedProperties[IAccountManager::PROPERTY_BIRTHDATE]['scope'] = $birthdateScope; + $expectedProperties[IAccountManager::PROPERTY_PRONOUNS]['value'] = $pronouns; + $expectedProperties[IAccountManager::PROPERTY_PRONOUNS]['scope'] = $pronounsScope; $this->mailer->expects($this->once())->method('validateMailAddress') ->willReturn(true); @@ -485,24 +494,27 @@ class UsersControllerTest extends \Test\TestCase { $address, $addressScope, $twitter, - $twitterScope + $twitterScope, + $bluesky, + $blueskyScope, + $fediverse, + $fediverseScope, + $birthdate, + $birthdateScope, + $pronouns, + $pronounsScope, ); } - /** - * @dataProvider dataTestSetUserSettingsSubset - * - * @param string $property - * @param string $propertyValue - */ - public function testSetUserSettingsSubset($property, $propertyValue) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestSetUserSettingsSubset')] + public function testSetUserSettingsSubset(string $property, string $propertyValue): void { $controller = $this->getController(false, ['saveUserSettings']); $user = $this->createMock(IUser::class); $user->method('getUID')->willReturn('johndoe'); $this->userSession->method('getUser')->willReturn($user); - /** @var IAccount|MockObject $userAccount */ + /** @var IAccount&MockObject $userAccount */ $userAccount = $this->getDefaultAccountMock(); $this->accountManager->expects($this->once()) @@ -523,10 +535,18 @@ class UsersControllerTest extends \Test\TestCase { $addressScope = ($property === 'addressScope') ? $propertyValue : null; $twitter = ($property === 'twitter') ? $propertyValue : null; $twitterScope = ($property === 'twitterScope') ? $propertyValue : null; - - /** @var IAccountProperty[]|MockObject[] $expectedProperties */ + $bluesky = ($property === 'bluesky') ? $propertyValue : null; + $blueskyScope = ($property === 'blueskyScope') ? $propertyValue : null; + $fediverse = ($property === 'fediverse') ? $propertyValue : null; + $fediverseScope = ($property === 'fediverseScope') ? $propertyValue : null; + $birthdate = ($property === 'birthdate') ? $propertyValue : null; + $birthdateScope = ($property === 'birthdateScope') ? $propertyValue : null; + $pronouns = ($property === 'pronouns') ? $propertyValue : null; + $pronounsScope = ($property === 'pronounsScope') ? $propertyValue : null; + + /** @var IAccountProperty[]&MockObject[] $expectedProperties */ $expectedProperties = $userAccount->getProperties(); - $isScope = strrpos($property, 'Scope') === strlen($property) - strlen(5); + $isScope = strrpos($property, 'Scope') === strlen($property) - strlen('5'); switch ($property) { case 'avatarScope': $propertyId = IAccountManager::PROPERTY_AVATAR; @@ -555,6 +575,22 @@ class UsersControllerTest extends \Test\TestCase { case 'twitterScope': $propertyId = IAccountManager::PROPERTY_TWITTER; break; + case 'bluesky': + case 'blueskyScope': + $propertyId = IAccountManager::PROPERTY_BLUESKY; + break; + case 'fediverse': + case 'fediverseScope': + $propertyId = IAccountManager::PROPERTY_FEDIVERSE; + break; + case 'birthdate': + case 'birthdateScope': + $propertyId = IAccountManager::PROPERTY_BIRTHDATE; + break; + case 'pronouns': + case 'pronounsScope': + $propertyId = IAccountManager::PROPERTY_PRONOUNS; + break; default: $propertyId = '404'; } @@ -564,7 +600,7 @@ class UsersControllerTest extends \Test\TestCase { if (!empty($email)) { $this->mailer->expects($this->once())->method('validateMailAddress') - ->willReturn(true); + ->willReturn(true); } $controller->expects($this->once()) @@ -584,11 +620,19 @@ class UsersControllerTest extends \Test\TestCase { $address, $addressScope, $twitter, - $twitterScope + $twitterScope, + $bluesky, + $blueskyScope, + $fediverse, + $fediverseScope, + $birthdate, + $birthdateScope, + $pronouns, + $pronounsScope, ); } - public function dataTestSetUserSettingsSubset() { + public static function dataTestSetUserSettingsSubset(): array { return [ ['avatarScope', IAccountManager::SCOPE_PUBLISHED], ['displayName', 'Display name'], @@ -603,20 +647,19 @@ class UsersControllerTest extends \Test\TestCase { ['addressScope', IAccountManager::SCOPE_PUBLISHED], ['twitter', '@nextclouders'], ['twitterScope', IAccountManager::SCOPE_PUBLISHED], + ['bluesky', 'nextclouders.net'], + ['blueskyScope', IAccountManager::SCOPE_PUBLISHED], + ['fediverse', '@nextclouders@floss.social'], + ['fediverseScope', IAccountManager::SCOPE_PUBLISHED], + ['birthdate', '2020-01-01'], + ['birthdateScope', IAccountManager::SCOPE_PUBLISHED], + ['pronouns', 'he/him'], + ['pronounsScope', IAccountManager::SCOPE_PUBLISHED], ]; } - /** - * @dataProvider dataTestSaveUserSettings - * - * @param array $data - * @param string $oldEmailAddress - * @param string $oldDisplayName - */ - public function testSaveUserSettings($data, - $oldEmailAddress, - $oldDisplayName - ) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestSaveUserSettings')] + public function testSaveUserSettings(array $data, ?string $oldEmailAddress, ?string $oldDisplayName): void { $controller = $this->getController(); $user = $this->createMock(IUser::class); @@ -624,16 +667,14 @@ class UsersControllerTest extends \Test\TestCase { $user->method('getSystemEMailAddress')->willReturn($oldEmailAddress); $user->method('canChangeDisplayName')->willReturn(true); - if ($data[IAccountManager::PROPERTY_EMAIL]['value'] === $oldEmailAddress || - ($oldEmailAddress === null && $data[IAccountManager::PROPERTY_EMAIL]['value'] === '')) { + if (strtolower($data[IAccountManager::PROPERTY_EMAIL]['value']) === strtolower($oldEmailAddress ?? '')) { $user->expects($this->never())->method('setSystemEMailAddress'); } else { $user->expects($this->once())->method('setSystemEMailAddress') ->with($data[IAccountManager::PROPERTY_EMAIL]['value']); } - if ($data[IAccountManager::PROPERTY_DISPLAYNAME]['value'] === $oldDisplayName || - ($oldDisplayName === null && $data[IAccountManager::PROPERTY_DISPLAYNAME]['value'] === '')) { + if ($data[IAccountManager::PROPERTY_DISPLAYNAME]['value'] === $oldDisplayName ?? '') { $user->expects($this->never())->method('setDisplayName'); } else { $user->expects($this->once())->method('setDisplayName') @@ -670,7 +711,7 @@ class UsersControllerTest extends \Test\TestCase { $this->invokePrivate($controller, 'saveUserSettings', [$account]); } - public function dataTestSaveUserSettings() { + public static function dataTestSaveUserSettings(): array { return [ [ [ @@ -720,20 +761,26 @@ class UsersControllerTest extends \Test\TestCase { 'john@example.com', null ], + [ + [ + IAccountManager::PROPERTY_EMAIL => ['value' => 'john@example.com'], + IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'john doe'], + ], + 'JOHN@example.com', + null + ], ]; } - /** - * @dataProvider dataTestSaveUserSettingsException - */ + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestSaveUserSettingsException')] public function testSaveUserSettingsException( array $data, string $oldEmailAddress, string $oldDisplayName, - bool $setDisplayNameResult, - bool $canChangeEmail - ) { + bool $setDisplayNameResult, + bool $canChangeEmail, + ): void { $this->expectException(ForbiddenException::class); $controller = $this->getController(); @@ -773,7 +820,7 @@ class UsersControllerTest extends \Test\TestCase { } - public function dataTestSaveUserSettingsException() { + public static function dataTestSaveUserSettingsException(): array { return [ [ [ @@ -809,20 +856,13 @@ class UsersControllerTest extends \Test\TestCase { ]; } - /** - * @param string $account - * @param string $type - * @param array $dataBefore - * @param array $expectedData - * - * @dataProvider dataTestGetVerificationCode - */ - public function testGetVerificationCode($account, $type, $dataBefore, $expectedData, $onlyVerificationCode) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGetVerificationCode')] + public function testGetVerificationCode(string $account, string $type, array $dataBefore, array $expectedData, bool $onlyVerificationCode): void { $message = 'Use my Federated Cloud ID to share with me: user@nextcloud.com'; $signature = 'theSignature'; $code = $message . ' ' . $signature; - if ($type === IAccountManager::PROPERTY_TWITTER) { + if ($type === IAccountManager::PROPERTY_TWITTER || $type === IAccountManager::PROPERTY_FEDIVERSE) { $code = $message . ' ' . md5($signature); } @@ -876,7 +916,7 @@ class UsersControllerTest extends \Test\TestCase { $this->assertSame($code, $data['code']); } - public function dataTestGetVerificationCode() { + public static function dataTestGetVerificationCode(): array { $accountDataBefore = [ IAccountManager::PROPERTY_WEBSITE => ['value' => 'https://nextcloud.com', 'verified' => IAccountManager::NOT_VERIFIED], IAccountManager::PROPERTY_TWITTER => ['value' => '@nextclouders', 'verified' => IAccountManager::NOT_VERIFIED, 'signature' => 'theSignature'], @@ -903,7 +943,7 @@ class UsersControllerTest extends \Test\TestCase { /** * test get verification code in case no valid user was given */ - public function testGetVerificationCodeInvalidUser() { + public function testGetVerificationCodeInvalidUser(): void { $controller = $this->getController(); $this->userSession->expects($this->once())->method('getUser')->willReturn(null); $result = $controller->getVerificationCode('account', false); @@ -911,18 +951,13 @@ class UsersControllerTest extends \Test\TestCase { $this->assertSame(Http::STATUS_BAD_REQUEST, $result->getStatus()); } - /** - * @dataProvider dataTestCanAdminChangeUserPasswords - * - * @param bool $encryptionEnabled - * @param bool $encryptionModuleLoaded - * @param bool $masterKeyEnabled - * @param bool $expected - */ - public function testCanAdminChangeUserPasswords($encryptionEnabled, - $encryptionModuleLoaded, - $masterKeyEnabled, - $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestCanAdminChangeUserPasswords')] + public function testCanAdminChangeUserPasswords( + bool $encryptionEnabled, + bool $encryptionModuleLoaded, + bool $masterKeyEnabled, + bool $expected, + ): void { $controller = $this->getController(); $this->encryptionManager->expects($this->any()) @@ -945,7 +980,7 @@ class UsersControllerTest extends \Test\TestCase { $this->assertSame($expected, $result); } - public function dataTestCanAdminChangeUserPasswords() { + public static function dataTestCanAdminChangeUserPasswords(): array { return [ // encryptionEnabled, encryptionModuleLoaded, masterKeyEnabled, expectedResult [true, true, true, true], diff --git a/apps/settings/tests/Mailer/NewUserMailHelperTest.php b/apps/settings/tests/Mailer/NewUserMailHelperTest.php index f2e1d87672d..f352a2b733d 100644 --- a/apps/settings/tests/Mailer/NewUserMailHelperTest.php +++ b/apps/settings/tests/Mailer/NewUserMailHelperTest.php @@ -1,40 +1,14 @@ <?php + /** - * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Brad Rubenstein <brad@wbr.tech> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jan-Christoph Borchardt <hey@jancborchardt.net> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Liam JACK <liamjack@users.noreply.github.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author medcloud <42641918+medcloud@users.noreply.github.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author zulan <git@zulan.net> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\Settings\Tests\Mailer; use OC\Mail\EMailTemplate; use OC\Mail\Message; +use OCA\Settings\Mailer\NewUserMailHelper; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Defaults; use OCP\IConfig; @@ -42,31 +16,25 @@ use OCP\IL10N; use OCP\IURLGenerator; use OCP\IUser; use OCP\L10N\IFactory; +use OCP\Mail\Headers\AutoSubmitted; use OCP\Mail\IEMailTemplate; use OCP\Mail\IMailer; use OCP\Security\ICrypto; use OCP\Security\ISecureRandom; +use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; class NewUserMailHelperTest extends TestCase { - /** @var Defaults|\PHPUnit\Framework\MockObject\MockObject */ - private $defaults; - /** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */ - private $urlGenerator; - /** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */ - private $l10n; - /** @var IMailer|\PHPUnit\Framework\MockObject\MockObject */ - private $mailer; - /** @var ISecureRandom|\PHPUnit\Framework\MockObject\MockObject */ - private $secureRandom; - /** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject */ - private $timeFactory; - /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */ - private $config; - /** @var ICrypto|\PHPUnit\Framework\MockObject\MockObject */ - private $crypto; - /** @var \OCA\Settings\Mailer\NewUserMailHelper */ - private $newUserMailHelper; + private Defaults&MockObject $defaults; + private IURLGenerator&MockObject $urlGenerator; + private IL10N&MockObject $l10n; + private IFactory&MockObject $l10nFactory; + private IMailer&MockObject $mailer; + private ISecureRandom&MockObject $secureRandom; + private ITimeFactory&MockObject $timeFactory; + private IConfig&MockObject $config; + private ICrypto&MockObject $crypto; + private NewUserMailHelper $newUserMailHelper; protected function setUp(): void { parent::setUp(); @@ -82,6 +50,8 @@ class NewUserMailHelperTest extends TestCase { $this->defaults, $this->urlGenerator, $this->l10nFactory, + null, + null, 'test.TestTemplate', [] ); @@ -112,7 +82,7 @@ class NewUserMailHelperTest extends TestCase { return $this->l10n; }); - $this->newUserMailHelper = new \OCA\Settings\Mailer\NewUserMailHelper( + $this->newUserMailHelper = new NewUserMailHelper( $this->defaults, $this->urlGenerator, $this->l10nFactory, @@ -125,7 +95,7 @@ class NewUserMailHelperTest extends TestCase { ); } - public function testGenerateTemplateWithPasswordResetToken() { + public function testGenerateTemplateWithPasswordResetToken(): void { $this->secureRandom ->expects($this->once()) ->method('generate') @@ -135,7 +105,7 @@ class NewUserMailHelperTest extends TestCase { ->expects($this->once()) ->method('getTime') ->willReturn(12345); - /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */ + /** @var IUser&MockObject $user */ $user = $this->createMock(IUser::class); $user ->expects($this->any()) @@ -155,7 +125,7 @@ class NewUserMailHelperTest extends TestCase { ->method('setUserValue') ->with('john', 'core', 'lostpassword', 'TokenCiphertext'); $this->urlGenerator - ->expects($this->at(0)) + ->expects($this->once()) ->method('linkToRouteAbsolute') ->with('core.lost.resetform', ['userId' => 'john', 'token' => 'MySuperLongSecureRandomToken']) ->willReturn('https://example.com/resetPassword/MySuperLongSecureRandomToken'); @@ -163,17 +133,17 @@ class NewUserMailHelperTest extends TestCase { ->expects($this->any()) ->method('getDisplayName') ->willReturn('john'); - $user - ->expects($this->at(5)) - ->method('getUID') - ->willReturn('john'); $this->defaults ->expects($this->any()) ->method('getName') ->willReturn('TestCloud'); $this->defaults - ->expects($this->any()) - ->method('getTextColorPrimary') + ->expects($this->atLeastOnce()) + ->method('getDefaultColorPrimary') + ->willReturn('#00679e'); + $this->defaults + ->expects($this->atLeastOnce()) + ->method('getDefaultTextColorPrimary') ->willReturn('#ffffff'); $expectedHtmlBody = <<<EOF @@ -201,7 +171,7 @@ class NewUserMailHelperTest extends TestCase { <table class="row collapse" style="border-collapse:collapse;border-spacing:0;display:table;padding:0;position:relative;text-align:left;vertical-align:top;width:100%"> <tbody> <tr style="padding:0;text-align:left;vertical-align:top"> - <center data-parsed="" style="background-color:;min-width:175px;max-height:175px; padding:35px 0px;border-radius:200px"> + <center data-parsed="" style="background-color:#00679e;min-width:175px;max-height:175px; padding:35px 0px;border-radius:200px"> <img class="logo float-center" src="" alt="TestCloud" align="center" style="-ms-interpolation-mode:bicubic;clear:both;display:block;float:none;margin:0 auto;outline:0;text-align:center;text-decoration:none;max-height:105px;max-width:105px;width:auto;height:auto"> </center> </tr> @@ -263,7 +233,7 @@ class NewUserMailHelperTest extends TestCase { <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%"> <tr style="padding:0;text-align:left;vertical-align:top"> <th style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:left"> - <p style="Margin:0;Margin-bottom:10px;color:#777;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;margin-bottom:10px;padding:0;text-align:center">Your username is: john</p> + <p style="Margin:0;Margin-bottom:10px;color:#777;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;margin-bottom:10px;padding:0;text-align:center">Your Login is: john</p> </th> <th class="expander" style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0!important;text-align:left;visibility:hidden;width:0"></th> </tr> @@ -286,32 +256,46 @@ class NewUserMailHelperTest extends TestCase { <tr style="padding:0;text-align:left;vertical-align:top"> <th style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:left"> <center data-parsed="" style="min-width:490px;width:100%"> - <table class="button btn default primary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0 0 30px 0;margin-right:15px;max-height:60px;max-width:300px;padding:0;text-align:center;vertical-align:top;width:auto;background:;background-color:;color:#fefefe;"> - <tr style="padding:0;text-align:left;vertical-align:top"> - <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> - <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%"> + <!--[if (gte mso 9)|(IE)]> + <table> + <tr> + <td> + <![endif]--> + <table class="button btn default primary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0 0 30px 0;margin-right:15px;border-radius:8px;max-width:300px;padding:0;text-align:center;vertical-align:top;width:auto;background:#00679e;background-color:#00679e;color:#fefefe;"> <tr style="padding:0;text-align:left;vertical-align:top"> - <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid ;border-collapse:collapse!important;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> - <a href="https://example.com/resetPassword/MySuperLongSecureRandomToken" style="Margin:0;border:0 solid ;border-radius:2px;color:#ffffff;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:1.3;margin:0;padding:10px 25px 10px 25px;text-align:left;outline:1px solid #ffffff;text-decoration:none">Set your password</a> + <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> + <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%"> + <tr style="padding:0;text-align:left;vertical-align:top"> + <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid #00679e;border-collapse:collapse!important;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> + <a href="https://example.com/resetPassword/MySuperLongSecureRandomToken" style="Margin:0;border:0 solid #00679e;color:#ffffff;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:normal;margin:0;padding:8px;text-align:left;outline:1px solid #ffffff;text-decoration:none">Set your password</a> + </td> + </tr> + </table> </td> </tr> </table> + <!--[if (gte mso 9)|(IE)]> </td> - </tr> - </table> - <table class="button btn default secondary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0 0 30px 0;max-height:40px;max-width:300px;padding:0;text-align:center;vertical-align:top;width:auto"> - <tr style="padding:0;text-align:left;vertical-align:top"> - <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> - <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%"> + <td> + <![endif]--> + <table class="button btn default secondary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;background-color: #ccc;margin:0 0 30px 0;max-height:40px;max-width:300px;padding:1px;border-radius:8px;text-align:center;vertical-align:top;width:auto"> <tr style="padding:0;text-align:left;vertical-align:top"> - <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;background:#777;border:0 solid #777;border-collapse:collapse!important;color:#fefefe;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> - <a href="https://nextcloud.com/install/#install-clients" style="Margin:0;background-color:#fff;border:0 solid #777;border-radius:2px;color:#6C6C6C!important;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:1.3;margin:0;outline:1px solid #CBCBCB;padding:10px 25px 10px 25px;text-align:left;text-decoration:none">Install Client</a> + <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> + <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%"> + <tr style="padding:0;text-align:left;vertical-align:top"> + <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid #777;border-collapse:collapse!important;color:#fefefe;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> + <a href="https://nextcloud.com/install/#install-clients" style="Margin:0;background-color:#fff;border:0 solid #777;color:#6C6C6C!important;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:normal;margin:0;border-radius: 7px;padding:8px;text-align:left;text-decoration:none">Install Client</a> + </td> + </tr> + </table> </td> </tr> </table> + <!--[if (gte mso 9)|(IE)]> </td> </tr> </table> + <![endif]--> </center> </th> <th class="expander" style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0!important;text-align:left;visibility:hidden;width:0"></th> @@ -364,14 +348,16 @@ Welcome aboard Welcome to your TestCloud account, you can add, protect, and share your data. -Your username is: john +Your Login is: john Set your password: https://example.com/resetPassword/MySuperLongSecureRandomToken Install Client: https://nextcloud.com/install/#install-clients --- +EOF; + $expectedTextBody .= "\n-- \n"; + $expectedTextBody .= <<<EOF TestCloud This is an automatically sent email, please do not reply. EOF; @@ -382,14 +368,16 @@ EOF; $this->assertSame('OC\Mail\EMailTemplate', get_class($result)); } - public function testGenerateTemplateWithoutPasswordResetToken() { + public function testGenerateTemplateWithoutPasswordResetToken(): void { $this->urlGenerator - ->expects($this->at(0)) + ->expects($this->any()) ->method('getAbsoluteURL') - ->with('/') - ->willReturn('https://example.com/'); + ->willReturnMap([ + ['/','https://example.com/'], + ['myLogo',''], + ]); - /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */ + /** @var IUser&MockObject $user */ $user = $this->createMock(IUser::class); $user ->expects($this->any()) @@ -404,8 +392,12 @@ EOF; ->method('getName') ->willReturn('TestCloud'); $this->defaults - ->expects($this->any()) - ->method('getTextColorPrimary') + ->expects($this->atLeastOnce()) + ->method('getDefaultColorPrimary') + ->willReturn('#00679e'); + $this->defaults + ->expects($this->atLeastOnce()) + ->method('getDefaultTextColorPrimary') ->willReturn('#ffffff'); $expectedHtmlBody = <<<EOF @@ -433,7 +425,7 @@ EOF; <table class="row collapse" style="border-collapse:collapse;border-spacing:0;display:table;padding:0;position:relative;text-align:left;vertical-align:top;width:100%"> <tbody> <tr style="padding:0;text-align:left;vertical-align:top"> - <center data-parsed="" style="background-color:;min-width:175px;max-height:175px; padding:35px 0px;border-radius:200px"> + <center data-parsed="" style="background-color:#00679e;min-width:175px;max-height:175px; padding:35px 0px;border-radius:200px"> <img class="logo float-center" src="" alt="TestCloud" align="center" style="-ms-interpolation-mode:bicubic;clear:both;display:block;float:none;margin:0 auto;outline:0;text-align:center;text-decoration:none;max-height:105px;max-width:105px;width:auto;height:auto"> </center> </tr> @@ -495,7 +487,7 @@ EOF; <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%"> <tr style="padding:0;text-align:left;vertical-align:top"> <th style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:left"> - <p style="Margin:0;Margin-bottom:10px;color:#777;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;margin-bottom:10px;padding:0;text-align:center">Your username is: john</p> + <p style="Margin:0;Margin-bottom:10px;color:#777;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;margin-bottom:10px;padding:0;text-align:center">Your Login is: john</p> </th> <th class="expander" style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0!important;text-align:left;visibility:hidden;width:0"></th> </tr> @@ -518,32 +510,46 @@ EOF; <tr style="padding:0;text-align:left;vertical-align:top"> <th style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:left"> <center data-parsed="" style="min-width:490px;width:100%"> - <table class="button btn default primary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0 0 30px 0;margin-right:15px;max-height:60px;max-width:300px;padding:0;text-align:center;vertical-align:top;width:auto;background:;background-color:;color:#fefefe;"> - <tr style="padding:0;text-align:left;vertical-align:top"> - <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> - <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%"> + <!--[if (gte mso 9)|(IE)]> + <table> + <tr> + <td> + <![endif]--> + <table class="button btn default primary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0 0 30px 0;margin-right:15px;border-radius:8px;max-width:300px;padding:0;text-align:center;vertical-align:top;width:auto;background:#00679e;background-color:#00679e;color:#fefefe;"> <tr style="padding:0;text-align:left;vertical-align:top"> - <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid ;border-collapse:collapse!important;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> - <a href="https://example.com/" style="Margin:0;border:0 solid ;border-radius:2px;color:#ffffff;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:1.3;margin:0;padding:10px 25px 10px 25px;text-align:left;outline:1px solid #ffffff;text-decoration:none">Go to TestCloud</a> + <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> + <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%"> + <tr style="padding:0;text-align:left;vertical-align:top"> + <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid #00679e;border-collapse:collapse!important;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> + <a href="https://example.com/" style="Margin:0;border:0 solid #00679e;color:#ffffff;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:normal;margin:0;padding:8px;text-align:left;outline:1px solid #ffffff;text-decoration:none">Go to TestCloud</a> + </td> + </tr> + </table> </td> </tr> </table> + <!--[if (gte mso 9)|(IE)]> </td> - </tr> - </table> - <table class="button btn default secondary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0 0 30px 0;max-height:40px;max-width:300px;padding:0;text-align:center;vertical-align:top;width:auto"> - <tr style="padding:0;text-align:left;vertical-align:top"> - <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> - <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%"> + <td> + <![endif]--> + <table class="button btn default secondary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;background-color: #ccc;margin:0 0 30px 0;max-height:40px;max-width:300px;padding:1px;border-radius:8px;text-align:center;vertical-align:top;width:auto"> <tr style="padding:0;text-align:left;vertical-align:top"> - <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;background:#777;border:0 solid #777;border-collapse:collapse!important;color:#fefefe;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> - <a href="https://nextcloud.com/install/#install-clients" style="Margin:0;background-color:#fff;border:0 solid #777;border-radius:2px;color:#6C6C6C!important;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:1.3;margin:0;outline:1px solid #CBCBCB;padding:10px 25px 10px 25px;text-align:left;text-decoration:none">Install Client</a> + <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> + <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%"> + <tr style="padding:0;text-align:left;vertical-align:top"> + <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid #777;border-collapse:collapse!important;color:#fefefe;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> + <a href="https://nextcloud.com/install/#install-clients" style="Margin:0;background-color:#fff;border:0 solid #777;color:#6C6C6C!important;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:normal;margin:0;border-radius: 7px;padding:8px;text-align:left;text-decoration:none">Install Client</a> + </td> + </tr> + </table> </td> </tr> </table> + <!--[if (gte mso 9)|(IE)]> </td> </tr> </table> + <![endif]--> </center> </th> <th class="expander" style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0!important;text-align:left;visibility:hidden;width:0"></th> @@ -596,14 +602,16 @@ Welcome aboard John Doe Welcome to your TestCloud account, you can add, protect, and share your data. -Your username is: john +Your Login is: john Go to TestCloud: https://example.com/ Install Client: https://nextcloud.com/install/#install-clients --- +EOF; + $expectedTextBody .= "\n-- \n"; + $expectedTextBody .= <<<EOF TestCloud This is an automatically sent email, please do not reply. EOF; @@ -614,14 +622,16 @@ EOF; $this->assertSame('OC\Mail\EMailTemplate', get_class($result)); } - public function testGenerateTemplateWithoutUserId() { + public function testGenerateTemplateWithoutUserId(): void { $this->urlGenerator - ->expects($this->at(0)) + ->expects($this->any()) ->method('getAbsoluteURL') - ->with('/') - ->willReturn('https://example.com/'); + ->willReturnMap([ + ['/', 'https://example.com/'], + ['myLogo', ''], + ]); - /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */ + /** @var IUser&MockObject $user */ $user = $this->createMock(IUser::class); $user ->expects($this->any()) @@ -640,8 +650,12 @@ EOF; ->method('getName') ->willReturn('TestCloud'); $this->defaults - ->expects($this->any()) - ->method('getTextColorPrimary') + ->expects($this->atLeastOnce()) + ->method('getDefaultColorPrimary') + ->willReturn('#00679e'); + $this->defaults + ->expects($this->atLeastOnce()) + ->method('getDefaultTextColorPrimary') ->willReturn('#ffffff'); $expectedHtmlBody = <<<EOF @@ -669,7 +683,7 @@ EOF; <table class="row collapse" style="border-collapse:collapse;border-spacing:0;display:table;padding:0;position:relative;text-align:left;vertical-align:top;width:100%"> <tbody> <tr style="padding:0;text-align:left;vertical-align:top"> - <center data-parsed="" style="background-color:;min-width:175px;max-height:175px; padding:35px 0px;border-radius:200px"> + <center data-parsed="" style="background-color:#00679e;min-width:175px;max-height:175px; padding:35px 0px;border-radius:200px"> <img class="logo float-center" src="" alt="TestCloud" align="center" style="-ms-interpolation-mode:bicubic;clear:both;display:block;float:none;margin:0 auto;outline:0;text-align:center;text-decoration:none;max-height:105px;max-width:105px;width:auto;height:auto"> </center> </tr> @@ -739,32 +753,46 @@ EOF; <tr style="padding:0;text-align:left;vertical-align:top"> <th style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:left"> <center data-parsed="" style="min-width:490px;width:100%"> - <table class="button btn default primary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0 0 30px 0;margin-right:15px;max-height:60px;max-width:300px;padding:0;text-align:center;vertical-align:top;width:auto;background:;background-color:;color:#fefefe;"> - <tr style="padding:0;text-align:left;vertical-align:top"> - <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> - <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%"> + <!--[if (gte mso 9)|(IE)]> + <table> + <tr> + <td> + <![endif]--> + <table class="button btn default primary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0 0 30px 0;margin-right:15px;border-radius:8px;max-width:300px;padding:0;text-align:center;vertical-align:top;width:auto;background:#00679e;background-color:#00679e;color:#fefefe;"> <tr style="padding:0;text-align:left;vertical-align:top"> - <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid ;border-collapse:collapse!important;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> - <a href="https://example.com/" style="Margin:0;border:0 solid ;border-radius:2px;color:#ffffff;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:1.3;margin:0;padding:10px 25px 10px 25px;text-align:left;outline:1px solid #ffffff;text-decoration:none">Go to TestCloud</a> + <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> + <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%"> + <tr style="padding:0;text-align:left;vertical-align:top"> + <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid #00679e;border-collapse:collapse!important;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> + <a href="https://example.com/" style="Margin:0;border:0 solid #00679e;color:#ffffff;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:normal;margin:0;padding:8px;text-align:left;outline:1px solid #ffffff;text-decoration:none">Go to TestCloud</a> + </td> + </tr> + </table> </td> </tr> </table> + <!--[if (gte mso 9)|(IE)]> </td> - </tr> - </table> - <table class="button btn default secondary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;margin:0 0 30px 0;max-height:40px;max-width:300px;padding:0;text-align:center;vertical-align:top;width:auto"> - <tr style="padding:0;text-align:left;vertical-align:top"> - <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> - <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%"> + <td> + <![endif]--> + <table class="button btn default secondary float-center" style="Margin:0 0 30px 0;border-collapse:collapse;border-spacing:0;display:inline-block;float:none;background-color: #ccc;margin:0 0 30px 0;max-height:40px;max-width:300px;padding:1px;border-radius:8px;text-align:center;vertical-align:top;width:auto"> <tr style="padding:0;text-align:left;vertical-align:top"> - <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;background:#777;border:0 solid #777;border-collapse:collapse!important;color:#fefefe;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:1.3;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> - <a href="https://nextcloud.com/install/#install-clients" style="Margin:0;background-color:#fff;border:0 solid #777;border-radius:2px;color:#6C6C6C!important;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:1.3;margin:0;outline:1px solid #CBCBCB;padding:10px 25px 10px 25px;text-align:left;text-decoration:none">Install Client</a> + <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> + <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%"> + <tr style="padding:0;text-align:left;vertical-align:top"> + <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border:0 solid #777;border-collapse:collapse!important;color:#fefefe;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:normal;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word"> + <a href="https://nextcloud.com/install/#install-clients" style="Margin:0;background-color:#fff;border:0 solid #777;color:#6C6C6C!important;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:regular;line-height:normal;margin:0;border-radius: 7px;padding:8px;text-align:left;text-decoration:none">Install Client</a> + </td> + </tr> + </table> </td> </tr> </table> + <!--[if (gte mso 9)|(IE)]> </td> </tr> </table> + <![endif]--> </center> </th> <th class="expander" style="Margin:0;color:#0a0a0a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',Arial,sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0!important;text-align:left;visibility:hidden;width:0"></th> @@ -822,7 +850,9 @@ Go to TestCloud: https://example.com/ Install Client: https://nextcloud.com/install/#install-clients --- +EOF; + $expectedTextBody .= "\n-- \n"; + $expectedTextBody .= <<<EOF TestCloud This is an automatically sent email, please do not reply. EOF; @@ -833,34 +863,38 @@ EOF; $this->assertSame('OC\Mail\EMailTemplate', get_class($result)); } - public function testSendMail() { - /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */ + public function testSendMail(): void { + /** @var IUser&MockObject $user */ $user = $this->createMock(IUser::class); $user - ->expects($this->at(0)) + ->expects($this->once()) ->method('getEMailAddress') ->willReturn('recipient@example.com'); $user - ->expects($this->at(1)) + ->expects($this->once()) ->method('getDisplayName') ->willReturn('John Doe'); - /** @var IEMailTemplate|\PHPUnit\Framework\MockObject\MockObject $emailTemplate */ + /** @var IEMailTemplate&MockObject $emailTemplate */ $emailTemplate = $this->createMock(IEMailTemplate::class); $message = $this->createMock(Message::class); $message - ->expects($this->at(0)) + ->expects($this->once()) ->method('setTo') ->with(['recipient@example.com' => 'John Doe']); $message - ->expects($this->at(1)) + ->expects($this->once()) ->method('setFrom') ->with(['no-reply@nextcloud.com' => 'TestCloud']); $message - ->expects($this->at(2)) + ->expects($this->once()) ->method('useTemplate') ->with($emailTemplate); + $message + ->expects($this->once()) + ->method('setAutoSubmitted') + ->with(AutoSubmitted::VALUE_AUTO_GENERATED); $this->defaults - ->expects($this->exactly(1)) + ->expects($this->once()) ->method('getName') ->willReturn('TestCloud'); $this->mailer diff --git a/apps/settings/tests/Middleware/SubadminMiddlewareTest.php b/apps/settings/tests/Middleware/SubadminMiddlewareTest.php index a6317173439..37cfb5ccc59 100644 --- a/apps/settings/tests/Middleware/SubadminMiddlewareTest.php +++ b/apps/settings/tests/Middleware/SubadminMiddlewareTest.php @@ -1,29 +1,13 @@ <?php + +declare(strict_types=1); + /** - * @copyright 2014 Lukas Reschke lukas@owncloud.com - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2014 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ + namespace OCA\Settings\Tests\Middleware; use OC\AppFramework\Middleware\Security\Exceptions\NotAdminException; @@ -31,7 +15,11 @@ use OC\AppFramework\Utility\ControllerMethodReflector; use OCA\Settings\Middleware\SubadminMiddleware; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\TemplateResponse; +use OCP\Group\ISubAdmin; use OCP\IL10N; +use OCP\IUser; +use OCP\IUserSession; +use PHPUnit\Framework\MockObject\MockObject; /** * Verifies whether an user has at least subadmin rights. @@ -40,87 +28,95 @@ use OCP\IL10N; * @package Tests\Settings\Middleware */ class SubadminMiddlewareTest extends \Test\TestCase { - /** @var SubadminMiddleware */ - private $subadminMiddlewareAsSubAdmin; - /** @var SubadminMiddleware */ - private $subadminMiddleware; - /** @var ControllerMethodReflector */ - private $reflector; - /** @var Controller */ - private $controller; - /** @var IL10N */ - private $l10n; + private SubadminMiddleware $subadminMiddleware; + private IUserSession&MockObject $userSession; + private ISubAdmin&MockObject $subAdminManager; + private ControllerMethodReflector&MockObject $reflector; + private Controller&MockObject $controller; + private IL10N&MockObject $l10n; protected function setUp(): void { parent::setUp(); - $this->reflector = $this->getMockBuilder(ControllerMethodReflector::class) - ->disableOriginalConstructor()->getMock(); - $this->controller = $this->getMockBuilder(Controller::class) - ->disableOriginalConstructor()->getMock(); + $this->reflector = $this->createMock(ControllerMethodReflector::class); + $this->userSession = $this->createMock(IUserSession::class); + $this->subAdminManager = $this->createMock(ISubAdmin::class); $this->l10n = $this->createMock(IL10N::class); - $this->subadminMiddlewareAsSubAdmin = new SubadminMiddleware($this->reflector, true, $this->l10n); - $this->subadminMiddleware = new SubadminMiddleware($this->reflector, false, $this->l10n); + $this->subadminMiddleware = new SubadminMiddleware( + $this->reflector, + $this->userSession, + $this->subAdminManager, + $this->l10n, + ); + + $this->controller = $this->createMock(Controller::class); + + $this->userSession + ->expects(self::any()) + ->method('getUser') + ->willReturn($this->createMock(IUser::class)); } - public function testBeforeControllerAsUserWithExemption() { - $this->expectException(\OC\AppFramework\Middleware\Security\Exceptions\NotAdminException::class); + public function testBeforeControllerAsUserWithoutAnnotation(): void { + $this->expectException(NotAdminException::class); $this->reflector - ->expects($this->at(0)) - ->method('hasAnnotation') - ->with('NoSubAdminRequired') - ->willReturn(false); - $this->reflector - ->expects($this->at(1)) + ->expects($this->exactly(2)) ->method('hasAnnotation') - ->with('AuthorizedAdminSetting') + ->willReturnMap([ + ['NoSubAdminRequired', false], + ['AuthorizedAdminSetting', false], + ]); + + $this->subAdminManager + ->expects(self::once()) + ->method('isSubAdmin') ->willReturn(false); + $this->subadminMiddleware->beforeController($this->controller, 'foo'); } - public function testBeforeControllerAsUserWithoutExemption() { + public function testBeforeControllerWithAnnotation(): void { $this->reflector ->expects($this->once()) ->method('hasAnnotation') ->with('NoSubAdminRequired') ->willReturn(true); + + $this->subAdminManager + ->expects(self::never()) + ->method('isSubAdmin'); + $this->subadminMiddleware->beforeController($this->controller, 'foo'); } - public function testBeforeControllerAsSubAdminWithoutExemption() { - $this->reflector - ->expects($this->at(0)) - ->method('hasAnnotation') - ->with('NoSubAdminRequired') - ->willReturn(false); + public function testBeforeControllerAsSubAdminWithoutAnnotation(): void { $this->reflector - ->expects($this->at(1)) + ->expects($this->exactly(2)) ->method('hasAnnotation') - ->with('AuthorizedAdminSetting') - ->willReturn(false); - $this->subadminMiddlewareAsSubAdmin->beforeController($this->controller, 'foo'); - } + ->willReturnMap([ + ['NoSubAdminRequired', false], + ['AuthorizedAdminSetting', false], + ]); - public function testBeforeControllerAsSubAdminWithExemption() { - $this->reflector - ->expects($this->once()) - ->method('hasAnnotation') - ->with('NoSubAdminRequired') + $this->subAdminManager + ->expects(self::once()) + ->method('isSubAdmin') ->willReturn(true); - $this->subadminMiddlewareAsSubAdmin->beforeController($this->controller, 'foo'); + + $this->subadminMiddleware->beforeController($this->controller, 'foo'); } - public function testAfterNotAdminException() { + public function testAfterNotAdminException(): void { $expectedResponse = new TemplateResponse('core', '403', [], 'guest'); $expectedResponse->setStatus(403); $this->assertEquals($expectedResponse, $this->subadminMiddleware->afterException($this->controller, 'foo', new NotAdminException(''))); } - public function testAfterRegularException() { + public function testAfterRegularException(): void { $this->expectException(\Exception::class); $expectedResponse = new TemplateResponse('core', '403', [], 'guest'); diff --git a/apps/settings/tests/Settings/Admin/MailTest.php b/apps/settings/tests/Settings/Admin/MailTest.php index 5fd3f1600c5..992c7d43dba 100644 --- a/apps/settings/tests/Settings/Admin/MailTest.php +++ b/apps/settings/tests/Settings/Admin/MailTest.php @@ -1,51 +1,29 @@ <?php + /** - * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\Settings\Tests\Settings\Admin; use OCA\Settings\Settings\Admin\Mail; use OCP\AppFramework\Http\TemplateResponse; +use OCP\IBinaryFinder; use OCP\IConfig; use OCP\IL10N; +use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; class MailTest extends TestCase { - /** @var Mail */ - private $admin; - /** @var IConfig */ - private $config; - /** @var IL10N */ - private $l10n; + + private Mail $admin; + private IConfig&MockObject $config; + private IL10N&MockObject $l10n; protected function setUp(): void { parent::setUp(); - $this->config = $this->getMockBuilder(IConfig::class)->getMock(); - $this->l10n = $this->getMockBuilder(IL10N::class)->getMock(); + $this->config = $this->createMock(IConfig::class); + $this->l10n = $this->createMock(IL10N::class); $this->admin = new Mail( $this->config, @@ -53,75 +31,49 @@ class MailTest extends TestCase { ); } - public function testGetForm() { - $this->config - ->expects($this->at(0)) - ->method('getSystemValue') - ->with('mail_domain', '') - ->willReturn('mx.nextcloud.com'); - $this->config - ->expects($this->at(1)) - ->method('getSystemValue') - ->with('mail_from_address', '') - ->willReturn('no-reply@nextcloud.com'); - $this->config - ->expects($this->at(2)) - ->method('getSystemValue') - ->with('mail_smtpmode', '') - ->willReturn('smtp'); - $this->config - ->expects($this->at(3)) - ->method('getSystemValue') - ->with('mail_smtpsecure', '') - ->willReturn(true); - $this->config - ->expects($this->at(4)) - ->method('getSystemValue') - ->with('mail_smtphost', '') - ->willReturn('smtp.nextcloud.com'); - $this->config - ->expects($this->at(5)) - ->method('getSystemValue') - ->with('mail_smtpport', '') - ->willReturn(25); - $this->config - ->expects($this->at(6)) - ->method('getSystemValue') - ->with('mail_smtpauthtype', '') - ->willReturn('login'); - $this->config - ->expects($this->at(7)) - ->method('getSystemValue') - ->with('mail_smtpauth', false) - ->willReturn(true); - $this->config - ->expects($this->at(8)) - ->method('getSystemValue') - ->with('mail_smtpname', '') - ->willReturn('smtp.sender.com'); - $this->config - ->expects($this->at(9)) - ->method('getSystemValue') - ->with('mail_smtppassword', '') - ->willReturn('mypassword'); + public static function dataGetForm(): array { + return [ + [true], + [false], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataGetForm')] + public function testGetForm(bool $sendmail) { + $finder = $this->createMock(IBinaryFinder::class); + $finder->expects(self::once()) + ->method('findBinaryPath') + ->with('sendmail') + ->willReturn($sendmail ? '/usr/bin/sendmail': false); + $this->overwriteService(IBinaryFinder::class, $finder); + $this->config - ->expects($this->at(10)) + ->expects($this->any()) ->method('getSystemValue') - ->with('mail_sendmailmode', 'smtp') - ->willReturn('smtp'); + ->willReturnMap([ + ['mail_domain', '', 'mx.nextcloud.com'], + ['mail_from_address', '', 'no-reply@nextcloud.com'], + ['mail_smtpmode', '', 'smtp'], + ['mail_smtpsecure', '', true], + ['mail_smtphost', '', 'smtp.nextcloud.com'], + ['mail_smtpport', '', 25], + ['mail_smtpauth', false, true], + ['mail_smtpname', '', 'smtp.sender.com'], + ['mail_smtppassword', '', 'mypassword'], + ['mail_sendmailmode', 'smtp', 'smtp'], + ]); $expected = new TemplateResponse( 'settings', 'settings/admin/additional-mail', [ - 'sendmail_is_available' => (bool) \OC_Helper::findBinaryPath('sendmail'), + 'sendmail_is_available' => $sendmail, 'mail_domain' => 'mx.nextcloud.com', 'mail_from_address' => 'no-reply@nextcloud.com', 'mail_smtpmode' => 'smtp', 'mail_smtpsecure' => true, 'mail_smtphost' => 'smtp.nextcloud.com', 'mail_smtpport' => 25, - 'mail_smtpauthtype' => 'login', 'mail_smtpauth' => true, 'mail_smtpname' => 'smtp.sender.com', 'mail_smtppassword' => '********', @@ -133,11 +85,11 @@ class MailTest extends TestCase { $this->assertEquals($expected, $this->admin->getForm()); } - public function testGetSection() { + public function testGetSection(): void { $this->assertSame('server', $this->admin->getSection()); } - public function testGetPriority() { + public function testGetPriority(): void { $this->assertSame(10, $this->admin->getPriority()); } } diff --git a/apps/settings/tests/Settings/Admin/SecurityTest.php b/apps/settings/tests/Settings/Admin/SecurityTest.php index da4ce62d9cd..89a6d8c0d88 100644 --- a/apps/settings/tests/Settings/Admin/SecurityTest.php +++ b/apps/settings/tests/Settings/Admin/SecurityTest.php @@ -1,29 +1,8 @@ <?php + /** - * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\Settings\Tests\Settings\Admin; @@ -32,26 +11,22 @@ use OC\Encryption\Manager; use OCA\Settings\Settings\Admin\Security; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Services\IInitialState; +use OCP\IURLGenerator; use OCP\IUserManager; use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; class SecurityTest extends TestCase { - /** @var Security */ - private $admin; - /** @var Manager */ - private $manager; - /** @var IUserManager */ - private $userManager; - /** @var MandatoryTwoFactor|MockObject */ - private $mandatoryTwoFactor; - /** @var IInitialState|MockObject */ - private $initialState; + private Manager $manager; + private IUserManager $userManager; + private MandatoryTwoFactor&MockObject $mandatoryTwoFactor; + private IInitialState&MockObject $initialState; + private Security $admin; protected function setUp(): void { parent::setUp(); - $this->manager = $this->getMockBuilder(Manager::class)->disableOriginalConstructor()->getMock(); - $this->userManager = $this->getMockBuilder(IUserManager::class)->getMock(); + $this->manager = $this->createMock(Manager::class); + $this->userManager = $this->createMock(IUserManager::class); $this->mandatoryTwoFactor = $this->createMock(MandatoryTwoFactor::class); $this->initialState = $this->createMock(IInitialState::class); @@ -59,25 +34,20 @@ class SecurityTest extends TestCase { $this->manager, $this->userManager, $this->mandatoryTwoFactor, - $this->initialState + $this->initialState, + $this->createMock(IURLGenerator::class) ); } - /** - * @return array - */ - public function encryptionSettingsProvider() { + public static function encryptionSettingsProvider(): array { return [ [true], [false], ]; } - /** - * @dataProvider encryptionSettingsProvider - * @param bool $enabled - */ - public function testGetFormWithOnlyOneBackend($enabled) { + #[\PHPUnit\Framework\Attributes\DataProvider('encryptionSettingsProvider')] + public function testGetFormWithOnlyOneBackend(bool $enabled): void { $this->manager ->expects($this->once()) ->method('isEnabled') @@ -97,22 +67,17 @@ class SecurityTest extends TestCase { $expected = new TemplateResponse( 'settings', 'settings/admin/security', - [ - 'encryptionEnabled' => $enabled, - 'encryptionReady' => $enabled, - 'externalBackendsEnabled' => false, - 'encryptionModules' => [] - ], + [], '' ); $this->assertEquals($expected, $this->admin->getForm()); } /** - * @dataProvider encryptionSettingsProvider * @param bool $enabled */ - public function testGetFormWithMultipleBackends($enabled) { + #[\PHPUnit\Framework\Attributes\DataProvider('encryptionSettingsProvider')] + public function testGetFormWithMultipleBackends($enabled): void { $this->manager ->expects($this->once()) ->method('isEnabled') @@ -132,22 +97,17 @@ class SecurityTest extends TestCase { $expected = new TemplateResponse( 'settings', 'settings/admin/security', - [ - 'encryptionEnabled' => $enabled, - 'encryptionReady' => $enabled, - 'externalBackendsEnabled' => true, - 'encryptionModules' => [] - ], + [ ], '' ); $this->assertEquals($expected, $this->admin->getForm()); } - public function testGetSection() { + public function testGetSection(): void { $this->assertSame('security', $this->admin->getSection()); } - public function testGetPriority() { + public function testGetPriority(): void { $this->assertSame(10, $this->admin->getPriority()); } } diff --git a/apps/settings/tests/Settings/Admin/ServerTest.php b/apps/settings/tests/Settings/Admin/ServerTest.php index acabf9abf5e..e2ca4cff3c6 100644 --- a/apps/settings/tests/Settings/Admin/ServerTest.php +++ b/apps/settings/tests/Settings/Admin/ServerTest.php @@ -3,31 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\Settings\Tests\Settings\Admin; @@ -36,9 +13,11 @@ use OCA\Settings\Settings\Admin\Server; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Services\IInitialState; use OCP\AppFramework\Utility\ITimeFactory; +use OCP\IAppConfig; use OCP\IConfig; use OCP\IDBConnection; use OCP\IL10N; +use OCP\IUrlGenerator; use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; @@ -46,29 +25,26 @@ use Test\TestCase; * @group DB */ class ServerTest extends TestCase { - /** @var Server */ - private $admin; - /** @var IDBConnection */ - private $connection; - /** @var IInitialState */ - private $initialStateService; - /** @var ProfileManager */ - private $profileManager; - /** @var ITimeFactory|MockObject */ - private $timeFactory; - /** @var IConfig|MockObject */ - private $config; - /** @var IL10N|MockObject */ - private $l10n; + private IDBConnection $connection; + private Server&MockObject $admin; + private IInitialState&MockObject $initialStateService; + private ProfileManager&MockObject $profileManager; + private ITimeFactory&MockObject $timeFactory; + private IConfig&MockObject $config; + private IAppConfig&MockObject $appConfig; + private IL10N&MockObject $l10n; + private IUrlGenerator&MockObject $urlGenerator; protected function setUp(): void { parent::setUp(); - $this->connection = \OC::$server->getDatabaseConnection(); + $this->connection = \OCP\Server::get(IDBConnection::class); $this->initialStateService = $this->createMock(IInitialState::class); $this->profileManager = $this->createMock(ProfileManager::class); $this->timeFactory = $this->createMock(ITimeFactory::class); $this->config = $this->createMock(IConfig::class); + $this->appConfig = $this->createMock(IAppConfig::class); $this->l10n = $this->createMock(IL10N::class); + $this->urlGenerator = $this->createMock(IUrlGenerator::class); $this->admin = $this->getMockBuilder(Server::class) ->onlyMethods(['cronMaxAge']) @@ -77,7 +53,9 @@ class ServerTest extends TestCase { $this->initialStateService, $this->profileManager, $this->timeFactory, + $this->urlGenerator, $this->config, + $this->appConfig, $this->l10n, ]) ->getMock(); @@ -88,20 +66,20 @@ class ServerTest extends TestCase { ->method('cronMaxAge') ->willReturn(1337); $this->config - ->expects($this->at(0)) + ->expects($this->any()) ->method('getAppValue') - ->with('core', 'backgroundjobs_mode', 'ajax') - ->willReturn('ajax'); - $this->config - ->expects($this->at(1)) - ->method('getAppValue') - ->with('core', 'lastcron', false) - ->willReturn(false); - $this->config - ->expects($this->at(2)) - ->method('getAppValue') - ->with('core', 'cronErrors') - ->willReturn(''); + ->willReturnMap([ + ['core', 'lastcron', '0', '0'], + ['core', 'cronErrors', ''], + ]); + $this->appConfig + ->expects($this->any()) + ->method('getValueString') + ->willReturnCallback(fn ($a, $b, $default) => $default); + $this->appConfig + ->expects($this->any()) + ->method('getValueBool') + ->willReturnCallback(fn ($a, $b, $default) => $default); $this->profileManager ->expects($this->exactly(2)) ->method('isProfileEnabled') @@ -110,12 +88,6 @@ class ServerTest extends TestCase { 'settings', 'settings/admin/server', [ - 'backgroundjobs_mode' => 'ajax', - 'lastcron' => false, - 'cronErrors' => '', - 'cronMaxAge' => 1337, - 'cli_based_cron_possible' => true, - 'cli_based_cron_user' => function_exists('posix_getpwuid') ? posix_getpwuid(fileowner(\OC::$configDir . 'config.php'))['name'] : '', // to not explode here because of posix extension not being disabled - which is already checked in the line above 'profileEnabledGlobally' => true, ], '' diff --git a/apps/settings/tests/Settings/Admin/SharingTest.php b/apps/settings/tests/Settings/Admin/SharingTest.php index e0693b4f963..f37ade2171f 100644 --- a/apps/settings/tests/Settings/Admin/SharingTest.php +++ b/apps/settings/tests/Settings/Admin/SharingTest.php @@ -1,75 +1,66 @@ <?php + /** - * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vincent Petry <vincent@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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\Settings\Tests\Settings\Admin; use OCA\Settings\Settings\Admin\Sharing; use OCP\App\IAppManager; use OCP\AppFramework\Http\TemplateResponse; +use OCP\AppFramework\Services\IInitialState; use OCP\Constants; +use OCP\IAppConfig; use OCP\IConfig; use OCP\IL10N; +use OCP\IURLGenerator; use OCP\Share\IManager; use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; class SharingTest extends TestCase { - /** @var Sharing */ - private $admin; - /** @var IConfig */ - private $config; - /** @var IL10N|MockObject */ - private $l10n; - /** @var IManager|MockObject */ - private $shareManager; - /** @var IAppManager|MockObject */ - private $appManager; + private Sharing $admin; + + private IConfig&MockObject $config; + private IAppConfig&MockObject $appConfig; + private IL10N&MockObject $l10n; + private IManager&MockObject $shareManager; + private IAppManager&MockObject $appManager; + private IURLGenerator&MockObject $urlGenerator; + private IInitialState&MockObject $initialState; protected function setUp(): void { parent::setUp(); - $this->config = $this->getMockBuilder(IConfig::class)->getMock(); - $this->l10n = $this->getMockBuilder(IL10N::class)->getMock(); + $this->config = $this->createMock(IConfig::class); + $this->appConfig = $this->createMock(IAppConfig::class); + $this->l10n = $this->createMock(IL10N::class); - $this->shareManager = $this->getMockBuilder(IManager::class)->getMock(); - $this->appManager = $this->getMockBuilder(IAppManager::class)->getMock(); + $this->shareManager = $this->createMock(IManager::class); + $this->appManager = $this->createMock(IAppManager::class); + $this->urlGenerator = $this->createMock(IURLGenerator::class); + $this->initialState = $this->createMock(IInitialState::class); $this->admin = new Sharing( $this->config, + $this->appConfig, $this->l10n, $this->shareManager, - $this->appManager + $this->appManager, + $this->urlGenerator, + $this->initialState, + 'settings', ); } public function testGetFormWithoutExcludedGroups(): void { + $this->appConfig + ->method('getValueBool') + ->willReturnMap([ + ['core', 'shareapi_allow_federation_on_public_shares', true], + ['core', 'shareapi_enable_link_password_by_default', true], + ]); + $this->config ->method('getAppValue') ->willReturnMap([ @@ -84,68 +75,86 @@ class SharingTest extends TestCase { ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'], ['core', 'shareapi_restrict_user_enumeration_full_match', 'yes', 'yes'], ['core', 'shareapi_restrict_user_enumeration_full_match_userid', 'yes', 'yes'], - ['core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_display_name', 'no', 'no'], + ['core', 'shareapi_restrict_user_enumeration_full_match_email', 'yes', 'yes'], + ['core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn', 'no', 'no'], ['core', 'shareapi_enabled', 'yes', 'yes'], ['core', 'shareapi_default_expire_date', 'no', 'no'], ['core', 'shareapi_expire_after_n_days', '7', '7'], ['core', 'shareapi_enforce_expire_date', 'no', 'no'], ['core', 'shareapi_exclude_groups', 'no', 'no'], - ['core', 'shareapi_public_link_disclaimertext', null, 'Lorem ipsum'], - ['core', 'shareapi_enable_link_password_by_default', 'no', 'yes'], - ['core', 'shareapi_default_permissions', Constants::PERMISSION_ALL, Constants::PERMISSION_ALL], + ['core', 'shareapi_public_link_disclaimertext', '', 'Lorem ipsum'], + ['core', 'shareapi_default_permissions', (string)Constants::PERMISSION_ALL, Constants::PERMISSION_ALL], ['core', 'shareapi_default_internal_expire_date', 'no', 'no'], ['core', 'shareapi_internal_expire_after_n_days', '7', '7'], ['core', 'shareapi_enforce_internal_expire_date', 'no', 'no'], ['core', 'shareapi_default_remote_expire_date', 'no', 'no'], ['core', 'shareapi_remote_expire_after_n_days', '7', '7'], ['core', 'shareapi_enforce_remote_expire_date', 'no', 'no'], + ['core', 'shareapi_enforce_links_password_excluded_groups', '', ''], + ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'], ]); $this->shareManager->method('shareWithGroupMembersOnly') ->willReturn(false); $this->appManager->method('isEnabledForUser')->with('files_sharing')->willReturn(false); + $initialStateCalls = []; + $this->initialState + ->expects($this->exactly(3)) + ->method('provideInitialState') + ->willReturnCallback(function (string $key) use (&$initialStateCalls): void { + $initialStateCalls[$key] = func_get_args(); + }); + + $expectedInitialStateCalls = [ + 'sharingAppEnabled' => false, + 'sharingDocumentation' => '', + 'sharingSettings' => [ + 'allowGroupSharing' => true, + 'allowLinks' => true, + 'allowPublicUpload' => true, + 'allowResharing' => true, + 'allowShareDialogUserEnumeration' => true, + 'allowFederationOnPublicShares' => true, + 'restrictUserEnumerationToGroup' => false, + 'restrictUserEnumerationToPhone' => false, + 'restrictUserEnumerationFullMatch' => true, + 'restrictUserEnumerationFullMatchUserId' => true, + 'restrictUserEnumerationFullMatchEmail' => true, + 'restrictUserEnumerationFullMatchIgnoreSecondDN' => false, + 'enforceLinksPassword' => false, + 'onlyShareWithGroupMembers' => false, + 'enabled' => true, + 'defaultExpireDate' => false, + 'expireAfterNDays' => '7', + 'enforceExpireDate' => false, + 'excludeGroups' => 'no', + 'excludeGroupsList' => [], + 'publicShareDisclaimerText' => 'Lorem ipsum', + 'enableLinkPasswordByDefault' => true, + 'defaultPermissions' => Constants::PERMISSION_ALL, + 'defaultInternalExpireDate' => false, + 'internalExpireAfterNDays' => '7', + 'enforceInternalExpireDate' => false, + 'defaultRemoteExpireDate' => false, + 'remoteExpireAfterNDays' => '7', + 'enforceRemoteExpireDate' => false, + 'allowLinksExcludeGroups' => [], + 'onlyShareWithGroupMembersExcludeGroupList' => [], + 'enforceLinksPasswordExcludedGroups' => [], + 'enforceLinksPasswordExcludedGroupsEnabled' => false, + ] + ]; + $expected = new TemplateResponse( 'settings', 'settings/admin/sharing', - [ - 'sharingAppEnabled' => false, - 'allowGroupSharing' => 'yes', - 'allowLinks' => 'yes', - 'allowPublicUpload' => 'yes', - 'allowResharing' => 'yes', - 'allowShareDialogUserEnumeration' => 'yes', - 'restrictUserEnumerationToGroup' => 'no', - 'restrictUserEnumerationToPhone' => 'no', - 'restrictUserEnumerationFullMatch' => 'yes', - 'restrictUserEnumerationFullMatchUserId' => 'yes', - 'restrictUserEnumerationFullMatchIgnoreSecondDisplayName' => 'no', - 'enforceLinkPassword' => false, - 'onlyShareWithGroupMembers' => false, - 'shareAPIEnabled' => 'yes', - 'shareDefaultExpireDateSet' => 'no', - 'shareExpireAfterNDays' => '7', - 'shareEnforceExpireDate' => 'no', - 'shareExcludeGroups' => false, - 'shareExcludedGroupsList' => '', - 'publicShareDisclaimerText' => 'Lorem ipsum', - 'enableLinkPasswordByDefault' => 'yes', - 'shareApiDefaultPermissions' => Constants::PERMISSION_ALL, - 'shareApiDefaultPermissionsCheckboxes' => self::invokePrivate($this->admin, 'getSharePermissionList', []), - 'shareDefaultInternalExpireDateSet' => 'no', - 'shareInternalExpireAfterNDays' => '7', - 'shareInternalEnforceExpireDate' => 'no', - 'shareDefaultRemoteExpireDateSet' => 'no', - 'shareRemoteExpireAfterNDays' => '7', - 'shareRemoteEnforceExpireDate' => 'no', - 'allowLinksExcludeGroups' => '', - 'passwordExcludedGroups' => '', - 'passwordExcludedGroupsFeatureEnabled' => false, - ], + [], '' ); $this->assertEquals($expected, $this->admin->getForm()); + $this->assertEquals(sort($expectedInitialStateCalls), sort($initialStateCalls), 'Provided initial state does not match'); } public function testGetFormWithExcludedGroups(): void { @@ -163,68 +172,86 @@ class SharingTest extends TestCase { ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'], ['core', 'shareapi_restrict_user_enumeration_full_match', 'yes', 'yes'], ['core', 'shareapi_restrict_user_enumeration_full_match_userid', 'yes', 'yes'], - ['core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_display_name', 'no', 'no'], + ['core', 'shareapi_restrict_user_enumeration_full_match_email', 'yes', 'yes'], + ['core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn', 'no', 'no'], ['core', 'shareapi_enabled', 'yes', 'yes'], ['core', 'shareapi_default_expire_date', 'no', 'no'], ['core', 'shareapi_expire_after_n_days', '7', '7'], ['core', 'shareapi_enforce_expire_date', 'no', 'no'], ['core', 'shareapi_exclude_groups', 'no', 'yes'], - ['core', 'shareapi_public_link_disclaimertext', null, 'Lorem ipsum'], + ['core', 'shareapi_public_link_disclaimertext', '', 'Lorem ipsum'], ['core', 'shareapi_enable_link_password_by_default', 'no', 'yes'], - ['core', 'shareapi_default_permissions', Constants::PERMISSION_ALL, Constants::PERMISSION_ALL], + ['core', 'shareapi_default_permissions', (string)Constants::PERMISSION_ALL, Constants::PERMISSION_ALL], ['core', 'shareapi_default_internal_expire_date', 'no', 'no'], ['core', 'shareapi_internal_expire_after_n_days', '7', '7'], ['core', 'shareapi_enforce_internal_expire_date', 'no', 'no'], ['core', 'shareapi_default_remote_expire_date', 'no', 'no'], ['core', 'shareapi_remote_expire_after_n_days', '7', '7'], ['core', 'shareapi_enforce_remote_expire_date', 'no', 'no'], + ['core', 'shareapi_enforce_links_password_excluded_groups', '', ''], + ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'], ]); $this->shareManager->method('shareWithGroupMembersOnly') ->willReturn(false); $this->appManager->method('isEnabledForUser')->with('files_sharing')->willReturn(true); - $expected = new TemplateResponse( - 'settings', - 'settings/admin/sharing', - [ - 'sharingAppEnabled' => true, - 'allowGroupSharing' => 'yes', - 'allowLinks' => 'yes', - 'allowPublicUpload' => 'yes', - 'allowResharing' => 'yes', - 'allowShareDialogUserEnumeration' => 'yes', - 'restrictUserEnumerationToGroup' => 'no', - 'restrictUserEnumerationToPhone' => 'no', - 'restrictUserEnumerationFullMatch' => 'yes', - 'restrictUserEnumerationFullMatchUserId' => 'yes', - 'restrictUserEnumerationFullMatchIgnoreSecondDisplayName' => 'no', - 'enforceLinkPassword' => false, + $initialStateCalls = []; + $this->initialState + ->expects($this->exactly(3)) + ->method('provideInitialState') + ->willReturnCallback(function (string $key) use (&$initialStateCalls): void { + $initialStateCalls[$key] = func_get_args(); + }); + + $expectedInitialStateCalls = [ + 'sharingAppEnabled' => true, + 'sharingDocumentation' => '', + 'sharingSettings' => [ + 'allowGroupSharing' => true, + 'allowLinks' => true, + 'allowPublicUpload' => true, + 'allowResharing' => true, + 'allowShareDialogUserEnumeration' => true, + 'restrictUserEnumerationToGroup' => false, + 'restrictUserEnumerationToPhone' => false, + 'restrictUserEnumerationFullMatch' => true, + 'restrictUserEnumerationFullMatchUserId' => true, + 'restrictUserEnumerationFullMatchEmail' => true, + 'restrictUserEnumerationFullMatchIgnoreSecondDN' => false, + 'enforceLinksPassword' => false, 'onlyShareWithGroupMembers' => false, - 'shareAPIEnabled' => 'yes', - 'shareDefaultExpireDateSet' => 'no', - 'shareExpireAfterNDays' => '7', - 'shareEnforceExpireDate' => 'no', - 'shareExcludeGroups' => true, - 'shareExcludedGroupsList' => 'NoSharers|OtherNoSharers', + 'enabled' => true, + 'defaultExpireDate' => false, + 'expireAfterNDays' => '7', + 'enforceExpireDate' => false, + 'excludeGroups' => 'yes', + 'excludeGroupsList' => ['NoSharers','OtherNoSharers'], 'publicShareDisclaimerText' => 'Lorem ipsum', - 'enableLinkPasswordByDefault' => 'yes', - 'shareApiDefaultPermissions' => Constants::PERMISSION_ALL, - 'shareApiDefaultPermissionsCheckboxes' => self::invokePrivate($this->admin, 'getSharePermissionList', []), - 'shareDefaultInternalExpireDateSet' => 'no', - 'shareInternalExpireAfterNDays' => '7', - 'shareInternalEnforceExpireDate' => 'no', - 'shareDefaultRemoteExpireDateSet' => 'no', - 'shareRemoteExpireAfterNDays' => '7', - 'shareRemoteEnforceExpireDate' => 'no', - 'allowLinksExcludeGroups' => '', - 'passwordExcludedGroups' => '', - 'passwordExcludedGroupsFeatureEnabled' => false, + 'enableLinkPasswordByDefault' => true, + 'defaultPermissions' => Constants::PERMISSION_ALL, + 'defaultInternalExpireDate' => false, + 'internalExpireAfterNDays' => '7', + 'enforceInternalExpireDate' => false, + 'defaultRemoteExpireDate' => false, + 'remoteExpireAfterNDays' => '7', + 'enforceRemoteExpireDate' => false, + 'allowLinksExcludeGroups' => [], + 'onlyShareWithGroupMembersExcludeGroupList' => [], + 'enforceLinksPasswordExcludedGroups' => [], + 'enforceLinksPasswordExcludedGroupsEnabled' => false, ], + ]; + + $expected = new TemplateResponse( + 'settings', + 'settings/admin/sharing', + [], '' ); $this->assertEquals($expected, $this->admin->getForm()); + $this->assertEquals(sort($expectedInitialStateCalls), sort($initialStateCalls), 'Provided initial state does not match'); } public function testGetSection(): void { diff --git a/apps/settings/tests/Settings/Personal/Security/AuthtokensTest.php b/apps/settings/tests/Settings/Personal/Security/AuthtokensTest.php index 8fae0a44d8f..0a0ff4d84af 100644 --- a/apps/settings/tests/Settings/Personal/Security/AuthtokensTest.php +++ b/apps/settings/tests/Settings/Personal/Security/AuthtokensTest.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\Settings\Tests\Settings\Personal\Security; @@ -30,30 +13,19 @@ use OC\Authentication\Token\PublicKeyToken; use OCA\Settings\Settings\Personal\Security\Authtokens; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Services\IInitialState; +use OCP\Authentication\Token\IToken; use OCP\ISession; use OCP\IUserSession; use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; class AuthtokensTest extends TestCase { - - /** @var IAuthTokenProvider|MockObject */ - private $authTokenProvider; - - /** @var ISession|MockObject */ - private $session; - - /** @var IUserSession|MockObject */ - private $userSession; - - /** @var IInitialState|MockObject */ - private $initialState; - - /** @var string */ - private $uid; - - /** @var Authtokens */ - private $section; + private IAuthTokenProvider&MockObject $authTokenProvider; + private ISession&MockObject $session; + private IUserSession&MockObject $userSession; + private IInitialState&MockObject $initialState; + private string $uid; + private Authtokens $section; protected function setUp(): void { parent::setUp(); @@ -73,7 +45,7 @@ class AuthtokensTest extends TestCase { ); } - public function testGetForm() { + public function testGetForm(): void { $token1 = new PublicKeyToken(); $token1->setId(100); $token2 = new PublicKeyToken(); @@ -96,33 +68,39 @@ class AuthtokensTest extends TestCase { ->method('getToken') ->with('session123') ->willReturn($sessionToken); - $this->initialState->expects($this->at(0)) - ->method('provideInitialState') - ->with('app_tokens', [ - [ - 'id' => 100, - 'name' => null, - 'lastActivity' => 0, - 'type' => 0, - 'canDelete' => false, - 'current' => true, - 'scope' => ['filesystem' => true], - 'canRename' => false, - ], - [ - 'id' => 200, - 'name' => null, - 'lastActivity' => 0, - 'type' => 0, - 'canDelete' => true, - 'scope' => ['filesystem' => true], - 'canRename' => true, - ], - ]); - $this->initialState->expects($this->at(1)) + $calls = [ + [ + 'app_tokens', [ + [ + 'id' => 100, + 'name' => null, + 'lastActivity' => 0, + 'type' => 0, + 'canDelete' => false, + 'current' => true, + 'scope' => [IToken::SCOPE_FILESYSTEM => true], + 'canRename' => false, + ], + [ + 'id' => 200, + 'name' => null, + 'lastActivity' => 0, + 'type' => 0, + 'canDelete' => true, + 'scope' => [IToken::SCOPE_FILESYSTEM => true], + 'canRename' => true, + ], + ] + ], + ['can_create_app_token', true], + ]; + $this->initialState->expects($this->exactly(2)) ->method('provideInitialState') - ->with('can_create_app_token', true); + ->willReturnCallback(function () use (&$calls): void { + $expected = array_shift($calls); + $this->assertEquals($expected, func_get_args()); + }); $form = $this->section->getForm(); diff --git a/apps/settings/tests/Settings/Personal/Security/PasswordTest.php b/apps/settings/tests/Settings/Personal/Security/PasswordTest.php index 2db1effd660..34a4b8e296f 100644 --- a/apps/settings/tests/Settings/Personal/Security/PasswordTest.php +++ b/apps/settings/tests/Settings/Personal/Security/PasswordTest.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\Settings\Tests\Settings\Personal\Security; @@ -34,15 +16,9 @@ use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; class PasswordTest extends TestCase { - - /** @var IUserManager|MockObject */ - private $userManager; - - /** @var string */ - private $uid; - - /** @var Password */ - private $section; + private IUserManager&MockObject $userManager; + private string $uid; + private Password $section; protected function setUp(): void { parent::setUp(); @@ -56,7 +32,7 @@ class PasswordTest extends TestCase { ); } - public function testGetForm() { + public function testGetForm(): void { $user = $this->createMock(IUser::class); $this->userManager->expects($this->once()) ->method('get') diff --git a/apps/settings/tests/SetupChecks/AppDirsWithDifferentOwnerTest.php b/apps/settings/tests/SetupChecks/AppDirsWithDifferentOwnerTest.php new file mode 100644 index 00000000000..423f932dcf5 --- /dev/null +++ b/apps/settings/tests/SetupChecks/AppDirsWithDifferentOwnerTest.php @@ -0,0 +1,102 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Settings\Tests\SetupChecks; + +use OCA\Settings\SetupChecks\AppDirsWithDifferentOwner; +use OCP\IL10N; +use Test\TestCase; + +class AppDirsWithDifferentOwnerTest extends TestCase { + private IL10N $l10n; + private AppDirsWithDifferentOwner $check; + + /** + * Holds a list of directories created during tests. + * + * @var array + */ + private $dirsToRemove = []; + + protected function setUp(): void { + parent::setUp(); + + $this->l10n = $this->createMock(IL10N::class); + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(function ($message, array $replace) { + return vsprintf($message, $replace); + }); + $this->check = new AppDirsWithDifferentOwner( + $this->l10n, + ); + } + + /** + * Setups a temp directory and some subdirectories. + * Then calls the 'getAppDirsWithDifferentOwner' method. + * The result is expected to be empty since + * there are no directories with different owners than the current user. + * + * @return void + */ + public function testAppDirectoryOwnersOk(): void { + $tempDir = tempnam(sys_get_temp_dir(), 'apps') . 'dir'; + mkdir($tempDir); + mkdir($tempDir . DIRECTORY_SEPARATOR . 'app1'); + mkdir($tempDir . DIRECTORY_SEPARATOR . 'app2'); + $this->dirsToRemove[] = $tempDir . DIRECTORY_SEPARATOR . 'app1'; + $this->dirsToRemove[] = $tempDir . DIRECTORY_SEPARATOR . 'app2'; + $this->dirsToRemove[] = $tempDir; + \OC::$APPSROOTS = [ + [ + 'path' => $tempDir, + 'url' => '/apps', + 'writable' => true, + ], + ]; + $this->assertSame( + [], + $this->invokePrivate($this->check, 'getAppDirsWithDifferentOwner', [posix_getuid()]) + ); + } + + /** + * Calls the check for a none existing app root that is marked as not writable. + * It's expected that no error happens since the check shouldn't apply. + * + * @return void + */ + public function testAppDirectoryOwnersNotWritable(): void { + $tempDir = tempnam(sys_get_temp_dir(), 'apps') . 'dir'; + \OC::$APPSROOTS = [ + [ + 'path' => $tempDir, + 'url' => '/apps', + 'writable' => false, + ], + ]; + $this->assertSame( + [], + $this->invokePrivate($this->check, 'getAppDirsWithDifferentOwner', [posix_getuid()]) + ); + } + + /** + * Removes directories created during tests. + * + * @after + * @return void + */ + public function removeTestDirectories() { + foreach ($this->dirsToRemove as $dirToRemove) { + rmdir($dirToRemove); + } + $this->dirsToRemove = []; + } +} diff --git a/apps/settings/tests/SetupChecks/CodeIntegrityTest.php b/apps/settings/tests/SetupChecks/CodeIntegrityTest.php new file mode 100644 index 00000000000..4dd54a644f5 --- /dev/null +++ b/apps/settings/tests/SetupChecks/CodeIntegrityTest.php @@ -0,0 +1,134 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Settings\Tests\SetupChecks; + +use OC\IntegrityCheck\Checker; +use OCA\Settings\SetupChecks\CodeIntegrity; +use OCP\IL10N; +use OCP\IURLGenerator; +use OCP\SetupCheck\SetupResult; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class CodeIntegrityTest extends TestCase { + + private IL10N&MockObject $l10n; + private IURLGenerator&MockObject $urlGenerator; + private Checker&MockObject $checker; + + protected function setUp(): void { + parent::setUp(); + + $this->l10n = $this->createMock(IL10N::class); + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(function ($message, array $replace) { + return vsprintf($message, $replace); + }); + $this->urlGenerator = $this->createMock(IURLGenerator::class); + $this->checker = $this->createMock(Checker::class); + } + + public function testSkipOnDisabled(): void { + $this->checker->expects($this->atLeastOnce()) + ->method('isCodeCheckEnforced') + ->willReturn(false); + + $check = new CodeIntegrity( + $this->l10n, + $this->urlGenerator, + $this->checker, + ); + $this->assertEquals(SetupResult::INFO, $check->run()->getSeverity()); + } + + public function testSuccessOnEmptyResults(): void { + $this->checker->expects($this->atLeastOnce()) + ->method('isCodeCheckEnforced') + ->willReturn(true); + $this->checker->expects($this->atLeastOnce()) + ->method('getResults') + ->willReturn([]); + $this->checker->expects(($this->atLeastOnce())) + ->method('hasPassedCheck') + ->willReturn(true); + + $check = new CodeIntegrity( + $this->l10n, + $this->urlGenerator, + $this->checker, + ); + $this->assertEquals(SetupResult::SUCCESS, $check->run()->getSeverity()); + } + + public function testCheckerIsReRunWithoutResults(): void { + $this->checker->expects($this->atLeastOnce()) + ->method('isCodeCheckEnforced') + ->willReturn(true); + $this->checker->expects($this->atLeastOnce()) + ->method('getResults') + ->willReturn(null); + $this->checker->expects(($this->atLeastOnce())) + ->method('hasPassedCheck') + ->willReturn(true); + + // This is important and must be called + $this->checker->expects($this->once()) + ->method('runInstanceVerification'); + + $check = new CodeIntegrity( + $this->l10n, + $this->urlGenerator, + $this->checker, + ); + $this->assertEquals(SetupResult::SUCCESS, $check->run()->getSeverity()); + } + + public function testCheckerIsNotReReInAdvance(): void { + $this->checker->expects($this->atLeastOnce()) + ->method('isCodeCheckEnforced') + ->willReturn(true); + $this->checker->expects($this->atLeastOnce()) + ->method('getResults') + ->willReturn(['mocked']); + $this->checker->expects(($this->atLeastOnce())) + ->method('hasPassedCheck') + ->willReturn(true); + + // There are results thus this must never be called + $this->checker->expects($this->never()) + ->method('runInstanceVerification'); + + $check = new CodeIntegrity( + $this->l10n, + $this->urlGenerator, + $this->checker, + ); + $this->assertEquals(SetupResult::SUCCESS, $check->run()->getSeverity()); + } + + public function testErrorOnMissingIntegrity(): void { + $this->checker->expects($this->atLeastOnce()) + ->method('isCodeCheckEnforced') + ->willReturn(true); + $this->checker->expects($this->atLeastOnce()) + ->method('getResults') + ->willReturn(['mocked']); + $this->checker->expects(($this->atLeastOnce())) + ->method('hasPassedCheck') + ->willReturn(false); + + $check = new CodeIntegrity( + $this->l10n, + $this->urlGenerator, + $this->checker, + ); + $this->assertEquals(SetupResult::ERROR, $check->run()->getSeverity()); + } +} diff --git a/apps/settings/tests/SetupChecks/DataDirectoryProtectedTest.php b/apps/settings/tests/SetupChecks/DataDirectoryProtectedTest.php new file mode 100644 index 00000000000..c20c78c6e16 --- /dev/null +++ b/apps/settings/tests/SetupChecks/DataDirectoryProtectedTest.php @@ -0,0 +1,117 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Settings\Tests\SetupChecks; + +use OCA\Settings\SetupChecks\DataDirectoryProtected; +use OCP\Http\Client\IClientService; +use OCP\Http\Client\IResponse; +use OCP\IConfig; +use OCP\IL10N; +use OCP\IURLGenerator; +use OCP\SetupCheck\SetupResult; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use Test\TestCase; + +class DataDirectoryProtectedTest extends TestCase { + private IL10N&MockObject $l10n; + private IConfig&MockObject $config; + private IURLGenerator&MockObject $urlGenerator; + private IClientService&MockObject $clientService; + private LoggerInterface&MockObject $logger; + private DataDirectoryProtected&MockObject $setupcheck; + + protected function setUp(): void { + parent::setUp(); + + $this->l10n = $this->createMock(IL10N::class); + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(function ($message, array $replace) { + return vsprintf($message, $replace); + }); + + $this->config = $this->createMock(IConfig::class); + $this->urlGenerator = $this->createMock(IURLGenerator::class); + $this->clientService = $this->createMock(IClientService::class); + $this->logger = $this->createMock(LoggerInterface::class); + + $this->setupcheck = $this->getMockBuilder(DataDirectoryProtected::class) + ->onlyMethods(['runRequest']) + ->setConstructorArgs([ + $this->l10n, + $this->config, + $this->urlGenerator, + $this->clientService, + $this->logger, + ]) + ->getMock(); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestStatusCode')] + public function testStatusCode(array $status, string $expected, bool $hasBody): void { + $responses = array_map(function ($state) use ($hasBody) { + $response = $this->createMock(IResponse::class); + $response->expects($this->any())->method('getStatusCode')->willReturn($state); + $response->expects(($this->atMost(1)))->method('getBody')->willReturn($hasBody ? '# Nextcloud data directory' : 'something else'); + return $response; + }, $status); + + $this->setupcheck + ->expects($this->once()) + ->method('runRequest') + ->will($this->generate($responses)); + + $this->config + ->expects($this->once()) + ->method('getSystemValueString') + ->willReturn(''); + + $result = $this->setupcheck->run(); + $this->assertEquals($expected, $result->getSeverity()); + } + + public static function dataTestStatusCode(): array { + return [ + 'success: forbidden access' => [[403], SetupResult::SUCCESS, true], + 'success: forbidden access with redirect' => [[200], SetupResult::SUCCESS, false], + 'error: can access' => [[200], SetupResult::ERROR, true], + 'error: one forbidden one can access' => [[403, 200], SetupResult::ERROR, true], + 'warning: connection issue' => [[], SetupResult::WARNING, true], + ]; + } + + public function testNoResponse(): void { + $response = $this->createMock(IResponse::class); + $response->expects($this->any())->method('getStatusCode')->willReturn(200); + + $this->setupcheck + ->expects($this->once()) + ->method('runRequest') + ->will($this->generate([])); + + $this->config + ->expects($this->once()) + ->method('getSystemValueString') + ->willReturn(''); + + $result = $this->setupcheck->run(); + $this->assertEquals(SetupResult::WARNING, $result->getSeverity()); + $this->assertMatchesRegularExpression('/^Could not check/', $result->getDescription()); + } + + /** + * Helper function creates a nicer interface for mocking Generator behavior + */ + protected function generate(array $yield_values) { + return $this->returnCallback(function () use ($yield_values) { + yield from $yield_values; + }); + } +} diff --git a/apps/settings/tests/SetupChecks/ForwardedForHeadersTest.php b/apps/settings/tests/SetupChecks/ForwardedForHeadersTest.php new file mode 100644 index 00000000000..9b4878b45cc --- /dev/null +++ b/apps/settings/tests/SetupChecks/ForwardedForHeadersTest.php @@ -0,0 +1,119 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Settings\Tests\SetupChecks; + +use OCA\Settings\SetupChecks\ForwardedForHeaders; +use OCP\IConfig; +use OCP\IL10N; +use OCP\IRequest; +use OCP\IURLGenerator; +use OCP\SetupCheck\SetupResult; +use Test\TestCase; + +class ForwardedForHeadersTest extends TestCase { + private IL10N $l10n; + private IConfig $config; + private IURLGenerator $urlGenerator; + private IRequest $request; + private ForwardedForHeaders $check; + + protected function setUp(): void { + parent::setUp(); + + $this->l10n = $this->createMock(IL10N::class); + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(function ($message, array $replace) { + return vsprintf($message, $replace); + }); + $this->config = $this->getMockBuilder(IConfig::class)->getMock(); + $this->urlGenerator = $this->getMockBuilder(IURLGenerator::class)->getMock(); + $this->request = $this->getMockBuilder(IRequest::class)->getMock(); + $this->check = new ForwardedForHeaders( + $this->l10n, + $this->config, + $this->urlGenerator, + $this->request, + ); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataForwardedForHeadersWorking')] + public function testForwardedForHeadersWorking(array $trustedProxies, string $remoteAddrNotForwarded, string $remoteAddr, string $result): void { + $this->config->expects($this->once()) + ->method('getSystemValue') + ->with('trusted_proxies', []) + ->willReturn($trustedProxies); + $this->request->expects($this->atLeastOnce()) + ->method('getHeader') + ->willReturnMap([ + ['REMOTE_ADDR', $remoteAddrNotForwarded], + ['X-Forwarded-Host', ''] + ]); + $this->request->expects($this->any()) + ->method('getRemoteAddress') + ->willReturn($remoteAddr); + + $this->assertEquals( + $result, + $this->check->run()->getSeverity() + ); + } + + public static function dataForwardedForHeadersWorking(): array { + return [ + // description => trusted proxies, getHeader('REMOTE_ADDR'), getRemoteAddr, expected result + 'no trusted proxies' => [[], '2.2.2.2', '2.2.2.2', SetupResult::SUCCESS], + 'trusted proxy, remote addr not trusted proxy' => [['1.1.1.1'], '2.2.2.2', '2.2.2.2', SetupResult::SUCCESS], + 'trusted proxy, remote addr is trusted proxy, x-forwarded-for working' => [['1.1.1.1'], '1.1.1.1', '2.2.2.2', SetupResult::SUCCESS], + 'trusted proxy, remote addr is trusted proxy, x-forwarded-for not set' => [['1.1.1.1'], '1.1.1.1', '1.1.1.1', SetupResult::WARNING], + ]; + } + + public function testForwardedHostPresentButTrustedProxiesNotAnArray(): void { + $this->config->expects($this->once()) + ->method('getSystemValue') + ->with('trusted_proxies', []) + ->willReturn('1.1.1.1'); + $this->request->expects($this->atLeastOnce()) + ->method('getHeader') + ->willReturnMap([ + ['REMOTE_ADDR', '1.1.1.1'], + ['X-Forwarded-Host', 'nextcloud.test'] + ]); + $this->request->expects($this->any()) + ->method('getRemoteAddress') + ->willReturn('1.1.1.1'); + + $this->assertEquals( + SetupResult::ERROR, + $this->check->run()->getSeverity() + ); + } + + public function testForwardedHostPresentButTrustedProxiesEmpty(): void { + $this->config->expects($this->once()) + ->method('getSystemValue') + ->with('trusted_proxies', []) + ->willReturn([]); + $this->request->expects($this->atLeastOnce()) + ->method('getHeader') + ->willReturnMap([ + ['REMOTE_ADDR', '1.1.1.1'], + ['X-Forwarded-Host', 'nextcloud.test'] + ]); + $this->request->expects($this->any()) + ->method('getRemoteAddress') + ->willReturn('1.1.1.1'); + + $this->assertEquals( + SetupResult::ERROR, + $this->check->run()->getSeverity() + ); + } +} diff --git a/apps/settings/tests/SetupChecks/LoggingLevelTest.php b/apps/settings/tests/SetupChecks/LoggingLevelTest.php new file mode 100644 index 00000000000..67224e11e3a --- /dev/null +++ b/apps/settings/tests/SetupChecks/LoggingLevelTest.php @@ -0,0 +1,76 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Settings\Tests\SetupChecks; + +use OCA\Settings\SetupChecks\LoggingLevel; +use OCP\IConfig; +use OCP\IL10N; +use OCP\ILogger; +use OCP\IURLGenerator; +use OCP\SetupCheck\SetupResult; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LogLevel; +use Test\TestCase; + +class LoggingLevelTest extends TestCase { + private IL10N&MockObject $l10n; + private IConfig&MockObject $config; + private IURLGenerator&MockObject $urlGenerator; + + protected function setUp(): void { + parent::setUp(); + + $this->l10n = $this->createMock(IL10N::class); + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(function ($message, array $replace) { + return vsprintf($message, $replace); + }); + $this->config = $this->createMock(IConfig::class); + $this->urlGenerator = $this->createMock(IURLGenerator::class); + } + + public static function dataRun(): array { + return [ + [ILogger::INFO, SetupResult::SUCCESS], + [ILogger::WARN, SetupResult::SUCCESS], + [ILogger::ERROR, SetupResult::SUCCESS], + [ILogger::FATAL, SetupResult::SUCCESS], + + // Debug is valid but will result in an warning + [ILogger::DEBUG, SetupResult::WARNING], + + // negative - invalid range + [-1, SetupResult::ERROR], + // string value instead of number + ['1', SetupResult::ERROR], + // random string value + ['error', SetupResult::ERROR], + // PSR logger value + [LogLevel::ALERT, SetupResult::ERROR], + // out of range + [ILogger::FATAL + 1, SetupResult::ERROR], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataRun')] + public function testRun(string|int $value, string $expected): void { + $this->urlGenerator->method('linkToDocs')->willReturn('admin-logging'); + + $this->config->expects(self::once()) + ->method('getSystemValue') + ->with('loglevel', ILogger::WARN) + ->willReturn($value); + + $check = new LoggingLevel($this->l10n, $this->config, $this->urlGenerator); + + $result = $check->run(); + $this->assertEquals($expected, $result->getSeverity()); + } +} diff --git a/apps/settings/tests/SetupChecks/OcxProvicersTest.php b/apps/settings/tests/SetupChecks/OcxProvicersTest.php new file mode 100644 index 00000000000..8e5a2c1b88b --- /dev/null +++ b/apps/settings/tests/SetupChecks/OcxProvicersTest.php @@ -0,0 +1,151 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Settings\Tests\SetupChecks; + +use OCA\Settings\SetupChecks\OcxProviders; +use OCP\Http\Client\IClientService; +use OCP\Http\Client\IResponse; +use OCP\IConfig; +use OCP\IL10N; +use OCP\IURLGenerator; +use OCP\SetupCheck\SetupResult; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use Test\TestCase; + +class OcxProvicersTest extends TestCase { + private IL10N|MockObject $l10n; + private IConfig|MockObject $config; + private IURLGenerator|MockObject $urlGenerator; + private IClientService|MockObject $clientService; + private LoggerInterface|MockObject $logger; + private OcxProviders|MockObject $setupcheck; + + protected function setUp(): void { + parent::setUp(); + + $this->l10n = $this->createMock(IL10N::class); + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(function ($message, array $replace) { + return vsprintf($message, $replace); + }); + + $this->config = $this->createMock(IConfig::class); + $this->urlGenerator = $this->createMock(IURLGenerator::class); + $this->clientService = $this->createMock(IClientService::class); + $this->logger = $this->createMock(LoggerInterface::class); + + $this->setupcheck = $this->getMockBuilder(OcxProviders::class) + ->onlyMethods(['runRequest']) + ->setConstructorArgs([ + $this->l10n, + $this->config, + $this->urlGenerator, + $this->clientService, + $this->logger, + ]) + ->getMock(); + } + + public function testSuccess(): void { + $response = $this->createMock(IResponse::class); + $response->expects($this->any())->method('getStatusCode')->willReturn(200); + + $this->setupcheck + ->expects($this->exactly(2)) + ->method('runRequest') + ->willReturnOnConsecutiveCalls($this->generate([$response]), $this->generate([$response])); + + $result = $this->setupcheck->run(); + $this->assertEquals(SetupResult::SUCCESS, $result->getSeverity()); + } + + public function testLateSuccess(): void { + $response1 = $this->createMock(IResponse::class); + $response1->expects($this->exactly(3))->method('getStatusCode')->willReturnOnConsecutiveCalls(404, 500, 200); + $response2 = $this->createMock(IResponse::class); + $response2->expects($this->any())->method('getStatusCode')->willReturnOnConsecutiveCalls(200); + + $this->setupcheck + ->expects($this->exactly(2)) + ->method('runRequest') + ->willReturnOnConsecutiveCalls($this->generate([$response1, $response1, $response1]), $this->generate([$response2])); // only one response out of two + + $result = $this->setupcheck->run(); + $this->assertEquals(SetupResult::SUCCESS, $result->getSeverity()); + } + + public function testNoResponse(): void { + $response = $this->createMock(IResponse::class); + $response->expects($this->any())->method('getStatusCode')->willReturn(200); + + $this->setupcheck + ->expects($this->exactly(2)) + ->method('runRequest') + ->willReturnOnConsecutiveCalls($this->generate([]), $this->generate([])); // No responses + + $result = $this->setupcheck->run(); + $this->assertEquals(SetupResult::WARNING, $result->getSeverity()); + $this->assertMatchesRegularExpression('/^Could not check/', $result->getDescription()); + } + + public function testPartialResponse(): void { + $response = $this->createMock(IResponse::class); + $response->expects($this->any())->method('getStatusCode')->willReturn(200); + + $this->setupcheck + ->expects($this->exactly(2)) + ->method('runRequest') + ->willReturnOnConsecutiveCalls($this->generate([$response]), $this->generate([])); // only one response out of two + + $result = $this->setupcheck->run(); + $this->assertEquals(SetupResult::WARNING, $result->getSeverity()); + $this->assertMatchesRegularExpression('/^Could not check/', $result->getDescription()); + } + + public function testInvalidResponse(): void { + $response = $this->createMock(IResponse::class); + $response->expects($this->any())->method('getStatusCode')->willReturn(404); + + $this->setupcheck + ->expects($this->exactly(2)) + ->method('runRequest') + ->willReturnOnConsecutiveCalls($this->generate([$response]), $this->generate([$response])); // only one response out of two + + $result = $this->setupcheck->run(); + $this->assertEquals(SetupResult::WARNING, $result->getSeverity()); + $this->assertMatchesRegularExpression('/^Your web server is not properly set up/', $result->getDescription()); + } + + public function testPartialInvalidResponse(): void { + $response1 = $this->createMock(IResponse::class); + $response1->expects($this->any())->method('getStatusCode')->willReturnOnConsecutiveCalls(200); + $response2 = $this->createMock(IResponse::class); + $response2->expects($this->any())->method('getStatusCode')->willReturnOnConsecutiveCalls(404); + + $this->setupcheck + ->expects($this->exactly(2)) + ->method('runRequest') + ->willReturnOnConsecutiveCalls($this->generate([$response1]), $this->generate([$response2])); + + $result = $this->setupcheck->run(); + $this->assertEquals(SetupResult::WARNING, $result->getSeverity()); + $this->assertMatchesRegularExpression('/^Your web server is not properly set up/', $result->getDescription()); + } + + /** + * Helper function creates a nicer interface for mocking Generator behavior + */ + protected function generate(array $yield_values) { + return $this->returnCallback(function () use ($yield_values) { + yield from $yield_values; + }); + } +} diff --git a/apps/settings/tests/SetupChecks/PhpDefaultCharsetTest.php b/apps/settings/tests/SetupChecks/PhpDefaultCharsetTest.php index eac671a0e13..3722346219a 100644 --- a/apps/settings/tests/SetupChecks/PhpDefaultCharsetTest.php +++ b/apps/settings/tests/SetupChecks/PhpDefaultCharsetTest.php @@ -3,42 +3,42 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020 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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ -namespace OCA\Settings\Tests; +namespace OCA\Settings\Tests\SetupChecks; use OCA\Settings\SetupChecks\PhpDefaultCharset; +use OCP\IL10N; +use OCP\SetupCheck\SetupResult; +use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; class PhpDefaultCharsetTest extends TestCase { + /** @var IL10N|MockObject */ + private $l10n; + + protected function setUp(): void { + parent::setUp(); + + $this->l10n = $this->createMock(IL10N::class); + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(function ($message, array $replace) { + return vsprintf($message, $replace); + }); + } + public function testPass(): void { - $check = new PhpDefaultCharset(); - $this->assertTrue($check->run()); + $check = new PhpDefaultCharset($this->l10n); + $this->assertEquals(SetupResult::SUCCESS, $check->run()->getSeverity()); } public function testFail(): void { ini_set('default_charset', 'ISO-8859-15'); - $check = new PhpDefaultCharset(); - $this->assertFalse($check->run()); + $check = new PhpDefaultCharset($this->l10n); + $this->assertEquals(SetupResult::WARNING, $check->run()->getSeverity()); ini_restore('default_charset'); } diff --git a/apps/settings/tests/SetupChecks/PhpOutputBufferingTest.php b/apps/settings/tests/SetupChecks/PhpOutputBufferingTest.php index 9e0301dcc12..de509347044 100644 --- a/apps/settings/tests/SetupChecks/PhpOutputBufferingTest.php +++ b/apps/settings/tests/SetupChecks/PhpOutputBufferingTest.php @@ -3,39 +3,39 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020 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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ -namespace OCA\Settings\Tests; +namespace OCA\Settings\Tests\SetupChecks; use OCA\Settings\SetupChecks\PhpOutputBuffering; +use OCP\IL10N; +use OCP\SetupCheck\SetupResult; +use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; class PhpOutputBufferingTest extends TestCase { + /** @var IL10N|MockObject */ + private $l10n; + + protected function setUp(): void { + parent::setUp(); + + $this->l10n = $this->createMock(IL10N::class); + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(function ($message, array $replace) { + return vsprintf($message, $replace); + }); + } + /* * output_buffer is PHP_INI_PERDIR and cannot changed at runtime. * Run this test with -d output_buffering=1 to validate the fail case. */ public function testPass(): void { - $check = new PhpOutputBuffering(); - $this->assertTrue($check->run()); + $check = new PhpOutputBuffering($this->l10n); + $this->assertEquals(SetupResult::SUCCESS, $check->run()->getSeverity()); } } diff --git a/apps/settings/tests/SetupChecks/SecurityHeadersTest.php b/apps/settings/tests/SetupChecks/SecurityHeadersTest.php new file mode 100644 index 00000000000..1f75907d427 --- /dev/null +++ b/apps/settings/tests/SetupChecks/SecurityHeadersTest.php @@ -0,0 +1,196 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Settings\Tests\SetupChecks; + +use OCA\Settings\SetupChecks\SecurityHeaders; +use OCP\Http\Client\IClientService; +use OCP\Http\Client\IResponse; +use OCP\IConfig; +use OCP\IL10N; +use OCP\IURLGenerator; +use OCP\SetupCheck\SetupResult; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use Test\TestCase; + +class SecurityHeadersTest extends TestCase { + private IL10N&MockObject $l10n; + private IConfig&MockObject $config; + private IURLGenerator&MockObject $urlGenerator; + private IClientService&MockObject $clientService; + private LoggerInterface&MockObject $logger; + private SecurityHeaders&MockObject $setupcheck; + + protected function setUp(): void { + parent::setUp(); + + $this->l10n = $this->createMock(IL10N::class); + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(function ($message, array $replace) { + return vsprintf($message, $replace); + }); + + $this->config = $this->createMock(IConfig::class); + $this->urlGenerator = $this->createMock(IURLGenerator::class); + $this->clientService = $this->createMock(IClientService::class); + $this->logger = $this->createMock(LoggerInterface::class); + + $this->setupcheck = $this->getMockBuilder(SecurityHeaders::class) + ->onlyMethods(['runRequest']) + ->setConstructorArgs([ + $this->l10n, + $this->config, + $this->urlGenerator, + $this->clientService, + $this->logger, + ]) + ->getMock(); + } + + public function testInvalidStatusCode(): void { + $this->setupResponse(500, []); + + $result = $this->setupcheck->run(); + $this->assertMatchesRegularExpression('/^Could not check that your web server serves security headers correctly/', $result->getDescription()); + $this->assertEquals(SetupResult::WARNING, $result->getSeverity()); + } + + public function testAllHeadersMissing(): void { + $this->setupResponse(200, []); + + $result = $this->setupcheck->run(); + $this->assertMatchesRegularExpression('/^Some headers are not set correctly on your instance/', $result->getDescription()); + $this->assertEquals(SetupResult::WARNING, $result->getSeverity()); + } + + public function testSomeHeadersMissing(): void { + $this->setupResponse( + 200, + [ + 'X-Robots-Tag' => 'noindex, nofollow', + 'X-Frame-Options' => 'SAMEORIGIN', + 'Strict-Transport-Security' => 'max-age=15768000;preload', + 'X-Permitted-Cross-Domain-Policies' => 'none', + 'Referrer-Policy' => 'no-referrer', + ] + ); + + $result = $this->setupcheck->run(); + $this->assertEquals( + "Some headers are not set correctly on your instance\n- The `X-Content-Type-Options` HTTP header is not set to `nosniff`. This is a potential security or privacy risk, as it is recommended to adjust this setting accordingly.\n", + $result->getDescription() + ); + $this->assertEquals(SetupResult::WARNING, $result->getSeverity()); + } + + public static function dataSuccess(): array { + return [ + // description => modifiedHeaders + 'basic' => [[]], + 'no-space-in-x-robots' => [['X-Robots-Tag' => 'noindex,nofollow']], + 'strict-origin-when-cross-origin' => [['Referrer-Policy' => 'strict-origin-when-cross-origin']], + 'referrer-no-referrer-when-downgrade' => [['Referrer-Policy' => 'no-referrer-when-downgrade']], + 'referrer-strict-origin' => [['Referrer-Policy' => 'strict-origin']], + 'referrer-strict-origin-when-cross-origin' => [['Referrer-Policy' => 'strict-origin-when-cross-origin']], + 'referrer-same-origin' => [['Referrer-Policy' => 'same-origin']], + 'hsts-minimum' => [['Strict-Transport-Security' => 'max-age=15552000']], + 'hsts-include-subdomains' => [['Strict-Transport-Security' => 'max-age=99999999; includeSubDomains']], + 'hsts-include-subdomains-preload' => [['Strict-Transport-Security' => 'max-age=99999999; preload; includeSubDomains']], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataSuccess')] + public function testSuccess(array $headers): void { + $headers = array_merge( + [ + 'X-Content-Type-Options' => 'nosniff', + 'X-Robots-Tag' => 'noindex, nofollow', + 'X-Frame-Options' => 'SAMEORIGIN', + 'Strict-Transport-Security' => 'max-age=15768000', + 'X-Permitted-Cross-Domain-Policies' => 'none', + 'Referrer-Policy' => 'no-referrer', + ], + $headers + ); + $this->setupResponse( + 200, + $headers + ); + + $result = $this->setupcheck->run(); + $this->assertEquals( + 'Your server is correctly configured to send security headers.', + $result->getDescription() + ); + $this->assertEquals(SetupResult::SUCCESS, $result->getSeverity()); + } + + public static function dataFailure(): array { + return [ + // description => modifiedHeaders + 'x-robots-none' => [['X-Robots-Tag' => 'none'], "- The `X-Robots-Tag` HTTP header is not set to `noindex,nofollow`. This is a potential security or privacy risk, as it is recommended to adjust this setting accordingly.\n"], + 'referrer-origin' => [['Referrer-Policy' => 'origin'], "- The `Referrer-Policy` HTTP header is not set to `no-referrer`, `no-referrer-when-downgrade`, `strict-origin`, `strict-origin-when-cross-origin` or `same-origin`. This can leak referer information. See the {w3c-recommendation}.\n"], + 'referrer-origin-when-cross-origin' => [['Referrer-Policy' => 'origin-when-cross-origin'], "- The `Referrer-Policy` HTTP header is not set to `no-referrer`, `no-referrer-when-downgrade`, `strict-origin`, `strict-origin-when-cross-origin` or `same-origin`. This can leak referer information. See the {w3c-recommendation}.\n"], + 'referrer-unsafe-url' => [['Referrer-Policy' => 'unsafe-url'], "- The `Referrer-Policy` HTTP header is not set to `no-referrer`, `no-referrer-when-downgrade`, `strict-origin`, `strict-origin-when-cross-origin` or `same-origin`. This can leak referer information. See the {w3c-recommendation}.\n"], + 'hsts-missing' => [['Strict-Transport-Security' => ''], "- The `Strict-Transport-Security` HTTP header is not set (should be at least `15552000` seconds). For enhanced security, it is recommended to enable HSTS.\n"], + 'hsts-too-low' => [['Strict-Transport-Security' => 'max-age=15551999'], "- The `Strict-Transport-Security` HTTP header is not set to at least `15552000` seconds (current value: `15551999`). For enhanced security, it is recommended to use a long HSTS policy.\n"], + 'hsts-malformed' => [['Strict-Transport-Security' => 'iAmABogusHeader342'], "- The `Strict-Transport-Security` HTTP header is malformed: `iAmABogusHeader342`. For enhanced security, it is recommended to enable HSTS.\n"], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataFailure')] + public function testFailure(array $headers, string $msg): void { + $headers = array_merge( + [ + 'X-Content-Type-Options' => 'nosniff', + 'X-Robots-Tag' => 'noindex, nofollow', + 'X-Frame-Options' => 'SAMEORIGIN', + 'Strict-Transport-Security' => 'max-age=15768000', + 'X-Permitted-Cross-Domain-Policies' => 'none', + 'Referrer-Policy' => 'no-referrer', + ], + $headers + ); + $this->setupResponse( + 200, + $headers + ); + + $result = $this->setupcheck->run(); + $this->assertEquals( + 'Some headers are not set correctly on your instance' . "\n$msg", + $result->getDescription() + ); + $this->assertEquals(SetupResult::WARNING, $result->getSeverity()); + } + + protected function setupResponse(int $statuscode, array $headers): void { + $response = $this->createMock(IResponse::class); + $response->expects($this->atLeastOnce())->method('getStatusCode')->willReturn($statuscode); + $response->expects($this->any())->method('getHeader') + ->willReturnCallback( + fn (string $header): string => $headers[$header] ?? '' + ); + + $this->setupcheck + ->expects($this->atLeastOnce()) + ->method('runRequest') + ->willReturnOnConsecutiveCalls($this->generate([$response])); + } + + /** + * Helper function creates a nicer interface for mocking Generator behavior + */ + protected function generate(array $yield_values) { + return $this->returnCallback(function () use ($yield_values) { + yield from $yield_values; + }); + } +} diff --git a/apps/settings/tests/SetupChecks/SupportedDatabaseTest.php b/apps/settings/tests/SetupChecks/SupportedDatabaseTest.php index 35c27769e78..6c75df47aa0 100644 --- a/apps/settings/tests/SetupChecks/SupportedDatabaseTest.php +++ b/apps/settings/tests/SetupChecks/SupportedDatabaseTest.php @@ -3,39 +3,49 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2021 Morris Jobke <hey@morrisjobke.de> - * - * @author Morris Jobke <hey@morrisjobke.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/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ -namespace OCA\Settings\Tests; +namespace OCA\Settings\Tests\SetupChecks; use OCA\Settings\SetupChecks\SupportedDatabase; +use OCP\IDBConnection; use OCP\IL10N; +use OCP\IURLGenerator; +use OCP\Server; +use OCP\SetupCheck\SetupResult; use Test\TestCase; /** * @group DB */ class SupportedDatabaseTest extends TestCase { + private IL10N $l10n; + private IUrlGenerator $urlGenerator; + private IDBConnection $connection; + + private SupportedDatabase $check; + + protected function setUp(): void { + parent::setUp(); + + $this->l10n = $this->createMock(IL10N::class); + $this->urlGenerator = $this->createMock(IURLGenerator::class); + $this->connection = Server::get(IDBConnection::class); + + $this->check = new SupportedDatabase( + $this->l10n, + $this->urlGenerator, + Server::get(IDBConnection::class) + ); + } + public function testPass(): void { - $l10n = $this->getMockBuilder(IL10N::class)->getMock(); - $check = new SupportedDatabase($l10n, \OC::$server->getDatabaseConnection()); - $this->assertTrue($check->run()); + if ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_SQLITE) { + /** SQlite always gets a warning */ + $this->assertEquals(SetupResult::WARNING, $this->check->run()->getSeverity()); + } else { + $this->assertContains($this->check->run()->getSeverity(), [SetupResult::SUCCESS, SetupResult::INFO]); + } } } diff --git a/apps/settings/tests/SetupChecks/TaskProcessingPickupSpeedTest.php b/apps/settings/tests/SetupChecks/TaskProcessingPickupSpeedTest.php new file mode 100644 index 00000000000..6375d9f6e7f --- /dev/null +++ b/apps/settings/tests/SetupChecks/TaskProcessingPickupSpeedTest.php @@ -0,0 +1,73 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Settings\Tests; + +use OCA\Settings\SetupChecks\TaskProcessingPickupSpeed; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\IL10N; +use OCP\SetupCheck\SetupResult; +use OCP\TaskProcessing\IManager; +use OCP\TaskProcessing\Task; +use Test\TestCase; + +class TaskProcessingPickupSpeedTest extends TestCase { + private IL10N $l10n; + private ITimeFactory $timeFactory; + private IManager $taskProcessingManager; + + private TaskProcessingPickupSpeed $check; + + protected function setUp(): void { + parent::setUp(); + + $this->l10n = $this->getMockBuilder(IL10N::class)->getMock(); + $this->timeFactory = $this->getMockBuilder(ITimeFactory::class)->getMock(); + $this->taskProcessingManager = $this->getMockBuilder(IManager::class)->getMock(); + + $this->check = new TaskProcessingPickupSpeed( + $this->l10n, + $this->taskProcessingManager, + $this->timeFactory, + ); + } + + public function testPass(): void { + $tasks = []; + for ($i = 0; $i < 100; $i++) { + $task = new Task('test', ['test' => 'test'], 'settings', 'user' . $i); + $task->setStartedAt(0); + if ($i < 15) { + $task->setScheduledAt(60 * 5); // 15% get 5mins + } else { + $task->setScheduledAt(60); // the rest gets 1min + } + $tasks[] = $task; + } + $this->taskProcessingManager->method('getTasks')->willReturn($tasks); + + $this->assertEquals(SetupResult::SUCCESS, $this->check->run()->getSeverity()); + } + + public function testFail(): void { + $tasks = []; + for ($i = 0; $i < 100; $i++) { + $task = new Task('test', ['test' => 'test'], 'settings', 'user' . $i); + $task->setStartedAt(0); + if ($i < 30) { + $task->setScheduledAt(60 * 5); // 30% get 5mins + } else { + $task->setScheduledAt(60); // the rest gets 1min + } + $tasks[] = $task; + } + $this->taskProcessingManager->method('getTasks')->willReturn($tasks); + + $this->assertEquals(SetupResult::WARNING, $this->check->run()->getSeverity()); + } +} diff --git a/apps/settings/tests/SetupChecks/WellKnownUrlsTest.php b/apps/settings/tests/SetupChecks/WellKnownUrlsTest.php new file mode 100644 index 00000000000..d55835d66fc --- /dev/null +++ b/apps/settings/tests/SetupChecks/WellKnownUrlsTest.php @@ -0,0 +1,215 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\Settings\Tests\SetupChecks; + +use OCA\Settings\SetupChecks\WellKnownUrls; +use OCP\Http\Client\IClientService; +use OCP\Http\Client\IResponse; +use OCP\IConfig; +use OCP\IL10N; +use OCP\IURLGenerator; +use OCP\SetupCheck\SetupResult; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use Test\TestCase; + +class WellKnownUrlsTest extends TestCase { + private IL10N&MockObject $l10n; + private IConfig&MockObject $config; + private IURLGenerator&MockObject $urlGenerator; + private IClientService&MockObject $clientService; + private LoggerInterface&MockObject $logger; + private WellKnownUrls&MockObject $setupcheck; + + protected function setUp(): void { + parent::setUp(); + + /** @var IL10N&MockObject */ + $this->l10n = $this->createMock(IL10N::class); + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(function ($message, array $replace) { + return vsprintf($message, $replace); + }); + + $this->config = $this->createMock(IConfig::class); + $this->urlGenerator = $this->createMock(IURLGenerator::class); + $this->clientService = $this->createMock(IClientService::class); + $this->logger = $this->createMock(LoggerInterface::class); + + $this->setupcheck = $this->getMockBuilder(WellKnownUrls::class) + ->onlyMethods(['runRequest']) + ->setConstructorArgs([ + $this->l10n, + $this->config, + $this->urlGenerator, + $this->clientService, + $this->logger, + ]) + ->getMock(); + } + + /** + * Test that the SetupCheck is skipped if the system config is set + */ + public function testDisabled(): void { + $this->config + ->expects($this->once()) + ->method('getSystemValueBool') + ->with('check_for_working_wellknown_setup') + ->willReturn(false); + + $this->setupcheck + ->expects($this->never()) + ->method('runRequest'); + + $result = $this->setupcheck->run(); + $this->assertEquals(SetupResult::INFO, $result->getSeverity()); + $this->assertMatchesRegularExpression('/check was skipped/', $result->getDescription()); + } + + /** + * Test what happens if the local server could not be reached (no response from the requests) + */ + public function testNoResponse(): void { + $this->config + ->expects($this->once()) + ->method('getSystemValueBool') + ->with('check_for_working_wellknown_setup') + ->willReturn(true); + + $this->setupcheck + ->expects($this->once()) + ->method('runRequest') + ->will($this->generate([])); + + $result = $this->setupcheck->run(); + $this->assertEquals(SetupResult::INFO, $result->getSeverity()); + $this->assertMatchesRegularExpression('/^Could not check/', $result->getDescription()); + } + + /** + * Test responses + */ + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestResponses')] + public function testResponses($responses, string $expectedSeverity): void { + $this->config + ->expects($this->once()) + ->method('getSystemValueBool') + ->with('check_for_working_wellknown_setup') + ->willReturn(true); + + $this->setupcheck + ->expects($this->atLeastOnce()) + ->method('runRequest') + ->willReturnOnConsecutiveCalls(...$responses); + + $result = $this->setupcheck->run(); + $this->assertEquals($expectedSeverity, $result->getSeverity()); + } + + public function dataTestResponses(): array { + $createResponse = function (int $statuscode, array $header = []): IResponse&MockObject { + $response = $this->createMock(IResponse::class); + $response->expects($this->any()) + ->method('getStatusCode') + ->willReturn($statuscode); + $response->expects($this->any()) + ->method('getHeader') + ->willReturnCallback(fn ($name) => $header[$name] ?? ''); + return $response; + }; + + $wellKnownHeader = ['X-NEXTCLOUD-WELL-KNOWN' => 'yes']; + + return [ + 'expected codes' => [ + [ + $this->generate([$createResponse(200, $wellKnownHeader)]), + $this->generate([$createResponse(200, $wellKnownHeader)]), + $this->generate([$createResponse(207)]), + $this->generate([$createResponse(207)]), + ], + SetupResult::SUCCESS, + ], + 'late response with expected codes' => [ + [ + $this->generate([$createResponse(404), $createResponse(200, $wellKnownHeader)]), + $this->generate([$createResponse(404), $createResponse(200, $wellKnownHeader)]), + $this->generate([$createResponse(404), $createResponse(207)]), + $this->generate([$createResponse(404), $createResponse(207)]), + ], + SetupResult::SUCCESS, + ], + 'working but disabled webfinger' => [ + [ + $this->generate([$createResponse(404, $wellKnownHeader)]), + $this->generate([$createResponse(404, $wellKnownHeader)]), + $this->generate([$createResponse(207)]), + $this->generate([$createResponse(207)]), + ], + SetupResult::SUCCESS, + ], + 'unauthorized webdav but with correct configured redirect' => [ + [ + $this->generate([$createResponse(404, $wellKnownHeader)]), + $this->generate([$createResponse(404, $wellKnownHeader)]), + $this->generate([$createResponse(401, ['X-Guzzle-Redirect-History' => 'https://example.com,https://example.com/remote.php/dav/'])]), + $this->generate([$createResponse(401, ['X-Guzzle-Redirect-History' => 'https://example.com/remote.php/dav/'])]), + ], + SetupResult::SUCCESS, + ], + 'not configured path' => [ + [ + $this->generate([$createResponse(404)]), + $this->generate([$createResponse(404)]), + $this->generate([$createResponse(404)]), + $this->generate([$createResponse(404)]), + ], + SetupResult::WARNING, + ], + 'Invalid webfinger' => [ + [ + $this->generate([$createResponse(404)]), + $this->generate([$createResponse(404, $wellKnownHeader)]), + $this->generate([$createResponse(207)]), + $this->generate([$createResponse(207)]), + ], + SetupResult::WARNING, + ], + 'Invalid nodeinfo' => [ + [ + $this->generate([$createResponse(404, $wellKnownHeader)]), + $this->generate([$createResponse(404)]), + $this->generate([$createResponse(207)]), + $this->generate([$createResponse(207)]), + ], + SetupResult::WARNING, + ], + 'Invalid caldav' => [ + [ + $this->generate([$createResponse(404, $wellKnownHeader)]), + $this->generate([$createResponse(404, $wellKnownHeader)]), + $this->generate([$createResponse(404)]), + $this->generate([$createResponse(207)]), + ], + SetupResult::WARNING, + ], + ]; + } + + /** + * Helper function creates a nicer interface for mocking Generator behavior + */ + protected function generate(array $yield_values) { + return $this->returnCallback(function () use ($yield_values) { + yield from $yield_values; + }); + } +} diff --git a/apps/settings/tests/UserMigration/AccountMigratorTest.php b/apps/settings/tests/UserMigration/AccountMigratorTest.php index 573d18380e5..b8f8301f777 100644 --- a/apps/settings/tests/UserMigration/AccountMigratorTest.php +++ b/apps/settings/tests/UserMigration/AccountMigratorTest.php @@ -1,29 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright 2022 Christopher Ng <chrng8@gmail.com> - * - * @author Christopher Ng <chrng8@gmail.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/>. - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OCA\Settings\Tests\UserMigration; use OCA\Settings\AppInfo\Application; @@ -31,9 +12,12 @@ use OCA\Settings\UserMigration\AccountMigrator; use OCP\Accounts\IAccountManager; use OCP\AppFramework\App; use OCP\IAvatarManager; +use OCP\IConfig; use OCP\IUserManager; +use OCP\Server; use OCP\UserMigration\IExportDestination; use OCP\UserMigration\IImportSource; +use PHPUnit\Framework\Constraint\JsonMatches; use PHPUnit\Framework\MockObject\MockObject; use Sabre\VObject\UUIDUtil; use Symfony\Component\Console\Output\OutputInterface; @@ -43,21 +27,12 @@ use Test\TestCase; * @group DB */ class AccountMigratorTest extends TestCase { - private IUserManager $userManager; - private IAvatarManager $avatarManager; - private AccountMigrator $migrator; - - /** @var IImportSource|MockObject */ - private $importSource; - - /** @var IExportDestination|MockObject */ - private $exportDestination; - - /** @var OutputInterface|MockObject */ - private $output; + private IImportSource&MockObject $importSource; + private IExportDestination&MockObject $exportDestination; + private OutputInterface&MockObject $output; private const ASSETS_DIR = __DIR__ . '/assets/'; @@ -65,9 +40,14 @@ class AccountMigratorTest extends TestCase { private const REGEX_AVATAR_FILE = '/^' . Application::APP_ID . '\/' . 'avatar\.(jpg|png)' . '$/'; + private const REGEX_CONFIG_FILE = '/^' . Application::APP_ID . '\/' . '[a-z]+\.json' . '$/'; + protected function setUp(): void { + parent::setUp(); + $app = new App(Application::APP_ID); $container = $app->getContainer(); + $container->get(IConfig::class)->setSystemValue('has_internet_connection', false); $this->userManager = $container->get(IUserManager::class); $this->avatarManager = $container->get(IAvatarManager::class); @@ -78,33 +58,39 @@ class AccountMigratorTest extends TestCase { $this->output = $this->createMock(OutputInterface::class); } - public function dataImportExportAccount(): array { + protected function tearDown(): void { + Server::get(IConfig::class)->setSystemValue('has_internet_connection', true); + parent::tearDown(); + } + + public static function dataImportExportAccount(): array { return array_map( - function (string $filename) { - $dataPath = self::ASSETS_DIR . $filename; - // For each json file there is an avatar image with the same basename - $avatarBasename = pathinfo($filename, PATHINFO_FILENAME); - $avatarPath = self::ASSETS_DIR . (file_exists(self::ASSETS_DIR . "$avatarBasename.jpg") ? "$avatarBasename.jpg" : "$avatarBasename.png"); + static function (string $filename): array { + $dataPath = static::ASSETS_DIR . $filename; + // For each account json file there is an avatar image and a config json file with the same basename + $basename = pathinfo($filename, PATHINFO_FILENAME); + $avatarPath = static::ASSETS_DIR . (file_exists(static::ASSETS_DIR . "$basename.jpg") ? "$basename.jpg" : "$basename.png"); + $configPath = static::ASSETS_DIR . "$basename-config." . pathinfo($filename, PATHINFO_EXTENSION); return [ UUIDUtil::getUUID(), json_decode(file_get_contents($dataPath), true, 512, JSON_THROW_ON_ERROR), $avatarPath, + json_decode(file_get_contents($configPath), true, 512, JSON_THROW_ON_ERROR), ]; }, array_filter( - scandir(self::ASSETS_DIR), - fn (string $filename) => pathinfo($filename, PATHINFO_EXTENSION) === 'json', + scandir(static::ASSETS_DIR), + fn (string $filename) => pathinfo($filename, PATHINFO_EXTENSION) === 'json' && mb_strpos(pathinfo($filename, PATHINFO_FILENAME), 'config') === false, ), ); } - /** - * @dataProvider dataImportExportAccount - */ - public function testImportExportAccount(string $userId, array $importData, string $avatarPath): void { + #[\PHPUnit\Framework\Attributes\DataProvider('dataImportExportAccount')] + public function testImportExportAccount(string $userId, array $importData, string $avatarPath, array $importConfig): void { $user = $this->userManager->createUser($userId, 'topsecretpassword'); $avatarExt = pathinfo($avatarPath, PATHINFO_EXTENSION); $exportData = $importData; + $exportConfig = $importConfig; // Verification status of email will be set to in progress on import so we set the export data to reflect that $exportData[IAccountManager::PROPERTY_EMAIL]['verified'] = IAccountManager::VERIFICATION_IN_PROGRESS; @@ -114,11 +100,18 @@ class AccountMigratorTest extends TestCase { ->with($this->migrator->getId()) ->willReturn(1); + $calls = [ + [static::REGEX_ACCOUNT_FILE, json_encode($importData)], + [static::REGEX_CONFIG_FILE, json_encode($importConfig)], + ]; $this->importSource - ->expects($this->once()) + ->expects($this->exactly(2)) ->method('getFileContents') - ->with($this->matchesRegularExpression(self::REGEX_ACCOUNT_FILE)) - ->willReturn(json_encode($importData)); + ->willReturnCallback(function ($path) use (&$calls) { + $expected = array_shift($calls); + $this->assertMatchesRegularExpression($expected[0], $path); + return $expected[1]; + }); $this->importSource ->expects($this->once()) @@ -129,7 +122,7 @@ class AccountMigratorTest extends TestCase { $this->importSource ->expects($this->once()) ->method('getFileAsStream') - ->with($this->matchesRegularExpression(self::REGEX_AVATAR_FILE)) + ->with($this->matchesRegularExpression(static::REGEX_AVATAR_FILE)) ->willReturn(fopen($avatarPath, 'r')); $this->migrator->import($user, $this->importSource, $this->output); @@ -149,15 +142,23 @@ class AccountMigratorTest extends TestCase { ); } + $calls = [ + [static::REGEX_ACCOUNT_FILE, new JsonMatches(json_encode($importData))], + [static::REGEX_CONFIG_FILE,new JsonMatches(json_encode($importConfig))], + ]; $this->exportDestination - ->expects($this->once()) + ->expects($this->exactly(2)) ->method('addFileContents') - ->with($this->matchesRegularExpression(self::REGEX_ACCOUNT_FILE), json_encode($exportData)); + ->willReturnCallback(function ($path) use (&$calls) { + $expected = array_shift($calls); + $this->assertMatchesRegularExpression($expected[0], $path); + return $expected[1]; + }); $this->exportDestination ->expects($this->once()) ->method('addFileAsStream') - ->with($this->matchesRegularExpression(self::REGEX_AVATAR_FILE), $this->isType('resource')); + ->with($this->matchesRegularExpression(static::REGEX_AVATAR_FILE), $this->isType('resource')); $this->migrator->export($user, $this->exportDestination, $this->output); } diff --git a/apps/settings/tests/UserMigration/assets/account-complex-config.json b/apps/settings/tests/UserMigration/assets/account-complex-config.json new file mode 100644 index 00000000000..fecf819057c --- /dev/null +++ b/apps/settings/tests/UserMigration/assets/account-complex-config.json @@ -0,0 +1 @@ +{"address":{"visibility":"show_users_only"},"avatar":{"visibility":"show_users_only"},"biography":{"visibility":"show"},"displayname":{"visibility":"show"},"fediverse":{"visibility":"show_users_only"},"headline":{"visibility":"show"},"organisation":{"visibility":"show"},"role":{"visibility":"show"},"email":{"visibility":"hide"},"phone":{"visibility":"hide"},"twitter":{"visibility":"show_users_only"},"website":{"visibility":"show_users_only"},"talk":{"visibility":"show"},"birthdate":{"visibility":"show_users_only"},"pronouns":{"visibility":"show"}}
\ No newline at end of file diff --git a/apps/settings/tests/UserMigration/assets/account-complex.jpg b/apps/settings/tests/UserMigration/assets/account-complex.jpg Binary files differindex bece3675c11..94508343322 100644 --- a/apps/settings/tests/UserMigration/assets/account-complex.jpg +++ b/apps/settings/tests/UserMigration/assets/account-complex.jpg diff --git a/apps/settings/tests/UserMigration/assets/account-complex.json b/apps/settings/tests/UserMigration/assets/account-complex.json index 4199b13b704..cb4668cf18c 100644 --- a/apps/settings/tests/UserMigration/assets/account-complex.json +++ b/apps/settings/tests/UserMigration/assets/account-complex.json @@ -1 +1 @@ -{"displayname":{"name":"displayname","value":"Steve Smith","scope":"v2-local","verified":"0","verificationData":""},"address":{"name":"address","value":"123 Water St","scope":"v2-local","verified":"0","verificationData":""},"website":{"name":"website","value":"https:\/\/example.org","scope":"v2-local","verified":"0","verificationData":""},"email":{"name":"email","value":"steve@example.org","scope":"v2-federated","verified":"0","verificationData":""},"avatar":{"name":"avatar","value":"","scope":"v2-local","verified":"0","verificationData":""},"phone":{"name":"phone","value":"+12178515387","scope":"v2-private","verified":"0","verificationData":""},"twitter":{"name":"twitter","value":"steve","scope":"v2-federated","verified":"0","verificationData":""},"organisation":{"name":"organisation","value":"Mytery Machine","scope":"v2-private","verified":"0","verificationData":""},"role":{"name":"role","value":"Manager","scope":"v2-private","verified":"0","verificationData":""},"headline":{"name":"headline","value":"I am Steve","scope":"v2-local","verified":"0","verificationData":""},"biography":{"name":"biography","value":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris porttitor ullamcorper dictum. Sed fermentum ut ligula scelerisque semper. Aliquam interdum convallis tellus eu dapibus. Integer in justo sollicitudin, hendrerit ligula sit amet, blandit sem.\n\nSuspendisse consectetur ultrices accumsan. Quisque sagittis bibendum lectus ut placerat. Mauris tincidunt ornare neque, et pulvinar tortor porttitor eu.","scope":"v2-local","verified":"0","verificationData":""},"profile_enabled":{"name":"profile_enabled","value":"1","scope":"v2-local","verified":"0","verificationData":""},"additional_mail":[{"name":"additional_mail","value":"steve@example.com","scope":"v2-published","verified":"0","verificationData":""},{"name":"additional_mail","value":"steve@earth.world","scope":"v2-local","verified":"0","verificationData":""}]}
\ No newline at end of file +{"displayname":{"name":"displayname","value":"Steve Smith","scope":"v2-local","verified":"0","verificationData":""},"address":{"name":"address","value":"123 Water St","scope":"v2-local","verified":"0","verificationData":""},"website":{"name":"website","value":"https://example.org","scope":"v2-local","verified":"0","verificationData":""},"email":{"name":"email","value":"steve@example.org","scope":"v2-federated","verified":"1","verificationData":""},"avatar":{"name":"avatar","value":"","scope":"v2-local","verified":"0","verificationData":""},"phone":{"name":"phone","value":"+12178515387","scope":"v2-private","verified":"0","verificationData":""},"twitter":{"name":"twitter","value":"steve","scope":"v2-federated","verified":"0","verificationData":""},"fediverse":{"name":"fediverse","value":"steve@floss.social","scope":"v2-federated","verified":"0","verificationData":""},"organisation":{"name":"organisation","value":"Mytery Machine","scope":"v2-private","verified":"0","verificationData":""},"role":{"name":"role","value":"Manager","scope":"v2-private","verified":"0","verificationData":""},"headline":{"name":"headline","value":"I am Steve","scope":"v2-local","verified":"0","verificationData":""},"biography":{"name":"biography","value":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris porttitor ullamcorper dictum. Sed fermentum ut ligula scelerisque semper. Aliquam interdum convallis tellus eu dapibus. Integer in justo sollicitudin, hendrerit ligula sit amet, blandit sem.\n\nSuspendisse consectetur ultrices accumsan. Quisque sagittis bibendum lectus ut placerat. Mauris tincidunt ornare neque, et pulvinar tortor porttitor eu.","scope":"v2-local","verified":"0","verificationData":""},"birthdate":{"name":"birthdate","value":"","scope":"v2-local","verified":"0","verificationData":""},"profile_enabled":{"name":"profile_enabled","value":"1","scope":"v2-local","verified":"0","verificationData":""},"pronouns":{"name":"pronouns","value":"they/them","scope":"v2-local","verified":"0","verificationData":""},"additional_mail":[{"name":"additional_mail","value":"steve@example.com","scope":"v2-published","verified":"0","verificationData":""},{"name":"additional_mail","value":"steve@earth.world","scope":"v2-local","verified":"0","verificationData":""}]}
\ No newline at end of file diff --git a/apps/settings/tests/UserMigration/assets/account-config.json b/apps/settings/tests/UserMigration/assets/account-config.json new file mode 100644 index 00000000000..a1250fab8e9 --- /dev/null +++ b/apps/settings/tests/UserMigration/assets/account-config.json @@ -0,0 +1 @@ +{"address":{"visibility":"show_users_only"},"avatar":{"visibility":"show"},"biography":{"visibility":"show"},"displayname":{"visibility":"show"},"fediverse":{"visibility":"show"},"headline":{"visibility":"show"},"organisation":{"visibility":"show"},"role":{"visibility":"show"},"email":{"visibility":"show_users_only"},"phone":{"visibility":"show_users_only"},"twitter":{"visibility":"show"},"website":{"visibility":"show"},"birthdate":{"visibility":"show"},"pronouns":{"visibility":"show"}}
\ No newline at end of file diff --git a/apps/settings/tests/UserMigration/assets/account.json b/apps/settings/tests/UserMigration/assets/account.json index 7f53f28c4d3..6bdc3c72d47 100644 --- a/apps/settings/tests/UserMigration/assets/account.json +++ b/apps/settings/tests/UserMigration/assets/account.json @@ -1 +1 @@ -{"displayname":{"name":"displayname","value":"Emma Jones","scope":"v2-federated","verified":"0","verificationData":""},"address":{"name":"address","value":"920 Grass St","scope":"v2-local","verified":"0","verificationData":""},"website":{"name":"website","value":"","scope":"v2-local","verified":"0","verificationData":""},"email":{"name":"email","value":"","scope":"v2-federated","verified":"0","verificationData":""},"avatar":{"name":"avatar","value":"","scope":"v2-federated","verified":"0","verificationData":""},"phone":{"name":"phone","value":"","scope":"v2-local","verified":"0","verificationData":""},"twitter":{"name":"twitter","value":"","scope":"v2-local","verified":"0","verificationData":""},"organisation":{"name":"organisation","value":"","scope":"v2-local","verified":"0","verificationData":""},"role":{"name":"role","value":"","scope":"v2-local","verified":"0","verificationData":""},"headline":{"name":"headline","value":"","scope":"v2-local","verified":"0","verificationData":""},"biography":{"name":"biography","value":"","scope":"v2-local","verified":"0","verificationData":""},"profile_enabled":{"name":"profile_enabled","value":"1","scope":"v2-local","verified":"0","verificationData":""},"additional_mail":[]}
\ No newline at end of file +{"displayname":{"name":"displayname","value":"Emma Jones","scope":"v2-federated","verified":"0","verificationData":""},"address":{"name":"address","value":"920 Grass St","scope":"v2-local","verified":"0","verificationData":""},"website":{"name":"website","value":"","scope":"v2-local","verified":"0","verificationData":""},"email":{"name":"email","value":"","scope":"v2-federated","verified":"1","verificationData":""},"avatar":{"name":"avatar","value":"","scope":"v2-federated","verified":"0","verificationData":""},"phone":{"name":"phone","value":"","scope":"v2-local","verified":"0","verificationData":""},"twitter":{"name":"twitter","value":"","scope":"v2-local","verified":"0","verificationData":""},"fediverse":{"name":"fediverse","value":"","scope":"v2-local","verified":"0","verificationData":""},"organisation":{"name":"organisation","value":"","scope":"v2-local","verified":"0","verificationData":""},"role":{"name":"role","value":"","scope":"v2-local","verified":"0","verificationData":""},"headline":{"name":"headline","value":"","scope":"v2-local","verified":"0","verificationData":""},"biography":{"name":"biography","value":"","scope":"v2-local","verified":"0","verificationData":""},"birthdate":{"name":"birthdate","value":"","scope":"v2-local","verified":"0","verificationData":""},"profile_enabled":{"name":"profile_enabled","value":"1","scope":"v2-local","verified":"0","verificationData":""},"pronouns":{"name":"pronouns","value":"","scope":"v2-federated","verified":"0","verificationData":""},"additional_mail":[]}
\ No newline at end of file diff --git a/apps/settings/tests/UserMigration/assets/account.png b/apps/settings/tests/UserMigration/assets/account.png Binary files differindex 12226f14334..41c4924e569 100644 --- a/apps/settings/tests/UserMigration/assets/account.png +++ b/apps/settings/tests/UserMigration/assets/account.png |