mirror of
				https://github.com/elyby/accounts.git
				synced 2025-05-31 14:11:46 +05:30 
			
		
		
		
	Rework email_activation model, get rid of behaviors, use json column to store additional data
This commit is contained in:
		| @@ -1,48 +0,0 @@ | ||||
| <?php | ||||
| namespace common\behaviors; | ||||
|  | ||||
| use yii\base\Behavior; | ||||
| use yii\helpers\ArrayHelper; | ||||
|  | ||||
| class DataBehavior extends Behavior { | ||||
|  | ||||
|     /** | ||||
|      * @var string attribute name to which this behavior will be applied | ||||
|      */ | ||||
|     public $attribute = '_data'; | ||||
|  | ||||
|     /** | ||||
|      * @param string $key | ||||
|      * @param mixed  $value | ||||
|      */ | ||||
|     protected function setKey(string $key, $value) { | ||||
|         $data = $this->getData(); | ||||
|         $data[$key] = $value; | ||||
|         $this->owner->{$this->attribute} = serialize($data); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $key | ||||
|      * @return mixed | ||||
|      */ | ||||
|     protected function getKey(string $key) { | ||||
|         return ArrayHelper::getValue($this->getData(), $key); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array | ||||
|      * @throws \yii\base\ErrorException Yii2 will catch Notice from the wrong deserialization and turn it | ||||
|      * into its own Exception, so that the program can continue to work normally (you still should catch an Exception) | ||||
|      */ | ||||
|     private function getData() { | ||||
|         $data = $this->owner->{$this->attribute}; | ||||
|         if (is_string($data)) { | ||||
|             $data = unserialize($data); | ||||
|         } else { | ||||
|             $data = []; | ||||
|         } | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,75 +0,0 @@ | ||||
| <?php | ||||
| namespace common\behaviors; | ||||
|  | ||||
| use yii\base\Behavior; | ||||
|  | ||||
| /** | ||||
|  * @property \common\models\EmailActivation $owner | ||||
|  */ | ||||
| class EmailActivationExpirationBehavior extends Behavior { | ||||
|  | ||||
|     /** | ||||
|      * @var int the number of seconds before the code can be sent again | ||||
|      * @see EmailActivation::canRepeat() | ||||
|      */ | ||||
|     public $repeatTimeout; | ||||
|  | ||||
|     /** | ||||
|      * @var int the number of seconds before this activation expires | ||||
|      * @see EmailActivation::isExpired() | ||||
|      */ | ||||
|     public $expirationTimeout; | ||||
|  | ||||
|     /** | ||||
|      * Is it allowed to resend a message of the current type? | ||||
|      * The value of EmailActivation::$repeatTimeout is used for checking as follows: | ||||
|      * - <0 will forbid you to resend this activation | ||||
|      * - =0 allows you to send messages at any time | ||||
|      * - >0 will check how many seconds have passed since the model was created | ||||
|      * | ||||
|      * @see EmailActivation::compareTime() | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function canRepeat(): bool { | ||||
|         return $this->compareTime($this->repeatTimeout); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Did the code expire? | ||||
|      * The value of EmailActivation::$expirationTimeout is used for checking as follows: | ||||
|      * - <0 means the code will never expire | ||||
|      * - =0 will always say that the code has expired | ||||
|      * - >0 will check how many seconds have passed since the model was created | ||||
|      * | ||||
|      * @see EmailActivation::compareTime() | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function isExpired(): bool { | ||||
|         return $this->compareTime($this->expirationTimeout); | ||||
|     } | ||||
|  | ||||
|     public function canRepeatIn(): int { | ||||
|         return $this->calculateTime($this->repeatTimeout); | ||||
|     } | ||||
|  | ||||
|     public function expireIn(): int { | ||||
|         return $this->calculateTime($this->expirationTimeout); | ||||
|     } | ||||
|  | ||||
|     private function compareTime(int $value): bool { | ||||
|         if ($value < 0) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         if ($value === 0) { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         return time() > $this->calculateTime($value); | ||||
|     } | ||||
|  | ||||
|     private function calculateTime(int $value): int { | ||||
|         return $this->owner->created_at + $value; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -3,10 +3,10 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace common\models; | ||||
|  | ||||
| use common\behaviors\DataBehavior; | ||||
| use common\behaviors\EmailActivationExpirationBehavior; | ||||
| use common\behaviors\PrimaryKeyValueBehavior; | ||||
| use common\components\UserFriendlyRandomKey; | ||||
| use DateInterval; | ||||
| use DateTimeImmutable; | ||||
| use yii\base\InvalidConfigException; | ||||
| use yii\behaviors\TimestampBehavior; | ||||
| use yii\db\ActiveRecord; | ||||
| @@ -14,19 +14,17 @@ use yii\helpers\ArrayHelper; | ||||
|  | ||||
| /** | ||||
|  * Fields: | ||||
|  * @property string  $key | ||||
|  * @property integer $account_id | ||||
|  * @property integer $type | ||||
|  * @property string  $_data | ||||
|  * @property integer $created_at | ||||
|  * @property string     $key | ||||
|  * @property int        $account_id | ||||
|  * @property int        $type | ||||
|  * @property array|null $data | ||||
|  * @property int        $created_at | ||||
|  * | ||||
|  * Relations: | ||||
|  * @property Account $account | ||||
|  * | ||||
|  * Behaviors: | ||||
|  * @mixin TimestampBehavior | ||||
|  * @mixin EmailActivationExpirationBehavior | ||||
|  * @mixin DataBehavior | ||||
|  */ | ||||
| class EmailActivation extends ActiveRecord { | ||||
|  | ||||
| @@ -39,41 +37,15 @@ class EmailActivation extends ActiveRecord { | ||||
|         return 'email_activations'; | ||||
|     } | ||||
|  | ||||
|     public static function find(): EmailActivationQuery { | ||||
|         return new EmailActivationQuery(static::class); | ||||
|     } | ||||
|  | ||||
|     public function behaviors(): array { | ||||
|     public static function getClassMap(): array { | ||||
|         return [ | ||||
|             [ | ||||
|                 'class' => TimestampBehavior::class, | ||||
|                 'updatedAtAttribute' => false, | ||||
|             ], | ||||
|             [ | ||||
|                 'class' => PrimaryKeyValueBehavior::class, | ||||
|                 'value' => function() { | ||||
|                     return UserFriendlyRandomKey::make(); | ||||
|                 }, | ||||
|             ], | ||||
|             'expirationBehavior' => [ | ||||
|                 'class' => EmailActivationExpirationBehavior::class, | ||||
|                 'repeatTimeout' => 5 * 60, // 5m | ||||
|                 'expirationTimeout' => -1, | ||||
|             ], | ||||
|             'dataBehavior' => [ | ||||
|                 'class' => DataBehavior::class, | ||||
|                 'attribute' => '_data', | ||||
|             ], | ||||
|             self::TYPE_REGISTRATION_EMAIL_CONFIRMATION => confirmations\RegistrationConfirmation::class, | ||||
|             self::TYPE_FORGOT_PASSWORD_KEY => confirmations\ForgotPassword::class, | ||||
|             self::TYPE_CURRENT_EMAIL_CONFIRMATION => confirmations\CurrentEmailConfirmation::class, | ||||
|             self::TYPE_NEW_EMAIL_CONFIRMATION => confirmations\NewEmailConfirmation::class, | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     public function getAccount(): AccountQuery { | ||||
|         return $this->hasOne(Account::class, ['id' => 'account_id']); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     public static function instantiate($row) { | ||||
|         $type = ArrayHelper::getValue($row, 'type'); | ||||
|         if ($type === null) { | ||||
| @@ -88,13 +60,79 @@ class EmailActivation extends ActiveRecord { | ||||
|         return new $classMap[$type](); | ||||
|     } | ||||
|  | ||||
|     public static function getClassMap(): array { | ||||
|     public static function find(): EmailActivationQuery { | ||||
|         return new EmailActivationQuery(static::class); | ||||
|     } | ||||
|  | ||||
|     public function behaviors(): array { | ||||
|         return [ | ||||
|             self::TYPE_REGISTRATION_EMAIL_CONFIRMATION => confirmations\RegistrationConfirmation::class, | ||||
|             self::TYPE_FORGOT_PASSWORD_KEY => confirmations\ForgotPassword::class, | ||||
|             self::TYPE_CURRENT_EMAIL_CONFIRMATION => confirmations\CurrentEmailConfirmation::class, | ||||
|             self::TYPE_NEW_EMAIL_CONFIRMATION => confirmations\NewEmailConfirmation::class, | ||||
|             [ | ||||
|                 'class' => TimestampBehavior::class, | ||||
|                 'updatedAtAttribute' => false, | ||||
|             ], | ||||
|             [ | ||||
|                 'class' => PrimaryKeyValueBehavior::class, | ||||
|                 'value' => function(): string { | ||||
|                     return UserFriendlyRandomKey::make(); | ||||
|                 }, | ||||
|             ], | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     public function getAccount(): AccountQuery { | ||||
|         /** @noinspection PhpIncompatibleReturnTypeInspection */ | ||||
|         return $this->hasOne(Account::class, ['id' => 'account_id']); | ||||
|     } | ||||
|  | ||||
|     public function canResend(): bool { | ||||
|         $timeout = $this->getResendTimeout(); | ||||
|         if ($timeout === null) { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         return $this->compareTime($timeout); | ||||
|     } | ||||
|  | ||||
|     public function canResendAt(): DateTimeImmutable { | ||||
|         return $this->calculateTime($this->getResendTimeout() ?? new DateInterval('PT0S')); | ||||
|     } | ||||
|  | ||||
|     public function isStale(): bool { | ||||
|         $duration = $this->getExpireDuration(); | ||||
|         if ($duration === null) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return $this->compareTime($duration); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * After which time the message for this action type can be resended. | ||||
|      * When null returned the message can be sent immediately. | ||||
|      * | ||||
|      * @return DateInterval|null | ||||
|      */ | ||||
|     protected function getResendTimeout(): ?DateInterval { | ||||
|         return new DateInterval('PT5M'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * How long the activation code should be valid. | ||||
|      * When null returned the code is never expires | ||||
|      * | ||||
|      * @return DateInterval|null | ||||
|      */ | ||||
|     protected function getExpireDuration(): ?DateInterval { | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     private function compareTime(DateInterval $value): bool { | ||||
|         return (new DateTimeImmutable()) > $this->calculateTime($value); | ||||
|     } | ||||
|  | ||||
|     private function calculateTime(DateInterval $interval): DateTimeImmutable { | ||||
|         /** @noinspection PhpUnhandledExceptionInspection */ | ||||
|         return (new DateTimeImmutable('@' . $this->created_at))->add($interval); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -5,7 +5,7 @@ namespace common\models\confirmations; | ||||
|  | ||||
| use common\models\EmailActivation; | ||||
| use common\models\EmailActivationQuery; | ||||
| use yii\helpers\ArrayHelper; | ||||
| use DateInterval; | ||||
|  | ||||
| class CurrentEmailConfirmation extends EmailActivation { | ||||
|  | ||||
| @@ -13,18 +13,17 @@ class CurrentEmailConfirmation extends EmailActivation { | ||||
|         return parent::find()->withType(EmailActivation::TYPE_CURRENT_EMAIL_CONFIRMATION); | ||||
|     } | ||||
|  | ||||
|     public function behaviors(): array { | ||||
|         return ArrayHelper::merge(parent::behaviors(), [ | ||||
|             'expirationBehavior' => [ | ||||
|                 'repeatTimeout' => 6 * 60 * 60, // 6h | ||||
|                 'expirationTimeout' => 1 * 60 * 60, // 1h | ||||
|             ], | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function init(): void { | ||||
|         parent::init(); | ||||
|         $this->type = EmailActivation::TYPE_CURRENT_EMAIL_CONFIRMATION; | ||||
|     } | ||||
|  | ||||
|     protected function getResendTimeout(): ?DateInterval { | ||||
|         return new DateInterval('PT6H'); | ||||
|     } | ||||
|  | ||||
|     protected function getExpireDuration(): ?DateInterval { | ||||
|         return new DateInterval('PT1H'); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -5,7 +5,7 @@ namespace common\models\confirmations; | ||||
|  | ||||
| use common\models\EmailActivation; | ||||
| use common\models\EmailActivationQuery; | ||||
| use yii\helpers\ArrayHelper; | ||||
| use DateInterval; | ||||
|  | ||||
| class ForgotPassword extends EmailActivation { | ||||
|  | ||||
| @@ -13,18 +13,17 @@ class ForgotPassword extends EmailActivation { | ||||
|         return parent::find()->withType(EmailActivation::TYPE_FORGOT_PASSWORD_KEY); | ||||
|     } | ||||
|  | ||||
|     public function behaviors(): array { | ||||
|         return ArrayHelper::merge(parent::behaviors(), [ | ||||
|             'expirationBehavior' => [ | ||||
|                 'repeatTimeout' => 30 * 60, | ||||
|                 'expirationTimeout' => 1 * 60 * 60, | ||||
|             ], | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function init(): void { | ||||
|         parent::init(); | ||||
|         $this->type = EmailActivation::TYPE_FORGOT_PASSWORD_KEY; | ||||
|     } | ||||
|  | ||||
|     protected function getResendTimeout(): ?DateInterval { | ||||
|         return new DateInterval('PT30M'); | ||||
|     } | ||||
|  | ||||
|     protected function getExpireDuration(): ?DateInterval { | ||||
|         return new DateInterval('PT1H'); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -7,30 +7,25 @@ use common\models\EmailActivation; | ||||
| use common\models\EmailActivationQuery; | ||||
| use yii\helpers\ArrayHelper; | ||||
|  | ||||
| /** | ||||
|  * Behaviors: | ||||
|  * @mixin NewEmailConfirmationBehavior | ||||
|  */ | ||||
| class NewEmailConfirmation extends EmailActivation { | ||||
|  | ||||
|     public static function find(): EmailActivationQuery { | ||||
|         return parent::find()->withType(EmailActivation::TYPE_NEW_EMAIL_CONFIRMATION); | ||||
|     } | ||||
|  | ||||
|     public function behaviors(): array { | ||||
|         return ArrayHelper::merge(parent::behaviors(), [ | ||||
|             'expirationBehavior' => [ | ||||
|                 'repeatTimeout' => 5 * 60, | ||||
|             ], | ||||
|             'dataBehavior' => [ | ||||
|                 'class' => NewEmailConfirmationBehavior::class, | ||||
|             ], | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function init(): void { | ||||
|         parent::init(); | ||||
|         $this->type = EmailActivation::TYPE_NEW_EMAIL_CONFIRMATION; | ||||
|     } | ||||
|  | ||||
|     public function getNewEmail(): string { | ||||
|         return $this->data['newEmail']; | ||||
|     } | ||||
|  | ||||
|     public function setNewEmail(string $newEmail): void { | ||||
|         $this->data = ArrayHelper::merge($this->data ?? [], [ | ||||
|             'newEmail' => $newEmail, | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,21 +0,0 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace common\models\confirmations; | ||||
|  | ||||
| use common\behaviors\DataBehavior; | ||||
|  | ||||
| /** | ||||
|  * @property string $newEmail | ||||
|  */ | ||||
| class NewEmailConfirmationBehavior extends DataBehavior { | ||||
|  | ||||
|     public function getNewEmail(): string { | ||||
|         return $this->getKey('newEmail'); | ||||
|     } | ||||
|  | ||||
|     public function setNewEmail(string $newEmail): void { | ||||
|         $this->setKey('newEmail', $newEmail); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										30
									
								
								common/tests/fixtures/data/email-activations.php
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										30
									
								
								common/tests/fixtures/data/email-activations.php
									
									
									
									
										vendored
									
									
								
							| @@ -1,46 +1,56 @@ | ||||
| <?php | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use common\models\EmailActivation; | ||||
|  | ||||
| return [ | ||||
|     'freshRegistrationConfirmation' => [ | ||||
|         'key' => 'HABGCABHJ1234HBHVD', | ||||
|         'account_id' => 3, | ||||
|         'type' => \common\models\EmailActivation::TYPE_REGISTRATION_EMAIL_CONFIRMATION, | ||||
|         'type' => EmailActivation::TYPE_REGISTRATION_EMAIL_CONFIRMATION, | ||||
|         'data' => null, | ||||
|         'created_at' => time(), | ||||
|     ], | ||||
|     'oldRegistrationConfirmation' => [ | ||||
|         'key' => 'H23HBDCHHAG2HGHGHS', | ||||
|         'account_id' => 4, | ||||
|         'type' => \common\models\EmailActivation::TYPE_REGISTRATION_EMAIL_CONFIRMATION, | ||||
|         'created_at' => time() - (new \common\models\confirmations\RegistrationConfirmation())->repeatTimeout - 10, | ||||
|         'type' => EmailActivation::TYPE_REGISTRATION_EMAIL_CONFIRMATION, | ||||
|         'data' => null, | ||||
|         'created_at' => Carbon::now()->subMinutes(5)->subSeconds(10)->unix(), | ||||
|     ], | ||||
|     'freshPasswordRecovery' => [ | ||||
|         'key' => 'H24HBDCHHAG2HGHGHS', | ||||
|         'account_id' => 5, | ||||
|         'type' => \common\models\EmailActivation::TYPE_FORGOT_PASSWORD_KEY, | ||||
|         'type' => EmailActivation::TYPE_FORGOT_PASSWORD_KEY, | ||||
|         'data' => null, | ||||
|         'created_at' => time(), | ||||
|     ], | ||||
|     'oldPasswordRecovery' => [ | ||||
|         'key' => 'H25HBDCHHAG2HGHGHS', | ||||
|         'account_id' => 6, | ||||
|         'type' => \common\models\EmailActivation::TYPE_FORGOT_PASSWORD_KEY, | ||||
|         'created_at' => time() - (new \common\models\confirmations\ForgotPassword())->repeatTimeout - 10, | ||||
|         'type' => EmailActivation::TYPE_FORGOT_PASSWORD_KEY, | ||||
|         'data' => null, | ||||
|         'created_at' => Carbon::now()->subMinutes(30)->subSeconds(10)->unix(), | ||||
|     ], | ||||
|     'currentChangeEmailConfirmation' => [ | ||||
|         'key' => 'H27HBDCHHAG2HGHGHS', | ||||
|         'account_id' => 7, | ||||
|         'type' => \common\models\EmailActivation::TYPE_CURRENT_EMAIL_CONFIRMATION, | ||||
|         'type' => EmailActivation::TYPE_CURRENT_EMAIL_CONFIRMATION, | ||||
|         'data' => null, | ||||
|         'created_at' => time() - 10, | ||||
|     ], | ||||
|     'newEmailConfirmation' => [ | ||||
|         'key' => 'H28HBDCHHAG2HGHGHS', | ||||
|         'account_id' => 8, | ||||
|         'type' => \common\models\EmailActivation::TYPE_NEW_EMAIL_CONFIRMATION, | ||||
|         '_data' => serialize(['newEmail' => 'my-new-email@ely.by']), | ||||
|         'type' => EmailActivation::TYPE_NEW_EMAIL_CONFIRMATION, | ||||
|         'data' => ['newEmail' => 'my-new-email@ely.by'], | ||||
|         'created_at' => time() - 10, | ||||
|     ], | ||||
|     'deeplyExpiredConfirmation' => [ | ||||
|         'key' => 'H29HBDCHHAG2HGHGHS', | ||||
|         'account_id' => 1, | ||||
|         'type' => \common\models\EmailActivation::TYPE_NEW_EMAIL_CONFIRMATION, | ||||
|         'type' => EmailActivation::TYPE_NEW_EMAIL_CONFIRMATION, | ||||
|         'data' => null, | ||||
|         'created_at' => 1487695872, | ||||
|     ], | ||||
| ]; | ||||
|   | ||||
| @@ -1,75 +0,0 @@ | ||||
| <?php | ||||
| namespace common\tests\unit\behaviors; | ||||
|  | ||||
| use Codeception\Specify; | ||||
| use common\behaviors\DataBehavior; | ||||
| use common\tests\_support\ProtectedCaller; | ||||
| use common\tests\unit\TestCase; | ||||
| use yii\base\ErrorException; | ||||
| use yii\base\Model; | ||||
|  | ||||
| class DataBehaviorTest extends TestCase { | ||||
|     use Specify; | ||||
|     use ProtectedCaller; | ||||
|  | ||||
|     public function testSetKey() { | ||||
|         $model = $this->createModel(); | ||||
|         /** @var DataBehavior $behavior */ | ||||
|         $behavior = $model->behaviors['dataBehavior']; | ||||
|         $this->callProtected($behavior, 'setKey', 'my-key', 'my-value'); | ||||
|         $this->assertSame(serialize(['my-key' => 'my-value']), $model->_data); | ||||
|     } | ||||
|  | ||||
|     public function testGetKey() { | ||||
|         $model = $this->createModel(); | ||||
|         $model->_data = serialize(['some-key' => 'some-value']); | ||||
|         /** @var DataBehavior $behavior */ | ||||
|         $behavior = $model->behaviors['dataBehavior']; | ||||
|         $this->assertSame('some-value', $this->callProtected($behavior, 'getKey', 'some-key')); | ||||
|     } | ||||
|  | ||||
|     public function testGetData() { | ||||
|         $this->specify('getting value from null field should return empty array', function() { | ||||
|             $model = $this->createModel(); | ||||
|             /** @var DataBehavior $behavior */ | ||||
|             $behavior = $model->behaviors['dataBehavior']; | ||||
|             $this->assertSame([], $this->callProtected($behavior, 'getData')); | ||||
|         }); | ||||
|  | ||||
|         $this->specify('getting value from serialized data field should return encoded value', function() { | ||||
|             $model = $this->createModel(); | ||||
|             $data = ['foo' => 'bar']; | ||||
|             $model->_data = serialize($data); | ||||
|             /** @var DataBehavior $behavior */ | ||||
|             $behavior = $model->behaviors['dataBehavior']; | ||||
|             $this->assertSame($data, $this->callProtected($behavior, 'getData')); | ||||
|         }); | ||||
|  | ||||
|         $this->specify('getting value from invalid serialization string', function() { | ||||
|             $model = $this->createModel(); | ||||
|             $model->_data = 'this is invalid serialization of string'; | ||||
|             /** @var DataBehavior $behavior */ | ||||
|             $behavior = $model->behaviors['dataBehavior']; | ||||
|             $this->expectException(ErrorException::class); | ||||
|             $this->callProtected($behavior, 'getData'); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Model | ||||
|      */ | ||||
|     private function createModel() { | ||||
|         return new class extends Model { | ||||
|             public $_data; | ||||
|  | ||||
|             public function behaviors() { | ||||
|                 return [ | ||||
|                     'dataBehavior' => [ | ||||
|                         'class' => DataBehavior::class, | ||||
|                     ], | ||||
|                 ]; | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,109 +0,0 @@ | ||||
| <?php | ||||
| namespace common\tests\unit\behaviors; | ||||
|  | ||||
| use Codeception\Specify; | ||||
| use common\behaviors\EmailActivationExpirationBehavior; | ||||
| use common\tests\_support\ProtectedCaller; | ||||
| use common\tests\unit\TestCase; | ||||
| use yii\base\Model; | ||||
|  | ||||
| class EmailActivationExpirationBehaviorTest extends TestCase { | ||||
|     use Specify; | ||||
|     use ProtectedCaller; | ||||
|  | ||||
|     public function testCalculateTime() { | ||||
|         $behavior = $this->createBehavior(); | ||||
|         $time = time(); | ||||
|         $behavior->owner->created_at = $time; | ||||
|         $this->assertSame($time + 10, $this->callProtected($behavior, 'calculateTime', 10)); | ||||
|     } | ||||
|  | ||||
|     public function testCompareTime() { | ||||
|         $this->specify('expect false, if passed value is less then 0', function() { | ||||
|             $behavior = $this->createBehavior(); | ||||
|             $this->assertFalse($this->callProtected($behavior, 'compareTime', -1)); | ||||
|         }); | ||||
|  | ||||
|         $this->specify('expect true, if passed value is equals 0', function() { | ||||
|             $behavior = $this->createBehavior(); | ||||
|             $this->assertTrue($this->callProtected($behavior, 'compareTime', 0)); | ||||
|         }); | ||||
|  | ||||
|         $this->specify('expect true, if passed value is more than 0 and current time is greater then calculated', function() { | ||||
|             $behavior = $this->createBehavior(); | ||||
|             $behavior->owner->created_at = time() - 10; | ||||
|             $this->assertTrue($this->callProtected($behavior, 'compareTime', 5)); | ||||
|         }); | ||||
|  | ||||
|         $this->specify('expect false, if passed value is more than 0 and current time is less then calculated', function() { | ||||
|             $behavior = $this->createBehavior(); | ||||
|             $behavior->owner->created_at = time() - 2; | ||||
|             $this->assertFalse($this->callProtected($behavior, 'compareTime', 7)); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     public function testCanRepeat() { | ||||
|         $this->specify('we can repeat, if created_at + repeatTimeout is greater, then current time', function() { | ||||
|             $behavior = $this->createBehavior(); | ||||
|             $behavior->repeatTimeout = 30; | ||||
|             $behavior->owner->created_at = time() - 60; | ||||
|             $this->assertTrue($behavior->canRepeat()); | ||||
|         }); | ||||
|  | ||||
|         $this->specify('we cannot repeat, if created_at + repeatTimeout is less, then current time', function() { | ||||
|             $behavior = $this->createBehavior(); | ||||
|             $behavior->repeatTimeout = 60; | ||||
|             $behavior->owner->created_at = time() - 30; | ||||
|             $this->assertFalse($behavior->canRepeat()); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     public function testIsExpired() { | ||||
|         $this->specify('key is not expired, if created_at + expirationTimeout is greater, then current time', function() { | ||||
|             $behavior = $this->createBehavior(); | ||||
|             $behavior->expirationTimeout = 30; | ||||
|             $behavior->owner->created_at = time() - 60; | ||||
|             $this->assertTrue($behavior->isExpired()); | ||||
|         }); | ||||
|  | ||||
|         $this->specify('key is not expired, if created_at + expirationTimeout is less, then current time', function() { | ||||
|             $behavior = $this->createBehavior(); | ||||
|             $behavior->expirationTimeout = 60; | ||||
|             $behavior->owner->created_at = time() - 30; | ||||
|             $this->assertFalse($behavior->isExpired()); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     public function testCanRepeatIn() { | ||||
|         $this->specify('get expected timestamp for repeat time moment', function() { | ||||
|             $behavior = $this->createBehavior(); | ||||
|             $behavior->repeatTimeout = 30; | ||||
|             $behavior->owner->created_at = time() - 60; | ||||
|             $this->assertSame($behavior->owner->created_at + $behavior->repeatTimeout, $behavior->canRepeatIn()); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     public function testExpireIn() { | ||||
|         $this->specify('get expected timestamp for key expire moment', function() { | ||||
|             $behavior = $this->createBehavior(); | ||||
|             $behavior->expirationTimeout = 30; | ||||
|             $behavior->owner->created_at = time() - 60; | ||||
|             $this->assertSame($behavior->owner->created_at + $behavior->expirationTimeout, $behavior->expireIn()); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return EmailActivationExpirationBehavior | ||||
|      */ | ||||
|     private function createBehavior() { | ||||
|         $behavior = new EmailActivationExpirationBehavior(); | ||||
|         /** @var Model $model */ | ||||
|         $model = new class extends Model { | ||||
|             public $created_at; | ||||
|         }; | ||||
|         $model->attachBehavior('email-activation-behavior', $behavior); | ||||
|  | ||||
|         return $behavior; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,10 +1,14 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace common\tests\unit\models; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use common\models\confirmations; | ||||
| use common\models\EmailActivation; | ||||
| use common\tests\fixtures\EmailActivationFixture; | ||||
| use common\tests\unit\TestCase; | ||||
| use DateInterval; | ||||
|  | ||||
| class EmailActivationTest extends TestCase { | ||||
|  | ||||
| @@ -14,22 +18,59 @@ class EmailActivationTest extends TestCase { | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     public function testInstantiate() { | ||||
|         $this->assertInstanceOf(confirmations\RegistrationConfirmation::class, EmailActivation::findOne([ | ||||
|             'type' => EmailActivation::TYPE_REGISTRATION_EMAIL_CONFIRMATION, | ||||
|         ])); | ||||
|     /** | ||||
|      * @dataProvider getInstantiateTestCases | ||||
|      */ | ||||
|     public function testInstantiate(int $type, string $expectedClassType) { | ||||
|         $this->assertInstanceOf($expectedClassType, EmailActivation::findOne(['type' => $type])); | ||||
|     } | ||||
|  | ||||
|         $this->assertInstanceOf(confirmations\ForgotPassword::class, EmailActivation::findOne([ | ||||
|             'type' => EmailActivation::TYPE_FORGOT_PASSWORD_KEY, | ||||
|         ])); | ||||
|     public function getInstantiateTestCases() { | ||||
|         yield [EmailActivation::TYPE_REGISTRATION_EMAIL_CONFIRMATION, confirmations\RegistrationConfirmation::class]; | ||||
|         yield [EmailActivation::TYPE_FORGOT_PASSWORD_KEY, confirmations\ForgotPassword::class]; | ||||
|         yield [EmailActivation::TYPE_CURRENT_EMAIL_CONFIRMATION, confirmations\CurrentEmailConfirmation::class]; | ||||
|         yield [EmailActivation::TYPE_NEW_EMAIL_CONFIRMATION, confirmations\NewEmailConfirmation::class]; | ||||
|     } | ||||
|  | ||||
|         $this->assertInstanceOf(confirmations\CurrentEmailConfirmation::class, EmailActivation::findOne([ | ||||
|             'type' => EmailActivation::TYPE_CURRENT_EMAIL_CONFIRMATION, | ||||
|         ])); | ||||
|     public function testCanResend() { | ||||
|         $model = $this->createPartialMock(EmailActivation::class, ['getResendTimeout']); | ||||
|         $model->method('getResendTimeout')->willReturn(new DateInterval('PT10M')); | ||||
|  | ||||
|         $this->assertInstanceOf(confirmations\NewEmailConfirmation::class, EmailActivation::findOne([ | ||||
|             'type' => EmailActivation::TYPE_NEW_EMAIL_CONFIRMATION, | ||||
|         ])); | ||||
|         $model->created_at = time(); | ||||
|         $this->assertFalse($model->canResend()); | ||||
|         $this->assertEqualsWithDelta(Carbon::now()->addMinutes(10), $model->canResendAt(), 3); | ||||
|  | ||||
|         $model->created_at = time() - 60 * 10 - 1; | ||||
|         $this->assertTrue($model->canResend()); | ||||
|         $this->assertEqualsWithDelta(Carbon::now()->subSecond(), $model->canResendAt(), 3); | ||||
|     } | ||||
|  | ||||
|     public function testCanResendWithNullTimeout() { | ||||
|         $model = $this->createPartialMock(EmailActivation::class, ['getResendTimeout']); | ||||
|         $model->method('getResendTimeout')->willReturn(null); | ||||
|  | ||||
|         $model->created_at = time(); | ||||
|         $this->assertTrue($model->canResend()); | ||||
|         $this->assertEqualsWithDelta(Carbon::now(), $model->canResendAt(), 3); | ||||
|     } | ||||
|  | ||||
|     public function testIsStale() { | ||||
|         $model = $this->createPartialMock(EmailActivation::class, ['getExpireDuration']); | ||||
|         $model->method('getExpireDuration')->willReturn(new DateInterval('PT10M')); | ||||
|  | ||||
|         $model->created_at = time(); | ||||
|         $this->assertFalse($model->isStale()); | ||||
|  | ||||
|         $model->created_at = time() - 60 * 10 - 1; | ||||
|         $this->assertTrue($model->isStale()); | ||||
|     } | ||||
|  | ||||
|     public function testIsStaleWithNullDuration() { | ||||
|         $model = $this->createPartialMock(EmailActivation::class, ['getExpireDuration']); | ||||
|         $model->method('getExpireDuration')->willReturn(null); | ||||
|  | ||||
|         $model->created_at = time(); | ||||
|         $this->assertFalse($model->isStale()); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user