From 2acf0362fa5ccd418322a9e3a470bce83dde260e Mon Sep 17 00:00:00 2001 From: Juan Pablo Vial Date: Tue, 29 Apr 2025 21:41:49 -0400 Subject: [PATCH] Ambiente de testeo --- app/phpunit.xml | 6 +- app/test.bootstrap.php | 177 +++++++++++++++++++++++++++++++++-------- docker-compose.yml | 14 ---- testing.compose.yml | 14 ++-- 4 files changed, 151 insertions(+), 60 deletions(-) diff --git a/app/phpunit.xml b/app/phpunit.xml index e1780ab..6ac4a65 100644 --- a/app/phpunit.xml +++ b/app/phpunit.xml @@ -2,7 +2,7 @@ - + diff --git a/app/test.bootstrap.php b/app/test.bootstrap.php index 7a1bb19..992792c 100644 --- a/app/test.bootstrap.php +++ b/app/test.bootstrap.php @@ -5,42 +5,149 @@ require_once implode(DIRECTORY_SEPARATOR, [ 'autoload.php' ]); -function setupDatabase(string $schemaFilename): void { - printf("Loading database schema from %s", $schemaFilename); - $start = microtime(true); - - $dsn = "mysql:host={$_ENV['DB_HOST']};dbname={$_ENV['DB_DATABASE']}"; - $pdo = new PDO($dsn, $_ENV['DB_USER'], $_ENV['DB_PASSWORD']); - - $sql = file_get_contents($schemaFilename); - $pdo->exec($sql); - - $end = microtime(true); - printf(" in %.2f seconds\n", $end - $start); -} -function truncateTables(): void { - printf("Truncating tables"); - $start = microtime(true); - - $dsn = "mysql:host={$_ENV['DB_HOST']};dbname={$_ENV['DB_DATABASE']}"; - $pdo = new PDO($dsn, $_ENV['DB_USER'], $_ENV['DB_PASSWORD']); - - $pdo->exec("SET FOREIGN_KEY_CHECKS=0"); - $statement = $pdo->query('SHOW TABLES'); - $tables = array_map(function(array $row) { - return $row["Tables_in_{$_ENV['DB_DATABASE']}"]; - }, $statement->fetchAll(PDO::FETCH_ASSOC)); - - foreach ($tables as $table) { - $pdo->exec("TRUNCATE TABLE `$table`"); +class Benchmark +{ + protected static array $queue = []; + public static function print(): void + { + if (empty(self::$queue)) { + return; + } + echo implode(PHP_EOL, self::$queue), PHP_EOL; + self::$queue = []; } - $pdo->exec("SET FOREIGN_KEY_CHECKS=1"); - $end = microtime(true); - printf(" in %.2f seconds\n", $end - $start); + public static function execute(callable $callback, ?array $args = null, ?string $description = null): mixed + { + if (null === $description) { + $description = self::getCallableName($callback); + } + $i = count(self::$queue); + self::$queue[$i] = "Executing $description"; + $start = microtime(true); + + if (null === $args) { + $result = call_user_func($callback); + } else { + $result = call_user_func_array($callback, $args); + } + + $end = microtime(true); + self::$queue[$i] .= sprintf(" in %.2f seconds", $end - $start); + return $result; + } + + public static function getCallableName(callable $callable): string + { + return match (true) { + is_string($callable) && strpos($callable, '::') => '[static] ' . $callable, + is_string($callable) => '[function] ' . $callable, + is_array($callable) && is_object($callable[0]) => '[method] ' . get_class($callable[0]) . '->' . $callable[1], + is_array($callable) => '[static] ' . $callable[0] . '::' . $callable[1], + $callable instanceof Closure => '[closure]', + is_object($callable) => '[invokable] ' . get_class($callable), + default => '[unknown]', + }; + } } -$schemaFilename = implode(DIRECTORY_SEPARATOR, [__DIR__, 'resources', 'database', 'schema.sql']); -setupDatabase($schemaFilename); -register_shutdown_function(function() { - truncateTables(); +class TestBootstrap +{ + public function __construct(protected array $configuration) {} + + public function run(): void + { + if (Benchmark::execute([$this, 'isMigrated'])) { + Benchmark::execute([$this, 'resetDatabase']); + } + Benchmark::execute([$this, 'migrate']); + } + + protected string $baseCommand = "bin/phinx"; + public function isMigrated(): bool + { + $cmd = "{$this->baseCommand} status -e testing -f json --no-info"; + $output = shell_exec($cmd); + $status = json_decode($output, true); + + return $status['missing_count'] > 0; + } + public function migrate(): void + { + $cmd = "{$this->baseCommand} migrate -e testing"; + shell_exec($cmd); + } + + public function resetDatabase(): void + { + $tables = $this->getTables(); + if ($this->connect()->beginTransaction()) { + try { + $this->connect()->query("SET FOREIGN_KEY_CHECKS=0;"); + foreach ($tables as $table) { + $this->connect()->query("DROP TABLE IF EXISTS `{$table}`"); + } + $this->connect()->query("SET FOREIGN_KEY_CHECKS=1;"); + if ($this->connect()->inTransaction()) { + $this->connect()->commit(); + } + } catch (PDOException $exception) { + if ($this->connect()->inTransaction()) { + $this->connect()->rollBack(); + } + throw $exception; + } + } + } + public function truncateTables(): void + { + $tables = $this->getTables(); + if ($this->connect()->beginTransaction()) { + try { + $this->connect()->query("SET FOREIGN_KEY_CHECKS=0;"); + foreach ($tables as $table) { + $this->connect()->query("TRUNCATE TABLE `{$table}`"); + } + $this->connect()->query("SET FOREIGN_KEY_CHECKS=1;"); + if ($this->connect()->inTransaction()) { + $this->connect()->commit(); + } + } catch (PDOException $exception) { + if ($this->connect()->inTransaction()) { + $this->connect()->rollBack(); + } + throw $exception; + } + } + } + + protected PDO $connection; + protected function connect(): PDO + { + if (!isset($this->connection)) { + $dsn = "mysql:host={$this->configuration['DB_HOST']};dbname={$this->configuration['DB_DATABASE']}"; + $this->connection = new PDO( + $dsn, + $this->configuration['DB_USER'], + $this->configuration['DB_PASSWORD'] + ); + } + return $this->connection; + } + protected array $tables; + protected function getTables(): array + { + if (!isset($this->tables)) { + $this->tables = $this->connect()->query("SHOW TABLES")->fetchAll(PDO::FETCH_COLUMN); + } + return $this->tables; + } +} + +$bootstrap = new TestBootstrap($_ENV); +Benchmark::execute([$bootstrap, 'run']); +Benchmark::print(); + +register_shutdown_function(function() use ($bootstrap) { + Benchmark::execute([$bootstrap, 'truncateTables']); + Benchmark::print(); }); diff --git a/docker-compose.yml b/docker-compose.yml index c646dd6..7b02b6a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -84,20 +84,6 @@ services: - ${CLI_PATH:-.}:/code - ./logs/cli:/logs - testing: - profiles: - - testing - container_name: incoviba_tests - build: . - restart: unless-stopped - env_file: - - ${APP_PATH:-.}/.env - - ./.key.env - volumes: - - ${APP_PATH:-.}/:/code - - ./logs/test:/logs - command: [ '/code/bin/phpunit-watcher', 'watch' ] - volumes: dbdata: {} incoviba_redis: {} diff --git a/testing.compose.yml b/testing.compose.yml index a07fd12..23d24f1 100644 --- a/testing.compose.yml +++ b/testing.compose.yml @@ -2,34 +2,32 @@ services: testing: profiles: - testing - container_name: incoviba_tests build: . - restart: unless-stopped + container_name: incoviba_tests env_file: - ${APP_PATH:-.}/.test.env - ./.key.env volumes: - ${APP_PATH:-.}/:/code - - ./logs/test:/logs command: [ '/code/bin/phpunit-watcher', 'watch' ] + networks: + - testing depends_on: - test-db test-db: profiles: - testing - image: mysql:5.7 + image: mariadb:latest container_name: incoviba_test_db - restart: unless-stopped env_file: ${APP_PATH:-.}/.test.db.env volumes: - test-db:/var/lib/mysql networks: - - default - - adminer_network + - testing volumes: test-db: {} networks: - adminer_network: {} + testing: {}