diff --git a/api/models/authentication/ConfirmEmailForm.php b/api/models/authentication/ConfirmEmailForm.php index d34113e..a568308 100644 --- a/api/models/authentication/ConfirmEmailForm.php +++ b/api/models/authentication/ConfirmEmailForm.php @@ -31,7 +31,7 @@ class ConfirmEmailForm extends KeyConfirmationForm { throw new ErrorException('Unable remove activation key.'); } - if (!$account->save(false)) { + if (!$account->save()) { throw new ErrorException('Unable activate user account.'); } diff --git a/api/models/authentication/LoginForm.php b/api/models/authentication/LoginForm.php index d56e7b6..c1ad5e6 100644 --- a/api/models/authentication/LoginForm.php +++ b/api/models/authentication/LoginForm.php @@ -77,7 +77,7 @@ class LoginForm extends ApiForm { } $account = $this->getAccount(); - if ($account->password_hash_strategy === Account::PASS_HASH_STRATEGY_OLD_ELY) { + if ($account->password_hash_strategy !== Account::PASS_HASH_STRATEGY_YII2) { $account->setPassword($this->password); $account->save(); } diff --git a/api/models/authentication/RecoverPasswordForm.php b/api/models/authentication/RecoverPasswordForm.php index 243638a..867268c 100644 --- a/api/models/authentication/RecoverPasswordForm.php +++ b/api/models/authentication/RecoverPasswordForm.php @@ -5,7 +5,7 @@ use api\models\AccountIdentity; use api\models\base\KeyConfirmationForm; use common\helpers\Error as E; use common\models\EmailActivation; -use common\validators\PasswordValidate; +use common\validators\PasswordValidator; use Yii; use yii\base\ErrorException; @@ -19,7 +19,7 @@ class RecoverPasswordForm extends KeyConfirmationForm { return array_merge(parent::rules(), [ ['newPassword', 'required', 'message' => E::NEW_PASSWORD_REQUIRED], ['newRePassword', 'required', 'message' => E::NEW_RE_PASSWORD_REQUIRED], - ['newPassword', PasswordValidate::class], + ['newPassword', PasswordValidator::class], ['newRePassword', 'validatePasswordAndRePasswordMatch'], ]); } diff --git a/api/models/authentication/RegistrationForm.php b/api/models/authentication/RegistrationForm.php index fea1245..c39a249 100644 --- a/api/models/authentication/RegistrationForm.php +++ b/api/models/authentication/RegistrationForm.php @@ -9,8 +9,10 @@ use common\models\Account; use common\models\confirmations\RegistrationConfirmation; use common\models\EmailActivation; use common\models\UsernameHistory; +use common\validators\EmailValidator; use common\validators\LanguageValidator; -use common\validators\PasswordValidate; +use common\validators\PasswordValidator; +use common\validators\UsernameValidator; use Exception; use Ramsey\Uuid\Uuid; use Yii; @@ -40,12 +42,12 @@ class RegistrationForm extends ApiForm { ['captcha', ReCaptchaValidator::class], ['rulesAgreement', 'required', 'message' => E::RULES_AGREEMENT_REQUIRED], - ['username', 'validateUsername', 'skipOnEmpty' => false], - ['email', 'validateEmail', 'skipOnEmpty' => false], + ['username', UsernameValidator::class], + ['email', EmailValidator::class], ['password', 'required', 'message' => E::PASSWORD_REQUIRED], ['rePassword', 'required', 'message' => E::RE_PASSWORD_REQUIRED], - ['password', PasswordValidate::class], + ['password', PasswordValidator::class], ['rePassword', 'validatePasswordAndRePasswordMatch'], ['lang', LanguageValidator::class], @@ -53,22 +55,6 @@ class RegistrationForm extends ApiForm { ]; } - public function validateUsername() { - $account = new Account(); - $account->username = $this->username; - if (!$account->validate(['username'])) { - $this->addErrors($account->getErrors()); - } - } - - public function validateEmail() { - $account = new Account(); - $account->email = $this->email; - if (!$account->validate(['email'])) { - $this->addErrors($account->getErrors()); - } - } - public function validatePasswordAndRePasswordMatch($attribute) { if (!$this->hasErrors()) { if ($this->password !== $this->rePassword) { @@ -97,7 +83,7 @@ class RegistrationForm extends ApiForm { $account->status = Account::STATUS_REGISTERED; $account->rules_agreement_version = LATEST_RULES_VERSION; $account->setRegistrationIp(Yii::$app->request->getUserIP()); - if (!$account->save(false)) { + if (!$account->save()) { throw new ErrorException('Account not created.'); } diff --git a/api/models/profile/AcceptRulesForm.php b/api/models/profile/AcceptRulesForm.php index 1e25381..05785f0 100644 --- a/api/models/profile/AcceptRulesForm.php +++ b/api/models/profile/AcceptRulesForm.php @@ -21,7 +21,7 @@ class AcceptRulesForm extends ApiForm { public function agreeWithLatestRules() : bool { $account = $this->getAccount(); $account->rules_agreement_version = LATEST_RULES_VERSION; - if (!$account->save(false)) { + if (!$account->save()) { throw new ErrorException('Cannot set user rules version'); } diff --git a/api/models/profile/ChangeEmail/NewEmailForm.php b/api/models/profile/ChangeEmail/NewEmailForm.php index c87ba6c..9dd8b76 100644 --- a/api/models/profile/ChangeEmail/NewEmailForm.php +++ b/api/models/profile/ChangeEmail/NewEmailForm.php @@ -2,10 +2,10 @@ namespace api\models\profile\ChangeEmail; use api\models\base\KeyConfirmationForm; -use common\helpers\Error as E; use common\models\Account; use common\models\confirmations\NewEmailConfirmation; use common\models\EmailActivation; +use common\validators\EmailValidator; use Yii; use yii\base\ErrorException; use yii\base\Exception; @@ -25,26 +25,14 @@ class NewEmailForm extends KeyConfirmationForm { parent::__construct($config); } - /** - * @return Account - */ - public function getAccount() { - return $this->account; - } - public function rules() { return array_merge(parent::rules(), [ - ['email', 'required', 'message' => E::EMAIL_REQUIRED], - ['email', 'validateEmail'], + ['email', EmailValidator::class], ]); } - public function validateEmail() { - $account = new Account(); - $account->email = $this->email; - if (!$account->validate(['email'])) { - $this->addErrors($account->getErrors()); - } + public function getAccount() : Account { + return $this->account; } public function sendNewEmailConfirmation() { diff --git a/api/models/profile/ChangeLanguageForm.php b/api/models/profile/ChangeLanguageForm.php index 391687a..fa44d26 100644 --- a/api/models/profile/ChangeLanguageForm.php +++ b/api/models/profile/ChangeLanguageForm.php @@ -31,7 +31,7 @@ class ChangeLanguageForm extends ApiForm { $account = $this->getAccount(); $account->lang = $this->lang; - if (!$account->save(false)) { + if (!$account->save()) { throw new ErrorException('Cannot change user language'); } diff --git a/api/models/profile/ChangePasswordForm.php b/api/models/profile/ChangePasswordForm.php index ecc7587..2d4741b 100644 --- a/api/models/profile/ChangePasswordForm.php +++ b/api/models/profile/ChangePasswordForm.php @@ -5,7 +5,7 @@ use api\models\base\ApiForm; use api\validators\PasswordRequiredValidator; use common\helpers\Error as E; use common\models\Account; -use common\validators\PasswordValidate; +use common\validators\PasswordValidator; use Yii; use yii\base\ErrorException; use yii\helpers\ArrayHelper; @@ -37,7 +37,7 @@ class ChangePasswordForm extends ApiForm { return ArrayHelper::merge(parent::rules(), [ ['newPassword', 'required', 'message' => E::NEW_PASSWORD_REQUIRED], ['newRePassword', 'required', 'message' => E::NEW_RE_PASSWORD_REQUIRED], - ['newPassword', PasswordValidate::class], + ['newPassword', PasswordValidator::class], ['newRePassword', 'validatePasswordAndRePasswordMatch'], ['logoutAll', 'boolean'], ['password', PasswordRequiredValidator::class, 'account' => $this->_account], @@ -77,7 +77,7 @@ class ChangePasswordForm extends ApiForm { } } - if (!$account->save(false)) { + if (!$account->save()) { throw new ErrorException('Cannot save user model'); } diff --git a/api/models/profile/ChangeUsernameForm.php b/api/models/profile/ChangeUsernameForm.php index 81f7957..fb0a931 100644 --- a/api/models/profile/ChangeUsernameForm.php +++ b/api/models/profile/ChangeUsernameForm.php @@ -4,11 +4,10 @@ namespace api\models\profile; use api\models\AccountIdentity; use api\models\base\ApiForm; use api\validators\PasswordRequiredValidator; -use common\helpers\Error; use common\helpers\Amqp; -use common\models\Account; use common\models\amqp\UsernameChanged; use common\models\UsernameHistory; +use common\validators\UsernameValidator; use Exception; use PhpAmqpLib\Message\AMQPMessage; use Yii; @@ -22,23 +21,13 @@ class ChangeUsernameForm extends ApiForm { public function rules() { return [ - ['username', 'required', 'message' => Error::USERNAME_REQUIRED], - ['username', 'validateUsername'], + ['username', UsernameValidator::class, 'accountCallback' => function() { + return $this->getAccount()->id; + }], ['password', PasswordRequiredValidator::class], ]; } - public function validateUsername($attribute) { - $account = new Account(); - $account->id = $this->getAccount()->id; - $account->username = $this->$attribute; - // Это чтобы unique validator учёл, что ник может быть забит на текущий аккаунт - $account->setIsNewRecord(false); - if (!$account->validate(['username'])) { - $this->addErrors($account->getErrors()); - } - } - public function change() { if (!$this->validate()) { return false; @@ -49,7 +38,7 @@ class ChangeUsernameForm extends ApiForm { $oldNickname = $account->username; try { $account->username = $this->username; - if (!$account->save(false)) { + if (!$account->save()) { throw new ErrorException('Cannot save account model with new username'); } diff --git a/common/models/Account.php b/common/models/Account.php index 4c94f08..d61acf6 100644 --- a/common/models/Account.php +++ b/common/models/Account.php @@ -1,9 +1,7 @@ 'trim'], - [['username'], 'required', 'message' => E::USERNAME_REQUIRED], - [['username'], 'string', 'min' => 3, 'max' => 21, - 'tooShort' => E::USERNAME_TOO_SHORT, - 'tooLong' => E::USERNAME_TOO_LONG, - ], - [['username'], 'match', 'pattern' => '/^[\p{L}\d-_\.!?#$%^&*()\[\]:;]+$/u', - 'message' => E::USERNAME_INVALID, - ], - [['username'], 'unique', 'message' => E::USERNAME_NOT_AVAILABLE], - - [['email'], 'filter', 'filter' => 'trim'], - [['email'], 'required', 'message' => E::EMAIL_REQUIRED], - [['email'], 'string', 'max' => 255, 'tooLong' => E::EMAIL_TOO_LONG], - [['email'], 'email', 'checkDNS' => true, 'enableIDN' => true, 'message' => E::EMAIL_INVALID], - [['email'], TempmailValidator::class, 'message' => E::EMAIL_IS_TEMPMAIL], - [['email'], 'unique', 'message' => E::EMAIL_NOT_AVAILABLE], - ]; - } - /** * Validates password * diff --git a/common/validators/EmailValidator.php b/common/validators/EmailValidator.php new file mode 100644 index 0000000..4309352 --- /dev/null +++ b/common/validators/EmailValidator.php @@ -0,0 +1,64 @@ + 'trim']); + + $required = new validators\RequiredValidator(); + $required->message = E::EMAIL_REQUIRED; + + $length = new validators\StringValidator(); + $length->max = 255; + $length->tooLong = E::EMAIL_TOO_LONG; + + $email = new validators\EmailValidator(); + $email->checkDNS = true; + $email->enableIDN = true; + $email->message = E::EMAIL_INVALID; + + $tempmail = new TempmailValidator(); + $tempmail->message = E::EMAIL_IS_TEMPMAIL; + + $unique = new validators\UniqueValidator(); + $unique->message = E::EMAIL_NOT_AVAILABLE; + $unique->targetClass = Account::class; + $unique->targetAttribute = 'email'; + if ($this->accountCallback !== null) { + $unique->filter = function(QueryInterface $query) { + $query->andWhere(['NOT', ['id' => ($this->accountCallback)()]]); + }; + } + + $this->executeValidation($filter, $model, $attribute) && + $this->executeValidation($required, $model, $attribute) && + $this->executeValidation($length, $model, $attribute) && + $this->executeValidation($email, $model, $attribute) && + $this->executeValidation($tempmail, $model, $attribute) && + $this->executeValidation($unique, $model, $attribute); + } + + protected function executeValidation(Validator $validator, Model $model, string $attribute) { + $validator->validateAttribute($model, $attribute); + + return !$model->hasErrors($attribute); + } + +} diff --git a/common/validators/PasswordValidate.php b/common/validators/PasswordValidator.php similarity index 86% rename from common/validators/PasswordValidate.php rename to common/validators/PasswordValidator.php index b4391f7..2b9c047 100644 --- a/common/validators/PasswordValidate.php +++ b/common/validators/PasswordValidator.php @@ -7,7 +7,7 @@ use yii\validators\StringValidator; /** * Класс должен реализовывать в себе все критерии валидации пароля пользователя */ -class PasswordValidate extends StringValidator { +class PasswordValidator extends StringValidator { public $min = 8; diff --git a/common/validators/UsernameValidator.php b/common/validators/UsernameValidator.php new file mode 100644 index 0000000..f5d8895 --- /dev/null +++ b/common/validators/UsernameValidator.php @@ -0,0 +1,59 @@ + 'trim']); + + $required = new validators\RequiredValidator(); + $required->message = E::USERNAME_REQUIRED; + + $length = new validators\StringValidator(); + $length->min = 3; + $length->max = 21; + $length->tooShort = E::USERNAME_TOO_SHORT; + $length->tooLong = E::USERNAME_TOO_LONG; + + $pattern = new validators\RegularExpressionValidator(['pattern' => '/^[\p{L}\d-_\.!?#$%^&*()\[\]:;]+$/u']); + $pattern->message = E::USERNAME_INVALID; + + $unique = new validators\UniqueValidator(); + $unique->message = E::USERNAME_NOT_AVAILABLE; + $unique->targetClass = Account::class; + $unique->targetAttribute = 'username'; + if ($this->accountCallback !== null) { + $unique->filter = function(QueryInterface $query) { + $query->andWhere(['NOT', ['id' => ($this->accountCallback)()]]); + }; + } + + $this->executeValidation($filter, $model, $attribute) && + $this->executeValidation($required, $model, $attribute) && + $this->executeValidation($length, $model, $attribute) && + $this->executeValidation($pattern, $model, $attribute) && + $this->executeValidation($unique, $model, $attribute); + } + + protected function executeValidation(Validator $validator, Model $model, string $attribute) { + $validator->validateAttribute($model, $attribute); + + return !$model->hasErrors($attribute); + } + +} diff --git a/tests/codeception/api/unit/models/profile/ChangeUsernameFormTest.php b/tests/codeception/api/unit/models/profile/ChangeUsernameFormTest.php index 75834aa..a41dd75 100644 --- a/tests/codeception/api/unit/models/profile/ChangeUsernameFormTest.php +++ b/tests/codeception/api/unit/models/profile/ChangeUsernameFormTest.php @@ -67,26 +67,6 @@ class ChangeUsernameFormTest extends TestCase { ); } - public function testValidateUsername() { - $this->specify('error.username_not_available expected if username is already taken', function() { - $model = new ChangeUsernameForm([ - 'password' => 'password_0', - 'username' => 'Jon', - ]); - $model->validateUsername('username'); - expect($model->getErrors('username'))->equals(['error.username_not_available']); - }); - - $this->specify('error.username_not_available is NOT expected if username is already taken by CURRENT user', function() { - $model = new ChangeUsernameForm([ - 'password' => 'password_0', - 'username' => $this->tester->grabFixture('accounts', 'admin')['username'], - ]); - $model->validateUsername('username'); - expect($model->getErrors('username'))->isEmpty(); - }); - } - public function testCreateTask() { $model = new ChangeUsernameForm(); $model->createEventTask('1', 'test1', 'test'); diff --git a/tests/codeception/common/unit/models/AccountTest.php b/tests/codeception/common/unit/models/AccountTest.php index a2078dc..2b791e3 100644 --- a/tests/codeception/common/unit/models/AccountTest.php +++ b/tests/codeception/common/unit/models/AccountTest.php @@ -4,8 +4,6 @@ namespace tests\codeception\common\unit\models; use Codeception\Specify; use common\components\UserPass; use common\models\Account; -use tests\codeception\common\fixtures\AccountFixture; -use tests\codeception\common\fixtures\MojangUsernameFixture; use tests\codeception\common\unit\TestCase; use Yii; use const common\LATEST_RULES_VERSION; @@ -13,109 +11,12 @@ use const common\LATEST_RULES_VERSION; class AccountTest extends TestCase { use Specify; - public function _fixtures() { - return [ - 'accounts' => AccountFixture::class, - 'mojangAccounts' => MojangUsernameFixture::class, - ]; - } - - public function testValidateUsername() { - $this->specify('username required', function() { - $model = new Account(['username' => null]); - expect($model->validate(['username']))->false(); - expect($model->getErrors('username'))->equals(['error.username_required']); - }); - - $this->specify('username should be at least 3 symbols length', function() { - $model = new Account(['username' => 'at']); - expect($model->validate(['username']))->false(); - expect($model->getErrors('username'))->equals(['error.username_too_short']); - }); - - $this->specify('username should be not more than 21 symbols length', function() { - $model = new Account(['username' => 'erickskrauch_erickskrauch']); - expect($model->validate(['username']))->false(); - expect($model->getErrors('username'))->equals(['error.username_too_long']); - }); - - $this->specify('username can contain many cool symbols', function() { - $shouldBeValid = [ - 'русский_ник', 'русский_ник_на_грани!', 'numbers1132', '*__*-Stars-*__*', '1-_.!?#$%^&*()[]', - '[ESP]Эрик', 'Свят_помидор;', 'зроблена_ў_беларусі:)', - ]; - foreach($shouldBeValid as $nickname) { - $model = new Account(['username' => $nickname]); - expect($nickname . ' passed validation', $model->validate(['username']))->true(); - expect($model->getErrors('username'))->isEmpty(); - } - }); - - $this->specify('username cannot contain some symbols', function() { - $shouldBeInvalid = [ - 'nick@name', 'spaced nick', - ]; - foreach($shouldBeInvalid as $nickname) { - $model = new Account(['username' => $nickname]); - expect($nickname . ' fail validation', $model->validate('username'))->false(); - expect($model->getErrors('username'))->equals(['error.username_invalid']); - } - }); - - $this->specify('username should be unique', function() { - $model = new Account(); - $model->username = $this->tester->grabFixture('accounts', 'admin')['username']; - expect($model->validate('username'))->false(); - expect($model->getErrors('username'))->equals(['error.username_not_available']); - }); - } - - public function testValidateEmail() { - // TODO: пропускать этот тест, если падает ошибка с недостпуностью интернет соединения - $this->specify('email required', function() { - $model = new Account(['email' => null]); - expect($model->validate(['email']))->false(); - expect($model->getErrors('email'))->equals(['error.email_required']); - }); - - $this->specify('email should be not more 255 symbols (I hope it\'s impossible to register)', function() { - $model = new Account([ - 'email' => 'emailemailemailemailemailemailemailemailemailemailemailemailemailemailemailemailemail' . - 'emailemailemailemailemailemailemailemailemailemailemailemailemailemailemailemailemail' . - 'emailemailemailemailemailemailemailemailemailemailemailemailemailemailemailemailemail' . - 'emailemail', // = 256 symbols - ]); - expect($model->validate(['email']))->false(); - expect($model->getErrors('email'))->equals(['error.email_too_long']); - }); - - $this->specify('email should be email (it test can fail, if you don\'t have internet connection)', function() { - $model = new Account(['email' => 'invalid_email']); - expect($model->validate(['email']))->false(); - expect($model->getErrors('email'))->equals(['error.email_invalid']); - }); - - $this->specify('email should be not tempmail', function() { - $model = new Account(['email' => 'ibrpycwyjdnt@dropmail.me']); - expect($model->validate(['email']))->false(); - expect($model->getErrors('email'))->equals(['error.email_is_tempmail']); - }); - - $this->specify('email should be unique', function() { - $model = new Account(['email' => $this->tester->grabFixture('accounts', 'admin')['email']]); - expect($model->validate('email'))->false(); - expect($model->getErrors('email'))->equals(['error.email_not_available']); - }); - } - public function testSetPassword() { - $this->specify('calling method should change password and set latest password hash algorithm', function() { - $model = new Account(); - $model->setPassword('12345678'); - expect('hash should be set', $model->password_hash)->notEmpty(); - expect('validation should be passed', $model->validatePassword('12345678'))->true(); - expect('latest password hash should be used', $model->password_hash_strategy)->equals(Account::PASS_HASH_STRATEGY_YII2); - }); + $model = new Account(); + $model->setPassword('12345678'); + $this->assertNotEmpty($model->password_hash, 'hash should be set'); + $this->assertTrue($model->validatePassword('12345678'), 'validation should be passed'); + $this->assertEquals(Account::PASS_HASH_STRATEGY_YII2, $model->password_hash_strategy, 'latest password hash should be used'); } public function testValidatePassword() { diff --git a/tests/codeception/common/unit/validators/EmailValidatorTest.php b/tests/codeception/common/unit/validators/EmailValidatorTest.php new file mode 100644 index 0000000..4bf7256 --- /dev/null +++ b/tests/codeception/common/unit/validators/EmailValidatorTest.php @@ -0,0 +1,109 @@ +validator = new EmailValidator(); + } + + public function testValidateAttributeRequired() { + $model = $this->createModel(''); + $this->validator->validateAttribute($model, 'field'); + $this->assertEquals(['error.email_required'], $model->getErrors('field')); + + $model = $this->createModel('email'); + $this->validator->validateAttribute($model, 'field'); + $this->assertNotEquals(['error.email_required'], $model->getErrors('field')); + } + + public function testValidateAttributeLength() { + $model = $this->createModel( + 'emailemailemailemailemailemailemailemailemailemailemailemailemailemailemailemailemail' . + 'emailemailemailemailemailemailemailemailemailemailemailemailemailemailemailemailemail' . + 'emailemailemailemailemailemailemailemailemailemailemailemailemailemailemailemailemail' . + '@gmail.com' // = 256 symbols + ); + $this->validator->validateAttribute($model, 'field'); + $this->assertEquals(['error.email_too_long'], $model->getErrors('field')); + + $model = $this->createModel('some-email@gmail.com'); + $this->validator->validateAttribute($model, 'field'); + $this->assertNotEquals(['error.email_too_long'], $model->getErrors('field')); + } + + public function testValidateAttributeEmail() { + $model = $this->createModel('non-email'); + $this->validator->validateAttribute($model, 'field'); + $this->assertEquals(['error.email_invalid'], $model->getErrors('field')); + + $model = $this->createModel('non-email@etot-domen-ne-suschestrvyet.de'); + $this->validator->validateAttribute($model, 'field'); + $this->assertEquals(['error.email_invalid'], $model->getErrors('field')); + + $model = $this->createModel('valid-email@gmail.com'); + $this->validator->validateAttribute($model, 'field'); + $this->assertNotEquals(['error.email_invalid'], $model->getErrors('field')); + } + + public function testValidateAttributeTempmail() { + $model = $this->createModel('ibrpycwyjdnt@dropmail.me'); + $this->validator->validateAttribute($model, 'field'); + $this->assertEquals(['error.email_is_tempmail'], $model->getErrors('field')); + + $model = $this->createModel('valid-email@gmail.com'); + $this->validator->validateAttribute($model, 'field'); + $this->assertNotEquals(['error.email_is_tempmail'], $model->getErrors('field')); + } + + public function testValidateAttributeUnique() { + $this->tester->haveFixtures([ + 'accounts' => AccountFixture::class, + ]); + + /** @var \common\models\Account $accountFixture */ + $accountFixture = $this->tester->grabFixture('accounts', 'admin'); + + $model = $this->createModel($accountFixture->email); + $this->validator->validateAttribute($model, 'field'); + $this->assertEquals(['error.email_not_available'], $model->getErrors('field')); + + $model = $this->createModel($accountFixture->email); + $this->validator->accountCallback = function() use ($accountFixture) { + return $accountFixture->id; + }; + $this->validator->validateAttribute($model, 'field'); + $this->assertNotEquals(['error.email_not_available'], $model->getErrors('field')); + $this->validator->accountCallback = null; + + $model = $this->createModel('some-unique-email@gmail.com'); + $this->validator->validateAttribute($model, 'field'); + $this->assertNotEquals(['error.email_not_available'], $model->getErrors('field')); + } + + /** + * @param string $fieldValue + * @return Model + */ + private function createModel(string $fieldValue) : Model { + $class = new class extends Model { + public $field; + }; + + $class->field = $fieldValue; + + return $class; + } + +} diff --git a/tests/codeception/common/unit/validators/UsernameValidatorTest.php b/tests/codeception/common/unit/validators/UsernameValidatorTest.php new file mode 100644 index 0000000..ecb8238 --- /dev/null +++ b/tests/codeception/common/unit/validators/UsernameValidatorTest.php @@ -0,0 +1,106 @@ +validator = new UsernameValidator(); + } + + public function testValidateAttributeRequired() { + $model = $this->createModel(''); + $this->validator->validateAttribute($model, 'field'); + $this->assertEquals(['error.username_required'], $model->getErrors('field')); + + $model = $this->createModel('username'); + $this->validator->validateAttribute($model, 'field'); + $this->assertNotEquals(['error.username_required'], $model->getErrors('field')); + } + + public function testValidateAttributeLength() { + $model = $this->createModel('at'); + $this->validator->validateAttribute($model, 'field'); + $this->assertEquals(['error.username_too_short'], $model->getErrors('field')); + + $model = $this->createModel('erickskrauch_erickskrauch'); + $this->validator->validateAttribute($model, 'field'); + $this->assertEquals(['error.username_too_long'], $model->getErrors('field')); + + $model = $this->createModel('username'); + $this->validator->validateAttribute($model, 'field'); + $this->assertNotEquals(['error.username_too_short'], $model->getErrors('field')); + $this->assertNotEquals(['error.username_too_long'], $model->getErrors('field')); + } + + public function testValidateAttributePattern() { + $shouldBeValid = [ + 'русский_ник', 'русский_ник_на_грани!', 'numbers1132', '*__*-Stars-*__*', '1-_.!?#$%^&*()[]', + '[ESP]Эрик', 'Свят_помидор;', 'зроблена_ў_беларусі:)', + ]; + foreach($shouldBeValid as $nickname) { + $model = $this->createModel($nickname); + $this->validator->validateAttribute($model, 'field'); + $this->assertNotEquals(['error.username_invalid'], $model->getErrors('field')); + } + + $shouldBeInvalid = [ + 'nick@name', 'spaced nick', + ]; + foreach($shouldBeInvalid as $nickname) { + $model = $this->createModel($nickname); + $this->validator->validateAttribute($model, 'field'); + $this->assertEquals(['error.username_invalid'], $model->getErrors('field')); + } + } + + public function testValidateAttributeUnique() { + $this->tester->haveFixtures([ + 'accounts' => AccountFixture::class, + ]); + + /** @var \common\models\Account $accountFixture */ + $accountFixture = $this->tester->grabFixture('accounts', 'admin'); + + $model = $this->createModel($accountFixture->username); + $this->validator->validateAttribute($model, 'field'); + $this->assertEquals(['error.username_not_available'], $model->getErrors('field')); + + $model = $this->createModel($accountFixture->username); + $this->validator->accountCallback = function() use ($accountFixture) { + return $accountFixture->id; + }; + $this->validator->validateAttribute($model, 'field'); + $this->assertNotEquals(['error.username_not_available'], $model->getErrors('field')); + $this->validator->accountCallback = null; + + $model = $this->createModel('some-unique-username'); + $this->validator->validateAttribute($model, 'field'); + $this->assertNotEquals(['error.username_not_available'], $model->getErrors('field')); + } + + /** + * @param string $fieldValue + * @return Model + */ + private function createModel(string $fieldValue) : Model { + $class = new class extends Model { + public $field; + }; + + $class->field = $fieldValue; + + return $class; + } + +}