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