|
11 | 11 | use PHPUnit\Framework\TestCase; |
12 | 12 |
|
13 | 13 | class DevMigratorTest extends TestCase { |
| 14 | + private function createProbe(Settings $settings, string $path):DevMigrator { |
| 15 | + return new class($settings, $path) extends DevMigrator { |
| 16 | + public function hasMigrationBeenAppliedPublic(string $fileName):bool { |
| 17 | + return $this->hasMigrationBeenApplied($fileName); |
| 18 | + } |
| 19 | + |
| 20 | + public function getStoredHashPublic(string $fileName):?string { |
| 21 | + return $this->getStoredHash($fileName); |
| 22 | + } |
| 23 | + |
| 24 | + public function recordMigrationSuccessPublic(string $fileName, string $hash):void { |
| 25 | + $this->recordMigrationSuccess($fileName, $hash); |
| 26 | + } |
| 27 | + |
| 28 | + public function getStoredProgressPublic(string $fileName):?array { |
| 29 | + return $this->getStoredProgress($fileName); |
| 30 | + } |
| 31 | + }; |
| 32 | + } |
| 33 | + |
14 | 34 | private function createProjectDir():string { |
15 | 35 | $root = Helper::getTmpDir(); |
16 | 36 | $project = implode(DIRECTORY_SEPARATOR, [$root, uniqid("dev-mig-")]); |
@@ -80,6 +100,68 @@ public function testDevMigrationIntegrityFailsWhenAppliedFileChanges():void { |
80 | 100 | $devMigrator->checkIntegrity($files); |
81 | 101 | } |
82 | 102 |
|
| 103 | + public function testProbeMethodsExposeStoredProgressForAppliedFile():void { |
| 104 | + $project = $this->createProjectDir(); |
| 105 | + $databasePath = $project . DIRECTORY_SEPARATOR . "dev.sqlite"; |
| 106 | + $settings = $this->createSettings($project, $databasePath); |
| 107 | + $devPath = $project . DIRECTORY_SEPARATOR . "query" . DIRECTORY_SEPARATOR . "_migration" . DIRECTORY_SEPARATOR . "dev"; |
| 108 | + $files = $this->createMigrationFiles($devPath, "feature", 1); |
| 109 | + |
| 110 | + $devMigrator = $this->createProbe($settings, $devPath); |
| 111 | + $devMigrator->createMigrationTable(); |
| 112 | + $devMigrator->performMigration($files); |
| 113 | + |
| 114 | + self::assertTrue($devMigrator->hasMigrationBeenAppliedPublic(basename($files[0]))); |
| 115 | + self::assertSame(md5_file($files[0]), $devMigrator->getStoredHashPublic(basename($files[0]))); |
| 116 | + } |
| 117 | + |
| 118 | + public function testLegacyDevMigrationRowReturnsNullLastStatement():void { |
| 119 | + $project = $this->createProjectDir(); |
| 120 | + $databasePath = $project . DIRECTORY_SEPARATOR . "dev.sqlite"; |
| 121 | + $settings = $this->createSettings($project, $databasePath); |
| 122 | + $devPath = $project . DIRECTORY_SEPARATOR . "query" . DIRECTORY_SEPARATOR . "_migration" . DIRECTORY_SEPARATOR . "dev"; |
| 123 | + mkdir($devPath, 0775, true); |
| 124 | + $file = $devPath . DIRECTORY_SEPARATOR . "001-feature.sql"; |
| 125 | + file_put_contents($file, "create table `test` (`id` int primary key)"); |
| 126 | + |
| 127 | + $db = new Database($settings); |
| 128 | + $db->executeSql(implode("\n", [ |
| 129 | + "create table `_migration_dev` (", |
| 130 | + "`fileName` varchar(255) primary key,", |
| 131 | + "`queryHash` varchar(32) not null,", |
| 132 | + "`migratedAt` datetime not null", |
| 133 | + ")", |
| 134 | + ])); |
| 135 | + $db->executeSql(implode("\n", [ |
| 136 | + "insert into `_migration_dev` (`fileName`, `queryHash`, `migratedAt`)", |
| 137 | + "values (?, ?, datetime('now'))", |
| 138 | + ]), [basename($file), md5_file($file)]); |
| 139 | + |
| 140 | + $devMigrator = $this->createProbe($settings, $devPath); |
| 141 | + $progress = $devMigrator->getStoredProgressPublic(basename($file)); |
| 142 | + |
| 143 | + self::assertSame(md5_file($file), $progress["hash"]); |
| 144 | + self::assertNull($progress["lastStatement"]); |
| 145 | + self::assertSame(0, $devMigrator->performMigration([$file])); |
| 146 | + } |
| 147 | + |
| 148 | + public function testRecordMigrationSuccessStoresCompleteDevMigration():void { |
| 149 | + $project = $this->createProjectDir(); |
| 150 | + $databasePath = $project . DIRECTORY_SEPARATOR . "dev.sqlite"; |
| 151 | + $settings = $this->createSettings($project, $databasePath); |
| 152 | + $devPath = $project . DIRECTORY_SEPARATOR . "query" . DIRECTORY_SEPARATOR . "_migration" . DIRECTORY_SEPARATOR . "dev"; |
| 153 | + mkdir($devPath, 0775, true); |
| 154 | + $fileName = "001-feature.sql"; |
| 155 | + |
| 156 | + $devMigrator = $this->createProbe($settings, $devPath); |
| 157 | + $devMigrator->createMigrationTable(); |
| 158 | + $devMigrator->recordMigrationSuccessPublic($fileName, "abc123"); |
| 159 | + |
| 160 | + $progress = $devMigrator->getStoredProgressPublic($fileName); |
| 161 | + self::assertSame("abc123", $progress["hash"]); |
| 162 | + self::assertNull($progress["lastStatement"]); |
| 163 | + } |
| 164 | + |
83 | 165 | public function testPerformDevMigrationRecordsCompletedStatementsAndResumes():void { |
84 | 166 | $project = $this->createProjectDir(); |
85 | 167 | $databasePath = $project . DIRECTORY_SEPARATOR . "dev.sqlite"; |
@@ -155,6 +237,37 @@ public function testDevMigrationIntegrityFailsWhenPartialFileChanges():void { |
155 | 237 | $devMigrator->checkIntegrity([$file]); |
156 | 238 | } |
157 | 239 |
|
| 240 | + public function testPerformDevMigrationThrowsWhenPartialFileChanges():void { |
| 241 | + $project = $this->createProjectDir(); |
| 242 | + $databasePath = $project . DIRECTORY_SEPARATOR . "dev.sqlite"; |
| 243 | + $settings = $this->createSettings($project, $databasePath); |
| 244 | + $devPath = $project . DIRECTORY_SEPARATOR . "query" . DIRECTORY_SEPARATOR . "_migration" . DIRECTORY_SEPARATOR . "dev"; |
| 245 | + mkdir($devPath, 0775, true); |
| 246 | + $file = $devPath . DIRECTORY_SEPARATOR . "001-feature.sql"; |
| 247 | + file_put_contents($file, implode(";\n", [ |
| 248 | + "create table `test` (`id` int primary key)", |
| 249 | + "insert into `helper` (`id`) values (1)", |
| 250 | + ]) . ";"); |
| 251 | + |
| 252 | + $devMigrator = new DevMigrator($settings, $devPath); |
| 253 | + $devMigrator->createMigrationTable(); |
| 254 | + |
| 255 | + try { |
| 256 | + $devMigrator->performMigration([$file]); |
| 257 | + self::fail("The dev migration should fail until the helper table exists."); |
| 258 | + } |
| 259 | + catch(\Gt\Database\DatabaseException) { |
| 260 | + } |
| 261 | + |
| 262 | + file_put_contents($file, implode(";\n", [ |
| 263 | + "create table `test` (`id` int primary key)", |
| 264 | + "insert into `helper` (`id`) values (2)", |
| 265 | + ]) . ";"); |
| 266 | + |
| 267 | + $this->expectException(MigrationIntegrityException::class); |
| 268 | + $devMigrator->performMigration([$file]); |
| 269 | + } |
| 270 | + |
158 | 271 | public function testMergeIntoMainMigrationDirectoryPromotesAppliedDevMigrations():void { |
159 | 272 | $project = $this->createProjectDir(); |
160 | 273 | $databasePath = $project . DIRECTORY_SEPARATOR . "dev.sqlite"; |
@@ -192,6 +305,76 @@ public function testMergeIntoMainMigrationDirectoryPromotesAppliedDevMigrations( |
192 | 305 | self::assertSame(3, $migrator->getMigrationCount()); |
193 | 306 | } |
194 | 307 |
|
| 308 | + public function testMergeIntoMainMigrationDirectoryCreatesTargetPathWhenMissing():void { |
| 309 | + $project = $this->createProjectDir(); |
| 310 | + $databasePath = $project . DIRECTORY_SEPARATOR . "dev.sqlite"; |
| 311 | + $settings = $this->createSettings($project, $databasePath); |
| 312 | + $mainPath = $project . DIRECTORY_SEPARATOR . "query" . DIRECTORY_SEPARATOR . "_migration"; |
| 313 | + $devPath = $project . DIRECTORY_SEPARATOR . "query" . DIRECTORY_SEPARATOR . "_dev-only"; |
| 314 | + mkdir($devPath, 0775, true); |
| 315 | + $devFile = $devPath . DIRECTORY_SEPARATOR . "001-feature-1.sql"; |
| 316 | + file_put_contents($devFile, "create table `test` (`id` int primary key)"); |
| 317 | + |
| 318 | + $migrator = new Migrator($settings, $mainPath); |
| 319 | + $migrator->createMigrationTable(); |
| 320 | + |
| 321 | + $devMigrator = new DevMigrator($settings, $devPath); |
| 322 | + $devMigrator->createMigrationTable(); |
| 323 | + $devMigrator->performMigration([$devFile]); |
| 324 | + |
| 325 | + self::assertSame(1, $devMigrator->mergeIntoMainMigrationDirectory($migrator, $mainPath)); |
| 326 | + self::assertFileExists($mainPath . DIRECTORY_SEPARATOR . "0001-feature-1.sql"); |
| 327 | + } |
| 328 | + |
| 329 | + public function testMergeIntoMainMigrationDirectoryThrowsWhenAppliedRecordIsMissing():void { |
| 330 | + $project = $this->createProjectDir(); |
| 331 | + $databasePath = $project . DIRECTORY_SEPARATOR . "dev.sqlite"; |
| 332 | + $settings = $this->createSettings($project, $databasePath); |
| 333 | + $mainPath = $project . DIRECTORY_SEPARATOR . "query" . DIRECTORY_SEPARATOR . "_migration"; |
| 334 | + $devPath = $mainPath . DIRECTORY_SEPARATOR . "dev"; |
| 335 | + mkdir($devPath, 0775, true); |
| 336 | + $devFile = $devPath . DIRECTORY_SEPARATOR . "001-feature-1.sql"; |
| 337 | + file_put_contents($devFile, "create table `test` (`id` int primary key)"); |
| 338 | + |
| 339 | + $migrator = new Migrator($settings, $mainPath); |
| 340 | + $migrator->createMigrationTable(); |
| 341 | + |
| 342 | + $devMigrator = new DevMigrator($settings, $devPath); |
| 343 | + $devMigrator->createMigrationTable(); |
| 344 | + |
| 345 | + $this->expectException(MigrationIntegrityException::class); |
| 346 | + $devMigrator->mergeIntoMainMigrationDirectory($migrator, $mainPath); |
| 347 | + } |
| 348 | + |
| 349 | + public function testMergeIntoMainMigrationDirectoryThrowsWhenTargetFileExists():void { |
| 350 | + $project = $this->createProjectDir(); |
| 351 | + $databasePath = $project . DIRECTORY_SEPARATOR . "dev.sqlite"; |
| 352 | + $settings = $this->createSettings($project, $databasePath); |
| 353 | + $mainPath = $project . DIRECTORY_SEPARATOR . "query" . DIRECTORY_SEPARATOR . "_migration"; |
| 354 | + $devPath = $mainPath . DIRECTORY_SEPARATOR . "dev"; |
| 355 | + $targetPath = $project . DIRECTORY_SEPARATOR . "query" . DIRECTORY_SEPARATOR . "_merge-target"; |
| 356 | + |
| 357 | + mkdir($mainPath, 0775, true); |
| 358 | + mkdir($devPath, 0775, true); |
| 359 | + file_put_contents($mainPath . DIRECTORY_SEPARATOR . "0001-existing.sql", "create table `test` (`id` int primary key)"); |
| 360 | + mkdir($targetPath, 0775, true); |
| 361 | + file_put_contents($targetPath . DIRECTORY_SEPARATOR . "0004-feature-1.sql", "select 1"); |
| 362 | + $devFile = $devPath . DIRECTORY_SEPARATOR . "001-feature-1.sql"; |
| 363 | + file_put_contents($devFile, "alter table `test` add `feature_1` text"); |
| 364 | + |
| 365 | + $migrator = new Migrator($settings, $mainPath); |
| 366 | + $migrator->createMigrationTable(); |
| 367 | + $migrator->performMigration([$mainPath . DIRECTORY_SEPARATOR . "0001-existing.sql"]); |
| 368 | + $migrator->resetMigrationSequence(3); |
| 369 | + |
| 370 | + $devMigrator = new DevMigrator($settings, $devPath); |
| 371 | + $devMigrator->createMigrationTable(); |
| 372 | + $devMigrator->performMigration([$devFile]); |
| 373 | + |
| 374 | + $this->expectException(MigrationSequenceOrderException::class); |
| 375 | + $devMigrator->mergeIntoMainMigrationDirectory($migrator, $targetPath); |
| 376 | + } |
| 377 | + |
195 | 378 | public function testDevMigratorThrowsOnGapsInSequence():void { |
196 | 379 | $project = $this->createProjectDir(); |
197 | 380 | $databasePath = $project . DIRECTORY_SEPARATOR . "dev.sqlite"; |
|
0 commit comments