feat(test): add multi-engine integration matrix

This commit is contained in:
kj
2026-09-05 16:15:28 -03:00
parent 48d3ed6b3f
commit d0c4b3c503
15 changed files with 1278 additions and 38 deletions

View File

@@ -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.
*/