feat(test): add multi-engine integration matrix
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
namespace Tests;
|
||||
|
||||
use Libs\Database;
|
||||
use Libs\Model;
|
||||
use PDO;
|
||||
use PHPUnit\Framework\TestCase as FrameworkTestCase;
|
||||
|
||||
@@ -21,6 +22,21 @@ abstract class TestCase extends FrameworkTestCase
|
||||
*/
|
||||
private static array $tables = [];
|
||||
|
||||
/**
|
||||
* Clears Model's static query-builder state before every test.
|
||||
*
|
||||
* A query that dies mid-flight (unsupported function, constraint
|
||||
* violation, ...) never reaches Model's internal resetQuery(), so
|
||||
* leftovers would leak into the next test otherwise. This keeps the
|
||||
* suite order-independent regardless of PHPUnit's executionOrder.
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
(new \ReflectionMethod(Model::class, 'resetQuery'))->invoke(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the PDO connection to the in-memory test database.
|
||||
*
|
||||
@@ -32,21 +48,80 @@ abstract class TestCase extends FrameworkTestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a table in the test database and registers it for cleanup.
|
||||
* DDL fragments per engine for each logical column type.
|
||||
*
|
||||
* @var array<string,array<string,string>>
|
||||
*/
|
||||
private const TYPE_DDL = [
|
||||
'sqlite' => [
|
||||
'pk' => 'INTEGER PRIMARY KEY AUTOINCREMENT',
|
||||
'string' => 'VARCHAR(255)',
|
||||
'text' => 'TEXT',
|
||||
'number' => 'REAL',
|
||||
'bool' => 'INTEGER',
|
||||
'timestamp' => 'TEXT',
|
||||
],
|
||||
'mysql' => [
|
||||
'pk' => 'INT AUTO_INCREMENT PRIMARY KEY',
|
||||
'string' => 'VARCHAR(255)',
|
||||
'text' => 'TEXT',
|
||||
'number' => 'DOUBLE',
|
||||
'bool' => 'TINYINT(1)',
|
||||
'timestamp' => 'DATETIME',
|
||||
],
|
||||
'pgsql' => [
|
||||
'pk' => 'SERIAL PRIMARY KEY',
|
||||
'string' => 'VARCHAR(255)',
|
||||
'text' => 'TEXT',
|
||||
'number' => 'DOUBLE PRECISION',
|
||||
'bool' => 'BOOLEAN',
|
||||
'timestamp' => 'TIMESTAMP',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Creates a table in the test database from logical column types and
|
||||
* registers it for cleanup. The DDL is translated per engine, so the
|
||||
* same declaration works on sqlite, MySQL/MariaDB and PostgreSQL.
|
||||
*
|
||||
* @param string $name
|
||||
* Table name.
|
||||
* Table name (avoid SQL reserved words).
|
||||
*
|
||||
* @param string $columns
|
||||
* Raw column definition, as accepted by the test
|
||||
* engine (sqlite): "id INTEGER PRIMARY KEY, x TEXT".
|
||||
* @param array<string,string> $columns
|
||||
* Map of column name to logical type:
|
||||
* pk|string|text|number|bool|timestamp.
|
||||
*/
|
||||
protected static function createTable(string $name, string $columns): void
|
||||
protected static function createTable(string $name, array $columns): void
|
||||
{
|
||||
static::db()->exec("CREATE TABLE IF NOT EXISTS {$name} ({$columns})");
|
||||
$types = self::TYPE_DDL[DB_TYPE] ?? throw new \InvalidArgumentException(
|
||||
'No type translation for DB_TYPE ' . DB_TYPE
|
||||
);
|
||||
|
||||
$ddl = [];
|
||||
foreach ($columns as $column => $type) {
|
||||
$fragment = $types[$type] ?? throw new \InvalidArgumentException(
|
||||
"Unknown logical type '{$type}' for column '{$column}'; "
|
||||
. 'expected: ' . implode(', ', array_keys($types))
|
||||
);
|
||||
$ddl[] = $column . ' ' . $fragment;
|
||||
}
|
||||
|
||||
static::db()->exec("CREATE TABLE IF NOT EXISTS {$name} (" . implode(', ', $ddl) . ')');
|
||||
static::$tables[] = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Never let an aborted test leak an open transaction into the next one.
|
||||
*/
|
||||
protected function tearDown(): void
|
||||
{
|
||||
if (static::db()->inTransaction()) {
|
||||
static::db()->rollBack();
|
||||
}
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops every table registered through createTable() during this class.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user