Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

459 lines
12 KiB

  1. <?php
  2. // this is in the same namespace as Install for mocking 'function_exists'
  3. namespace Friendica\Core;
  4. use Dice\Dice;
  5. use Friendica\BaseObject;
  6. use Friendica\Core\Config\Cache\ConfigCache;
  7. use Friendica\Network\CurlResult;
  8. use Friendica\Object\Image;
  9. use Friendica\Test\MockedTest;
  10. use Friendica\Test\Util\VFSTrait;
  11. use Friendica\Util\Network;
  12. use Mockery\MockInterface;
  13. class InstallerTest extends MockedTest
  14. {
  15. use VFSTrait;
  16. /**
  17. * @var \Friendica\Core\L10n\L10n|MockInterface
  18. */
  19. private $l10nMock;
  20. public function setUp()
  21. {
  22. parent::setUp();
  23. $this->setUpVfsDir();
  24. $this->l10nMock = \Mockery::mock(\Friendica\Core\L10n\L10n::class);
  25. /** @var Dice|MockInterface $dice */
  26. $dice = \Mockery::mock(Dice::class)->makePartial();
  27. $dice = $dice->addRules(include __DIR__ . '/../../../static/dependencies.config.php');
  28. $dice->shouldReceive('create')
  29. ->with(\Friendica\Core\L10n\L10n::class)
  30. ->andReturn($this->l10nMock);
  31. BaseObject::setDependencyInjection($dice);
  32. }
  33. private function mockL10nT(string $text, $times = null)
  34. {
  35. $this->l10nMock->shouldReceive('t')->with($text)->andReturn($text)->times($times);
  36. }
  37. /**
  38. * Mocking the L10n::t() calls for the function checks
  39. */
  40. private function mockFunctionL10TCalls()
  41. {
  42. $this->mockL10nT('Apache mod_rewrite module', 1);
  43. $this->mockL10nT('PDO or MySQLi PHP module', 1);
  44. $this->mockL10nT('libCurl PHP module', 1);
  45. $this->mockL10nT('Error: libCURL PHP module required but not installed.', 1);
  46. $this->mockL10nT('XML PHP module', 1);
  47. $this->mockL10nT('GD graphics PHP module', 1);
  48. $this->mockL10nT('Error: GD graphics PHP module with JPEG support required but not installed.', 1);
  49. $this->mockL10nT('OpenSSL PHP module', 1);
  50. $this->mockL10nT('Error: openssl PHP module required but not installed.', 1);
  51. $this->mockL10nT('mb_string PHP module', 1);
  52. $this->mockL10nT('Error: mb_string PHP module required but not installed.', 1);
  53. $this->mockL10nT('iconv PHP module', 1);
  54. $this->mockL10nT('Error: iconv PHP module required but not installed.', 1);
  55. $this->mockL10nT('POSIX PHP module', 1);
  56. $this->mockL10nT('Error: POSIX PHP module required but not installed.', 1);
  57. $this->mockL10nT('JSON PHP module', 1);
  58. $this->mockL10nT('Error: JSON PHP module required but not installed.', 1);
  59. $this->mockL10nT('File Information PHP module', 1);
  60. $this->mockL10nT('Error: File Information PHP module required but not installed.', 1);
  61. }
  62. private function assertCheckExist($position, $title, $help, $status, $required, $assertionArray)
  63. {
  64. $this->assertArraySubset([$position => [
  65. 'title' => $title,
  66. 'status' => $status,
  67. 'required' => $required,
  68. 'error_msg' => null,
  69. 'help' => $help]
  70. ], $assertionArray);
  71. }
  72. /**
  73. * Replaces function_exists results with given mocks
  74. *
  75. * @param array $functions a list from function names and their result
  76. */
  77. private function setFunctions($functions)
  78. {
  79. global $phpMock;
  80. $phpMock['function_exists'] = function($function) use ($functions) {
  81. foreach ($functions as $name => $value) {
  82. if ($function == $name) {
  83. return $value;
  84. }
  85. }
  86. return '__phpunit_continue__';
  87. };
  88. }
  89. /**
  90. * Replaces class_exist results with given mocks
  91. *
  92. * @param array $classes a list from class names and their results
  93. */
  94. private function setClasses($classes)
  95. {
  96. global $phpMock;
  97. $phpMock['class_exists'] = function($class) use ($classes) {
  98. foreach ($classes as $name => $value) {
  99. if ($class == $name) {
  100. return $value;
  101. }
  102. }
  103. return '__phpunit_continue__';
  104. };
  105. }
  106. /**
  107. * @small
  108. */
  109. public function testCheckKeys()
  110. {
  111. $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
  112. $this->setFunctions(['openssl_pkey_new' => false]);
  113. $install = new Installer();
  114. $this->assertFalse($install->checkKeys());
  115. $this->setFunctions(['openssl_pkey_new' => true]);
  116. $install = new Installer();
  117. $this->assertTrue($install->checkKeys());
  118. }
  119. /**
  120. * @small
  121. */
  122. public function testCheckFunctions()
  123. {
  124. $this->mockFunctionL10TCalls();
  125. $this->setFunctions(['curl_init' => false, 'imagecreatefromjpeg' => true]);
  126. $install = new Installer();
  127. $this->assertFalse($install->checkFunctions());
  128. $this->assertCheckExist(3,
  129. 'libCurl PHP module',
  130. 'Error: libCURL PHP module required but not installed.',
  131. false,
  132. true,
  133. $install->getChecks());
  134. $this->mockFunctionL10TCalls();
  135. $this->setFunctions(['imagecreatefromjpeg' => false]);
  136. $install = new Installer();
  137. $this->assertFalse($install->checkFunctions());
  138. $this->assertCheckExist(4,
  139. 'GD graphics PHP module',
  140. 'Error: GD graphics PHP module with JPEG support required but not installed.',
  141. false,
  142. true,
  143. $install->getChecks());
  144. $this->mockFunctionL10TCalls();
  145. $this->setFunctions(['openssl_public_encrypt' => false]);
  146. $install = new Installer();
  147. $this->assertFalse($install->checkFunctions());
  148. $this->assertCheckExist(5,
  149. 'OpenSSL PHP module',
  150. 'Error: openssl PHP module required but not installed.',
  151. false,
  152. true,
  153. $install->getChecks());
  154. $this->mockFunctionL10TCalls();
  155. $this->setFunctions(['mb_strlen' => false]);
  156. $install = new Installer();
  157. $this->assertFalse($install->checkFunctions());
  158. $this->assertCheckExist(6,
  159. 'mb_string PHP module',
  160. 'Error: mb_string PHP module required but not installed.',
  161. false,
  162. true,
  163. $install->getChecks());
  164. $this->mockFunctionL10TCalls();
  165. $this->setFunctions(['iconv_strlen' => false]);
  166. $install = new Installer();
  167. $this->assertFalse($install->checkFunctions());
  168. $this->assertCheckExist(7,
  169. 'iconv PHP module',
  170. 'Error: iconv PHP module required but not installed.',
  171. false,
  172. true,
  173. $install->getChecks());
  174. $this->mockFunctionL10TCalls();
  175. $this->setFunctions(['posix_kill' => false]);
  176. $install = new Installer();
  177. $this->assertFalse($install->checkFunctions());
  178. $this->assertCheckExist(8,
  179. 'POSIX PHP module',
  180. 'Error: POSIX PHP module required but not installed.',
  181. false,
  182. true,
  183. $install->getChecks());
  184. $this->mockFunctionL10TCalls();
  185. $this->setFunctions(['json_encode' => false]);
  186. $install = new Installer();
  187. $this->assertFalse($install->checkFunctions());
  188. $this->assertCheckExist(9,
  189. 'JSON PHP module',
  190. 'Error: JSON PHP module required but not installed.',
  191. false,
  192. true,
  193. $install->getChecks());
  194. $this->mockFunctionL10TCalls();
  195. $this->setFunctions(['finfo_open' => false]);
  196. $install = new Installer();
  197. $this->assertFalse($install->checkFunctions());
  198. $this->assertCheckExist(10,
  199. 'File Information PHP module',
  200. 'Error: File Information PHP module required but not installed.',
  201. false,
  202. true,
  203. $install->getChecks());
  204. $this->mockFunctionL10TCalls();
  205. $this->setFunctions([
  206. 'curl_init' => true,
  207. 'imagecreatefromjpeg' => true,
  208. 'openssl_public_encrypt' => true,
  209. 'mb_strlen' => true,
  210. 'iconv_strlen' => true,
  211. 'posix_kill' => true,
  212. 'json_encode' => true,
  213. 'finfo_open' => true,
  214. ]);
  215. $install = new Installer();
  216. $this->assertTrue($install->checkFunctions());
  217. }
  218. /**
  219. * @small
  220. */
  221. public function testCheckLocalIni()
  222. {
  223. $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
  224. $this->assertTrue($this->root->hasChild('config/local.config.php'));
  225. $install = new Installer();
  226. $this->assertTrue($install->checkLocalIni());
  227. $this->delConfigFile('local.config.php');
  228. $this->assertFalse($this->root->hasChild('config/local.config.php'));
  229. $install = new Installer();
  230. $this->assertTrue($install->checkLocalIni());
  231. }
  232. /**
  233. * @small
  234. * @runInSeparateProcess
  235. * @preserveGlobalState disabled
  236. */
  237. public function testCheckHtAccessFail()
  238. {
  239. $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
  240. // Mocking the CURL Response
  241. $curlResult = \Mockery::mock(CurlResult::class);
  242. $curlResult
  243. ->shouldReceive('getReturnCode')
  244. ->andReturn('404');
  245. $curlResult
  246. ->shouldReceive('getRedirectUrl')
  247. ->andReturn('');
  248. $curlResult
  249. ->shouldReceive('getError')
  250. ->andReturn('test Error');
  251. // Mocking the CURL Request
  252. $networkMock = \Mockery::mock('alias:' . Network::class);
  253. $networkMock
  254. ->shouldReceive('fetchUrlFull')
  255. ->with('https://test/install/testrewrite')
  256. ->andReturn($curlResult);
  257. $networkMock
  258. ->shouldReceive('fetchUrlFull')
  259. ->with('http://test/install/testrewrite')
  260. ->andReturn($curlResult);
  261. // Mocking that we can use CURL
  262. $this->setFunctions(['curl_init' => true]);
  263. $install = new Installer();
  264. $this->assertFalse($install->checkHtAccess('https://test'));
  265. $this->assertSame('test Error', $install->getChecks()[0]['error_msg']['msg']);
  266. }
  267. /**
  268. * @small
  269. * @runInSeparateProcess
  270. * @preserveGlobalState disabled
  271. */
  272. public function testCheckHtAccessWork()
  273. {
  274. $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
  275. // Mocking the failed CURL Response
  276. $curlResultF = \Mockery::mock(CurlResult::class);
  277. $curlResultF
  278. ->shouldReceive('getReturnCode')
  279. ->andReturn('404');
  280. // Mocking the working CURL Response
  281. $curlResultW = \Mockery::mock(CurlResult::class);
  282. $curlResultW
  283. ->shouldReceive('getReturnCode')
  284. ->andReturn('204');
  285. // Mocking the CURL Request
  286. $networkMock = \Mockery::mock('alias:' . Network::class);
  287. $networkMock
  288. ->shouldReceive('fetchUrlFull')
  289. ->with('https://test/install/testrewrite')
  290. ->andReturn($curlResultF);
  291. $networkMock
  292. ->shouldReceive('fetchUrlFull')
  293. ->with('http://test/install/testrewrite')
  294. ->andReturn($curlResultW);
  295. // Mocking that we can use CURL
  296. $this->setFunctions(['curl_init' => true]);
  297. $install = new Installer();
  298. $this->assertTrue($install->checkHtAccess('https://test'));
  299. }
  300. /**
  301. * @small
  302. * @runInSeparateProcess
  303. * @preserveGlobalState disabled
  304. */
  305. public function testImagick()
  306. {
  307. $this->markTestIncomplete('needs adapted class_exists() mock');
  308. $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
  309. $this->setClasses(['Imagick' => true]);
  310. $install = new Installer();
  311. // even there is no supported type, Imagick should return true (because it is not required)
  312. $this->assertTrue($install->checkImagick());
  313. $this->assertCheckExist(1,
  314. L10n::t('ImageMagick supports GIF'),
  315. '',
  316. true,
  317. false,
  318. $install->getChecks());
  319. }
  320. /**
  321. * @small
  322. * @runInSeparateProcess
  323. * @preserveGlobalState disabled
  324. */
  325. public function testImagickNotFound()
  326. {
  327. $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
  328. $this->setClasses(['Imagick' => true]);
  329. $install = new Installer();
  330. // even there is no supported type, Imagick should return true (because it is not required)
  331. $this->assertTrue($install->checkImagick());
  332. $this->assertCheckExist(1,
  333. L10n::t('ImageMagick supports GIF'),
  334. '',
  335. false,
  336. false,
  337. $install->getChecks());
  338. }
  339. public function testImagickNotInstalled()
  340. {
  341. $this->setClasses(['Imagick' => false]);
  342. $this->mockL10nT('ImageMagick PHP extension is not installed');
  343. $install = new Installer();
  344. // even there is no supported type, Imagick should return true (because it is not required)
  345. $this->assertTrue($install->checkImagick());
  346. $this->assertCheckExist(0,
  347. 'ImageMagick PHP extension is not installed',
  348. '',
  349. false,
  350. false,
  351. $install->getChecks());
  352. }
  353. /**
  354. * Test the setup of the config cache for installation
  355. */
  356. public function testSetUpCache()
  357. {
  358. $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
  359. $install = new Installer();
  360. $configCache = \Mockery::mock(ConfigCache::class);
  361. $configCache->shouldReceive('set')->with('config', 'php_path', \Mockery::any())->once();
  362. $configCache->shouldReceive('set')->with('system', 'basepath', '/test/')->once();
  363. $install->setUpCache($configCache, '/test/');
  364. }
  365. }
  366. /**
  367. * A workaround to replace the PHP native function_exists with a mocked function
  368. *
  369. * @param string $function_name the Name of the function
  370. *
  371. * @return bool true or false
  372. */
  373. function function_exists($function_name)
  374. {
  375. global $phpMock;
  376. if (isset($phpMock['function_exists'])) {
  377. $result = call_user_func_array($phpMock['function_exists'], func_get_args());
  378. if ($result !== '__phpunit_continue__') {
  379. return $result;
  380. }
  381. }
  382. return call_user_func_array('\function_exists', func_get_args());
  383. }
  384. function class_exists($class_name)
  385. {
  386. global $phpMock;
  387. if (isset($phpMock['class_exists'])) {
  388. $result = call_user_func_array($phpMock['class_exists'], func_get_args());
  389. if ($result !== '__phpunit_continue__') {
  390. return $result;
  391. }
  392. }
  393. return call_user_func_array('\class_exists', func_get_args());
  394. }