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