From 84b8d72103389b9ad8f0c10e16ef656210802e69 Mon Sep 17 00:00:00 2001 From: Watheq Alshowaiter Date: Sat, 20 Sep 2025 15:54:55 +0300 Subject: [PATCH 01/10] test: add dynamic string class names facade test - Add test for facade accepting model class names as strings - Test both with and without leading backslashes - Add missing required fields tests for Mother, Son, and Brother models - Reorganize test structure and improve code formatting --- tests/FieldsTest.php | 88 +++++++++++++++++++++++++++++++++----------- 1 file changed, 67 insertions(+), 21 deletions(-) diff --git a/tests/FieldsTest.php b/tests/FieldsTest.php index 3227b61..e107fe6 100644 --- a/tests/FieldsTest.php +++ b/tests/FieldsTest.php @@ -44,8 +44,7 @@ public function test_macro_is_overridden_when_same_static_method_name_added() $table->timestamps(); }); - $testModelClass = new class extends Model - { + $testModelClass = new class extends Model { protected $table = 'test_table'; public static function requiredFields() @@ -60,27 +59,8 @@ 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", + "WatheqAlshowaiter\ModelFields\Tests\Models\Mother" + ]; + + foreach ($modelClasses as $modelClass) { + $this->assertEquals(['id'], Fields::model($modelClass)->primaryField()); + } + } + public function test_all_fields_for_father_model() { $expected = [ @@ -193,6 +185,41 @@ 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 +374,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); + } } From 49c18aa3989d821f76ffd19367f6b062e8b1116d Mon Sep 17 00:00:00 2001 From: WatheqAlshowaiter <24838274+WatheqAlshowaiter@users.noreply.github.com> Date: Sat, 20 Sep 2025 13:02:30 +0000 Subject: [PATCH 02/10] Fix styling --- tests/FieldsTest.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/FieldsTest.php b/tests/FieldsTest.php index e107fe6..1b1c269 100644 --- a/tests/FieldsTest.php +++ b/tests/FieldsTest.php @@ -44,7 +44,8 @@ public function test_macro_is_overridden_when_same_static_method_name_added() $table->timestamps(); }); - $testModelClass = new class extends Model { + $testModelClass = new class extends Model + { protected $table = 'test_table'; public static function requiredFields() @@ -61,7 +62,6 @@ public static function requiredFields() $this->assertEquals(['created_at', 'updated_at'], $testModelClass::nullableFields()); } - public function test_throw_exception_if_model_is_not_extends_of_eloquent_model() { $this->expectException(InvalidModelClassException::class); @@ -95,7 +95,7 @@ public function test_facade_accepts_dynamic_string_class_names() { $modelClasses = [ "WatheqAlshowaiter\ModelFields\Tests\Models\Father", - "WatheqAlshowaiter\ModelFields\Tests\Models\Mother" + "WatheqAlshowaiter\ModelFields\Tests\Models\Mother", ]; foreach ($modelClasses as $modelClass) { @@ -219,7 +219,6 @@ public function test_required_fields_for_brother_model() $this->assertEquals($expected, Brother::requiredFieldsForOlderVersions()); } - public function test_nullable_fields_for_father_model() { $expected = [ @@ -380,7 +379,7 @@ public function test_default_fields_for_brother_model() */ private function removeMacro(string $class, string $macro): void { - if (!method_exists($class, 'hasMacro')) { + if (! method_exists($class, 'hasMacro')) { return; } From 863b1e8442a78e1f73c89981562bb95bb948a47b Mon Sep 17 00:00:00 2001 From: WatheqAlshowaiter <24838274+WatheqAlshowaiter@users.noreply.github.com> Date: Sat, 20 Sep 2025 13:05:46 +0000 Subject: [PATCH 03/10] Update CHANGELOG --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86c27f2..1d90e42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to `model-required-fields` will be documented in this file. +## 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 From 0f42d81ae50cda6109ae363cdea94a67aefceb62 Mon Sep 17 00:00:00 2001 From: Watheq Alshowaiter Date: Sat, 20 Sep 2025 16:16:40 +0300 Subject: [PATCH 04/10] test: add more test detail --- tests/FieldsTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/FieldsTest.php b/tests/FieldsTest.php index e107fe6..576d30b 100644 --- a/tests/FieldsTest.php +++ b/tests/FieldsTest.php @@ -94,8 +94,8 @@ public function test_throw_exception_if_use_get_older_versions_methods_before_us public function test_facade_accepts_dynamic_string_class_names() { $modelClasses = [ - "WatheqAlshowaiter\ModelFields\Tests\Models\Father", - "WatheqAlshowaiter\ModelFields\Tests\Models\Mother" + "WatheqAlshowaiter\ModelFields\Tests\Models\Father", // with leading slash + "\WatheqAlshowaiter\ModelFields\Tests\Models\Mother" // without leading slash ]; foreach ($modelClasses as $modelClass) { From 05cd71193f2aa7983ba768fab0d454d1fed4b75a Mon Sep 17 00:00:00 2001 From: WatheqAlshowaiter <24838274+WatheqAlshowaiter@users.noreply.github.com> Date: Sat, 20 Sep 2025 13:18:33 +0000 Subject: [PATCH 05/10] Fix styling --- tests/FieldsTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/FieldsTest.php b/tests/FieldsTest.php index 3e620e9..457bce2 100644 --- a/tests/FieldsTest.php +++ b/tests/FieldsTest.php @@ -95,7 +95,7 @@ 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 + "\WatheqAlshowaiter\ModelFields\Tests\Models\Mother", // without leading slash ]; foreach ($modelClasses as $modelClass) { From af99658721ec5b2e6d7e0de96036573b1fe886f7 Mon Sep 17 00:00:00 2001 From: WatheqAlshowaiter <24838274+WatheqAlshowaiter@users.noreply.github.com> Date: Sat, 20 Sep 2025 13:19:34 +0000 Subject: [PATCH 06/10] Update CHANGELOG --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d90e42..7e01a3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to `model-required-fields` will be documented in this file. +## 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 From 07dbd108d5925e524b9e65ad2701ecd202201cdd Mon Sep 17 00:00:00 2001 From: Watheq Alshowaiter Date: Sat, 20 Sep 2025 16:32:35 +0300 Subject: [PATCH 07/10] fix: improve sql server workflow configuration - Remove unnecessary fail-fast strategy from SQL Server job - Fix PATH export to use GITHUB_PATH instead of GITHUB_ENV - Add -C flag to sqlcmd for certificate trust - Simplify sqlcmd path reference after PATH update --- .github/workflows/tests-for-databases.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) 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 From 6aaed3a42d88e8ee2f6792f940919ab5d2a63489 Mon Sep 17 00:00:00 2001 From: WatheqAlshowaiter <24838274+WatheqAlshowaiter@users.noreply.github.com> Date: Sat, 20 Sep 2025 13:36:55 +0000 Subject: [PATCH 08/10] Update CHANGELOG --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e01a3d..652a966 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to `model-required-fields` will be documented in this file. +## 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 From b02486f508a700d5663166ecc5580dfd1f24bcfb Mon Sep 17 00:00:00 2001 From: Watheq Alshowaiter Date: Sat, 20 Sep 2025 18:50:46 +0300 Subject: [PATCH 09/10] refactor(tests): centralize cache management and improve maintainability - Add STAR_PROMPT_CACHE_KEY constant to ModelFieldsCommand - Centralize cache setup/teardown in test base methods - Remove repetitive cache operations from individual tests - Remove unused Symfony Command import --- src/Console/ModelFieldsCommand.php | 4 +- tests/ModelFieldsCommandTest.php | 63 +++++++++--------------------- 2 files changed, 21 insertions(+), 46 deletions(-) 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/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'); } } From 568bd046f1f335a00428aaa8eb4cb712c57b0df7 Mon Sep 17 00:00:00 2001 From: WatheqAlshowaiter <24838274+WatheqAlshowaiter@users.noreply.github.com> Date: Sat, 20 Sep 2025 15:53:38 +0000 Subject: [PATCH 10/10] Update CHANGELOG --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 652a966..acbe8e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ 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