AppManagerTest.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
  5. * This file is licensed under the Affero General Public License version 3 or
  6. * later.
  7. * See the COPYING-README file.
  8. */
  9. namespace Test\App;
  10. use OC\App\AppManager;
  11. use OC\AppConfig;
  12. use OCP\App\AppPathNotFoundException;
  13. use OCP\App\Events\AppDisableEvent;
  14. use OCP\App\Events\AppEnableEvent;
  15. use OCP\App\IAppManager;
  16. use OCP\EventDispatcher\IEventDispatcher;
  17. use OCP\ICache;
  18. use OCP\ICacheFactory;
  19. use OCP\IConfig;
  20. use OCP\IGroup;
  21. use OCP\IGroupManager;
  22. use OCP\IURLGenerator;
  23. use OCP\IUser;
  24. use OCP\IUserSession;
  25. use PHPUnit\Framework\MockObject\MockObject;
  26. use Psr\Log\LoggerInterface;
  27. use Test\TestCase;
  28. /**
  29. * Class AppManagerTest
  30. *
  31. * @package Test\App
  32. */
  33. class AppManagerTest extends TestCase {
  34. /**
  35. * @return AppConfig|MockObject
  36. */
  37. protected function getAppConfig() {
  38. $appConfig = [];
  39. $config = $this->createMock(AppConfig::class);
  40. $config->expects($this->any())
  41. ->method('getValue')
  42. ->willReturnCallback(function ($app, $key, $default) use (&$appConfig) {
  43. return (isset($appConfig[$app]) and isset($appConfig[$app][$key])) ? $appConfig[$app][$key] : $default;
  44. });
  45. $config->expects($this->any())
  46. ->method('setValue')
  47. ->willReturnCallback(function ($app, $key, $value) use (&$appConfig) {
  48. if (!isset($appConfig[$app])) {
  49. $appConfig[$app] = [];
  50. }
  51. $appConfig[$app][$key] = $value;
  52. });
  53. $config->expects($this->any())
  54. ->method('getValues')
  55. ->willReturnCallback(function ($app, $key) use (&$appConfig) {
  56. if ($app) {
  57. return $appConfig[$app];
  58. } else {
  59. $values = [];
  60. foreach ($appConfig as $appid => $appData) {
  61. if (isset($appData[$key])) {
  62. $values[$appid] = $appData[$key];
  63. }
  64. }
  65. return $values;
  66. }
  67. });
  68. return $config;
  69. }
  70. /** @var IUserSession|MockObject */
  71. protected $userSession;
  72. /** @var IConfig|MockObject */
  73. private $config;
  74. /** @var IGroupManager|MockObject */
  75. protected $groupManager;
  76. /** @var AppConfig|MockObject */
  77. protected $appConfig;
  78. /** @var ICache|MockObject */
  79. protected $cache;
  80. /** @var ICacheFactory|MockObject */
  81. protected $cacheFactory;
  82. /** @var IEventDispatcher|MockObject */
  83. protected $eventDispatcher;
  84. /** @var LoggerInterface|MockObject */
  85. protected $logger;
  86. protected IURLGenerator|MockObject $urlGenerator;
  87. /** @var IAppManager */
  88. protected $manager;
  89. protected function setUp(): void {
  90. parent::setUp();
  91. $this->userSession = $this->createMock(IUserSession::class);
  92. $this->groupManager = $this->createMock(IGroupManager::class);
  93. $this->config = $this->createMock(IConfig::class);
  94. $this->appConfig = $this->getAppConfig();
  95. $this->cacheFactory = $this->createMock(ICacheFactory::class);
  96. $this->cache = $this->createMock(ICache::class);
  97. $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
  98. $this->logger = $this->createMock(LoggerInterface::class);
  99. $this->urlGenerator = $this->createMock(IURLGenerator::class);
  100. $this->cacheFactory->expects($this->any())
  101. ->method('createDistributed')
  102. ->with('settings')
  103. ->willReturn($this->cache);
  104. $this->manager = new AppManager(
  105. $this->userSession,
  106. $this->config,
  107. $this->appConfig,
  108. $this->groupManager,
  109. $this->cacheFactory,
  110. $this->eventDispatcher,
  111. $this->logger,
  112. $this->urlGenerator,
  113. );
  114. }
  115. /**
  116. * @dataProvider dataGetAppIcon
  117. */
  118. public function testGetAppIcon($callback, ?bool $dark, string|null $expected) {
  119. $this->urlGenerator->expects($this->atLeastOnce())
  120. ->method('imagePath')
  121. ->willReturnCallback($callback);
  122. if ($dark !== null) {
  123. $this->assertEquals($expected, $this->manager->getAppIcon('test', $dark));
  124. } else {
  125. $this->assertEquals($expected, $this->manager->getAppIcon('test'));
  126. }
  127. }
  128. public function dataGetAppIcon(): array {
  129. $nothing = function ($appId) {
  130. $this->assertEquals('test', $appId);
  131. throw new \RuntimeException();
  132. };
  133. $createCallback = function ($workingIcons) {
  134. return function ($appId, $icon) use ($workingIcons) {
  135. $this->assertEquals('test', $appId);
  136. if (in_array($icon, $workingIcons)) {
  137. return '/path/' . $icon;
  138. }
  139. throw new \RuntimeException();
  140. };
  141. };
  142. return [
  143. 'does not find anything' => [
  144. $nothing,
  145. false,
  146. null,
  147. ],
  148. 'nothing if request dark but only bright available' => [
  149. $createCallback(['app.svg']),
  150. true,
  151. null,
  152. ],
  153. 'nothing if request bright but only dark available' => [
  154. $createCallback(['app-dark.svg']),
  155. false,
  156. null,
  157. ],
  158. 'bright and only app.svg' => [
  159. $createCallback(['app.svg']),
  160. false,
  161. '/path/app.svg',
  162. ],
  163. 'dark and only app-dark.svg' => [
  164. $createCallback(['app-dark.svg']),
  165. true,
  166. '/path/app-dark.svg',
  167. ],
  168. 'dark only appname -dark.svg' => [
  169. $createCallback(['test-dark.svg']),
  170. true,
  171. '/path/test-dark.svg',
  172. ],
  173. 'bright and only appname.svg' => [
  174. $createCallback(['test.svg']),
  175. false,
  176. '/path/test.svg',
  177. ],
  178. 'priotize custom over default' => [
  179. $createCallback(['app.svg', 'test.svg']),
  180. false,
  181. '/path/test.svg',
  182. ],
  183. 'defaults to bright' => [
  184. $createCallback(['test-dark.svg', 'test.svg']),
  185. null,
  186. '/path/test.svg',
  187. ],
  188. 'no dark icon on default' => [
  189. $createCallback(['test-dark.svg', 'test.svg', 'app-dark.svg', 'app.svg']),
  190. false,
  191. '/path/test.svg',
  192. ],
  193. 'no bright icon on dark' => [
  194. $createCallback(['test-dark.svg', 'test.svg', 'app-dark.svg', 'app.svg']),
  195. true,
  196. '/path/test-dark.svg',
  197. ],
  198. ];
  199. }
  200. public function testEnableApp() {
  201. // making sure "files_trashbin" is disabled
  202. if ($this->manager->isEnabledForUser('files_trashbin')) {
  203. $this->manager->disableApp('files_trashbin');
  204. }
  205. $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new AppEnableEvent('files_trashbin'));
  206. $this->manager->enableApp('files_trashbin');
  207. $this->assertEquals('yes', $this->appConfig->getValue('files_trashbin', 'enabled', 'no'));
  208. }
  209. public function testDisableApp() {
  210. $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new AppDisableEvent('files_trashbin'));
  211. $this->manager->disableApp('files_trashbin');
  212. $this->assertEquals('no', $this->appConfig->getValue('files_trashbin', 'enabled', 'no'));
  213. }
  214. public function testNotEnableIfNotInstalled() {
  215. try {
  216. $this->manager->enableApp('some_random_name_which_i_hope_is_not_an_app');
  217. $this->assertFalse(true, 'If this line is reached the expected exception is not thrown.');
  218. } catch (AppPathNotFoundException $e) {
  219. // Exception is expected
  220. $this->assertEquals('Could not find path for some_random_name_which_i_hope_is_not_an_app', $e->getMessage());
  221. }
  222. $this->assertEquals('no', $this->appConfig->getValue(
  223. 'some_random_name_which_i_hope_is_not_an_app', 'enabled', 'no'
  224. ));
  225. }
  226. public function testEnableAppForGroups() {
  227. $group1 = $this->createMock(IGroup::class);
  228. $group1->method('getGID')
  229. ->willReturn('group1');
  230. $group2 = $this->createMock(IGroup::class);
  231. $group2->method('getGID')
  232. ->willReturn('group2');
  233. $groups = [$group1, $group2];
  234. /** @var AppManager|MockObject $manager */
  235. $manager = $this->getMockBuilder(AppManager::class)
  236. ->setConstructorArgs([
  237. $this->userSession,
  238. $this->config,
  239. $this->appConfig,
  240. $this->groupManager,
  241. $this->cacheFactory,
  242. $this->eventDispatcher,
  243. $this->logger,
  244. $this->urlGenerator,
  245. ])
  246. ->onlyMethods([
  247. 'getAppPath',
  248. ])
  249. ->getMock();
  250. $manager->expects($this->exactly(2))
  251. ->method('getAppPath')
  252. ->with('test')
  253. ->willReturn('apps/test');
  254. $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new AppEnableEvent('test', ['group1', 'group2']));
  255. $manager->enableAppForGroups('test', $groups);
  256. $this->assertEquals('["group1","group2"]', $this->appConfig->getValue('test', 'enabled', 'no'));
  257. }
  258. public function dataEnableAppForGroupsAllowedTypes() {
  259. return [
  260. [[]],
  261. [[
  262. 'types' => [],
  263. ]],
  264. [[
  265. 'types' => ['nickvergessen'],
  266. ]],
  267. ];
  268. }
  269. /**
  270. * @dataProvider dataEnableAppForGroupsAllowedTypes
  271. *
  272. * @param array $appInfo
  273. */
  274. public function testEnableAppForGroupsAllowedTypes(array $appInfo) {
  275. $group1 = $this->createMock(IGroup::class);
  276. $group1->method('getGID')
  277. ->willReturn('group1');
  278. $group2 = $this->createMock(IGroup::class);
  279. $group2->method('getGID')
  280. ->willReturn('group2');
  281. $groups = [$group1, $group2];
  282. /** @var AppManager|MockObject $manager */
  283. $manager = $this->getMockBuilder(AppManager::class)
  284. ->setConstructorArgs([
  285. $this->userSession,
  286. $this->config,
  287. $this->appConfig,
  288. $this->groupManager,
  289. $this->cacheFactory,
  290. $this->eventDispatcher,
  291. $this->logger,
  292. $this->urlGenerator,
  293. ])
  294. ->onlyMethods([
  295. 'getAppPath',
  296. 'getAppInfo',
  297. ])
  298. ->getMock();
  299. $manager->expects($this->once())
  300. ->method('getAppPath')
  301. ->with('test')
  302. ->willReturn(null);
  303. $manager->expects($this->once())
  304. ->method('getAppInfo')
  305. ->with('test')
  306. ->willReturn($appInfo);
  307. $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new AppEnableEvent('test', ['group1', 'group2']));
  308. $manager->enableAppForGroups('test', $groups);
  309. $this->assertEquals('["group1","group2"]', $this->appConfig->getValue('test', 'enabled', 'no'));
  310. }
  311. public function dataEnableAppForGroupsForbiddenTypes() {
  312. return [
  313. ['filesystem'],
  314. ['prelogin'],
  315. ['authentication'],
  316. ['logging'],
  317. ['prevent_group_restriction'],
  318. ];
  319. }
  320. /**
  321. * @dataProvider dataEnableAppForGroupsForbiddenTypes
  322. *
  323. * @param string $type
  324. *
  325. */
  326. public function testEnableAppForGroupsForbiddenTypes($type) {
  327. $this->expectException(\Exception::class);
  328. $this->expectExceptionMessage('test can\'t be enabled for groups.');
  329. $group1 = $this->createMock(IGroup::class);
  330. $group1->method('getGID')
  331. ->willReturn('group1');
  332. $group2 = $this->createMock(IGroup::class);
  333. $group2->method('getGID')
  334. ->willReturn('group2');
  335. $groups = [$group1, $group2];
  336. /** @var AppManager|MockObject $manager */
  337. $manager = $this->getMockBuilder(AppManager::class)
  338. ->setConstructorArgs([
  339. $this->userSession,
  340. $this->config,
  341. $this->appConfig,
  342. $this->groupManager,
  343. $this->cacheFactory,
  344. $this->eventDispatcher,
  345. $this->logger,
  346. $this->urlGenerator,
  347. ])
  348. ->onlyMethods([
  349. 'getAppPath',
  350. 'getAppInfo',
  351. ])
  352. ->getMock();
  353. $manager->expects($this->once())
  354. ->method('getAppPath')
  355. ->with('test')
  356. ->willReturn(null);
  357. $manager->expects($this->once())
  358. ->method('getAppInfo')
  359. ->with('test')
  360. ->willReturn([
  361. 'types' => [$type],
  362. ]);
  363. $this->eventDispatcher->expects($this->never())->method('dispatchTyped')->with(new AppEnableEvent('test', ['group1', 'group2']));
  364. $manager->enableAppForGroups('test', $groups);
  365. }
  366. public function testIsInstalledEnabled() {
  367. $this->appConfig->setValue('test', 'enabled', 'yes');
  368. $this->assertTrue($this->manager->isInstalled('test'));
  369. }
  370. public function testIsInstalledDisabled() {
  371. $this->appConfig->setValue('test', 'enabled', 'no');
  372. $this->assertFalse($this->manager->isInstalled('test'));
  373. }
  374. public function testIsInstalledEnabledForGroups() {
  375. $this->appConfig->setValue('test', 'enabled', '["foo"]');
  376. $this->assertTrue($this->manager->isInstalled('test'));
  377. }
  378. private function newUser($uid) {
  379. $user = $this->createMock(IUser::class);
  380. $user->method('getUID')
  381. ->willReturn($uid);
  382. return $user;
  383. }
  384. public function testIsEnabledForUserEnabled() {
  385. $this->appConfig->setValue('test', 'enabled', 'yes');
  386. $user = $this->newUser('user1');
  387. $this->assertTrue($this->manager->isEnabledForUser('test', $user));
  388. }
  389. public function testIsEnabledForUserDisabled() {
  390. $this->appConfig->setValue('test', 'enabled', 'no');
  391. $user = $this->newUser('user1');
  392. $this->assertFalse($this->manager->isEnabledForUser('test', $user));
  393. }
  394. public function testGetAppPath() {
  395. $this->assertEquals(\OC::$SERVERROOT . '/apps/files', $this->manager->getAppPath('files'));
  396. }
  397. public function testGetAppPathSymlink() {
  398. $fakeAppDirname = sha1(uniqid('test', true));
  399. $fakeAppPath = sys_get_temp_dir() . '/' . $fakeAppDirname;
  400. $fakeAppLink = \OC::$SERVERROOT . '/' . $fakeAppDirname;
  401. mkdir($fakeAppPath);
  402. if (symlink($fakeAppPath, $fakeAppLink) === false) {
  403. $this->markTestSkipped('Failed to create symlink');
  404. }
  405. // Use the symlink as the app path
  406. \OC::$APPSROOTS[] = [
  407. 'path' => $fakeAppLink,
  408. 'url' => \OC::$WEBROOT . '/' . $fakeAppDirname,
  409. 'writable' => false,
  410. ];
  411. $fakeTestAppPath = $fakeAppPath . '/' . 'test-test-app';
  412. mkdir($fakeTestAppPath);
  413. $generatedAppPath = $this->manager->getAppPath('test-test-app');
  414. rmdir($fakeTestAppPath);
  415. unlink($fakeAppLink);
  416. rmdir($fakeAppPath);
  417. $this->assertEquals($fakeAppLink . '/test-test-app', $generatedAppPath);
  418. }
  419. public function testGetAppPathFail() {
  420. $this->expectException(AppPathNotFoundException::class);
  421. $this->manager->getAppPath('testnotexisting');
  422. }
  423. public function testIsEnabledForUserEnabledForGroup() {
  424. $user = $this->newUser('user1');
  425. $this->groupManager->expects($this->once())
  426. ->method('getUserGroupIds')
  427. ->with($user)
  428. ->willReturn(['foo', 'bar']);
  429. $this->appConfig->setValue('test', 'enabled', '["foo"]');
  430. $this->assertTrue($this->manager->isEnabledForUser('test', $user));
  431. }
  432. public function testIsEnabledForUserDisabledForGroup() {
  433. $user = $this->newUser('user1');
  434. $this->groupManager->expects($this->once())
  435. ->method('getUserGroupIds')
  436. ->with($user)
  437. ->willReturn(['bar']);
  438. $this->appConfig->setValue('test', 'enabled', '["foo"]');
  439. $this->assertFalse($this->manager->isEnabledForUser('test', $user));
  440. }
  441. public function testIsEnabledForUserLoggedOut() {
  442. $this->appConfig->setValue('test', 'enabled', '["foo"]');
  443. $this->assertFalse($this->manager->isEnabledForUser('test'));
  444. }
  445. public function testIsEnabledForUserLoggedIn() {
  446. $user = $this->newUser('user1');
  447. $this->userSession->expects($this->once())
  448. ->method('getUser')
  449. ->willReturn($user);
  450. $this->groupManager->expects($this->once())
  451. ->method('getUserGroupIds')
  452. ->with($user)
  453. ->willReturn(['foo', 'bar']);
  454. $this->appConfig->setValue('test', 'enabled', '["foo"]');
  455. $this->assertTrue($this->manager->isEnabledForUser('test'));
  456. }
  457. public function testGetInstalledApps() {
  458. $this->appConfig->setValue('test1', 'enabled', 'yes');
  459. $this->appConfig->setValue('test2', 'enabled', 'no');
  460. $this->appConfig->setValue('test3', 'enabled', '["foo"]');
  461. $apps = [
  462. 'cloud_federation_api',
  463. 'dav',
  464. 'federatedfilesharing',
  465. 'files',
  466. 'lookup_server_connector',
  467. 'oauth2',
  468. 'provisioning_api',
  469. 'settings',
  470. 'test1',
  471. 'test3',
  472. 'theming',
  473. 'twofactor_backupcodes',
  474. 'viewer',
  475. 'workflowengine',
  476. ];
  477. $this->assertEquals($apps, $this->manager->getInstalledApps());
  478. }
  479. public function testGetAppsForUser() {
  480. $user = $this->newUser('user1');
  481. $this->groupManager->expects($this->any())
  482. ->method('getUserGroupIds')
  483. ->with($user)
  484. ->willReturn(['foo', 'bar']);
  485. $this->appConfig->setValue('test1', 'enabled', 'yes');
  486. $this->appConfig->setValue('test2', 'enabled', 'no');
  487. $this->appConfig->setValue('test3', 'enabled', '["foo"]');
  488. $this->appConfig->setValue('test4', 'enabled', '["asd"]');
  489. $enabled = [
  490. 'cloud_federation_api',
  491. 'dav',
  492. 'federatedfilesharing',
  493. 'files',
  494. 'lookup_server_connector',
  495. 'oauth2',
  496. 'provisioning_api',
  497. 'settings',
  498. 'test1',
  499. 'test3',
  500. 'theming',
  501. 'twofactor_backupcodes',
  502. 'viewer',
  503. 'workflowengine',
  504. ];
  505. $this->assertEquals($enabled, $this->manager->getEnabledAppsForUser($user));
  506. }
  507. public function testGetAppsNeedingUpgrade() {
  508. /** @var AppManager|MockObject $manager */
  509. $manager = $this->getMockBuilder(AppManager::class)
  510. ->setConstructorArgs([
  511. $this->userSession,
  512. $this->config,
  513. $this->appConfig,
  514. $this->groupManager,
  515. $this->cacheFactory,
  516. $this->eventDispatcher,
  517. $this->logger,
  518. $this->urlGenerator,
  519. ])
  520. ->onlyMethods(['getAppInfo'])
  521. ->getMock();
  522. $appInfos = [
  523. 'cloud_federation_api' => ['id' => 'cloud_federation_api'],
  524. 'dav' => ['id' => 'dav'],
  525. 'files' => ['id' => 'files'],
  526. 'federatedfilesharing' => ['id' => 'federatedfilesharing'],
  527. 'provisioning_api' => ['id' => 'provisioning_api'],
  528. 'lookup_server_connector' => ['id' => 'lookup_server_connector'],
  529. 'test1' => ['id' => 'test1', 'version' => '1.0.1', 'requiremax' => '9.0.0'],
  530. 'test2' => ['id' => 'test2', 'version' => '1.0.0', 'requiremin' => '8.2.0'],
  531. 'test3' => ['id' => 'test3', 'version' => '1.2.4', 'requiremin' => '9.0.0'],
  532. 'test4' => ['id' => 'test4', 'version' => '3.0.0', 'requiremin' => '8.1.0'],
  533. 'testnoversion' => ['id' => 'testnoversion', 'requiremin' => '8.2.0'],
  534. 'settings' => ['id' => 'settings'],
  535. 'theming' => ['id' => 'theming'],
  536. 'twofactor_backupcodes' => ['id' => 'twofactor_backupcodes'],
  537. 'viewer' => ['id' => 'viewer'],
  538. 'workflowengine' => ['id' => 'workflowengine'],
  539. 'oauth2' => ['id' => 'oauth2'],
  540. ];
  541. $manager->expects($this->any())
  542. ->method('getAppInfo')
  543. ->willReturnCallback(
  544. function ($appId) use ($appInfos) {
  545. return $appInfos[$appId];
  546. }
  547. );
  548. $this->appConfig->setValue('test1', 'enabled', 'yes');
  549. $this->appConfig->setValue('test1', 'installed_version', '1.0.0');
  550. $this->appConfig->setValue('test2', 'enabled', 'yes');
  551. $this->appConfig->setValue('test2', 'installed_version', '1.0.0');
  552. $this->appConfig->setValue('test3', 'enabled', 'yes');
  553. $this->appConfig->setValue('test3', 'installed_version', '1.0.0');
  554. $this->appConfig->setValue('test4', 'enabled', 'yes');
  555. $this->appConfig->setValue('test4', 'installed_version', '2.4.0');
  556. $apps = $manager->getAppsNeedingUpgrade('8.2.0');
  557. $this->assertCount(2, $apps);
  558. $this->assertEquals('test1', $apps[0]['id']);
  559. $this->assertEquals('test4', $apps[1]['id']);
  560. }
  561. public function testGetIncompatibleApps() {
  562. /** @var AppManager|MockObject $manager */
  563. $manager = $this->getMockBuilder(AppManager::class)
  564. ->setConstructorArgs([
  565. $this->userSession,
  566. $this->config,
  567. $this->appConfig,
  568. $this->groupManager,
  569. $this->cacheFactory,
  570. $this->eventDispatcher,
  571. $this->logger,
  572. $this->urlGenerator,
  573. ])
  574. ->onlyMethods(['getAppInfo'])
  575. ->getMock();
  576. $appInfos = [
  577. 'cloud_federation_api' => ['id' => 'cloud_federation_api'],
  578. 'dav' => ['id' => 'dav'],
  579. 'files' => ['id' => 'files'],
  580. 'federatedfilesharing' => ['id' => 'federatedfilesharing'],
  581. 'provisioning_api' => ['id' => 'provisioning_api'],
  582. 'lookup_server_connector' => ['id' => 'lookup_server_connector'],
  583. 'test1' => ['id' => 'test1', 'version' => '1.0.1', 'requiremax' => '8.0.0'],
  584. 'test2' => ['id' => 'test2', 'version' => '1.0.0', 'requiremin' => '8.2.0'],
  585. 'test3' => ['id' => 'test3', 'version' => '1.2.4', 'requiremin' => '9.0.0'],
  586. 'settings' => ['id' => 'settings'],
  587. 'testnoversion' => ['id' => 'testnoversion', 'requiremin' => '8.2.0'],
  588. 'theming' => ['id' => 'theming'],
  589. 'twofactor_backupcodes' => ['id' => 'twofactor_backupcodes'],
  590. 'workflowengine' => ['id' => 'workflowengine'],
  591. 'oauth2' => ['id' => 'oauth2'],
  592. 'viewer' => ['id' => 'viewer'],
  593. ];
  594. $manager->expects($this->any())
  595. ->method('getAppInfo')
  596. ->willReturnCallback(
  597. function ($appId) use ($appInfos) {
  598. return $appInfos[$appId];
  599. }
  600. );
  601. $this->appConfig->setValue('test1', 'enabled', 'yes');
  602. $this->appConfig->setValue('test2', 'enabled', 'yes');
  603. $this->appConfig->setValue('test3', 'enabled', 'yes');
  604. $apps = $manager->getIncompatibleApps('8.2.0');
  605. $this->assertCount(2, $apps);
  606. $this->assertEquals('test1', $apps[0]['id']);
  607. $this->assertEquals('test3', $apps[1]['id']);
  608. }
  609. public function testGetEnabledAppsForGroup() {
  610. $group = $this->createMock(IGroup::class);
  611. $group->expects($this->any())
  612. ->method('getGID')
  613. ->willReturn('foo');
  614. $this->appConfig->setValue('test1', 'enabled', 'yes');
  615. $this->appConfig->setValue('test2', 'enabled', 'no');
  616. $this->appConfig->setValue('test3', 'enabled', '["foo"]');
  617. $this->appConfig->setValue('test4', 'enabled', '["asd"]');
  618. $enabled = [
  619. 'cloud_federation_api',
  620. 'dav',
  621. 'federatedfilesharing',
  622. 'files',
  623. 'lookup_server_connector',
  624. 'oauth2',
  625. 'provisioning_api',
  626. 'settings',
  627. 'test1',
  628. 'test3',
  629. 'theming',
  630. 'twofactor_backupcodes',
  631. 'viewer',
  632. 'workflowengine',
  633. ];
  634. $this->assertEquals($enabled, $this->manager->getEnabledAppsForGroup($group));
  635. }
  636. public function testGetAppRestriction() {
  637. $this->appConfig->setValue('test1', 'enabled', 'yes');
  638. $this->appConfig->setValue('test2', 'enabled', 'no');
  639. $this->appConfig->setValue('test3', 'enabled', '["foo"]');
  640. $this->assertEquals([], $this->manager->getAppRestriction('test1'));
  641. $this->assertEquals([], $this->manager->getAppRestriction('test2'));
  642. $this->assertEquals(['foo'], $this->manager->getAppRestriction('test3'));
  643. }
  644. public function provideDefaultApps(): array {
  645. return [
  646. // none specified, default to files
  647. [
  648. '',
  649. '',
  650. '{}',
  651. true,
  652. 'files',
  653. ],
  654. // none specified, without fallback
  655. [
  656. '',
  657. '',
  658. '{}',
  659. false,
  660. '',
  661. ],
  662. // unexisting or inaccessible app specified, default to files
  663. [
  664. 'unexist',
  665. '',
  666. '{}',
  667. true,
  668. 'files',
  669. ],
  670. // unexisting or inaccessible app specified, without fallbacks
  671. [
  672. 'unexist',
  673. '',
  674. '{}',
  675. false,
  676. '',
  677. ],
  678. // non-standard app
  679. [
  680. 'settings',
  681. '',
  682. '{}',
  683. true,
  684. 'settings',
  685. ],
  686. // non-standard app, without fallback
  687. [
  688. 'settings',
  689. '',
  690. '{}',
  691. false,
  692. 'settings',
  693. ],
  694. // non-standard app with fallback
  695. [
  696. 'unexist,settings',
  697. '',
  698. '{}',
  699. true,
  700. 'settings',
  701. ],
  702. // system default app and user apporder
  703. [
  704. // system default is settings
  705. 'unexist,settings',
  706. '',
  707. // apporder says default app is files (order is lower)
  708. '{"files_id":{"app":"files","order":1},"settings_id":{"app":"settings","order":2}}',
  709. true,
  710. // system default should override apporder
  711. 'settings'
  712. ],
  713. // user-customized defaultapp
  714. [
  715. '',
  716. 'files',
  717. '',
  718. true,
  719. 'files',
  720. ],
  721. // user-customized defaultapp with systemwide
  722. [
  723. 'unexist,settings',
  724. 'files',
  725. '',
  726. true,
  727. 'files',
  728. ],
  729. // user-customized defaultapp with system wide and apporder
  730. [
  731. 'unexist,settings',
  732. 'files',
  733. '{"settings_id":{"app":"settings","order":1},"files_id":{"app":"files","order":2}}',
  734. true,
  735. 'files',
  736. ],
  737. // user-customized apporder fallback
  738. [
  739. '',
  740. '',
  741. '{"settings_id":{"app":"settings","order":1},"files":{"app":"files","order":2}}',
  742. true,
  743. 'settings',
  744. ],
  745. // user-customized apporder fallback with missing app key (entries added by closures does not always have an app key set (Nextcloud 27 spreed app for example))
  746. [
  747. '',
  748. '',
  749. '{"spreed":{"order":1},"files":{"app":"files","order":2}}',
  750. true,
  751. 'files',
  752. ],
  753. // user-customized apporder, but called without fallback
  754. [
  755. '',
  756. '',
  757. '{"settings":{"app":"settings","order":1},"files":{"app":"files","order":2}}',
  758. false,
  759. '',
  760. ],
  761. // user-customized apporder with an app that has multiple routes
  762. [
  763. '',
  764. '',
  765. '{"settings_id":{"app":"settings","order":1},"settings_id_2":{"app":"settings","order":3},"id_files":{"app":"files","order":2}}',
  766. true,
  767. 'settings',
  768. ],
  769. ];
  770. }
  771. /**
  772. * @dataProvider provideDefaultApps
  773. */
  774. public function testGetDefaultAppForUser($defaultApps, $userDefaultApps, $userApporder, $withFallbacks, $expectedApp) {
  775. $user = $this->newUser('user1');
  776. $this->userSession->expects($this->once())
  777. ->method('getUser')
  778. ->willReturn($user);
  779. $this->config->expects($this->once())
  780. ->method('getSystemValueString')
  781. ->with('defaultapp', $this->anything())
  782. ->willReturn($defaultApps);
  783. $this->config->expects($this->atLeastOnce())
  784. ->method('getUserValue')
  785. ->willReturnMap([
  786. ['user1', 'core', 'defaultapp', '', $userDefaultApps],
  787. ['user1', 'core', 'apporder', '[]', $userApporder],
  788. ]);
  789. $this->assertEquals($expectedApp, $this->manager->getDefaultAppForUser(null, $withFallbacks));
  790. }
  791. }