]> git.mxchange.org Git - friendica.git/blob - tests/src/Core/InstallerTest.php
Removed test mode
[friendica.git] / tests / src / Core / InstallerTest.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2020, Friendica
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 // this is in the same namespace as Install for mocking 'function_exists'
23 namespace Friendica\Core;
24
25 use Dice\Dice;
26 use Friendica\Core\Config\Cache;
27 use Friendica\DI;
28 use Friendica\Network\CurlResult;
29 use Friendica\Test\MockedTest;
30 use Friendica\Test\Util\VFSTrait;
31 use Friendica\Util\Network;
32 use Mockery\MockInterface;
33
34 class InstallerTest extends MockedTest
35 {
36         use VFSTrait;
37
38         /**
39          * @var \Friendica\Core\L10n|MockInterface
40          */
41         private $l10nMock;
42
43         public function setUp()
44         {
45                 parent::setUp();
46
47                 $this->setUpVfsDir();
48
49                 $this->l10nMock = \Mockery::mock(\Friendica\Core\L10n::class);
50
51                 /** @var Dice|MockInterface $dice */
52                 $dice = \Mockery::mock(Dice::class)->makePartial();
53                 $dice = $dice->addRules(include __DIR__ . '/../../../static/dependencies.config.php');
54
55                 $dice->shouldReceive('create')
56                            ->with(\Friendica\Core\L10n::class)
57                            ->andReturn($this->l10nMock);
58
59                 DI::init($dice);
60         }
61
62         private function mockL10nT(string $text, $times = null)
63         {
64                 $this->l10nMock->shouldReceive('t')->with($text)->andReturn($text)->times($times);
65         }
66
67         /**
68          * Mocking the DI::l10n()->t() calls for the function checks
69          */
70         private function mockFunctionL10TCalls()
71         {
72                 $this->mockL10nT('Apache mod_rewrite module', 1);
73                 $this->mockL10nT('PDO or MySQLi PHP module', 1);
74                 $this->mockL10nT('libCurl PHP module', 1);
75                 $this->mockL10nT('Error: libCURL PHP module required but not installed.', 1);
76                 $this->mockL10nT('XML PHP module', 1);
77                 $this->mockL10nT('GD graphics PHP module', 1);
78                 $this->mockL10nT('Error: GD graphics PHP module with JPEG support required but not installed.', 1);
79                 $this->mockL10nT('OpenSSL PHP module', 1);
80                 $this->mockL10nT('Error: openssl PHP module required but not installed.', 1);
81                 $this->mockL10nT('mb_string PHP module', 1);
82                 $this->mockL10nT('Error: mb_string PHP module required but not installed.', 1);
83                 $this->mockL10nT('iconv PHP module', 1);
84                 $this->mockL10nT('Error: iconv PHP module required but not installed.', 1);
85                 $this->mockL10nT('POSIX PHP module', 1);
86                 $this->mockL10nT('Error: POSIX PHP module required but not installed.', 1);
87                 $this->mockL10nT('JSON PHP module', 1);
88                 $this->mockL10nT('Error: JSON PHP module required but not installed.', 1);
89                 $this->mockL10nT('File Information PHP module', 1);
90                 $this->mockL10nT('Error: File Information PHP module required but not installed.', 1);
91         }
92
93         private function assertCheckExist($position, $title, $help, $status, $required, $assertionArray)
94         {
95                 $subSet = [$position => [
96                         'title' => $title,
97                         'status' => $status,
98                         'required' => $required,
99                         'error_msg' => null,
100                         'help' => $help]
101                 ];
102
103                 $this->assertArraySubset($subSet, $assertionArray, false, "expected subset: " . PHP_EOL . print_r($subSet, true) . PHP_EOL . "current subset: " . print_r($assertionArray, true));
104         }
105
106         /**
107          * Replaces function_exists results with given mocks
108          *
109          * @param array $functions a list from function names and their result
110          */
111         private function setFunctions($functions)
112         {
113                 global $phpMock;
114                 $phpMock['function_exists'] = function($function) use ($functions) {
115                         foreach ($functions as $name => $value) {
116                                 if ($function == $name) {
117                                         return $value;
118                                 }
119                         }
120                         return '__phpunit_continue__';
121                 };
122         }
123
124         /**
125          * Replaces class_exist results with given mocks
126          *
127          * @param array $classes a list from class names and their results
128          */
129         private function setClasses($classes)
130         {
131                 global $phpMock;
132                 $phpMock['class_exists'] = function($class) use ($classes) {
133                         foreach ($classes as $name => $value) {
134                                 if ($class == $name) {
135                                         return $value;
136                                 }
137                         }
138                         return '__phpunit_continue__';
139                 };
140         }
141
142         /**
143          * @small
144          */
145         public function testCheckKeys()
146         {
147                 $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
148
149                 $this->setFunctions(['openssl_pkey_new' => false]);
150                 $install = new Installer();
151                 $this->assertFalse($install->checkKeys());
152
153                 $this->setFunctions(['openssl_pkey_new' => true]);
154                 $install = new Installer();
155                 $this->assertTrue($install->checkKeys());
156         }
157
158         /**
159          * @small
160          */
161         public function testCheckFunctions()
162         {
163                 $this->mockFunctionL10TCalls();
164                 $this->setFunctions(['curl_init' => false, 'imagecreatefromjpeg' => true]);
165                 $install = new Installer();
166                 $this->assertFalse($install->checkFunctions());
167                 $this->assertCheckExist(3,
168                         'libCurl PHP module',
169                         'Error: libCURL PHP module required but not installed.',
170                         false,
171                         true,
172                         $install->getChecks());
173
174                 $this->mockFunctionL10TCalls();
175                 $this->setFunctions(['imagecreatefromjpeg' => false]);
176                 $install = new Installer();
177                 $this->assertFalse($install->checkFunctions());
178                 $this->assertCheckExist(4,
179                         'GD graphics PHP module',
180                         'Error: GD graphics PHP module with JPEG support required but not installed.',
181                         false,
182                         true,
183                         $install->getChecks());
184
185                 $this->mockFunctionL10TCalls();
186                 $this->setFunctions(['openssl_public_encrypt' => false]);
187                 $install = new Installer();
188                 $this->assertFalse($install->checkFunctions());
189                 $this->assertCheckExist(5,
190                         'OpenSSL PHP module',
191                         'Error: openssl PHP module required but not installed.',
192                         false,
193                         true,
194                         $install->getChecks());
195
196                 $this->mockFunctionL10TCalls();
197                 $this->setFunctions(['mb_strlen' => false]);
198                 $install = new Installer();
199                 $this->assertFalse($install->checkFunctions());
200                 $this->assertCheckExist(6,
201                         'mb_string PHP module',
202                         'Error: mb_string PHP module required but not installed.',
203                         false,
204                         true,
205                         $install->getChecks());
206
207                 $this->mockFunctionL10TCalls();
208                 $this->setFunctions(['iconv_strlen' => false]);
209                 $install = new Installer();
210                 $this->assertFalse($install->checkFunctions());
211                 $this->assertCheckExist(7,
212                         'iconv PHP module',
213                         'Error: iconv PHP module required but not installed.',
214                         false,
215                         true,
216                         $install->getChecks());
217
218                 $this->mockFunctionL10TCalls();
219                 $this->setFunctions(['posix_kill' => false]);
220                 $install = new Installer();
221                 $this->assertFalse($install->checkFunctions());
222                 $this->assertCheckExist(8,
223                         'POSIX PHP module',
224                         'Error: POSIX PHP module required but not installed.',
225                         false,
226                         true,
227                         $install->getChecks());
228
229                 $this->mockFunctionL10TCalls();
230                 $this->setFunctions(['json_encode' => false]);
231                 $install = new Installer();
232                 $this->assertFalse($install->checkFunctions());
233                 $this->assertCheckExist(9,
234                         'JSON PHP module',
235                         'Error: JSON PHP module required but not installed.',
236                         false,
237                         true,
238                         $install->getChecks());
239
240                 $this->mockFunctionL10TCalls();
241                 $this->setFunctions(['finfo_open' => false]);
242                 $install = new Installer();
243                 $this->assertFalse($install->checkFunctions());
244                 $this->assertCheckExist(10,
245                         'File Information PHP module',
246                         'Error: File Information PHP module required but not installed.',
247                         false,
248                         true,
249                         $install->getChecks());
250
251                 $this->mockFunctionL10TCalls();
252                 $this->setFunctions([
253                         'curl_init' => true,
254                         'imagecreatefromjpeg' => true,
255                         'openssl_public_encrypt' => true,
256                         'mb_strlen' => true,
257                         'iconv_strlen' => true,
258                         'posix_kill' => true,
259                         'json_encode' => true,
260                         'finfo_open' => true,
261                 ]);
262                 $install = new Installer();
263                 $this->assertTrue($install->checkFunctions());
264         }
265
266         /**
267          * @small
268          */
269         public function testCheckLocalIni()
270         {
271                 $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
272
273                 $this->assertTrue($this->root->hasChild('config/local.config.php'));
274
275                 $install = new Installer();
276                 $this->assertTrue($install->checkLocalIni());
277
278                 $this->delConfigFile('local.config.php');
279
280                 $this->assertFalse($this->root->hasChild('config/local.config.php'));
281
282                 $install = new Installer();
283                 $this->assertTrue($install->checkLocalIni());
284         }
285
286         /**
287          * @small
288          * @runInSeparateProcess
289          * @preserveGlobalState disabled
290          */
291         public function testCheckHtAccessFail()
292         {
293                 $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
294
295                 // Mocking the CURL Response
296                 $curlResult = \Mockery::mock(CurlResult::class);
297                 $curlResult
298                         ->shouldReceive('getReturnCode')
299                         ->andReturn('404');
300                 $curlResult
301                         ->shouldReceive('getRedirectUrl')
302                         ->andReturn('');
303                 $curlResult
304                         ->shouldReceive('getError')
305                         ->andReturn('test Error');
306
307                 // Mocking the CURL Request
308                 $networkMock = \Mockery::mock('alias:' . Network::class);
309                 $networkMock
310                         ->shouldReceive('fetchUrlFull')
311                         ->with('https://test/install/testrewrite')
312                         ->andReturn($curlResult);
313                 $networkMock
314                         ->shouldReceive('fetchUrlFull')
315                         ->with('http://test/install/testrewrite')
316                         ->andReturn($curlResult);
317
318                 // Mocking that we can use CURL
319                 $this->setFunctions(['curl_init' => true]);
320
321                 $install = new Installer();
322
323                 $this->assertFalse($install->checkHtAccess('https://test'));
324                 $this->assertSame('test Error', $install->getChecks()[0]['error_msg']['msg']);
325         }
326
327         /**
328          * @small
329          * @runInSeparateProcess
330          * @preserveGlobalState disabled
331          */
332         public function testCheckHtAccessWork()
333         {
334                 $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
335
336                 // Mocking the failed CURL Response
337                 $curlResultF = \Mockery::mock(CurlResult::class);
338                 $curlResultF
339                         ->shouldReceive('getReturnCode')
340                         ->andReturn('404');
341
342                 // Mocking the working CURL Response
343                 $curlResultW = \Mockery::mock(CurlResult::class);
344                 $curlResultW
345                         ->shouldReceive('getReturnCode')
346                         ->andReturn('204');
347
348                 // Mocking the CURL Request
349                 $networkMock = \Mockery::mock('alias:' . Network::class);
350                 $networkMock
351                         ->shouldReceive('fetchUrlFull')
352                         ->with('https://test/install/testrewrite')
353                         ->andReturn($curlResultF);
354                 $networkMock
355                         ->shouldReceive('fetchUrlFull')
356                         ->with('http://test/install/testrewrite')
357                         ->andReturn($curlResultW);
358
359                 // Mocking that we can use CURL
360                 $this->setFunctions(['curl_init' => true]);
361
362                 $install = new Installer();
363
364                 $this->assertTrue($install->checkHtAccess('https://test'));
365         }
366
367         /**
368          * @small
369          * @runInSeparateProcess
370          * @preserveGlobalState disabled
371          */
372         public function testImagick()
373         {
374                 $this->markTestIncomplete('needs adapted class_exists() mock');
375
376                 $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
377
378                 $this->setClasses(['Imagick' => true]);
379
380                 $install = new Installer();
381
382                 // even there is no supported type, Imagick should return true (because it is not required)
383                 $this->assertTrue($install->checkImagick());
384
385                 $this->assertCheckExist(1,
386                         $this->l10nMock->t('ImageMagick supports GIF'),
387                         '',
388                         true,
389                         false,
390                         $install->getChecks());
391         }
392
393         /**
394          * @small
395          * @runInSeparateProcess
396          * @preserveGlobalState disabled
397          */
398         public function testImagickNotFound()
399         {
400                 $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
401
402                 $this->setClasses(['Imagick' => true]);
403
404                 $install = new Installer();
405
406                 // even there is no supported type, Imagick should return true (because it is not required)
407                 $this->assertTrue($install->checkImagick());
408                 $this->assertCheckExist(1,
409                         $this->l10nMock->t('ImageMagick supports GIF'),
410                         '',
411                         false,
412                         false,
413                         $install->getChecks());
414         }
415
416         public function testImagickNotInstalled()
417         {
418                 $this->setClasses(['Imagick' => false]);
419                 $this->mockL10nT('ImageMagick PHP extension is not installed');
420
421                 $install = new Installer();
422
423                 // even there is no supported type, Imagick should return true (because it is not required)
424                 $this->assertTrue($install->checkImagick());
425                 $this->assertCheckExist(0,
426                         'ImageMagick PHP extension is not installed',
427                         '',
428                         false,
429                         false,
430                         $install->getChecks());
431         }
432
433         /**
434          * Test the setup of the config cache for installation
435          */
436         public function testSetUpCache()
437         {
438                 $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
439
440                 $install = new Installer();
441                 $configCache = \Mockery::mock(Cache::class);
442                 $configCache->shouldReceive('set')->with('config', 'php_path', \Mockery::any())->once();
443                 $configCache->shouldReceive('set')->with('system', 'basepath', '/test/')->once();
444
445                 $install->setUpCache($configCache, '/test/');
446         }
447 }
448
449 /**
450  * A workaround to replace the PHP native function_exists with a mocked function
451  *
452  * @param string $function_name the Name of the function
453  *
454  * @return bool true or false
455  */
456 function function_exists($function_name)
457 {
458         global $phpMock;
459         if (isset($phpMock['function_exists'])) {
460                 $result = call_user_func_array($phpMock['function_exists'], func_get_args());
461                 if ($result !== '__phpunit_continue__') {
462                         return $result;
463                 }
464         }
465         return call_user_func_array('\function_exists', func_get_args());
466 }
467
468 function class_exists($class_name)
469 {
470         global $phpMock;
471         if (isset($phpMock['class_exists'])) {
472                 $result = call_user_func_array($phpMock['class_exists'], func_get_args());
473                 if ($result !== '__phpunit_continue__') {
474                         return $result;
475                 }
476         }
477         return call_user_func_array('\class_exists', func_get_args());
478 }