diff --git a/composer.json b/composer.json
index c711a99..ce0c555 100644
--- a/composer.json
+++ b/composer.json
@@ -12,7 +12,8 @@
"php": "^7.4|^8.0"
},
"require-dev": {
- "orchestra/testbench": "6.x"
+ "orchestra/testbench": "6.x",
+ "doctrine/dbal": "^2.12.1"
},
"autoload": {
"psr-4": {
@@ -30,5 +31,13 @@
"A2Workspace\\DatabasePatcher\\ServiceProvider"
]
}
+ },
+ "scripts": {
+ "test": [
+ "vendor/bin/phpunit"
+ ],
+ "test-coverage": [
+ "vendor/bin/phpunit --coverage-html coverage"
+ ]
}
}
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index b9fa7c2..23acb3a 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -13,12 +13,14 @@
stopOnFailure="false"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
>
+
+
+ src
+
+
-
- ./tests/Unit
-
-
- ./tests/Feature
+
+ ./tests/
diff --git a/src/Commands/DbPatchCommand.php b/src/Commands/DbPatchCommand.php
index c9bd80b..c1f1af8 100644
--- a/src/Commands/DbPatchCommand.php
+++ b/src/Commands/DbPatchCommand.php
@@ -2,10 +2,9 @@
namespace A2Workspace\DatabasePatcher\Commands;
+use Illuminate\Support\Str;
use Illuminate\Console\Command;
use Illuminate\Support\Collection;
-use Illuminate\Support\Str;
-use Illuminate\Filesystem\Filesystem;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;
@@ -47,7 +46,7 @@ class DbPatchCommand extends Command
*
* @return int
*/
- public function handle()
+ public function handle(): int
{
$file = $this->determinePatchFile();
if (!$file) {
@@ -55,36 +54,24 @@ public function handle()
}
$path = $file->getRealPath();
- $path = Str::after($path, base_path());
-
- // 這邊我們判斷
- // 若為回復模式則呼叫滾回命令並傳入補丁檔案路徑
- if ($this->option('revert')) {
- $this->info("Running: php artisan migrate:rollback --path={$path}");
- $this->call('migrate:rollback', [
- '--path' => $path,
- ]);
+ if ($this->usingRevertion()) {
+ return $this->callMigrateCommand($path, 'migrate:rollback');
}
- // 呼叫遷移命令並傳入補丁檔案路徑
- else {
- $this->info("Running: php artisan migrate --path={$path}");
-
- $this->call('migrate', [
- '--path' => $path,
- ]);
- }
-
- return 0;
+ return $this->callMigrateCommand($path);
}
+ // =========================================================================
+ // = DeterminePatchFile()
+ // =========================================================================
+
/**
* 決定要被使用的檔案
*
* @return \Symfony\Component\Finder\SplFileInfo|null
*/
- protected function determinePatchFile()
+ protected function determinePatchFile(): ?SplFileInfo
{
// 取得 patches 目錄的檔案列表,若結果為空則提前終止
$files = $this->getFileList();
@@ -125,7 +112,7 @@ protected function getFilterInput()
*/
protected function getFileList(): Collection
{
- $paths = [database_path('patches')];
+ $paths = $this->laravel['config']['database.patcher.paths'] ?? database_path('patches');
return collect($paths)
->map(fn ($path) => $this->getFileListInDirectory($path))
@@ -144,6 +131,7 @@ protected function getFileListInDirectory(string $path): array
->filter(function (SplFileInfo $file) {
return !in_array($file->getRelativePathname(), $this->excludedNames);
})
+ ->ignoreDotFiles(true)
->files()
->in($path)
->depth(0)
@@ -178,4 +166,39 @@ protected function choiceFromFileList($question, Collection $files): SplFileInfo
return $value[0] === $input;
})[1];
}
+
+ // =========================================================================
+ // = UsingRevertion()
+ // =========================================================================
+
+ /**
+ * 判定是否為 revert 模式
+ *
+ * @return bool
+ */
+ protected function usingRevertion(): bool
+ {
+ return $this->input->hasOption('revert') && $this->option('revert');
+ }
+
+ // =========================================================================
+ // = CallMigrateCommand()
+ // =========================================================================
+
+ /**
+ * 呼叫運行 migrate 指令並傳入路徑
+ *
+ * @param string $path
+ * @param string $command
+ * @return int
+ */
+ protected function callMigrateCommand($path, string $command = 'migrate'): int
+ {
+ $path = Str::after($path, base_path());
+
+ $this->info("Running: php artisan {$command} --path={$path}");
+ $this->call($command, ['--path' => $path]);
+
+ return 0;
+ }
}
diff --git a/tests/CallDbPatchArtisanCommandTest.php b/tests/CallDbPatchArtisanCommandTest.php
new file mode 100644
index 0000000..e489b46
--- /dev/null
+++ b/tests/CallDbPatchArtisanCommandTest.php
@@ -0,0 +1,158 @@
+increments('id');
+ $table->string('ian')->unique();
+ $table->string('name');
+ });
+
+ if (empty(static::$published)) {
+ Artisan::call('vendor:publish', [
+ '--tag' => '@a2workspace/laravel-database-patcher',
+ ]);
+
+ static::$published = database_path('patches');
+ }
+ }
+
+ public static function tearDownAfterClass(): void
+ {
+ @unlink(static::$published);
+ @rmdir(static::$published);
+ }
+
+ // =========================================================================
+ // = Tests
+ // =========================================================================
+
+ public function test_call_artisan_command()
+ {
+ // =====================================================================
+ // = Step 1: Install specified patch file.
+ // =====================================================================
+
+ $command = $this->artisan('db:patch');
+
+ $command->expectsChoice(
+ '選擇補丁檔案',
+ $this->parseLabel('2022_07_19_000000_add_priority_to_products_table.php'),
+ [
+ $this->parseLabel('2022_07_19_000000_add_priority_to_products_table.php')
+ ],
+ );
+
+ $command->expectsOutput(sprintf(
+ 'Running: php artisan migrate --path=%s',
+ $this->resolvePath('/database/patches/2022_07_19_000000_add_priority_to_products_table.php')
+ ));
+
+ $command->assertExitCode(0);
+ $command->run();
+
+ $this->assertDatabaseHas(
+ 'migrations',
+ ['migration' => '2022_07_19_000000_add_priority_to_products_table']
+ );
+
+ $this->assertDatabaseTableHasColumn('products', 'priority');
+
+ // =====================================================================
+ // = Step 2: Revert it.
+ // =====================================================================
+
+ $command2 = $this->artisan('db:patch', ['--revert' => true]);
+
+ $command2->expectsChoice(
+ '選擇補丁檔案',
+ $this->parseLabel('2022_07_19_000000_add_priority_to_products_table.php'),
+ [
+ $this->parseLabel('2022_07_19_000000_add_priority_to_products_table.php')
+ ],
+ );
+
+ $command2->expectsOutput(sprintf(
+ 'Running: php artisan migrate:rollback --path=%s',
+ $this->resolvePath('/database/patches/2022_07_19_000000_add_priority_to_products_table.php')
+ ));
+
+ $command2->assertExitCode(0);
+ $command2->run();
+
+ $this->assertDatabaseTableMissingColumn('products', 'priority');
+
+ $this->assertDatabaseMissing(
+ 'migrations',
+ ['migration' => '2022_07_19_000000_add_priority_to_products_table']
+ );
+ }
+
+ public function test_call_artisan_command_with_filter()
+ {
+ $command = $this->artisan('db:patch', [
+ 'filter' => 'product',
+ ]);
+
+ $command->expectsChoice(
+ '選擇補丁檔案',
+ $this->parseLabel('2022_07_19_000000_add_priority_to_products_table.php'),
+ [
+ $this->parseLabel('2022_07_19_000000_add_priority_to_products_table.php')
+ ],
+ );
+
+ $command->expectsOutput(sprintf(
+ 'Running: php artisan migrate --path=%s',
+ $this->resolvePath('/database/patches/2022_07_19_000000_add_priority_to_products_table.php')
+ ));
+
+ $command->assertExitCode(0);
+ $command->run();
+
+ $this->assertDatabaseHas(
+ 'migrations',
+ ['migration' => '2022_07_19_000000_add_priority_to_products_table']
+ );
+
+ $this->assertDatabaseTableHasColumn('products', 'priority');
+ }
+
+ public function test_call_artisan_command_with_filter_then_not_found()
+ {
+ $command = $this->artisan('db:patch', [
+ 'filter' => '__IMIFUMEI__',
+ ]);
+
+ $command->expectsOutput('找不到符合的補丁檔案');
+ $command->assertExitCode(1);
+ $command->run();
+ }
+
+ public function test_call_artisan_command_when_directory_is_empty()
+ {
+ $this->app->config->set('database.patcher.paths', []);
+
+ $command = $this->artisan('db:patch');
+
+ $command->expectsOutput('找不到任何補丁檔案');
+ $command->assertExitCode(1);
+ $command->run();
+ }
+}
diff --git a/tests/TestArtisanCommandHelpers.php b/tests/TestArtisanCommandHelpers.php
new file mode 100644
index 0000000..342ceec
--- /dev/null
+++ b/tests/TestArtisanCommandHelpers.php
@@ -0,0 +1,55 @@
+ {$option}";
+ }
+
+ /**
+ * @param string $table
+ * @param string $column
+ * @return self
+ */
+ private function assertDatabaseTableHasColumn($table, $column)
+ {
+ $this->assertTrue(
+ Schema::hasColumn($table, $column),
+ sprintf(
+ 'The table [%s] doesn\'t have the column named %s',
+ $table,
+ $column
+ )
+ );
+
+ return $this;
+ }
+
+ /**
+ * @param string $table
+ * @param string $column
+ * @return self
+ */
+ private function assertDatabaseTableMissingColumn($table, $column)
+ {
+ $this->assertFalse(
+ Schema::hasColumn($table, $column),
+ sprintf(
+ 'The table [%s] have the column named %s',
+ $table,
+ $column
+ )
+ );
+
+ return $this;
+ }
+}
diff --git a/tests/TestCase.php b/tests/TestCase.php
new file mode 100644
index 0000000..5b4285e
--- /dev/null
+++ b/tests/TestCase.php
@@ -0,0 +1,51 @@
+$name(...func_get_args());
+ };
+
+ return $ref->bindTo($scope, $scope);
+ }
+
+ /**
+ * 處理不同作業系統下的路徑
+ *
+ * @param string $path
+ * @return string
+ */
+ protected function resolvePath(string $path): string
+ {
+ return str_replace('/', DIRECTORY_SEPARATOR, $path);
+ }
+}