Browse Source

Add a schema loader for CakePHP core tests

I was planning on using phinx and a single phinx migration for this,
however that is not going to work. Phinx depends on cakephp/database
which cannot be installed as it would conflict with the root package.

Because of that limitation I've added a way to use the Database
package's schema generation features more directly. Retaining this long
term will term will require having some schema generation capabilities
in CakePHP. Those features could be extracted out into an internal package
for 5.x.
Mark Story 4 years ago
parent
commit
f317944eb7

+ 93 - 0
src/TestSuite/Schema/SchemaGenerator.php

@@ -0,0 +1,93 @@
+<?php
+declare(strict_types=1);
+
+/**
+ * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the LICENSE.txt
+ * Redistributions of files must retain the above copyright notice
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ * @since         4.3.0
+ * @license       https://opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\TestSuite\Schema;
+
+use Cake\Database\Schema\TableSchema;
+use Cake\Datasource\ConnectionManager;
+use RuntimeException;
+
+/**
+ * Create database schema from the provided metadata file.
+ *
+ * @internal
+ */
+class SchemaGenerator
+{
+    /**
+     * The metadata file to load.
+     *
+     * @var string
+     */
+    protected $file;
+
+    /**
+     * @var string
+     */
+    protected $connection;
+
+    /**
+     * Constructor
+     *
+     * @param string $file The file to load
+     * @param string $connection The connection to use.
+     * @return void
+     */
+    public function __construct(string $file, string $connection)
+    {
+        $this->file = $file;
+        $this->connection = $connection;
+    }
+
+    /**
+     * Reload the schema.
+     *
+     * Will drop all tables and re-create them from the metadata file.
+     *
+     * @param ?string[] $tables The list of tables to reset. Primarily for testing.
+     * @return void
+     */
+    public function reload(?array $tables = null): void
+    {
+        if (!file_exists($this->file)) {
+            throw new RuntimeException("Cannot load `{$this->file}`");
+        }
+
+        $cleaner = new SchemaCleaner();
+        $cleaner->dropTables($this->connection, $tables);
+
+        $config = include $this->file;
+        $connection = ConnectionManager::get($this->connection);
+
+        foreach ($config as $metadata) {
+            $table = new TableSchema($metadata['table'], $metadata['columns']);
+            if (isset($metadata['indexes'])) {
+                foreach ($metadata['indexes'] as $key => $index) {
+                    $table->addIndex($key, $index);
+                }
+            }
+            if (isset($metadata['constraints'])) {
+                foreach ($metadata['constraints'] as $key => $index) {
+                    $table->addConstraint($key, $index);
+                }
+            }
+            // Generate SQL for each table.
+            $stmts = $table->createSql($connection);
+            foreach ($stmts as $stmt) {
+                $connection->execute($stmt);
+            }
+        }
+    }
+}

+ 54 - 0
tests/TestCase/TestSuite/Schema/SchemaGeneratorTest.php

@@ -0,0 +1,54 @@
+<?php
+declare(strict_types=1);
+
+/**
+ * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the LICENSE.txt
+ * Redistributions of files must retain the above copyright notice
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ * @since         4.3.0
+ * @license       https://opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\Test\TestCase\TestSuite\Schema;
+
+use Cake\Datasource\ConnectionManager;
+use Cake\TestSuite\Schema\SchemaGenerator;
+use Cake\TestSuite\TestCase;
+
+/**
+ * SchemaGenerator Test
+ */
+class SchemaGeneratorTest extends TestCase
+{
+    /**
+     * test reload on a table subset.
+     *
+     * @return void
+     */
+    public function testReload()
+    {
+        $generator = new SchemaGenerator(__DIR__ . '/test_schema.php', 'test');
+
+        // only drop tables we'll create again.
+        $tables = ['schema_generator', 'schema_generator_comment'];
+        $generator->reload($tables);
+
+        $connection = ConnectionManager::get('test');
+        $schema = $connection->getSchemaCollection();
+
+        $tables = $schema->listTables();
+        $this->assertContains('schema_generator', $tables);
+        $this->assertContains('schema_generator_comment', $tables);
+
+        foreach ($tables as $table) {
+            $meta = $schema->describe($table);
+            foreach ($meta->dropSql($connection) as $stmt) {
+                $connection->execute($stmt);
+            }
+        }
+    }
+}

+ 35 - 0
tests/TestCase/TestSuite/Schema/test_schema.php

@@ -0,0 +1,35 @@
+<?php
+declare(strict_types=1);
+
+return [
+    [
+        'table' => 'schema_generator',
+        'columns' => [
+            'id' => ['type' => 'integer'],
+            'relation_id' => ['type' => 'integer', 'null' => true],
+            'title' => ['type' => 'string', 'null' => true],
+            'body' => 'text',
+        ],
+        'constraints' => [
+            'primary' => ['type' => 'primary', 'columns' => ['id']],
+            'relation_idx' => [
+                'type' => 'foreign',
+                'columns' => ['relation_id'],
+                'references' => ['schema_generator_comment', 'id'],
+            ],
+        ],
+        'indexes' => [
+            'title_idx' => [
+                'type' => 'index',
+                'columns' => ['title'],
+            ],
+        ],
+    ],
+    [
+        'table' => 'schema_generator_comment',
+        'columns' => [
+            'id' => ['type' => 'integer'],
+            'title' => ['type' => 'string', 'null' => true],
+        ],
+    ],
+];

+ 5 - 0
tests/bootstrap.php

@@ -19,6 +19,7 @@ use Cake\Core\Configure;
 use Cake\Datasource\ConnectionManager;
 use Cake\Error\Debug\TextFormatter;
 use Cake\Log\Log;
+use Cake\TestSuite\Schema\SchemaGenerator;
 use Cake\Utility\Security;
 
 if (is_file('vendor/autoload.php')) {
@@ -139,3 +140,7 @@ ini_set('session.gc_divisor', '1');
 // does not allow the sessionid to be set after stdout
 // has been written to.
 session_id('cli');
+
+// Create test database schema.
+$schema = new SchemaGenerator(CORE_TESTS . 'schema.php', 'test');
+$schema->reload();

+ 12 - 0
tests/schema.php

@@ -0,0 +1,12 @@
+<?php
+declare(strict_types=1);
+
+/**
+ * Abstract schema for CakePHP tests.
+ *
+ * This format resembles the existing fixture schema
+ * and is converted to SQL via the Schema generation
+ * features of the Database package.
+ */
+return [
+];