mirror of
				https://github.com/elyby/accounts.git
				synced 2025-05-31 14:11:46 +05:30 
			
		
		
		
	Implemented account deletion. Not all cases covered with tests [skip ci]
This commit is contained in:
		| @@ -3,8 +3,11 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace common\models; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use common\components\UserPass; | ||||
| use common\tasks\CreateWebHooksDeliveries; | ||||
| use DateInterval; | ||||
| use Webmozart\Assert\Assert; | ||||
| use Yii; | ||||
| use yii\base\InvalidConfigException; | ||||
| use yii\behaviors\TimestampBehavior; | ||||
| @@ -14,21 +17,22 @@ use const common\LATEST_RULES_VERSION; | ||||
|  | ||||
| /** | ||||
|  * Fields: | ||||
|  * @property int     $id | ||||
|  * @property string  $uuid | ||||
|  * @property string  $username | ||||
|  * @property string  $email | ||||
|  * @property string  $password_hash | ||||
|  * @property int     $password_hash_strategy | ||||
|  * @property string  $lang | ||||
|  * @property int     $status | ||||
|  * @property int     $rules_agreement_version | ||||
|  * @property string  $registration_ip | ||||
|  * @property string  $otp_secret | ||||
|  * @property int     $is_otp_enabled | ||||
|  * @property int     $created_at | ||||
|  * @property int     $updated_at | ||||
|  * @property int     $password_changed_at | ||||
|  * @property int         $id | ||||
|  * @property string      $uuid | ||||
|  * @property string      $username | ||||
|  * @property string      $email | ||||
|  * @property string      $password_hash | ||||
|  * @property int         $password_hash_strategy | ||||
|  * @property string      $lang | ||||
|  * @property int         $status | ||||
|  * @property int|null    $rules_agreement_version | ||||
|  * @property string|null $registration_ip | ||||
|  * @property string|null $otp_secret | ||||
|  * @property int         $is_otp_enabled | ||||
|  * @property int         $created_at | ||||
|  * @property int         $updated_at | ||||
|  * @property int         $password_changed_at | ||||
|  * @property int|null    $deleted_at shows the time, when the account was marked as deleted | ||||
|  * | ||||
|  * Getters-setters: | ||||
|  * @property-write string $password plain user's password | ||||
| @@ -55,8 +59,10 @@ class Account extends ActiveRecord { | ||||
|     public const PASS_HASH_STRATEGY_OLD_ELY = 0; | ||||
|     public const PASS_HASH_STRATEGY_YII2 = 1; | ||||
|  | ||||
|     public const ACCOUNT_DELETION_DELAY = 'P7D'; | ||||
|  | ||||
|     public static function tableName(): string { | ||||
|         return '{{%accounts}}'; | ||||
|         return 'accounts'; | ||||
|     } | ||||
|  | ||||
|     public static function find(): AccountQuery { | ||||
| @@ -153,17 +159,24 @@ class Account extends ActiveRecord { | ||||
|         return $this->registration_ip === null ? null : inet_ntop($this->registration_ip); | ||||
|     } | ||||
|  | ||||
|     public function afterSave($insert, $changedAttributes) { | ||||
|     public function getDeleteAt(): Carbon { | ||||
|         Assert::notNull($this->deleted_at, 'This method should not be called on not deleted records'); | ||||
|         return Carbon::createFromTimestamp($this->deleted_at)->add(new DateInterval(Account::ACCOUNT_DELETION_DELAY)); | ||||
|     } | ||||
|  | ||||
|     public function afterSave($insert, $changedAttributes): void { | ||||
|         parent::afterSave($insert, $changedAttributes); | ||||
|  | ||||
|         if ($insert) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         $meaningfulFields = ['username', 'email', 'uuid', 'status', 'lang']; | ||||
|         $meaningfulChangedAttributes = array_filter($changedAttributes, function(string $key) use ($meaningfulFields) { | ||||
|             return in_array($key, $meaningfulFields, true); | ||||
|         }, ARRAY_FILTER_USE_KEY); | ||||
|         $meaningfulFields = ['username', 'email', 'uuid', 'status', 'lang', 'deleted_at']; | ||||
|         $meaningfulChangedAttributes = array_filter( | ||||
|             $changedAttributes, | ||||
|             fn(string $key): bool => in_array($key, $meaningfulFields, true), | ||||
|             ARRAY_FILTER_USE_KEY, | ||||
|         ); | ||||
|         if (empty($meaningfulChangedAttributes)) { | ||||
|             return; | ||||
|         } | ||||
| @@ -171,4 +184,9 @@ class Account extends ActiveRecord { | ||||
|         Yii::$app->queue->push(CreateWebHooksDeliveries::createAccountEdit($this, $meaningfulChangedAttributes)); | ||||
|     } | ||||
|  | ||||
|     public function afterDelete(): void { | ||||
|         parent::afterDelete(); | ||||
|         Yii::$app->queue->push(CreateWebHooksDeliveries::createAccountDeletion($this)); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -10,6 +10,10 @@ use yii\db\ActiveQuery; | ||||
|  */ | ||||
| class AccountQuery extends ActiveQuery { | ||||
|  | ||||
|     public function excludeDeleted(): self { | ||||
|         return $this->andWhere(['NOT', ['status' => Account::STATUS_DELETED]]); | ||||
|     } | ||||
|  | ||||
|     public function andWhereLogin(string $login): self { | ||||
|         return $this->andWhere([$this->getLoginAttribute($login) => $login]); | ||||
|     } | ||||
|   | ||||
| @@ -1,29 +1,32 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace common\models; | ||||
|  | ||||
| use yii\behaviors\TimestampBehavior; | ||||
| use yii\db\ActiveQuery; | ||||
| use yii\db\ActiveRecord; | ||||
|  | ||||
| /** | ||||
|  * Fields: | ||||
|  * @property integer $id | ||||
|  * @property string  $username | ||||
|  * @property integer $account_id | ||||
|  * @property integer $applied_in | ||||
|  * @property int      $id | ||||
|  * @property string   $username | ||||
|  * @property int|null $account_id | ||||
|  * @property int      $applied_in | ||||
|  * | ||||
|  * Relations: | ||||
|  * @property Account $account | ||||
|  * @property-read Account $account | ||||
|  * | ||||
|  * Behaviors: | ||||
|  * @mixin TimestampBehavior | ||||
|  */ | ||||
| class UsernameHistory extends ActiveRecord { | ||||
|  | ||||
|     public static function tableName() { | ||||
|         return '{{%usernames_history}}'; | ||||
|     public static function tableName(): string { | ||||
|         return 'usernames_history'; | ||||
|     } | ||||
|  | ||||
|     public function behaviors() { | ||||
|     public function behaviors(): array { | ||||
|         return [ | ||||
|             [ | ||||
|                 'class' => TimestampBehavior::class, | ||||
| @@ -33,19 +36,17 @@ class UsernameHistory extends ActiveRecord { | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     public function rules() { | ||||
|         return []; | ||||
|     } | ||||
|  | ||||
|     public function getAccount() { | ||||
|     public function getAccount(): ActiveQuery { | ||||
|         return $this->hasOne(Account::class, ['id' => 'account_id']); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Find the username after the current of the account. | ||||
|      * | ||||
|      * @param int $afterTime | ||||
|      * @return UsernameHistory|null | ||||
|      */ | ||||
|     public function findNext(int $afterTime = null): ?self { | ||||
|     public function findNextOwnerUsername(int $afterTime = null): ?self { | ||||
|         return self::find() | ||||
|             ->andWhere(['account_id' => $this->account_id]) | ||||
|             ->andWhere(['>', 'applied_in', $afterTime ?: $this->applied_in]) | ||||
|   | ||||
| @@ -7,15 +7,12 @@ use common\models\Account; | ||||
| use Yii; | ||||
| use yii\queue\RetryableJobInterface; | ||||
|  | ||||
| class ClearAccountSessions implements RetryableJobInterface { | ||||
| final class ClearAccountSessions implements RetryableJobInterface { | ||||
|  | ||||
|     public $accountId; | ||||
|     private int $accountId; | ||||
|  | ||||
|     public static function createFromAccount(Account $account): self { | ||||
|         $result = new static(); | ||||
|         $result->accountId = $account->id; | ||||
|  | ||||
|         return $result; | ||||
|     public function __construct(int $accountId) { | ||||
|         $this->accountId = $accountId; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -40,23 +37,23 @@ class ClearAccountSessions implements RetryableJobInterface { | ||||
|      * @throws \Exception | ||||
|      */ | ||||
|     public function execute($queue): void { | ||||
|         $account = Account::findOne($this->accountId); | ||||
|         $account = Account::findOne(['id' => $this->accountId]); | ||||
|         if ($account === null) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         /** @var \common\models\AccountSession $authSession */ | ||||
|         foreach ($account->getSessions()->each(100, Yii::$app->unbufferedDb) as $authSession) { | ||||
|             /** @var \common\models\AccountSession $authSession */ | ||||
|             $authSession->delete(); | ||||
|         } | ||||
|  | ||||
|         /** @var \common\models\MinecraftAccessKey $key */ | ||||
|         foreach ($account->getMinecraftAccessKeys()->each(100, Yii::$app->unbufferedDb) as $key) { | ||||
|             /** @var \common\models\MinecraftAccessKey $key */ | ||||
|             $key->delete(); | ||||
|         } | ||||
|  | ||||
|         /** @var \common\models\OauthSession $oauthSession */ | ||||
|         foreach ($account->getOauthSessions()->each(100, Yii::$app->unbufferedDb) as $oauthSession) { | ||||
|             /** @var \common\models\OauthSession $oauthSession */ | ||||
|             $oauthSession->delete(); | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -7,26 +7,22 @@ use common\models\OauthClient; | ||||
| use Yii; | ||||
| use yii\queue\RetryableJobInterface; | ||||
|  | ||||
| class ClearOauthSessions implements RetryableJobInterface { | ||||
| final class ClearOauthSessions implements RetryableJobInterface { | ||||
|  | ||||
|     public string $clientId; | ||||
|  | ||||
|     /** | ||||
|      * @var int | ||||
|      * @var int|null unix timestamp, that allows to limit this task to clear only some old sessions | ||||
|      */ | ||||
|     public $clientId; | ||||
|     public ?int $notSince; | ||||
|  | ||||
|     /** | ||||
|      * @var int unix timestamp, that allows to limit this task to clear only some old sessions | ||||
|      */ | ||||
|     public $notSince; | ||||
|     public function __construct(string $clientId, int $notSince = null) { | ||||
|         $this->clientId = $clientId; | ||||
|         $this->notSince = $notSince; | ||||
|     } | ||||
|  | ||||
|     public static function createFromOauthClient(OauthClient $client, int $notSince = null): self { | ||||
|         $result = new static(); | ||||
|         $result->clientId = $client->id; | ||||
|         if ($notSince !== null) { | ||||
|             $result->notSince = $notSince; | ||||
|         } | ||||
|  | ||||
|         return $result; | ||||
|         return new self($client->id, $notSince); | ||||
|     } | ||||
|  | ||||
|     public function getTtr(): int { | ||||
|   | ||||
| @@ -8,39 +8,46 @@ use common\models\WebHook; | ||||
| use Yii; | ||||
| use yii\queue\RetryableJobInterface; | ||||
|  | ||||
| class CreateWebHooksDeliveries implements RetryableJobInterface { | ||||
| final class CreateWebHooksDeliveries implements RetryableJobInterface { | ||||
|  | ||||
|     /** | ||||
|      * @var string | ||||
|      */ | ||||
|     public $type; | ||||
|     public string $type; | ||||
|  | ||||
|     /** | ||||
|      * @var array | ||||
|      */ | ||||
|     public $payloads; | ||||
|     public array $payloads; | ||||
|  | ||||
|     public function __construct(string $type, array $payloads) { | ||||
|         $this->type = $type; | ||||
|         $this->payloads = $payloads; | ||||
|     } | ||||
|  | ||||
|     public static function createAccountEdit(Account $account, array $changedAttributes): self { | ||||
|         $result = new static(); | ||||
|         $result->type = 'account.edit'; | ||||
|         $result->payloads = [ | ||||
|         return new static('account.edit', [ | ||||
|             'id' => $account->id, | ||||
|             'uuid' => $account->uuid, | ||||
|             'username' => $account->username, | ||||
|             'email' => $account->email, | ||||
|             'lang' => $account->lang, | ||||
|             'isActive' => $account->status === Account::STATUS_ACTIVE, | ||||
|             'isDeleted' => $account->status === Account::STATUS_DELETED, | ||||
|             'registered' => date('c', (int)$account->created_at), | ||||
|             'changedAttributes' => $changedAttributes, | ||||
|         ]; | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|         return $result; | ||||
|     public static function createAccountDeletion(Account $account): self { | ||||
|         return new static('account.deletion', [ | ||||
|             'id' => $account->id, | ||||
|             'uuid' => $account->uuid, | ||||
|             'username' => $account->username, | ||||
|             'email' => $account->email, | ||||
|             'registered' => date('c', (int)$account->created_at), | ||||
|             'deleted' => date('c', (int)$account->deleted_at), | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return int time to reserve in seconds | ||||
|      */ | ||||
|     public function getTtr() { | ||||
|     public function getTtr(): int { | ||||
|         return 10; | ||||
|     } | ||||
|  | ||||
| @@ -50,14 +57,14 @@ class CreateWebHooksDeliveries implements RetryableJobInterface { | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function canRetry($attempt, $error) { | ||||
|     public function canRetry($attempt, $error): bool { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param \yii\queue\Queue $queue which pushed and is handling the job | ||||
|      */ | ||||
|     public function execute($queue) { | ||||
|     public function execute($queue): void { | ||||
|         /** @var WebHook[] $targets */ | ||||
|         $targets = WebHook::find() | ||||
|             ->joinWith('events e', false) | ||||
|   | ||||
							
								
								
									
										62
									
								
								common/tasks/DeleteAccount.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								common/tasks/DeleteAccount.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace common\tasks; | ||||
|  | ||||
| use common\models\Account; | ||||
| use Yii; | ||||
| use yii\queue\RetryableJobInterface; | ||||
|  | ||||
| final class DeleteAccount implements RetryableJobInterface { | ||||
|  | ||||
|     private int $accountId; | ||||
|  | ||||
|     public function __construct(int $accountId) { | ||||
|         $this->accountId = $accountId; | ||||
|     } | ||||
|  | ||||
|     public function getTtr(): int { | ||||
|         return PHP_INT_MAX; // Let it work as long as it needs to | ||||
|     } | ||||
|  | ||||
|     public function canRetry($attempt, $error): bool { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param \yii\queue\Queue $queue | ||||
|      * @throws \Throwable | ||||
|      */ | ||||
|     public function execute($queue): void { | ||||
|         $account = Account::findOne(['id' => $this->accountId]); | ||||
|         if ($account === null || $this->shouldAccountBeDeleted($account) === false) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         $transaction = Yii::$app->db->beginTransaction(); | ||||
|  | ||||
|         (new ClearAccountSessions($account->id))->execute($queue); | ||||
|         foreach ($account->oauthClients as $oauthClient) { | ||||
|             (new ClearOauthSessions($oauthClient->id))->execute($queue); | ||||
|         } | ||||
|  | ||||
|         /** @var \common\models\EmailActivation $emailActivation */ | ||||
|         foreach ($account->getEmailActivations()->each(100, Yii::$app->unbufferedDb) as $emailActivation) { | ||||
|             $emailActivation->delete(); | ||||
|         } | ||||
|  | ||||
|         /** @var \common\models\UsernameHistory $usernameHistoryEntry */ | ||||
|         foreach ($account->getUsernameHistory()->each(100, Yii::$app->unbufferedDb) as $usernameHistoryEntry) { | ||||
|             $usernameHistoryEntry->delete(); | ||||
|         } | ||||
|  | ||||
|         $account->delete(); | ||||
|  | ||||
|         $transaction->commit(); | ||||
|     } | ||||
|  | ||||
|     private function shouldAccountBeDeleted(Account $account): bool { | ||||
|         return $account->status === Account::STATUS_DELETED && $account->getDeleteAt()->subSecond()->isPast(); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										31
									
								
								common/tests/fixtures/data/accounts.php
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										31
									
								
								common/tests/fixtures/data/accounts.php
									
									
									
									
										vendored
									
									
								
							| @@ -13,6 +13,7 @@ return [ | ||||
|         'created_at' => 1451775316, | ||||
|         'updated_at' => 1451775316, | ||||
|         'password_changed_at' => 1451775316, | ||||
|         'deleted_at' => null, | ||||
|     ], | ||||
|     'user-with-old-password-type' => [ | ||||
|         'id' => 2, | ||||
| @@ -27,6 +28,7 @@ return [ | ||||
|         'created_at' => 1385225069, | ||||
|         'updated_at' => 1385225069, | ||||
|         'password_changed_at' => 1385225069, | ||||
|         'deleted_at' => null, | ||||
|     ], | ||||
|     'not-activated-account' => [ | ||||
|         'id' => 3, | ||||
| @@ -41,6 +43,7 @@ return [ | ||||
|         'created_at' => 1453146616, | ||||
|         'updated_at' => 1453146616, | ||||
|         'password_changed_at' => 1453146616, | ||||
|         'deleted_at' => null, | ||||
|     ], | ||||
|     'not-activated-account-with-expired-message' => [ | ||||
|         'id' => 4, | ||||
| @@ -55,6 +58,7 @@ return [ | ||||
|         'created_at' => 1457890086, | ||||
|         'updated_at' => 1457890086, | ||||
|         'password_changed_at' => 1457890086, | ||||
|         'deleted_at' => null, | ||||
|     ], | ||||
|     'account-with-fresh-forgot-password-message' => [ | ||||
|         'id' => 5, | ||||
| @@ -69,6 +73,7 @@ return [ | ||||
|         'created_at' => 1462891432, | ||||
|         'updated_at' => 1462891432, | ||||
|         'password_changed_at' => 1462891432, | ||||
|         'deleted_at' => null, | ||||
|     ], | ||||
|     'account-with-expired-forgot-password-message' => [ | ||||
|         'id' => 6, | ||||
| @@ -83,6 +88,7 @@ return [ | ||||
|         'created_at' => 1462891612, | ||||
|         'updated_at' => 1462891612, | ||||
|         'password_changed_at' => 1462891612, | ||||
|         'deleted_at' => null, | ||||
|     ], | ||||
|     'account-with-change-email-init-state' => [ | ||||
|         'id' => 7, | ||||
| @@ -97,6 +103,7 @@ return [ | ||||
|         'created_at' => 1463427287, | ||||
|         'updated_at' => 1463427287, | ||||
|         'password_changed_at' => 1463427287, | ||||
|         'deleted_at' => null, | ||||
|     ], | ||||
|     'account-with-change-email-finish-state' => [ | ||||
|         'id' => 8, | ||||
| @@ -111,6 +118,7 @@ return [ | ||||
|         'created_at' => 1463349615, | ||||
|         'updated_at' => 1463349615, | ||||
|         'password_changed_at' => 1463349615, | ||||
|         'deleted_at' => null, | ||||
|     ], | ||||
|     'account-with-old-rules-version' => [ | ||||
|         'id' => 9, | ||||
| @@ -125,6 +133,7 @@ return [ | ||||
|         'created_at' => 1470499952, | ||||
|         'updated_at' => 1470499952, | ||||
|         'password_changed_at' => 1470499952, | ||||
|         'deleted_at' => null, | ||||
|     ], | ||||
|     'banned-account' => [ | ||||
|         'id' => 10, | ||||
| @@ -139,6 +148,7 @@ return [ | ||||
|         'created_at' => 1472682343, | ||||
|         'updated_at' => 1472682343, | ||||
|         'password_changed_at' => 1472682343, | ||||
|         'deleted_at' => null, | ||||
|     ], | ||||
|     'account-with-usernames-history' => [ | ||||
|         'id' => 11, | ||||
| @@ -153,6 +163,7 @@ return [ | ||||
|         'created_at' => 1474404139, | ||||
|         'updated_at' => 1474404149, | ||||
|         'password_changed_at' => 1474404149, | ||||
|         'deleted_at' => null, | ||||
|     ], | ||||
|     'account-with-otp-secret' => [ | ||||
|         'id' => 12, | ||||
| @@ -169,6 +180,7 @@ return [ | ||||
|         'created_at' => 1485124615, | ||||
|         'updated_at' => 1485124615, | ||||
|         'password_changed_at' => 1485124615, | ||||
|         'deleted_at' => null, | ||||
|     ], | ||||
|     'account-with-enabled-otp' => [ | ||||
|         'id' => 13, | ||||
| @@ -185,6 +197,7 @@ return [ | ||||
|         'created_at' => 1485124685, | ||||
|         'updated_at' => 1485124685, | ||||
|         'password_changed_at' => 1485124685, | ||||
|         'deleted_at' => null, | ||||
|     ], | ||||
|     'account-with-two-oauth-clients' => [ | ||||
|         'id' => 14, | ||||
| @@ -201,5 +214,23 @@ return [ | ||||
|         'created_at' => 1519487320, | ||||
|         'updated_at' => 1519487320, | ||||
|         'password_changed_at' => 1519487320, | ||||
|         'deleted_at' => null, | ||||
|     ], | ||||
|     'deleted-account' => [ | ||||
|         'id' => 15, | ||||
|         'uuid' => '6383de63-8f85-4ed5-92b7-5401a1fa68cd', | ||||
|         'username' => 'DeletedAccount', | ||||
|         'email' => 'deleted-account@ely.by', | ||||
|         'password_hash' => '$2y$13$2rYkap5T6jG8z/mMK8a3Ou6aZxJcmAaTha6FEuujvHEmybSHRzW5e', # password_0 | ||||
|         'password_hash_strategy' => \common\models\Account::PASS_HASH_STRATEGY_YII2, | ||||
|         'lang' => 'ru', | ||||
|         'status' => \common\models\Account::STATUS_DELETED, | ||||
|         'rules_agreement_version' => \common\LATEST_RULES_VERSION, | ||||
|         'otp_secret' => null, | ||||
|         'is_otp_enabled' => false, | ||||
|         'created_at' => 1591893532, | ||||
|         'updated_at' => 1591893532, | ||||
|         'password_changed_at' => 1591893532, | ||||
|         'deleted_at' => time(), | ||||
|     ], | ||||
| ]; | ||||
|   | ||||
| @@ -21,4 +21,11 @@ return [ | ||||
|         'created_at' => time() - 10, | ||||
|         'updated_at' => time() - 10, | ||||
|     ], | ||||
|     'deleted-token' => [ | ||||
|         'access_token' => '239ba889-7020-4383-8d99-cd8c8aab4a2f', | ||||
|         'client_token' => '47443658-4ff8-45e7-b33e-dc8915ab6421', | ||||
|         'account_id' => 15, | ||||
|         'created_at' => time() - 10, | ||||
|         'updated_at' => time() - 10, | ||||
|     ], | ||||
| ]; | ||||
|   | ||||
| @@ -24,4 +24,10 @@ return [ | ||||
|         'account_id' => 11, | ||||
|         'applied_in' => 1474404143, | ||||
|     ], | ||||
|     [ | ||||
|         'id' => 5, | ||||
|         'username' => 'DeletedAccount', | ||||
|         'account_id' => 15, | ||||
|         'applied_in' => 1591893532, | ||||
|     ], | ||||
| ]; | ||||
|   | ||||
| @@ -133,7 +133,19 @@ class AccountTest extends TestCase { | ||||
|         /** @var CreateWebHooksDeliveries $job */ | ||||
|         $job = $this->tester->grabLastQueuedJob(); | ||||
|         $this->assertInstanceOf(CreateWebHooksDeliveries::class, $job); | ||||
|         $this->assertSame($job->payloads['changedAttributes'], $changedAttributes); | ||||
|         $this->assertSame('account.edit', $job->type); | ||||
|         $this->assertSame($changedAttributes, $job->payloads['changedAttributes']); | ||||
|     } | ||||
|  | ||||
|     public function testAfterDeletePushEvent() { | ||||
|         $account = new Account(); | ||||
|         $account->id = 1; | ||||
|         $account->afterDelete(); | ||||
|         /** @var CreateWebHooksDeliveries $job */ | ||||
|         $job = $this->tester->grabLastQueuedJob(); | ||||
|         $this->assertInstanceOf(CreateWebHooksDeliveries::class, $job); | ||||
|         $this->assertSame('account.deletion', $job->type); | ||||
|         $this->assertSame(1, $job->payloads['id']); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -3,7 +3,6 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace common\tests\unit\tasks; | ||||
|  | ||||
| use common\models\Account; | ||||
| use common\tasks\ClearAccountSessions; | ||||
| use common\tests\fixtures; | ||||
| use common\tests\unit\TestCase; | ||||
| @@ -23,18 +22,10 @@ class ClearAccountSessionsTest extends TestCase { | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     public function testCreateFromAccount() { | ||||
|         $account = new Account(); | ||||
|         $account->id = 123; | ||||
|         $task = ClearAccountSessions::createFromAccount($account); | ||||
|         $this->assertSame(123, $task->accountId); | ||||
|     } | ||||
|  | ||||
|     public function testExecute() { | ||||
|         /** @var \common\models\Account $bannedAccount */ | ||||
|         $bannedAccount = $this->tester->grabFixture('accounts', 'banned-account'); | ||||
|         $task = new ClearAccountSessions(); | ||||
|         $task->accountId = $bannedAccount->id; | ||||
|         $task = new ClearAccountSessions($bannedAccount->id); | ||||
|         $task->execute($this->createMock(Queue::class)); | ||||
|         $this->assertEmpty($bannedAccount->sessions); | ||||
|         $this->assertEmpty($bannedAccount->minecraftAccessKeys); | ||||
|   | ||||
| @@ -1,4 +1,6 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace common\tests\unit\tasks; | ||||
|  | ||||
| use common\models\OauthClient; | ||||
| @@ -33,22 +35,18 @@ class ClearOauthSessionsTest extends TestCase { | ||||
|     } | ||||
|  | ||||
|     public function testExecute() { | ||||
|         $task = new ClearOauthSessions(); | ||||
|         $task->clientId = 'deleted-oauth-client-with-sessions'; | ||||
|         $task->notSince = 1519510065; | ||||
|         $task = new ClearOauthSessions('deleted-oauth-client-with-sessions', 1519510065); | ||||
|         $task->execute($this->createMock(Queue::class)); | ||||
|  | ||||
|         $this->assertFalse(OauthSession::find()->andWhere(['legacy_id' => 3])->exists()); | ||||
|         $this->assertTrue(OauthSession::find()->andWhere(['legacy_id' => 4])->exists()); | ||||
|  | ||||
|         $task = new ClearOauthSessions(); | ||||
|         $task->clientId = 'deleted-oauth-client-with-sessions'; | ||||
|         $task = new ClearOauthSessions('deleted-oauth-client-with-sessions'); | ||||
|         $task->execute($this->createMock(Queue::class)); | ||||
|  | ||||
|         $this->assertFalse(OauthSession::find()->andWhere(['legacy_id' => 4])->exists()); | ||||
|  | ||||
|         $task = new ClearOauthSessions(); | ||||
|         $task->clientId = 'some-not-exists-client-id'; | ||||
|         $task = new ClearOauthSessions('some-not-exists-client-id'); | ||||
|         $task->execute($this->createMock(Queue::class)); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -52,9 +52,7 @@ class CreateWebHooksDeliveriesTest extends TestCase { | ||||
|     } | ||||
|  | ||||
|     public function testExecute() { | ||||
|         $task = new CreateWebHooksDeliveries(); | ||||
|         $task->type = 'account.edit'; | ||||
|         $task->payloads = [ | ||||
|         $task = new CreateWebHooksDeliveries('account.edit', [ | ||||
|             'id' => 123, | ||||
|             'uuid' => 'afc8dc7a-4bbf-4d3a-8699-68890088cf84', | ||||
|             'username' => 'mock-username', | ||||
| @@ -68,7 +66,7 @@ class CreateWebHooksDeliveriesTest extends TestCase { | ||||
|                 'email' => 'old-email@ely.by', | ||||
|                 'status' => 0, | ||||
|             ], | ||||
|         ]; | ||||
|         ]); | ||||
|         $task->execute($this->createMock(Queue::class)); | ||||
|         /** @var DeliveryWebHook[] $tasks */ | ||||
|         $tasks = $this->tester->grabQueueJobs(); | ||||
|   | ||||
							
								
								
									
										90
									
								
								common/tests/unit/tasks/DeleteAccountTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								common/tests/unit/tasks/DeleteAccountTest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | ||||
| <?php | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace common\tests\unit\tasks; | ||||
|  | ||||
| use common\models\Account; | ||||
| use common\tasks\DeleteAccount; | ||||
| use common\tests\fixtures; | ||||
| use common\tests\unit\TestCase; | ||||
| use yii\queue\Queue; | ||||
|  | ||||
| /** | ||||
|  * @covers \common\tasks\DeleteAccount | ||||
|  */ | ||||
| class DeleteAccountTest extends TestCase { | ||||
|  | ||||
|     public function _fixtures(): array { | ||||
|         return [ | ||||
|             'accounts' => fixtures\AccountFixture::class, | ||||
|             'authSessions' => fixtures\AccountSessionFixture::class, | ||||
|             'emailActivations' => fixtures\EmailActivationFixture::class, | ||||
|             'minecraftAccessKeys' => fixtures\MinecraftAccessKeyFixture::class, | ||||
|             'usernamesHistory' => fixtures\UsernameHistoryFixture::class, | ||||
|             'oauthClients' => fixtures\OauthClientFixture::class, | ||||
|             'oauthSessions' => fixtures\OauthSessionFixture::class, | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     public function testExecute() { | ||||
|         /** @var Account $account */ | ||||
|         $account = $this->tester->grabFixture('accounts', 'admin'); | ||||
|         $account->status = Account::STATUS_DELETED; | ||||
|         $account->deleted_at = time() - 60 * 60 * 24 * 7; | ||||
|         $account->save(); | ||||
|  | ||||
|         $task = new DeleteAccount($account->id); | ||||
|         $task->execute($this->createMock(Queue::class)); | ||||
|         $this->assertEmpty($account->emailActivations); | ||||
|         $this->assertEmpty($account->sessions); | ||||
|         $this->assertEmpty($account->minecraftAccessKeys); | ||||
|         $this->assertEmpty($account->oauthSessions); | ||||
|         $this->assertEmpty($account->usernameHistory); | ||||
|         $this->assertEmpty($account->oauthClients); | ||||
|         $this->assertFalse($account->refresh()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * When a user restores his account back, the task doesn't removed | ||||
|      * @throws \Throwable | ||||
|      */ | ||||
|     public function testExecuteOnNotDeletedAccount() { | ||||
|         /** @var Account $account */ | ||||
|         $account = $this->tester->grabFixture('accounts', 'admin'); | ||||
|         // By default, this account is active | ||||
|  | ||||
|         $task = new DeleteAccount($account->id); | ||||
|         $task->execute($this->createMock(Queue::class)); | ||||
|         $this->assertNotEmpty($account->emailActivations); | ||||
|         $this->assertNotEmpty($account->sessions); | ||||
|         $this->assertNotEmpty($account->minecraftAccessKeys); | ||||
|         $this->assertNotEmpty($account->oauthSessions); | ||||
|         $this->assertNotEmpty($account->usernameHistory); | ||||
|         $this->assertNotEmpty($account->oauthClients); | ||||
|         $this->assertTrue($account->refresh()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * User also might delete his account, restore it and delete again. | ||||
|      * For each deletion the job will be created, so assert, that job for restored deleting will not work | ||||
|      * @throws \Throwable | ||||
|      */ | ||||
|     public function testExecuteOnDeletedAccountWhichWasRestoredAndThenDeletedAgain() { | ||||
|         /** @var Account $account */ | ||||
|         $account = $this->tester->grabFixture('accounts', 'admin'); | ||||
|         $account->status = Account::STATUS_DELETED; | ||||
|         $account->deleted_at = time() - 60 * 60 * 24 * 2; | ||||
|         $account->save(); | ||||
|  | ||||
|         $task = new DeleteAccount($account->id); | ||||
|         $task->execute($this->createMock(Queue::class)); | ||||
|         $this->assertNotEmpty($account->emailActivations); | ||||
|         $this->assertNotEmpty($account->sessions); | ||||
|         $this->assertNotEmpty($account->minecraftAccessKeys); | ||||
|         $this->assertNotEmpty($account->oauthSessions); | ||||
|         $this->assertNotEmpty($account->usernameHistory); | ||||
|         $this->assertNotEmpty($account->oauthClients); | ||||
|         $this->assertTrue($account->refresh()); | ||||
|     } | ||||
|  | ||||
| } | ||||
		Reference in New Issue
	
	Block a user