1
0

AppTest.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace Test;
  8. use OC\App\AppManager;
  9. use OC\App\InfoParser;
  10. use OC\AppConfig;
  11. use OCP\EventDispatcher\IEventDispatcher;
  12. use OCP\IAppConfig;
  13. use OCP\IURLGenerator;
  14. use PHPUnit\Framework\MockObject\MockObject;
  15. use Psr\Log\LoggerInterface;
  16. /**
  17. * Class AppTest
  18. *
  19. * @group DB
  20. */
  21. class AppTest extends \Test\TestCase {
  22. public const TEST_USER1 = 'user1';
  23. public const TEST_USER2 = 'user2';
  24. public const TEST_USER3 = 'user3';
  25. public const TEST_GROUP1 = 'group1';
  26. public const TEST_GROUP2 = 'group2';
  27. public function appVersionsProvider() {
  28. return [
  29. // exact match
  30. [
  31. '6.0.0.0',
  32. [
  33. 'requiremin' => '6.0',
  34. 'requiremax' => '6.0',
  35. ],
  36. true
  37. ],
  38. // in-between match
  39. [
  40. '6.0.0.0',
  41. [
  42. 'requiremin' => '5.0',
  43. 'requiremax' => '7.0',
  44. ],
  45. true
  46. ],
  47. // app too old
  48. [
  49. '6.0.0.0',
  50. [
  51. 'requiremin' => '5.0',
  52. 'requiremax' => '5.0',
  53. ],
  54. false
  55. ],
  56. // app too new
  57. [
  58. '5.0.0.0',
  59. [
  60. 'requiremin' => '6.0',
  61. 'requiremax' => '6.0',
  62. ],
  63. false
  64. ],
  65. // only min specified
  66. [
  67. '6.0.0.0',
  68. [
  69. 'requiremin' => '6.0',
  70. ],
  71. true
  72. ],
  73. // only min specified fail
  74. [
  75. '5.0.0.0',
  76. [
  77. 'requiremin' => '6.0',
  78. ],
  79. false
  80. ],
  81. // only min specified legacy
  82. [
  83. '6.0.0.0',
  84. [
  85. 'require' => '6.0',
  86. ],
  87. true
  88. ],
  89. // only min specified legacy fail
  90. [
  91. '4.0.0.0',
  92. [
  93. 'require' => '6.0',
  94. ],
  95. false
  96. ],
  97. // only max specified
  98. [
  99. '5.0.0.0',
  100. [
  101. 'requiremax' => '6.0',
  102. ],
  103. true
  104. ],
  105. // only max specified fail
  106. [
  107. '7.0.0.0',
  108. [
  109. 'requiremax' => '6.0',
  110. ],
  111. false
  112. ],
  113. // variations of versions
  114. // single OC number
  115. [
  116. '4',
  117. [
  118. 'require' => '4.0',
  119. ],
  120. true
  121. ],
  122. // multiple OC number
  123. [
  124. '4.3.1',
  125. [
  126. 'require' => '4.3',
  127. ],
  128. true
  129. ],
  130. // single app number
  131. [
  132. '4',
  133. [
  134. 'require' => '4',
  135. ],
  136. true
  137. ],
  138. // single app number fail
  139. [
  140. '4.3',
  141. [
  142. 'require' => '5',
  143. ],
  144. false
  145. ],
  146. // complex
  147. [
  148. '5.0.0',
  149. [
  150. 'require' => '4.5.1',
  151. ],
  152. true
  153. ],
  154. // complex fail
  155. [
  156. '4.3.1',
  157. [
  158. 'require' => '4.3.2',
  159. ],
  160. false
  161. ],
  162. // two numbers
  163. [
  164. '4.3.1',
  165. [
  166. 'require' => '4.4',
  167. ],
  168. false
  169. ],
  170. // one number fail
  171. [
  172. '4.3.1',
  173. [
  174. 'require' => '5',
  175. ],
  176. false
  177. ],
  178. // pre-alpha app
  179. [
  180. '5.0.3',
  181. [
  182. 'require' => '4.93',
  183. ],
  184. true
  185. ],
  186. // pre-alpha OC
  187. [
  188. '6.90.0.2',
  189. [
  190. 'require' => '6.90',
  191. ],
  192. true
  193. ],
  194. // pre-alpha OC max
  195. [
  196. '6.90.0.2',
  197. [
  198. 'requiremax' => '7',
  199. ],
  200. true
  201. ],
  202. // expect same major number match
  203. [
  204. '5.0.3',
  205. [
  206. 'require' => '5',
  207. ],
  208. true
  209. ],
  210. // expect same major number match
  211. [
  212. '5.0.3',
  213. [
  214. 'requiremax' => '5',
  215. ],
  216. true
  217. ],
  218. // dependencies versions before require*
  219. [
  220. '6.0.0.0',
  221. [
  222. 'requiremin' => '5.0',
  223. 'requiremax' => '7.0',
  224. 'dependencies' => [
  225. 'owncloud' => [
  226. '@attributes' => [
  227. 'min-version' => '7.0',
  228. 'max-version' => '7.0',
  229. ],
  230. ],
  231. ],
  232. ],
  233. false
  234. ],
  235. [
  236. '6.0.0.0',
  237. [
  238. 'requiremin' => '5.0',
  239. 'requiremax' => '7.0',
  240. 'dependencies' => [
  241. 'owncloud' => [
  242. '@attributes' => [
  243. 'min-version' => '5.0',
  244. 'max-version' => '5.0',
  245. ],
  246. ],
  247. ],
  248. ],
  249. false
  250. ],
  251. [
  252. '6.0.0.0',
  253. [
  254. 'requiremin' => '5.0',
  255. 'requiremax' => '5.0',
  256. 'dependencies' => [
  257. 'owncloud' => [
  258. '@attributes' => [
  259. 'min-version' => '5.0',
  260. 'max-version' => '7.0',
  261. ],
  262. ],
  263. ],
  264. ],
  265. true
  266. ],
  267. [
  268. '9.2.0.0',
  269. [
  270. 'dependencies' => [
  271. 'owncloud' => [
  272. '@attributes' => [
  273. 'min-version' => '9.0',
  274. 'max-version' => '9.1',
  275. ],
  276. ],
  277. 'nextcloud' => [
  278. '@attributes' => [
  279. 'min-version' => '9.1',
  280. 'max-version' => '9.2',
  281. ],
  282. ],
  283. ],
  284. ],
  285. true
  286. ],
  287. [
  288. '9.2.0.0',
  289. [
  290. 'dependencies' => [
  291. 'nextcloud' => [
  292. '@attributes' => [
  293. 'min-version' => '9.1',
  294. 'max-version' => '9.2',
  295. ],
  296. ],
  297. ],
  298. ],
  299. true
  300. ],
  301. ];
  302. }
  303. /**
  304. * @dataProvider appVersionsProvider
  305. */
  306. public function testIsAppCompatible($ocVersion, $appInfo, $expectedResult) {
  307. $this->assertEquals($expectedResult, \OC_App::isAppCompatible($ocVersion, $appInfo));
  308. }
  309. /**
  310. * Tests that the app order is correct
  311. */
  312. public function testGetEnabledAppsIsSorted() {
  313. $apps = \OC_App::getEnabledApps();
  314. // copy array
  315. $sortedApps = $apps;
  316. sort($sortedApps);
  317. // 'files' is always on top
  318. unset($sortedApps[array_search('files', $sortedApps)]);
  319. array_unshift($sortedApps, 'files');
  320. $this->assertEquals($sortedApps, $apps);
  321. }
  322. /**
  323. * Providers for the app config values
  324. */
  325. public function appConfigValuesProvider() {
  326. return [
  327. // logged in user1
  328. [
  329. self::TEST_USER1,
  330. [
  331. 'files',
  332. 'app1',
  333. 'app3',
  334. 'appforgroup1',
  335. 'appforgroup12',
  336. 'cloud_federation_api',
  337. 'dav',
  338. 'federatedfilesharing',
  339. 'lookup_server_connector',
  340. 'oauth2',
  341. 'provisioning_api',
  342. 'settings',
  343. 'theming',
  344. 'twofactor_backupcodes',
  345. 'viewer',
  346. 'workflowengine',
  347. ],
  348. false
  349. ],
  350. // logged in user2
  351. [
  352. self::TEST_USER2,
  353. [
  354. 'files',
  355. 'app1',
  356. 'app3',
  357. 'appforgroup12',
  358. 'appforgroup2',
  359. 'cloud_federation_api',
  360. 'dav',
  361. 'federatedfilesharing',
  362. 'lookup_server_connector',
  363. 'oauth2',
  364. 'provisioning_api',
  365. 'settings',
  366. 'theming',
  367. 'twofactor_backupcodes',
  368. 'viewer',
  369. 'workflowengine',
  370. ],
  371. false
  372. ],
  373. // logged in user3
  374. [
  375. self::TEST_USER3,
  376. [
  377. 'files',
  378. 'app1',
  379. 'app3',
  380. 'appforgroup1',
  381. 'appforgroup12',
  382. 'appforgroup2',
  383. 'cloud_federation_api',
  384. 'dav',
  385. 'federatedfilesharing',
  386. 'lookup_server_connector',
  387. 'oauth2',
  388. 'provisioning_api',
  389. 'settings',
  390. 'theming',
  391. 'twofactor_backupcodes',
  392. 'viewer',
  393. 'workflowengine',
  394. ],
  395. false
  396. ],
  397. // no user, returns all apps
  398. [
  399. null,
  400. [
  401. 'files',
  402. 'app1',
  403. 'app3',
  404. 'appforgroup1',
  405. 'appforgroup12',
  406. 'appforgroup2',
  407. 'cloud_federation_api',
  408. 'dav',
  409. 'federatedfilesharing',
  410. 'lookup_server_connector',
  411. 'oauth2',
  412. 'provisioning_api',
  413. 'settings',
  414. 'theming',
  415. 'twofactor_backupcodes',
  416. 'viewer',
  417. 'workflowengine',
  418. ],
  419. false,
  420. ],
  421. // user given, but ask for all
  422. [
  423. self::TEST_USER1,
  424. [
  425. 'files',
  426. 'app1',
  427. 'app3',
  428. 'appforgroup1',
  429. 'appforgroup12',
  430. 'appforgroup2',
  431. 'cloud_federation_api',
  432. 'dav',
  433. 'federatedfilesharing',
  434. 'lookup_server_connector',
  435. 'oauth2',
  436. 'provisioning_api',
  437. 'settings',
  438. 'theming',
  439. 'twofactor_backupcodes',
  440. 'viewer',
  441. 'workflowengine',
  442. ],
  443. true,
  444. ],
  445. ];
  446. }
  447. /**
  448. * Test enabled apps
  449. *
  450. * @dataProvider appConfigValuesProvider
  451. */
  452. public function testEnabledApps($user, $expectedApps, $forceAll) {
  453. $userManager = \OC::$server->getUserManager();
  454. $groupManager = \OC::$server->getGroupManager();
  455. $user1 = $userManager->createUser(self::TEST_USER1, self::TEST_USER1);
  456. $user2 = $userManager->createUser(self::TEST_USER2, self::TEST_USER2);
  457. $user3 = $userManager->createUser(self::TEST_USER3, self::TEST_USER3);
  458. $group1 = $groupManager->createGroup(self::TEST_GROUP1);
  459. $group1->addUser($user1);
  460. $group1->addUser($user3);
  461. $group2 = $groupManager->createGroup(self::TEST_GROUP2);
  462. $group2->addUser($user2);
  463. $group2->addUser($user3);
  464. \OC_User::setUserId($user);
  465. $this->setupAppConfigMock()->expects($this->once())
  466. ->method('getValues')
  467. ->willReturn(
  468. [
  469. 'app3' => 'yes',
  470. 'app2' => 'no',
  471. 'app1' => 'yes',
  472. 'appforgroup1' => '["group1"]',
  473. 'appforgroup2' => '["group2"]',
  474. 'appforgroup12' => '["group2","group1"]',
  475. ]
  476. );
  477. $apps = \OC_App::getEnabledApps(false, $forceAll);
  478. $this->restoreAppConfig();
  479. \OC_User::setUserId(null);
  480. $user1->delete();
  481. $user2->delete();
  482. $user3->delete();
  483. $group1->delete();
  484. $group2->delete();
  485. $this->assertEquals($expectedApps, $apps);
  486. }
  487. /**
  488. * Test isEnabledApps() with cache, not re-reading the list of
  489. * enabled apps more than once when a user is set.
  490. */
  491. public function testEnabledAppsCache() {
  492. $userManager = \OC::$server->getUserManager();
  493. $user1 = $userManager->createUser(self::TEST_USER1, self::TEST_USER1);
  494. \OC_User::setUserId(self::TEST_USER1);
  495. $this->setupAppConfigMock()->expects($this->once())
  496. ->method('getValues')
  497. ->willReturn(
  498. [
  499. 'app3' => 'yes',
  500. 'app2' => 'no',
  501. ]
  502. );
  503. $apps = \OC_App::getEnabledApps();
  504. $this->assertEquals(['files', 'app3', 'cloud_federation_api', 'dav', 'federatedfilesharing', 'lookup_server_connector', 'oauth2', 'provisioning_api', 'settings', 'theming', 'twofactor_backupcodes', 'viewer', 'workflowengine'], $apps);
  505. // mock should not be called again here
  506. $apps = \OC_App::getEnabledApps();
  507. $this->assertEquals(['files', 'app3', 'cloud_federation_api', 'dav', 'federatedfilesharing', 'lookup_server_connector', 'oauth2', 'provisioning_api', 'settings', 'theming', 'twofactor_backupcodes', 'viewer', 'workflowengine'], $apps);
  508. $this->restoreAppConfig();
  509. \OC_User::setUserId(null);
  510. $user1->delete();
  511. }
  512. private function setupAppConfigMock() {
  513. /** @var AppConfig|MockObject */
  514. $appConfig = $this->getMockBuilder(AppConfig::class)
  515. ->setMethods(['getValues'])
  516. ->setConstructorArgs([\OC::$server->getDatabaseConnection()])
  517. ->disableOriginalConstructor()
  518. ->getMock();
  519. $this->registerAppConfig($appConfig);
  520. return $appConfig;
  521. }
  522. /**
  523. * Register an app config mock for testing purposes.
  524. *
  525. * @param IAppConfig $appConfig app config mock
  526. */
  527. private function registerAppConfig(AppConfig $appConfig) {
  528. $this->overwriteService(AppConfig::class, $appConfig);
  529. $this->overwriteService(AppManager::class, new AppManager(
  530. \OC::$server->getUserSession(),
  531. \OC::$server->getConfig(),
  532. \OC::$server->getGroupManager(),
  533. \OC::$server->getMemCacheFactory(),
  534. \OC::$server->get(IEventDispatcher::class),
  535. \OC::$server->get(LoggerInterface::class),
  536. \OC::$server->get(IURLGenerator::class),
  537. ));
  538. }
  539. /**
  540. * Restore the original app config service.
  541. */
  542. private function restoreAppConfig() {
  543. $this->restoreService(AppConfig::class);
  544. $this->restoreService(AppManager::class);
  545. // Remove the cache of the mocked apps list with a forceRefresh
  546. \OC_App::getEnabledApps();
  547. }
  548. /**
  549. * Providers for the app data values
  550. */
  551. public function appDataProvider() {
  552. return [
  553. [
  554. ['description' => " \t This is a multiline \n test with \n \t \n \n some new lines "],
  555. ['description' => "This is a multiline \n test with \n \t \n \n some new lines"],
  556. ],
  557. [
  558. ['description' => " \t This is a multiline \n test with \n \t some new lines "],
  559. ['description' => "This is a multiline \n test with \n \t some new lines"],
  560. ],
  561. [
  562. ['description' => hex2bin('5065726d657420646520732761757468656e7469666965722064616e732070697769676f20646972656374656d656e74206176656320736573206964656e74696669616e7473206f776e636c6f75642073616e73206c65732072657461706572206574206d657420c3a0206a6f757273206365757820636920656e20636173206465206368616e67656d656e74206465206d6f742064652070617373652e0d0a0d')],
  563. ['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."],
  564. ],
  565. [
  566. ['not-a-description' => " \t This is a multiline \n test with \n \t some new lines "],
  567. [
  568. 'not-a-description' => " \t This is a multiline \n test with \n \t some new lines ",
  569. 'description' => '',
  570. ],
  571. ],
  572. [
  573. ['description' => [100, 'bla']],
  574. ['description' => ''],
  575. ],
  576. ];
  577. }
  578. /**
  579. * Test app info parser
  580. *
  581. * @dataProvider appDataProvider
  582. * @param array $data
  583. * @param array $expected
  584. */
  585. public function testParseAppInfo(array $data, array $expected) {
  586. $this->assertSame($expected, \OC_App::parseAppInfo($data));
  587. }
  588. public function testParseAppInfoL10N() {
  589. $parser = new InfoParser();
  590. $data = $parser->parse(\OC::$SERVERROOT. "/tests/data/app/description-multi-lang.xml");
  591. $this->assertEquals('English', \OC_App::parseAppInfo($data, 'en')['description']);
  592. $this->assertEquals('German', \OC_App::parseAppInfo($data, 'de')['description']);
  593. }
  594. public function testParseAppInfoL10NSingleLanguage() {
  595. $parser = new InfoParser();
  596. $data = $parser->parse(\OC::$SERVERROOT. "/tests/data/app/description-single-lang.xml");
  597. $this->assertEquals('English', \OC_App::parseAppInfo($data, 'en')['description']);
  598. }
  599. }