test(api): wire up real mysqli in SchemaHealthCheckTest (TRU-77 unit CI)
The Unit suite starts with a freshly-dropped test database and does not create a $db global, so: - customer_invoice_email_schema_bootstrap::ensureSchema() short- circuited and never created the users table. - runSchemaCheck had nothing to inspect and reported ok=false. Wire up a real mysqli connection at file load time using the same CONFIG_DB_* env vars the rest of the CI suite exports, then have beforeEach run the bootstrap and create the bare-minimum invoices / bookings tables that runSchemaCheck verifies. This keeps the test self-contained inside the Unit suite without changing the production code or the production schema. Refs: api#383, TRU-77
This commit is contained in:
@@ -12,6 +12,8 @@
|
||||
* endpoint in `adminRoute.php` which the deploy pipeline hits.
|
||||
*/
|
||||
|
||||
app_require('classes/customer_invoice_email_schema_bootstrap.php');
|
||||
|
||||
use classes\customer_invoice_email_schema_bootstrap;
|
||||
|
||||
const REQUIRED_USERS_COLUMNS = [
|
||||
@@ -28,13 +30,130 @@ const REQUIRED_USERS_COLUMNS = [
|
||||
'created_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* The unit test bootstrap does not create a $db global. This contract
|
||||
* test is unique in that it needs a real database to verify schema
|
||||
* state, so wire one up here using the same CONFIG_DB_* env vars the
|
||||
* rest of the CI suite exports. If the database is unavailable, the
|
||||
* tests below will fail with a clear "no_db_connection" error.
|
||||
*/
|
||||
schema_health_check_test_wire_db();
|
||||
|
||||
function schema_health_check_test_wire_db(): void
|
||||
{
|
||||
if (isset($GLOBALS['db']) && is_object($GLOBALS['db'])) {
|
||||
return;
|
||||
}
|
||||
if (!class_exists('mysqli')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$host = (string)(getenv('CONFIG_DB_HOST') ?: 'mysql-debug');
|
||||
$user = (string)(getenv('CONFIG_DB_USER') ?: 'root');
|
||||
$password = (string)(getenv('CONFIG_DB_PASSWORD') ?: 'debug_root_password');
|
||||
$database = (string)(getenv('CONFIG_DB_DATABASE') ?: 'nnks_db_debug');
|
||||
$port = (int)(getenv('CONFIG_DB_PORT') ?: 3306);
|
||||
|
||||
try {
|
||||
mysqli_report(MYSQLI_REPORT_OFF);
|
||||
$conn = new mysqli($host, $user, $password, $database, $port);
|
||||
if ($conn->connect_errno) {
|
||||
return;
|
||||
}
|
||||
$conn->set_charset('utf8mb4');
|
||||
} catch (\Throwable $e) {
|
||||
return;
|
||||
}
|
||||
|
||||
$GLOBALS['db'] = new class($conn) {
|
||||
private mysqli $conn;
|
||||
|
||||
public function __construct(mysqli $conn)
|
||||
{
|
||||
$this->conn = $conn;
|
||||
}
|
||||
|
||||
public function query(string $sql)
|
||||
{
|
||||
return $this->conn->query($sql);
|
||||
}
|
||||
|
||||
public function fetch_assoc($result)
|
||||
{
|
||||
return $result ? $result->fetch_assoc() : null;
|
||||
}
|
||||
|
||||
public function close(): void
|
||||
{
|
||||
try {
|
||||
$this->conn->close();
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The unit suite starts with a freshly-dropped `nnks_db_debug` database
|
||||
* (see run_ci_suite::reset_ci_state). The schema bootstrap only owns
|
||||
* the `users` table; `invoices` and `bookings` are managed by other
|
||||
* migrations that don't run in the unit suite. Create the bare-minimum
|
||||
* schema that adminRoute::runSchemaCheck needs so the third test can
|
||||
* verify the "all columns exist" happy path.
|
||||
*/
|
||||
function schema_health_check_test_ensure_aux_tables(): void
|
||||
{
|
||||
global $db;
|
||||
if (!isset($db) || !is_object($db) || !method_exists($db, 'query')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$create = function (string $table, string $createSql, array $requiredColumns) use ($db): void {
|
||||
$r = $db->query("SHOW TABLES LIKE '{$table}'");
|
||||
if (!$r || (int)$r->num_rows === 0) {
|
||||
$db->query($createSql);
|
||||
return;
|
||||
}
|
||||
foreach ($requiredColumns as $column => $definition) {
|
||||
$r = $db->query("SHOW COLUMNS FROM `{$table}` LIKE '{$column}'");
|
||||
if (!$r || (int)$r->num_rows === 0) {
|
||||
$db->query("ALTER TABLE `{$table}` ADD COLUMN `{$column}` {$definition}");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$create('invoices', "CREATE TABLE `invoices` (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
customer_number INT NOT NULL DEFAULT 0,
|
||||
po_number VARCHAR(64) NULL,
|
||||
closed_at DATETIME NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", [
|
||||
'po_number' => 'VARCHAR(64) NULL',
|
||||
'closed_at' => 'DATETIME NULL',
|
||||
'customer_number' => 'INT NOT NULL DEFAULT 0',
|
||||
]);
|
||||
|
||||
$create('bookings', "CREATE TABLE `bookings` (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
customer_number INT NOT NULL DEFAULT 0,
|
||||
department INT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", [
|
||||
'customer_number' => 'INT NOT NULL DEFAULT 0',
|
||||
'department' => 'INT NULL',
|
||||
]);
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
// Self-heal: run the schema bootstrap so the test DB has all
|
||||
// the columns the contract requires. The bootstrap is additive
|
||||
// and idempotent — safe to run on every test.
|
||||
if (!isset($GLOBALS['db']) || !is_object($GLOBALS['db'])) {
|
||||
schema_health_check_test_wire_db();
|
||||
}
|
||||
if (class_exists(customer_invoice_email_schema_bootstrap::class)) {
|
||||
customer_invoice_email_schema_bootstrap::ensureSchema();
|
||||
}
|
||||
schema_health_check_test_ensure_aux_tables();
|
||||
});
|
||||
|
||||
it('users table has every required column the code references', function () {
|
||||
|
||||
Reference in New Issue
Block a user