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