Quelltext des aktuellen Standes
<?php
declare(strict_types=1);
use OpenNIT\Core\SchemaInspector;
/**
* Prüft den (DB-freien) Schema-Parser des SchemaInspector gegen die echten
* Migrationsdateien. Fängt u. a. die „ticket_email_templates.body"-Klasse ab
* (TEXT-Spalte, die von außen verkleinert zum Migrationsabbruch führt).
*/
return static function (): void {
$migrationsDir = dirname(__DIR__) . '/database/migrations';
// Ohne DB konstruieren (inspect() braucht die DB nicht – expectedSchema() liest nur Dateien)
$ref = new ReflectionClass(SchemaInspector::class);
$insp = $ref->newInstanceWithoutConstructor();
$prop = $ref->getProperty('migrationsDir');
$prop->setAccessible(true);
$prop->setValue($insp, $migrationsDir);
$method = $ref->getMethod('expectedSchema');
$method->setAccessible(true);
/** @var array{tables:string[],wide_columns:array<string,array{type:string,cap:int}>} $exp */
$exp = $method->invoke($insp);
test('expectedSchema: erkennt viele Tabellen', function () use ($exp) {
assertTrue(count($exp['tables']) > 100, 'Es sollten >100 Tabellen erkannt werden, erkannt: ' . count($exp['tables']));
});
test('expectedSchema: ticket_email_templates ist bekannt', function () use ($exp) {
assertTrue(in_array('ticket_email_templates', $exp['tables'], true), 'ticket_email_templates fehlt in der Tabellenliste');
});
test('expectedSchema: ticket_email_templates.body = TEXT (Incident-Spalte)', function () use ($exp) {
$col = $exp['wide_columns']['ticket_email_templates.body'] ?? null;
assertTrue($col !== null, 'body-Spalte wird nicht überwacht');
assertSame('text', $col['type']);
assertSame(65535, $col['cap']);
});
test('expectedSchema: ALTER MODIFY wird berücksichtigt (ticket_replies.body = LONGTEXT)', function () use ($exp) {
$col = $exp['wide_columns']['ticket_replies.body'] ?? null;
assertTrue($col !== null, 'ticket_replies.body wird nicht überwacht');
assertSame('longtext', $col['type'], 'Die spätere ALTER … MODIFY LONGTEXT muss gewinnen');
});
test('expectedSchema: BLOB-Spalten werden überwacht (vault_entry_files.data_enc = MEDIUMBLOB)', function () use ($exp) {
$col = $exp['wide_columns']['vault_entry_files.data_enc'] ?? null;
assertTrue($col !== null, 'data_enc wird nicht überwacht');
assertSame('mediumblob', $col['type']);
});
test('expectedSchema: per DROP TABLE entfernte Tabelle gilt NICHT als erwartet (ai_context_keywords)', function () use ($exp) {
assertFalse(in_array('ai_context_keywords', $exp['tables'], true),
'ai_context_keywords wird in 001 erstellt und später gedroppt – darf nicht als erwartet/fehlend gelten');
});
test('expectedSchema: schmale Spalten werden NICHT überwacht (kein VARCHAR(50))', function () use ($exp) {
foreach ($exp['wide_columns'] as $key => $col) {
assertTrue($col['cap'] >= 65535, "Nur TEXT/BLOB+ überwachen, aber {$key} hat cap={$col['cap']}");
}
});
};