From 6f3ed10144e1f930b401ee817726d4a2ade98b18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D?= Date: Sun, 4 Feb 2024 10:15:28 +0100 Subject: [PATCH] chore(tests): UT for BackupFileHelper class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rubén D --- .../Controllers/Config/BackupController.php | 12 +- app/modules/cli/Commands/BackupCommand.php | 10 +- .../DownloadBackupAppController.php | 6 +- .../DownloadBackupDbController.php | 6 +- .../ConfigBackup/FileBackupController.php | 8 +- .../ConfigManager/IndexController.php | 6 +- .../web/Controllers/ControllerBase.php | 8 +- lib/SP/Core/Definitions/CoreDefinitions.php | 7 +- lib/SP/Core/PhpExtensionChecker.php | 6 +- ...ace.php => PhpExtensionCheckerService.php} | 4 +- ...erface.php => BackupFileHelperService.php} | 4 +- ...iceInterface.php => BackupFileService.php} | 4 +- .../{FileBackupService.php => BackupFile.php} | 27 ++-- .../{BackupFiles.php => BackupFileHelper.php} | 77 +++------ .../File/Ports/DirectoryHandlerService.php | 52 +++++++ lib/SP/Infrastructure/File/ArchiveHandler.php | 19 +-- .../Infrastructure/File/DirectoryHandler.php | 89 +++++++++++ .../Export/Services/BackupFileHelperTest.php | 147 ++++++++++++++++++ .../Export/Services/FileBackupServiceTest.php | 18 +-- .../Cli/Commands/BackupCommandTest.php | 6 +- tests/SPT/Stubs/PhpExtensionCheckerStub.php | 78 ++++++++++ 21 files changed, 463 insertions(+), 131 deletions(-) rename lib/SP/Domain/Core/{PhpExtensionCheckerInterface.php => PhpExtensionCheckerService.php} (93%) rename lib/SP/Domain/Export/Ports/{BackupFilesInterface.php => BackupFileHelperService.php} (94%) rename lib/SP/Domain/Export/Ports/{FileBackupServiceInterface.php => BackupFileService.php} (91%) rename lib/SP/Domain/Export/Services/{FileBackupService.php => BackupFile.php} (90%) rename lib/SP/Domain/Export/Services/{BackupFiles.php => BackupFileHelper.php} (62%) create mode 100644 lib/SP/Domain/File/Ports/DirectoryHandlerService.php create mode 100644 lib/SP/Infrastructure/File/DirectoryHandler.php create mode 100644 tests/SPT/Domain/Export/Services/BackupFileHelperTest.php create mode 100644 tests/SPT/Stubs/PhpExtensionCheckerStub.php diff --git a/app/modules/api/Controllers/Config/BackupController.php b/app/modules/api/Controllers/Config/BackupController.php index 0252d8ee..2a1e5787 100644 --- a/app/modules/api/Controllers/Config/BackupController.php +++ b/app/modules/api/Controllers/Config/BackupController.php @@ -34,8 +34,8 @@ use SP\Domain\Api\Ports\ApiService; use SP\Domain\Core\Acl\AclActionsInterface; use SP\Domain\Core\Acl\AclInterface; use SP\Domain\Core\Exceptions\InvalidClassException; -use SP\Domain\Export\Ports\FileBackupServiceInterface; -use SP\Domain\Export\Services\BackupFiles; +use SP\Domain\Export\Ports\BackupFileService; +use SP\Domain\Export\Services\BackupFileHelper; use SP\Modules\Api\Controllers\ControllerBase; use SP\Modules\Api\Controllers\Help\ConfigHelp; @@ -46,7 +46,7 @@ use SP\Modules\Api\Controllers\Help\ConfigHelp; */ final class BackupController extends ControllerBase { - private FileBackupServiceInterface $fileBackupService; + private BackupFileService $fileBackupService; /** * @throws InvalidClassException @@ -56,7 +56,7 @@ final class BackupController extends ControllerBase Klein $router, ApiService $apiService, AclInterface $acl, - FileBackupServiceInterface $fileBackupService + BackupFileService $fileBackupService ) { parent::__construct($application, $router, $apiService, $acl); @@ -106,12 +106,12 @@ final class BackupController extends ControllerBase { return [ 'files' => [ - 'app' => BackupFiles::getAppBackupFilename( + 'app' => BackupFileHelper::getAppBackupFilename( $path, $this->fileBackupService->getHash(), true ), - 'db' => BackupFiles::getDbBackupFilename( + 'db' => BackupFileHelper::getDbBackupFilename( $path, $this->fileBackupService->getHash(), true diff --git a/app/modules/cli/Commands/BackupCommand.php b/app/modules/cli/Commands/BackupCommand.php index 6982c1ba..c5ffa8d3 100644 --- a/app/modules/cli/Commands/BackupCommand.php +++ b/app/modules/cli/Commands/BackupCommand.php @@ -28,8 +28,8 @@ use Exception; use Psr\Log\LoggerInterface; use RuntimeException; use SP\Domain\Config\Ports\ConfigFileService; -use SP\Domain\Export\Ports\FileBackupServiceInterface; -use SP\Domain\Export\Services\FileBackupService; +use SP\Domain\Export\Ports\BackupFileService; +use SP\Domain\Export\Services\BackupFile; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -52,11 +52,11 @@ final class BackupCommand extends CommandBase /** * @var string */ - protected static $defaultName = 'sp:backup'; - private FileBackupService $fileBackupService; + protected static $defaultName = 'sp:backup'; + private BackupFile $fileBackupService; public function __construct( - FileBackupServiceInterface $fileBackupService, + BackupFileService $fileBackupService, LoggerInterface $logger, ConfigFileService $config ) diff --git a/app/modules/web/Controllers/ConfigBackup/DownloadBackupAppController.php b/app/modules/web/Controllers/ConfigBackup/DownloadBackupAppController.php index d3db20e4..51b2d03b 100644 --- a/app/modules/web/Controllers/ConfigBackup/DownloadBackupAppController.php +++ b/app/modules/web/Controllers/ConfigBackup/DownloadBackupAppController.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -33,7 +33,7 @@ use SP\Domain\Core\Acl\AclActionsInterface; use SP\Domain\Core\Acl\UnauthorizedPageException; use SP\Domain\Core\Exceptions\SessionTimeout; use SP\Domain\Core\Exceptions\SPException; -use SP\Domain\Export\Services\BackupFiles; +use SP\Domain\Export\Services\BackupFileHelper; use SP\Infrastructure\File\FileHandler; use SP\Modules\Web\Controllers\SimpleControllerBase; use SP\Modules\Web\Controllers\Traits\JsonTrait; @@ -57,7 +57,7 @@ final class DownloadBackupAppController extends SimpleControllerBase try { SessionContext::close(); - $filePath = BackupFiles::getAppBackupFilename( + $filePath = BackupFileHelper::getAppBackupFilename( BACKUP_PATH, $this->configData->getBackupHash(), true diff --git a/app/modules/web/Controllers/ConfigBackup/DownloadBackupDbController.php b/app/modules/web/Controllers/ConfigBackup/DownloadBackupDbController.php index 4e4c92da..6c56d033 100644 --- a/app/modules/web/Controllers/ConfigBackup/DownloadBackupDbController.php +++ b/app/modules/web/Controllers/ConfigBackup/DownloadBackupDbController.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -32,7 +32,7 @@ use SP\Domain\Core\Acl\AclActionsInterface; use SP\Domain\Core\Acl\UnauthorizedPageException; use SP\Domain\Core\Exceptions\SessionTimeout; use SP\Domain\Core\Exceptions\SPException; -use SP\Domain\Export\Services\BackupFiles; +use SP\Domain\Export\Services\BackupFileHelper; use SP\Infrastructure\File\FileHandler; use SP\Modules\Web\Controllers\SimpleControllerBase; use SP\Modules\Web\Controllers\Traits\JsonTrait; @@ -58,7 +58,7 @@ final class DownloadBackupDbController extends SimpleControllerBase try { SessionContext::close(); - $filePath = BackupFiles::getDbBackupFilename( + $filePath = BackupFileHelper::getDbBackupFilename( BACKUP_PATH, $this->configData->getBackupHash(), true diff --git a/app/modules/web/Controllers/ConfigBackup/FileBackupController.php b/app/modules/web/Controllers/ConfigBackup/FileBackupController.php index df813b48..ff2a54f4 100644 --- a/app/modules/web/Controllers/ConfigBackup/FileBackupController.php +++ b/app/modules/web/Controllers/ConfigBackup/FileBackupController.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -34,7 +34,7 @@ use SP\Domain\Core\Acl\AclActionsInterface; use SP\Domain\Core\Acl\UnauthorizedPageException; use SP\Domain\Core\Exceptions\SessionTimeout; use SP\Domain\Core\Exceptions\SPException; -use SP\Domain\Export\Ports\FileBackupServiceInterface; +use SP\Domain\Export\Ports\BackupFileService; use SP\Http\JsonMessage; use SP\Modules\Web\Controllers\SimpleControllerBase; use SP\Modules\Web\Controllers\Traits\ConfigTrait; @@ -47,12 +47,12 @@ final class FileBackupController extends SimpleControllerBase { use ConfigTrait; - private FileBackupServiceInterface $fileBackupService; + private BackupFileService $fileBackupService; public function __construct( Application $application, SimpleControllerHelper $simpleControllerHelper, - FileBackupServiceInterface $fileBackupService + BackupFileService $fileBackupService ) { parent::__construct($application, $simpleControllerHelper); diff --git a/app/modules/web/Controllers/ConfigManager/IndexController.php b/app/modules/web/Controllers/ConfigManager/IndexController.php index 515d0cd4..8648d1ec 100644 --- a/app/modules/web/Controllers/ConfigManager/IndexController.php +++ b/app/modules/web/Controllers/ConfigManager/IndexController.php @@ -45,7 +45,7 @@ use SP\Domain\Core\Exceptions\SPException; use SP\Domain\Core\File\MimeType; use SP\Domain\Core\File\MimeTypesService; use SP\Domain\Crypt\Services\TemporaryMasterPass; -use SP\Domain\Export\Services\BackupFiles; +use SP\Domain\Export\Services\BackupFileHelper; use SP\Domain\Export\Services\XmlExportService; use SP\Domain\Task\Services\Task; use SP\Domain\User\Ports\UserGroupServiceInterface; @@ -465,14 +465,14 @@ final class IndexController extends ControllerBase $template->assign('siteName', AppInfoInterface::APP_NAME); $backupAppFile = new FileHandler( - BackupFiles::getAppBackupFilename( + BackupFileHelper::getAppBackupFilename( BACKUP_PATH, $this->configData->getBackupHash() ?: '', true ) ); $backupDbFile = new FileHandler( - BackupFiles::getDbBackupFilename( + BackupFileHelper::getDbBackupFilename( BACKUP_PATH, $this->configData->getBackupHash() ?: '', true diff --git a/app/modules/web/Controllers/ControllerBase.php b/app/modules/web/Controllers/ControllerBase.php index c0a83e6b..6de2777d 100644 --- a/app/modules/web/Controllers/ControllerBase.php +++ b/app/modules/web/Controllers/ControllerBase.php @@ -40,7 +40,7 @@ use SP\Domain\Core\Context\SessionContextInterface; use SP\Domain\Core\Exceptions\FileNotFoundException; use SP\Domain\Core\Exceptions\SessionTimeout; use SP\Domain\Core\Exceptions\SPException; -use SP\Domain\Core\PhpExtensionCheckerInterface; +use SP\Domain\Core\PhpExtensionCheckerService; use SP\Domain\Core\UI\ThemeInterface; use SP\Domain\Http\RequestInterface; use SP\Domain\User\Services\UserLoginResponse; @@ -69,9 +69,9 @@ abstract class ControllerBase protected ThemeInterface $theme; protected AclInterface $acl; protected ConfigDataInterface $configData; - protected RequestInterface $request; - protected PhpExtensionCheckerInterface $extensionChecker; - protected TemplateInterface $view; + protected RequestInterface $request; + protected PhpExtensionCheckerService $extensionChecker; + protected TemplateInterface $view; protected ?UserLoginResponse $userData = null; protected ?ProfileData $userProfileData = null; protected bool $isAjax; diff --git a/lib/SP/Core/Definitions/CoreDefinitions.php b/lib/SP/Core/Definitions/CoreDefinitions.php index 95a1f48b..2ff23b83 100644 --- a/lib/SP/Core/Definitions/CoreDefinitions.php +++ b/lib/SP/Core/Definitions/CoreDefinitions.php @@ -66,6 +66,8 @@ use SP\Domain\Core\LanguageInterface; use SP\Domain\Core\UI\ThemeContextInterface; use SP\Domain\Core\UI\ThemeIconsInterface; use SP\Domain\Core\UI\ThemeInterface; +use SP\Domain\Export\Ports\BackupFileHelperService; +use SP\Domain\Export\Services\BackupFileHelper; use SP\Domain\Html\MinifyInterface; use SP\Domain\Http\RequestInterface; use SP\Domain\Install\Adapters\InstallDataFactory; @@ -82,6 +84,7 @@ use SP\Infrastructure\Database\DatabaseConnectionData; use SP\Infrastructure\Database\DatabaseInterface; use SP\Infrastructure\Database\DbStorageInterface; use SP\Infrastructure\Database\MysqlHandler; +use SP\Infrastructure\File\DirectoryHandler; use SP\Infrastructure\File\FileCache; use SP\Infrastructure\File\FileHandler; use SP\Infrastructure\File\XmlHandler; @@ -248,7 +251,9 @@ final class CoreDefinitions get(RequestInterface::class) ), RequestBasedPasswordInterface::class => autowire(RequestBasedPassword::class), - MinifyInterface::class => autowire(Minify::class) + MinifyInterface::class => autowire(Minify::class), + BackupFileHelperService::class => autowire(BackupFileHelper::class) + ->constructorParameter('path', new DirectoryHandler(BACKUP_PATH)) ]; } } diff --git a/lib/SP/Core/PhpExtensionChecker.php b/lib/SP/Core/PhpExtensionChecker.php index b2f40474..3d1ba9b1 100644 --- a/lib/SP/Core/PhpExtensionChecker.php +++ b/lib/SP/Core/PhpExtensionChecker.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -26,7 +26,7 @@ namespace SP\Core; use RuntimeException; use SP\Domain\Core\Exceptions\CheckException; -use SP\Domain\Core\PhpExtensionCheckerInterface; +use SP\Domain\Core\PhpExtensionCheckerService; use function SP\__u; use function SP\logger; @@ -34,7 +34,7 @@ use function SP\logger; /** * Class PhpExtensionChecker */ -final class PhpExtensionChecker implements PhpExtensionCheckerInterface +final class PhpExtensionChecker implements PhpExtensionCheckerService { /** * Array of extensions needed by sysPass. diff --git a/lib/SP/Domain/Core/PhpExtensionCheckerInterface.php b/lib/SP/Domain/Core/PhpExtensionCheckerService.php similarity index 93% rename from lib/SP/Domain/Core/PhpExtensionCheckerInterface.php rename to lib/SP/Domain/Core/PhpExtensionCheckerService.php index 51592223..852767d9 100644 --- a/lib/SP/Domain/Core/PhpExtensionCheckerInterface.php +++ b/lib/SP/Domain/Core/PhpExtensionCheckerService.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -34,7 +34,7 @@ use SP\Domain\Core\Exceptions\CheckException; * @method bool checkPhar(bool $exception = false) * @method bool checkGd(bool $exception = false) */ -interface PhpExtensionCheckerInterface +interface PhpExtensionCheckerService { /** * Checks if the extension is installed diff --git a/lib/SP/Domain/Export/Ports/BackupFilesInterface.php b/lib/SP/Domain/Export/Ports/BackupFileHelperService.php similarity index 94% rename from lib/SP/Domain/Export/Ports/BackupFilesInterface.php rename to lib/SP/Domain/Export/Ports/BackupFileHelperService.php index cb59c373..3b162ff7 100644 --- a/lib/SP/Domain/Export/Ports/BackupFilesInterface.php +++ b/lib/SP/Domain/Export/Ports/BackupFileHelperService.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -31,7 +31,7 @@ use SP\Infrastructure\File\FileHandlerInterface; /** * BackupFiles */ -interface BackupFilesInterface +interface BackupFileHelperService { /** * @return FileHandlerInterface diff --git a/lib/SP/Domain/Export/Ports/FileBackupServiceInterface.php b/lib/SP/Domain/Export/Ports/BackupFileService.php similarity index 91% rename from lib/SP/Domain/Export/Ports/FileBackupServiceInterface.php rename to lib/SP/Domain/Export/Ports/BackupFileService.php index 06e7cff8..9d299574 100644 --- a/lib/SP/Domain/Export/Ports/FileBackupServiceInterface.php +++ b/lib/SP/Domain/Export/Ports/BackupFileService.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -29,7 +29,7 @@ use SP\Domain\Common\Services\ServiceException; /** * Esta clase es la encargada de realizar la copia de sysPass. */ -interface FileBackupServiceInterface +interface BackupFileService { /** * Realizar backup de la BBDD y aplicación. diff --git a/lib/SP/Domain/Export/Services/FileBackupService.php b/lib/SP/Domain/Export/Services/BackupFile.php similarity index 90% rename from lib/SP/Domain/Export/Services/FileBackupService.php rename to lib/SP/Domain/Export/Services/BackupFile.php index 2876c8a4..6c5d1799 100644 --- a/lib/SP/Domain/Export/Services/FileBackupService.php +++ b/lib/SP/Domain/Export/Services/BackupFile.php @@ -39,8 +39,8 @@ use SP\Domain\Core\Exceptions\CheckException; use SP\Domain\Core\Exceptions\ConstraintException; use SP\Domain\Core\Exceptions\QueryException; use SP\Domain\Core\Exceptions\SPException; -use SP\Domain\Export\Ports\BackupFilesInterface; -use SP\Domain\Export\Ports\FileBackupServiceInterface; +use SP\Domain\Export\Ports\BackupFileHelperService; +use SP\Domain\Export\Ports\BackupFileService; use SP\Infrastructure\Common\Repositories\Query; use SP\Infrastructure\Database\DatabaseInterface; use SP\Infrastructure\Database\DatabaseUtil; @@ -53,7 +53,7 @@ use function SP\__u; /** * Esta clase es la encargada de realizar la copia de sysPass. */ -final class FileBackupService implements FileBackupServiceInterface +final class BackupFile implements BackupFileService { public const BACKUP_INCLUDE_REGEX = /** @lang RegExp */ '#^(?:[A-Z]:)?(?:/(?!(\.git|backup|cache|temp|vendor|tests))[^/]+)+/[^/]+\.\w+$#Di'; @@ -64,10 +64,10 @@ final class FileBackupService implements FileBackupServiceInterface private ?string $backupPath = null; public function __construct( - Application $application, - private readonly DatabaseInterface $database, - private readonly DatabaseUtil $databaseUtil, - private readonly BackupFilesInterface $backupFiles + Application $application, + private readonly DatabaseInterface $database, + private readonly DatabaseUtil $databaseUtil, + private readonly BackupFileHelperService $backupFileHelperService ) { $this->config = $application->getConfig(); $this->eventDispatcher = $application->getEventDispatcher(); @@ -94,10 +94,10 @@ final class FileBackupService implements FileBackupServiceInterface new Event($this, EventMessage::factory()->addDescription(__u('Make Backup'))) ); - $this->backupTables($this->backupFiles->getDbBackupFileHandler()); + $this->backupTables($this->backupFileHelperService->getDbBackupFileHandler()); $this->backupApp($applicationPath); - $this->configData->setBackupHash($this->backupFiles->getHash()); + $this->configData->setBackupHash($this->backupFileHelperService->getHash()); $this->config->save($this->configData); } catch (Exception $e) { $this->eventDispatcher->notify('exception', new Event($e)); @@ -255,7 +255,7 @@ final class FileBackupService implements FileBackupServiceInterface $fileHandler->write(implode(PHP_EOL, $sqlOut)); $fileHandler->close(); - $this->backupFiles->getDbBackupArchiveHandler()->compressFile($fileHandler->getFile()); + $this->backupFileHelperService->getDbBackupArchiveHandler()->compressFile($fileHandler->getFile()); $fileHandler->delete(); } @@ -289,11 +289,14 @@ final class FileBackupService implements FileBackupServiceInterface new Event($this, EventMessage::factory()->addDescription(__u('Copying application'))) ); - $this->backupFiles->getAppBackupArchiveHandler()->compressDirectory($directory, self::BACKUP_INCLUDE_REGEX); + $this->backupFileHelperService->getAppBackupArchiveHandler()->compressDirectory( + $directory, + self::BACKUP_INCLUDE_REGEX + ); } public function getHash(): string { - return $this->backupFiles->getHash(); + return $this->backupFileHelperService->getHash(); } } diff --git a/lib/SP/Domain/Export/Services/BackupFiles.php b/lib/SP/Domain/Export/Services/BackupFileHelper.php similarity index 62% rename from lib/SP/Domain/Export/Services/BackupFiles.php rename to lib/SP/Domain/Export/Services/BackupFileHelper.php index d1fe5dee..db90c78a 100644 --- a/lib/SP/Domain/Export/Services/BackupFiles.php +++ b/lib/SP/Domain/Export/Services/BackupFileHelper.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -24,89 +24,56 @@ namespace SP\Domain\Export\Services; -use SP\Core\PhpExtensionChecker; use SP\Domain\Core\AppInfoInterface; use SP\Domain\Core\Exceptions\CheckException; -use SP\Domain\Export\Ports\BackupFilesInterface; +use SP\Domain\Core\PhpExtensionCheckerService; +use SP\Domain\Export\Ports\BackupFileHelperService; +use SP\Domain\File\Ports\DirectoryHandlerService; use SP\Infrastructure\File\ArchiveHandler; use SP\Infrastructure\File\ArchiveHandlerInterface; use SP\Infrastructure\File\FileHandler; use SP\Infrastructure\File\FileHandlerInterface; - -use function SP\__; -use function SP\__u; +use SP\Util\FileUtil; /** - * BackupFiles + * BackupFileHelper */ -final class BackupFiles implements BackupFilesInterface +final class BackupFileHelper implements BackupFileHelperService { private const BACKUP_PREFFIX = 'sysPassBackup'; private string $hash; - private string $path; private string $appBackupFilename; private string $dbBackupFilename; - /** - * @var PhpExtensionChecker - */ - private PhpExtensionChecker $extensionChecker; /** - * @param string $path The path where to store the backup files - * * @throws CheckException */ - public function __construct(PhpExtensionChecker $extensionChecker, string $path = BACKUP_PATH) - { - $this->extensionChecker = $extensionChecker; - $this->path = $path; - $this->hash = $this->getBackupHash(); - - $this->checkBackupDir(); - - $this->appBackupFilename = self::getAppBackupFilename($this->path, $this->hash); - $this->dbBackupFilename = self::getDbBackupFilename($this->path, $this->hash); + public function __construct( + private readonly PhpExtensionCheckerService $phpExtensionCheckerService, + private readonly DirectoryHandlerService $directoryHandlerService + ) { + $this->hash = $this->buildHash(); + $this->directoryHandlerService->checkOrCreate(); + $this->appBackupFilename = self::getAppBackupFilename($this->directoryHandlerService->getPath(), $this->hash); + $this->dbBackupFilename = self::getDbBackupFilename($this->directoryHandlerService->getPath(), $this->hash); } /** - * Generate a unique hash to avoid unwated downloads + * Generate a unique hash to avoid unwanted downloads * * @return string */ - private function getBackupHash(): string + private function buildHash(): string { return sha1(uniqid(self::BACKUP_PREFFIX, true)); } - /** - * Check and create the backup dir - * - * @throws CheckException - */ - private function checkBackupDir(): void - { - if (is_dir($this->path) === false - && !@mkdir($concurrentDirectory = $this->path, 0750, true) - && !is_dir($concurrentDirectory) - ) { - throw new CheckException( - sprintf(__('Unable to create the backups directory ("%s")'), $this->path) - ); - } - - if (!is_writable($this->path)) { - throw new CheckException( - __u('Please, check the backup directory permissions') - ); - } - } - public static function getAppBackupFilename( string $path, string $hash, bool $compressed = false ): string { - $file = $path . DIRECTORY_SEPARATOR . AppInfoInterface::APP_NAME . '_app-' . $hash; + $file = sprintf('%s_app-%s', FileUtil::buildPath($path, AppInfoInterface::APP_NAME), $hash); if ($compressed) { return $file . ArchiveHandler::COMPRESS_EXTENSION; @@ -120,7 +87,7 @@ final class BackupFiles implements BackupFilesInterface string $hash, bool $compressed = false ): string { - $file = $path . DIRECTORY_SEPARATOR . AppInfoInterface::APP_NAME . '_db-' . $hash; + $file = sprintf('%s_db-%s', FileUtil::buildPath($path, AppInfoInterface::APP_NAME), $hash); if ($compressed) { return $file . ArchiveHandler::COMPRESS_EXTENSION; @@ -147,20 +114,18 @@ final class BackupFiles implements BackupFilesInterface /** * @return ArchiveHandlerInterface - * @throws CheckException */ public function getDbBackupArchiveHandler(): ArchiveHandlerInterface { - return new ArchiveHandler($this->dbBackupFilename, $this->extensionChecker); + return new ArchiveHandler($this->dbBackupFilename, $this->phpExtensionCheckerService); } /** * @return ArchiveHandlerInterface - * @throws CheckException */ public function getAppBackupArchiveHandler(): ArchiveHandlerInterface { - return new ArchiveHandler($this->appBackupFilename, $this->extensionChecker); + return new ArchiveHandler($this->appBackupFilename, $this->phpExtensionCheckerService); } /** diff --git a/lib/SP/Domain/File/Ports/DirectoryHandlerService.php b/lib/SP/Domain/File/Ports/DirectoryHandlerService.php new file mode 100644 index 00000000..01d7a4c1 --- /dev/null +++ b/lib/SP/Domain/File/Ports/DirectoryHandlerService.php @@ -0,0 +1,52 @@ +. + */ + +namespace SP\Domain\File\Ports; + +use Directory; +use SP\Domain\Core\Exceptions\CheckException; + +/** + * Interface DirectoryHandlerService + */ +interface DirectoryHandlerService +{ + /** + * @throws CheckException + */ + public function checkOrCreate(): void; + + public function isDir(): bool; + + public function create(int $permissions = 0750): bool; + + public function isWritable(): bool; + + /** + * @throws CheckException + */ + public function getDir(): Directory; + + public function getPath(): string; +} diff --git a/lib/SP/Infrastructure/File/ArchiveHandler.php b/lib/SP/Infrastructure/File/ArchiveHandler.php index 65255b12..5954a906 100644 --- a/lib/SP/Infrastructure/File/ArchiveHandler.php +++ b/lib/SP/Infrastructure/File/ArchiveHandler.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -24,16 +24,12 @@ namespace SP\Infrastructure\File; - use Phar; use PharData; -use SP\Core\PhpExtensionChecker; -use SP\Domain\Core\Exceptions\CheckException; +use SP\Domain\Core\PhpExtensionCheckerService; /** * Class ArchiveHandler - * - * @package SP\Infrastructure\File */ final class ArchiveHandler implements ArchiveHandlerInterface { @@ -41,12 +37,9 @@ final class ArchiveHandler implements ArchiveHandlerInterface private PharData $archive; - /** - * @throws CheckException - */ - public function __construct(string $archive, PhpExtensionChecker $extensionChecker) + public function __construct(string $archive, PhpExtensionCheckerService $phpExtensionCheckerService) { - $extensionChecker->checkPhar(true); + $phpExtensionCheckerService->checkPhar(true); $this->archive = new PharData(self::makeArchiveName($archive)); } @@ -64,10 +57,10 @@ final class ArchiveHandler implements ArchiveHandlerInterface $archive, 0, strrpos($archive, '.') ?: strlen($archive) - ).$archiveExtension; + ) . $archiveExtension; } - return $archive.$archiveExtension; + return $archive . $archiveExtension; } /** diff --git a/lib/SP/Infrastructure/File/DirectoryHandler.php b/lib/SP/Infrastructure/File/DirectoryHandler.php new file mode 100644 index 00000000..9f0fa0de --- /dev/null +++ b/lib/SP/Infrastructure/File/DirectoryHandler.php @@ -0,0 +1,89 @@ +. + */ + +namespace SP\Infrastructure\File; + +use Directory; +use SP\Domain\Core\Exceptions\CheckException; +use SP\Domain\File\Ports\DirectoryHandlerService; + +use function SP\__u; + +/** + * Class DirectoryHandler + */ +final class DirectoryHandler implements DirectoryHandlerService +{ + public function __construct(private readonly string $path) + { + } + + /** + * @throws CheckException + */ + public function checkOrCreate(): void + { + if (!$this->isDir() && !$this->create()) { + throw CheckException::error(sprintf(__u('Unable to create directory ("%s")'), $this->path)); + } + + if (!$this->isWritable()) { + throw CheckException::error(__u('Please, check the directory permissions')); + } + } + + public function isDir(): bool + { + return @is_dir($this->path); + } + + public function create(int $permissions = 0750): bool + { + return @mkdir($this->path, $permissions, true); + } + + public function isWritable(): bool + { + return @is_writable($this->path); + } + + /** + * @throws CheckException + */ + public function getDir(): Directory + { + $dir = dir($this->path); + + if (!$dir) { + throw CheckException::error(sprintf(__u('Unable to open directory ("%s")'), $this->path)); + } + + return $dir; + } + + public function getPath(): string + { + return $this->path; + } +} diff --git a/tests/SPT/Domain/Export/Services/BackupFileHelperTest.php b/tests/SPT/Domain/Export/Services/BackupFileHelperTest.php new file mode 100644 index 00000000..304d1e52 --- /dev/null +++ b/tests/SPT/Domain/Export/Services/BackupFileHelperTest.php @@ -0,0 +1,147 @@ +. + */ + +namespace SPT\Domain\Export\Services; + +use PHPUnit\Framework\MockObject\Exception; +use PHPUnit\Framework\MockObject\MockObject; +use SP\Core\Context\ContextException; +use SP\Domain\Core\Exceptions\CheckException; +use SP\Domain\Core\PhpExtensionCheckerService; +use SP\Domain\Export\Services\BackupFileHelper; +use SP\Domain\File\Ports\DirectoryHandlerService; +use SPT\Stubs\PhpExtensionCheckerStub; +use SPT\UnitaryTestCase; + +/** + * Class BackupFileHelperTest + * + * @group unitary + */ +class BackupFileHelperTest extends UnitaryTestCase +{ + + private PhpExtensionCheckerService|MockObject $phpExtensionCheckerService; + private BackupFileHelper $backupFileHelper; + + public function testGetAppBackupFilename() + { + $hash = self::$faker->sha1(); + $out = BackupFileHelper::getAppBackupFilename('/a/path', $hash); + $expected = sprintf('/a/path/sysPass_app-%s', $hash); + + $this->assertEquals($expected, $out); + } + + public function testGetAppBackupFilenameWithCompress() + { + $hash = self::$faker->sha1(); + $out = BackupFileHelper::getAppBackupFilename('/a/path', $hash, true); + $expected = sprintf('/a/path/sysPass_app-%s.tar.gz', $hash); + + $this->assertEquals($expected, $out); + } + + public function testGetDbBackupFilename() + { + $hash = self::$faker->sha1(); + $out = BackupFileHelper::getDbBackupFilename('/a/path', $hash); + $expected = sprintf('/a/path/sysPass_db-%s.sql', $hash); + + $this->assertEquals($expected, $out); + } + + public function testGetDbBackupFilenameWithCompress() + { + $hash = self::$faker->sha1(); + $out = BackupFileHelper::getDbBackupFilename('/a/path', $hash, true); + $expected = sprintf('/a/path/sysPass_db-%s.tar.gz', $hash); + + $this->assertEquals($expected, $out); + } + + public function testGetDbBackupArchiveHandler() + { + $this->phpExtensionCheckerService + ->expects(self::once()) + ->method('checkPhar') + ->with(true); + + $this->backupFileHelper->getDbBackupArchiveHandler(); + } + + public function testGetAppBackupFileHandler() + { + $this->phpExtensionCheckerService + ->expects(self::once()) + ->method('checkPhar') + ->with(true); + + $this->backupFileHelper->getAppBackupArchiveHandler(); + } + + public function testGetDbBackupFileHandler() + { + $expected = BackupFileHelper::getDbBackupFilename(TMP_PATH, $this->backupFileHelper->getHash()); + $out = $this->backupFileHelper->getDbBackupFileHandler(); + + $this->assertEquals($expected, $out->getFile()); + } + + public function testGetAppBackupArchiveHandler() + { + $expected = BackupFileHelper::getAppBackupFilename(TMP_PATH, $this->backupFileHelper->getHash()); + $out = $this->backupFileHelper->getAppBackupFileHandler(); + + $this->assertEquals($expected, $out->getFile()); + } + + /** + * @throws Exception + * @throws ContextException + * @throws CheckException + */ + protected function setUp(): void + { + parent::setUp(); + + $this->phpExtensionCheckerService = $this->createMock(PhpExtensionCheckerStub::class); + + $directoryHandlerService = $this->createMock(DirectoryHandlerService::class); + $directoryHandlerService + ->expects(self::once()) + ->method('checkOrCreate'); + + $directoryHandlerService + ->expects(self::exactly(2)) + ->method('getPath') + ->willReturn(TMP_PATH); + + $this->backupFileHelper = new BackupFileHelper( + $this->phpExtensionCheckerService, + $directoryHandlerService + ); + } +} + diff --git a/tests/SPT/Domain/Export/Services/FileBackupServiceTest.php b/tests/SPT/Domain/Export/Services/FileBackupServiceTest.php index 33cc2c6f..b1722a74 100644 --- a/tests/SPT/Domain/Export/Services/FileBackupServiceTest.php +++ b/tests/SPT/Domain/Export/Services/FileBackupServiceTest.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2023, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -33,8 +33,8 @@ use SP\Core\Context\ContextException; use SP\Domain\Common\Models\Simple; use SP\Domain\Common\Services\ServiceException; use SP\Domain\Core\Exceptions\SPException; -use SP\Domain\Export\Ports\BackupFilesInterface; -use SP\Domain\Export\Services\FileBackupService; +use SP\Domain\Export\Ports\BackupFileHelperService; +use SP\Domain\Export\Services\BackupFile; use SP\Infrastructure\Database\DatabaseInterface; use SP\Infrastructure\Database\DatabaseUtil; use SP\Infrastructure\Database\DbStorageInterface; @@ -52,9 +52,9 @@ use SPT\UnitaryTestCase; */ class FileBackupServiceTest extends UnitaryTestCase { - private FileBackupService $fileBackupService; - private BackupFilesInterface|MockObject $backupFiles; - private DatabaseInterface|MockObject $database; + private BackupFile $fileBackupService; + private BackupFileHelperService|MockObject $backupFiles; + private DatabaseInterface|MockObject $database; /** * @throws ServiceException @@ -125,7 +125,7 @@ class FileBackupServiceTest extends UnitaryTestCase ->method('compressDirectory') ->with( APP_ROOT, - FileBackupService::BACKUP_INCLUDE_REGEX + BackupFile::BACKUP_INCLUDE_REGEX ); $fileHandler = $this->createMock(FileHandlerInterface::class); @@ -224,9 +224,9 @@ class FileBackupServiceTest extends UnitaryTestCase parent::setUp(); $this->database = $this->createMock(DatabaseInterface::class); - $this->backupFiles = $this->createMock(BackupFilesInterface::class); + $this->backupFiles = $this->createMock(BackupFileHelperService::class); - $this->fileBackupService = new FileBackupService( + $this->fileBackupService = new BackupFile( $this->application, $this->database, $this->createStub(DatabaseUtil::class), diff --git a/tests/SPT/Modules/Cli/Commands/BackupCommandTest.php b/tests/SPT/Modules/Cli/Commands/BackupCommandTest.php index e7a20cff..aa4955be 100644 --- a/tests/SPT/Modules/Cli/Commands/BackupCommandTest.php +++ b/tests/SPT/Modules/Cli/Commands/BackupCommandTest.php @@ -28,7 +28,7 @@ use DI\DependencyException; use DI\NotFoundException; use SP\Domain\Config\Services\ConfigFile; use SP\Domain\Core\Exceptions\FileNotFoundException; -use SP\Domain\Export\Services\BackupFiles; +use SP\Domain\Export\Services\BackupFileHelper; use SP\Modules\Cli\Commands\BackupCommand; use SPT\Modules\Cli\CliTestCase; @@ -97,14 +97,14 @@ class BackupCommandTest extends CliTestCase $configData = self::$dic->get(ConfigFile::class)->getConfigData(); $this->assertFileExists( - BackupFiles::getAppBackupFilename( + BackupFileHelper::getAppBackupFilename( TMP_PATH, $configData->getBackupHash(), true ) ); $this->assertFileExists( - BackupFiles::getDbBackupFilename( + BackupFileHelper::getDbBackupFilename( TMP_PATH, $configData->getBackupHash(), true diff --git a/tests/SPT/Stubs/PhpExtensionCheckerStub.php b/tests/SPT/Stubs/PhpExtensionCheckerStub.php new file mode 100644 index 00000000..cb1d7c7f --- /dev/null +++ b/tests/SPT/Stubs/PhpExtensionCheckerStub.php @@ -0,0 +1,78 @@ +. + */ + +namespace SPT\Stubs; + +use SP\Domain\Core\PhpExtensionCheckerService; + +/** + * Class PhpExtensionCheckerStub + */ +class PhpExtensionCheckerStub implements PhpExtensionCheckerService +{ + + /** + * @inheritDoc + */ + public function checkIsAvailable(string $extension, bool $exception = false): bool + { + return true; + } + + /** + * @inheritDoc + */ + public function checkMandatory(): void + { + // TODO: Implement checkMandatory() method. + } + + /** + * @inheritDoc + */ + public function getMissing(): array + { + return []; + } + + public function checkCurl(bool $exception = false): bool + { + return true; + } + + public function checkLdap(bool $exception = false): bool + { + return true; + } + + public function checkPhar(bool $exception = false): bool + { + return true; + } + + public function checkGd(bool $exception = false): bool + { + return true; + } +}