From d48f9207e358e7fccffde3e8ff3a39b7acfd32eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D?= Date: Fri, 29 Mar 2024 19:20:49 +0100 Subject: [PATCH] chore(tests): UT for MysqlFileParser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rubén D --- .../Database/DatabaseFileInterface.php | 6 +- .../Database/MysqlFileParser.php | 57 ++++-------- lib/SP/Infrastructure/File/FileHandler.php | 12 +++ .../File/FileHandlerInterface.php | 5 ++ .../Database/MysqlFileParserTest.php | 89 +++++++++++++++++++ 5 files changed, 124 insertions(+), 45 deletions(-) create mode 100644 tests/SPT/Infrastructure/Database/MysqlFileParserTest.php diff --git a/lib/SP/Infrastructure/Database/DatabaseFileInterface.php b/lib/SP/Infrastructure/Database/DatabaseFileInterface.php index fd6b2fc0..fadc03e0 100644 --- a/lib/SP/Infrastructure/Database/DatabaseFileInterface.php +++ b/lib/SP/Infrastructure/Database/DatabaseFileInterface.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2021, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2024, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -34,5 +34,5 @@ interface DatabaseFileInterface /** * Parses a database script file and returns an array of lines parsed */ - public function parse(string $delimiter = ';'): array; -} \ No newline at end of file + public function parse(string $delimiter = ';'): iterable; +} diff --git a/lib/SP/Infrastructure/Database/MysqlFileParser.php b/lib/SP/Infrastructure/Database/MysqlFileParser.php index 173130d4..2f7fd900 100644 --- a/lib/SP/Infrastructure/Database/MysqlFileParser.php +++ b/lib/SP/Infrastructure/Database/MysqlFileParser.php @@ -25,71 +25,44 @@ namespace SP\Infrastructure\Database; use SP\Infrastructure\File\FileException; -use SP\Infrastructure\File\FileHandler; use SP\Infrastructure\File\FileHandlerInterface; /** * Class MysqlFileParser - * - * @package SP\Storage */ -final class MysqlFileParser implements DatabaseFileInterface +final readonly class MysqlFileParser implements DatabaseFileInterface { - private FileHandler $fileHandler; - - public function __construct(FileHandlerInterface $fileHandler) + public function __construct(private FileHandlerInterface $fileHandler) { - $this->fileHandler = $fileHandler; } /** - * Parses a database script file and returns an array of lines parsed + * Parses a database script file and yields the queries parsed * * @throws FileException */ - public function parse(string $delimiter = ';'): array + public function parse(string $delimiter = ';'): iterable { - $queries = []; - $query = ''; + $query = []; $delimiterLength = strlen($delimiter); $this->fileHandler->checkIsReadable(); - $handle = $this->fileHandler->open(); + foreach ($this->fileHandler->read() as $data) { + $line = trim($data); + $lineLength = strlen($line); - while (($buffer = fgets($handle)) !== false) { - $buffer = trim($buffer); - $length = strlen($buffer); + if ($lineLength > 0 && !(str_starts_with($line, '--') || str_contains($line, 'DELIMITER'))) { + if (substr($line, -$delimiterLength) === $delimiter) { + $query[] = substr($line, 0, $lineLength - $delimiterLength); - if ($length > 0 - && strpos($buffer, '--') !== 0 - ) { - // CHecks if delimiter based EOL is reached - $end = strrpos($buffer, $delimiter) === $length - $delimiterLength; + yield implode(' ', $query); - // Checks if line is an SQL statement wrapped by a comment - if (preg_match('#^(?/\*!\d+.*\*/)#', $buffer, $matches)) { - if (!$end) { - $query .= $matches['stmt'].PHP_EOL; - } else { - $queries[] = $query.$matches['stmt']; - - $query = ''; - } - } elseif (!$end) { - $query .= $buffer.PHP_EOL; - } elseif (strpos($buffer, 'DELIMITER') === false) { - $queries[] = $query. - trim( - substr_replace($buffer, '', $length - $delimiterLength), - $delimiterLength - ); - - $query = ''; + $query = []; + } else { + $query[] = $line; } } } - - return $queries; } } diff --git a/lib/SP/Infrastructure/File/FileHandler.php b/lib/SP/Infrastructure/File/FileHandler.php index f4f50282..28366b3c 100644 --- a/lib/SP/Infrastructure/File/FileHandler.php +++ b/lib/SP/Infrastructure/File/FileHandler.php @@ -101,6 +101,18 @@ final class FileHandler extends SplFileObject implements FileHandlerInterface } } + /** + * Reads data from a file line by line + */ + public function read(): iterable + { + $this->autoDetectEOL(); + + while (!$this->eof()) { + yield $this->fgets(); + } + } + /** * Saves a string into a file * diff --git a/lib/SP/Infrastructure/File/FileHandlerInterface.php b/lib/SP/Infrastructure/File/FileHandlerInterface.php index 98504fab..1c0db449 100644 --- a/lib/SP/Infrastructure/File/FileHandlerInterface.php +++ b/lib/SP/Infrastructure/File/FileHandlerInterface.php @@ -142,4 +142,9 @@ interface FileHandlerInterface * @throws FileException */ public function readFromCsv(string $delimiter): iterable; + + /** + * Reads data from a file line by line + */ + public function read(): iterable; } diff --git a/tests/SPT/Infrastructure/Database/MysqlFileParserTest.php b/tests/SPT/Infrastructure/Database/MysqlFileParserTest.php new file mode 100644 index 00000000..40f30c20 --- /dev/null +++ b/tests/SPT/Infrastructure/Database/MysqlFileParserTest.php @@ -0,0 +1,89 @@ +. + */ + +namespace SPT\Infrastructure\Database; + +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\MockObject\Exception; +use SP\Infrastructure\Database\MysqlFileParser; +use SP\Infrastructure\File\FileException; +use SP\Infrastructure\File\FileHandlerInterface; +use SPT\UnitaryTestCase; + +/** + * Class MysqlFileParserTest + */ +#[Group('unitary')] +class MysqlFileParserTest extends UnitaryTestCase +{ + + /** + * @throws Exception + * @throws FileException + */ + public function testParse() + { + $lines = static function () { + yield 'DELIMITER $$'; + yield '-- Test'; + yield '/*!40101 SET @OLD_CHARACTER_SET_CLIENT = @@CHARACTER_SET_CLIENT */$$'; + yield '/*!40101 SET NAMES utf8mb4 */$$'; + yield 'CREATE TABLE `Account`'; + yield '('; + yield '`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,'; + yield 'PRIMARY KEY (`id`),'; + yield 'CONSTRAINT `fk_Account_categoryId` FOREIGN KEY (`categoryId`) REFERENCES `Category` (`id`)'; + yield ') ENGINE = InnoDB'; + yield 'COLLATE = utf8mb3_unicode_ci$$'; + }; + + $fileHandler = $this->createMock(FileHandlerInterface::class); + + $fileHandler->expects($this->once()) + ->method('checkIsReadable'); + + $fileHandler->expects($this->once()) + ->method('read') + ->willReturnCallback($lines); + + $mysqlFileParser = new MysqlFileParser($fileHandler); + + $counter = 0; + $queries = []; + + foreach ($mysqlFileParser->parse('$$') as $query) { + $counter++; + $this->assertNotEmpty($query); + $queries[] = $query; + } + + $this->assertEquals(3, $counter); + $this->assertEquals('/*!40101 SET @OLD_CHARACTER_SET_CLIENT = @@CHARACTER_SET_CLIENT */', $queries[0]); + $this->assertEquals('/*!40101 SET NAMES utf8mb4 */', $queries[1]); + $this->assertEquals( + 'CREATE TABLE `Account` ( `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, PRIMARY KEY (`id`), CONSTRAINT `fk_Account_categoryId` FOREIGN KEY (`categoryId`) REFERENCES `Category` (`id`) ) ENGINE = InnoDB COLLATE = utf8mb3_unicode_ci', + $queries[2] + ); + } +}