From 0187008af29d9048332150fe6187443b09a5ed88 Mon Sep 17 00:00:00 2001 From: Paul Klimov Date: Thu, 21 Dec 2023 18:48:39 +0200 Subject: [PATCH] add tests --- src/AttributeTypecastBehavior.php | 20 ++-- tests/AttributeTypecastBehaviorTest.php | 147 +++++++++++++++++++++++- tests/TestCase.php | 11 +- tests/data/Item.php | 24 +--- tests/data/ItemWithTypecast.php | 44 +++++++ 5 files changed, 214 insertions(+), 32 deletions(-) create mode 100644 tests/data/ItemWithTypecast.php diff --git a/src/AttributeTypecastBehavior.php b/src/AttributeTypecastBehavior.php index c183cdc..955eca5 100644 --- a/src/AttributeTypecastBehavior.php +++ b/src/AttributeTypecastBehavior.php @@ -5,13 +5,15 @@ use CActiveRecord; use CBehavior; use CBooleanValidator; +use CEvent; use CModelEvent; use CNumberValidator; use CStringValidator; use InvalidArgumentException; /** - * @property \CModel|CActiveRecord $owner The owner component that this behavior is attached to. + * @property \CModel|\CActiveRecord $owner The owner component that this behavior is attached to. + * @property array $attributeTypes * * @author Paul Klimov * @since 1.0 @@ -78,10 +80,10 @@ class AttributeTypecastBehavior extends CBehavior * the database (after find or refresh). * This option may be disabled in order to achieve better performance. * For example, in case of [[\yii\db\ActiveRecord]] usage, typecasting after find - * will grant no benefit in most cases an thus can be disabled. + * will grant no benefit in most cases and thus can be disabled. * Note that changing this option value will have no effect after this behavior has been attached to the model. */ - public $typecastAfterFind = false; + public $typecastAfterFind = true; /** * @var array internal static cache for auto detected [[attributeTypes]] values @@ -250,9 +252,9 @@ public function events(): array /** * Handles owner 'afterValidate' event, ensuring attribute typecasting. - * @param \CModelEvent $event event instance. + * @param \CEvent $event event instance. */ - public function afterValidate(CModelEvent $event): void + public function afterValidate(CEvent $event): void { if (!$this->owner->hasErrors()) { $this->typecastAttributes(); @@ -270,18 +272,18 @@ public function beforeSave(CModelEvent $event): void /** * Handles owner 'afterSave' event, ensuring attribute typecasting. - * @param \CModelEvent $event event instance. + * @param \CEvent $event event instance. */ - public function afterSave(CModelEvent $event): void + public function afterSave(CEvent $event): void { $this->typecastAttributes(); } /** * Handles owner 'afterFind' event, ensuring attribute typecasting. - * @param \CModelEvent $event event instance. + * @param \CEvent $event event instance. */ - public function afterFind(CModelEvent $event): void + public function afterFind(CEvent $event): void { $this->typecastAttributes(); } diff --git a/tests/AttributeTypecastBehaviorTest.php b/tests/AttributeTypecastBehaviorTest.php index 4cb4335..dd9c70e 100644 --- a/tests/AttributeTypecastBehaviorTest.php +++ b/tests/AttributeTypecastBehaviorTest.php @@ -2,10 +2,153 @@ namespace yii1tech\model\typecast\test; +use yii1tech\model\typecast\AttributeTypecastBehavior; +use yii1tech\model\typecast\test\data\Item; +use yii1tech\model\typecast\test\data\ItemWithTypecast; + class AttributeTypecastBehaviorTest extends TestCase { - public function testTypecastAttributes(): void + public function testTypecast(): void { - $this->assertTrue(true); + $model = new ItemWithTypecast(); + + $model->name = 123; + $model->category_id = '58'; + $model->price = '100.8'; + $model->is_active = 1; + $model->callback = 'foo'; + + $model->typecastAttributes(); + + $this->assertSame('123', $model->name); + $this->assertSame(58, $model->category_id); + $this->assertSame(100.8, $model->price); + $this->assertTrue($model->is_active); + $this->assertSame('callback: foo', $model->callback); + } + + /** + * @depends testTypecast + */ + public function testSkipNull() + { + $model = new ItemWithTypecast(); + $model->skipOnNull = true; + + $model->name = null; + $model->category_id = null; + $model->price = null; + $model->is_active = null; + $model->callback = null; + + $model->typecastAttributes(); + + $this->assertNull($model->name); + $this->assertNull($model->category_id); + $this->assertNull($model->price); + $this->assertNull($model->is_active); + $this->assertNull($model->callback); + + $model->skipOnNull = false; + $model->typecastAttributes(); + + $this->assertSame('', $model->name); + $this->assertSame(0, $model->category_id); + $this->assertSame(0.0, $model->price); + $this->assertFalse($model->is_active); + $this->assertSame('callback: ', $model->callback); + } + + /** + * @depends testTypecast + */ + public function testAfterFindEvent(): void + { + $model = new ItemWithTypecast(); + + $model->validate(); + $model->save(false); + + $model->updateAll(['callback' => 'find']); + $model->refresh(); + $this->assertSame('callback: find', $model->callback); + } + + /** + * @depends testTypecast + */ + public function testAfterValidateEvent(): void + { + $model = new ItemWithTypecast(); + + $model->callback = 'validate'; + $model->validate(); + $this->assertSame('callback: validate', $model->callback); + } + + /** + * @depends testTypecast + */ + public function testSaveEvents() + { + $baseBehavior = new AttributeTypecastBehavior(); + $baseBehavior->attributeTypes = [ + 'callback' => function ($value) { + return 'callback: ' . $value; + }, + ]; + + $model = new Item(); + $behavior = clone $baseBehavior; + $behavior->typecastBeforeSave = true; + $behavior->typecastAfterSave = false; + $model->attachBehavior('typecast', $behavior); + $model->callback = 'before save'; + $model->save(false); + $this->assertSame('callback: before save', $model->callback); + + $model = new Item(); + $behavior = clone $baseBehavior; + $behavior->typecastBeforeSave = false; + $behavior->typecastAfterSave = true; + $model->attachBehavior('typecast', $behavior); + $model->callback = 'after save'; + $model->save(false); + $this->assertSame('callback: after save', $model->callback); + + $model = new Item(); + $behavior = clone $baseBehavior; + $behavior->typecastBeforeSave = false; + $behavior->typecastAfterSave = false; + $model->attachBehavior('typecast', $behavior); + $model->callback = 'no typecast'; + $model->save(false); + $this->assertSame('no typecast', $model->callback); + } + + /** + * @depends testSkipNull + */ + public function testSkipNotSelectedAttribute() + { + $model = new ItemWithTypecast(); + $model->name = 'skip-not-selected'; + $model->category_id = '58'; + $model->price = '100.8'; + $model->is_active = 1; + $model->callback = 'foo'; + $model->save(false); + + /* @var $model ItemWithTypecast */ + $model = ItemWithTypecast::model()->find([ + 'select' => ['id', 'name'], + 'condition' => "id = {$model->id}", + ]); + + $model->typecastAttributes(); + $model->save(false); + + $model->refresh(); + $this->assertSame(58, $model->category_id); } } \ No newline at end of file diff --git a/tests/TestCase.php b/tests/TestCase.php index dc475eb..7875580 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -5,6 +5,7 @@ use CConsoleApplication; use CMap; use Yii; +use yii1tech\model\typecast\AttributeTypecastBehavior; class TestCase extends \PHPUnit\Framework\TestCase { @@ -25,6 +26,8 @@ protected function setUp(): void */ protected function tearDown(): void { + AttributeTypecastBehavior::clearAutoDetectedAttributeTypes(); + $this->destroyApplication(); parent::tearDown(); @@ -76,9 +79,11 @@ protected function setupTestDbData() 'id' => 'pk', 'category_id' => 'integer', 'name' => 'string', - 'is_deleted' => 'boolean DEFAULT 0', + 'price' => 'float', + 'is_active' => 'boolean DEFAULT 0', 'created_at' => 'integer', 'created_date' => 'datetime', + 'callback' => 'string', ]); // Data : @@ -88,14 +93,14 @@ protected function setupTestDbData() [ 'category_id' => 1, 'name' => 'item1', - 'is_deleted' => 0, + 'is_active' => 0, 'created_at' => time(), 'created_date' => date('Y-m-d H:i:s'), ], [ 'category_id' => 2, 'name' => 'item2', - 'is_deleted' => 1, + 'is_active' => 1, 'created_at' => time(), 'created_date' => date('Y-m-d H:i:s'), ], diff --git a/tests/data/Item.php b/tests/data/Item.php index c5c7308..df4b3bb 100644 --- a/tests/data/Item.php +++ b/tests/data/Item.php @@ -1,18 +1,18 @@ [ - 'class' => AttributeTypecastBehavior::class, - ], - ]; - } } \ No newline at end of file diff --git a/tests/data/ItemWithTypecast.php b/tests/data/ItemWithTypecast.php new file mode 100644 index 0000000..53ddd50 --- /dev/null +++ b/tests/data/ItemWithTypecast.php @@ -0,0 +1,44 @@ + [ + 'class' => AttributeTypecastBehavior::class, + 'typecastBeforeSave' => true, + 'typecastAfterFind' => true, + 'attributeTypes' => [ + 'name' => AttributeTypecastBehavior::TYPE_STRING, + 'category_id' => AttributeTypecastBehavior::TYPE_INTEGER, + 'price' => AttributeTypecastBehavior::TYPE_FLOAT, + 'is_active' => AttributeTypecastBehavior::TYPE_BOOLEAN, + 'callback' => function ($value) { + return 'callback: ' . $value; + }, + ], + ], + ]; + } +} \ No newline at end of file