]> git.mxchange.org Git - friendica.git/blob - tests/src/Core/Console/AutomaticInstallationConsoleTest.php
5b83b014ca5f787dea05a353b0083cad18ba732b
[friendica.git] / tests / src / Core / Console / AutomaticInstallationConsoleTest.php
1 <?php
2
3 namespace Friendica\Test\src\Core\Console;
4
5 use Friendica\Core\Config\Cache\ConfigCache;
6 use Friendica\Core\Console\AutomaticInstallation;
7 use Friendica\Core\Installer;
8 use Friendica\Core\Logger;
9 use Friendica\Test\Util\DBAMockTrait;
10 use Friendica\Test\Util\DBStructureMockTrait;
11 use Friendica\Test\Util\L10nMockTrait;
12 use Friendica\Test\Util\RendererMockTrait;
13 use Friendica\Util\Logger\VoidLogger;
14 use org\bovigo\vfs\vfsStream;
15 use org\bovigo\vfs\vfsStreamFile;
16
17 /**
18  * @runTestsInSeparateProcesses
19  * @preserveGlobalState disabled
20  * @requires PHP 7.0
21  */
22 class AutomaticInstallationConsoleTest extends ConsoleTest
23 {
24         use L10nMockTrait;
25         use DBAMockTrait;
26         use DBStructureMockTrait;
27         use RendererMockTrait;
28
29         /**
30          * @var vfsStreamFile Assert file without DB credentials
31          */
32         private $assertFile;
33         /**
34          * @var vfsStreamFile Assert file with DB credentials
35          */
36         private $assertFileDb;
37
38         /**
39          * @var ConfigCache The configuration cache to check after each test
40          */
41         private $configCache;
42
43         public function setUp()
44         {
45                 parent::setUp();
46
47                 if ($this->root->hasChild('config' . DIRECTORY_SEPARATOR . 'local.config.php')) {
48                         $this->root->getChild('config')
49                                 ->removeChild('local.config.php');
50                 }
51
52                 $this->mockL10nT();
53
54                 $this->configCache = new ConfigCache();
55                 $this->configCache->set('system', 'basepath', $this->root->url());
56                 $this->configCache->set('config', 'php_path', trim(shell_exec('which php')));
57
58                 $this->mockApp($this->root, null, true);
59
60                 $this->configMock->shouldReceive('set')->andReturnUsing(function ($cat, $key, $value) {
61                         if ($key !== 'basepath') {
62                                 return $this->configCache->set($cat, $key, $value);
63                         } else {
64                                 return true;
65                         }
66                 });
67
68                 $this->configMock->shouldReceive('has')->andReturn(true);
69                 $this->configMock->shouldReceive('get')->andReturnUsing(function ($cat, $key) {
70                         return $this->configCache->get($cat, $key);
71                 });
72                 $this->configMock->shouldReceive('load')->andReturnUsing(function ($config, $overwrite = false) {
73                         return $this->configCache->load($config, $overwrite);
74                 });
75
76                 $this->mode->shouldReceive('isInstall')->andReturn(true);
77                 Logger::init(new VoidLogger());
78         }
79
80         /**
81          * Returns the dataset for each automatic installation test
82          *
83          * @return array the dataset
84          */
85         public function dataInstaller()
86         {
87                 return [
88                         'empty' => [
89                                 'data' => [
90                                         'database' => [
91                                                 'hostname'    => '',
92                                                 'username'    => '',
93                                                 'password'    => '',
94                                                 'database'    => '',
95                                                 'port'        => '',
96                                         ],
97                                         'config' => [
98                                                 'php_path'    => '',
99                                                 'hostname'    => '',
100                                                 'admin_email' => '',
101                                         ],
102                                         'system' => [
103                                                 'basepath'    => '',
104                                                 'urlpath'     => '',
105                                                 'url'         => '',
106                                                 'ssl_policy'  => '',
107                                                 'default_timezone' => '',
108                                                 'language'    => '',
109                                         ],
110                                 ],
111                         ],
112                         'normal' => [
113                                 'data' => [
114                                         'database' => [
115                                                 'hostname'    => 'testhost',
116                                                 'port'        => 3306,
117                                                 'username'    => 'friendica',
118                                                 'password'    => 'a password',
119                                                 'database'    => 'database',
120                                         ],
121                                         'config' => [
122                                                 'php_path'    => '',
123                                                 'hostname'    => 'friendica.local',
124                                                 'admin_email' => 'admin@philipp.info',
125                                         ],
126                                         'system' => [
127                                                 'urlpath'     => 'test/it',
128                                                 'url'         => 'friendica.local/test/it',
129                                                 'basepath'    => '',
130                                                 'ssl_policy'  => '2',
131                                                 'default_timezone' => 'en',
132                                                 'language'    => 'Europe/Berlin',
133                                         ],
134                                 ],
135                         ],
136                         'special' => [
137                                 'data' => [
138                                         'database' => [
139                                                 'hostname'    => 'testhost.new.domain',
140                                                 'port'        => 3341,
141                                                 'username'    => 'fr"ยง%ica',
142                                                 'password'    => '$%\"gse',
143                                                 'database'    => 'db',
144                                         ],
145                                         'config' => [
146                                                 'php_path'    => '',
147                                                 'hostname'    => 'friendica.local',
148                                                 'admin_email' => 'admin@philipp.info',
149                                         ],
150                                         'system' => [
151                                                 'urlpath'     => 'test/it',
152                                                 'url'         => 'friendica.local/test/it',
153                                                 'basepath'    => '',
154                                                 'ssl_policy'  => '1',
155                                                 'default_timezone' => 'en',
156                                                 'language'    => 'Europe/Berlin',
157                                         ],
158                                 ],
159                         ],
160                 ];
161         }
162
163         private function assertFinished($txt, $withconfig = false, $copyfile = false)
164         {
165                 $cfg = '';
166
167                 if ($withconfig) {
168                         $cfg = <<<CFG
169
170
171 Creating config file...
172
173  Complete!
174 CFG;
175                 }
176
177                 if ($copyfile) {
178                         $cfg = <<<CFG
179
180
181 Copying config file...
182
183  Complete!
184 CFG;
185                 }
186
187                 $finished = <<<FIN
188 Initializing setup...
189
190  Complete!
191
192
193 Checking environment...
194
195  NOTICE: Not checking .htaccess/URL-Rewrite during CLI installation.
196
197  Complete!
198 {$cfg}
199
200
201 Checking database...
202
203  Complete!
204
205
206 Inserting data into database...
207
208  Complete!
209
210
211 Installing theme
212
213  Complete
214
215
216
217 Installation is finished
218
219
220 FIN;
221                 $this->assertEquals($finished, $txt);
222         }
223
224         private function assertStuckDB($txt)
225         {
226                 $finished = <<<FIN
227 Initializing setup...
228
229  Complete!
230
231
232 Checking environment...
233
234  NOTICE: Not checking .htaccess/URL-Rewrite during CLI installation.
235
236  Complete!
237
238
239 Creating config file...
240
241  Complete!
242
243
244 Checking database...
245
246 [Error] --------
247 Could not connect to database.: 
248
249
250 FIN;
251
252                 $this->assertEquals($finished, $txt);
253         }
254
255         /**
256          * Asserts one config entry
257          *
258          * @param string     $cat           The category to test
259          * @param string     $key           The key to test
260          * @param null|array $assertion     The asserted value (null = empty, or array/string)
261          * @param string     $default_value The default value
262          */
263         public function assertConfigEntry($cat, $key, $assertion = null, $default_value = null)
264         {
265                 if (!empty($assertion[$cat][$key])) {
266                         $this->assertEquals($assertion[$cat][$key], $this->configCache->get($cat, $key));
267                 } elseif (!empty($assertion) && !is_array($assertion)) {
268                         $this->assertEquals($assertion, $this->configCache->get($cat, $key));
269                 } elseif (!empty($default_value)) {
270                         $this->assertEquals($default_value, $this->configCache->get($cat, $key));
271                 } else {
272                         $this->assertEmpty($this->configCache->get($cat, $key), $this->configCache->get($cat, $key));
273                 }
274         }
275
276         /**
277          * Asserts all config entries
278          *
279          * @param null|array $assertion    The optional assertion array
280          * @param boolean    $createConfig True, if a config file has to get generated
281          * @param boolean    $saveDb       True, if the db credentials should get saved to the file
282          * @param boolean    $default      True, if we use the default values
283          * @param boolean    $defaultDb    True, if we use the default value for the DB
284          */
285         public function assertConfig($assertion = null, $createConfig = true, $saveDb = false, $default = true, $defaultDb = true)
286         {
287                 if (!empty($assertion['database']['hostname'])) {
288                         $assertion['database']['hostname'] .= (!empty($assertion['database']['port']) ? ':' . $assertion['database']['port'] : '');
289                 }
290
291                 $this->assertConfigEntry('database', 'hostname', ($saveDb) ? $assertion : null, (!$saveDb || $defaultDb) ? Installer::DEFAULT_HOST : null);
292                 $this->assertConfigEntry('database', 'username', ($saveDb) ? $assertion : null);
293                 $this->assertConfigEntry('database', 'password', ($saveDb) ? $assertion : null);
294                 $this->assertConfigEntry('database', 'database', ($saveDb) ? $assertion : null);
295
296                 $this->assertConfigEntry('config', 'hostname', $assertion);
297                 $this->assertConfigEntry('config', 'admin_email', $assertion);
298                 $this->assertConfigEntry('config', 'php_path', trim(shell_exec('which php')));
299
300                 $this->assertConfigEntry('system', 'default_timezone', $assertion, ($default) ? Installer::DEFAULT_TZ : null);
301                 $this->assertConfigEntry('system', 'language', $assertion, ($default) ? Installer::DEFAULT_LANG : null);
302                 $this->assertConfigEntry('system', 'ssl_policy', $assertion, ($default) ? SSL_POLICY_NONE : null);
303                 $this->assertConfigEntry('system', 'urlpath', $assertion);
304                 $this->assertConfigEntry('system', 'basepath', $createConfig ? $this->root->url() : $assertion);
305         }
306
307         /**
308          * Test the automatic installation without any parameter/setting
309          */
310         public function testEmpty()
311         {
312                 $this->mockConnect(true, 1);
313                 $this->mockConnected(true, 1);
314                 $this->mockExistsTable('user', false, 1);
315                 $this->mockUpdate([$this->root->url(), false, true, true], null, 1);
316
317                 $this->mockGetMarkupTemplate('local.config.tpl', 'testTemplate', 1);
318                 $this->mockReplaceMacros('testTemplate', \Mockery::any(), '', 1);
319
320                 $console = new AutomaticInstallation($this->consoleArgv);
321
322                 $txt = $this->dumpExecute($console);
323
324                 $this->assertFinished($txt, true, false);
325                 $this->assertTrue($this->root->hasChild('config' . DIRECTORY_SEPARATOR . 'local.config.php'));
326
327                 $this->assertConfig();
328         }
329
330         /**
331          * Test the automatic installation with a prepared config file
332          * @dataProvider dataInstaller
333          */
334         public function testWithConfig(array $data)
335         {
336                 $this->mockConnect(true, 1);
337                 $this->mockConnected(true, 1);
338                 $this->mockExistsTable('user', false, 1);
339                 $this->mockUpdate([$this->root->url(), false, true, true], null, 1);
340
341                 $conf = function ($cat, $key) use ($data) {
342                         if ($cat == 'database' && $key == 'hostname' && !empty($data['database']['port'])) {
343                                 return $data[$cat][$key] . ':' . $data['database']['port'];
344                         }
345                         return $data[$cat][$key];
346                 };
347
348                 $config = <<<CONF
349 <?php
350
351 // Local configuration
352
353 // If you're unsure about what any of the config keys below do, please check the config/defaults.config.php for detailed
354 // documentation of their data type and behavior.
355
356 return [
357         'database' => [
358                 'hostname' => '{$conf('database', 'hostname')}',
359                 'username' => '{$conf('database', 'username')}',
360                 'password' => '{$conf('database', 'password')}',
361                 'database' => '{$conf('database', 'database')}',
362                 'charset' => 'utf8mb4',
363         ],
364
365         // ****************************************************************
366         // The configuration below will be overruled by the admin panel.
367         // Changes made below will only have an effect if the database does
368         // not contain any configuration for the friendica system.
369         // ****************************************************************
370
371         'config' => [
372                 'admin_email' => '{$conf('config', 'admin_email')}',
373                 'hostname' => '{$conf('config', 'hostname')}',
374                 'sitename' => 'Friendica Social Network',
375                 'register_policy' => \Friendica\Module\Register::OPEN,
376                 'register_text' => '',
377         ],
378         'system' => [
379                 'basepath' => '{$conf('system', 'basepath')}',
380                 'urlpath' => '{$conf('system', 'urlpath')}',
381                 'url' => '{$conf('system', 'url')}',
382                 'default_timezone' => '{$conf('system', 'default_timezone')}',
383                 'language' => '{$conf('system', 'language')}',
384                 'ssl_policy' => '{$conf('system', 'ssl_policy')}',
385         ],
386 ];
387 CONF;
388
389                 vfsStream::newFile('prepared.config.php')
390                         ->at($this->root)
391                         ->setContent($config);
392
393                 $console = new AutomaticInstallation($this->consoleArgv);
394                 $console->setOption('f', 'prepared.config.php');
395
396                 $txt = $this->dumpExecute($console);
397
398                 $this->assertFinished($txt, false, true);
399
400                 $this->assertTrue($this->root->hasChild('config' . DIRECTORY_SEPARATOR . 'local.config.php'));
401                 $this->assertEquals($config, file_get_contents($this->root->getChild('config' . DIRECTORY_SEPARATOR . 'local.config.php')->url()));
402
403                 $this->assertConfig($data, false, true, false, false);
404         }
405
406         /**
407          * Test the automatic installation with environment variables
408          * Includes saving the DB credentials to the file
409          * @dataProvider dataInstaller
410          */
411         public function testWithEnvironmentAndSave(array $data)
412         {
413                 $this->mockConnect(true, 1);
414                 $this->mockConnected(true, 1);
415                 $this->mockExistsTable('user', false, 1);
416                 $this->mockUpdate([$this->root->url(), false, true, true], null, 1);
417
418                 $this->mockGetMarkupTemplate('local.config.tpl', 'testTemplate', 1);
419                 $this->mockReplaceMacros('testTemplate', \Mockery::any(), '', 1);
420
421                 $this->assertTrue(putenv('MYSQL_HOST='     . $data['database']['hostname']));
422                 $this->assertTrue(putenv('MYSQL_PORT='     . $data['database']['port']));
423                 $this->assertTrue(putenv('MYSQL_DATABASE=' . $data['database']['database']));
424                 $this->assertTrue(putenv('MYSQL_USERNAME=' . $data['database']['username']));
425                 $this->assertTrue(putenv('MYSQL_PASSWORD=' . $data['database']['password']));
426
427                 $this->assertTrue(putenv('FRIENDICA_URL_PATH='   . $data['system']['urlpath']));
428                 $this->assertTrue(putenv('FRIENDICA_BASE_PATH='  . $data['system']['basepath']));
429                 $this->assertTrue(putenv('FRIENDICA_PHP_PATH='   . $data['config']['php_path']));
430                 $this->assertTrue(putenv('FRIENDICA_SSL_POLICY=' . $data['system']['ssl_policy']));
431                 $this->assertTrue(putenv('FRIENDICA_HOSTNAME='   . $data['config']['hostname']));
432                 $this->assertTrue(putenv('FRIENDICA_ADMIN_MAIL=' . $data['config']['admin_email']));
433                 $this->assertTrue(putenv('FRIENDICA_TZ='         . $data['system']['default_timezone']));
434                 $this->assertTrue(putenv('FRIENDICA_LANG='       . $data['system']['language']));
435
436                 $console = new AutomaticInstallation($this->consoleArgv);
437                 $console->setOption('savedb', true);
438
439                 $txt = $this->dumpExecute($console);
440
441                 $this->assertFinished($txt, true);
442                 $this->assertConfig($data, true, true, true, false);
443         }
444
445         /**
446          * Test the automatic installation with environment variables
447          * Don't save the db credentials to the file
448          * @dataProvider dataInstaller
449          */
450         public function testWithEnvironmentWithoutSave(array $data)
451         {
452                 $this->mockConnect(true, 1);
453                 $this->mockConnected(true, 1);
454                 $this->mockExistsTable('user', false, 1);
455                 $this->mockUpdate([$this->root->url(), false, true, true], null, 1);
456
457                 $this->mockGetMarkupTemplate('local.config.tpl', 'testTemplate', 1);
458                 $this->mockReplaceMacros('testTemplate', \Mockery::any(), '', 1);
459
460                 $this->assertTrue(putenv('MYSQL_HOST=' . $data['database']['hostname']));
461                 $this->assertTrue(putenv('MYSQL_PORT=' . $data['database']['port']));
462                 $this->assertTrue(putenv('MYSQL_DATABASE=' . $data['database']['database']));
463                 $this->assertTrue(putenv('MYSQL_USERNAME=' . $data['database']['username']));
464                 $this->assertTrue(putenv('MYSQL_PASSWORD=' . $data['database']['password']));
465
466                 $this->assertTrue(putenv('FRIENDICA_URL_PATH=' . $data['system']['urlpath']));
467                 $this->assertTrue(putenv('FRIENDICA_BASE_PATH=' . $data['system']['basepath']));
468                 $this->assertTrue(putenv('FRIENDICA_PHP_PATH=' . $data['config']['php_path']));
469                 $this->assertTrue(putenv('FRIENDICA_SSL_POLICY=' . $data['system']['ssl_policy']));
470                 $this->assertTrue(putenv('FRIENDICA_HOSTNAME=' . $data['config']['hostname']));
471                 $this->assertTrue(putenv('FRIENDICA_ADMIN_MAIL=' . $data['config']['admin_email']));
472                 $this->assertTrue(putenv('FRIENDICA_TZ=' . $data['system']['default_timezone']));
473                 $this->assertTrue(putenv('FRIENDICA_LANG=' . $data['system']['language']));
474
475                 $console = new AutomaticInstallation($this->consoleArgv);
476
477                 $txt = $this->dumpExecute($console);
478
479                 $this->assertFinished($txt, true);
480                 $this->assertConfig($data, true, false, true);
481         }
482
483         /**
484          * Test the automatic installation with arguments
485          * @dataProvider dataInstaller
486          */
487         public function testWithArguments(array $data)
488         {
489                 $this->mockConnect(true, 1);
490                 $this->mockConnected(true, 1);
491                 $this->mockExistsTable('user', false, 1);
492                 $this->mockUpdate([$this->root->url(), false, true, true], null, 1);
493
494                 $this->mockGetMarkupTemplate('local.config.tpl', 'testTemplate', 1);
495                 $this->mockReplaceMacros('testTemplate', \Mockery::any(), '', 1);
496
497                 $console = new AutomaticInstallation($this->consoleArgv);
498
499                 $option = function($var, $cat, $key) use ($data, $console) {
500                         if (!empty($data[$cat][$key])) {
501                                 $console->setOption($var, $data[$cat][$key]);
502                         }
503                 };
504                 $option('dbhost'   , 'database', 'hostname');
505                 $option('dbport'   , 'database', 'port');
506                 $option('dbuser'   , 'database', 'username');
507                 $option('dbpass'   , 'database', 'password');
508                 $option('dbdata'   , 'database', 'database');
509                 $option('urlpath'  , 'system'  , 'urlpath');
510                 $option('basepath' , 'system'  , 'basepath');
511                 $option('phppath'  , 'config'  , 'php_path');
512                 $option('sslpolicy', 'system'  , 'ssl_policy');
513                 $option('hostname' , 'config'  , 'hostname');
514                 $option('admin'    , 'config'  , 'admin_email');
515                 $option('tz'       , 'system'  , 'default_timezone');
516                 $option('lang'     , 'system'  , 'language');
517
518                 $txt = $this->dumpExecute($console);
519
520                 $this->assertFinished($txt, true);
521                 $this->assertConfig($data, true, true, true, true);
522         }
523
524         /**
525          * Test the automatic installation with a wrong database connection
526          */
527         public function testNoDatabaseConnection()
528         {
529                 $this->mockConnect(false, 1);
530
531                 $this->mockGetMarkupTemplate('local.config.tpl', 'testTemplate', 1);
532                 $this->mockReplaceMacros('testTemplate', \Mockery::any(), '', 1);
533
534                 $console = new AutomaticInstallation($this->consoleArgv);
535
536                 $txt = $this->dumpExecute($console);
537
538                 $this->assertStuckDB($txt);
539                 $this->assertTrue($this->root->hasChild('config' . DIRECTORY_SEPARATOR . 'local.config.php'));
540
541                 $this->assertConfig(null, true, false, true, false);
542         }
543
544         public function testGetHelp()
545         {
546                 // Usable to purposely fail if new commands are added without taking tests into account
547                 $theHelp = <<<HELP
548 Installation - Install Friendica automatically
549 Synopsis
550         bin/console autoinstall [-h|--help|-?] [-v] [-a] [-f]
551
552 Description
553     Installs Friendica with data based on the local.config.php file or environment variables
554
555 Notes
556     Not checking .htaccess/URL-Rewrite during CLI installation.
557
558 Options
559     -h|--help|-?            Show help information
560     -v                      Show more debug information.
561     -a                      All setup checks are required (except .htaccess)
562     -f|--file <config>      prepared config file (e.g. "config/local.config.php" itself) which will override every other config option - except the environment variables)
563     -s|--savedb                 Save the DB credentials to the file (if environment variables is used)
564     -H|--dbhost <host>          The host of the mysql/mariadb database (env MYSQL_HOST)
565     -p|--dbport <port>          The port of the mysql/mariadb database (env MYSQL_PORT)
566     -d|--dbdata <database>      The name of the mysql/mariadb database (env MYSQL_DATABASE)
567     -U|--dbuser <username>      The username of the mysql/mariadb database login (env MYSQL_USER or MYSQL_USERNAME)
568     -P|--dbpass <password>      The password of the mysql/mariadb database login (env MYSQL_PASSWORD)
569     -U|--urlpath <url_path>     The URL path of Friendica - f.e. '/friendica' (env FRIENDICA_URL_PATH) 
570     -B|--phppath <php_path>     The path of the PHP binary (env FRIENDICA_PHP_PATH)
571     -b|--basepath <base_path>   The basepath of Friendica(env FRIENDICA_BASE_PATH)
572     -S|--sslpolicy <ssl_policy> The SSL policy of Friendica (env FRIENDICA_SSL_POLICY) 
573     -n|--hostname <hostname>    The hostname of Friendica (env FRIENDICA_PHP_HOSTNAME)  
574     -t|--tz <timezone>          The timezone of Friendica (env FRIENDICA_TZ)
575     -L|--lang <language>        The language of Friendica (env FRIENDICA_LANG)
576  
577 Environment variables
578    MYSQL_HOST                  The host of the mysql/mariadb database (mandatory if mysql and environment is used)
579    MYSQL_PORT                  The port of the mysql/mariadb database
580    MYSQL_USERNAME|MYSQL_USER   The username of the mysql/mariadb database login (MYSQL_USERNAME is for mysql, MYSQL_USER for mariadb)
581    MYSQL_PASSWORD              The password of the mysql/mariadb database login
582    MYSQL_DATABASE              The name of the mysql/mariadb database
583    FRIENDICA_URL_PATH          The URL path of Friendica (f.e. '/friendica') - leave empty for auto detection
584    FRIENDICA_PHP_PATH          The path of the PHP binary - leave empty for auto detection
585    FRIENDICA_BASE_PATH         The basepath of Friendica - leave empty for auto detection
586    FRIENDICA_ADMIN_MAIL        The admin email address of Friendica (this email will be used for admin access)
587    FRIENDICA_SSL_POLICY        The SSL policy of Friendica (default is NO SSL)
588    FRIENDICA_HOSTNAME          The hostname of Friendica - leave empty for auto detection
589    FRIENDICA_TZ                The timezone of Friendica
590    FRIENDICA_LANG              The langauge of Friendica
591    
592 Examples
593         bin/console autoinstall -f 'input.config.php
594                 Installs Friendica with the prepared 'input.config.php' file
595
596         bin/console autoinstall --savedb
597                 Installs Friendica with environment variables and saves them to the 'config/local.config.php' file
598
599         bin/console autoinstall -h localhost -p 3365 -U user -P passwort1234 -d friendica
600                 Installs Friendica with a local mysql database with credentials
601
602 HELP;
603
604                 $console = new AutomaticInstallation($this->consoleArgv);
605                 $console->setOption('help', true);
606
607                 $txt = $this->dumpExecute($console);
608
609                 $this->assertEquals($txt, $theHelp);
610         }
611 }