diff --git a/.github/workflows/tests-for-databases.yml b/.github/workflows/tests-for-databases.yml index 3827931..c57bea5 100644 --- a/.github/workflows/tests-for-databases.yml +++ b/.github/workflows/tests-for-databases.yml @@ -204,9 +204,6 @@ jobs: ports: - 1433:1433 - strategy: - fail-fast: true - name: SQL Server 2019 steps: @@ -227,7 +224,7 @@ jobs: curl https://packages.microsoft.com/config/ubuntu/22.04/prod.list | sudo tee /etc/apt/sources.list.d/msprod.list sudo apt-get update sudo ACCEPT_EULA=Y apt-get install -y msodbcsql18 mssql-tools18 unixodbc-dev - echo 'export PATH="$PATH:/opt/mssql-tools18/bin"' >> $GITHUB_ENV + echo "/opt/mssql-tools18/bin" >> $GITHUB_PATH - name: Set Framework version run: composer config version "12.x-dev" @@ -239,7 +236,7 @@ jobs: run: | echo "Waiting for SQL Server to start..." for i in {1..30}; do - /opt/mssql-tools18/bin/sqlcmd -S localhost -U SA -P Forge123 -Q "SELECT 1" && break + sqlcmd -S localhost -U SA -P Forge123 -Q "SELECT 1" -C && break echo "SQL Server is starting up..." sleep 2 done diff --git a/CHANGELOG.md b/CHANGELOG.md index 86c27f2..acbe8e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,34 @@ All notable changes to `model-required-fields` will be documented in this file. +## 3.1.5 - 2025-09-20 + +### What's Changed + +* refactor tests and cache name + +**Full Changelog**: https://github.com/WatheqAlshowaiter/model-fields/compare/3.1.4...3.1.5 + +## 3.1.4 - 2025-09-20 + +### What's Changed + +* chore: fix SQL server cert issue in CI + +**Full Changelog**: https://github.com/WatheqAlshowaiter/model-fields/compare/3.1.3...3.1.4 + +## 3.1.3 - 2025-09-20 + +Improve the test + +**Full Changelog**: https://github.com/WatheqAlshowaiter/model-fields/compare/3.1.2...3.1.3 + +## 3.1.2 - 2025-09-20 + +* Add more tests + +**Full Changelog**: https://github.com/WatheqAlshowaiter/model-fields/compare/3.1.1...3.1.2 + ## 3.1.1 - 2025-09-20 ### What's Changed diff --git a/src/Console/ModelFieldsCommand.php b/src/Console/ModelFieldsCommand.php index b8da93d..98f2a81 100644 --- a/src/Console/ModelFieldsCommand.php +++ b/src/Console/ModelFieldsCommand.php @@ -8,6 +8,8 @@ class ModelFieldsCommand extends Command { + const STAR_PROMPT_CACHE_KEY = 'model-fields.github_star_prompted'; + protected $signature = 'model:fields {model : The model class name (e.g., User, App\\Models\\Post)} {--a|all : Get all fields} @@ -208,7 +210,7 @@ private function askToStarRepository() return; } - $cacheKey = 'model-fields.banner_shown'; + $cacheKey = self::STAR_PROMPT_CACHE_KEY; $repo = 'https://github.com/WatheqAlshowaiter/model-fields'; if (Cache::get($cacheKey)) { diff --git a/tests/FieldsTest.php b/tests/FieldsTest.php index 3227b61..457bce2 100644 --- a/tests/FieldsTest.php +++ b/tests/FieldsTest.php @@ -60,26 +60,6 @@ public static function requiredFields() // but other methods works fine $this->assertEquals(['created_at', 'updated_at'], $testModelClass::nullableFields()); - - } - - /** - * @throws ReflectionException - */ - private function removeMacro(string $class, string $macro): void - { - if (! method_exists($class, 'hasMacro')) { - return; - } - - $reflection = new ReflectionClass($class); - $property = $reflection->getProperty('macros'); - $property->setAccessible(true); - - $macros = $property->getValue(); - unset($macros[$macro]); - $property->setValue($macros); - $property->setAccessible(false); } public function test_throw_exception_if_model_is_not_extends_of_eloquent_model() @@ -111,6 +91,18 @@ public function test_throw_exception_if_use_get_older_versions_methods_before_us Fields::primaryField(); } + public function test_facade_accepts_dynamic_string_class_names() + { + $modelClasses = [ + "WatheqAlshowaiter\ModelFields\Tests\Models\Father", // with leading slash + "\WatheqAlshowaiter\ModelFields\Tests\Models\Mother", // without leading slash + ]; + + foreach ($modelClasses as $modelClass) { + $this->assertEquals(['id'], Fields::model($modelClass)->primaryField()); + } + } + public function test_all_fields_for_father_model() { $expected = [ @@ -193,6 +185,40 @@ public function test_required_fields_in_order() ], Father::requiredFieldsForOlderVersions()); } + public function test_required_fields_for_mother_model() + { + $expected = [ + 'uuid', + 'ulid', + ]; + $this->assertEquals($expected, Fields::model(Mother::class)->requiredFields()); + $this->assertEquals($expected, Fields::model(Mother::class)->requiredFieldsForOlderVersions()); + $this->assertEquals($expected, Mother::requiredFields()); + $this->assertEquals($expected, Mother::requiredFieldsForOlderVersions()); + } + + public function test_required_fields_for_son_model() + { + $expected = [ + 'father_id', + ]; + $this->assertEquals($expected, Fields::model(Son::class)->requiredFields()); + $this->assertEquals($expected, Fields::model(Son::class)->requiredFieldsForOlderVersions()); + $this->assertEquals($expected, Son::requiredFields()); + $this->assertEquals($expected, Son::requiredFieldsForOlderVersions()); + } + + public function test_required_fields_for_brother_model() + { + $expected = [ + 'email', + ]; + $this->assertEquals($expected, Fields::model(Brother::class)->requiredFields()); + $this->assertEquals($expected, Fields::model(Brother::class)->requiredFieldsForOlderVersions()); + $this->assertEquals($expected, Brother::requiredFields()); + $this->assertEquals($expected, Brother::requiredFieldsForOlderVersions()); + } + public function test_nullable_fields_for_father_model() { $expected = [ @@ -347,4 +373,23 @@ public function test_default_fields_for_brother_model() $this->assertEquals($expected, Fields::model(Brother::class)->defaultFields()); $this->assertEquals($expected, Brother::defaultFields()); } + + /** + * @throws ReflectionException + */ + private function removeMacro(string $class, string $macro): void + { + if (! method_exists($class, 'hasMacro')) { + return; + } + + $reflection = new ReflectionClass($class); + $property = $reflection->getProperty('macros'); + $property->setAccessible(true); + + $macros = $property->getValue(); + unset($macros[$macro]); + $property->setValue($macros); + $property->setAccessible(false); + } } diff --git a/tests/ModelFieldsCommandTest.php b/tests/ModelFieldsCommandTest.php index aca36f4..da9ee0d 100644 --- a/tests/ModelFieldsCommandTest.php +++ b/tests/ModelFieldsCommandTest.php @@ -6,7 +6,6 @@ use Illuminate\Support\Facades\Cache; use InvalidArgumentException; use RuntimeException; -use Symfony\Component\Console\Command\Command; use WatheqAlshowaiter\ModelFields\Console\ModelFieldsCommand; use WatheqAlshowaiter\ModelFields\Tests\Models\Father; @@ -18,6 +17,18 @@ class ModelFieldsCommandTest extends TestCase public const FAILURE_EXIT_CODE = 1; + protected function setUp(): void + { + parent::setUp(); + Cache::forever(ModelFieldsCommand::STAR_PROMPT_CACHE_KEY, true); + } + + protected function tearDown(): void + { + Cache::forget(ModelFieldsCommand::STAR_PROMPT_CACHE_KEY); + parent::tearDown(); + } + public function test_error_when_no_model_provided() { $this->expectException(RuntimeException::class); @@ -55,12 +66,11 @@ public function test_failed_when_format_value_is_not_valid() public function test_use_list_format_when_not_provided() { - Cache::forever('model-fields.banner_shown', true); + Cache::forever(ModelFieldsCommand::STAR_PROMPT_CACHE_KEY, true); $this->artisan('model:fields', ['model' => Father::class]) ->assertExitCode(self::SUCCESS_EXIT_CODE); - Cache::forget('model-fields.banner_shown'); } public function test_fail_when_provided_more_than_one_field_type() @@ -76,8 +86,6 @@ public function test_fail_when_provided_more_than_one_field_type() public function test_default_to_all_fields_when_no_type_specified() { - Cache::forever('model-fields.banner_shown', true); - $this->artisan('model:fields', [ 'model' => Father::class, ]) @@ -90,14 +98,10 @@ public function test_default_to_all_fields_when_no_type_specified() ->expectsOutput(' - created_at') ->expectsOutput(' - updated_at') ->expectsOutput(' - deleted_at'); - - Cache::forget('model-fields.banner_shown'); } public function test_uses_type_when_specified() { - Cache::forever('model-fields.banner_shown', true); - $this->artisan('model:fields', [ 'model' => Father::class, '--required' => true, @@ -105,14 +109,10 @@ public function test_uses_type_when_specified() ->expectsOutput('Father required fields:') ->expectsOutput(' - name') ->expectsOutput(' - email'); - - Cache::forget('model-fields.banner_shown'); } public function test_uses_type_shortname_when_specified() { - Cache::forever('model-fields.banner_shown', true); - $this->artisan('model:fields', [ 'model' => Father::class, '-r' => true, @@ -120,14 +120,10 @@ public function test_uses_type_shortname_when_specified() ->expectsOutput('Father required fields:') ->expectsOutput(' - name') ->expectsOutput(' - email'); - - Cache::forget('model-fields.banner_shown'); } public function test_json_format() { - Cache::forever('model-fields.banner_shown', true); - $this->artisan('model:fields', [ 'model' => Father::class, '--nullable' => true, @@ -139,14 +135,10 @@ public function test_json_format() "updated_at", "deleted_at" ]'); - - Cache::forget('model-fields.banner_shown'); } public function test_table_format() { - Cache::forever('model-fields.banner_shown', true); - $this->artisan('model:fields', [ 'model' => Father::class, '--primary' => true, @@ -157,64 +149,48 @@ public function test_table_format() ->expectsOutput('+-----------------------+') ->expectsOutput('| id |') ->expectsOutput('+-----------------------+'); - - Cache::forget('model-fields.banner_shown'); } public function test_json_format_with_empty_results() { - Cache::forever('model-fields.banner_shown', true); - $this->artisan('model:fields', [ 'model' => Father::class, '-A' => true, '--format' => 'json', ])->expectsOutput('No app-default fields found for Father model.'); - - Cache::forget('model-fields.banner_shown'); } public function test_table_format_with_empty_results() { - Cache::forever('model-fields.banner_shown', true); - $this->artisan('model:fields', [ 'model' => Father::class, '-A' => true, '--format' => 'table', ])->expectsOutput('No app-default fields found for Father model.'); - - Cache::forget('model-fields.banner_shown'); } public function test_list_format_with_empty_results() { - Cache::forever('model-fields.banner_shown', true); - $this->artisan('model:fields', [ 'model' => Father::class, '-A' => true, ])->expectsOutput('No app-default fields found for Father model.'); - - Cache::forget('model-fields.banner_shown'); } public function test_does_not_ask_if_already_cached() { - Cache::forever('model-fields.banner_shown', true); + Cache::forever(ModelFieldsCommand::STAR_PROMPT_CACHE_KEY, true); // make it clear here $this->artisan('model:fields', [ 'model' => Father::class, ])->assertExitCode(0); - $this->assertTrue(Cache::get('model-fields.banner_shown')); - - Cache::forget('model-fields.banner_shown'); + $this->assertTrue(Cache::get(ModelFieldsCommand::STAR_PROMPT_CACHE_KEY)); } public function test_asks_and_user_declines() { - Cache::forget('model-fields.banner_shown'); + Cache::forget(ModelFieldsCommand::STAR_PROMPT_CACHE_KEY); $this->artisan('model:fields', [ 'model' => Father::class, @@ -222,13 +198,12 @@ public function test_asks_and_user_declines() ->expectsQuestion('🌟 Help other developers find this package by starring it on GitHub?', false) ->assertExitCode(self::SUCCESS_EXIT_CODE); - $this->assertTrue(Cache::get('model-fields.banner_shown')); - Cache::forget('model-fields.banner_shown'); + $this->assertTrue(Cache::get(ModelFieldsCommand::STAR_PROMPT_CACHE_KEY)); } public function test_asks_and_user_accepts() { - Cache::forget('model-fields.banner_shown'); + Cache::forget(ModelFieldsCommand::STAR_PROMPT_CACHE_KEY); // Create a subclass that overrides openUrl() $stubCommand = new class extends ModelFieldsCommand @@ -254,7 +229,5 @@ protected function openUrl(string $url): void ->assertExitCode(self::SUCCESS_EXIT_CODE); $this->assertStringContainsString('github.com/WatheqAlshowaiter/model-fields', $stubCommand->calledWith); - - Cache::forget('model-fields.banner_shown'); } }