diff --git a/src/Migrator.php b/src/Migrator.php index 9d20670..7a0e1d5 100644 --- a/src/Migrator.php +++ b/src/Migrator.php @@ -18,6 +18,8 @@ final class Migrator { + private const DB_DATE_FORMAT = 'Y-m-d H:i:s'; + /** @var MigrationConfig */ private $config; @@ -116,9 +118,10 @@ public function getMigrations(): array * Execute one migration and return it's instance. * * @param CapsuleInterface $capsule + * * @return null|MigrationInterface * - * @throws \Throwable + * @throws MigrationException */ public function run(CapsuleInterface $capsule = null): ?MigrationInterface { @@ -131,21 +134,37 @@ public function run(CapsuleInterface $capsule = null): ?MigrationInterface continue; } - $capsule = $capsule ?? new Capsule($this->dbal->database($migration->getDatabase())); - $capsule->getDatabase($migration->getDatabase())->transaction( - static function () use ($migration, $capsule): void { - $migration->withCapsule($capsule)->up(); - } - ); - - $this->migrationTable($migration->getDatabase())->insertOne( - [ - 'migration' => $migration->getState()->getName(), - 'time_executed' => new \DateTime('now') - ] - ); - - return $migration->withState($this->resolveState($migration)); + try { + $capsule = $capsule ?? new Capsule($this->dbal->database($migration->getDatabase())); + $capsule->getDatabase($migration->getDatabase())->transaction( + static function () use ($migration, $capsule): void { + $migration->withCapsule($capsule)->up(); + } + ); + + $this->migrationTable($migration->getDatabase())->insertOne( + [ + 'migration' => $migration->getState()->getName(), + 'time_executed' => new \DateTime('now') + ] + ); + + return $migration->withState($this->resolveState($migration)); + } catch (\Throwable $exception) { + throw new MigrationException( + \sprintf( + 'Error in the migration (%s) occurred: %s', + \sprintf( + '%s (%s)', + $migration->getState()->getName(), + $migration->getState()->getTimeCreated()->format(self::DB_DATE_FORMAT) + ), + $exception->getMessage() + ), + $exception->getCode(), + $exception + ); + } } return null; diff --git a/tests/Migrations/AtomizerTest.php b/tests/Migrations/AtomizerTest.php index 14e1b65..5f8bb59 100644 --- a/tests/Migrations/AtomizerTest.php +++ b/tests/Migrations/AtomizerTest.php @@ -303,9 +303,6 @@ public function testSetPrimaryKeys(): void $this->assertFalse($this->db->hasTable('sample')); } - /** - * @expectedException \Spiral\Migrations\Exception\Operation\TableException - */ public function testChangePrimaryKeys(): void { //Create thought migration @@ -327,6 +324,13 @@ public function testChangePrimaryKeys(): void $schema->setPrimaryKeys(['id']); $this->atomize('migration2', [$schema]); + + $this->expectException(\Spiral\Migrations\Exception\MigrationException::class); + $this->expectExceptionMessageMatches( + "/Error in the migration \([0-9a-z_\-]+ \(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\)\) occurred: " + . "Unable to set primary keys for table \'.+\'\.\'.+\', table already exists/" + ); + $this->migrator->run(); } diff --git a/tests/Migrations/ExceptionsTest.php b/tests/Migrations/ExceptionsTest.php index b1d068e..d8ae318 100644 --- a/tests/Migrations/ExceptionsTest.php +++ b/tests/Migrations/ExceptionsTest.php @@ -11,7 +11,6 @@ namespace Spiral\Migrations\Tests; -use DateTime; use Spiral\Migrations\Fixtures\AddForeignKeyMigration; use Spiral\Migrations\Fixtures\AlterForeignKeyMigration; use Spiral\Migrations\Fixtures\AlterNonExistedColumnMigration; @@ -24,36 +23,41 @@ use Spiral\Migrations\Fixtures\DuplicateColumnMigration; use Spiral\Migrations\Fixtures\RenameColumnMigration; use Spiral\Migrations\Fixtures\RenameTableMigration; +use Spiral\Migrations\Exception\MigrationException; abstract class ExceptionsTest extends BaseTest { - /** - * @expectedException \Spiral\Migrations\Exception\Operation\TableException - */ + private const MIGRATION_EXCEPTION_PREFIX_REGEX = "/Error in the migration \([0-9a-z_\-]+ \(\d{4}-\d{2}-\d{2} " + . "\d{2}:\d{2}:\d{2}\)\) occurred: "; + public function testDropNonExisted(): void { //Create thought migration $this->migrator->configure(); $this->repository->registerMigration('m', DropNonExistedMigration::class); + $this->expectException(MigrationException::class); + $this->expectExceptionMessageMatches( + self::MIGRATION_EXCEPTION_PREFIX_REGEX . "Unable to drop table \'.+\'\.\'.+\', table does not exists/" + ); + $this->migrator->run(); } - /** - * @expectedException \Spiral\Migrations\Exception\Operation\TableException - */ public function testCreateEmpty(): void { //Create thought migration $this->migrator->configure(); $this->repository->registerMigration('m', CreateEmptyMigration::class); + $this->expectException(MigrationException::class); + $this->expectExceptionMessageMatches( + self::MIGRATION_EXCEPTION_PREFIX_REGEX . "Unable to create table \'.+\'\.\'.+\', no columns were added/" + ); + $this->migrator->run(); } - /** - * @expectedException \Spiral\Migrations\Exception\Operation\TableException - */ public function testCreateDuplicate(): void { //Create thought migration @@ -64,36 +68,45 @@ public function testCreateDuplicate(): void $s->save(); $this->repository->registerMigration('m', CreateSampleMigration::class); + + $this->expectException(MigrationException::class); + $this->expectExceptionMessageMatches( + self::MIGRATION_EXCEPTION_PREFIX_REGEX . "Unable to create table '.+'\.'.+', table already exists/" + ); + $this->migrator->run(); } - /** - * @expectedException \Spiral\Migrations\Exception\Operation\TableException - */ public function testUpdateNonExisted(): void { //Create thought migration $this->migrator->configure(); $this->repository->registerMigration('m', DuplicateColumnMigration::class); + + $this->expectException(MigrationException::class); + $this->expectExceptionMessageMatches( + self::MIGRATION_EXCEPTION_PREFIX_REGEX . "Unable to update table '.+'\.'.+', no table exists/" + ); + $this->migrator->run(); } - /** - * @expectedException \Spiral\Migrations\Exception\Operation\TableException - */ public function testRenameNonExisted(): void { //Create thought migration $this->migrator->configure(); $this->repository->registerMigration('m', RenameTableMigration::class); + + $this->expectException(MigrationException::class); + $this->expectExceptionMessageMatches( + self::MIGRATION_EXCEPTION_PREFIX_REGEX . "Unable to rename table '.+'\.'.+', table does not exists/" + ); + $this->migrator->run(); } - /** - * @expectedException \Spiral\Migrations\Exception\Operation\TableException - */ public function testRenameButBusy(): void { //Create thought migration @@ -108,12 +121,15 @@ public function testRenameButBusy(): void $s->save(); $this->repository->registerMigration('m', RenameTableMigration::class); + + $this->expectException(MigrationException::class); + $this->expectExceptionMessageMatches( + self::MIGRATION_EXCEPTION_PREFIX_REGEX . "Unable to rename table '.+'\.'.+', table '.+' already exists/" + ); + $this->migrator->run(); } - /** - * @expectedException \Spiral\Migrations\Exception\Operation\ColumnException - */ public function testDuplicateColumn(): void { //Create thought migration @@ -125,12 +141,15 @@ public function testDuplicateColumn(): void $s->save(); $this->repository->registerMigration('m', DuplicateColumnMigration::class); + + $this->expectException(MigrationException::class); + $this->expectExceptionMessageMatches( + self::MIGRATION_EXCEPTION_PREFIX_REGEX . "Unable to create column '.+'\.'.+', column already exists/" + ); + $this->migrator->run(); } - /** - * @expectedException \Spiral\Migrations\Exception\Operation\IndexException - */ public function testDropNonExistedIndex(): void { //Create thought migration @@ -142,12 +161,15 @@ public function testDropNonExistedIndex(): void $s->save(); $this->repository->registerMigration('m', DropNonExistedIndexMigration::class); + + $this->expectException(MigrationException::class); + $this->expectExceptionMessageMatches( + self::MIGRATION_EXCEPTION_PREFIX_REGEX . "Unable to drop index '.+'\.(.+), index does not exists/" + ); + $this->migrator->run(); } - /** - * @expectedException \Spiral\Migrations\Exception\Operation\IndexException - */ public function testAlterNonExistedIndex(): void { //Create thought migration @@ -159,12 +181,15 @@ public function testAlterNonExistedIndex(): void $s->save(); $this->repository->registerMigration('m', AlterNonExistedIndexMigration::class); + + $this->expectException(MigrationException::class); + $this->expectExceptionMessageMatches( + self::MIGRATION_EXCEPTION_PREFIX_REGEX . "Unable to alter index '.+'\.(.+), no such index/" + ); + $this->migrator->run(); } - /** - * @expectedException \Spiral\Migrations\Exception\Operation\ColumnException - */ public function testAlterNonExistedColumn(): void { //Create thought migration @@ -175,12 +200,15 @@ public function testAlterNonExistedColumn(): void $s->save(); $this->repository->registerMigration('m', AlterNonExistedColumnMigration::class); + + $this->expectException(MigrationException::class); + $this->expectExceptionMessageMatches( + self::MIGRATION_EXCEPTION_PREFIX_REGEX . "Unable to alter column '.+'\.'.+', column does not exists/" + ); + $this->migrator->run(); } - /** - * @expectedException \Spiral\Migrations\Exception\Operation\ColumnException - */ public function testRenameNonExistedColumn(): void { //Create thought migration @@ -191,12 +219,15 @@ public function testRenameNonExistedColumn(): void $s->save(); $this->repository->registerMigration('m', RenameColumnMigration::class); + + $this->expectException(MigrationException::class); + $this->expectExceptionMessageMatches( + self::MIGRATION_EXCEPTION_PREFIX_REGEX . "Unable to rename column '.+'\.'.+', column does not exists/" + ); + $this->migrator->run(); } - /** - * @expectedException \Spiral\Migrations\Exception\Operation\ColumnException - */ public function testRenameDuplicateExistedColumn(): void { //Create thought migration @@ -209,12 +240,15 @@ public function testRenameDuplicateExistedColumn(): void $s->save(); $this->repository->registerMigration('m', RenameColumnMigration::class); + + $this->expectException(MigrationException::class); + $this->expectExceptionMessageMatches( + self::MIGRATION_EXCEPTION_PREFIX_REGEX . "Unable to rename column '.+'\.'.+', column '.+' already exists/" + ); + $this->migrator->run(); } - /** - * @expectedException \Spiral\Migrations\Exception\Operation\ForeignKeyException - */ public function testAddForeignNoTarget(): void { //Create thought migration @@ -226,12 +260,16 @@ public function testAddForeignNoTarget(): void $s->save(); $this->repository->registerMigration('m', AddForeignKeyMigration::class); + + $this->expectException(MigrationException::class); + $this->expectExceptionMessageMatches( + self::MIGRATION_EXCEPTION_PREFIX_REGEX + . "Unable to add foreign key 'tests_sample'.'column', foreign table 'target' does not exists/" + ); + $this->migrator->run(); } - /** - * @expectedException \Spiral\Migrations\Exception\Operation\ForeignKeyException - */ public function testAddForeignNoTargetColumn(): void { //Create thought migration @@ -247,12 +285,16 @@ public function testAddForeignNoTargetColumn(): void $s2->save(); $this->repository->registerMigration('m', AddForeignKeyMigration::class); + + $this->expectException(MigrationException::class); + $this->expectExceptionMessageMatches( + self::MIGRATION_EXCEPTION_PREFIX_REGEX + . "Unable to add foreign key '.+'\.'.+', foreign column '.+'\.'.+' does not exists/" + ); + $this->migrator->run(); } - /** - * @expectedException \Spiral\Migrations\Exception\Operation\ForeignKeyException - */ public function testAlterForeignNoFK(): void { //Create thought migration @@ -264,12 +306,15 @@ public function testAlterForeignNoFK(): void $s->save(); $this->repository->registerMigration('m', AlterForeignKeyMigration::class); + + $this->expectException(MigrationException::class); + $this->expectExceptionMessageMatches( + self::MIGRATION_EXCEPTION_PREFIX_REGEX . "Unable to alter foreign key '.+'\.(.+), key does not exists/" + ); + $this->migrator->run(); } - /** - * @expectedException \Spiral\Migrations\Exception\Operation\ForeignKeyException - */ public function testAlterForeignNoTable(): void { //Create thought migration @@ -286,12 +331,16 @@ public function testAlterForeignNoTable(): void $s->save(); $this->repository->registerMigration('m', AlterForeignKeyMigration::class); + + $this->expectException(MigrationException::class); + $this->expectExceptionMessageMatches( + self::MIGRATION_EXCEPTION_PREFIX_REGEX + . "Unable to alter foreign key '.+'\.'.+', foreign table '.+' does not exists/" + ); + $this->migrator->run(); } - /** - * @expectedException \Spiral\Migrations\Exception\Operation\ForeignKeyException - */ public function testAlterForeignNoColumn(): void { //Create thought migration @@ -312,12 +361,16 @@ public function testAlterForeignNoColumn(): void $s->save(); $this->repository->registerMigration('m', AlterForeignKeyMigration::class); + + $this->expectException(MigrationException::class); + $this->expectExceptionMessageMatches( + self::MIGRATION_EXCEPTION_PREFIX_REGEX + . "Unable to alter foreign key '.+'\.'.+', foreign column '.+'\.'.+' does not exists/" + ); + $this->migrator->run(); } - /** - * @expectedException \Spiral\Migrations\Exception\Operation\ForeignKeyException - */ public function testDropNonExistedFK(): void { //Create thought migration @@ -329,12 +382,16 @@ public function testDropNonExistedFK(): void $s->save(); $this->repository->registerMigration('m', DropForeignKeyMigration::class); + + $this->expectException(MigrationException::class); + $this->expectExceptionMessageMatches( + self::MIGRATION_EXCEPTION_PREFIX_REGEX + . "Unable to drop foreign key '.+'\.'.+', foreign key does not exists/" + ); + $this->migrator->run(); } - /** - * @expectedException \Spiral\Migrations\Exception\Operation\ForeignKeyException - */ public function testAddExisted(): void { //Create thought migration @@ -351,6 +408,12 @@ public function testAddExisted(): void $s->save(); $this->repository->registerMigration('m', AddForeignKeyMigration::class); + + $this->expectException(MigrationException::class); + $this->expectExceptionMessageMatches( + self::MIGRATION_EXCEPTION_PREFIX_REGEX . "Unable to add foreign key '.+'\.(.+), foreign key already exists/" + ); + $this->migrator->run(); } diff --git a/tests/Migrations/MigratorTest.php b/tests/Migrations/MigratorTest.php index bc0ae32..7f0bf82 100644 --- a/tests/Migrations/MigratorTest.php +++ b/tests/Migrations/MigratorTest.php @@ -13,6 +13,7 @@ use Spiral\Migrations\Capsule; use Spiral\Migrations\State; +use Spiral\Migrations\Exception\MigrationException; abstract class MigratorTest extends BaseTest { @@ -51,19 +52,19 @@ public function testConfig(): void $this->assertSame($this->migrationConfig, $this->migrator->getConfig()); } - /** - * @expectedException \Spiral\Migrations\Exception\MigrationException - */ public function testRunUnconfigured(): void { + $this->expectException(MigrationException::class); + $this->expectExceptionMessage("Unable to run migration, Migrator not configured"); + $this->migrator->run(); } - /** - * @expectedException \Spiral\Migrations\Exception\MigrationException - */ public function testRollbackUnconfigured(): void { + $this->expectException(MigrationException::class); + $this->expectExceptionMessage("Unable to run migration, Migrator not configured"); + $this->migrator->rollback(); } @@ -90,29 +91,29 @@ public function testCapsuleException(): void ]); } - /** - * @expectedException \Spiral\Migrations\Exception\MigrationException - */ public function testNoState(): void { + $this->expectException(MigrationException::class); + $this->expectExceptionMessage("Unable to get migration state, no state are set"); + $x = new TestMigration(); $x->up(); } - /** - * @expectedException \Spiral\Migrations\Exception\MigrationException - */ public function testNoCapsule(): void { + $this->expectException(MigrationException::class); + $this->expectExceptionMessage("Unable to get table blueprint, no capsule are set"); + $x = new TestMigration(); $x->getTable(); } - /** - * @expectedException \Spiral\Migrations\Exception\MigrationException - */ public function testNoCapsule2(): void { + $this->expectException(MigrationException::class); + $this->expectExceptionMessage("Unable to get database, no capsule are set"); + $x = new TestMigration(); $x->down(); }