diff --git a/PHPCI/Command/InstallCommand.php b/PHPCI/Command/InstallCommand.php index e4af4722..e24fe2f3 100644 --- a/PHPCI/Command/InstallCommand.php +++ b/PHPCI/Command/InstallCommand.php @@ -23,7 +23,6 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Helper\DialogHelper; use PHPCI\Service\UserService; - /** * Install console command - Installs PHPCI. * @author Dan Cryer @@ -32,10 +31,23 @@ use PHPCI\Service\UserService; */ class InstallCommand extends Command { + protected $configFilePath; + protected function configure() { + $defaultPath = PHPCI_DIR . 'PHPCI/config.yml'; + $this ->setName('phpci:install') + ->addOption('url', null, InputOption::VALUE_OPTIONAL, 'PHPCI Installation URL') + ->addOption('db-host', null, InputOption::VALUE_OPTIONAL, 'Database hostname') + ->addOption('db-name', null, InputOption::VALUE_OPTIONAL, 'Database name') + ->addOption('db-user', null, InputOption::VALUE_OPTIONAL, 'Database username') + ->addOption('db-pass', null, InputOption::VALUE_OPTIONAL, 'Database password') + ->addOption('admin-name', null, InputOption::VALUE_OPTIONAL, 'Admin username') + ->addOption('admin-pass', null, InputOption::VALUE_OPTIONAL, 'Admin password') + ->addOption('admin-mail', null, InputOption::VALUE_OPTIONAL, 'Admin e-mail') + ->addOption('config-path', null, InputOption::VALUE_OPTIONAL, 'Config file path', $defaultPath) ->setDescription('Install PHPCI.'); } @@ -44,6 +56,8 @@ class InstallCommand extends Command */ protected function execute(InputInterface $input, OutputInterface $output) { + $this->configFilePath = $input->getOption('config-path'); + $this->verifyNotInstalled($output); $output->writeln(''); @@ -58,60 +72,39 @@ class InstallCommand extends Command $output->writeln('-------------------------------------'); $output->writeln(''); - - /** - * @var \Symfony\Component\Console\Helper\DialogHelper - */ - $dialog = $this->getHelperSet()->get('dialog'); - // ---- // Get MySQL connection information and verify that it works: // ---- $connectionVerified = false; while (!$connectionVerified) { - $db = array(); - $db['servers']['read'] = $dialog->ask($output, 'Please enter your MySQL host [localhost]: ', 'localhost'); - $db['servers']['write'] = $db['servers']['read']; - $db['name'] = $dialog->ask($output, 'Please enter your database name [phpci]: ', 'phpci'); - $db['username'] = $dialog->ask($output, 'Please enter your database username [phpci]: ', 'phpci'); - $db['password'] = $dialog->askHiddenResponse($output, 'Please enter your database password: '); + $db = $this->getDatabaseInformation($input, $output); $connectionVerified = $this->verifyDatabaseDetails($db, $output); } $output->writeln(''); + $conf = array(); + $conf['b8']['database'] = $db; + // ---- // Get basic installation details (URL, etc) // ---- - - $conf = array(); - $conf['b8']['database'] = $db; - $conf['phpci']['url'] = $dialog->askAndValidate( - $output, - 'Your PHPCI URL ("http://phpci.local" for example): ', - function ($answer) { - if (!filter_var($answer, FILTER_VALIDATE_URL)) { - throw new Exception('Must be a valid URL'); - } - - return rtrim($answer, '/'); - }, - false - ); + $conf['phpci'] = $this->getPhpciConfigInformation($input, $output); $this->writeConfigFile($conf); $this->setupDatabase($output); - $this->createAdminUser($output, $dialog); + $admin = $this->getAdminInforamtion($input, $output); + $this->createAdminUser($admin, $output); } /** * Check PHP version, required modules and for disabled functions. + * * @param OutputInterface $output * @throws \Exception */ - protected function checkRequirements(OutputInterface $output) { $output->write('Checking requirements...'); @@ -160,6 +153,127 @@ class InstallCommand extends Command $output->writeln(''); } + /** + * Load information for admin user form CLI options or ask info to user. + * + * @param InputInterface $input + * @param OutputInterface $output + * @return array + */ + protected function getAdminInforamtion(InputInterface $input, OutputInterface $output) + { + $admin = array(); + + /** + * @var \Symfony\Component\Console\Helper\DialogHelper + */ + $dialog = $this->getHelperSet()->get('dialog'); + + // Function to validate mail address. + $mailValidator =function ($answer) { + if (!filter_var($answer, FILTER_VALIDATE_EMAIL)) { + throw new Exception('Must be a valid email address.'); + } + + return $answer; + }; + + if ($adminEmail = $input->getOption('admin-mail')) { + $adminEmail = $mailValidator($adminEmail); + } else { + $adminEmail = $dialog->askAndValidate($output, 'Your email address: ', $mailValidator, false); + } + if (!$adminName = $input->getOption('admin-name')) { + $adminName = $dialog->ask($output, 'Enter your name: '); + } + if (!$adminPass = $input->getOption('admin-pass')) { + $adminPass = $dialog->askHiddenResponse($output, 'Enter your desired admin password: '); + } + + $admin['mail'] = $adminEmail; + $admin['name'] = $adminName; + $admin['pass'] = $adminPass; + + return $admin; + } + + /** + * Load configuration for PHPCI form CLI options or ask info to user. + * + * @param InputInterface $input + * @param OutputInterface $output + * @return array + */ + protected function getPhpciConfigInformation(InputInterface $input, OutputInterface $output) + { + $phpci = array(); + + /** + * @var \Symfony\Component\Console\Helper\DialogHelper + */ + $dialog = $this->getHelperSet()->get('dialog'); + + // FUnction do validate URL. + $urlValidator = function ($answer) { + if (!filter_var($answer, FILTER_VALIDATE_URL)) { + throw new Exception('Must be a valid URL'); + } + + return rtrim($answer, '/'); + }; + + if ($url = $input->getOption('url')) { + $url = $urlValidator($url); + } else { + $url = $dialog->askAndValidate($output, 'Your PHPCI URL ("http://phpci.local" for example): ', $urlValidator, false); + } + + $phpci['url'] = $url; + + return $phpci; + } + + /** + * Load configuration for DB form CLI options or ask info to user. + * + * @param InputInterface $input + * @param OutputInterface $output + * @return array + */ + protected function getDatabaseInformation(InputInterface $input, OutputInterface $output) + { + $db = array(); + + /** + * @var \Symfony\Component\Console\Helper\DialogHelper + */ + $dialog = $this->getHelperSet()->get('dialog'); + + if (!$dbHost = $input->getOption('db-host')) { + $dbHost = $dialog->ask($output, 'Please enter your MySQL host [localhost]: ', 'localhost'); + } + + if (!$dbName = $input->getOption('db-name')) { + $dbName = $dialog->ask($output, 'Please enter your database name [phpci]: ', 'phpci'); + } + + if (!$dbUser = $input->getOption('db-user')) { + $dbUser = $dialog->ask($output, 'Please enter your database username [phpci]: ', 'phpci'); + } + + if (!$dbPass = $input->getOption('db-pass')) { + $dbPass = $dialog->askHiddenResponse($output, 'Please enter your database password: '); + } + + $db['servers']['read'] = $dbHost; + $db['servers']['write'] = $dbHost; + $db['name'] = $dbName; + $db['username'] = $dbUser; + $db['password'] = $dbPass; + + return $db; + } + /** * Try and connect to MySQL using the details provided. * @param array $db @@ -200,7 +314,7 @@ class InstallCommand extends Command $dumper = new \Symfony\Component\Yaml\Dumper(); $yaml = $dumper->dump($config, 2); - file_put_contents(PHPCI_DIR . 'PHPCI/config.yml', $yaml); + file_put_contents($this->configFilePath, $yaml); } protected function setupDatabase(OutputInterface $output) @@ -213,34 +327,19 @@ class InstallCommand extends Command } /** + * Create admin user using information loaded before. + * + * @param array $admin * @param OutputInterface $output - * @param DialogHelper $dialog */ - protected function createAdminUser(OutputInterface $output, DialogHelper $dialog) + protected function createAdminUser($admin, $output) { - // Try to create a user account: - $adminEmail = $dialog->askAndValidate( - $output, - 'Your email address: ', - function ($answer) { - if (!filter_var($answer, FILTER_VALIDATE_EMAIL)) { - throw new Exception('Must be a valid email address.'); - } - - return $answer; - }, - false - ); - - $adminPass = $dialog->askHiddenResponse($output, 'Enter your desired admin password: '); - $adminName = $dialog->ask($output, 'Enter your name: '); - try { $this->reloadConfig(); $userStore = Factory::getStore('User'); $userService = new UserService($userStore); - $userService->createUser($adminName, $adminEmail, $adminPass, 1); + $userService->createUser($admin['name'], $admin['mail'], $admin['pass'], 1); $output->writeln('User account created!'); } catch (\Exception $ex) { @@ -252,11 +351,10 @@ class InstallCommand extends Command protected function reloadConfig() { - $configFile = PHPCI_DIR . 'PHPCI/config.yml'; - $config = Config::getInstance(); + $config = Config::getInstance(); - if (file_exists($configFile)) { - $config->loadYaml($configFile); + if (file_exists($this->configFilePath)) { + $config->loadYaml($this->configFilePath); } } @@ -265,11 +363,11 @@ class InstallCommand extends Command */ protected function verifyNotInstalled(OutputInterface $output) { - if (file_exists(PHPCI_DIR . 'PHPCI/config.yml')) { - $content = file_get_contents(PHPCI_DIR . 'PHPCI/config.yml'); + if (file_exists($this->configFilePath)) { + $content = file_get_contents($this->configFilePath); if (!empty($content)) { - $output->writeln('PHPCI/config.yml exists and is not empty.'); + $output->writeln('The PHPCI config file exists and is not empty.'); $output->writeln('If you were trying to update PHPCI, please use phpci:update instead.'); die; } diff --git a/Tests/PHPCI/Command/InstallCommandTest.php b/Tests/PHPCI/Command/InstallCommandTest.php new file mode 100644 index 00000000..29c22303 --- /dev/null +++ b/Tests/PHPCI/Command/InstallCommandTest.php @@ -0,0 +1,237 @@ +command = $this->getMockBuilder('PHPCI\\Command\\InstallCommand') + ->setMethods(array( + 'reloadConfig', + 'verifyNotInstalled', + 'verifyDatabaseDetails', + 'setupDatabase', + 'createAdminUser', + 'writeConfigFile', + )) + ->getMock(); + + $this->command->expects($this->once())->method('verifyDatabaseDetails')->willReturn(true); + $this->command->expects($this->once())->method('setupDatabase')->willReturn(true); + $this->command->expects($this->once())->method('createAdminUser')->will( + $this->returnCallback(function ($adm) {// use (&$admin) { + $this->admin = $adm; + }) + ); + $this->command->expects($this->once())->method('writeConfigFile')->will( + $this->returnCallback(function ($cfg) { //use (&$config) { + $this->config = $cfg; + }) + ); + + // We check that there's no interaction with user. + $this->dialog = $this->getMockBuilder('Symfony\\Component\\Console\\Helper\\DialogHelper') + ->setMethods(array( + 'ask', + 'askConfirmation', + 'askAndValidate', + 'askHiddenResponse', + 'askHiddenResponseAndValidate', + )) + ->getMock(); + + $this->application = new Application(); + $this->application->setHelperSet(new HelperSet()); + } + + protected function getCommandTester() + { + $this->application->getHelperSet()->set($this->dialog, 'dialog'); + $this->application->add($this->command); + $command = $this->application->find('phpci:install'); + $commandTester = new CommandTester($command); + + return $commandTester; + } + + protected function getConfig($exclude = null) + { + $config = array( + '--db-host' => 'localhost', + '--db-name' => 'phpci1', + '--db-user' => 'phpci2', + '--db-pass' => 'phpci3', + '--admin-mail' => 'phpci@phpci.test', + '--admin-name' => 'phpci4', + '--admin-pass' => 'phpci5', + '--url' => 'http://test.phpci.org', + ); + + if (!is_null($exclude)) { + unset($config[$exclude]); + } + + return $config; + } + + protected function executeWithoutParam($param = null) + { + // Clean result variables. + $this->admin = array(); + $this->config = array(); + + // Get tester and execute with extracted parameters. + $commandTester = $this->getCommandTester(); + $parameters = $this->getConfig($param); + $commandTester->execute($parameters); + } + + public function testAutomticInstallation() + { + $this->dialog->expects($this->never())->method('ask'); + $this->dialog->expects($this->never())->method('askConfirmation'); + $this->dialog->expects($this->never())->method('askAndValidate'); + $this->dialog->expects($this->never())->method('askHiddenResponse'); + $this->dialog->expects($this->never())->method('askHiddenResponseAndValidate'); + + $this->executeWithoutParam(); + } + + public function testDatabaseHostnameConfig() + { + // We specified an input value for hostname. + $this->dialog->expects($this->once())->method('ask')->willReturn('testedvalue'); + $this->dialog->expects($this->never())->method('askConfirmation'); + $this->dialog->expects($this->never())->method('askAndValidate'); + $this->dialog->expects($this->never())->method('askHiddenResponse'); + $this->dialog->expects($this->never())->method('askHiddenResponseAndValidate'); + + $this->executeWithoutParam('--db-host'); + + // Check that specified arguments are correctly loaded. + $this->assertEquals('testedvalue', $this->config['b8']['database']['servers']['read']); + $this->assertEquals('testedvalue', $this->config['b8']['database']['servers']['write']); + } + + public function testDatabaseNameConfig() + { + // We specified an input value for hostname. + $this->dialog->expects($this->once())->method('ask')->willReturn('testedvalue'); + $this->dialog->expects($this->never())->method('askConfirmation'); + $this->dialog->expects($this->never())->method('askAndValidate'); + $this->dialog->expects($this->never())->method('askHiddenResponse'); + $this->dialog->expects($this->never())->method('askHiddenResponseAndValidate'); + + $this->executeWithoutParam('--db-name'); + + // Check that specified arguments are correctly loaded. + $this->assertEquals('testedvalue', $this->config['b8']['database']['name']); + } + + public function testDatabaseUserameConfig() + { + // We specified an input value for hostname. + $this->dialog->expects($this->once())->method('ask')->willReturn('testedvalue'); + $this->dialog->expects($this->never())->method('askConfirmation'); + $this->dialog->expects($this->never())->method('askAndValidate'); + $this->dialog->expects($this->never())->method('askHiddenResponse'); + $this->dialog->expects($this->never())->method('askHiddenResponseAndValidate'); + + $this->executeWithoutParam('--db-user'); + + // Check that specified arguments are correctly loaded. + $this->assertEquals('testedvalue', $this->config['b8']['database']['username']); + } + + public function testDatabasePasswordConfig() + { + // We specified an input value for hostname. + $this->dialog->expects($this->never())->method('ask'); + $this->dialog->expects($this->never())->method('askConfirmation'); + $this->dialog->expects($this->never())->method('askAndValidate'); + $this->dialog->expects($this->once())->method('askHiddenResponse')->willReturn('testedvalue'); + $this->dialog->expects($this->never())->method('askHiddenResponseAndValidate'); + + $this->executeWithoutParam('--db-pass'); + + // Check that specified arguments are correctly loaded. + $this->assertEquals('testedvalue', $this->config['b8']['database']['password']); + } + + public function testPhpciUrlConfig() + { + // We specified an input value for hostname. + $this->dialog->expects($this->never())->method('ask'); + $this->dialog->expects($this->never())->method('askConfirmation'); + $this->dialog->expects($this->once())->method('askAndValidate')->willReturn('http://testedvalue.com'); + $this->dialog->expects($this->never())->method('askHiddenResponse'); + $this->dialog->expects($this->never())->method('askHiddenResponseAndValidate'); + + $this->executeWithoutParam('--url'); + + // Check that specified arguments are correctly loaded. + $this->assertEquals('http://testedvalue.com', $this->config['phpci']['url']); + } + + public function testAdminEmailConfig() + { + // We specified an input value for hostname. + $this->dialog->expects($this->never())->method('ask'); + $this->dialog->expects($this->never())->method('askConfirmation'); + $this->dialog->expects($this->once())->method('askAndValidate')->willReturn('test@phpci.com'); + $this->dialog->expects($this->never())->method('askHiddenResponse'); + $this->dialog->expects($this->never())->method('askHiddenResponseAndValidate'); + + $this->executeWithoutParam('--admin-mail'); + + // Check that specified arguments are correctly loaded. + $this->assertEquals('test@phpci.com', $this->admin['mail']); + } + + public function testAdminUserameConfig() + { + // Define expectation for dialog. + $this->dialog->expects($this->once())->method('ask')->willReturn('testedvalue'); + $this->dialog->expects($this->never())->method('askConfirmation'); + $this->dialog->expects($this->never())->method('askAndValidate'); + $this->dialog->expects($this->never())->method('askHiddenResponse'); + $this->dialog->expects($this->never())->method('askHiddenResponseAndValidate'); + + $this->executeWithoutParam('--admin-name'); + + // Check that specified arguments are correctly loaded. + $this->assertEquals('testedvalue', $this->admin['name']); + } + + public function testAdminPasswordConfig() + { + // We specified an input value for hostname. + $this->dialog->expects($this->never())->method('ask'); + $this->dialog->expects($this->never())->method('askConfirmation'); + $this->dialog->expects($this->never())->method('askAndValidate'); + $this->dialog->expects($this->once())->method('askHiddenResponse')->willReturn('testedvalue'); + $this->dialog->expects($this->never())->method('askHiddenResponseAndValidate'); + + $this->executeWithoutParam('--admin-pass'); + + // Check that specified arguments are correctly loaded. + $this->assertEquals('testedvalue', $this->admin['pass']); + } +} diff --git a/phpunit.xml b/phpunit.xml index 78685417..fdddf34d 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -11,6 +11,9 @@ bootstrap="./Tests/bootstrap.php" > + + ./Tests/PHPCI/Command + ./Tests/PHPCI/Helper