AppManagerTest.php 22 KB

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