mirror of
https://github.com/elyby/accounts.git
synced 2024-12-27 23:50:19 +05:30
В проект внедрён RabbitMQ.
Контроллер для работы с RabbitMQ научился создавать типизированные аргументы для $body Добавлена таблица с историей ников Добавлена таблица Mojang ников Добавлена проверка активированности аккаунта в AccountsController
This commit is contained in:
parent
067fc1d3d6
commit
cba769a1ec
@ -16,10 +16,20 @@ class AccountsController extends Controller {
|
|||||||
'class' => AccessControl::class,
|
'class' => AccessControl::class,
|
||||||
'rules' => [
|
'rules' => [
|
||||||
[
|
[
|
||||||
'actions' => ['current', 'change-password', 'change-username'],
|
'actions' => ['current'],
|
||||||
'allow' => true,
|
'allow' => true,
|
||||||
'roles' => ['@'],
|
'roles' => ['@'],
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'actions' => ['change-password', 'change-username'],
|
||||||
|
'allow' => true,
|
||||||
|
'roles' => ['@'],
|
||||||
|
'matchCallback' => function() {
|
||||||
|
/** @var Account $account */
|
||||||
|
$account = Yii::$app->user->identity;
|
||||||
|
return $account->status > Account::STATUS_REGISTERED;
|
||||||
|
},
|
||||||
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
@ -29,6 +39,7 @@ class AccountsController extends Controller {
|
|||||||
return [
|
return [
|
||||||
'current' => ['GET'],
|
'current' => ['GET'],
|
||||||
'change-password' => ['POST'],
|
'change-password' => ['POST'],
|
||||||
|
'change-username' => ['POST'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,6 +55,7 @@ class AccountsController extends Controller {
|
|||||||
'shouldChangePassword' => $account->password_hash_strategy === Account::PASS_HASH_STRATEGY_OLD_ELY,
|
'shouldChangePassword' => $account->password_hash_strategy === Account::PASS_HASH_STRATEGY_OLD_ELY,
|
||||||
'isActive' => $account->status === Account::STATUS_ACTIVE,
|
'isActive' => $account->status === Account::STATUS_ACTIVE,
|
||||||
'passwordChangedAt' => $account->password_changed_at,
|
'passwordChangedAt' => $account->password_changed_at,
|
||||||
|
'hasMojangUsernameCollision' => $account->hasMojangUsernameCollision(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,13 @@
|
|||||||
namespace api\models;
|
namespace api\models;
|
||||||
|
|
||||||
use api\models\base\PasswordProtectedForm;
|
use api\models\base\PasswordProtectedForm;
|
||||||
|
use common\helpers\Amqp;
|
||||||
use common\models\Account;
|
use common\models\Account;
|
||||||
|
use common\models\amqp\UsernameChanged;
|
||||||
|
use common\models\UsernameHistory;
|
||||||
|
use PhpAmqpLib\Message\AMQPMessage;
|
||||||
use Yii;
|
use Yii;
|
||||||
|
use yii\base\ErrorException;
|
||||||
use yii\helpers\ArrayHelper;
|
use yii\helpers\ArrayHelper;
|
||||||
|
|
||||||
class ChangeUsernameForm extends PasswordProtectedForm {
|
class ChangeUsernameForm extends PasswordProtectedForm {
|
||||||
@ -30,10 +35,52 @@ class ChangeUsernameForm extends PasswordProtectedForm {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$transaction = Yii::$app->db->beginTransaction();
|
||||||
$account = $this->getAccount();
|
$account = $this->getAccount();
|
||||||
$account->username = $this->username;
|
$oldNickname = $account->username;
|
||||||
|
try {
|
||||||
|
$account->username = $this->username;
|
||||||
|
if (!$account->save()) {
|
||||||
|
throw new ErrorException('Cannot save account model with new username');
|
||||||
|
}
|
||||||
|
|
||||||
return $account->save();
|
$usernamesHistory = new UsernameHistory();
|
||||||
|
$usernamesHistory->account_id = $account->id;
|
||||||
|
$usernamesHistory->username = $account->username;
|
||||||
|
if (!$usernamesHistory->save()) {
|
||||||
|
throw new ErrorException('Cannot save username history record');
|
||||||
|
}
|
||||||
|
|
||||||
|
$transaction->commit();
|
||||||
|
} catch (ErrorException $e) {
|
||||||
|
$transaction->rollBack();
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->createTask($account->id, $account->username, $oldNickname);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: вынести это в отдельную сущность, т.к. эта команда используется внутри формы регистрации
|
||||||
|
*
|
||||||
|
* @param integer $accountId
|
||||||
|
* @param string $newNickname
|
||||||
|
* @param string $oldNickname
|
||||||
|
*/
|
||||||
|
public function createTask($accountId, $newNickname, $oldNickname) {
|
||||||
|
$message = Amqp::getInstance()->prepareMessage(new UsernameChanged([
|
||||||
|
'accountId' => $accountId,
|
||||||
|
'oldUsername' => $oldNickname,
|
||||||
|
'newUsername' => $newNickname,
|
||||||
|
]), [
|
||||||
|
'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT,
|
||||||
|
]);
|
||||||
|
|
||||||
|
Amqp::sendToExchange('account', 'username-changed', $message, [
|
||||||
|
3 => true, // durable -> true
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -95,6 +95,9 @@ class RegistrationForm extends ApiForm {
|
|||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$changeUsernameForm = new ChangeUsernameForm();
|
||||||
|
$changeUsernameForm->createTask($account->id, $account->username, null);
|
||||||
|
|
||||||
return $account;
|
return $account;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ class Api {
|
|||||||
* @return UsernameToUUIDResponse
|
* @return UsernameToUUIDResponse
|
||||||
* @throws MojangApiException
|
* @throws MojangApiException
|
||||||
* @throws NoContentException
|
* @throws NoContentException
|
||||||
|
* @url http://wiki.vg/Mojang_API#Username_-.3E_UUID_at_time
|
||||||
*/
|
*/
|
||||||
public function usernameToUUID($username, $atTime = null) {
|
public function usernameToUUID($username, $atTime = null) {
|
||||||
$client = $this->createClient();
|
$client = $this->createClient();
|
||||||
@ -28,7 +29,7 @@ class Api {
|
|||||||
$response = $client->send($request);
|
$response = $client->send($request);
|
||||||
if ($response->getStatusCode() === 204) {
|
if ($response->getStatusCode() === 204) {
|
||||||
throw new NoContentException('Username not found');
|
throw new NoContentException('Username not found');
|
||||||
} elseif ($response->getStatusCode()) {
|
} elseif ($response->getStatusCode() !== 200) {
|
||||||
throw new MojangApiException('Unexpected request result');
|
throw new MojangApiException('Unexpected request result');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
namespace common\components\RabbitMQ;
|
namespace common\components\RabbitMQ;
|
||||||
|
|
||||||
use PhpAmqpLib\Message\AMQPMessage;
|
use PhpAmqpLib\Message\AMQPMessage;
|
||||||
|
use ReflectionMethod;
|
||||||
use Yii;
|
use Yii;
|
||||||
use yii\db\Exception;
|
use yii\db\Exception;
|
||||||
use yii\helpers\ArrayHelper;
|
use yii\helpers\ArrayHelper;
|
||||||
@ -95,7 +96,7 @@ abstract class Controller extends \yii\console\Controller {
|
|||||||
|
|
||||||
private function getResult($method, $body, $msg) {
|
private function getResult($method, $body, $msg) {
|
||||||
try {
|
try {
|
||||||
$result = $this->$method($body, $msg);
|
$result = $this->$method($this->prepareArguments($method, $body), $msg);
|
||||||
} catch(Exception $e) {
|
} catch(Exception $e) {
|
||||||
if (strstr($e->getMessage(), '2006 MySQL server has gone away') !== false) {
|
if (strstr($e->getMessage(), '2006 MySQL server has gone away') !== false) {
|
||||||
Console::output(Console::ansiFormat('Server gone away, try to reconnect', [Console::FG_GREY]));
|
Console::output(Console::ansiFormat('Server gone away, try to reconnect', [Console::FG_GREY]));
|
||||||
@ -111,6 +112,35 @@ abstract class Controller extends \yii\console\Controller {
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function prepareArguments($methodName, $body) {
|
||||||
|
$method = new ReflectionMethod($this, $methodName);
|
||||||
|
$parameters = $method->getParameters();
|
||||||
|
if (!isset($parameters[0])) {
|
||||||
|
return $body;
|
||||||
|
}
|
||||||
|
|
||||||
|
$bodyParam = $parameters[0];
|
||||||
|
if (PHP_MAJOR_VERSION === 7) {
|
||||||
|
// TODO: логика для php7 не тестировалась, так то не факт, что оно взлетит на php7
|
||||||
|
if (!$bodyParam->hasType() || $bodyParam->isArray()) {
|
||||||
|
return $body;
|
||||||
|
}
|
||||||
|
|
||||||
|
$type = (string)$bodyParam->getType();
|
||||||
|
$object = new $type;
|
||||||
|
} else {
|
||||||
|
$class = $bodyParam->getClass();
|
||||||
|
if ($class === null) {
|
||||||
|
return $body;
|
||||||
|
}
|
||||||
|
|
||||||
|
$type = $class->name;
|
||||||
|
$object = new $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Yii::configure($object, $body);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Список аргументов, с которым будет вызван метод \PhpAmqpLib\Channel\AMQPChannel::exchange_declare()
|
* Список аргументов, с которым будет вызван метод \PhpAmqpLib\Channel\AMQPChannel::exchange_declare()
|
||||||
* По умолчанию создаётся очередь с типом fanout. Кроме того, в отличие от стандартных аргументов,
|
* По умолчанию создаётся очередь с типом fanout. Кроме того, в отличие от стандартных аргументов,
|
||||||
|
@ -15,21 +15,22 @@ use yii\web\IdentityInterface;
|
|||||||
* @property integer $id
|
* @property integer $id
|
||||||
* @property string $uuid
|
* @property string $uuid
|
||||||
* @property string $username
|
* @property string $username
|
||||||
* @property string $email
|
* @property string $email
|
||||||
* @property string $password_hash
|
* @property string $password_hash
|
||||||
* @property integer $password_hash_strategy
|
* @property integer $password_hash_strategy
|
||||||
* @property string $password_reset_token
|
* @property string $password_reset_token
|
||||||
* @property integer $status
|
* @property integer $status
|
||||||
* @property integer $created_at
|
* @property integer $created_at
|
||||||
* @property integer $updated_at
|
* @property integer $updated_at
|
||||||
* @property integer $password_changed_at
|
* @property integer $password_changed_at
|
||||||
*
|
*
|
||||||
* Геттеры-сеттеры:
|
* Геттеры-сеттеры:
|
||||||
* @property string $password пароль пользователя (только для записи)
|
* @property string $password пароль пользователя (только для записи)
|
||||||
*
|
*
|
||||||
* Отношения:
|
* Отношения:
|
||||||
* @property EmailActivation[] $emailActivations
|
* @property EmailActivation[] $emailActivations
|
||||||
* @property OauthSession[] $sessions
|
* @property OauthSession[] $sessions
|
||||||
|
* @property UsernameHistory[] $usernameHistory
|
||||||
*
|
*
|
||||||
* Поведения:
|
* Поведения:
|
||||||
* @mixin TimestampBehavior
|
* @mixin TimestampBehavior
|
||||||
@ -206,8 +207,12 @@ class Account extends ActiveRecord implements IdentityInterface {
|
|||||||
return $this->hasMany(OauthSession::class, ['owner_id' => 'id']);
|
return $this->hasMany(OauthSession::class, ['owner_id' => 'id']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getUsernameHistory() {
|
||||||
|
return $this->hasMany(UsernameHistory::class, ['account_id' => 'id']);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Метод проверяет, может ли текщий пользователь быть автоматически авторизован
|
* Метод проверяет, может ли текущий пользователь быть автоматически авторизован
|
||||||
* для указанного клиента без запроса доступа к необходимому списку прав
|
* для указанного клиента без запроса доступа к необходимому списку прав
|
||||||
*
|
*
|
||||||
* @param OauthClient $client
|
* @param OauthClient $client
|
||||||
@ -240,8 +245,7 @@ class Account extends ActiveRecord implements IdentityInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getter for "header" array that's used for generation of JWT
|
* @inheritdoc
|
||||||
* @return array JWT Header Token param, see http://jwt.io/ for details
|
|
||||||
*/
|
*/
|
||||||
protected static function getHeaderToken() {
|
protected static function getHeaderToken() {
|
||||||
return [
|
return [
|
||||||
@ -250,4 +254,14 @@ class Account extends ActiveRecord implements IdentityInterface {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Выполняет проверку, принадлежит ли этому нику аккаунт у Mojang
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasMojangUsernameCollision() {
|
||||||
|
return MojangUsername::find()
|
||||||
|
->andWhere(['username' => $this->username])
|
||||||
|
->exists();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
32
common/models/MojangUsername.php
Normal file
32
common/models/MojangUsername.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
namespace common\models;
|
||||||
|
|
||||||
|
use yii\behaviors\TimestampBehavior;
|
||||||
|
use yii\db\ActiveRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Поля модели:
|
||||||
|
* @property string $username
|
||||||
|
* @property string $uuid
|
||||||
|
* @property integer $last_pulled_at
|
||||||
|
*
|
||||||
|
* Поведения:
|
||||||
|
* @mixin TimestampBehavior
|
||||||
|
*/
|
||||||
|
class MojangUsername extends ActiveRecord {
|
||||||
|
|
||||||
|
public static function tableName() {
|
||||||
|
return '{{%mojang_usernames}}';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function behaviors() {
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'class' => TimestampBehavior::class,
|
||||||
|
'createdAtAttribute' => false,
|
||||||
|
'updatedAtAttribute' => 'last_pulled_at',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
45
common/models/UsernameHistory.php
Normal file
45
common/models/UsernameHistory.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
namespace common\models;
|
||||||
|
|
||||||
|
use Yii;
|
||||||
|
use yii\behaviors\TimestampBehavior;
|
||||||
|
use yii\db\ActiveRecord;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Поля модели:
|
||||||
|
* @property integer $id
|
||||||
|
* @property string $username
|
||||||
|
* @property integer $account_id
|
||||||
|
* @property integer $applied_in
|
||||||
|
*
|
||||||
|
* Отношения:
|
||||||
|
* @property Account $account
|
||||||
|
*
|
||||||
|
* Поведения:
|
||||||
|
* @mixin TimestampBehavior
|
||||||
|
*/
|
||||||
|
class UsernameHistory extends ActiveRecord {
|
||||||
|
|
||||||
|
public static function tableName() {
|
||||||
|
return '{{%usernames_history}}';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function behaviors() {
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'class' => TimestampBehavior::class,
|
||||||
|
'createdAtAttribute' => 'applied_in',
|
||||||
|
'updatedAtAttribute' => false,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rules() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAccount() {
|
||||||
|
return $this->hasOne(Account::class, ['id' => 'account_id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
14
common/models/amqp/UsernameChanged.php
Normal file
14
common/models/amqp/UsernameChanged.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
namespace common\models\amqp;
|
||||||
|
|
||||||
|
use yii\base\Object;
|
||||||
|
|
||||||
|
class UsernameChanged extends Object {
|
||||||
|
|
||||||
|
public $accountId;
|
||||||
|
|
||||||
|
public $oldUsername;
|
||||||
|
|
||||||
|
public $newUsername;
|
||||||
|
|
||||||
|
}
|
@ -1,8 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace console\controllers;
|
namespace console\controllers;
|
||||||
|
|
||||||
use common\components\RabbitMQ\Component as RabbitMQComponent;
|
use common\components\Mojang\Api as MojangApi;
|
||||||
|
use common\components\Mojang\exceptions\NoContentException;
|
||||||
|
use common\models\amqp\UsernameChanged;
|
||||||
|
use common\models\MojangUsername;
|
||||||
use console\controllers\base\AmqpController;
|
use console\controllers\base\AmqpController;
|
||||||
|
use Yii;
|
||||||
|
|
||||||
class AccountQueueController extends AmqpController {
|
class AccountQueueController extends AmqpController {
|
||||||
|
|
||||||
@ -16,17 +20,38 @@ class AccountQueueController extends AmqpController {
|
|||||||
|
|
||||||
public function getExchangeDeclareArgs() {
|
public function getExchangeDeclareArgs() {
|
||||||
return array_replace(parent::getExchangeDeclareArgs(), [
|
return array_replace(parent::getExchangeDeclareArgs(), [
|
||||||
1 => RabbitMQComponent::TYPE_DIRECT, // exchange-type -> direct
|
3 => true, // durable -> true
|
||||||
3 => false, // no-ack -> false
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getQueueBindArgs($exchangeName, $queueName) {
|
public function routeUsernameChanged(UsernameChanged $body) {
|
||||||
return [$exchangeName, $queueName, '#']; // Мы хотим получать сюда все события по аккаунту
|
$mojangApi = new MojangApi();
|
||||||
}
|
try {
|
||||||
|
$response = $mojangApi->usernameToUUID($body->newUsername);
|
||||||
|
} catch (NoContentException $e) {
|
||||||
|
$response = false;
|
||||||
|
}
|
||||||
|
|
||||||
public function routeChangeUsername($body) {
|
/** @var MojangUsername|null $mojangUsername */
|
||||||
// TODO: implement this
|
$mojangUsername = MojangUsername::findOne($body->newUsername);
|
||||||
|
if ($response === false) {
|
||||||
|
if ($mojangUsername !== null) {
|
||||||
|
$mojangUsername->delete();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($mojangUsername === null) {
|
||||||
|
$mojangUsername = new MojangUsername();
|
||||||
|
$mojangUsername->username = $response->name;
|
||||||
|
$mojangUsername->uuid = $response->id;
|
||||||
|
} else {
|
||||||
|
$mojangUsername->uuid = $response->id;
|
||||||
|
$mojangUsername->touch('last_pulled_at');
|
||||||
|
}
|
||||||
|
|
||||||
|
$mojangUsername->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use console\db\Migration;
|
||||||
|
|
||||||
|
class m160414_231110_account_nicknames_history extends Migration {
|
||||||
|
|
||||||
|
public function safeUp() {
|
||||||
|
$this->createTable('{{%usernames_history}}', [
|
||||||
|
'id' => $this->primaryKey(),
|
||||||
|
'username' => $this->string()->notNull(),
|
||||||
|
'account_id' => $this->getDb()->getSchema()->getTableSchema('{{%accounts}}')->getColumn('id')->dbType . ' NOT NULL',
|
||||||
|
'applied_in' => $this->integer()->notNull(),
|
||||||
|
], $this->tableOptions);
|
||||||
|
|
||||||
|
$this->addForeignKey('FK_usernames_history_to_account', '{{%usernames_history}}', 'account_id', '{{%accounts}}', 'id', 'CASCADE', 'CASCADE');
|
||||||
|
|
||||||
|
$accountNicknames = $this->getDb()->createCommand('
|
||||||
|
SELECT id,
|
||||||
|
username,
|
||||||
|
updated_at
|
||||||
|
FROM {{%accounts}}
|
||||||
|
')->queryAll();
|
||||||
|
|
||||||
|
foreach($accountNicknames as $row) {
|
||||||
|
$this->insert('{{%usernames_history}}', [
|
||||||
|
'username' => $row['username'],
|
||||||
|
'account_id' => $row['id'],
|
||||||
|
'applied_in' => $row['updated_at'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function safeDown() {
|
||||||
|
$this->dropTable('{{%usernames_history}}');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use console\db\Migration;
|
||||||
|
|
||||||
|
class m160422_230911_mojang_account_collisions extends Migration {
|
||||||
|
|
||||||
|
public function safeUp() {
|
||||||
|
$this->createTable('{{%mojang_usernames}}', [
|
||||||
|
'username' => $this->string()->notNull(),
|
||||||
|
'uuid' => $this->string(32)->notNull(),
|
||||||
|
'last_pulled_at' => $this->integer()->unsigned()->notNull(),
|
||||||
|
], $this->tableOptions);
|
||||||
|
|
||||||
|
$this->addPrimaryKey('username', '{{%mojang_usernames}}', 'username');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function safeDown() {
|
||||||
|
$this->dropTable('{{%mojang_usernames}}');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -21,9 +21,9 @@ return [
|
|||||||
'amqp' => [
|
'amqp' => [
|
||||||
'host' => 'localhost',
|
'host' => 'localhost',
|
||||||
'port' => 5672,
|
'port' => 5672,
|
||||||
'user' => 'root',
|
'user' => 'ely-accounts-app',
|
||||||
'password' => '',
|
'password' => 'app-password',
|
||||||
'vhost' => '/',
|
'vhost' => '/account.ely.by',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
@ -15,9 +15,9 @@ return [
|
|||||||
'amqp' => [
|
'amqp' => [
|
||||||
'host' => 'localhost',
|
'host' => 'localhost',
|
||||||
'port' => 5672,
|
'port' => 5672,
|
||||||
'user' => 'root',
|
'user' => 'ely-accounts-app',
|
||||||
'password' => '',
|
'password' => 'app-password',
|
||||||
'vhost' => '/',
|
'vhost' => '/account.ely.by',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
@ -1,11 +1,3 @@
|
|||||||
# Codeception Test Suite Configuration
|
|
||||||
|
|
||||||
# suite for functional (integration) tests.
|
|
||||||
# emulate web requests and make application process them.
|
|
||||||
# (tip: better to use with frameworks).
|
|
||||||
|
|
||||||
# RUN `build` COMMAND AFTER ADDING/REMOVING MODULES.
|
|
||||||
#basic/web/index.php
|
|
||||||
class_name: FunctionalTester
|
class_name: FunctionalTester
|
||||||
modules:
|
modules:
|
||||||
enabled:
|
enabled:
|
||||||
@ -14,6 +6,7 @@ modules:
|
|||||||
- tests\codeception\common\_support\FixtureHelper
|
- tests\codeception\common\_support\FixtureHelper
|
||||||
- REST
|
- REST
|
||||||
- Redis
|
- Redis
|
||||||
|
- AMQP
|
||||||
config:
|
config:
|
||||||
Yii2:
|
Yii2:
|
||||||
configFile: '../config/api/functional.php'
|
configFile: '../config/api/functional.php'
|
||||||
@ -21,3 +14,10 @@ modules:
|
|||||||
host: localhost
|
host: localhost
|
||||||
port: 6379
|
port: 6379
|
||||||
database: 1
|
database: 1
|
||||||
|
AMQP:
|
||||||
|
host: localhost
|
||||||
|
port: 5672
|
||||||
|
username: 'ely-accounts-tester'
|
||||||
|
password: 'tester-password'
|
||||||
|
vhost: '/account.ely.by/tests'
|
||||||
|
queues: ['account-operations']
|
||||||
|
@ -5,7 +5,6 @@ use Codeception\Scenario;
|
|||||||
use Codeception\Specify;
|
use Codeception\Specify;
|
||||||
use common\models\Account;
|
use common\models\Account;
|
||||||
use tests\codeception\api\_pages\AccountsRoute;
|
use tests\codeception\api\_pages\AccountsRoute;
|
||||||
use tests\codeception\api\_pages\LoginRoute;
|
|
||||||
use tests\codeception\api\functional\_steps\AccountSteps;
|
use tests\codeception\api\functional\_steps\AccountSteps;
|
||||||
use tests\codeception\api\FunctionalTester;
|
use tests\codeception\api\FunctionalTester;
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ namespace tests\codeception\api\models;
|
|||||||
use api\models\ChangeUsernameForm;
|
use api\models\ChangeUsernameForm;
|
||||||
use Codeception\Specify;
|
use Codeception\Specify;
|
||||||
use common\models\Account;
|
use common\models\Account;
|
||||||
|
use common\models\UsernameHistory;
|
||||||
use tests\codeception\api\unit\DbTestCase;
|
use tests\codeception\api\unit\DbTestCase;
|
||||||
use tests\codeception\common\fixtures\AccountFixture;
|
use tests\codeception\common\fixtures\AccountFixture;
|
||||||
use Yii;
|
use Yii;
|
||||||
@ -31,9 +32,17 @@ class ChangeUsernameFormTest extends DbTestCase {
|
|||||||
]);
|
]);
|
||||||
expect($model->change())->true();
|
expect($model->change())->true();
|
||||||
expect(Account::findOne(1)->username)->equals('my_new_nickname');
|
expect(Account::findOne(1)->username)->equals('my_new_nickname');
|
||||||
|
expect(UsernameHistory::findOne(['username' => 'my_new_nickname']))->isInstanceOf(UsernameHistory::class);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testCreateTask() {
|
||||||
|
$model = new DummyChangeUsernameForm();
|
||||||
|
$model->createTask('1', 'test1', 'test');
|
||||||
|
// TODO: у меня пока нет идей о том, чтобы это как-то успешно протестировать, увы
|
||||||
|
// но по крайней мере можно убедиться, что оно не падает где-то на этом шаге
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: тут образуется магическая переменная 1, что не круто. После перехода на php7 можно заюзать анонимный класс
|
// TODO: тут образуется магическая переменная 1, что не круто. После перехода на php7 можно заюзать анонимный класс
|
||||||
|
@ -13,3 +13,4 @@ $_SERVER['SERVER_NAME'] = 'localhost';
|
|||||||
$_SERVER['SERVER_PORT'] = '80';
|
$_SERVER['SERVER_PORT'] = '80';
|
||||||
|
|
||||||
Yii::setAlias('@tests', dirname(dirname(__DIR__)));
|
Yii::setAlias('@tests', dirname(dirname(__DIR__)));
|
||||||
|
\Codeception\Specify\Config::setDeepClone(false);
|
||||||
|
11
tests/codeception/common/fixtures/MojangUsernameFixture.php
Normal file
11
tests/codeception/common/fixtures/MojangUsernameFixture.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
namespace tests\codeception\common\fixtures;
|
||||||
|
|
||||||
|
use common\models\MojangUsername;
|
||||||
|
use yii\test\ActiveFixture;
|
||||||
|
|
||||||
|
class MojangUsernameFixture extends ActiveFixture {
|
||||||
|
|
||||||
|
public $modelClass = MojangUsername::class;
|
||||||
|
|
||||||
|
}
|
23
tests/codeception/common/fixtures/data/mojang-usernames.php
Normal file
23
tests/codeception/common/fixtures/data/mojang-usernames.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
'ErickSkrauch' => [
|
||||||
|
'username' => 'ErickSkrauch',
|
||||||
|
'uuid' => '3e3ee6c35afa48abb61e8cd8c42fc0d9',
|
||||||
|
'last_pulled_at' => 1461429129,
|
||||||
|
],
|
||||||
|
'Notch' => [
|
||||||
|
'username' => 'Notch',
|
||||||
|
'uuid' => '069a79f444e94726a5befca90e38aaf5',
|
||||||
|
'last_pulled_at' => 1461429207,
|
||||||
|
],
|
||||||
|
'not-exists' => [
|
||||||
|
'username' => '100_not_exists',
|
||||||
|
'uuid' => 'f8d4ac1fb24d423483d619832b5d475c',
|
||||||
|
'last_pulled_at' => 1461433981,
|
||||||
|
],
|
||||||
|
'uuid-changed' => [
|
||||||
|
'username' => 'jeb',
|
||||||
|
'uuid' => 'ef4c8352cecc417bbcae32f1f3e2828b',
|
||||||
|
'last_pulled_at' => 1461434315,
|
||||||
|
],
|
||||||
|
];
|
@ -5,11 +5,13 @@ use Codeception\Specify;
|
|||||||
use common\components\UserPass;
|
use common\components\UserPass;
|
||||||
use common\models\Account;
|
use common\models\Account;
|
||||||
use tests\codeception\common\fixtures\AccountFixture;
|
use tests\codeception\common\fixtures\AccountFixture;
|
||||||
|
use tests\codeception\common\fixtures\MojangUsernameFixture;
|
||||||
use tests\codeception\common\unit\DbTestCase;
|
use tests\codeception\common\unit\DbTestCase;
|
||||||
use Yii;
|
use Yii;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property array $accounts
|
* @property array $accounts
|
||||||
|
* @property array $mojangAccounts
|
||||||
*/
|
*/
|
||||||
class AccountTest extends DbTestCase {
|
class AccountTest extends DbTestCase {
|
||||||
use Specify;
|
use Specify;
|
||||||
@ -20,6 +22,10 @@ class AccountTest extends DbTestCase {
|
|||||||
'class' => AccountFixture::class,
|
'class' => AccountFixture::class,
|
||||||
'dataFile' => '@tests/codeception/common/fixtures/data/accounts.php',
|
'dataFile' => '@tests/codeception/common/fixtures/data/accounts.php',
|
||||||
],
|
],
|
||||||
|
'mojangAccounts' => [
|
||||||
|
'class' => MojangUsernameFixture::class,
|
||||||
|
'dataFile' => '@tests/codeception/common/fixtures/data/mojang-usernames.php',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,6 +79,7 @@ class AccountTest extends DbTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function testValidateEmail() {
|
public function testValidateEmail() {
|
||||||
|
// TODO: пропускать этот тест, если падает ошибка с недостпуностью интернет соединения
|
||||||
$this->specify('email required', function() {
|
$this->specify('email required', function() {
|
||||||
$model = new Account(['email' => null]);
|
$model = new Account(['email' => null]);
|
||||||
expect($model->validate(['email']))->false();
|
expect($model->validate(['email']))->false();
|
||||||
@ -149,4 +156,18 @@ class AccountTest extends DbTestCase {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testHasMojangUsernameCollision() {
|
||||||
|
$this->specify('Expect true if collision with current username', function() {
|
||||||
|
$model = new Account();
|
||||||
|
$model->username = 'ErickSkrauch';
|
||||||
|
expect($model->hasMojangUsernameCollision())->true();
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->specify('Expect false if some rare username without any collision on Mojang', function() {
|
||||||
|
$model = new Account();
|
||||||
|
$model->username = 'rare-username';
|
||||||
|
expect($model->hasMojangUsernameCollision())->false();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,5 +25,10 @@ return [
|
|||||||
'redis' => [
|
'redis' => [
|
||||||
'database' => 1,
|
'database' => 1,
|
||||||
],
|
],
|
||||||
|
'amqp' => [
|
||||||
|
'user' => 'ely-accounts-tester',
|
||||||
|
'password' => 'tester-password',
|
||||||
|
'vhost' => '/account.ely.by/tests',
|
||||||
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace tests\codeception\console\unit;
|
namespace tests\codeception\console\unit;
|
||||||
|
|
||||||
/**
|
class DbTestCase extends \yii\codeception\DbTestCase {
|
||||||
* @inheritdoc
|
|
||||||
*/
|
public $appConfig = '@tests/codeception/config/console/unit.php';
|
||||||
class DbTestCase extends \yii\codeception\DbTestCase
|
|
||||||
{
|
|
||||||
public $appConfig = '@tests/codeception/config/console/config.php';
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,95 @@
|
|||||||
|
<?php
|
||||||
|
namespace codeception\console\unit\controllers;
|
||||||
|
|
||||||
|
use Codeception\Specify;
|
||||||
|
use common\models\amqp\UsernameChanged;
|
||||||
|
use common\models\MojangUsername;
|
||||||
|
use console\controllers\AccountQueueController;
|
||||||
|
use tests\codeception\common\fixtures\AccountFixture;
|
||||||
|
use tests\codeception\common\fixtures\MojangUsernameFixture;
|
||||||
|
use tests\codeception\console\unit\DbTestCase;
|
||||||
|
use Yii;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property array $accounts
|
||||||
|
* @property array $mojangUsernames
|
||||||
|
*/
|
||||||
|
class AccountQueueControllerTest extends DbTestCase {
|
||||||
|
use Specify;
|
||||||
|
|
||||||
|
public function fixtures() {
|
||||||
|
return [
|
||||||
|
'accounts' => [
|
||||||
|
'class' => AccountFixture::class,
|
||||||
|
'dataFile' => '@tests/codeception/common/fixtures/data/accounts.php',
|
||||||
|
],
|
||||||
|
'mojangUsernames' => [
|
||||||
|
'class' => MojangUsernameFixture::class,
|
||||||
|
'dataFile' => '@tests/codeception/common/fixtures/data/mojang-usernames.php',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRouteUsernameChanged() {
|
||||||
|
// TODO: пропустить тест, если у нас нету интернета
|
||||||
|
$controller = new AccountQueueController('account-queue', Yii::$app);
|
||||||
|
$this->specify('Update last_pulled_at time if username exists', function() use ($controller) {
|
||||||
|
$accountInfo = $this->accounts['admin'];
|
||||||
|
$body = new UsernameChanged([
|
||||||
|
'accountId' => $accountInfo['id'],
|
||||||
|
'oldUsername' => $accountInfo['username'],
|
||||||
|
'newUsername' => 'Notch',
|
||||||
|
]);
|
||||||
|
$controller->routeUsernameChanged($body);
|
||||||
|
/** @var MojangUsername|null $mojangUsername */
|
||||||
|
$mojangUsername = MojangUsername::findOne('Notch');
|
||||||
|
expect($mojangUsername)->isInstanceOf(MojangUsername::class);
|
||||||
|
expect($mojangUsername->last_pulled_at)->greaterThan($this->mojangUsernames['Notch']['last_pulled_at']);
|
||||||
|
expect($mojangUsername->last_pulled_at)->lessOrEquals(time());
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->specify('Add new MojangUsername if don\'t exists', function() use ($controller) {
|
||||||
|
$accountInfo = $this->accounts['admin'];
|
||||||
|
$body = new UsernameChanged([
|
||||||
|
'accountId' => $accountInfo['id'],
|
||||||
|
'oldUsername' => $accountInfo['username'],
|
||||||
|
'newUsername' => 'Chest',
|
||||||
|
]);
|
||||||
|
$controller->routeUsernameChanged($body);
|
||||||
|
/** @var MojangUsername|null $mojangUsername */
|
||||||
|
$mojangUsername = MojangUsername::findOne('Chest');
|
||||||
|
expect($mojangUsername)->isInstanceOf(MojangUsername::class);
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->specify('Remove MojangUsername, if now it\'s does\'t exists', function() use ($controller) {
|
||||||
|
$accountInfo = $this->accounts['admin'];
|
||||||
|
$username = $this->mojangUsernames['not-exists']['username'];
|
||||||
|
$body = new UsernameChanged([
|
||||||
|
'accountId' => $accountInfo['id'],
|
||||||
|
'oldUsername' => $accountInfo['username'],
|
||||||
|
'newUsername' => $username,
|
||||||
|
]);
|
||||||
|
$controller->routeUsernameChanged($body);
|
||||||
|
/** @var MojangUsername|null $mojangUsername */
|
||||||
|
$mojangUsername = MojangUsername::findOne($username);
|
||||||
|
expect($mojangUsername)->null();
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->specify('Update uuid if username for now owned by other player', function() use ($controller) {
|
||||||
|
$accountInfo = $this->accounts['admin'];
|
||||||
|
$mojangInfo = $this->mojangUsernames['uuid-changed'];
|
||||||
|
$username = $mojangInfo['username'];
|
||||||
|
$body = new UsernameChanged([
|
||||||
|
'accountId' => $accountInfo['id'],
|
||||||
|
'oldUsername' => $accountInfo['username'],
|
||||||
|
'newUsername' => $username,
|
||||||
|
]);
|
||||||
|
$controller->routeUsernameChanged($body);
|
||||||
|
/** @var MojangUsername|null $mojangUsername */
|
||||||
|
$mojangUsername = MojangUsername::findOne($username);
|
||||||
|
expect($mojangUsername)->isInstanceOf(MojangUsername::class);
|
||||||
|
expect($mojangUsername->uuid)->notEquals($mojangInfo['uuid']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user