mirror of
https://github.com/Vateron-Media/XC_VM.git
synced 2026-11-05 03:03:31 +01:00
- Tables a core migration creates (category_templates, ...) and MigrationRunner's own log were taken for module tables: only database.sql was read. coreSchemaTables() reads the up-migrations and counts the log too. - A module table that is the parent of a foreign key (telegram_bots) could not be dropped on its own: the drops run with foreign-key checks off, and a saved table loses its foreign keys, which name the backup's other tables. - No options file (migrate run by hand) and a lines table without pid no longer warn.
119 lines
5.6 KiB
PHP
119 lines
5.6 KiB
PHP
<?php
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
use XcVm\Cli\Commands\MigrateCommand;
|
|
use XcVm\Core\Events\Migration\LegacyTableMigrationEvent;
|
|
|
|
/**
|
|
* Backup tables the core does not own (MigrateCommand::moduleTables(), no list
|
|
* of modules): the migration saves each to a file of its own, from which its
|
|
* module copies it, at the migration or when it is installed later.
|
|
* The file is SQL: the table's own definition (as `legacy_<table>`) and its
|
|
* rows as INSERTs, loaded into that staging table for a later module.
|
|
*/
|
|
final class MigrateModuleTablesTest extends TestCase {
|
|
private string $rDir;
|
|
|
|
protected function setUp(): void {
|
|
$this->rDir = sys_get_temp_dir() . '/xcvm-module-tables-' . bin2hex(random_bytes(4)) . '/';
|
|
LegacyTableMigrationEvent::useDir($this->rDir);
|
|
}
|
|
|
|
protected function tearDown(): void {
|
|
LegacyTableMigrationEvent::useDir(null);
|
|
foreach (glob($this->rDir . '*') ?: [] as $rFile) {
|
|
unlink($rFile);
|
|
}
|
|
@rmdir($this->rDir);
|
|
}
|
|
|
|
public function testTablesTheCoreDoesNotOwnAreSavedForModulesWhateverTheirNames(): void {
|
|
$rBackup = ['streams', 'lines', 'lines_activity', 'user_activity', 'reg_users', 'category_templates', 'migrations', 'watch_folders', 'watch_logs', 'acme_addon_items'];
|
|
|
|
$rSaved = MigrateCommand::moduleTables($rBackup, MigrateCommand::coreSchemaTables());
|
|
|
|
$this->assertSame(['watch_folders', 'watch_logs', 'acme_addon_items'], $rSaved, 'migrated (streams, reg_users), core schema (lines_activity, category_templates, migrations) and junk (user_activity) stay out');
|
|
}
|
|
|
|
public function testTheCoreSchemaIsReadFromDatabaseSql(): void {
|
|
$rTables = MigrateCommand::coreSchemaTables();
|
|
|
|
$this->assertContains('streams', $rTables);
|
|
$this->assertContains('lines_activity', $rTables);
|
|
$this->assertNotContains('watch_folders', $rTables, 'a module table is not the core\'s');
|
|
$this->assertContains('category_templates', $rTables, 'created by a core migration, not by database.sql');
|
|
$this->assertContains('migrations', $rTables, "MigrationRunner's own log");
|
|
}
|
|
|
|
public function testATableIsSavedAsSqlAndLoadedBackForAModuleInstalledLater(): void {
|
|
$rDb = new TestDb();
|
|
$rDb->exec('CREATE TABLE `watch_folders` (`id` int PRIMARY KEY, `directory` varchar(255), `bouquets` varchar(255) NULL);');
|
|
$rDb->exec("INSERT INTO `watch_folders` VALUES (1, 'it''s a \"dir\"\nwith ? and \\\\ in it', NULL);");
|
|
$rValues = [];
|
|
foreach (range(2, 2500) as $i) {
|
|
$rValues[] = '(' . $i . ", '/media/Фильмы " . $i . "', '[\"" . $i . "\"]')";
|
|
}
|
|
$rDb->exec('INSERT INTO `watch_folders` VALUES ' . implode(',', $rValues) . ';');
|
|
|
|
$rNow = LegacyTableMigrationEvent::dump($rDb, 'watch_folders', 'xc');
|
|
$this->assertCount(2500, iterator_to_array($rNow->rows(), false), 'at the migration: read from the backup table, in chunks');
|
|
$this->assertFileExists(LegacyTableMigrationEvent::sqlFile('watch_folders'));
|
|
|
|
$rDb->exec('DROP TABLE `watch_folders`;'); // the migration drops it from the backup
|
|
$rLater = LegacyTableMigrationEvent::fromBackup('watch_folders', $rDb);
|
|
$this->assertSame('xc', $rLater->format);
|
|
$rRows = iterator_to_array($rLater->rows(), false);
|
|
$this->assertCount(2500, $rRows);
|
|
$this->assertEquals(['id' => 1, 'directory' => "it's a \"dir\"\nwith ? and \\ in it", 'bouquets' => null], $rRows[0]);
|
|
$this->assertEquals(['id' => 2500, 'directory' => '/media/Фильмы 2500', 'bouquets' => '["2500"]'], $rRows[2499]);
|
|
|
|
$rLater->discard();
|
|
$this->assertFileDoesNotExist(LegacyTableMigrationEvent::sqlFile('watch_folders'));
|
|
$rDb->query("SHOW TABLES LIKE 'legacy_watch_folders';");
|
|
$this->assertSame(0, $rDb->num_rows(), 'the staging table goes too');
|
|
$this->assertNull(LegacyTableMigrationEvent::fromBackup('watch_folders', $rDb));
|
|
}
|
|
|
|
public function testASavedTableLosesItsForeignKeysAndLoadsWithoutItsParent(): void {
|
|
$rDb = new TestDb();
|
|
$rDb->exec('CREATE TABLE `acme_bots` (`id` int PRIMARY KEY) ENGINE=InnoDB;');
|
|
$rDb->exec('CREATE TABLE `acme_chats` (`id` int PRIMARY KEY, `bot_id` int, CONSTRAINT `fk_acme_bot` FOREIGN KEY (`bot_id`) REFERENCES `acme_bots` (`id`) ON DELETE CASCADE) ENGINE=InnoDB;');
|
|
$rDb->query('INSERT INTO `acme_bots` VALUES (7);');
|
|
$rDb->query('INSERT INTO `acme_chats` VALUES (1, 7);');
|
|
|
|
LegacyTableMigrationEvent::dump($rDb, 'acme_chats', 'xui');
|
|
$this->assertStringNotContainsString('FOREIGN KEY', (string) file_get_contents(LegacyTableMigrationEvent::sqlFile('acme_chats')));
|
|
|
|
$rDb->exec('SET FOREIGN_KEY_CHECKS = 0;');
|
|
$rDb->exec('DROP TABLE `acme_bots`;');
|
|
$rDb->exec('DROP TABLE `acme_chats`;');
|
|
$rDb->exec('SET FOREIGN_KEY_CHECKS = 1;');
|
|
$rLater = LegacyTableMigrationEvent::fromBackup('acme_chats', $rDb);
|
|
$this->assertEquals([['id' => 1, 'bot_id' => 7]], iterator_to_array($rLater->rows(), false));
|
|
$rLater->discard();
|
|
}
|
|
|
|
public function testAnEmptyOrAbsentTableSavesNothing(): void {
|
|
$rDb = new TestDb();
|
|
$this->assertNull(LegacyTableMigrationEvent::dump($rDb, 'watch_folders', 'xui'));
|
|
$rDb->exec('CREATE TABLE `watch_folders` (`id` int PRIMARY KEY);');
|
|
$this->assertNull(LegacyTableMigrationEvent::dump($rDb, 'watch_folders', 'xui'));
|
|
$this->assertNull(LegacyTableMigrationEvent::fromBackup('watch_folders', $rDb));
|
|
}
|
|
|
|
public function testATableThatCannotBeSavedSaysSo(): void {
|
|
$rDb = new TestDb();
|
|
$rDb->exec('CREATE TABLE `watch_folders` (`id` int PRIMARY KEY);');
|
|
$rDb->query('INSERT INTO `watch_folders` VALUES (1);');
|
|
touch(rtrim($this->rDir, '/')); // a file where the directory should be
|
|
LegacyTableMigrationEvent::useDir($this->rDir . 'sub/');
|
|
|
|
$this->expectException(RuntimeException::class);
|
|
try {
|
|
LegacyTableMigrationEvent::dump($rDb, 'watch_folders', 'xui');
|
|
} finally {
|
|
unlink(rtrim($this->rDir, '/'));
|
|
}
|
|
}
|
|
}
|