diff --git a/app/modules/cli/Commands/BackupCommand.php b/app/modules/cli/Commands/BackupCommand.php
new file mode 100644
index 00000000..7aec502e
--- /dev/null
+++ b/app/modules/cli/Commands/BackupCommand.php
@@ -0,0 +1,140 @@
+.
+ */
+
+namespace SP\Modules\Cli\Commands;
+
+use Exception;
+use Psr\Log\LoggerInterface;
+use RuntimeException;
+use SP\Config\Config;
+use SP\Services\Backup\FileBackupService;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\StyleInterface;
+use Symfony\Component\Console\Style\SymfonyStyle;
+
+/**
+ * Class BackupCommand
+ *
+ * @package SP\Modules\Cli\Commands
+ */
+final class BackupCommand extends CommandBase
+{
+ /**
+ * @var string[]
+ */
+ public static array $envVarsMapping = [
+ 'path' => 'BACKUP_PATH'
+ ];
+ /**
+ * @var string
+ */
+ protected static $defaultName = 'sp:backup';
+ /**
+ * @var FileBackupService
+ */
+ private FileBackupService $fileBackupService;
+
+ public function __construct(FileBackupService $fileBackupService,
+ LoggerInterface $logger,
+ Config $config)
+ {
+ $this->fileBackupService = $fileBackupService;
+
+ parent::__construct($logger, $config);
+ }
+
+ protected function configure(): void
+ {
+ $this->setDescription(__('Backup actions'))
+ ->setHelp(__('This command performs a file based backup from sysPass database and application'))
+ ->addOption('path',
+ null,
+ InputOption::VALUE_OPTIONAL,
+ __('Path where to store the backup files'),
+ BACKUP_PATH);
+ }
+
+ /**
+ * @param InputInterface $input
+ * @param OutputInterface $output
+ *
+ * @return int
+ */
+ protected function execute(InputInterface $input, OutputInterface $output): int
+ {
+ $style = new SymfonyStyle($input, $output);
+
+ try {
+ $this->checkInstalled();
+
+ $path = $this->getPath($input, $style);
+
+ $this->logger->info(sprintf(__u('Backup path set to: %s'), $path));
+
+ $this->fileBackupService->doBackup($path);
+
+ $this->logger->info(__u('Application and database backup completed successfully'));
+
+ $style->success(__('Application and database backup completed successfully'));
+
+ return self::SUCCESS;
+ } catch (Exception $e) {
+ $this->logger->error($e->getTraceAsString());
+ $this->logger->error($e->getMessage());
+
+ $style->error(__($e->getMessage()));
+ }
+
+ return self::FAILURE;
+ }
+
+ private function checkInstalled(): void
+ {
+ if (!defined('TEST_ROOT')
+ && !$this->configData->isInstalled()) {
+ throw new RuntimeException(__u('sysPass is not installed'));
+ }
+ }
+
+ /**
+ * @param InputInterface $input
+ * @param StyleInterface $style
+ *
+ * @return string
+ */
+ private function getPath(InputInterface $input, StyleInterface $style): string
+ {
+ $path = self::getEnvVarOrOption('path', $input);
+
+ if (empty($path)) {
+ $this->logger->debug(__u('Ask for path'));
+
+ return $style->ask(__('Please enter the path where to store the backup files'), BACKUP_PATH);
+ }
+
+ return $path;
+ }
+}
\ No newline at end of file
diff --git a/app/modules/cli/Commands/CommandBase.php b/app/modules/cli/Commands/CommandBase.php
index d841b433..32e147fa 100644
--- a/app/modules/cli/Commands/CommandBase.php
+++ b/app/modules/cli/Commands/CommandBase.php
@@ -28,6 +28,7 @@ use Psr\Log\LoggerInterface;
use SP\Config\Config;
use SP\Config\ConfigData;
use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
/**
* Class CommandBase
@@ -36,19 +37,22 @@ use Symfony\Component\Console\Command\Command;
*/
abstract class CommandBase extends Command
{
+ /**
+ * @var string[]
+ */
+ public static array $envVarsMapping = [];
/**
* @var LoggerInterface
*/
- protected $logger;
-
+ protected LoggerInterface $logger;
/**
* @var Config
*/
- protected $config;
+ protected Config $config;
/**
* @var ConfigData
*/
- protected $configData;
+ protected ConfigData $configData;
/**
* CommandBase constructor.
@@ -67,4 +71,44 @@ abstract class CommandBase extends Command
parent::__construct();
}
+
+ /**
+ * @param string $option
+ * @param InputInterface $input
+ *
+ * @return array|false|mixed|string
+ */
+ protected static function getEnvVarOrOption(
+ string $option,
+ InputInterface $input
+ )
+ {
+ return static::getEnvVarForOption($option)
+ ?: $input->getOption($option);
+ }
+
+ /**
+ * @param string $option
+ *
+ * @return string|false
+ */
+ protected static function getEnvVarForOption(string $option)
+ {
+ return getenv(static::$envVarsMapping[$option]);
+ }
+
+ /**
+ * @param string $argument
+ * @param InputInterface $input
+ *
+ * @return array|false|mixed|string
+ */
+ protected static function getEnvVarOrArgument(
+ string $argument,
+ InputInterface $input
+ )
+ {
+ return static::getEnvVarForOption($argument)
+ ?: $input->getArgument($argument);
+ }
}
\ No newline at end of file
diff --git a/app/modules/cli/Commands/InstallCommand.php b/app/modules/cli/Commands/InstallCommand.php
index 65d024af..05bf48a5 100644
--- a/app/modules/cli/Commands/InstallCommand.php
+++ b/app/modules/cli/Commands/InstallCommand.php
@@ -27,7 +27,6 @@ namespace SP\Modules\Cli\Commands;
use Closure;
use Exception;
use Psr\Log\LoggerInterface;
-use RuntimeException;
use SP\Config\Config;
use SP\Core\Exceptions\InstallError;
use SP\Core\Exceptions\InvalidArgumentException;
@@ -52,7 +51,7 @@ final class InstallCommand extends CommandBase
/**
* @var string[]
*/
- public static $envVarsMapping = [
+ public static array $envVarsMapping = [
'adminLogin' => 'ADMIN_LOGIN',
'adminPassword' => 'ADMIN_PASSWORD',
'databaseHost' => 'DATABASE_HOST',
@@ -72,7 +71,7 @@ final class InstallCommand extends CommandBase
/**
* @var Installer
*/
- private $installer;
+ private Installer $installer;
public function __construct(LoggerInterface $logger,
Config $config,
@@ -185,16 +184,8 @@ final class InstallCommand extends CommandBase
*/
private function getInstallData(InputInterface $input, StyleInterface $style): InstallData
{
- $passNotEmptyValidator = function ($value) {
- if (empty($value)) {
- throw new RuntimeException(__('Password cannot be blank'));
- }
-
- return $value;
- };
-
- $adminPassword = $this->getAdminPassword($input, $style, $passNotEmptyValidator);
- $masterPassword = $this->getMasterPassword($input, $style, $passNotEmptyValidator);
+ $adminPassword = $this->getAdminPassword($input, $style);
+ $masterPassword = $this->getMasterPassword($input, $style);
$databasePassword = $this->getDatabasePassword($input, $style);
$language = $this->getLanguage($input, $style);
$hostingMode = $this->isHostingMode($input);
@@ -220,15 +211,13 @@ final class InstallCommand extends CommandBase
/**
* @param InputInterface $input
* @param StyleInterface $style
- * @param Closure $passNotEmptyValidator
*
* @return array|false|mixed|string
* @throws InstallError
*/
private function getAdminPassword(
InputInterface $input,
- StyleInterface $style,
- Closure $passNotEmptyValidator
+ StyleInterface $style
)
{
$option = 'adminPassword';
@@ -242,12 +231,18 @@ final class InstallCommand extends CommandBase
$password = $style->askHidden(
__('Please provide sysPass admin\'s password'),
- $passNotEmptyValidator
+ fn($value) => Validators::valueNotEmpty(
+ $value,
+ sprintf(__u('%s cannot be blank'), 'Admin password')
+ )
);
$passwordRepeat = $style->askHidden(
__('Please provide sysPass admin\'s password again'),
- $passNotEmptyValidator
+ fn($value) => Validators::valueNotEmpty(
+ $value,
+ sprintf(__u('%s cannot be blank'), 'Admin password')
+ )
);
if ($password !== $passwordRepeat) {
@@ -260,18 +255,17 @@ final class InstallCommand extends CommandBase
return $password;
}
+
/**
* @param InputInterface $input
* @param StyleInterface $style
- * @param Closure $passNotEmptyValidator
*
* @return array|false|mixed|string
* @throws InstallError
*/
private function getMasterPassword(
InputInterface $input,
- StyleInterface $style,
- Closure $passNotEmptyValidator
+ StyleInterface $style
)
{
$password = self::getEnvVarOrOption('masterPassword', $input);
@@ -281,11 +275,17 @@ final class InstallCommand extends CommandBase
$password = $style->askHidden(
__('Please provide sysPass master password'),
- $passNotEmptyValidator
+ fn($value) => Validators::valueNotEmpty(
+ $value,
+ sprintf(__u('%s cannot be blank'), 'Master password')
+ )
);
$passwordRepeat = $style->askHidden(
__('Please provide sysPass master password again'),
- $passNotEmptyValidator
+ fn($value) => Validators::valueNotEmpty(
+ $value,
+ sprintf(__u('%s cannot be blank'), 'Master password')
+ )
);
if ($password !== $passwordRepeat) {
@@ -298,31 +298,6 @@ final class InstallCommand extends CommandBase
return $password;
}
- /**
- * @param string $option
- * @param InputInterface $input
- *
- * @return array|false|mixed|string
- */
- private static function getEnvVarOrOption(
- string $option,
- InputInterface $input
- )
- {
- return self::getEnvVarForOption($option)
- ?: $input->getOption($option);
- }
-
- /**
- * @param string $option
- *
- * @return string|false
- */
- public static function getEnvVarForOption(string $option)
- {
- return getenv(self::$envVarsMapping[$option]);
- }
-
/**
* @param InputInterface $input
* @param StyleInterface $style
@@ -387,21 +362,6 @@ final class InstallCommand extends CommandBase
: $input->getOption($option);
}
- /**
- * @param string $argument
- * @param InputInterface $input
- *
- * @return array|false|mixed|string
- */
- private static function getEnvVarOrArgument(
- string $argument,
- InputInterface $input
- )
- {
- return self::getEnvVarForOption($argument)
- ?: $input->getArgument($argument);
- }
-
/**
* @param InputInterface $input
*
diff --git a/app/modules/cli/Commands/Validators.php b/app/modules/cli/Commands/Validators.php
new file mode 100644
index 00000000..6cf02631
--- /dev/null
+++ b/app/modules/cli/Commands/Validators.php
@@ -0,0 +1,48 @@
+.
+ */
+
+namespace SP\Modules\Cli\Commands;
+
+use RuntimeException;
+
+/**
+ *
+ */
+final class Validators
+{
+ /**
+ * @param string|null $value
+ * @param string|null $message
+ *
+ * @return string
+ */
+ public static function valueNotEmpty(?string $value, ?string $message): string
+ {
+ if (empty($value)) {
+ throw new RuntimeException($message ?? __u('Value cannot be blank'));
+ }
+
+ return $value;
+ }
+}
\ No newline at end of file
diff --git a/app/modules/cli/Init.php b/app/modules/cli/Init.php
index 722f8e87..32db8c6d 100644
--- a/app/modules/cli/Init.php
+++ b/app/modules/cli/Init.php
@@ -4,7 +4,7 @@
*
* @author nuxsmin
* @link https://syspass.org
- * @copyright 2012-2020, Rubén Domínguez nuxsmin@$syspass.org
+ * @copyright 2012-2021, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
@@ -19,7 +19,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with sysPass. If not, see .
+ * along with sysPass. If not, see .
*/
namespace SP\Modules\Cli;
@@ -32,6 +32,7 @@ use SP\Core\Context\ContextException;
use SP\Core\Context\StatelessContext;
use SP\Core\Language;
use SP\Core\ModuleBase;
+use SP\Modules\Cli\Commands\BackupCommand;
use SP\Modules\Cli\Commands\InstallCommand;
use SP\Util\VersionUtil;
use Symfony\Component\Console\Application;
@@ -46,7 +47,8 @@ use Symfony\Component\Console\Output\OutputInterface;
final class Init extends ModuleBase
{
private const CLI_COMMANDS = [
- InstallCommand::class
+ InstallCommand::class,
+ BackupCommand::class
];
/**
* @var StatelessContext
diff --git a/app/modules/cli/module.php b/app/modules/cli/module.php
index 205d247e..62202b4f 100644
--- a/app/modules/cli/module.php
+++ b/app/modules/cli/module.php
@@ -4,7 +4,7 @@
*
* @author nuxsmin
* @link https://syspass.org
- * @copyright 2012-2020, Rubén Domínguez nuxsmin@$syspass.org
+ * @copyright 2012-2021, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
@@ -19,8 +19,8 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with sysPass. If not, see .
+ * along with sysPass. If not, see .
*/
-define('MODULE_PATH', __DIR__);
-define('PLUGINS_PATH', MODULE_PATH . DIRECTORY_SEPARATOR . 'plugins');
+const MODULE_PATH = __DIR__;
+const PLUGINS_PATH = MODULE_PATH . DIRECTORY_SEPARATOR . 'plugins';
diff --git a/lib/SP/Config/Config.php b/lib/SP/Config/Config.php
index f30eb82f..ff02d557 100644
--- a/lib/SP/Config/Config.php
+++ b/lib/SP/Config/Config.php
@@ -202,15 +202,18 @@ final class Config
* @return Config
* @throws FileException
*/
- public function saveConfig(ConfigData $configData, $backup = true): Config
+ public function saveConfig(ConfigData $configData, ?bool $backup = true): Config
{
if ($backup) {
$this->dic->get(ConfigBackupService::class)
->backup($configData);
}
+ $configSaver = $this->context->getUserData()->getLogin()
+ ?: AppInfoInterface::APP_NAME;
+
$configData->setConfigDate(time());
- $configData->setConfigSaver($this->context->getUserData()->getLogin() ?: AppInfoInterface::APP_NAME);
+ $configData->setConfigSaver($configSaver);
$configData->setConfigHash();
// Save only attributes to avoid a parent attributes node within the XML
@@ -258,7 +261,7 @@ final class Config
*
* @return ConfigData
*/
- public function loadConfig($reload = false): ConfigData
+ public function loadConfig(?bool $reload = false): ConfigData
{
try {
$configData = $this->fileCache->load();
diff --git a/lib/SP/Core/Context/ContextInterface.php b/lib/SP/Core/Context/ContextInterface.php
index 46390c2b..fda059e6 100644
--- a/lib/SP/Core/Context/ContextInterface.php
+++ b/lib/SP/Core/Context/ContextInterface.php
@@ -85,9 +85,9 @@ interface ContextInterface
/**
* Devuelve los datos del usuario en la sesión.
*
- * @return UserLoginResponse|null
+ * @return UserLoginResponse
*/
- public function getUserData(): ?UserLoginResponse;
+ public function getUserData(): UserLoginResponse;
/**
* Establecer el lenguaje de la sesión
diff --git a/lib/SP/Core/Context/StatelessContext.php b/lib/SP/Core/Context/StatelessContext.php
index 521f3850..963e7c00 100644
--- a/lib/SP/Core/Context/StatelessContext.php
+++ b/lib/SP/Core/Context/StatelessContext.php
@@ -119,7 +119,7 @@ final class StatelessContext extends ContextBase
*
* @return UserLoginResponse
*/
- public function getUserData(): ?UserLoginResponse
+ public function getUserData(): UserLoginResponse
{
return $this->getContextKey('userData', new UserLoginResponse());
}
diff --git a/lib/SP/Providers/Auth/Ldap/LdapConnection.php b/lib/SP/Providers/Auth/Ldap/LdapConnection.php
index b0d83959..aed48f7a 100644
--- a/lib/SP/Providers/Auth/Ldap/LdapConnection.php
+++ b/lib/SP/Providers/Auth/Ldap/LdapConnection.php
@@ -203,22 +203,6 @@ final class LdapConnection implements LdapConnectionInterface
return $this;
}
- public function getServerUri(): string
- {
- $server = $this->getServer();
- $port = $this->ldapParams->getPort();
-
- if (strpos($server, '://') !== false) {
- return $server . ':' . $port;
- } elseif ($port === 389 || $port === null) {
- return 'ldap://' . $server;
- } elseif ($port === 636) {
- return 'ldaps://' . $server;
- }
-
- return 'ldap://' . $server . ':' . $port;
- }
-
/**
* @inheritDoc
*/
@@ -229,9 +213,13 @@ final class LdapConnection implements LdapConnectionInterface
if (strpos($server, '://') !== false) {
return $server . ':' . $port;
- } elseif ($port === 389 || $port === null) {
+ }
+
+ if ($port === 389 || $port === null) {
return 'ldap://' . $server;
- } elseif ($port === 636) {
+ }
+
+ if ($port === 636) {
return 'ldaps://' . $server;
}
diff --git a/lib/SP/Services/Backup/FileBackupService.php b/lib/SP/Services/Backup/FileBackupService.php
index d29ca87a..4e552dcb 100644
--- a/lib/SP/Services/Backup/FileBackupService.php
+++ b/lib/SP/Services/Backup/FileBackupService.php
@@ -137,13 +137,14 @@ final class FileBackupService extends Service
/**
* Comprobar y crear el directorio de backups.
*
- * @return bool
+ * @return void
* @throws ServiceException
*/
- private function checkBackupDir()
+ private function checkBackupDir(): void
{
if (is_dir($this->path) === false
- && @mkdir($this->path, 0750) === false
+ && !@mkdir($concurrentDirectory = $this->path, 0750)
+ && !is_dir($concurrentDirectory)
) {
throw new ServiceException(
sprintf(__('Unable to create the backups directory ("%s")'), $this->path));
@@ -154,7 +155,6 @@ final class FileBackupService extends Service
__u('Please, check the backup directory permissions'));
}
- return true;
}
/**
diff --git a/lib/SP/Util/FileUtil.php b/lib/SP/Util/FileUtil.php
index d2162957..8c41044d 100644
--- a/lib/SP/Util/FileUtil.php
+++ b/lib/SP/Util/FileUtil.php
@@ -54,11 +54,19 @@ final class FileUtil
throw new FileNotFoundException('Directory does not exist');
}
- $it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST);
+ $it = new RecursiveIteratorIterator(
+ new RecursiveDirectoryIterator(
+ $dir,
+ FilesystemIterator::SKIP_DOTS),
+ RecursiveIteratorIterator::CHILD_FIRST
+ );
foreach ($it as $file) {
- if ($file->isDir()) rmdir($file->getPathname());
- else unlink($file->getPathname());
+ if ($file->isDir()) {
+ rmdir($file->getPathname());
+ } else {
+ unlink($file->getPathname());
+ }
}
return rmdir($dir);
diff --git a/tests/SP/Modules/Cli/CliTestCase.php b/tests/SP/Modules/Cli/CliTestCase.php
index 85a91919..35d10d9e 100644
--- a/tests/SP/Modules/Cli/CliTestCase.php
+++ b/tests/SP/Modules/Cli/CliTestCase.php
@@ -25,9 +25,15 @@
namespace SP\Tests\Modules\Cli;
use DI\ContainerBuilder;
+use DI\DependencyException;
+use DI\NotFoundException;
use Exception;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerInterface;
+use SP\Core\Context\ContextInterface;
+use SP\Storage\Database\DBStorageInterface;
+use Symfony\Component\Console\Tester\CommandTester;
+use function SP\Tests\getDbHandler;
/**
* Class CliTestCase
@@ -40,6 +46,10 @@ abstract class CliTestCase extends TestCase
* @var ContainerInterface
*/
protected static $dic;
+ /**
+ * @var string[]
+ */
+ protected static array $commandInputData = [];
/**
* This method is called before the first test of this test class is run.
@@ -57,5 +67,43 @@ abstract class CliTestCase extends TestCase
);
self::$dic = $builder->build();
+
+ $context = self::$dic->get(ContextInterface::class);
+ $context->initialize();
+ }
+
+ /**
+ * @param string $commandClass
+ * @param array|null $inputData
+ * @param bool $useInputData
+ *
+ * @return CommandTester
+ * @throws DependencyException
+ * @throws NotFoundException
+ */
+ protected function executeCommandTest(
+ string $commandClass,
+ ?array $inputData = null,
+ bool $useInputData = true
+ ): CommandTester
+ {
+ $installCommand = self::$dic->get($commandClass);
+
+ if (null === $inputData && $useInputData) {
+ $inputData = static::$commandInputData;
+ }
+
+ $commandTester = new CommandTester($installCommand);
+ $commandTester->execute(
+ $inputData ?? [],
+ ['interactive' => false]
+ );
+
+ return $commandTester;
+ }
+
+ protected function setupDatabase(): void
+ {
+ self::$dic->set(DBStorageInterface::class, getDbHandler());
}
}
\ No newline at end of file
diff --git a/tests/SP/Modules/Cli/Commands/BackupCommandTest.php b/tests/SP/Modules/Cli/Commands/BackupCommandTest.php
new file mode 100644
index 00000000..37d19a9a
--- /dev/null
+++ b/tests/SP/Modules/Cli/Commands/BackupCommandTest.php
@@ -0,0 +1,168 @@
+.
+ */
+
+namespace SP\Tests\Modules\Cli\Commands;
+
+use DI\DependencyException;
+use DI\NotFoundException;
+use SP\Config\Config;
+use SP\Core\Exceptions\FileNotFoundException;
+use SP\Modules\Cli\Commands\BackupCommand;
+use SP\Services\Backup\FileBackupService;
+use SP\Tests\Modules\Cli\CliTestCase;
+use function SP\Tests\recreateDir;
+
+/**
+ *
+ */
+class BackupCommandTest extends CliTestCase
+{
+ /**
+ * @var string
+ */
+ protected static string $currentConfig;
+ /**
+ * @var string[]
+ */
+ protected static array $commandInputData = [
+ '--path' => TMP_PATH
+ ];
+
+ /**
+ * @throws DependencyException
+ * @throws NotFoundException
+ */
+ public function testBackupFails(): void
+ {
+ $inputData = ['--path' => '/non/existant/path'];
+
+ $commandTester = $this->executeCommandTest(
+ BackupCommand::class,
+ $inputData
+ );
+
+ // the output of the command in the console
+ $output = $commandTester->getDisplay();
+ $this->assertStringContainsString('Unable to create the backups directory', $output);
+ }
+
+ /**
+ * @throws DependencyException
+ * @throws FileNotFoundException
+ * @throws NotFoundException
+ */
+ public function testBackupIsSuccessful(): void
+ {
+ $this->setupDatabase();
+
+ $commandTester = $this->executeCommandTest(
+ BackupCommand::class,
+ self::$commandInputData
+ );
+
+ // the output of the command in the console
+ $output = $commandTester->getDisplay();
+ $this->assertStringContainsString('Application and database backup completed successfully', $output);
+
+ $this->checkBackupFilesAreCreated();
+
+ // Recreate cache directory to avoid unwanted behavior
+ recreateDir(CACHE_PATH);
+ }
+
+ private function checkBackupFilesAreCreated(): void
+ {
+ $configData = self::$dic->get(Config::class)->getConfigData();
+
+ $this->assertFileExists(
+ FileBackupService::getAppBackupFilename(
+ TMP_PATH,
+ $configData->getBackupHash(),
+ true
+ )
+ );
+ $this->assertFileExists(
+ FileBackupService::getDbBackupFilename(
+ TMP_PATH,
+ $configData->getBackupHash(),
+ true
+ )
+ );
+ }
+
+ /**
+ * @throws DependencyException
+ * @throws NotFoundException
+ */
+ public function testBackupFromEnvironmentVarIsAbort(): void
+ {
+ putenv(sprintf('%s=%s',
+ BackupCommand::$envVarsMapping['path'],
+ '/non/existant/path')
+ );
+
+ $commandTester = $this->executeCommandTest(
+ BackupCommand::class,
+ null,
+ false
+ );
+
+ // the output of the command in the console
+ $output = $commandTester->getDisplay();
+ $this->assertStringContainsString('Unable to create the backups directory', $output);
+ }
+
+ /**
+ * @throws DependencyException
+ * @throws NotFoundException
+ * @throws FileNotFoundException
+ */
+ public function testBackupFromEnvironmentVarIsSuccessful(): void
+ {
+ $this->setEnvironmentVariables();
+
+ $commandTester = $this->executeCommandTest(
+ BackupCommand::class,
+ null,
+ false
+ );
+
+ // the output of the command in the console
+ $output = $commandTester->getDisplay();
+ $this->assertStringContainsString('Application and database backup completed successfully', $output);
+
+ $this->checkBackupFilesAreCreated();
+
+ // Recreate cache directory to avoid unwanted behavior
+ recreateDir(CACHE_PATH);
+ }
+
+ private function setEnvironmentVariables(): void
+ {
+ putenv(sprintf('%s=%s',
+ BackupCommand::$envVarsMapping['path'],
+ TMP_PATH)
+ );
+ }
+}
diff --git a/tests/SP/Modules/Cli/Commands/InstallCommandTest.php b/tests/SP/Modules/Cli/Commands/InstallCommandTest.php
index df96c908..7fc4293d 100644
--- a/tests/SP/Modules/Cli/Commands/InstallCommandTest.php
+++ b/tests/SP/Modules/Cli/Commands/InstallCommandTest.php
@@ -32,7 +32,6 @@ use SP\Modules\Cli\Commands\InstallCommand;
use SP\Storage\Database\DatabaseException;
use SP\Tests\DatabaseUtil;
use SP\Tests\Modules\Cli\CliTestCase;
-use Symfony\Component\Console\Tester\CommandTester;
use function SP\Tests\getResource;
use function SP\Tests\recreateDir;
use function SP\Tests\saveResource;
@@ -45,11 +44,11 @@ class InstallCommandTest extends CliTestCase
/**
* @var string
*/
- protected static $currentConfig;
+ protected static string $currentConfig;
/**
* @var string[]
*/
- private static $commandInputData = [
+ protected static array $commandInputData = [
'adminLogin' => 'Admin',
'databaseHost' => 'localhost',
'databaseName' => 'syspass-test-install',
@@ -92,41 +91,13 @@ class InstallCommandTest extends CliTestCase
*/
public function testInstallationIsAborted(): void
{
- $commandTester = $this->executeCommandTest();
+ $commandTester = $this->executeCommandTest(InstallCommand::class);
// the output of the command in the console
$output = $commandTester->getDisplay();
$this->assertStringContainsString('Installation aborted', $output);
}
- /**
- * @param array|null $inputData
- * @param bool $useInputData
- *
- * @return CommandTester
- * @throws DependencyException
- * @throws NotFoundException
- */
- private function executeCommandTest(
- ?array $inputData = null,
- bool $useInputData = true
- ): CommandTester
- {
- $installCommand = self::$dic->get(InstallCommand::class);
-
- if (null === $inputData && $useInputData) {
- $inputData = self::$commandInputData;
- }
-
- $commandTester = new CommandTester($installCommand);
- $commandTester->execute(
- $inputData ?? [],
- ['interactive' => false]
- );
-
- return $commandTester;
- }
-
/**
* @throws DependencyException
* @throws NotFoundException
@@ -138,7 +109,10 @@ class InstallCommandTest extends CliTestCase
['--forceInstall' => null]
);
- $commandTester = $this->executeCommandTest($inputData);
+ $commandTester = $this->executeCommandTest(
+ InstallCommand::class,
+ $inputData
+ );
// the output of the command in the console
$output = $commandTester->getDisplay();
@@ -156,7 +130,10 @@ class InstallCommandTest extends CliTestCase
['--adminPassword' => '']
);
- $commandTester = $this->executeCommandTest($inputData);
+ $commandTester = $this->executeCommandTest(
+ InstallCommand::class,
+ $inputData
+ );
// the output of the command in the console
$output = $commandTester->getDisplay();
@@ -174,7 +151,10 @@ class InstallCommandTest extends CliTestCase
['--masterPassword' => '']
);
- $commandTester = $this->executeCommandTest($inputData);
+ $commandTester = $this->executeCommandTest(
+ InstallCommand::class,
+ $inputData
+ );
// the output of the command in the console
$output = $commandTester->getDisplay();
@@ -198,7 +178,10 @@ class InstallCommandTest extends CliTestCase
]
);
- $commandTester = $this->executeCommandTest($inputData);
+ $commandTester = $this->executeCommandTest(
+ InstallCommand::class,
+ $inputData
+ );
// the output of the command in the console
$output = $commandTester->getDisplay();
@@ -229,7 +212,10 @@ class InstallCommandTest extends CliTestCase
]
);
- $commandTester = $this->executeCommandTest($inputData);
+ $commandTester = $this->executeCommandTest(
+ InstallCommand::class,
+ $inputData
+ );
// the output of the command in the console
$output = $commandTester->getDisplay();
@@ -273,7 +259,10 @@ class InstallCommandTest extends CliTestCase
]
);
- $commandTester = $this->executeCommandTest($inputData);
+ $commandTester = $this->executeCommandTest(
+ InstallCommand::class,
+ $inputData
+ );
// the output of the command in the console
$output = $commandTester->getDisplay();
@@ -296,7 +285,11 @@ class InstallCommandTest extends CliTestCase
{
$this->setEnvironmentVariables();
- $commandTester = $this->executeCommandTest(null, false);
+ $commandTester = $this->executeCommandTest(
+ InstallCommand::class,
+ null,
+ false
+ );
// the output of the command in the console
$output = $commandTester->getDisplay();
@@ -347,7 +340,11 @@ class InstallCommandTest extends CliTestCase
$this->setEnvironmentVariables();
- $commandTester = $this->executeCommandTest(null, false);
+ $commandTester = $this->executeCommandTest(
+ InstallCommand::class,
+ null,
+ false
+ );
// the output of the command in the console
$output = $commandTester->getDisplay();
@@ -370,7 +367,11 @@ class InstallCommandTest extends CliTestCase
$this->setEnvironmentVariables();
- $commandTester = $this->executeCommandTest(null, false);
+ $commandTester = $this->executeCommandTest(
+ InstallCommand::class,
+ null,
+ false
+ );
// the output of the command in the console
$output = $commandTester->getDisplay();
diff --git a/tests/SP/bootstrap.php b/tests/SP/bootstrap.php
index 24f98b90..756ccac4 100644
--- a/tests/SP/bootstrap.php
+++ b/tests/SP/bootstrap.php
@@ -27,13 +27,16 @@ namespace SP\Tests;
use DI\Container;
use DI\ContainerBuilder;
use Exception;
+use RuntimeException;
use SP\Core\Context\ContextException;
use SP\Core\Context\ContextInterface;
+use SP\Core\Exceptions\FileNotFoundException;
use SP\DataModel\ProfileData;
use SP\Services\User\UserLoginResponse;
use SP\Storage\Database\DatabaseConnectionData;
use SP\Storage\Database\DBStorageInterface;
use SP\Storage\Database\MySQLHandler;
+use SP\Util\FileUtil;
define('DEBUG', true);
@@ -46,11 +49,11 @@ define('CONFIG_PATH', RESOURCE_PATH . DIRECTORY_SEPARATOR . 'config');
define('CONFIG_FILE', CONFIG_PATH . DIRECTORY_SEPARATOR . 'config.xml');
define('ACTIONS_FILE', CONFIG_PATH . DIRECTORY_SEPARATOR . 'actions.xml');
-define('MODULES_PATH', APP_ROOT. DIRECTORY_SEPARATOR. 'app' . DIRECTORY_SEPARATOR . 'modules');
+define('MODULES_PATH', APP_ROOT . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'modules');
define('SQL_PATH', APP_ROOT . DIRECTORY_SEPARATOR . 'schemas');
define('CACHE_PATH', RESOURCE_PATH . DIRECTORY_SEPARATOR . 'cache');
define('TMP_PATH', TEST_ROOT . DIRECTORY_SEPARATOR . 'tmp');
-
+define('BACKUP_PATH', TMP_PATH);
define('XML_SCHEMA', APP_ROOT . DIRECTORY_SEPARATOR . 'schemas' . DIRECTORY_SEPARATOR . 'syspass.xsd');
@@ -189,19 +192,20 @@ function saveResource($dir, $file, $data): string
/**
* @param $dir
+ *
+ * @throws FileNotFoundException
*/
function recreateDir($dir)
{
- if (!is_dir($dir)) {
- print 'Creating ' . $dir . PHP_EOL;
-
- if (!mkdir($dir) && !is_dir($dir)) {
- throw new \RuntimeException(sprintf('Directory "%s" was not created', $dir));
- }
- } else {
+ if (is_dir($dir)) {
print 'Deleting ' . $dir . PHP_EOL;
- // Delete tmp dir ...
- array_map('unlink', glob($dir . DIRECTORY_SEPARATOR . '*'));
+ FileUtil::rmdir_recursive($dir);
+ }
+
+ print 'Creating ' . $dir . PHP_EOL;
+
+ if (!mkdir($dir) && !is_dir($dir)) {
+ throw new RuntimeException(sprintf('Directory "%s" was not created', $dir));
}
}
\ No newline at end of file