AppTest.php 13 KB

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