* Copyright (c) 2014 Vincent Petry * This file is licensed under the Affero General Public License version 3 or * later. * See the COPYING-README file. */ namespace Test; use OC\App\AppManager; use OC\App\InfoParser; use OC\AppConfig; use OCP\EventDispatcher\IEventDispatcher; use OCP\IAppConfig; use OCP\IURLGenerator; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; /** * Class AppTest * * @group DB */ class AppTest extends \Test\TestCase { public const TEST_USER1 = 'user1'; public const TEST_USER2 = 'user2'; public const TEST_USER3 = 'user3'; public const TEST_GROUP1 = 'group1'; public const TEST_GROUP2 = 'group2'; public function appVersionsProvider() { return [ // exact match [ '6.0.0.0', [ 'requiremin' => '6.0', 'requiremax' => '6.0', ], true ], // in-between match [ '6.0.0.0', [ 'requiremin' => '5.0', 'requiremax' => '7.0', ], true ], // app too old [ '6.0.0.0', [ 'requiremin' => '5.0', 'requiremax' => '5.0', ], false ], // app too new [ '5.0.0.0', [ 'requiremin' => '6.0', 'requiremax' => '6.0', ], false ], // only min specified [ '6.0.0.0', [ 'requiremin' => '6.0', ], true ], // only min specified fail [ '5.0.0.0', [ 'requiremin' => '6.0', ], false ], // only min specified legacy [ '6.0.0.0', [ 'require' => '6.0', ], true ], // only min specified legacy fail [ '4.0.0.0', [ 'require' => '6.0', ], false ], // only max specified [ '5.0.0.0', [ 'requiremax' => '6.0', ], true ], // only max specified fail [ '7.0.0.0', [ 'requiremax' => '6.0', ], false ], // variations of versions // single OC number [ '4', [ 'require' => '4.0', ], true ], // multiple OC number [ '4.3.1', [ 'require' => '4.3', ], true ], // single app number [ '4', [ 'require' => '4', ], true ], // single app number fail [ '4.3', [ 'require' => '5', ], false ], // complex [ '5.0.0', [ 'require' => '4.5.1', ], true ], // complex fail [ '4.3.1', [ 'require' => '4.3.2', ], false ], // two numbers [ '4.3.1', [ 'require' => '4.4', ], false ], // one number fail [ '4.3.1', [ 'require' => '5', ], false ], // pre-alpha app [ '5.0.3', [ 'require' => '4.93', ], true ], // pre-alpha OC [ '6.90.0.2', [ 'require' => '6.90', ], true ], // pre-alpha OC max [ '6.90.0.2', [ 'requiremax' => '7', ], true ], // expect same major number match [ '5.0.3', [ 'require' => '5', ], true ], // expect same major number match [ '5.0.3', [ 'requiremax' => '5', ], true ], // dependencies versions before require* [ '6.0.0.0', [ 'requiremin' => '5.0', 'requiremax' => '7.0', 'dependencies' => [ 'owncloud' => [ '@attributes' => [ 'min-version' => '7.0', 'max-version' => '7.0', ], ], ], ], false ], [ '6.0.0.0', [ 'requiremin' => '5.0', 'requiremax' => '7.0', 'dependencies' => [ 'owncloud' => [ '@attributes' => [ 'min-version' => '5.0', 'max-version' => '5.0', ], ], ], ], false ], [ '6.0.0.0', [ 'requiremin' => '5.0', 'requiremax' => '5.0', 'dependencies' => [ 'owncloud' => [ '@attributes' => [ 'min-version' => '5.0', 'max-version' => '7.0', ], ], ], ], true ], [ '9.2.0.0', [ 'dependencies' => [ 'owncloud' => [ '@attributes' => [ 'min-version' => '9.0', 'max-version' => '9.1', ], ], 'nextcloud' => [ '@attributes' => [ 'min-version' => '9.1', 'max-version' => '9.2', ], ], ], ], true ], [ '9.2.0.0', [ 'dependencies' => [ 'nextcloud' => [ '@attributes' => [ 'min-version' => '9.1', 'max-version' => '9.2', ], ], ], ], true ], ]; } /** * @dataProvider appVersionsProvider */ public function testIsAppCompatible($ocVersion, $appInfo, $expectedResult) { $this->assertEquals($expectedResult, \OC_App::isAppCompatible($ocVersion, $appInfo)); } /** * Tests that the app order is correct */ public function testGetEnabledAppsIsSorted() { $apps = \OC_App::getEnabledApps(); // copy array $sortedApps = $apps; sort($sortedApps); // 'files' is always on top unset($sortedApps[array_search('files', $sortedApps)]); array_unshift($sortedApps, 'files'); $this->assertEquals($sortedApps, $apps); } /** * Providers for the app config values */ public function appConfigValuesProvider() { return [ // logged in user1 [ self::TEST_USER1, [ 'files', 'app1', 'app3', 'appforgroup1', 'appforgroup12', 'cloud_federation_api', 'dav', 'federatedfilesharing', 'lookup_server_connector', 'oauth2', 'provisioning_api', 'settings', 'theming', 'twofactor_backupcodes', 'viewer', 'workflowengine', ], false ], // logged in user2 [ self::TEST_USER2, [ 'files', 'app1', 'app3', 'appforgroup12', 'appforgroup2', 'cloud_federation_api', 'dav', 'federatedfilesharing', 'lookup_server_connector', 'oauth2', 'provisioning_api', 'settings', 'theming', 'twofactor_backupcodes', 'viewer', 'workflowengine', ], false ], // logged in user3 [ self::TEST_USER3, [ 'files', 'app1', 'app3', 'appforgroup1', 'appforgroup12', 'appforgroup2', 'cloud_federation_api', 'dav', 'federatedfilesharing', 'lookup_server_connector', 'oauth2', 'provisioning_api', 'settings', 'theming', 'twofactor_backupcodes', 'viewer', 'workflowengine', ], false ], // no user, returns all apps [ null, [ 'files', 'app1', 'app3', 'appforgroup1', 'appforgroup12', 'appforgroup2', 'cloud_federation_api', 'dav', 'federatedfilesharing', 'lookup_server_connector', 'oauth2', 'provisioning_api', 'settings', 'theming', 'twofactor_backupcodes', 'viewer', 'workflowengine', ], false, ], // user given, but ask for all [ self::TEST_USER1, [ 'files', 'app1', 'app3', 'appforgroup1', 'appforgroup12', 'appforgroup2', 'cloud_federation_api', 'dav', 'federatedfilesharing', 'lookup_server_connector', 'oauth2', 'provisioning_api', 'settings', 'theming', 'twofactor_backupcodes', 'viewer', 'workflowengine', ], true, ], ]; } /** * Test enabled apps * * @dataProvider appConfigValuesProvider */ public function testEnabledApps($user, $expectedApps, $forceAll) { $userManager = \OC::$server->getUserManager(); $groupManager = \OC::$server->getGroupManager(); $user1 = $userManager->createUser(self::TEST_USER1, self::TEST_USER1); $user2 = $userManager->createUser(self::TEST_USER2, self::TEST_USER2); $user3 = $userManager->createUser(self::TEST_USER3, self::TEST_USER3); $group1 = $groupManager->createGroup(self::TEST_GROUP1); $group1->addUser($user1); $group1->addUser($user3); $group2 = $groupManager->createGroup(self::TEST_GROUP2); $group2->addUser($user2); $group2->addUser($user3); \OC_User::setUserId($user); $this->setupAppConfigMock()->expects($this->once()) ->method('getValues') ->willReturn( [ 'app3' => 'yes', 'app2' => 'no', 'app1' => 'yes', 'appforgroup1' => '["group1"]', 'appforgroup2' => '["group2"]', 'appforgroup12' => '["group2","group1"]', ] ); $apps = \OC_App::getEnabledApps(false, $forceAll); $this->restoreAppConfig(); \OC_User::setUserId(null); $user1->delete(); $user2->delete(); $user3->delete(); $group1->delete(); $group2->delete(); $this->assertEquals($expectedApps, $apps); } /** * Test isEnabledApps() with cache, not re-reading the list of * enabled apps more than once when a user is set. */ public function testEnabledAppsCache() { $userManager = \OC::$server->getUserManager(); $user1 = $userManager->createUser(self::TEST_USER1, self::TEST_USER1); \OC_User::setUserId(self::TEST_USER1); $this->setupAppConfigMock()->expects($this->once()) ->method('getValues') ->willReturn( [ 'app3' => 'yes', 'app2' => 'no', ] ); $apps = \OC_App::getEnabledApps(); $this->assertEquals(['files', 'app3', 'cloud_federation_api', 'dav', 'federatedfilesharing', 'lookup_server_connector', 'oauth2', 'provisioning_api', 'settings', 'theming', 'twofactor_backupcodes', 'viewer', 'workflowengine'], $apps); // mock should not be called again here $apps = \OC_App::getEnabledApps(); $this->assertEquals(['files', 'app3', 'cloud_federation_api', 'dav', 'federatedfilesharing', 'lookup_server_connector', 'oauth2', 'provisioning_api', 'settings', 'theming', 'twofactor_backupcodes', 'viewer', 'workflowengine'], $apps); $this->restoreAppConfig(); \OC_User::setUserId(null); $user1->delete(); } private function setupAppConfigMock() { /** @var AppConfig|MockObject */ $appConfig = $this->getMockBuilder(AppConfig::class) ->setMethods(['getValues']) ->setConstructorArgs([\OC::$server->getDatabaseConnection()]) ->disableOriginalConstructor() ->getMock(); $this->registerAppConfig($appConfig); return $appConfig; } /** * Register an app config mock for testing purposes. * * @param IAppConfig $appConfig app config mock */ private function registerAppConfig(AppConfig $appConfig) { $this->overwriteService(AppConfig::class, $appConfig); $this->overwriteService(AppManager::class, new AppManager( \OC::$server->getUserSession(), \OC::$server->getConfig(), \OC::$server->getGroupManager(), \OC::$server->getMemCacheFactory(), \OC::$server->get(IEventDispatcher::class), \OC::$server->get(LoggerInterface::class), \OC::$server->get(IURLGenerator::class), )); } /** * Restore the original app config service. */ private function restoreAppConfig() { $this->restoreService(AppConfig::class); $this->restoreService(AppManager::class); // Remove the cache of the mocked apps list with a forceRefresh \OC_App::getEnabledApps(); } /** * Providers for the app data values */ public function appDataProvider() { return [ [ ['description' => " \t This is a multiline \n test with \n \t \n \n some new lines "], ['description' => "This is a multiline \n test with \n \t \n \n some new lines"], ], [ ['description' => " \t This is a multiline \n test with \n \t some new lines "], ['description' => "This is a multiline \n test with \n \t some new lines"], ], [ ['description' => hex2bin('5065726d657420646520732761757468656e7469666965722064616e732070697769676f20646972656374656d656e74206176656320736573206964656e74696669616e7473206f776e636c6f75642073616e73206c65732072657461706572206574206d657420c3a0206a6f757273206365757820636920656e20636173206465206368616e67656d656e74206465206d6f742064652070617373652e0d0a0d')], ['description' => "Permet de s'authentifier dans piwigo directement avec ses identifiants owncloud sans les retaper et met à jours ceux ci en cas de changement de mot de passe."], ], [ ['not-a-description' => " \t This is a multiline \n test with \n \t some new lines "], [ 'not-a-description' => " \t This is a multiline \n test with \n \t some new lines ", 'description' => '', ], ], [ ['description' => [100, 'bla']], ['description' => ''], ], ]; } /** * Test app info parser * * @dataProvider appDataProvider * @param array $data * @param array $expected */ public function testParseAppInfo(array $data, array $expected) { $this->assertSame($expected, \OC_App::parseAppInfo($data)); } public function testParseAppInfoL10N() { $parser = new InfoParser(); $data = $parser->parse(\OC::$SERVERROOT. "/tests/data/app/description-multi-lang.xml"); $this->assertEquals('English', \OC_App::parseAppInfo($data, 'en')['description']); $this->assertEquals('German', \OC_App::parseAppInfo($data, 'de')['description']); } public function testParseAppInfoL10NSingleLanguage() { $parser = new InfoParser(); $data = $parser->parse(\OC::$SERVERROOT. "/tests/data/app/description-single-lang.xml"); $this->assertEquals('English', \OC_App::parseAppInfo($data, 'en')['description']); } }