mirror of
https://github.com/elyby/accounts.git
synced 2025-05-31 14:11:46 +05:30
Rework tests structure. Upgrade codeception to 2.5.3. Merge params configuration into app configuration.
This commit is contained in:
26
api/codeception.dist.yml
Normal file
26
api/codeception.dist.yml
Normal file
@@ -0,0 +1,26 @@
|
||||
namespace: api\tests
|
||||
actor_suffix: Tester
|
||||
paths:
|
||||
tests: tests
|
||||
log: tests/_output
|
||||
data: tests/_data
|
||||
helpers: tests/_support
|
||||
settings:
|
||||
bootstrap: _bootstrap.php
|
||||
colors: true
|
||||
memory_limit: 1024M
|
||||
coverage:
|
||||
enabled: true
|
||||
remote: true
|
||||
whitelist:
|
||||
include:
|
||||
- ./*
|
||||
exclude:
|
||||
- aop/*
|
||||
- config/*
|
||||
- runtime/*
|
||||
- tests/*
|
||||
- web/*
|
||||
- codeception.dist.yml
|
||||
- codeception.yml
|
||||
c3url: 'http://localhost/api/web/index.php'
|
26
api/config/config-test.php
Normal file
26
api/config/config-test.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
return [
|
||||
'components' => [
|
||||
'user' => [
|
||||
'secret' => 'tests-secret-key',
|
||||
],
|
||||
'reCaptcha' => [
|
||||
'public' => 'public-key',
|
||||
'secret' => 'private-key',
|
||||
],
|
||||
],
|
||||
'params' => [
|
||||
'authserverHost' => 'localhost',
|
||||
],
|
||||
'container' => [
|
||||
'definitions' => [
|
||||
api\components\ReCaptcha\Validator::class => function() {
|
||||
return new class(new GuzzleHttp\Client()) extends api\components\ReCaptcha\Validator {
|
||||
protected function validateValue($value) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
},
|
||||
],
|
||||
],
|
||||
];
|
@@ -1,15 +1,12 @@
|
||||
<?php
|
||||
$params = array_merge(
|
||||
require __DIR__ . '/../../common/config/params.php',
|
||||
require __DIR__ . '/params.php'
|
||||
);
|
||||
|
||||
return [
|
||||
'id' => 'accounts-site-api',
|
||||
'basePath' => dirname(__DIR__),
|
||||
'bootstrap' => ['log', 'authserver', 'internal'],
|
||||
'bootstrap' => ['log', 'authserver', 'internal', 'mojang'],
|
||||
'controllerNamespace' => 'api\controllers',
|
||||
'params' => $params,
|
||||
'params' => [
|
||||
'authserverHost' => getenv('AUTHSERVER_HOST'),
|
||||
],
|
||||
'components' => [
|
||||
'user' => [
|
||||
'class' => api\components\User\Component::class,
|
||||
@@ -79,10 +76,7 @@ return [
|
||||
],
|
||||
],
|
||||
'modules' => [
|
||||
'authserver' => [
|
||||
'class' => api\modules\authserver\Module::class,
|
||||
'host' => $params['authserverHost'],
|
||||
],
|
||||
'authserver' => api\modules\authserver\Module::class,
|
||||
'session' => api\modules\session\Module::class,
|
||||
'mojang' => api\modules\mojang\Module::class,
|
||||
'internal' => api\modules\internal\Module::class,
|
||||
|
@@ -1,4 +0,0 @@
|
||||
<?php
|
||||
return [
|
||||
'authserverHost' => getenv('AUTHSERVER_HOST'),
|
||||
];
|
@@ -1,7 +1,4 @@
|
||||
<?php
|
||||
/**
|
||||
* @var array $params
|
||||
*/
|
||||
return [
|
||||
// Oauth module routes
|
||||
'/oauth2/v1/<action>' => 'oauth/authorization/<action>',
|
||||
@@ -46,8 +43,4 @@ return [
|
||||
'/mojang/profiles/<username>' => 'mojang/api/uuid-by-username',
|
||||
'/mojang/profiles/<uuid>/names' => 'mojang/api/usernames-by-uuid',
|
||||
'POST /mojang/profiles' => 'mojang/api/uuids-by-usernames',
|
||||
|
||||
"//{$params['authserverHost']}/mojang/api/users/profiles/minecraft/<username>" => 'mojang/api/uuid-by-username',
|
||||
"//{$params['authserverHost']}/mojang/api/user/profiles/<uuid>/names" => 'mojang/api/usernames-by-uuid',
|
||||
"POST //{$params['authserverHost']}/mojang/api/profiles/minecraft" => 'mojang/api/uuids-by-usernames',
|
||||
];
|
||||
|
@@ -1,9 +1,10 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace api\modules\authserver;
|
||||
|
||||
use Yii;
|
||||
use yii\base\BootstrapInterface;
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\web\NotFoundHttpException;
|
||||
|
||||
class Module extends \yii\base\Module implements BootstrapInterface {
|
||||
@@ -12,18 +13,6 @@ class Module extends \yii\base\Module implements BootstrapInterface {
|
||||
|
||||
public $defaultRoute = 'index';
|
||||
|
||||
/**
|
||||
* @var string базовый домен, запросы на который этот модуль должен обрабатывать
|
||||
*/
|
||||
public $host = 'authserver.ely.by';
|
||||
|
||||
public function init() {
|
||||
parent::init();
|
||||
if ($this->host === null) {
|
||||
throw new InvalidConfigException('base domain must be specified');
|
||||
}
|
||||
}
|
||||
|
||||
public function beforeAction($action) {
|
||||
if (!parent::beforeAction($action)) {
|
||||
return false;
|
||||
@@ -35,11 +24,12 @@ class Module extends \yii\base\Module implements BootstrapInterface {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \yii\base\Application $app the application currently running
|
||||
* @param \yii\base\Application $app
|
||||
*/
|
||||
public function bootstrap($app) {
|
||||
$legacyHost = $app->params['authserverHost'];
|
||||
$app->getUrlManager()->addRules([
|
||||
"//$this->host/$this->id/auth/<action>" => "$this->id/authentication/<action>",
|
||||
"//{$legacyHost}/authserver/auth/<action>" => "{$this->id}/authentication/<action>",
|
||||
], false);
|
||||
}
|
||||
|
||||
@@ -59,7 +49,7 @@ class Module extends \yii\base\Module implements BootstrapInterface {
|
||||
* @throws NotFoundHttpException
|
||||
*/
|
||||
protected function checkHost() {
|
||||
if (parse_url(Yii::$app->request->getHostInfo(), PHP_URL_HOST) !== $this->host) {
|
||||
if (parse_url(Yii::$app->request->getHostInfo(), PHP_URL_HOST) !== Yii::$app->params['authserverHost']) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
}
|
||||
|
@@ -1,10 +1,26 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace api\modules\mojang;
|
||||
|
||||
class Module extends \yii\base\Module {
|
||||
use yii\base\BootstrapInterface;
|
||||
|
||||
class Module extends \yii\base\Module implements BootstrapInterface {
|
||||
|
||||
public $id = 'mojang';
|
||||
|
||||
public $defaultRoute = 'api';
|
||||
|
||||
/**
|
||||
* @param \yii\base\Application $app
|
||||
*/
|
||||
public function bootstrap($app): void {
|
||||
$legacyHost = $app->params['authserverHost'];
|
||||
$app->getUrlManager()->addRules([
|
||||
"//{$legacyHost}/mojang/api/users/profiles/minecraft/<username>" => "{$this->id}/api/uuid-by-username",
|
||||
"//{$legacyHost}/mojang/api/user/profiles/<uuid>/names" => "{$this->id}/api/usernames-by-uuid",
|
||||
"POST //{$legacyHost}/mojang/api/profiles/minecraft" => "{$this->id}/api/uuids-by-usernames",
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -55,7 +55,10 @@ class AuthorizationController extends Controller {
|
||||
}
|
||||
|
||||
private function createOauthProcess(): OauthProcess {
|
||||
return new OauthProcess(Yii::$app->oauth->authServer);
|
||||
$server = Yii::$app->oauth->authServer;
|
||||
$server->setRequest(null); // Enforce request recreation (test environment bug)
|
||||
|
||||
return new OauthProcess($server);
|
||||
}
|
||||
|
||||
}
|
||||
|
3
api/tests/.gitignore
vendored
Normal file
3
api/tests/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
functional.suite.yml
|
||||
unit.suite.yml
|
||||
_support/_generated
|
10
api/tests/_bootstrap.php
Normal file
10
api/tests/_bootstrap.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
defined('YII_DEBUG') or define('YII_DEBUG', true);
|
||||
defined('YII_ENV') or define('YII_ENV', 'test');
|
||||
|
||||
defined('YII_APP_BASE_PATH') or define('YII_APP_BASE_PATH', __DIR__ . '/../../');
|
||||
|
||||
require_once YII_APP_BASE_PATH . '/vendor/autoload.php';
|
||||
require_once YII_APP_BASE_PATH . '/vendor/yiisoft/yii2/Yii.php';
|
||||
require_once YII_APP_BASE_PATH . '/common/config/bootstrap.php';
|
||||
require_once __DIR__ . '/../config/bootstrap.php';
|
2
api/tests/_output/.gitignore
vendored
Normal file
2
api/tests/_output/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
80
api/tests/_pages/AccountsRoute.php
Normal file
80
api/tests/_pages/AccountsRoute.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
namespace api\tests\_pages;
|
||||
|
||||
class AccountsRoute extends BasePage {
|
||||
|
||||
public function get(int $accountId) {
|
||||
$this->getActor()->sendGET("/api/v1/accounts/{$accountId}");
|
||||
}
|
||||
|
||||
public function changePassword(int $accountId, $currentPassword = null, $newPassword = null, $newRePassword = null) {
|
||||
$this->getActor()->sendPOST("/api/v1/accounts/{$accountId}/password", [
|
||||
'password' => $currentPassword,
|
||||
'newPassword' => $newPassword,
|
||||
'newRePassword' => $newRePassword,
|
||||
]);
|
||||
}
|
||||
|
||||
public function changeUsername(int $accountId, $currentPassword = null, $newUsername = null) {
|
||||
$this->getActor()->sendPOST("/api/v1/accounts/{$accountId}/username", [
|
||||
'password' => $currentPassword,
|
||||
'username' => $newUsername,
|
||||
]);
|
||||
}
|
||||
|
||||
public function changeEmailInitialize(int $accountId, $password = '') {
|
||||
$this->getActor()->sendPOST("/api/v1/accounts/{$accountId}/email-verification", [
|
||||
'password' => $password,
|
||||
]);
|
||||
}
|
||||
|
||||
public function changeEmailSubmitNewEmail(int $accountId, $key = null, $email = null) {
|
||||
$this->getActor()->sendPOST("/api/v1/accounts/{$accountId}/new-email-verification", [
|
||||
'key' => $key,
|
||||
'email' => $email,
|
||||
]);
|
||||
}
|
||||
|
||||
public function changeEmail(int $accountId, $key = null) {
|
||||
$this->getActor()->sendPOST("/api/v1/accounts/{$accountId}/email", [
|
||||
'key' => $key,
|
||||
]);
|
||||
}
|
||||
|
||||
public function changeLanguage(int $accountId, $lang = null) {
|
||||
$this->getActor()->sendPOST("/api/v1/accounts/{$accountId}/language", [
|
||||
'lang' => $lang,
|
||||
]);
|
||||
}
|
||||
|
||||
public function acceptRules(int $accountId) {
|
||||
$this->getActor()->sendPOST("/api/v1/accounts/{$accountId}/rules");
|
||||
}
|
||||
|
||||
public function getTwoFactorAuthCredentials(int $accountId) {
|
||||
$this->getActor()->sendGET("/api/v1/accounts/{$accountId}/two-factor-auth");
|
||||
}
|
||||
|
||||
public function enableTwoFactorAuth(int $accountId, $totp = null, $password = null) {
|
||||
$this->getActor()->sendPOST("/api/v1/accounts/{$accountId}/two-factor-auth", [
|
||||
'totp' => $totp,
|
||||
'password' => $password,
|
||||
]);
|
||||
}
|
||||
|
||||
public function disableTwoFactorAuth(int $accountId, $totp = null, $password = null) {
|
||||
$this->getActor()->sendDELETE("/api/v1/accounts/{$accountId}/two-factor-auth", [
|
||||
'totp' => $totp,
|
||||
'password' => $password,
|
||||
]);
|
||||
}
|
||||
|
||||
public function ban(int $accountId) {
|
||||
$this->getActor()->sendPOST("/api/v1/accounts/{$accountId}/ban");
|
||||
}
|
||||
|
||||
public function pardon(int $accountId) {
|
||||
$this->getActor()->sendDELETE("/api/v1/accounts/{$accountId}/ban");
|
||||
}
|
||||
|
||||
}
|
52
api/tests/_pages/AuthenticationRoute.php
Normal file
52
api/tests/_pages/AuthenticationRoute.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
namespace api\tests\_pages;
|
||||
|
||||
class AuthenticationRoute extends BasePage {
|
||||
|
||||
/**
|
||||
* @param string $login
|
||||
* @param string $password
|
||||
* @param string|bool|null $rememberMeOrToken
|
||||
* @param bool $rememberMe
|
||||
*/
|
||||
public function login($login = '', $password = '', $rememberMeOrToken = null, $rememberMe = false) {
|
||||
$params = [
|
||||
'login' => $login,
|
||||
'password' => $password,
|
||||
];
|
||||
|
||||
if ((is_bool($rememberMeOrToken) && $rememberMeOrToken) || $rememberMe) {
|
||||
$params['rememberMe'] = 1;
|
||||
} elseif ($rememberMeOrToken !== null) {
|
||||
$params['totp'] = $rememberMeOrToken;
|
||||
}
|
||||
|
||||
$this->getActor()->sendPOST('/api/authentication/login', $params);
|
||||
}
|
||||
|
||||
public function logout() {
|
||||
$this->getActor()->sendPOST('/api/authentication/logout');
|
||||
}
|
||||
|
||||
public function forgotPassword($login = null, $token = null) {
|
||||
$this->getActor()->sendPOST('/api/authentication/forgot-password', [
|
||||
'login' => $login,
|
||||
'totp' => $token,
|
||||
]);
|
||||
}
|
||||
|
||||
public function recoverPassword($key = null, $newPassword = null, $newRePassword = null) {
|
||||
$this->getActor()->sendPOST('/api/authentication/recover-password', [
|
||||
'key' => $key,
|
||||
'newPassword' => $newPassword,
|
||||
'newRePassword' => $newRePassword,
|
||||
]);
|
||||
}
|
||||
|
||||
public function refreshToken($refreshToken = null) {
|
||||
$this->getActor()->sendPOST('/api/authentication/refresh-token', [
|
||||
'refresh_token' => $refreshToken,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
26
api/tests/_pages/AuthserverRoute.php
Normal file
26
api/tests/_pages/AuthserverRoute.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
namespace api\tests\_pages;
|
||||
|
||||
class AuthserverRoute extends BasePage {
|
||||
|
||||
public function authenticate($params) {
|
||||
$this->getActor()->sendPOST('/api/authserver/authentication/authenticate', $params);
|
||||
}
|
||||
|
||||
public function refresh($params) {
|
||||
$this->getActor()->sendPOST('/api/authserver/authentication/refresh', $params);
|
||||
}
|
||||
|
||||
public function validate($params) {
|
||||
$this->getActor()->sendPOST('/api/authserver/authentication/validate', $params);
|
||||
}
|
||||
|
||||
public function invalidate($params) {
|
||||
$this->getActor()->sendPOST('/api/authserver/authentication/invalidate', $params);
|
||||
}
|
||||
|
||||
public function signout($params) {
|
||||
$this->getActor()->sendPOST('/api/authserver/authentication/signout', $params);
|
||||
}
|
||||
|
||||
}
|
21
api/tests/_pages/BasePage.php
Normal file
21
api/tests/_pages/BasePage.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
namespace api\tests\_pages;
|
||||
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class BasePage {
|
||||
|
||||
/**
|
||||
* @var FunctionalTester
|
||||
*/
|
||||
private $actor;
|
||||
|
||||
public function __construct(FunctionalTester $I) {
|
||||
$this->actor = $I;
|
||||
}
|
||||
|
||||
public function getActor(): FunctionalTester {
|
||||
return $this->actor;
|
||||
}
|
||||
|
||||
}
|
10
api/tests/_pages/IdentityInfoRoute.php
Normal file
10
api/tests/_pages/IdentityInfoRoute.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
namespace api\tests\_pages;
|
||||
|
||||
class IdentityInfoRoute extends BasePage {
|
||||
|
||||
public function info() {
|
||||
$this->getActor()->sendGET('/api/account/v1/info');
|
||||
}
|
||||
|
||||
}
|
10
api/tests/_pages/InternalRoute.php
Normal file
10
api/tests/_pages/InternalRoute.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
namespace api\tests\_pages;
|
||||
|
||||
class InternalRoute extends BasePage {
|
||||
|
||||
public function info(string $param, string $value) {
|
||||
$this->getActor()->sendGET('/api/internal/accounts/info', [$param => $value]);
|
||||
}
|
||||
|
||||
}
|
19
api/tests/_pages/MojangApiRoute.php
Normal file
19
api/tests/_pages/MojangApiRoute.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
namespace api\tests\_pages;
|
||||
|
||||
class MojangApiRoute extends BasePage {
|
||||
|
||||
public function usernameToUuid($username, $at = null) {
|
||||
$params = $at === null ? [] : ['at' => $at];
|
||||
$this->getActor()->sendGET("/api/mojang/profiles/{$username}", $params);
|
||||
}
|
||||
|
||||
public function usernamesByUuid($uuid) {
|
||||
$this->getActor()->sendGET("/api/mojang/profiles/{$uuid}/names");
|
||||
}
|
||||
|
||||
public function uuidsByUsernames($uuids) {
|
||||
$this->getActor()->sendPOST('/api/mojang/profiles', $uuids);
|
||||
}
|
||||
|
||||
}
|
42
api/tests/_pages/OauthRoute.php
Normal file
42
api/tests/_pages/OauthRoute.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
namespace api\tests\_pages;
|
||||
|
||||
class OauthRoute extends BasePage {
|
||||
|
||||
public function validate(array $queryParams): void {
|
||||
$this->getActor()->sendGET('/api/oauth2/v1/validate', $queryParams);
|
||||
}
|
||||
|
||||
public function complete(array $queryParams = [], array $postParams = []): void {
|
||||
$this->getActor()->sendPOST('/api/oauth2/v1/complete?' . http_build_query($queryParams), $postParams);
|
||||
}
|
||||
|
||||
public function issueToken(array $postParams = []): void {
|
||||
$this->getActor()->sendPOST('/api/oauth2/v1/token', $postParams);
|
||||
}
|
||||
|
||||
public function createClient(string $type, array $postParams): void {
|
||||
$this->getActor()->sendPOST('/api/v1/oauth2/' . $type, $postParams);
|
||||
}
|
||||
|
||||
public function updateClient(string $clientId, array $params): void {
|
||||
$this->getActor()->sendPUT('/api/v1/oauth2/' . $clientId, $params);
|
||||
}
|
||||
|
||||
public function deleteClient(string $clientId): void {
|
||||
$this->getActor()->sendDELETE('/api/v1/oauth2/' . $clientId);
|
||||
}
|
||||
|
||||
public function resetClient(string $clientId, bool $regenerateSecret = false): void {
|
||||
$this->getActor()->sendPOST("/api/v1/oauth2/$clientId/reset" . ($regenerateSecret ? '?regenerateSecret' : ''));
|
||||
}
|
||||
|
||||
public function getClient(string $clientId): void {
|
||||
$this->getActor()->sendGET("/api/v1/oauth2/$clientId");
|
||||
}
|
||||
|
||||
public function getPerAccount(int $accountId): void {
|
||||
$this->getActor()->sendGET("/api/v1/accounts/$accountId/oauth2/clients");
|
||||
}
|
||||
|
||||
}
|
10
api/tests/_pages/OptionsRoute.php
Normal file
10
api/tests/_pages/OptionsRoute.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
namespace api\tests\_pages;
|
||||
|
||||
class OptionsRoute extends BasePage {
|
||||
|
||||
public function get() {
|
||||
$this->getActor()->sendGET('/api/options');
|
||||
}
|
||||
|
||||
}
|
26
api/tests/_pages/SessionServerRoute.php
Normal file
26
api/tests/_pages/SessionServerRoute.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
namespace api\tests\_pages;
|
||||
|
||||
class SessionServerRoute extends BasePage {
|
||||
|
||||
public function join($params) {
|
||||
$this->getActor()->sendPOST('/api/minecraft/session/join', $params);
|
||||
}
|
||||
|
||||
public function joinLegacy(array $params) {
|
||||
$this->getActor()->sendGET('/api/minecraft/session/legacy/join', $params);
|
||||
}
|
||||
|
||||
public function hasJoined(array $params) {
|
||||
$this->getActor()->sendGET('/api/minecraft/session/hasJoined', $params);
|
||||
}
|
||||
|
||||
public function hasJoinedLegacy(array $params) {
|
||||
$this->getActor()->sendGET('/api/minecraft/session/legacy/hasJoined', $params);
|
||||
}
|
||||
|
||||
public function profile($profileUuid) {
|
||||
$this->getActor()->sendGET("/api/minecraft/session/profile/{$profileUuid}");
|
||||
}
|
||||
|
||||
}
|
20
api/tests/_pages/SignupRoute.php
Normal file
20
api/tests/_pages/SignupRoute.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
namespace api\tests\_pages;
|
||||
|
||||
class SignupRoute extends BasePage {
|
||||
|
||||
public function register(array $registrationData) {
|
||||
$this->getActor()->sendPOST('/api/signup', $registrationData);
|
||||
}
|
||||
|
||||
public function sendRepeatMessage($email = '') {
|
||||
$this->getActor()->sendPOST('/api/signup/repeat-message', ['email' => $email]);
|
||||
}
|
||||
|
||||
public function confirm($key = '') {
|
||||
$this->getActor()->sendPOST('/api/signup/confirm', [
|
||||
'key' => $key,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
43
api/tests/_support/FunctionalTester.php
Normal file
43
api/tests/_support/FunctionalTester.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace api\tests;
|
||||
|
||||
use api\tests\_generated\FunctionalTesterActions;
|
||||
use Codeception\Actor;
|
||||
use common\models\Account;
|
||||
use InvalidArgumentException;
|
||||
use Yii;
|
||||
|
||||
class FunctionalTester extends Actor {
|
||||
use FunctionalTesterActions;
|
||||
|
||||
public function amAuthenticated(string $asUsername = 'admin') {
|
||||
/** @var Account $account */
|
||||
$account = Account::findOne(['username' => $asUsername]);
|
||||
if ($account === null) {
|
||||
throw new InvalidArgumentException("Cannot find account for username \"$asUsername\"");
|
||||
}
|
||||
|
||||
$result = Yii::$app->user->createJwtAuthenticationToken($account, false);
|
||||
$this->amBearerAuthenticated($result->getJwt());
|
||||
|
||||
return $account->id;
|
||||
}
|
||||
|
||||
public function notLoggedIn(): void {
|
||||
$this->haveHttpHeader('Authorization', null);
|
||||
Yii::$app->user->logout();
|
||||
}
|
||||
|
||||
public function canSeeAuthCredentials($expectRefresh = false): void {
|
||||
$this->canSeeResponseJsonMatchesJsonPath('$.access_token');
|
||||
$this->canSeeResponseJsonMatchesJsonPath('$.expires_in');
|
||||
if ($expectRefresh) {
|
||||
$this->canSeeResponseJsonMatchesJsonPath('$.refresh_token');
|
||||
} else {
|
||||
$this->cantSeeResponseJsonMatchesJsonPath('$.refresh_token');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
12
api/tests/_support/UnitTester.php
Normal file
12
api/tests/_support/UnitTester.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace api\tests;
|
||||
|
||||
use api\tests\_generated\UnitTesterActions;
|
||||
use Codeception\Actor;
|
||||
|
||||
class UnitTester extends Actor {
|
||||
use UnitTesterActions;
|
||||
|
||||
}
|
8
api/tests/config/functional.php
Normal file
8
api/tests/config/functional.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
use common\config\ConfigLoader;
|
||||
use yii\helpers\ArrayHelper;
|
||||
|
||||
return ArrayHelper::merge(ConfigLoader::load('api'), [
|
||||
|
||||
]);
|
8
api/tests/config/unit.php
Normal file
8
api/tests/config/unit.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
use common\config\ConfigLoader;
|
||||
use yii\helpers\ArrayHelper;
|
||||
|
||||
return ArrayHelper::merge(ConfigLoader::load('api'), [
|
||||
|
||||
]);
|
20
api/tests/functional.suite.dist.yml
Normal file
20
api/tests/functional.suite.dist.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
suite_namespace: api\tests\functional
|
||||
actor: FunctionalTester
|
||||
modules:
|
||||
enabled:
|
||||
- Asserts
|
||||
- Filesystem
|
||||
- Yii2:
|
||||
configFile: tests/config/functional.php
|
||||
entryScript: /api/web/index.php
|
||||
recreateApplication: true
|
||||
transaction: false
|
||||
- common\tests\_support\FixtureHelper
|
||||
- common\tests\_support\Mockery
|
||||
- REST:
|
||||
depends: Yii2
|
||||
- Redis:
|
||||
host: redis
|
||||
port: 6379
|
||||
database: 0
|
||||
cleanupBefore: 'test'
|
61
api/tests/functional/EmailConfirmationCest.php
Normal file
61
api/tests/functional/EmailConfirmationCest.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
namespace api\tests\functional;
|
||||
|
||||
use api\tests\_pages\SignupRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class EmailConfirmationCest {
|
||||
|
||||
public function testConfirmEmailByCorrectKey(FunctionalTester $I) {
|
||||
$route = new SignupRoute($I);
|
||||
|
||||
$I->wantTo('confirm my email using correct activation key');
|
||||
$route->confirm('HABGCABHJ1234HBHVD');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
$I->cantSeeResponseJsonMatchesJsonPath('$.errors');
|
||||
$I->canSeeAuthCredentials(true);
|
||||
}
|
||||
|
||||
public function testConfirmEmailByInvalidKey(FunctionalTester $I) {
|
||||
$route = new SignupRoute($I);
|
||||
|
||||
$I->wantTo('see error.key_is_required expected if key is not set');
|
||||
$route->confirm();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'key' => 'error.key_required',
|
||||
],
|
||||
]);
|
||||
|
||||
$I->wantTo('see error.key_not_exists expected if key not exists in database');
|
||||
$route->confirm('not-exists-key');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'key' => 'error.key_not_exists',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testConfirmByInvalidEmojiString(FunctionalTester $I) {
|
||||
$route = new SignupRoute($I);
|
||||
|
||||
$I->wantTo('try to submit some long emoji string (Sentry ACCOUNTS-43Y)');
|
||||
$route->confirm(
|
||||
'ALWAYS 🕔 make sure 👍 to shave 🔪🍑 because ✌️ the last time 🕒 we let 👐😪 a bush 🌳 ' .
|
||||
'in our lives 👈😜👉 it did 9/11 💥🏢🏢✈️🔥🔥🔥 ALWAYS 🕔 make sure 👍 to shave 🔪🍑 ' .
|
||||
'because ✌️ the last time 🕒 we let 👐😪 a bush 🌳 in our lives 👈😜👉 it did 9/11 ' .
|
||||
'💥🏢🏢✈️🔥🔥🔥/'
|
||||
);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'key' => 'error.key_not_exists',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
37
api/tests/functional/FeedbackCest.php
Normal file
37
api/tests/functional/FeedbackCest.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
namespace api\tests\functional;
|
||||
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class FeedbackCest {
|
||||
|
||||
public function testFeedbackWithoutAuth(FunctionalTester $I) {
|
||||
$I->sendPOST('/api/feedback', [
|
||||
'subject' => 'Test',
|
||||
'email' => 'email@ely.by',
|
||||
'type' => 0,
|
||||
'message' => 'Hello world',
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function testFeedbackWithAuth(FunctionalTester $I) {
|
||||
$I->amAuthenticated();
|
||||
$I->sendPOST('/api/feedback', [
|
||||
'subject' => 'Test',
|
||||
'email' => 'email@ely.by',
|
||||
'type' => 0,
|
||||
'message' => 'Hello world',
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
77
api/tests/functional/ForgotPasswordCest.php
Normal file
77
api/tests/functional/ForgotPasswordCest.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
namespace api\tests\functional;
|
||||
|
||||
use api\tests\_pages\AuthenticationRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class ForgotPasswordCest {
|
||||
|
||||
/**
|
||||
* @var AuthenticationRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new AuthenticationRoute($I);
|
||||
}
|
||||
|
||||
public function testWrongInput(FunctionalTester $I) {
|
||||
$I->wantTo('see reaction on invalid input');
|
||||
|
||||
$this->route->forgotPassword();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'login' => 'error.login_required',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->route->forgotPassword('becauseimbatman!');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'login' => 'error.login_not_exist',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testForgotPasswordByEmail(FunctionalTester $I) {
|
||||
$I->wantTo('create new password recover request by passing email');
|
||||
$this->route->forgotPassword('admin@ely.by');
|
||||
$this->assertSuccessResponse($I, false);
|
||||
}
|
||||
|
||||
public function testForgotPasswordByUsername(FunctionalTester $I) {
|
||||
$I->wantTo('create new password recover request by passing username');
|
||||
$this->route->forgotPassword('Admin');
|
||||
$this->assertSuccessResponse($I, true);
|
||||
}
|
||||
|
||||
public function testDataForFrequencyError(FunctionalTester $I) {
|
||||
$I->wantTo('get info about time to repeat recover password request');
|
||||
$this->route->forgotPassword('Notch');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'login' => 'error.recently_sent_message',
|
||||
],
|
||||
]);
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.data.canRepeatIn');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.data.repeatFrequency');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FunctionalTester $I
|
||||
*/
|
||||
private function assertSuccessResponse(FunctionalTester $I, bool $expectEmailMask = false): void {
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.data.canRepeatIn');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.data.repeatFrequency');
|
||||
if ($expectEmailMask) {
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.data.emailMask');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
218
api/tests/functional/LoginCest.php
Normal file
218
api/tests/functional/LoginCest.php
Normal file
@@ -0,0 +1,218 @@
|
||||
<?php
|
||||
namespace api\tests\functional;
|
||||
|
||||
use api\tests\FunctionalTester;
|
||||
use OTPHP\TOTP;
|
||||
use api\tests\_pages\AuthenticationRoute;
|
||||
|
||||
class LoginCest {
|
||||
|
||||
public function testLoginEmailOrUsername(FunctionalTester $I) {
|
||||
$route = new AuthenticationRoute($I);
|
||||
|
||||
$I->wantTo('see error.login_required expected if login is not set');
|
||||
$route->login();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'login' => 'error.login_required',
|
||||
],
|
||||
]);
|
||||
|
||||
$I->wantTo('see error.login_not_exist expected if username not exists in database');
|
||||
$route->login('non-exist-username');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'login' => 'error.login_not_exist',
|
||||
],
|
||||
]);
|
||||
|
||||
$I->wantTo('see error.login_not_exist expected if email not exists in database');
|
||||
$route->login('not-exist@user.com');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'login' => 'error.login_not_exist',
|
||||
],
|
||||
]);
|
||||
|
||||
$I->wantTo('see error.account_not_activated expected if credentials are valid, but account is not activated');
|
||||
$route->login('howe.garnett', 'password_0');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'login' => 'error.account_not_activated',
|
||||
],
|
||||
]);
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.data.email');
|
||||
|
||||
$I->wantTo('don\'t see errors on login field if username is correct and exists in database');
|
||||
$route->login('Admin');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
]);
|
||||
$I->cantSeeResponseJsonMatchesJsonPath('$.errors.login');
|
||||
|
||||
$I->wantTo('don\'t see errors on login field if email is correct and exists in database');
|
||||
$route->login('admin@ely.by');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
]);
|
||||
$I->cantSeeResponseJsonMatchesJsonPath('$.errors.login');
|
||||
}
|
||||
|
||||
public function testLoginPassword(FunctionalTester $I) {
|
||||
$route = new AuthenticationRoute($I);
|
||||
|
||||
$I->wantTo('see password doesn\'t have errors if email or username not set');
|
||||
$route->login();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
]);
|
||||
$I->cantSeeResponseJsonMatchesJsonPath('$.errors.password');
|
||||
|
||||
$I->wantTo('see password doesn\'t have errors if username not exists in database');
|
||||
$route->login('non-exist-username', 'random-password');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
]);
|
||||
$I->cantSeeResponseJsonMatchesJsonPath('$.errors.password');
|
||||
|
||||
$I->wantTo('see password doesn\'t has errors if email not exists in database');
|
||||
$route->login('not-exist@user.com', 'random-password');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
]);
|
||||
$I->cantSeeResponseJsonMatchesJsonPath('$.errors.password');
|
||||
|
||||
$I->wantTo('see error.password_incorrect if email correct, but password wrong');
|
||||
$route->login('admin@ely.by', 'wrong-password');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'password' => 'error.password_incorrect',
|
||||
],
|
||||
]);
|
||||
|
||||
$I->wantTo('see error.password_incorrect if username correct, but password wrong');
|
||||
$route->login('Admin', 'wrong-password');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'password' => 'error.password_incorrect',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testLoginToken(FunctionalTester $I) {
|
||||
$route = new AuthenticationRoute($I);
|
||||
|
||||
$I->wantTo('see totp don\'t have errors if email, username or totp not set');
|
||||
$route->login();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
]);
|
||||
$I->cantSeeResponseJsonMatchesJsonPath('$.errors.totp');
|
||||
|
||||
$I->wantTo('see totp don\'t have errors if username not exists in database');
|
||||
$route->login('non-exist-username', 'random-password');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
]);
|
||||
$I->cantSeeResponseJsonMatchesJsonPath('$.errors.totp');
|
||||
|
||||
$I->wantTo('see totp don\'t has errors if email not exists in database');
|
||||
$route->login('not-exist@user.com', 'random-password');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
]);
|
||||
$I->cantSeeResponseJsonMatchesJsonPath('$.errors.totp');
|
||||
|
||||
$I->wantTo('see totp don\'t has errors if email correct, but password wrong');
|
||||
$route->login('not-exist@user.com', 'random-password');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
]);
|
||||
$I->cantSeeResponseJsonMatchesJsonPath('$.errors.totp');
|
||||
|
||||
$I->wantTo('see error.totp_required if username and password correct, but account have enable otp');
|
||||
$route->login('AccountWithEnabledOtp', 'password_0');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'totp' => 'error.totp_required',
|
||||
],
|
||||
]);
|
||||
|
||||
$I->wantTo('see error.totp_incorrect if username and password correct, but totp wrong');
|
||||
$route->login('AccountWithEnabledOtp', 'password_0', '123456');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'totp' => 'error.totp_incorrect',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testLoginByUsernameCorrect(FunctionalTester $I) {
|
||||
$route = new AuthenticationRoute($I);
|
||||
|
||||
$I->wantTo('login into account using correct username and password');
|
||||
$route->login('Admin', 'password_0');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
$I->cantSeeResponseJsonMatchesJsonPath('$.errors');
|
||||
$I->canSeeAuthCredentials(false);
|
||||
}
|
||||
|
||||
public function testLoginByEmailCorrect(FunctionalTester $I) {
|
||||
$route = new AuthenticationRoute($I);
|
||||
|
||||
$I->wantTo('login into account using correct email and password');
|
||||
$route->login('admin@ely.by', 'password_0');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
$I->cantSeeResponseJsonMatchesJsonPath('$.errors');
|
||||
$I->canSeeAuthCredentials(false);
|
||||
}
|
||||
|
||||
public function testLoginInAccWithPasswordMethod(FunctionalTester $I) {
|
||||
$route = new AuthenticationRoute($I);
|
||||
|
||||
$I->wantTo('login into account with old password hash function using correct username and password');
|
||||
$route->login('AccWithOldPassword', '12345678');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
$I->cantSeeResponseJsonMatchesJsonPath('$.errors');
|
||||
$I->canSeeAuthCredentials(false);
|
||||
}
|
||||
|
||||
public function testLoginByEmailWithRemember(FunctionalTester $I) {
|
||||
$route = new AuthenticationRoute($I);
|
||||
|
||||
$I->wantTo('login into account using correct data and get refresh_token');
|
||||
$route->login('admin@ely.by', 'password_0', true);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
$I->cantSeeResponseJsonMatchesJsonPath('$.errors');
|
||||
$I->canSeeAuthCredentials(true);
|
||||
}
|
||||
|
||||
public function testLoginByAccountWithOtp(FunctionalTester $I) {
|
||||
$route = new AuthenticationRoute($I);
|
||||
|
||||
$I->wantTo('login into account with enabled otp');
|
||||
$route->login('AccountWithEnabledOtp', 'password_0', (TOTP::create('BBBB'))->now());
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
$I->cantSeeResponseJsonMatchesJsonPath('$.errors');
|
||||
$I->canSeeAuthCredentials(false);
|
||||
}
|
||||
|
||||
}
|
19
api/tests/functional/LogoutCest.php
Normal file
19
api/tests/functional/LogoutCest.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
namespace api\tests\functional;
|
||||
|
||||
use api\tests\_pages\AuthenticationRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class LogoutCest {
|
||||
|
||||
public function testLoginEmailOrUsername(FunctionalTester $I) {
|
||||
$route = new AuthenticationRoute($I);
|
||||
|
||||
$I->amAuthenticated();
|
||||
$route->logout();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
29
api/tests/functional/OptionsCest.php
Normal file
29
api/tests/functional/OptionsCest.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
namespace api\tests\functional;
|
||||
|
||||
use api\tests\_pages\OptionsRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class OptionsCest {
|
||||
|
||||
/**
|
||||
* @var OptionsRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new OptionsRoute($I);
|
||||
}
|
||||
|
||||
public function testRecaptchaPublicKey(FunctionalTester $I) {
|
||||
$I->wantTo('Get recaptcha public key');
|
||||
|
||||
$this->route->get();
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'reCaptchaPublicKey' => 'public-key',
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
36
api/tests/functional/RecoverPasswordCest.php
Normal file
36
api/tests/functional/RecoverPasswordCest.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
namespace api\tests\functional;
|
||||
|
||||
use api\tests\_pages\AccountsRoute;
|
||||
use api\tests\_pages\AuthenticationRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class RecoverPasswordCest {
|
||||
|
||||
public function testDataForFrequencyError(FunctionalTester $I) {
|
||||
$authRoute = new AuthenticationRoute($I);
|
||||
|
||||
$I->wantTo('change my account password, using key from email');
|
||||
$authRoute->recoverPassword('H24HBDCHHAG2HGHGHS', '12345678', '12345678');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
$I->canSeeAuthCredentials(false);
|
||||
|
||||
$I->wantTo('ensure, that jwt token is valid');
|
||||
$jwt = $I->grabDataFromResponseByJsonPath('$.access_token')[0];
|
||||
$I->amBearerAuthenticated($jwt);
|
||||
$accountRoute = new AccountsRoute($I);
|
||||
$accountRoute->get(5);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->notLoggedIn();
|
||||
|
||||
$I->wantTo('check, that password is really changed');
|
||||
$authRoute->login('Notch', '12345678');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
32
api/tests/functional/RefreshTokenCest.php
Normal file
32
api/tests/functional/RefreshTokenCest.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
namespace api\tests\functional;
|
||||
|
||||
use api\tests\_pages\AuthenticationRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class RefreshTokenCest {
|
||||
|
||||
public function testRefreshInvalidToken(FunctionalTester $I) {
|
||||
$route = new AuthenticationRoute($I);
|
||||
|
||||
$I->wantTo('get error.refresh_token_not_exist if passed token is invalid');
|
||||
$route->refreshToken('invalid-token');
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'refresh_token' => 'error.refresh_token_not_exist',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testRefreshToken(FunctionalTester $I) {
|
||||
$route = new AuthenticationRoute($I);
|
||||
|
||||
$I->wantTo('get new access_token by my refresh_token');
|
||||
$route->refreshToken('SOutIr6Seeaii3uqMVy3Wan8sKFVFrNz');
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeAuthCredentials(false);
|
||||
}
|
||||
|
||||
}
|
299
api/tests/functional/RegisterCest.php
Normal file
299
api/tests/functional/RegisterCest.php
Normal file
@@ -0,0 +1,299 @@
|
||||
<?php
|
||||
namespace api\tests\functional;
|
||||
|
||||
use Codeception\Example;
|
||||
use api\tests\_pages\SignupRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class RegisterCest {
|
||||
|
||||
/**
|
||||
* @var SignupRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new SignupRoute($I);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getSuccessInputExamples
|
||||
*/
|
||||
public function testUserCorrectRegistration(FunctionalTester $I, Example $example) {
|
||||
$I->wantTo($example->offsetGet('case'));
|
||||
$this->route->register($example->offsetGet('request'));
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson(['success' => true]);
|
||||
$I->cantSeeResponseJsonMatchesJsonPath('$.errors');
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getInvalidInputExamples
|
||||
*/
|
||||
public function testIncorrectRegistration(FunctionalTester $I, Example $example) {
|
||||
$I->wantTo($example->offsetGet('case'));
|
||||
$this->route->register($example->offsetGet('request'));
|
||||
if ($example->offsetExists('canSee')) {
|
||||
$I->canSeeResponseContainsJson($example->offsetGet('canSee'));
|
||||
}
|
||||
|
||||
if ($example->offsetExists('cantSee')) {
|
||||
$I->cantSeeResponseContainsJson($example->offsetGet('cantSee'));
|
||||
}
|
||||
|
||||
if ($example->offsetExists('shouldNotMatch')) {
|
||||
foreach ((array)$example->offsetGet('shouldNotMatch') as $jsonPath) {
|
||||
$I->cantSeeResponseJsonMatchesJsonPath($jsonPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function getSuccessInputExamples(): array {
|
||||
return [
|
||||
[
|
||||
'case' => 'ensure that signup works',
|
||||
'request' => [
|
||||
'username' => 'some_username',
|
||||
'email' => 'some_email@example.com',
|
||||
'password' => 'some_password',
|
||||
'rePassword' => 'some_password',
|
||||
'rulesAgreement' => true,
|
||||
'lang' => 'ru',
|
||||
],
|
||||
],
|
||||
[
|
||||
'case' => 'ensure that signup allow reassign not finished registration username',
|
||||
'request' => [
|
||||
'username' => 'howe.garnett',
|
||||
'email' => 'custom-email@gmail.com',
|
||||
'password' => 'some_password',
|
||||
'rePassword' => 'some_password',
|
||||
'rulesAgreement' => true,
|
||||
'lang' => 'ru',
|
||||
],
|
||||
],
|
||||
[
|
||||
'case' => 'ensure that signup allow reassign not finished registration email',
|
||||
'request' => [
|
||||
'username' => 'CustomUsername',
|
||||
'email' => 'achristiansen@gmail.com',
|
||||
'password' => 'some_password',
|
||||
'rePassword' => 'some_password',
|
||||
'rulesAgreement' => true,
|
||||
'lang' => 'ru',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
protected function getInvalidInputExamples(): array {
|
||||
return [
|
||||
[
|
||||
'case' => 'get error.rulesAgreement_required if we don\'t accept rules',
|
||||
'request' => [
|
||||
'username' => 'ErickSkrauch',
|
||||
'email' => 'erickskrauch@ely.by',
|
||||
'password' => 'some_password',
|
||||
'rePassword' => 'some_password',
|
||||
],
|
||||
'canSee' => [
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'rulesAgreement' => 'error.rulesAgreement_required',
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'case' => 'don\'t see error.rulesAgreement_requireds if we accept rules',
|
||||
'request' => [
|
||||
'rulesAgreement' => true,
|
||||
],
|
||||
'cantSee' => [
|
||||
'errors' => [
|
||||
'rulesAgreement' => 'error.rulesAgreement_required',
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'case' => 'see error.username_required if username is not set',
|
||||
'request' => [
|
||||
'username' => '',
|
||||
'email' => '',
|
||||
'password' => '',
|
||||
'rePassword' => '',
|
||||
'rulesAgreement' => true,
|
||||
],
|
||||
'canSee' => [
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'username' => 'error.username_required',
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'case' => 'don\'t see error.username_required if username is not set',
|
||||
'request' => [
|
||||
'username' => 'valid_nickname',
|
||||
'email' => '',
|
||||
'password' => '',
|
||||
'rePassword' => '',
|
||||
'rulesAgreement' => true,
|
||||
],
|
||||
'cantSee' => [
|
||||
'errors' => [
|
||||
'username' => 'error.username_required',
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'case' => 'see error.email_required if email is not set',
|
||||
'request' => [
|
||||
'username' => 'valid_nickname',
|
||||
'email' => '',
|
||||
'password' => '',
|
||||
'rePassword' => '',
|
||||
'rulesAgreement' => true,
|
||||
],
|
||||
'canSee' => [
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'email' => 'error.email_required',
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'case' => 'see error.email_invalid if email is set, but invalid',
|
||||
'request' => [
|
||||
'username' => 'valid_nickname',
|
||||
'email' => 'invalid@email',
|
||||
'password' => '',
|
||||
'rePassword' => '',
|
||||
'rulesAgreement' => true,
|
||||
],
|
||||
'canSee' => [
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'email' => 'error.email_invalid',
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'case' => 'see error.email_invalid if email is set, valid, but domain doesn\'t exist or don\'t have mx record',
|
||||
'request' => [
|
||||
'username' => 'valid_nickname',
|
||||
'email' => 'invalid@this-should-be-really-no-exists-domain-63efd7ab-1529-46d5-9426-fa5ed9f710e6.com',
|
||||
'password' => '',
|
||||
'rePassword' => '',
|
||||
'rulesAgreement' => true,
|
||||
],
|
||||
'canSee' => [
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'email' => 'error.email_invalid',
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'case' => 'see error.email_not_available if email is set, fully valid, but not available for registration',
|
||||
'request' => [
|
||||
'username' => 'valid_nickname',
|
||||
'email' => 'admin@ely.by',
|
||||
'password' => '',
|
||||
'rePassword' => '',
|
||||
'rulesAgreement' => true,
|
||||
],
|
||||
'canSee' => [
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'email' => 'error.email_not_available',
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'case' => 'don\'t see errors on email if email valid',
|
||||
'request' => [
|
||||
'username' => 'valid_nickname',
|
||||
'email' => 'erickskrauch@ely.by',
|
||||
'password' => '',
|
||||
'rePassword' => '',
|
||||
'rulesAgreement' => true,
|
||||
],
|
||||
'shouldNotMatch' => [
|
||||
'$.errors.email',
|
||||
],
|
||||
],
|
||||
[
|
||||
'case' => 'see error.password_required if password is not set',
|
||||
'request' => [
|
||||
'username' => 'valid_nickname',
|
||||
'email' => 'erickskrauch@ely.by',
|
||||
'password' => '',
|
||||
'rePassword' => '',
|
||||
'rulesAgreement' => true,
|
||||
],
|
||||
'canSee' => [
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'password' => 'error.password_required',
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'case' => 'see error.password_too_short before it will be compared with rePassword',
|
||||
'request' => [
|
||||
'username' => 'valid_nickname',
|
||||
'email' => 'correct-email@ely.by',
|
||||
'password' => 'short',
|
||||
'rePassword' => 'password',
|
||||
'rulesAgreement' => true,
|
||||
],
|
||||
'canSee' => [
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'password' => 'error.password_too_short',
|
||||
],
|
||||
],
|
||||
'shouldNotMatch' => [
|
||||
'$.errors.rePassword',
|
||||
],
|
||||
],
|
||||
[
|
||||
'case' => 'see error.rePassword_required if password valid and rePassword not set',
|
||||
'request' => [
|
||||
'username' => 'valid_nickname',
|
||||
'email' => 'correct-email@ely.by',
|
||||
'password' => 'valid-password',
|
||||
'rePassword' => '',
|
||||
'rulesAgreement' => true,
|
||||
],
|
||||
'canSee' => [
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'rePassword' => 'error.rePassword_required',
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'case' => 'see error.rePassword_does_not_match if password valid and rePassword doesn\'t match it',
|
||||
'request' => [
|
||||
'username' => 'valid_nickname',
|
||||
'email' => 'correct-email@ely.by',
|
||||
'password' => 'valid-password',
|
||||
'rePassword' => 'password',
|
||||
'rulesAgreement' => true,
|
||||
],
|
||||
'canSee' => [
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'rePassword' => 'error.rePassword_does_not_match',
|
||||
],
|
||||
],
|
||||
'shouldNotMatch' => [
|
||||
'$.errors.password',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
70
api/tests/functional/RepeatAccountActivationCest.php
Normal file
70
api/tests/functional/RepeatAccountActivationCest.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
namespace api\tests\functional;
|
||||
|
||||
use api\tests\_pages\SignupRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class RepeatAccountActivationCest {
|
||||
|
||||
public function testInvalidEmailOrAccountState(FunctionalTester $I) {
|
||||
$route = new SignupRoute($I);
|
||||
|
||||
$I->wantTo('error.email_required on empty for submitting');
|
||||
$route->sendRepeatMessage();
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'email' => 'error.email_required',
|
||||
],
|
||||
]);
|
||||
|
||||
$I->wantTo('error.email_not_found if email is not presented in db');
|
||||
$route->sendRepeatMessage('im-not@exists.net');
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'email' => 'error.email_not_found',
|
||||
],
|
||||
]);
|
||||
|
||||
$I->wantTo('error.account_already_activated if passed email matches with already activated account');
|
||||
$route->sendRepeatMessage('admin@ely.by');
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'email' => 'error.account_already_activated',
|
||||
],
|
||||
]);
|
||||
|
||||
$I->wantTo('error.recently_sent_message if last message was send too recently');
|
||||
$route->sendRepeatMessage('achristiansen@gmail.com');
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'email' => 'error.recently_sent_message',
|
||||
],
|
||||
]);
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.data.canRepeatIn');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.data.repeatFrequency');
|
||||
}
|
||||
|
||||
public function testSuccess(FunctionalTester $I) {
|
||||
$route = new SignupRoute($I);
|
||||
|
||||
$I->wantTo('successfully resend account activation message');
|
||||
$route->sendRepeatMessage('jon@ely.by');
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson(['success' => true]);
|
||||
$I->cantSeeResponseJsonMatchesJsonPath('$.errors');
|
||||
}
|
||||
|
||||
}
|
1
api/tests/functional/_bootstrap.php
Normal file
1
api/tests/functional/_bootstrap.php
Normal file
@@ -0,0 +1 @@
|
||||
<?php
|
26
api/tests/functional/_steps/AuthserverSteps.php
Normal file
26
api/tests/functional/_steps/AuthserverSteps.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace api\tests\functional\_steps;
|
||||
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use api\tests\_pages\AuthserverRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class AuthserverSteps extends FunctionalTester {
|
||||
|
||||
public function amAuthenticated(string $asUsername = 'admin', string $password = 'password_0'): array {
|
||||
$route = new AuthserverRoute($this);
|
||||
$clientToken = Uuid::uuid4()->toString();
|
||||
$route->authenticate([
|
||||
'username' => $asUsername,
|
||||
'password' => $password,
|
||||
'clientToken' => $clientToken,
|
||||
]);
|
||||
|
||||
$accessToken = $this->grabDataFromResponseByJsonPath('$.accessToken')[0];
|
||||
|
||||
return [$accessToken, $clientToken];
|
||||
}
|
||||
|
||||
}
|
67
api/tests/functional/_steps/OauthSteps.php
Normal file
67
api/tests/functional/_steps/OauthSteps.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
namespace api\tests\functional\_steps;
|
||||
|
||||
use api\components\OAuth2\Storage\ScopeStorage as S;
|
||||
use api\tests\_pages\OauthRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class OauthSteps extends FunctionalTester {
|
||||
|
||||
public function getAuthCode(array $permissions = []) {
|
||||
$this->amAuthenticated();
|
||||
$route = new OauthRoute($this);
|
||||
$route->complete([
|
||||
'client_id' => 'ely',
|
||||
'redirect_uri' => 'http://ely.by',
|
||||
'response_type' => 'code',
|
||||
'scope' => implode(',', $permissions),
|
||||
], ['accept' => true]);
|
||||
$this->canSeeResponseJsonMatchesJsonPath('$.redirectUri');
|
||||
$response = json_decode($this->grabResponse(), true);
|
||||
preg_match('/code=([\w-]+)/', $response['redirectUri'], $matches);
|
||||
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
public function getAccessToken(array $permissions = []) {
|
||||
$authCode = $this->getAuthCode($permissions);
|
||||
$response = $this->issueToken($authCode);
|
||||
|
||||
return $response['access_token'];
|
||||
}
|
||||
|
||||
public function getRefreshToken(array $permissions = []) {
|
||||
$authCode = $this->getAuthCode(array_merge([S::OFFLINE_ACCESS], $permissions));
|
||||
$response = $this->issueToken($authCode);
|
||||
|
||||
return $response['refresh_token'];
|
||||
}
|
||||
|
||||
public function issueToken($authCode) {
|
||||
$route = new OauthRoute($this);
|
||||
$route->issueToken([
|
||||
'code' => $authCode,
|
||||
'client_id' => 'ely',
|
||||
'client_secret' => 'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
|
||||
'redirect_uri' => 'http://ely.by',
|
||||
'grant_type' => 'authorization_code',
|
||||
]);
|
||||
|
||||
return json_decode($this->grabResponse(), true);
|
||||
}
|
||||
|
||||
public function getAccessTokenByClientCredentialsGrant(array $permissions = [], $useTrusted = true) {
|
||||
$route = new OauthRoute($this);
|
||||
$route->issueToken([
|
||||
'client_id' => $useTrusted ? 'trusted-client' : 'default-client',
|
||||
'client_secret' => $useTrusted ? 'tXBbyvMcyaOgHMOAXBpN2EC7uFoJAaL9' : 'AzWRy7ZjS1yRQUk2vRBDic8fprOKDB1W',
|
||||
'grant_type' => 'client_credentials',
|
||||
'scope' => implode(',', $permissions),
|
||||
]);
|
||||
|
||||
$response = json_decode($this->grabResponse(), true);
|
||||
|
||||
return $response['access_token'];
|
||||
}
|
||||
|
||||
}
|
67
api/tests/functional/_steps/SessionServerSteps.php
Normal file
67
api/tests/functional/_steps/SessionServerSteps.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
namespace api\tests\functional\_steps;
|
||||
|
||||
use common\rbac\Permissions as P;
|
||||
use Faker\Provider\Uuid;
|
||||
use api\tests\_pages\SessionServerRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class SessionServerSteps extends FunctionalTester {
|
||||
|
||||
public function amJoined($byLegacy = false) {
|
||||
$oauthSteps = new OauthSteps($this->scenario);
|
||||
$accessToken = $oauthSteps->getAccessToken([P::MINECRAFT_SERVER_SESSION]);
|
||||
$route = new SessionServerRoute($this);
|
||||
$serverId = Uuid::uuid();
|
||||
$username = 'Admin';
|
||||
|
||||
if ($byLegacy) {
|
||||
$route->joinLegacy([
|
||||
'sessionId' => 'token:' . $accessToken . ':' . 'df936908-b2e1-544d-96f8-2977ec213022',
|
||||
'user' => $username,
|
||||
'serverId' => $serverId,
|
||||
]);
|
||||
|
||||
$this->canSeeResponseEquals('OK');
|
||||
} else {
|
||||
$route->join([
|
||||
'accessToken' => $accessToken,
|
||||
'selectedProfile' => 'df936908-b2e1-544d-96f8-2977ec213022',
|
||||
'serverId' => $serverId,
|
||||
]);
|
||||
|
||||
$this->canSeeResponseContainsJson(['id' => 'OK']);
|
||||
}
|
||||
|
||||
return [$username, $serverId];
|
||||
}
|
||||
|
||||
public function canSeeValidTexturesResponse($expectedUsername, $expectedUuid) {
|
||||
$this->seeResponseIsJson();
|
||||
$this->canSeeResponseContainsJson([
|
||||
'name' => $expectedUsername,
|
||||
'id' => $expectedUuid,
|
||||
'ely' => true,
|
||||
'properties' => [
|
||||
[
|
||||
'name' => 'textures',
|
||||
'signature' => 'Cg==',
|
||||
],
|
||||
],
|
||||
]);
|
||||
$this->canSeeResponseJsonMatchesJsonPath('$.properties[0].value');
|
||||
$value = json_decode($this->grabResponse(), true)['properties'][0]['value'];
|
||||
$decoded = json_decode(base64_decode($value), true);
|
||||
$this->assertArrayHasKey('timestamp', $decoded);
|
||||
$this->assertArrayHasKey('textures', $decoded);
|
||||
$this->assertEquals($expectedUuid, $decoded['profileId']);
|
||||
$this->assertEquals($expectedUsername, $decoded['profileName']);
|
||||
$this->assertTrue($decoded['ely']);
|
||||
$textures = $decoded['textures'];
|
||||
$this->assertArrayHasKey('SKIN', $textures);
|
||||
$skinTextures = $textures['SKIN'];
|
||||
$this->assertArrayHasKey('url', $skinTextures);
|
||||
$this->assertArrayHasKey('hash', $skinTextures);
|
||||
}
|
||||
|
||||
}
|
28
api/tests/functional/accounts/AcceptRulesCest.php
Normal file
28
api/tests/functional/accounts/AcceptRulesCest.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
namespace api\tests\functional\accounts;
|
||||
|
||||
use api\tests\_pages\AccountsRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class AcceptRulesCest {
|
||||
|
||||
/**
|
||||
* @var AccountsRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new AccountsRoute($I);
|
||||
}
|
||||
|
||||
public function testCurrent(FunctionalTester $I) {
|
||||
$I->amAuthenticated('Veleyaba');
|
||||
$this->route->acceptRules(9);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
47
api/tests/functional/accounts/BanCest.php
Normal file
47
api/tests/functional/accounts/BanCest.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
namespace api\tests\functional\accounts;
|
||||
|
||||
use common\rbac\Permissions as P;
|
||||
use api\tests\_pages\AccountsRoute;
|
||||
use api\tests\functional\_steps\OauthSteps;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class BanCest {
|
||||
|
||||
/**
|
||||
* @var AccountsRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new AccountsRoute($I);
|
||||
}
|
||||
|
||||
public function testBanAccount(OauthSteps $I) {
|
||||
$accessToken = $I->getAccessTokenByClientCredentialsGrant([P::BLOCK_ACCOUNT]);
|
||||
$I->amBearerAuthenticated($accessToken);
|
||||
|
||||
$this->route->ban(1);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function testBanBannedAccount(OauthSteps $I) {
|
||||
$accessToken = $I->getAccessTokenByClientCredentialsGrant([P::BLOCK_ACCOUNT]);
|
||||
$I->amBearerAuthenticated($accessToken);
|
||||
|
||||
$this->route->ban(10);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'account' => 'error.account_already_banned',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
namespace api\tests\functional\accounts;
|
||||
|
||||
use api\tests\_pages\AccountsRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class ChangeEmailConfirmNewEmailCest {
|
||||
|
||||
/**
|
||||
* @var AccountsRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new AccountsRoute($I);
|
||||
}
|
||||
|
||||
public function testConfirmNewEmail(FunctionalTester $I) {
|
||||
$I->wantTo('change my email and get changed value');
|
||||
$I->amAuthenticated('CrafterGameplays');
|
||||
|
||||
$this->route->changeEmail(8, 'H28HBDCHHAG2HGHGHS');
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'email' => 'my-new-email@ely.by',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
45
api/tests/functional/accounts/ChangeEmailInitializeCest.php
Normal file
45
api/tests/functional/accounts/ChangeEmailInitializeCest.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
namespace api\tests\functional\accounts;
|
||||
|
||||
use api\tests\_pages\AccountsRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class ChangeEmailInitializeCest {
|
||||
|
||||
/**
|
||||
* @var AccountsRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new AccountsRoute($I);
|
||||
}
|
||||
|
||||
public function testChangeEmailInitialize(FunctionalTester $I) {
|
||||
$I->wantTo('send current email confirmation');
|
||||
$id = $I->amAuthenticated();
|
||||
|
||||
$this->route->changeEmailInitialize($id, 'password_0');
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function testChangeEmailInitializeFrequencyError(FunctionalTester $I) {
|
||||
$I->wantTo('see change email request frequency error');
|
||||
$id = $I->amAuthenticated('ILLIMUNATI');
|
||||
|
||||
$this->route->changeEmailInitialize($id, 'password_0');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'email' => 'error.recently_sent_message',
|
||||
],
|
||||
]);
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.data.canRepeatIn');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.data.repeatFrequency');
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
namespace api\tests\functional\accounts;
|
||||
|
||||
use api\tests\_pages\AccountsRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
use common\tests\helpers\Mock;
|
||||
use yii\validators\EmailValidator;
|
||||
|
||||
class ChangeEmailSubmitNewEmailCest {
|
||||
|
||||
/**
|
||||
* @var AccountsRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new AccountsRoute($I);
|
||||
}
|
||||
|
||||
public function testSubmitNewEmail(FunctionalTester $I) {
|
||||
Mock::func(EmailValidator::class, 'checkdnsrr')->andReturnTrue();
|
||||
|
||||
$I->wantTo('submit new email');
|
||||
$id = $I->amAuthenticated('ILLIMUNATI');
|
||||
|
||||
$this->route->changeEmailSubmitNewEmail($id, 'H27HBDCHHAG2HGHGHS', 'my-new-email@ely.by');
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
30
api/tests/functional/accounts/ChangeLangCest.php
Normal file
30
api/tests/functional/accounts/ChangeLangCest.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
namespace api\tests\functional\accounts;
|
||||
|
||||
use api\tests\_pages\AccountsRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class ChangeLangCest {
|
||||
|
||||
/**
|
||||
* @var AccountsRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new AccountsRoute($I);
|
||||
}
|
||||
|
||||
public function testSubmitNewEmail(FunctionalTester $I) {
|
||||
$I->wantTo('change my account language');
|
||||
$id = $I->amAuthenticated();
|
||||
|
||||
$this->route->changeLanguage($id, 'ru');
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
61
api/tests/functional/accounts/ChangePasswordCest.php
Normal file
61
api/tests/functional/accounts/ChangePasswordCest.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
namespace api\tests\functional\accounts;
|
||||
|
||||
use common\models\Account;
|
||||
use api\tests\_pages\AccountsRoute;
|
||||
use api\tests\_pages\AuthenticationRoute;
|
||||
use api\tests\functional\_steps\OauthSteps;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class ChangePasswordCest {
|
||||
|
||||
/**
|
||||
* @var AccountsRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new AccountsRoute($I);
|
||||
}
|
||||
|
||||
public function _after() {
|
||||
/** @var Account $account */
|
||||
$account = Account::findOne(1);
|
||||
$account->setPassword('password_0');
|
||||
$account->save();
|
||||
}
|
||||
|
||||
public function testChangePassword(FunctionalTester $I) {
|
||||
$I->wantTo('change my password');
|
||||
$id = $I->amAuthenticated();
|
||||
|
||||
$this->route->changePassword($id, 'password_0', 'new-password', 'new-password');
|
||||
$this->assertSuccessResponse($I);
|
||||
|
||||
$I->notLoggedIn();
|
||||
|
||||
$loginRoute = new AuthenticationRoute($I);
|
||||
$loginRoute->login('Admin', 'new-password');
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function testChangePasswordInternal(OauthSteps $I) {
|
||||
$accessToken = $I->getAccessTokenByClientCredentialsGrant(['change_account_password', 'escape_identity_verification']);
|
||||
$I->amBearerAuthenticated($accessToken);
|
||||
|
||||
$this->route->changePassword(1, null, 'new-password-1', 'new-password-1');
|
||||
$this->assertSuccessResponse($I);
|
||||
}
|
||||
|
||||
private function assertSuccessResponse(FunctionalTester $I): void {
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
69
api/tests/functional/accounts/ChangeUsernameCest.php
Normal file
69
api/tests/functional/accounts/ChangeUsernameCest.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
namespace api\tests\functional\accounts;
|
||||
|
||||
use common\models\Account;
|
||||
use api\tests\_pages\AccountsRoute;
|
||||
use api\tests\functional\_steps\OauthSteps;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class ChangeUsernameCest {
|
||||
|
||||
/**
|
||||
* @var AccountsRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new AccountsRoute($I);
|
||||
}
|
||||
|
||||
public function _after() {
|
||||
/** @var Account $account */
|
||||
$account = Account::findOne(1);
|
||||
$account->username = 'Admin';
|
||||
$account->save();
|
||||
}
|
||||
|
||||
public function testChangeUsername(FunctionalTester $I) {
|
||||
$I->wantTo('change my nickname');
|
||||
$id = $I->amAuthenticated();
|
||||
|
||||
$this->route->changeUsername($id, 'password_0', 'bruce_wayne');
|
||||
$this->assertSuccessResponse($I);
|
||||
}
|
||||
|
||||
public function testChangeUsernameNotAvailable(FunctionalTester $I) {
|
||||
$I->wantTo('see, that nickname "in use" is not available');
|
||||
$id = $I->amAuthenticated();
|
||||
|
||||
$this->route->changeUsername($id, 'password_0', 'Jon');
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'username' => 'error.username_not_available',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testChangeUsernameInternal(OauthSteps $I) {
|
||||
$accessToken = $I->getAccessTokenByClientCredentialsGrant(['change_account_username', 'escape_identity_verification']);
|
||||
$I->amBearerAuthenticated($accessToken);
|
||||
|
||||
$this->route->changeUsername(1, null, 'im_batman');
|
||||
$this->assertSuccessResponse($I);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FunctionalTester $I
|
||||
*/
|
||||
private function assertSuccessResponse(FunctionalTester $I): void {
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
61
api/tests/functional/accounts/DisableTwoFactorAuthCest.php
Normal file
61
api/tests/functional/accounts/DisableTwoFactorAuthCest.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
namespace api\tests\functional\accounts;
|
||||
|
||||
use OTPHP\TOTP;
|
||||
use api\tests\_pages\AccountsRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class DisableTwoFactorAuthCest {
|
||||
|
||||
/**
|
||||
* @var AccountsRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new AccountsRoute($I);
|
||||
}
|
||||
|
||||
public function testFails(FunctionalTester $I) {
|
||||
$accountId = $I->amAuthenticated('AccountWithEnabledOtp');
|
||||
|
||||
$this->route->disableTwoFactorAuth($accountId);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'totp' => 'error.totp_required',
|
||||
'password' => 'error.password_required',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->route->disableTwoFactorAuth($accountId, '123456', 'invalid_password');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'totp' => 'error.totp_incorrect',
|
||||
'password' => 'error.password_incorrect',
|
||||
],
|
||||
]);
|
||||
|
||||
$accountId = $I->amAuthenticated('AccountWithOtpSecret');
|
||||
$this->route->disableTwoFactorAuth($accountId, '123456', 'invalid_password');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'account' => 'error.otp_not_enabled',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testSuccessEnable(FunctionalTester $I) {
|
||||
$accountId = $I->amAuthenticated('AccountWithEnabledOtp');
|
||||
$totp = TOTP::create('BBBB');
|
||||
$this->route->disableTwoFactorAuth($accountId, $totp->now(), 'password_0');
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
72
api/tests/functional/accounts/EnableTwoFactorAuthCest.php
Normal file
72
api/tests/functional/accounts/EnableTwoFactorAuthCest.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
namespace api\tests\functional\accounts;
|
||||
|
||||
use OTPHP\TOTP;
|
||||
use api\tests\_pages\AccountsRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class EnableTwoFactorAuthCest {
|
||||
|
||||
/**
|
||||
* @var AccountsRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new AccountsRoute($I);
|
||||
}
|
||||
|
||||
public function testFails(FunctionalTester $I) {
|
||||
$accountId = $I->amAuthenticated('AccountWithOtpSecret');
|
||||
|
||||
$this->route->enableTwoFactorAuth($accountId);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'totp' => 'error.totp_required',
|
||||
'password' => 'error.password_required',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->route->enableTwoFactorAuth($accountId, '123456', 'invalid_password');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'totp' => 'error.totp_incorrect',
|
||||
'password' => 'error.password_incorrect',
|
||||
],
|
||||
]);
|
||||
|
||||
$accountId = $I->amAuthenticated('AccountWithEnabledOtp');
|
||||
$this->route->enableTwoFactorAuth($accountId, '123456', 'invalid_password');
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'account' => 'error.otp_already_enabled',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testSuccessEnable(FunctionalTester $I) {
|
||||
$accountId = $I->amAuthenticated('AccountWithOtpSecret');
|
||||
$totp = TOTP::create('AAAA');
|
||||
$this->route->enableTwoFactorAuth($accountId, $totp->now(), 'password_0');
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function testSuccessEnableWithNotSoExpiredCode(FunctionalTester $I) {
|
||||
$accountId = $I->amAuthenticated('AccountWithOtpSecret');
|
||||
$totp = TOTP::create('AAAA');
|
||||
$this->route->enableTwoFactorAuth($accountId, $totp->at(time() - 35), 'password_0');
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
114
api/tests/functional/accounts/GetCest.php
Normal file
114
api/tests/functional/accounts/GetCest.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
namespace api\tests\functional\accounts;
|
||||
|
||||
use api\tests\_pages\AccountsRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class GetCest {
|
||||
|
||||
/**
|
||||
* @var AccountsRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new AccountsRoute($I);
|
||||
}
|
||||
|
||||
public function testGetInfo(FunctionalTester $I) {
|
||||
$accountId = $I->amAuthenticated();
|
||||
|
||||
$this->route->get($accountId);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'id' => 1,
|
||||
'uuid' => 'df936908-b2e1-544d-96f8-2977ec213022',
|
||||
'username' => 'Admin',
|
||||
'isOtpEnabled' => false,
|
||||
'email' => 'admin@ely.by',
|
||||
'lang' => 'en',
|
||||
'isActive' => true,
|
||||
'hasMojangUsernameCollision' => false,
|
||||
'shouldAcceptRules' => false,
|
||||
'elyProfileLink' => 'http://ely.by/u1',
|
||||
]);
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.passwordChangedAt');
|
||||
}
|
||||
|
||||
public function testGetInfoAboutCurrentUser(FunctionalTester $I) {
|
||||
$I->wantTo('get info about user with 0 id, e.g. current');
|
||||
$I->amAuthenticated();
|
||||
|
||||
$this->route->get(0);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'id' => 1,
|
||||
'uuid' => 'df936908-b2e1-544d-96f8-2977ec213022',
|
||||
'username' => 'Admin',
|
||||
'isOtpEnabled' => false,
|
||||
'email' => 'admin@ely.by',
|
||||
'lang' => 'en',
|
||||
'isActive' => true,
|
||||
'hasMojangUsernameCollision' => false,
|
||||
'shouldAcceptRules' => false,
|
||||
'elyProfileLink' => 'http://ely.by/u1',
|
||||
]);
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.passwordChangedAt');
|
||||
}
|
||||
|
||||
public function testGetWithNotAcceptedLatestRules(FunctionalTester $I) {
|
||||
$accountId = $I->amAuthenticated('Veleyaba');
|
||||
|
||||
$this->route->get($accountId);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'id' => 9,
|
||||
'uuid' => '410462d3-8e71-47cc-bac6-64f77f88cf80',
|
||||
'username' => 'Veleyaba',
|
||||
'email' => 'veleyaba@gmail.com',
|
||||
'isOtpEnabled' => false,
|
||||
'lang' => 'en',
|
||||
'isActive' => true,
|
||||
'hasMojangUsernameCollision' => false,
|
||||
'shouldAcceptRules' => true,
|
||||
'elyProfileLink' => 'http://ely.by/u9',
|
||||
]);
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.passwordChangedAt');
|
||||
}
|
||||
|
||||
public function testGetInfoWithExpiredToken(FunctionalTester $I) {
|
||||
// Устанавливаем заведомо истёкший токен
|
||||
$I->amBearerAuthenticated(
|
||||
'eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE0NjQ2Mjc1NDUsImV4cCI6MTQ2NDYzMTE0NSwic3ViIjoiZWx5fDEiLCJlbHktc' .
|
||||
'2NvcGVzIjoiYWNjb3VudHNfd2ViX3VzZXIifQ.v1u8V5wk2RkWmnZtH3jZvM3zO1Gpgbp2DQFfLfy8jHY'
|
||||
);
|
||||
|
||||
$this->route->get(1);
|
||||
$I->canSeeResponseCodeIs(401);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'name' => 'Unauthorized',
|
||||
'message' => 'Token expired',
|
||||
'code' => 0,
|
||||
'status' => 401,
|
||||
]);
|
||||
}
|
||||
|
||||
public function testGetInfoNotCurrentAccount(FunctionalTester $I) {
|
||||
$I->amAuthenticated();
|
||||
|
||||
$this->route->get(10);
|
||||
$I->canSeeResponseCodeIs(403);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'name' => 'Forbidden',
|
||||
'message' => 'You are not allowed to perform this action.',
|
||||
'code' => 0,
|
||||
'status' => 403,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
47
api/tests/functional/accounts/PardonCest.php
Normal file
47
api/tests/functional/accounts/PardonCest.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
namespace api\tests\functional\accounts;
|
||||
|
||||
use common\rbac\Permissions as P;
|
||||
use api\tests\_pages\AccountsRoute;
|
||||
use api\tests\functional\_steps\OauthSteps;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class PardonCest {
|
||||
|
||||
/**
|
||||
* @var AccountsRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new AccountsRoute($I);
|
||||
}
|
||||
|
||||
public function testPardonAccount(OauthSteps $I) {
|
||||
$accessToken = $I->getAccessTokenByClientCredentialsGrant([P::BLOCK_ACCOUNT]);
|
||||
$I->amBearerAuthenticated($accessToken);
|
||||
|
||||
$this->route->pardon(10);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function testPardonNotBannedAccount(OauthSteps $I) {
|
||||
$accessToken = $I->getAccessTokenByClientCredentialsGrant([P::BLOCK_ACCOUNT]);
|
||||
$I->amBearerAuthenticated($accessToken);
|
||||
|
||||
$this->route->pardon(1);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'account' => 'error.account_not_banned',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
namespace api\tests\functional\accounts;
|
||||
|
||||
use api\tests\_pages\AccountsRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class TwoFactorAuthCredentialsCest {
|
||||
|
||||
/**
|
||||
* @var AccountsRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new AccountsRoute($I);
|
||||
}
|
||||
|
||||
public function testGetCredentials(FunctionalTester $I) {
|
||||
$accountId = $I->amAuthenticated();
|
||||
$this->route->getTwoFactorAuthCredentials($accountId);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.secret');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.uri');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.qr');
|
||||
}
|
||||
|
||||
}
|
158
api/tests/functional/authserver/AuthorizationCest.php
Normal file
158
api/tests/functional/authserver/AuthorizationCest.php
Normal file
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
namespace api\tests\functional\authserver;
|
||||
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use api\tests\_pages\AuthserverRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class AuthorizationCest {
|
||||
|
||||
/**
|
||||
* @var AuthserverRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new AuthserverRoute($I);
|
||||
}
|
||||
|
||||
public function byName(FunctionalTester $I) {
|
||||
$I->wantTo('authenticate by username and password');
|
||||
$this->route->authenticate([
|
||||
'username' => 'admin',
|
||||
'password' => 'password_0',
|
||||
'clientToken' => Uuid::uuid4()->toString(),
|
||||
]);
|
||||
|
||||
$this->testSuccessResponse($I);
|
||||
}
|
||||
|
||||
public function byEmail(FunctionalTester $I) {
|
||||
$I->wantTo('authenticate by email and password');
|
||||
$this->route->authenticate([
|
||||
'username' => 'admin@ely.by',
|
||||
'password' => 'password_0',
|
||||
'clientToken' => Uuid::uuid4()->toString(),
|
||||
]);
|
||||
|
||||
$this->testSuccessResponse($I);
|
||||
}
|
||||
|
||||
public function byNamePassedViaPOSTBody(FunctionalTester $I) {
|
||||
$I->wantTo('authenticate by username and password sent via post body');
|
||||
$this->route->authenticate(json_encode([
|
||||
'username' => 'admin',
|
||||
'password' => 'password_0',
|
||||
'clientToken' => Uuid::uuid4()->toString(),
|
||||
]));
|
||||
|
||||
$this->testSuccessResponse($I);
|
||||
}
|
||||
|
||||
public function byEmailWithEnabledTwoFactorAuth(FunctionalTester $I) {
|
||||
$I->wantTo('get valid error by authenticate account with enabled two factor auth');
|
||||
$this->route->authenticate([
|
||||
'username' => 'otp@gmail.com',
|
||||
'password' => 'password_0',
|
||||
'clientToken' => Uuid::uuid4()->toString(),
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(401);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'ForbiddenOperationException',
|
||||
'errorMessage' => 'Account protected with two factor auth.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function byEmailWithParamsAsJsonInPostBody(FunctionalTester $I) {
|
||||
$I->wantTo('authenticate by email and password, passing values as serialized string in post body');
|
||||
$this->route->authenticate(json_encode([
|
||||
'username' => 'admin@ely.by',
|
||||
'password' => 'password_0',
|
||||
'clientToken' => Uuid::uuid4()->toString(),
|
||||
]));
|
||||
|
||||
$this->testSuccessResponse($I);
|
||||
}
|
||||
|
||||
public function longClientToken(FunctionalTester $I) {
|
||||
$I->wantTo('send non uuid clientToken, but less then 255 characters');
|
||||
$this->route->authenticate([
|
||||
'username' => 'admin@ely.by',
|
||||
'password' => 'password_0',
|
||||
'clientToken' => str_pad('', 255, 'x'),
|
||||
]);
|
||||
$this->testSuccessResponse($I);
|
||||
}
|
||||
|
||||
public function tooLongClientToken(FunctionalTester $I) {
|
||||
$I->wantTo('send non uuid clientToken with more then 255 characters length');
|
||||
$this->route->authenticate([
|
||||
'username' => 'admin@ely.by',
|
||||
'password' => 'password_0',
|
||||
'clientToken' => str_pad('', 256, 'x'),
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'IllegalArgumentException',
|
||||
'errorMessage' => 'clientToken is too long.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function wrongArguments(FunctionalTester $I) {
|
||||
$I->wantTo('get error on wrong amount of arguments');
|
||||
$this->route->authenticate([
|
||||
'key' => 'value',
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'IllegalArgumentException',
|
||||
'errorMessage' => 'credentials can not be null.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function wrongNicknameAndPassword(FunctionalTester $I) {
|
||||
$I->wantTo('authenticate by username and password with wrong data');
|
||||
$this->route->authenticate([
|
||||
'username' => 'nonexistent_user',
|
||||
'password' => 'nonexistent_password',
|
||||
'clientToken' => Uuid::uuid4()->toString(),
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(401);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'ForbiddenOperationException',
|
||||
'errorMessage' => 'Invalid credentials. Invalid nickname or password.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function bannedAccount(FunctionalTester $I) {
|
||||
$I->wantTo('authenticate in suspended account');
|
||||
$this->route->authenticate([
|
||||
'username' => 'Banned',
|
||||
'password' => 'password_0',
|
||||
'clientToken' => Uuid::uuid4()->toString(),
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(401);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'ForbiddenOperationException',
|
||||
'errorMessage' => 'This account has been suspended.',
|
||||
]);
|
||||
}
|
||||
|
||||
private function testSuccessResponse(FunctionalTester $I) {
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->seeResponseIsJson();
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.accessToken');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.clientToken');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.availableProfiles[0].id');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.availableProfiles[0].name');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.availableProfiles[0].legacy');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.selectedProfile.id');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.selectedProfile.name');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.selectedProfile.legacy');
|
||||
}
|
||||
|
||||
}
|
53
api/tests/functional/authserver/InvalidateCest.php
Normal file
53
api/tests/functional/authserver/InvalidateCest.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
namespace api\tests\functional\authserver;
|
||||
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use api\tests\_pages\AuthserverRoute;
|
||||
use api\tests\functional\_steps\AuthserverSteps;
|
||||
|
||||
class InvalidateCest {
|
||||
|
||||
/**
|
||||
* @var AuthserverRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(AuthserverSteps $I) {
|
||||
$this->route = new AuthserverRoute($I);
|
||||
}
|
||||
|
||||
public function invalidate(AuthserverSteps $I) {
|
||||
$I->wantTo('invalidate my token');
|
||||
[$accessToken, $clientToken] = $I->amAuthenticated();
|
||||
$this->route->invalidate([
|
||||
'accessToken' => $accessToken,
|
||||
'clientToken' => $clientToken,
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseEquals('');
|
||||
}
|
||||
|
||||
public function wrongArguments(AuthserverSteps $I) {
|
||||
$I->wantTo('get error on wrong amount of arguments');
|
||||
$this->route->invalidate([
|
||||
'key' => 'value',
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'IllegalArgumentException',
|
||||
'errorMessage' => 'credentials can not be null.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function wrongAccessTokenOrClientToken(AuthserverSteps $I) {
|
||||
$I->wantTo('invalidate by wrong client and access token');
|
||||
$this->route->invalidate([
|
||||
'accessToken' => Uuid::uuid4()->toString(),
|
||||
'clientToken' => Uuid::uuid4()->toString(),
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseEquals('');
|
||||
}
|
||||
|
||||
}
|
77
api/tests/functional/authserver/RefreshCest.php
Normal file
77
api/tests/functional/authserver/RefreshCest.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
namespace api\tests\functional\authserver;
|
||||
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use api\tests\_pages\AuthserverRoute;
|
||||
use api\tests\functional\_steps\AuthserverSteps;
|
||||
|
||||
class RefreshCest {
|
||||
|
||||
/**
|
||||
* @var AuthserverRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(AuthserverSteps $I) {
|
||||
$this->route = new AuthserverRoute($I);
|
||||
}
|
||||
|
||||
public function refresh(AuthserverSteps $I) {
|
||||
$I->wantTo('refresh my accessToken');
|
||||
[$accessToken, $clientToken] = $I->amAuthenticated();
|
||||
$this->route->refresh([
|
||||
'accessToken' => $accessToken,
|
||||
'clientToken' => $clientToken,
|
||||
]);
|
||||
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->seeResponseIsJson();
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.accessToken');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.clientToken');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.selectedProfile.id');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.selectedProfile.name');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.selectedProfile.legacy');
|
||||
$I->cantSeeResponseJsonMatchesJsonPath('$.availableProfiles');
|
||||
}
|
||||
|
||||
public function wrongArguments(AuthserverSteps $I) {
|
||||
$I->wantTo('get error on wrong amount of arguments');
|
||||
$this->route->refresh([
|
||||
'key' => 'value',
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'IllegalArgumentException',
|
||||
'errorMessage' => 'credentials can not be null.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function wrongAccessToken(AuthserverSteps $I) {
|
||||
$I->wantTo('get error on wrong access or client tokens');
|
||||
$this->route->refresh([
|
||||
'accessToken' => Uuid::uuid4()->toString(),
|
||||
'clientToken' => Uuid::uuid4()->toString(),
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(401);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'ForbiddenOperationException',
|
||||
'errorMessage' => 'Invalid token.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function refreshTokenFromBannedUser(AuthserverSteps $I) {
|
||||
$I->wantTo('refresh token from suspended account');
|
||||
$this->route->refresh([
|
||||
'accessToken' => '918ecb41-616c-40ee-a7d2-0b0ef0d0d732',
|
||||
'clientToken' => '6042634a-a1e2-4aed-866c-c661fe4e63e2',
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(401);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'ForbiddenOperationException',
|
||||
'errorMessage' => 'This account has been suspended.',
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
75
api/tests/functional/authserver/SignoutCest.php
Normal file
75
api/tests/functional/authserver/SignoutCest.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
namespace api\tests\functional\authserver;
|
||||
|
||||
use api\tests\_pages\AuthserverRoute;
|
||||
use api\tests\functional\_steps\AuthserverSteps;
|
||||
|
||||
class SignoutCest {
|
||||
|
||||
/**
|
||||
* @var AuthserverRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(AuthserverSteps $I) {
|
||||
$this->route = new AuthserverRoute($I);
|
||||
}
|
||||
|
||||
public function byName(AuthserverSteps $I) {
|
||||
$I->wantTo('signout by nickname and password');
|
||||
$this->route->signout([
|
||||
'username' => 'admin',
|
||||
'password' => 'password_0',
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseEquals('');
|
||||
}
|
||||
|
||||
public function byEmail(AuthserverSteps $I) {
|
||||
$I->wantTo('signout by email and password');
|
||||
$this->route->signout([
|
||||
'username' => 'admin@ely.by',
|
||||
'password' => 'password_0',
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseEquals('');
|
||||
}
|
||||
|
||||
public function wrongArguments(AuthserverSteps $I) {
|
||||
$I->wantTo('get error on wrong amount of arguments');
|
||||
$this->route->signout([
|
||||
'key' => 'value',
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'IllegalArgumentException',
|
||||
'errorMessage' => 'credentials can not be null.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function wrongNicknameAndPassword(AuthserverSteps $I) {
|
||||
$I->wantTo('signout by nickname and password with wrong data');
|
||||
$this->route->signout([
|
||||
'username' => 'nonexistent_user',
|
||||
'password' => 'nonexistent_password',
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(401);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'ForbiddenOperationException',
|
||||
'errorMessage' => 'Invalid credentials. Invalid nickname or password.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function bannedAccount(AuthserverSteps $I) {
|
||||
$I->wantTo('signout from banned account');
|
||||
$this->route->signout([
|
||||
'username' => 'Banned',
|
||||
'password' => 'password_0',
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseEquals('');
|
||||
}
|
||||
|
||||
}
|
69
api/tests/functional/authserver/ValidateCest.php
Normal file
69
api/tests/functional/authserver/ValidateCest.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
namespace api\tests\functional\authserver;
|
||||
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use api\tests\_pages\AuthserverRoute;
|
||||
use api\tests\functional\_steps\AuthserverSteps;
|
||||
|
||||
class ValidateCest {
|
||||
|
||||
/**
|
||||
* @var AuthserverRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(AuthserverSteps $I) {
|
||||
$this->route = new AuthserverRoute($I);
|
||||
}
|
||||
|
||||
public function validate(AuthserverSteps $I) {
|
||||
$I->wantTo('validate my accessToken');
|
||||
[$accessToken] = $I->amAuthenticated();
|
||||
$this->route->validate([
|
||||
'accessToken' => $accessToken,
|
||||
]);
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->canSeeResponseEquals('');
|
||||
}
|
||||
|
||||
public function wrongArguments(AuthserverSteps $I) {
|
||||
$I->wantTo('get error on wrong amount of arguments');
|
||||
$this->route->validate([
|
||||
'key' => 'value',
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'IllegalArgumentException',
|
||||
'errorMessage' => 'credentials can not be null.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function wrongAccessToken(AuthserverSteps $I) {
|
||||
$I->wantTo('get error on wrong accessToken');
|
||||
$this->route->validate([
|
||||
'accessToken' => Uuid::uuid4()->toString(),
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(401);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'ForbiddenOperationException',
|
||||
'errorMessage' => 'Invalid token.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function expiredAccessToken(AuthserverSteps $I) {
|
||||
$I->wantTo('get error on expired accessToken');
|
||||
$this->route->validate([
|
||||
// Заведомо истёкший токен из дампа
|
||||
'accessToken' => '6042634a-a1e2-4aed-866c-c661fe4e63e2',
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(401);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'ForbiddenOperationException',
|
||||
'errorMessage' => 'Token expired.',
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
73
api/tests/functional/internal/InfoCest.php
Normal file
73
api/tests/functional/internal/InfoCest.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
namespace api\tests\functional\internal;
|
||||
|
||||
use api\tests\_pages\InternalRoute;
|
||||
use api\tests\functional\_steps\OauthSteps;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class InfoCest {
|
||||
|
||||
/**
|
||||
* @var InternalRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new InternalRoute($I);
|
||||
}
|
||||
|
||||
public function testGetInfoById(OauthSteps $I) {
|
||||
$accessToken = $I->getAccessTokenByClientCredentialsGrant(['internal_account_info']);
|
||||
$I->amBearerAuthenticated($accessToken);
|
||||
|
||||
$this->route->info('id', 1);
|
||||
$this->expectSuccessResponse($I);
|
||||
}
|
||||
|
||||
public function testGetInfoByUuid(OauthSteps $I) {
|
||||
$accessToken = $I->getAccessTokenByClientCredentialsGrant(['internal_account_info']);
|
||||
$I->amBearerAuthenticated($accessToken);
|
||||
|
||||
$this->route->info('uuid', 'df936908-b2e1-544d-96f8-2977ec213022');
|
||||
$this->expectSuccessResponse($I);
|
||||
}
|
||||
|
||||
public function testGetInfoByUsername(OauthSteps $I) {
|
||||
$accessToken = $I->getAccessTokenByClientCredentialsGrant(['internal_account_info']);
|
||||
$I->amBearerAuthenticated($accessToken);
|
||||
|
||||
$this->route->info('username', 'admin');
|
||||
$this->expectSuccessResponse($I);
|
||||
}
|
||||
|
||||
public function testInvalidParams(OauthSteps $I) {
|
||||
$accessToken = $I->getAccessTokenByClientCredentialsGrant(['internal_account_info']);
|
||||
$I->amBearerAuthenticated($accessToken);
|
||||
|
||||
$this->route->info('', '');
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
}
|
||||
|
||||
public function testAccountNotFound(OauthSteps $I) {
|
||||
$accessToken = $I->getAccessTokenByClientCredentialsGrant(['internal_account_info']);
|
||||
$I->amBearerAuthenticated($accessToken);
|
||||
|
||||
$this->route->info('username', 'this-user-not-exists');
|
||||
$I->canSeeResponseCodeIs(404);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param OauthSteps $I
|
||||
*/
|
||||
private function expectSuccessResponse(OauthSteps $I): void {
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'id' => 1,
|
||||
'uuid' => 'df936908-b2e1-544d-96f8-2977ec213022',
|
||||
'email' => 'admin@ely.by',
|
||||
'username' => 'Admin',
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
67
api/tests/functional/mojang/UsernameToUuidCest.php
Normal file
67
api/tests/functional/mojang/UsernameToUuidCest.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
namespace api\tests\functional\authserver;
|
||||
|
||||
use api\tests\_pages\MojangApiRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class UsernameToUuidCest {
|
||||
|
||||
/**
|
||||
* @var MojangApiRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new MojangApiRoute($I);
|
||||
}
|
||||
|
||||
public function getUuidByUsername(FunctionalTester $I) {
|
||||
$I->wantTo('get user uuid by his username');
|
||||
$this->route->usernameToUuid('Admin');
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'id' => 'df936908b2e1544d96f82977ec213022',
|
||||
'name' => 'Admin',
|
||||
]);
|
||||
}
|
||||
|
||||
public function getUuidByUsernameAtMoment(FunctionalTester $I) {
|
||||
$I->wantTo('get user uuid by his username at fixed moment');
|
||||
$this->route->usernameToUuid('klik201', 1474404142);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'id' => 'd6b3e93564664cb886dbb5df91ae6541',
|
||||
'name' => 'klik202',
|
||||
]);
|
||||
}
|
||||
|
||||
public function getUuidByUsernameAtWrongMoment(FunctionalTester $I) {
|
||||
$I->wantTo('get 204 if passed once used, but changed username at moment, when it was changed');
|
||||
$this->route->usernameToUuid('klik201', 1474404144);
|
||||
$I->canSeeResponseCodeIs(204);
|
||||
$I->canSeeResponseEquals('');
|
||||
}
|
||||
|
||||
public function getUuidByUsernameWithoutMoment(FunctionalTester $I) {
|
||||
$I->wantTo('get 204 if username not busy and not passed valid time mark, when it was busy');
|
||||
$this->route->usernameToUuid('klik201');
|
||||
$I->canSeeResponseCodeIs(204);
|
||||
$I->canSeeResponseEquals('');
|
||||
}
|
||||
|
||||
public function getUuidByWrongUsername(FunctionalTester $I) {
|
||||
$I->wantTo('get user uuid by some wrong username');
|
||||
$this->route->usernameToUuid('not-exists-user');
|
||||
$I->canSeeResponseCodeIs(204);
|
||||
$I->canSeeResponseEquals('');
|
||||
}
|
||||
|
||||
public function nonPassedUsername(FunctionalTester $I) {
|
||||
$I->wantTo('get 404 on not passed username');
|
||||
$this->route->usernameToUuid('');
|
||||
$I->canSeeResponseCodeIs(404);
|
||||
}
|
||||
|
||||
}
|
121
api/tests/functional/mojang/UsernamesToUuidsCest.php
Normal file
121
api/tests/functional/mojang/UsernamesToUuidsCest.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
namespace api\tests\functional\authserver;
|
||||
|
||||
use api\tests\_pages\MojangApiRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class UsernamesToUuidsCest {
|
||||
|
||||
/**
|
||||
* @var MojangApiRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new MojangApiRoute($I);
|
||||
}
|
||||
|
||||
public function geUuidByOneUsername(FunctionalTester $I) {
|
||||
$I->wantTo('get uuid by one username');
|
||||
$this->route->uuidsByUsernames(['Admin']);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
[
|
||||
'id' => 'df936908b2e1544d96f82977ec213022',
|
||||
'name' => 'Admin',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function getUuidsByUsernames(FunctionalTester $I) {
|
||||
$I->wantTo('get uuids by few usernames');
|
||||
$this->route->uuidsByUsernames(['Admin', 'AccWithOldPassword', 'Notch']);
|
||||
$this->validateFewValidUsernames($I);
|
||||
}
|
||||
|
||||
public function getUuidsByUsernamesWithPostString(FunctionalTester $I) {
|
||||
$I->wantTo('get uuids by few usernames');
|
||||
$this->route->uuidsByUsernames(json_encode(['Admin', 'AccWithOldPassword', 'Notch']));
|
||||
$this->validateFewValidUsernames($I);
|
||||
}
|
||||
|
||||
public function getUuidsByPartialNonexistentUsernames(FunctionalTester $I) {
|
||||
$I->wantTo('get uuids by few usernames and some nonexistent');
|
||||
$this->route->uuidsByUsernames(['Admin', 'not-exists-user']);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
[
|
||||
'id' => 'df936908b2e1544d96f82977ec213022',
|
||||
'name' => 'Admin',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function passAllNonexistentUsernames(FunctionalTester $I) {
|
||||
$I->wantTo('get specific response when pass all nonexistent usernames');
|
||||
$this->route->uuidsByUsernames(['not-exists-1', 'not-exists-2']);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([]);
|
||||
}
|
||||
|
||||
public function passTooManyUsernames(FunctionalTester $I) {
|
||||
$I->wantTo('get specific response when pass too many usernames');
|
||||
$usernames = [];
|
||||
for ($i = 0; $i < 150; $i++) {
|
||||
$usernames[] = random_bytes(10);
|
||||
}
|
||||
|
||||
$this->route->uuidsByUsernames($usernames);
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'IllegalArgumentException',
|
||||
'errorMessage' => 'Not more that 100 profile name per call is allowed.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function passEmptyUsername(FunctionalTester $I) {
|
||||
$I->wantTo('get specific response when pass empty username');
|
||||
$this->route->uuidsByUsernames(['Admin', '']);
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'IllegalArgumentException',
|
||||
'errorMessage' => 'profileName can not be null, empty or array key.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function passEmptyField(FunctionalTester $I) {
|
||||
$I->wantTo('get response when pass empty array');
|
||||
$this->route->uuidsByUsernames([]);
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'IllegalArgumentException',
|
||||
'errorMessage' => 'Passed array of profile names is an invalid JSON string.',
|
||||
]);
|
||||
}
|
||||
|
||||
private function validateFewValidUsernames(FunctionalTester $I) {
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
[
|
||||
'id' => 'df936908b2e1544d96f82977ec213022',
|
||||
'name' => 'Admin',
|
||||
],
|
||||
[
|
||||
'id' => 'bdc239f08a22518d8b93f02d4827c3eb',
|
||||
'name' => 'AccWithOldPassword',
|
||||
],
|
||||
[
|
||||
'id' => '4aaf4f003b5b4d3692529e8ee0c86679',
|
||||
'name' => 'Notch',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
69
api/tests/functional/mojang/UuidToUsernamesHistoryCest.php
Normal file
69
api/tests/functional/mojang/UuidToUsernamesHistoryCest.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
namespace api\tests\functional\authserver;
|
||||
|
||||
use Faker\Provider\Uuid;
|
||||
use api\tests\_pages\MojangApiRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class UuidToUsernamesHistoryCest {
|
||||
|
||||
/**
|
||||
* @var MojangApiRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new MojangApiRoute($I);
|
||||
}
|
||||
|
||||
public function getUsernameByUuid(FunctionalTester $I) {
|
||||
$I->wantTo('get usernames history by uuid for user, without history');
|
||||
$this->route->usernamesByUuid('df936908b2e1544d96f82977ec213022');
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
[
|
||||
'name' => 'Admin',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function getUsernameByUuidWithHistory(FunctionalTester $I) {
|
||||
$I->wantTo('get usernames history by dashed uuid and expect history with time marks');
|
||||
$this->route->usernamesByUuid('d6b3e935-6466-4cb8-86db-b5df91ae6541');
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
[
|
||||
'name' => 'klik202',
|
||||
],
|
||||
[
|
||||
'name' => 'klik201',
|
||||
'changedToAt' => 1474404141000,
|
||||
],
|
||||
[
|
||||
'name' => 'klik202',
|
||||
'changedToAt' => 1474404143000,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function passWrongUuid(FunctionalTester $I) {
|
||||
$I->wantTo('get user username by some wrong uuid');
|
||||
$this->route->usernamesByUuid(Uuid::uuid());
|
||||
$I->canSeeResponseCodeIs(204);
|
||||
$I->canSeeResponseEquals('');
|
||||
}
|
||||
|
||||
public function passWrongUuidFormat(FunctionalTester $I) {
|
||||
$I->wantTo('call profile route with invalid uuid string');
|
||||
$this->route->usernamesByUuid('bla-bla-bla');
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'IllegalArgumentException',
|
||||
'errorMessage' => 'Invalid uuid format.',
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
113
api/tests/functional/oauth/AccessTokenCest.php
Normal file
113
api/tests/functional/oauth/AccessTokenCest.php
Normal file
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
namespace api\tests\functional\oauth;
|
||||
|
||||
use api\tests\_pages\OauthRoute;
|
||||
use api\tests\functional\_steps\OauthSteps;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class AccessTokenCest {
|
||||
|
||||
/**
|
||||
* @var OauthRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new OauthRoute($I);
|
||||
}
|
||||
|
||||
public function testIssueTokenWithWrongArgs(OauthSteps $I) {
|
||||
$I->wantTo('check behavior on on request without any credentials');
|
||||
$this->route->issueToken();
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'invalid_request',
|
||||
'message' => 'The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Check the "grant_type" parameter.',
|
||||
]);
|
||||
|
||||
$I->wantTo('check behavior on passing invalid auth code');
|
||||
$this->route->issueToken($this->buildParams(
|
||||
'wrong-auth-code',
|
||||
'ely',
|
||||
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
|
||||
'http://ely.by'
|
||||
));
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'invalid_request',
|
||||
'message' => 'The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Check the "code" parameter.',
|
||||
]);
|
||||
|
||||
$authCode = $I->getAuthCode();
|
||||
$I->wantTo('check behavior on passing invalid redirect_uri');
|
||||
$this->route->issueToken($this->buildParams(
|
||||
$authCode,
|
||||
'ely',
|
||||
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
|
||||
'http://some-other.domain'
|
||||
));
|
||||
$I->canSeeResponseCodeIs(401);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'invalid_client',
|
||||
'message' => 'Client authentication failed.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function testIssueToken(OauthSteps $I) {
|
||||
$authCode = $I->getAuthCode();
|
||||
$this->route->issueToken($this->buildParams(
|
||||
$authCode,
|
||||
'ely',
|
||||
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
|
||||
'http://ely.by'
|
||||
));
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'token_type' => 'Bearer',
|
||||
]);
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.access_token');
|
||||
$I->cantSeeResponseJsonMatchesJsonPath('$.refresh_token');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.expires_in');
|
||||
}
|
||||
|
||||
public function testIssueTokenWithRefreshToken(OauthSteps $I) {
|
||||
$authCode = $I->getAuthCode(['offline_access']);
|
||||
$this->route->issueToken($this->buildParams(
|
||||
$authCode,
|
||||
'ely',
|
||||
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
|
||||
'http://ely.by'
|
||||
));
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'token_type' => 'Bearer',
|
||||
]);
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.access_token');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.refresh_token');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.expires_in');
|
||||
}
|
||||
|
||||
private function buildParams($code = null, $clientId = null, $clientSecret = null, $redirectUri = null) {
|
||||
$params = ['grant_type' => 'authorization_code'];
|
||||
if ($code !== null) {
|
||||
$params['code'] = $code;
|
||||
}
|
||||
|
||||
if ($clientId !== null) {
|
||||
$params['client_id'] = $clientId;
|
||||
}
|
||||
|
||||
if ($clientSecret !== null) {
|
||||
$params['client_secret'] = $clientSecret;
|
||||
}
|
||||
|
||||
if ($redirectUri !== null) {
|
||||
$params['redirect_uri'] = $redirectUri;
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
}
|
304
api/tests/functional/oauth/AuthCodeCest.php
Normal file
304
api/tests/functional/oauth/AuthCodeCest.php
Normal file
@@ -0,0 +1,304 @@
|
||||
<?php
|
||||
namespace api\tests\functional\oauth;
|
||||
|
||||
use common\rbac\Permissions as P;
|
||||
use api\tests\_pages\OauthRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class AuthCodeCest {
|
||||
|
||||
/**
|
||||
* @var OauthRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new OauthRoute($I);
|
||||
}
|
||||
|
||||
public function testValidateRequest(FunctionalTester $I) {
|
||||
$this->testOauthParamsValidation($I, 'validate');
|
||||
|
||||
$I->wantTo('validate and obtain information about new auth request');
|
||||
$this->route->validate($this->buildQueryParams(
|
||||
'ely',
|
||||
'http://ely.by',
|
||||
'code',
|
||||
[P::MINECRAFT_SERVER_SESSION, 'account_info', 'account_email'],
|
||||
'test-state'
|
||||
));
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
'oAuth' => [
|
||||
'client_id' => 'ely',
|
||||
'redirect_uri' => 'http://ely.by',
|
||||
'response_type' => 'code',
|
||||
'scope' => 'minecraft_server_session,account_info,account_email',
|
||||
'state' => 'test-state',
|
||||
],
|
||||
'client' => [
|
||||
'id' => 'ely',
|
||||
'name' => 'Ely.by',
|
||||
'description' => 'Всем знакомое елуби',
|
||||
],
|
||||
'session' => [
|
||||
'scopes' => [
|
||||
'minecraft_server_session',
|
||||
'account_info',
|
||||
'account_email',
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testValidateWithDescriptionReplaceRequest(FunctionalTester $I) {
|
||||
$I->amAuthenticated();
|
||||
$I->wantTo('validate and get information with description replacement');
|
||||
$this->route->validate($this->buildQueryParams(
|
||||
'ely',
|
||||
'http://ely.by',
|
||||
'code',
|
||||
null,
|
||||
null,
|
||||
[
|
||||
'description' => 'all familiar eliby',
|
||||
]
|
||||
));
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'client' => [
|
||||
'description' => 'all familiar eliby',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testCompleteValidationAction(FunctionalTester $I) {
|
||||
$I->amAuthenticated();
|
||||
$I->wantTo('validate all oAuth params on complete request');
|
||||
$this->testOauthParamsValidation($I, 'complete');
|
||||
}
|
||||
|
||||
public function testCompleteActionOnWrongConditions(FunctionalTester $I) {
|
||||
$I->amAuthenticated();
|
||||
|
||||
$I->wantTo('get accept_required if I don\'t require any scope, but this is first time request');
|
||||
$this->route->complete($this->buildQueryParams(
|
||||
'ely',
|
||||
'http://ely.by',
|
||||
'code'
|
||||
));
|
||||
$I->canSeeResponseCodeIs(401);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'error' => 'accept_required',
|
||||
'parameter' => '',
|
||||
'statusCode' => 401,
|
||||
]);
|
||||
|
||||
$I->wantTo('get accept_required if I require some scopes on first time');
|
||||
$this->route->complete($this->buildQueryParams(
|
||||
'ely',
|
||||
'http://ely.by',
|
||||
'code',
|
||||
[P::MINECRAFT_SERVER_SESSION]
|
||||
));
|
||||
$I->canSeeResponseCodeIs(401);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'error' => 'accept_required',
|
||||
'parameter' => '',
|
||||
'statusCode' => 401,
|
||||
]);
|
||||
}
|
||||
|
||||
public function testCompleteActionSuccess(FunctionalTester $I) {
|
||||
$I->amAuthenticated();
|
||||
$I->wantTo('get auth code if I require some scope and pass accept field');
|
||||
$this->route->complete($this->buildQueryParams(
|
||||
'ely',
|
||||
'http://ely.by',
|
||||
'code',
|
||||
[P::MINECRAFT_SERVER_SESSION]
|
||||
), ['accept' => true]);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.redirectUri');
|
||||
|
||||
$I->wantTo('get auth code if I don\'t require any scope and don\'t pass accept field, but previously have ' .
|
||||
'successful request');
|
||||
$this->route->complete($this->buildQueryParams(
|
||||
'ely',
|
||||
'http://ely.by',
|
||||
'code'
|
||||
));
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.redirectUri');
|
||||
|
||||
$I->wantTo('get auth code if I require some scopes and don\'t pass accept field, but previously have successful ' .
|
||||
'request with same scopes');
|
||||
$this->route->complete($this->buildQueryParams(
|
||||
'ely',
|
||||
'http://ely.by',
|
||||
'code',
|
||||
[P::MINECRAFT_SERVER_SESSION]
|
||||
));
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.redirectUri');
|
||||
}
|
||||
|
||||
public function testAcceptRequiredOnNewScope(FunctionalTester $I) {
|
||||
$I->amAuthenticated();
|
||||
$I->wantTo('get accept_required if I have previous successful request, but now require some new scope');
|
||||
$this->route->complete($this->buildQueryParams(
|
||||
'ely',
|
||||
'http://ely.by',
|
||||
'code',
|
||||
[P::MINECRAFT_SERVER_SESSION]
|
||||
), ['accept' => true]);
|
||||
$this->route->complete($this->buildQueryParams(
|
||||
'ely',
|
||||
'http://ely.by',
|
||||
'code',
|
||||
[P::MINECRAFT_SERVER_SESSION, 'account_info']
|
||||
));
|
||||
$I->canSeeResponseCodeIs(401);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'error' => 'accept_required',
|
||||
'parameter' => '',
|
||||
'statusCode' => 401,
|
||||
]);
|
||||
}
|
||||
|
||||
public function testCompleteActionWithDismissState(FunctionalTester $I) {
|
||||
$I->amAuthenticated();
|
||||
$I->wantTo('get access_denied error if I pass accept in false state');
|
||||
$this->route->complete($this->buildQueryParams(
|
||||
'ely',
|
||||
'http://ely.by',
|
||||
'code',
|
||||
[P::MINECRAFT_SERVER_SESSION]
|
||||
), ['accept' => false]);
|
||||
$I->canSeeResponseCodeIs(401);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'error' => 'access_denied',
|
||||
'parameter' => '',
|
||||
'statusCode' => 401,
|
||||
]);
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.redirectUri');
|
||||
}
|
||||
|
||||
private function buildQueryParams(
|
||||
$clientId = null,
|
||||
$redirectUri = null,
|
||||
$responseType = null,
|
||||
$scopes = [],
|
||||
$state = null,
|
||||
$customData = []
|
||||
) {
|
||||
$params = $customData;
|
||||
if ($clientId !== null) {
|
||||
$params['client_id'] = $clientId;
|
||||
}
|
||||
|
||||
if ($redirectUri !== null) {
|
||||
$params['redirect_uri'] = $redirectUri;
|
||||
}
|
||||
|
||||
if ($responseType !== null) {
|
||||
$params['response_type'] = $responseType;
|
||||
}
|
||||
|
||||
if ($state !== null) {
|
||||
$params['state'] = $state;
|
||||
}
|
||||
|
||||
if (!empty($scopes)) {
|
||||
if (is_array($scopes)) {
|
||||
$scopes = implode(',', $scopes);
|
||||
}
|
||||
|
||||
$params['scope'] = $scopes;
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
private function testOauthParamsValidation(FunctionalTester $I, $action) {
|
||||
$I->wantTo('check behavior on invalid request without one or few params');
|
||||
$this->route->$action($this->buildQueryParams());
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'error' => 'invalid_request',
|
||||
'parameter' => 'client_id',
|
||||
'statusCode' => 400,
|
||||
]);
|
||||
|
||||
$I->wantTo('check behavior on invalid client id');
|
||||
$this->route->$action($this->buildQueryParams('non-exists-client', 'http://some-resource.by', 'code'));
|
||||
$I->canSeeResponseCodeIs(401);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'error' => 'invalid_client',
|
||||
'statusCode' => 401,
|
||||
]);
|
||||
|
||||
$I->wantTo('check behavior on invalid response type');
|
||||
$this->route->$action($this->buildQueryParams('ely', 'http://ely.by', 'kitty'));
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'error' => 'unsupported_response_type',
|
||||
'parameter' => 'kitty',
|
||||
'statusCode' => 400,
|
||||
]);
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.redirectUri');
|
||||
|
||||
$I->wantTo('check behavior on some invalid scopes');
|
||||
$this->route->$action($this->buildQueryParams('ely', 'http://ely.by', 'code', [
|
||||
P::MINECRAFT_SERVER_SESSION,
|
||||
'some_wrong_scope',
|
||||
]));
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'error' => 'invalid_scope',
|
||||
'parameter' => 'some_wrong_scope',
|
||||
'statusCode' => 400,
|
||||
]);
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.redirectUri');
|
||||
|
||||
$I->wantTo('check behavior on request internal scope');
|
||||
$this->route->$action($this->buildQueryParams('ely', 'http://ely.by', 'code', [
|
||||
P::MINECRAFT_SERVER_SESSION,
|
||||
P::BLOCK_ACCOUNT,
|
||||
]));
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'error' => 'invalid_scope',
|
||||
'parameter' => P::BLOCK_ACCOUNT,
|
||||
'statusCode' => 400,
|
||||
]);
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.redirectUri');
|
||||
}
|
||||
|
||||
}
|
120
api/tests/functional/oauth/ClientCredentialsCest.php
Normal file
120
api/tests/functional/oauth/ClientCredentialsCest.php
Normal file
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
namespace api\tests\functional\oauth;
|
||||
|
||||
use api\tests\_pages\OauthRoute;
|
||||
use api\tests\functional\_steps\OauthSteps;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class ClientCredentialsCest {
|
||||
|
||||
/**
|
||||
* @var OauthRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new OauthRoute($I);
|
||||
}
|
||||
|
||||
public function testIssueTokenWithWrongArgs(FunctionalTester $I) {
|
||||
$I->wantTo('check behavior on on request without any credentials');
|
||||
$this->route->issueToken($this->buildParams());
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'invalid_request',
|
||||
]);
|
||||
|
||||
$I->wantTo('check behavior on passing invalid client_id');
|
||||
$this->route->issueToken($this->buildParams(
|
||||
'invalid-client',
|
||||
'invalid-secret',
|
||||
['invalid-scope']
|
||||
));
|
||||
$I->canSeeResponseCodeIs(401);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'invalid_client',
|
||||
]);
|
||||
|
||||
$I->wantTo('check behavior on passing invalid client_secret');
|
||||
$this->route->issueToken($this->buildParams(
|
||||
'ely',
|
||||
'invalid-secret',
|
||||
['invalid-scope']
|
||||
));
|
||||
$I->canSeeResponseCodeIs(401);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'invalid_client',
|
||||
]);
|
||||
|
||||
$I->wantTo('check behavior on passing invalid client_secret');
|
||||
$this->route->issueToken($this->buildParams(
|
||||
'ely',
|
||||
'invalid-secret',
|
||||
['invalid-scope']
|
||||
));
|
||||
$I->canSeeResponseCodeIs(401);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'invalid_client',
|
||||
]);
|
||||
}
|
||||
|
||||
public function testIssueTokenWithPublicScopes(OauthSteps $I) {
|
||||
// TODO: у нас пока нет публичных скоупов, поэтому тест прогоняется с пустым набором
|
||||
$this->route->issueToken($this->buildParams(
|
||||
'ely',
|
||||
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
|
||||
[]
|
||||
));
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'token_type' => 'Bearer',
|
||||
]);
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.access_token');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.expires_in');
|
||||
}
|
||||
|
||||
public function testIssueTokenWithInternalScopes(OauthSteps $I) {
|
||||
$this->route->issueToken($this->buildParams(
|
||||
'ely',
|
||||
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
|
||||
['account_block']
|
||||
));
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'invalid_scope',
|
||||
]);
|
||||
|
||||
$this->route->issueToken($this->buildParams(
|
||||
'trusted-client',
|
||||
'tXBbyvMcyaOgHMOAXBpN2EC7uFoJAaL9',
|
||||
['account_block']
|
||||
));
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'token_type' => 'Bearer',
|
||||
]);
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.access_token');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.expires_in');
|
||||
}
|
||||
|
||||
private function buildParams($clientId = null, $clientSecret = null, array $scopes = null) {
|
||||
$params = ['grant_type' => 'client_credentials'];
|
||||
if ($clientId !== null) {
|
||||
$params['client_id'] = $clientId;
|
||||
}
|
||||
|
||||
if ($clientSecret !== null) {
|
||||
$params['client_secret'] = $clientSecret;
|
||||
}
|
||||
|
||||
if ($scopes !== null) {
|
||||
$params['scope'] = implode(',', $scopes);
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
}
|
110
api/tests/functional/oauth/CreateClientCest.php
Normal file
110
api/tests/functional/oauth/CreateClientCest.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
namespace api\tests\functional\oauth;
|
||||
|
||||
use api\tests\_pages\OauthRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class CreateClientCest {
|
||||
|
||||
/**
|
||||
* @var OauthRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new OauthRoute($I);
|
||||
}
|
||||
|
||||
public function testCreateApplicationWithWrongParams(FunctionalTester $I) {
|
||||
$I->amAuthenticated('admin');
|
||||
|
||||
$this->route->createClient('application', []);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'name' => 'error.name_required',
|
||||
'redirectUri' => 'error.redirectUri_required',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->route->createClient('application', [
|
||||
'name' => 'my test oauth client',
|
||||
'redirectUri' => 'localhost',
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => false,
|
||||
'errors' => [
|
||||
'redirectUri' => 'error.redirectUri_invalid',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testCreateApplication(FunctionalTester $I) {
|
||||
$I->amAuthenticated('admin');
|
||||
$this->route->createClient('application', [
|
||||
'name' => 'My admin application',
|
||||
'description' => 'Application description.',
|
||||
'redirectUri' => 'http://some-site.com/oauth/ely',
|
||||
'websiteUrl' => 'http://some-site.com',
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'clientId' => 'my-admin-application',
|
||||
'name' => 'My admin application',
|
||||
'description' => 'Application description.',
|
||||
'websiteUrl' => 'http://some-site.com',
|
||||
'countUsers' => 0,
|
||||
'redirectUri' => 'http://some-site.com/oauth/ely',
|
||||
],
|
||||
]);
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.data.clientSecret');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.data.createdAt');
|
||||
}
|
||||
|
||||
public function testCreateMinecraftServer(FunctionalTester $I) {
|
||||
$I->amAuthenticated('admin');
|
||||
$this->route->createClient('minecraft-server', [
|
||||
'name' => 'My amazing server',
|
||||
'websiteUrl' => 'http://some-site.com',
|
||||
'minecraftServerIp' => 'hypixel.com:25565',
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'clientId' => 'my-amazing-server',
|
||||
'name' => 'My amazing server',
|
||||
'websiteUrl' => 'http://some-site.com',
|
||||
'minecraftServerIp' => 'hypixel.com:25565',
|
||||
],
|
||||
]);
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.data.clientSecret');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.data.createdAt');
|
||||
}
|
||||
|
||||
public function testCreateApplicationWithTheSameNameAsDeletedApp(FunctionalTester $I) {
|
||||
$I->wantTo('create application with the same name as the recently deleted application');
|
||||
$I->amAuthenticated('admin');
|
||||
$this->route->createClient('application', [
|
||||
'name' => 'Deleted OAuth Client',
|
||||
'description' => '',
|
||||
'redirectUri' => 'http://some-site.com/oauth/ely',
|
||||
'websiteUrl' => 'http://some-site.com',
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'clientId' => 'deleted-oauth-client1',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
28
api/tests/functional/oauth/DeleteClientCest.php
Normal file
28
api/tests/functional/oauth/DeleteClientCest.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
namespace api\tests\functional\oauth;
|
||||
|
||||
use api\tests\_pages\OauthRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class DeleteClientCest {
|
||||
|
||||
/**
|
||||
* @var OauthRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new OauthRoute($I);
|
||||
}
|
||||
|
||||
public function testDelete(FunctionalTester $I) {
|
||||
$I->amAuthenticated('TwoOauthClients');
|
||||
$this->route->deleteClient('first-test-oauth-client');
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
88
api/tests/functional/oauth/GetClientsCest.php
Normal file
88
api/tests/functional/oauth/GetClientsCest.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
namespace api\tests\functional\oauth;
|
||||
|
||||
use api\tests\_pages\OauthRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class GetClientsCest {
|
||||
|
||||
/**
|
||||
* @var OauthRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new OauthRoute($I);
|
||||
}
|
||||
|
||||
public function testGet(FunctionalTester $I) {
|
||||
$I->amAuthenticated('admin');
|
||||
$this->route->getClient('admin-oauth-client');
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'clientId' => 'admin-oauth-client',
|
||||
'clientSecret' => 'FKyO71iCIlv4YM2IHlLbhsvYoIJScUzTZt1kEK7DQLXXYISLDvURVXK32Q58sHWS',
|
||||
'type' => 'application',
|
||||
'name' => 'Admin\'s oauth client',
|
||||
'description' => 'Personal oauth client',
|
||||
'redirectUri' => 'http://some-site.com/oauth/ely',
|
||||
'websiteUrl' => '',
|
||||
'createdAt' => 1519254133,
|
||||
]);
|
||||
}
|
||||
|
||||
public function testGetNotOwn(FunctionalTester $I) {
|
||||
$I->amAuthenticated('admin');
|
||||
$this->route->getClient('another-test-oauth-client');
|
||||
$I->canSeeResponseCodeIs(403);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'name' => 'Forbidden',
|
||||
'status' => 403,
|
||||
'message' => 'You are not allowed to perform this action.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function testGetAllPerAccountList(FunctionalTester $I) {
|
||||
$I->amAuthenticated('TwoOauthClients');
|
||||
$this->route->getPerAccount(14);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
[
|
||||
'clientId' => 'first-test-oauth-client',
|
||||
'clientSecret' => 'Zt1kEK7DQLXXYISLDvURVXK32Q58sHWSFKyO71iCIlv4YM2IHlLbhsvYoIJScUzT',
|
||||
'type' => 'application',
|
||||
'name' => 'First test oauth client',
|
||||
'description' => 'Some description to the first oauth client',
|
||||
'redirectUri' => 'http://some-site-1.com/oauth/ely',
|
||||
'websiteUrl' => '',
|
||||
'countUsers' => 0,
|
||||
'createdAt' => 1519487434,
|
||||
],
|
||||
[
|
||||
'clientId' => 'another-test-oauth-client',
|
||||
'clientSecret' => 'URVXK32Q58sHWSFKyO71iCIlv4YM2Zt1kEK7DQLXXYISLDvIHlLbhsvYoIJScUzT',
|
||||
'type' => 'minecraft-server',
|
||||
'name' => 'Another test oauth client',
|
||||
'websiteUrl' => '',
|
||||
'minecraftServerIp' => '136.243.88.97:25565',
|
||||
'createdAt' => 1519487472,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testGetAllPerNotOwnAccount(FunctionalTester $I) {
|
||||
$I->amAuthenticated('TwoOauthClients');
|
||||
$this->route->getPerAccount(1);
|
||||
$I->canSeeResponseCodeIs(403);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'name' => 'Forbidden',
|
||||
'status' => 403,
|
||||
'message' => 'You are not allowed to perform this action.',
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
79
api/tests/functional/oauth/IdentityInfoCest.php
Normal file
79
api/tests/functional/oauth/IdentityInfoCest.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
namespace api\tests\functional\oauth;
|
||||
|
||||
use api\tests\_pages\IdentityInfoRoute;
|
||||
use api\tests\functional\_steps\OauthSteps;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class IdentityInfoCest {
|
||||
|
||||
/**
|
||||
* @var IdentityInfoRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new IdentityInfoRoute($I);
|
||||
}
|
||||
|
||||
public function testGetErrorIfNoAccessToken(OauthSteps $I) {
|
||||
$I->wantToTest('behavior when this endpoint called without Authorization header');
|
||||
$this->route->info();
|
||||
$I->canSeeResponseCodeIs(401);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'name' => 'Unauthorized',
|
||||
'status' => 401,
|
||||
'message' => 'Your request was made with invalid credentials.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function testGetErrorIfNotEnoughPerms(OauthSteps $I) {
|
||||
$I->wantToTest('behavior when this endpoint called with token, that have not enough scopes');
|
||||
$accessToken = $I->getAccessToken();
|
||||
$I->amBearerAuthenticated($accessToken);
|
||||
$this->route->info();
|
||||
$I->canSeeResponseCodeIs(403);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'name' => 'Forbidden',
|
||||
'status' => 403,
|
||||
'message' => 'You are not allowed to perform this action.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function testGetInfo(OauthSteps $I) {
|
||||
$accessToken = $I->getAccessToken(['account_info']);
|
||||
$I->amBearerAuthenticated($accessToken);
|
||||
$this->route->info();
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'id' => 1,
|
||||
'uuid' => 'df936908-b2e1-544d-96f8-2977ec213022',
|
||||
'username' => 'Admin',
|
||||
'registeredAt' => 1451775316,
|
||||
'profileLink' => 'http://ely.by/u1',
|
||||
'preferredLanguage' => 'en',
|
||||
]);
|
||||
$I->cantSeeResponseJsonMatchesJsonPath('$.email');
|
||||
}
|
||||
|
||||
public function testGetInfoWithEmail(OauthSteps $I) {
|
||||
$accessToken = $I->getAccessToken(['account_info', 'account_email']);
|
||||
$I->amBearerAuthenticated($accessToken);
|
||||
$this->route->info();
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'id' => 1,
|
||||
'uuid' => 'df936908-b2e1-544d-96f8-2977ec213022',
|
||||
'username' => 'Admin',
|
||||
'registeredAt' => 1451775316,
|
||||
'profileLink' => 'http://ely.by/u1',
|
||||
'preferredLanguage' => 'en',
|
||||
'email' => 'admin@ely.by',
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
124
api/tests/functional/oauth/RefreshTokenCest.php
Normal file
124
api/tests/functional/oauth/RefreshTokenCest.php
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
namespace api\tests\functional\oauth;
|
||||
|
||||
use api\components\OAuth2\Storage\ScopeStorage as S;
|
||||
use common\rbac\Permissions as P;
|
||||
use api\tests\_pages\OauthRoute;
|
||||
use api\tests\functional\_steps\OauthSteps;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class RefreshTokenCest {
|
||||
|
||||
/**
|
||||
* @var OauthRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new OauthRoute($I);
|
||||
}
|
||||
|
||||
public function testInvalidRefreshToken(OauthSteps $I) {
|
||||
$this->route->issueToken($this->buildParams(
|
||||
'some-invalid-refresh-token',
|
||||
'ely',
|
||||
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM'
|
||||
));
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'invalid_request',
|
||||
'message' => 'The refresh token is invalid.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function testRefreshToken(OauthSteps $I) {
|
||||
$refreshToken = $I->getRefreshToken();
|
||||
$this->route->issueToken($this->buildParams(
|
||||
$refreshToken,
|
||||
'ely',
|
||||
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM'
|
||||
));
|
||||
$this->canSeeRefreshTokenSuccess($I);
|
||||
}
|
||||
|
||||
public function testRefreshTokenWithSameScopes(OauthSteps $I) {
|
||||
$refreshToken = $I->getRefreshToken([P::MINECRAFT_SERVER_SESSION]);
|
||||
$this->route->issueToken($this->buildParams(
|
||||
$refreshToken,
|
||||
'ely',
|
||||
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
|
||||
[P::MINECRAFT_SERVER_SESSION, S::OFFLINE_ACCESS]
|
||||
));
|
||||
$this->canSeeRefreshTokenSuccess($I);
|
||||
}
|
||||
|
||||
public function testRefreshTokenTwice(OauthSteps $I) {
|
||||
$refreshToken = $I->getRefreshToken([P::MINECRAFT_SERVER_SESSION]);
|
||||
$this->route->issueToken($this->buildParams(
|
||||
$refreshToken,
|
||||
'ely',
|
||||
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
|
||||
[P::MINECRAFT_SERVER_SESSION, S::OFFLINE_ACCESS]
|
||||
));
|
||||
$this->canSeeRefreshTokenSuccess($I);
|
||||
|
||||
$this->route->issueToken($this->buildParams(
|
||||
$refreshToken,
|
||||
'ely',
|
||||
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
|
||||
[P::MINECRAFT_SERVER_SESSION, S::OFFLINE_ACCESS]
|
||||
));
|
||||
$this->canSeeRefreshTokenSuccess($I);
|
||||
}
|
||||
|
||||
public function testRefreshTokenWithNewScopes(OauthSteps $I) {
|
||||
$refreshToken = $I->getRefreshToken([P::MINECRAFT_SERVER_SESSION]);
|
||||
$this->route->issueToken($this->buildParams(
|
||||
$refreshToken,
|
||||
'ely',
|
||||
'ZuM1vGchJz-9_UZ5HC3H3Z9Hg5PzdbkM',
|
||||
[P::MINECRAFT_SERVER_SESSION, S::OFFLINE_ACCESS, 'account_email']
|
||||
));
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'invalid_scope',
|
||||
]);
|
||||
}
|
||||
|
||||
private function buildParams($refreshToken = null, $clientId = null, $clientSecret = null, $scopes = []) {
|
||||
$params = ['grant_type' => 'refresh_token'];
|
||||
if ($refreshToken !== null) {
|
||||
$params['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
if ($clientId !== null) {
|
||||
$params['client_id'] = $clientId;
|
||||
}
|
||||
|
||||
if ($clientSecret !== null) {
|
||||
$params['client_secret'] = $clientSecret;
|
||||
}
|
||||
|
||||
if (!empty($scopes)) {
|
||||
if (is_array($scopes)) {
|
||||
$scopes = implode(',', $scopes);
|
||||
}
|
||||
|
||||
$params['scope'] = $scopes;
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
private function canSeeRefreshTokenSuccess(OauthSteps $I) {
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'token_type' => 'Bearer',
|
||||
]);
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.access_token');
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.expires_in');
|
||||
$I->cantSeeResponseJsonMatchesJsonPath('$.refresh_token');
|
||||
}
|
||||
|
||||
}
|
60
api/tests/functional/oauth/ResetClientCest.php
Normal file
60
api/tests/functional/oauth/ResetClientCest.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
namespace api\tests\functional\oauth;
|
||||
|
||||
use api\tests\_pages\OauthRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class ResetClientCest {
|
||||
|
||||
/**
|
||||
* @var OauthRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new OauthRoute($I);
|
||||
}
|
||||
|
||||
public function testReset(FunctionalTester $I) {
|
||||
$I->amAuthenticated('TwoOauthClients');
|
||||
$this->route->resetClient('first-test-oauth-client');
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'clientId' => 'first-test-oauth-client',
|
||||
'clientSecret' => 'Zt1kEK7DQLXXYISLDvURVXK32Q58sHWSFKyO71iCIlv4YM2IHlLbhsvYoIJScUzT',
|
||||
'name' => 'First test oauth client',
|
||||
'description' => 'Some description to the first oauth client',
|
||||
'redirectUri' => 'http://some-site-1.com/oauth/ely',
|
||||
'websiteUrl' => '',
|
||||
'countUsers' => 0,
|
||||
'createdAt' => 1519487434,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testResetWithSecretChanging(FunctionalTester $I) {
|
||||
$I->amAuthenticated('TwoOauthClients');
|
||||
$this->route->resetClient('first-test-oauth-client', true);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'clientId' => 'first-test-oauth-client',
|
||||
'name' => 'First test oauth client',
|
||||
'description' => 'Some description to the first oauth client',
|
||||
'redirectUri' => 'http://some-site-1.com/oauth/ely',
|
||||
'websiteUrl' => '',
|
||||
'countUsers' => 0,
|
||||
'createdAt' => 1519487434,
|
||||
],
|
||||
]);
|
||||
$I->canSeeResponseJsonMatchesJsonPath('$.data.clientSecret');
|
||||
$secret = $I->grabDataFromResponseByJsonPath('$.data.clientSecret')[0];
|
||||
$I->assertNotEquals('Zt1kEK7DQLXXYISLDvURVXK32Q58sHWSFKyO71iCIlv4YM2IHlLbhsvYoIJScUzT', $secret);
|
||||
}
|
||||
|
||||
}
|
65
api/tests/functional/oauth/UpdateClientCest.php
Normal file
65
api/tests/functional/oauth/UpdateClientCest.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
namespace api\tests\functional\oauth;
|
||||
|
||||
use api\tests\_pages\OauthRoute;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class UpdateClientCest {
|
||||
|
||||
/**
|
||||
* @var OauthRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new OauthRoute($I);
|
||||
}
|
||||
|
||||
public function testUpdateApplication(FunctionalTester $I) {
|
||||
$I->amAuthenticated('TwoOauthClients');
|
||||
$this->route->updateClient('first-test-oauth-client', [
|
||||
'name' => 'Updated name',
|
||||
'description' => 'Updated description.',
|
||||
'redirectUri' => 'http://new-site.com/oauth/ely',
|
||||
'websiteUrl' => 'http://new-site.com',
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'clientId' => 'first-test-oauth-client',
|
||||
'clientSecret' => 'Zt1kEK7DQLXXYISLDvURVXK32Q58sHWSFKyO71iCIlv4YM2IHlLbhsvYoIJScUzT',
|
||||
'name' => 'Updated name',
|
||||
'description' => 'Updated description.',
|
||||
'redirectUri' => 'http://new-site.com/oauth/ely',
|
||||
'websiteUrl' => 'http://new-site.com',
|
||||
'createdAt' => 1519487434,
|
||||
'countUsers' => 0,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testUpdateMinecraftServer(FunctionalTester $I) {
|
||||
$I->amAuthenticated('TwoOauthClients');
|
||||
$this->route->updateClient('another-test-oauth-client', [
|
||||
'name' => 'Updated server name',
|
||||
'websiteUrl' => 'http://new-site.com',
|
||||
'minecraftServerIp' => 'hypixel.com:25565',
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(200);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'clientId' => 'another-test-oauth-client',
|
||||
'clientSecret' => 'URVXK32Q58sHWSFKyO71iCIlv4YM2Zt1kEK7DQLXXYISLDvIHlLbhsvYoIJScUzT',
|
||||
'name' => 'Updated server name',
|
||||
'websiteUrl' => 'http://new-site.com',
|
||||
'minecraftServerIp' => 'hypixel.com:25565',
|
||||
'createdAt' => 1519487472,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
59
api/tests/functional/sessionserver/HasJoinedCest.php
Normal file
59
api/tests/functional/sessionserver/HasJoinedCest.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
namespace api\tests\functional\sessionserver;
|
||||
|
||||
use Faker\Provider\Uuid;
|
||||
use api\tests\_pages\SessionServerRoute;
|
||||
use api\tests\functional\_steps\SessionServerSteps;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class HasJoinedCest {
|
||||
|
||||
/**
|
||||
* @var SessionServerRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new SessionServerRoute($I);
|
||||
}
|
||||
|
||||
public function hasJoined(SessionServerSteps $I) {
|
||||
$I->wantTo('check hasJoined user to some server');
|
||||
list($username, $serverId) = $I->amJoined();
|
||||
|
||||
$this->route->hasJoined([
|
||||
'username' => $username,
|
||||
'serverId' => $serverId,
|
||||
]);
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->canSeeValidTexturesResponse($username, 'df936908b2e1544d96f82977ec213022');
|
||||
}
|
||||
|
||||
public function wrongArguments(FunctionalTester $I) {
|
||||
$I->wantTo('get error on wrong amount of arguments');
|
||||
$this->route->hasJoined([
|
||||
'wrong' => 'argument',
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'IllegalArgumentException',
|
||||
'errorMessage' => 'credentials can not be null.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function hasJoinedWithNoJoinOperation(FunctionalTester $I) {
|
||||
$I->wantTo('hasJoined to some server without join call');
|
||||
$this->route->hasJoined([
|
||||
'username' => 'some-username',
|
||||
'serverId' => Uuid::uuid(),
|
||||
]);
|
||||
$I->seeResponseCodeIs(401);
|
||||
$I->seeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'ForbiddenOperationException',
|
||||
'errorMessage' => 'Invalid token.',
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
51
api/tests/functional/sessionserver/HasJoinedLegacyCest.php
Normal file
51
api/tests/functional/sessionserver/HasJoinedLegacyCest.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
namespace api\tests\functional\sessionserver;
|
||||
|
||||
use Faker\Provider\Uuid;
|
||||
use api\tests\_pages\SessionServerRoute;
|
||||
use api\tests\functional\_steps\SessionServerSteps;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class HasJoinedLegacyCest {
|
||||
|
||||
/**
|
||||
* @var SessionServerRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new SessionServerRoute($I);
|
||||
}
|
||||
|
||||
public function hasJoined(SessionServerSteps $I) {
|
||||
$I->wantTo('test hasJoined user to some server by legacy version');
|
||||
list($username, $serverId) = $I->amJoined(true);
|
||||
|
||||
$this->route->hasJoinedLegacy([
|
||||
'user' => $username,
|
||||
'serverId' => $serverId,
|
||||
]);
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->canSeeResponseEquals('YES');
|
||||
}
|
||||
|
||||
public function wrongArguments(FunctionalTester $I) {
|
||||
$I->wantTo('get error on wrong amount of arguments');
|
||||
$this->route->hasJoinedLegacy([
|
||||
'wrong' => 'argument',
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseEquals('credentials can not be null.');
|
||||
}
|
||||
|
||||
public function hasJoinedWithNoJoinOperation(FunctionalTester $I) {
|
||||
$I->wantTo('hasJoined by legacy version to some server without join call');
|
||||
$this->route->hasJoinedLegacy([
|
||||
'user' => 'random-username',
|
||||
'serverId' => Uuid::uuid(),
|
||||
]);
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->canSeeResponseEquals('NO');
|
||||
}
|
||||
|
||||
}
|
148
api/tests/functional/sessionserver/JoinCest.php
Normal file
148
api/tests/functional/sessionserver/JoinCest.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
namespace api\tests\functional\sessionserver;
|
||||
|
||||
use common\rbac\Permissions as P;
|
||||
use Faker\Provider\Uuid;
|
||||
use api\tests\_pages\SessionServerRoute;
|
||||
use api\tests\functional\_steps\AuthserverSteps;
|
||||
use api\tests\functional\_steps\OauthSteps;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class JoinCest {
|
||||
|
||||
/**
|
||||
* @var SessionServerRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(AuthserverSteps $I) {
|
||||
$this->route = new SessionServerRoute($I);
|
||||
}
|
||||
|
||||
public function joinByLegacyAuthserver(AuthserverSteps $I) {
|
||||
$I->wantTo('join to server, using legacy authserver access token');
|
||||
[$accessToken] = $I->amAuthenticated();
|
||||
$this->route->join([
|
||||
'accessToken' => $accessToken,
|
||||
'selectedProfile' => 'df936908-b2e1-544d-96f8-2977ec213022',
|
||||
'serverId' => Uuid::uuid(),
|
||||
]);
|
||||
$this->expectSuccessResponse($I);
|
||||
}
|
||||
|
||||
public function joinByPassJsonInPost(AuthserverSteps $I) {
|
||||
$I->wantTo('join to server, passing data in body as encoded json');
|
||||
[$accessToken] = $I->amAuthenticated();
|
||||
$this->route->join(json_encode([
|
||||
'accessToken' => $accessToken,
|
||||
'selectedProfile' => 'df936908-b2e1-544d-96f8-2977ec213022',
|
||||
'serverId' => Uuid::uuid(),
|
||||
]));
|
||||
$this->expectSuccessResponse($I);
|
||||
}
|
||||
|
||||
public function joinByOauth2Token(OauthSteps $I) {
|
||||
$I->wantTo('join to server, using modern oAuth2 generated token');
|
||||
$accessToken = $I->getAccessToken([P::MINECRAFT_SERVER_SESSION]);
|
||||
$this->route->join([
|
||||
'accessToken' => $accessToken,
|
||||
'selectedProfile' => 'df936908-b2e1-544d-96f8-2977ec213022',
|
||||
'serverId' => Uuid::uuid(),
|
||||
]);
|
||||
$this->expectSuccessResponse($I);
|
||||
}
|
||||
|
||||
public function joinByOauth2TokenWithNotDashedUUID(OauthSteps $I) {
|
||||
$I->wantTo('join to server, using modern oAuth2 generated token and non dashed uuid');
|
||||
$accessToken = $I->getAccessToken([P::MINECRAFT_SERVER_SESSION]);
|
||||
$this->route->join([
|
||||
'accessToken' => $accessToken,
|
||||
'selectedProfile' => 'df936908b2e1544d96f82977ec213022',
|
||||
'serverId' => Uuid::uuid(),
|
||||
]);
|
||||
$this->expectSuccessResponse($I);
|
||||
}
|
||||
|
||||
public function joinByModernOauth2TokenWithoutPermission(OauthSteps $I) {
|
||||
$I->wantTo('join to server, using moder oAuth2 generated token, but without minecraft auth permission');
|
||||
$accessToken = $I->getAccessToken(['account_info', 'account_email']);
|
||||
$this->route->join([
|
||||
'accessToken' => $accessToken,
|
||||
'selectedProfile' => 'df936908-b2e1-544d-96f8-2977ec213022',
|
||||
'serverId' => Uuid::uuid(),
|
||||
]);
|
||||
$I->seeResponseCodeIs(401);
|
||||
$I->seeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'ForbiddenOperationException',
|
||||
'errorMessage' => 'The token does not have required scope.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function joinWithExpiredToken(FunctionalTester $I) {
|
||||
$I->wantTo('join to some server with expired accessToken');
|
||||
$this->route->join([
|
||||
'accessToken' => '6042634a-a1e2-4aed-866c-c661fe4e63e2',
|
||||
'selectedProfile' => 'df936908-b2e1-544d-96f8-2977ec213022',
|
||||
'serverId' => Uuid::uuid(),
|
||||
]);
|
||||
$I->seeResponseCodeIs(401);
|
||||
$I->seeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'ForbiddenOperationException',
|
||||
'errorMessage' => 'Expired access_token.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function wrongArguments(FunctionalTester $I) {
|
||||
$I->wantTo('get error on wrong amount of arguments');
|
||||
$this->route->join([
|
||||
'wrong' => 'argument',
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'IllegalArgumentException',
|
||||
'errorMessage' => 'credentials can not be null.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function joinWithWrongAccessToken(FunctionalTester $I) {
|
||||
$I->wantTo('join to some server with wrong accessToken');
|
||||
$this->route->join([
|
||||
'accessToken' => Uuid::uuid(),
|
||||
'selectedProfile' => 'df936908-b2e1-544d-96f8-2977ec213022',
|
||||
'serverId' => Uuid::uuid(),
|
||||
]);
|
||||
$I->seeResponseCodeIs(401);
|
||||
$I->seeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'ForbiddenOperationException',
|
||||
'errorMessage' => 'Invalid access_token.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function joinWithNilUuids(FunctionalTester $I) {
|
||||
$I->wantTo('join to some server with nil accessToken and selectedProfile');
|
||||
$this->route->join([
|
||||
'accessToken' => '00000000-0000-0000-0000-000000000000',
|
||||
'selectedProfile' => 'df936908-b2e1-544d-96f8-2977ec213022',
|
||||
'serverId' => Uuid::uuid(),
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'IllegalArgumentException',
|
||||
'errorMessage' => 'credentials can not be null.',
|
||||
]);
|
||||
}
|
||||
|
||||
private function expectSuccessResponse(FunctionalTester $I) {
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->seeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'id' => 'OK',
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
114
api/tests/functional/sessionserver/JoinLegacyCest.php
Normal file
114
api/tests/functional/sessionserver/JoinLegacyCest.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
namespace api\tests\functional\sessionserver;
|
||||
|
||||
use common\rbac\Permissions as P;
|
||||
use Faker\Provider\Uuid;
|
||||
use api\tests\_pages\SessionServerRoute;
|
||||
use api\tests\functional\_steps\AuthserverSteps;
|
||||
use api\tests\functional\_steps\OauthSteps;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class JoinLegacyCest {
|
||||
|
||||
/**
|
||||
* @var SessionServerRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(AuthserverSteps $I) {
|
||||
$this->route = new SessionServerRoute($I);
|
||||
}
|
||||
|
||||
public function joinByLegacyAuthserver(AuthserverSteps $I) {
|
||||
$I->wantTo('join to server by legacy protocol, using legacy authserver access token');
|
||||
[$accessToken] = $I->amAuthenticated();
|
||||
$this->route->joinLegacy([
|
||||
'sessionId' => $accessToken,
|
||||
'user' => 'Admin',
|
||||
'serverId' => Uuid::uuid(),
|
||||
]);
|
||||
$this->expectSuccessResponse($I);
|
||||
}
|
||||
|
||||
public function joinByOauth2TokenAndDifferentLetterCase(AuthserverSteps $I) {
|
||||
$I->wantTo('join to server by legacy protocol, using legacy authserver access token and different letter case');
|
||||
[$accessToken] = $I->amAuthenticated();
|
||||
$this->route->joinLegacy([
|
||||
'sessionId' => $accessToken,
|
||||
'user' => 'admin',
|
||||
'serverId' => Uuid::uuid(),
|
||||
]);
|
||||
$this->expectSuccessResponse($I);
|
||||
}
|
||||
|
||||
public function joinByNewSessionFormat(AuthserverSteps $I) {
|
||||
$I->wantTo('join to server by legacy protocol with new launcher session format, using legacy authserver');
|
||||
[$accessToken] = $I->amAuthenticated();
|
||||
$this->route->joinLegacy([
|
||||
'sessionId' => 'token:' . $accessToken . ':' . 'df936908-b2e1-544d-96f8-2977ec213022',
|
||||
'user' => 'Admin',
|
||||
'serverId' => Uuid::uuid(),
|
||||
]);
|
||||
$this->expectSuccessResponse($I);
|
||||
}
|
||||
|
||||
public function joinByOauth2Token(OauthSteps $I) {
|
||||
$I->wantTo('join to server using modern oAuth2 generated token with new launcher session format');
|
||||
$accessToken = $I->getAccessToken([P::MINECRAFT_SERVER_SESSION]);
|
||||
$this->route->joinLegacy([
|
||||
'sessionId' => 'token:' . $accessToken . ':' . 'df936908-b2e1-544d-96f8-2977ec213022',
|
||||
'user' => 'Admin',
|
||||
'serverId' => Uuid::uuid(),
|
||||
]);
|
||||
$this->expectSuccessResponse($I);
|
||||
}
|
||||
|
||||
public function wrongArguments(FunctionalTester $I) {
|
||||
$I->wantTo('get error on wrong amount of arguments');
|
||||
$this->route->joinLegacy([
|
||||
'wrong' => 'argument',
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseContains('credentials can not be null.');
|
||||
}
|
||||
|
||||
public function joinWithWrongAccessToken(FunctionalTester $I) {
|
||||
$I->wantTo('join to some server with wrong accessToken');
|
||||
$this->route->joinLegacy([
|
||||
'sessionId' => 'token:' . Uuid::uuid() . ':' . Uuid::uuid(),
|
||||
'user' => 'random-username',
|
||||
'serverId' => Uuid::uuid(),
|
||||
]);
|
||||
$I->seeResponseCodeIs(401);
|
||||
$I->canSeeResponseContains('Ely.by authorization required');
|
||||
}
|
||||
|
||||
public function joinWithAccessTokenWithoutMinecraftPermission(OauthSteps $I) {
|
||||
$I->wantTo('join to some server with wrong accessToken');
|
||||
$accessToken = $I->getAccessToken(['account_info']);
|
||||
$this->route->joinLegacy([
|
||||
'sessionId' => 'token:' . $accessToken . ':' . 'df936908-b2e1-544d-96f8-2977ec213022',
|
||||
'user' => 'Admin',
|
||||
'serverId' => Uuid::uuid(),
|
||||
]);
|
||||
$I->seeResponseCodeIs(401);
|
||||
$I->canSeeResponseContains('Ely.by authorization required');
|
||||
}
|
||||
|
||||
public function joinWithNilUuids(FunctionalTester $I) {
|
||||
$I->wantTo('join to some server by legacy protocol with nil accessToken and selectedProfile');
|
||||
$this->route->joinLegacy([
|
||||
'sessionId' => 'token:00000000-0000-0000-0000-000000000000:00000000-0000-0000-0000-000000000000',
|
||||
'user' => 'SomeUser',
|
||||
'serverId' => Uuid::uuid(),
|
||||
]);
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseContains('credentials can not be null.');
|
||||
}
|
||||
|
||||
private function expectSuccessResponse(FunctionalTester $I) {
|
||||
$I->seeResponseCodeIs(200);
|
||||
$I->canSeeResponseEquals('OK');
|
||||
}
|
||||
|
||||
}
|
61
api/tests/functional/sessionserver/ProfileCest.php
Normal file
61
api/tests/functional/sessionserver/ProfileCest.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
namespace api\tests\functional\sessionserver;
|
||||
|
||||
use Faker\Provider\Uuid;
|
||||
use api\tests\_pages\SessionServerRoute;
|
||||
use api\tests\functional\_steps\SessionServerSteps;
|
||||
use api\tests\FunctionalTester;
|
||||
|
||||
class ProfileCest {
|
||||
|
||||
/**
|
||||
* @var SessionServerRoute
|
||||
*/
|
||||
private $route;
|
||||
|
||||
public function _before(FunctionalTester $I) {
|
||||
$this->route = new SessionServerRoute($I);
|
||||
}
|
||||
|
||||
public function getProfile(SessionServerSteps $I) {
|
||||
$I->wantTo('get info about player textures by uuid');
|
||||
$this->route->profile('df936908-b2e1-544d-96f8-2977ec213022');
|
||||
$I->canSeeValidTexturesResponse('Admin', 'df936908b2e1544d96f82977ec213022');
|
||||
}
|
||||
|
||||
public function getProfileByUuidWithoutDashes(SessionServerSteps $I) {
|
||||
$I->wantTo('get info about player textures by uuid without dashes');
|
||||
$this->route->profile('df936908b2e1544d96f82977ec213022');
|
||||
$I->canSeeValidTexturesResponse('Admin', 'df936908b2e1544d96f82977ec213022');
|
||||
}
|
||||
|
||||
public function directCallWithoutUuidPart(FunctionalTester $I) {
|
||||
$I->wantTo('call profile route without passing uuid');
|
||||
$this->route->profile('');
|
||||
$I->canSeeResponseCodeIs(404);
|
||||
}
|
||||
|
||||
public function callWithInvalidUuid(FunctionalTester $I) {
|
||||
$I->wantTo('call profile route with invalid uuid string');
|
||||
$this->route->profile('bla-bla-bla');
|
||||
$I->canSeeResponseCodeIs(400);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'IllegalArgumentException',
|
||||
'errorMessage' => 'Invalid uuid format.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function getProfileWithNonexistentUuid(FunctionalTester $I) {
|
||||
$I->wantTo('get info about nonexistent uuid');
|
||||
$this->route->profile(Uuid::uuid());
|
||||
$I->canSeeResponseCodeIs(401);
|
||||
$I->canSeeResponseIsJson();
|
||||
$I->seeResponseIsJson();
|
||||
$I->canSeeResponseContainsJson([
|
||||
'error' => 'ForbiddenOperationException',
|
||||
'errorMessage' => 'Invalid uuid.',
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
10
api/tests/unit.suite.dist.yml
Normal file
10
api/tests/unit.suite.dist.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
suite_namespace: api\tests\unit
|
||||
actor: UnitTester
|
||||
modules:
|
||||
enabled:
|
||||
- Asserts
|
||||
- Yii2:
|
||||
part: [orm, email, fixtures]
|
||||
configFile: tests/config/unit.php
|
||||
- common\tests\_support\queue\CodeceptionQueueHelper
|
||||
- common\tests\_support\Mockery
|
29
api/tests/unit/TestCase.php
Normal file
29
api/tests/unit/TestCase.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
namespace api\tests\unit;
|
||||
|
||||
use Mockery;
|
||||
|
||||
class TestCase extends \Codeception\Test\Unit {
|
||||
|
||||
/**
|
||||
* @var \api\tests\UnitTester
|
||||
*/
|
||||
protected $tester;
|
||||
|
||||
protected function tearDown() {
|
||||
parent::tearDown();
|
||||
Mockery::close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Список фикстур, что будут загружены перед тестом, но после зачистки базы данных
|
||||
*
|
||||
* @url http://codeception.com/docs/modules/Yii2#fixtures
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function _fixtures() {
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
2
api/tests/unit/_bootstrap.php
Normal file
2
api/tests/unit/_bootstrap.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// Here you can initialize variables that will for your tests
|
74
api/tests/unit/components/ReCaptcha/ValidatorTest.php
Normal file
74
api/tests/unit/components/ReCaptcha/ValidatorTest.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
namespace codeception\api\unit\components\ReCaptcha;
|
||||
|
||||
use api\components\ReCaptcha\Validator;
|
||||
use GuzzleHttp\ClientInterface;
|
||||
use GuzzleHttp\Exception\ConnectException;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use phpmock\mockery\PHPMockery;
|
||||
use ReflectionClass;
|
||||
use api\tests\unit\TestCase;
|
||||
|
||||
class ValidatorTest extends TestCase {
|
||||
|
||||
public function testValidateEmptyValue() {
|
||||
$validator = new Validator(mock(ClientInterface::class));
|
||||
$this->assertFalse($validator->validate('', $error));
|
||||
$this->assertEquals('error.captcha_required', $error, 'Get error.captcha_required, if passed empty value');
|
||||
}
|
||||
|
||||
public function testValidateInvalidValue() {
|
||||
$mockClient = mock(ClientInterface::class);
|
||||
$mockClient->shouldReceive('request')->andReturn(new Response(200, [], json_encode([
|
||||
'success' => false,
|
||||
'error-codes' => [
|
||||
'invalid-input-response', // The response parameter is invalid or malformed.
|
||||
],
|
||||
])));
|
||||
|
||||
$validator = new Validator($mockClient);
|
||||
$this->assertFalse($validator->validate('12341234', $error));
|
||||
$this->assertEquals('error.captcha_invalid', $error, 'Get error.captcha_invalid, if passed wrong value');
|
||||
}
|
||||
|
||||
public function testValidateWithNetworkTroubles() {
|
||||
$mockClient = mock(ClientInterface::class);
|
||||
$mockClient->shouldReceive('request')->andThrow(mock(ConnectException::class))->once();
|
||||
$mockClient->shouldReceive('request')->andReturn(new Response(200, [], json_encode([
|
||||
'success' => true,
|
||||
'error-codes' => [
|
||||
'invalid-input-response', // The response parameter is invalid or malformed.
|
||||
],
|
||||
])))->once();
|
||||
PHPMockery::mock($this->getClassNamespace(Validator::class), 'sleep')->once();
|
||||
|
||||
$validator = new Validator($mockClient);
|
||||
$this->assertTrue($validator->validate('12341234', $error));
|
||||
$this->assertNull($error);
|
||||
}
|
||||
|
||||
public function testValidateWithHugeNetworkTroubles() {
|
||||
$mockClient = mock(ClientInterface::class);
|
||||
$mockClient->shouldReceive('request')->andThrow(mock(ConnectException::class))->times(3);
|
||||
PHPMockery::mock($this->getClassNamespace(Validator::class), 'sleep')->times(2);
|
||||
|
||||
$validator = new Validator($mockClient);
|
||||
$this->expectException(ConnectException::class);
|
||||
$validator->validate('12341234', $error);
|
||||
}
|
||||
|
||||
public function testValidateValidValue() {
|
||||
$mockClient = mock(ClientInterface::class);
|
||||
$mockClient->shouldReceive('request')->andReturn(new Response(200, [], json_encode([
|
||||
'success' => true,
|
||||
])));
|
||||
$validator = new Validator($mockClient);
|
||||
$this->assertTrue($validator->validate('12341234', $error));
|
||||
$this->assertNull($error);
|
||||
}
|
||||
|
||||
private function getClassNamespace(string $className): string {
|
||||
return (new ReflectionClass($className))->getNamespaceName();
|
||||
}
|
||||
|
||||
}
|
197
api/tests/unit/components/User/ComponentTest.php
Normal file
197
api/tests/unit/components/User/ComponentTest.php
Normal file
@@ -0,0 +1,197 @@
|
||||
<?php
|
||||
namespace codeception\api\unit\components\User;
|
||||
|
||||
use api\components\User\AuthenticationResult;
|
||||
use api\components\User\Component;
|
||||
use api\components\User\Identity;
|
||||
use common\models\Account;
|
||||
use common\models\AccountSession;
|
||||
use Emarref\Jwt\Claim;
|
||||
use Emarref\Jwt\Jwt;
|
||||
use Emarref\Jwt\Token;
|
||||
use api\tests\unit\TestCase;
|
||||
use common\tests\_support\ProtectedCaller;
|
||||
use common\tests\fixtures\AccountFixture;
|
||||
use common\tests\fixtures\AccountSessionFixture;
|
||||
use common\tests\fixtures\MinecraftAccessKeyFixture;
|
||||
use Yii;
|
||||
use yii\web\Request;
|
||||
|
||||
class ComponentTest extends TestCase {
|
||||
use ProtectedCaller;
|
||||
|
||||
/**
|
||||
* @var Component|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
private $component;
|
||||
|
||||
public function _before() {
|
||||
parent::_before();
|
||||
$this->component = new Component($this->getComponentConfig());
|
||||
}
|
||||
|
||||
public function _fixtures() {
|
||||
return [
|
||||
'accounts' => AccountFixture::class,
|
||||
'sessions' => AccountSessionFixture::class,
|
||||
'minecraftSessions' => MinecraftAccessKeyFixture::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function testCreateJwtAuthenticationToken() {
|
||||
$this->mockRequest();
|
||||
|
||||
$account = new Account(['id' => 1]);
|
||||
$result = $this->component->createJwtAuthenticationToken($account, false);
|
||||
$this->assertInstanceOf(AuthenticationResult::class, $result);
|
||||
$this->assertNull($result->getSession());
|
||||
$this->assertEquals($account, $result->getAccount());
|
||||
$payloads = (new Jwt())->deserialize($result->getJwt())->getPayload();
|
||||
/** @noinspection NullPointerExceptionInspection */
|
||||
$this->assertEquals(time(), $payloads->findClaimByName(Claim\IssuedAt::NAME)->getValue(), '', 3);
|
||||
/** @noinspection SummerTimeUnsafeTimeManipulationInspection */
|
||||
/** @noinspection NullPointerExceptionInspection */
|
||||
$this->assertEquals(time() + 60 * 60 * 24 * 7, $payloads->findClaimByName('exp')->getValue(), '', 3);
|
||||
/** @noinspection NullPointerExceptionInspection */
|
||||
$this->assertEquals('ely|1', $payloads->findClaimByName('sub')->getValue());
|
||||
/** @noinspection NullPointerExceptionInspection */
|
||||
$this->assertEquals('accounts_web_user', $payloads->findClaimByName('ely-scopes')->getValue());
|
||||
$this->assertNull($payloads->findClaimByName('jti'));
|
||||
|
||||
/** @var Account $account */
|
||||
$account = $this->tester->grabFixture('accounts', 'admin');
|
||||
$result = $this->component->createJwtAuthenticationToken($account, true);
|
||||
$this->assertInstanceOf(AuthenticationResult::class, $result);
|
||||
$this->assertInstanceOf(AccountSession::class, $result->getSession());
|
||||
$this->assertEquals($account, $result->getAccount());
|
||||
/** @noinspection NullPointerExceptionInspection */
|
||||
$this->assertTrue($result->getSession()->refresh());
|
||||
$payloads = (new Jwt())->deserialize($result->getJwt())->getPayload();
|
||||
/** @noinspection NullPointerExceptionInspection */
|
||||
$this->assertEquals(time(), $payloads->findClaimByName(Claim\IssuedAt::NAME)->getValue(), '', 3);
|
||||
/** @noinspection NullPointerExceptionInspection */
|
||||
$this->assertEquals(time() + 3600, $payloads->findClaimByName('exp')->getValue(), '', 3);
|
||||
/** @noinspection NullPointerExceptionInspection */
|
||||
$this->assertEquals('ely|1', $payloads->findClaimByName('sub')->getValue());
|
||||
/** @noinspection NullPointerExceptionInspection */
|
||||
$this->assertEquals('accounts_web_user', $payloads->findClaimByName('ely-scopes')->getValue());
|
||||
/** @noinspection NullPointerExceptionInspection */
|
||||
$this->assertEquals($result->getSession()->id, $payloads->findClaimByName('jti')->getValue());
|
||||
}
|
||||
|
||||
public function testRenewJwtAuthenticationToken() {
|
||||
$userIP = '192.168.0.1';
|
||||
$this->mockRequest($userIP);
|
||||
/** @var AccountSession $session */
|
||||
$session = $this->tester->grabFixture('sessions', 'admin');
|
||||
$result = $this->component->renewJwtAuthenticationToken($session);
|
||||
$this->assertInstanceOf(AuthenticationResult::class, $result);
|
||||
$this->assertEquals($session, $result->getSession());
|
||||
$this->assertEquals($session->account_id, $result->getAccount()->id);
|
||||
$session->refresh(); // reload data from db
|
||||
$this->assertEquals(time(), $session->last_refreshed_at, '', 3);
|
||||
$this->assertEquals($userIP, $session->getReadableIp());
|
||||
$payloads = (new Jwt())->deserialize($result->getJwt())->getPayload();
|
||||
/** @noinspection NullPointerExceptionInspection */
|
||||
$this->assertEquals(time(), $payloads->findClaimByName(Claim\IssuedAt::NAME)->getValue(), '', 3);
|
||||
/** @noinspection NullPointerExceptionInspection */
|
||||
$this->assertEquals(time() + 3600, $payloads->findClaimByName('exp')->getValue(), '', 3);
|
||||
/** @noinspection NullPointerExceptionInspection */
|
||||
$this->assertEquals('ely|1', $payloads->findClaimByName('sub')->getValue());
|
||||
/** @noinspection NullPointerExceptionInspection */
|
||||
$this->assertEquals('accounts_web_user', $payloads->findClaimByName('ely-scopes')->getValue());
|
||||
/** @noinspection NullPointerExceptionInspection */
|
||||
$this->assertEquals($session->id, $payloads->findClaimByName('jti')->getValue(), 'session has not changed');
|
||||
}
|
||||
|
||||
public function testParseToken() {
|
||||
$this->mockRequest();
|
||||
$token = $this->callProtected($this->component, 'createToken', new Account(['id' => 1]));
|
||||
$jwt = $this->callProtected($this->component, 'serializeToken', $token);
|
||||
$this->assertInstanceOf(Token::class, $this->component->parseToken($jwt), 'success get RenewResult object');
|
||||
}
|
||||
|
||||
public function testGetActiveSession() {
|
||||
$account = $this->tester->grabFixture('accounts', 'admin');
|
||||
$result = $this->component->createJwtAuthenticationToken($account, true);
|
||||
$this->component->logout();
|
||||
|
||||
/** @var Component|\PHPUnit_Framework_MockObject_MockObject $component */
|
||||
$component = $this->getMockBuilder(Component::class)
|
||||
->setMethods(['getIsGuest'])
|
||||
->setConstructorArgs([$this->getComponentConfig()])
|
||||
->getMock();
|
||||
|
||||
$component
|
||||
->expects($this->any())
|
||||
->method('getIsGuest')
|
||||
->willReturn(false);
|
||||
|
||||
$this->mockAuthorizationHeader($result->getJwt());
|
||||
|
||||
$session = $component->getActiveSession();
|
||||
$this->assertInstanceOf(AccountSession::class, $session);
|
||||
/** @noinspection NullPointerExceptionInspection */
|
||||
$this->assertEquals($session->id, $result->getSession()->id);
|
||||
}
|
||||
|
||||
public function testTerminateSessions() {
|
||||
/** @var AccountSession $session */
|
||||
$session = AccountSession::findOne($this->tester->grabFixture('sessions', 'admin2')['id']);
|
||||
|
||||
/** @var Component|\Mockery\MockInterface $component */
|
||||
$component = mock(Component::class . '[getActiveSession]', [$this->getComponentConfig()])->shouldDeferMissing();
|
||||
$component->shouldReceive('getActiveSession')->times(1)->andReturn($session);
|
||||
|
||||
/** @var Account $account */
|
||||
$account = $this->tester->grabFixture('accounts', 'admin');
|
||||
$component->createJwtAuthenticationToken($account, true);
|
||||
|
||||
$component->terminateSessions($account, Component::KEEP_MINECRAFT_SESSIONS | Component::KEEP_SITE_SESSIONS);
|
||||
$this->assertNotEmpty($account->getMinecraftAccessKeys()->all());
|
||||
$this->assertNotEmpty($account->getSessions()->all());
|
||||
|
||||
$component->terminateSessions($account, Component::KEEP_SITE_SESSIONS);
|
||||
$this->assertEmpty($account->getMinecraftAccessKeys()->all());
|
||||
$this->assertNotEmpty($account->getSessions()->all());
|
||||
|
||||
$component->terminateSessions($account, Component::KEEP_CURRENT_SESSION);
|
||||
$sessions = $account->getSessions()->all();
|
||||
$this->assertEquals(1, count($sessions));
|
||||
$this->assertTrue($sessions[0]->id === $session->id);
|
||||
|
||||
$component->terminateSessions($account);
|
||||
$this->assertEmpty($account->getSessions()->all());
|
||||
$this->assertEmpty($account->getMinecraftAccessKeys()->all());
|
||||
}
|
||||
|
||||
private function mockRequest($userIP = '127.0.0.1') {
|
||||
/** @var Request|\Mockery\MockInterface $request */
|
||||
$request = mock(Request::class . '[getHostInfo,getUserIP]')->shouldDeferMissing();
|
||||
$request->shouldReceive('getHostInfo')->andReturn('http://localhost');
|
||||
$request->shouldReceive('getUserIP')->andReturn($userIP);
|
||||
|
||||
Yii::$app->set('request', $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $bearerToken
|
||||
*/
|
||||
private function mockAuthorizationHeader($bearerToken = null) {
|
||||
if ($bearerToken !== null) {
|
||||
$bearerToken = 'Bearer ' . $bearerToken;
|
||||
}
|
||||
|
||||
Yii::$app->request->headers->set('Authorization', $bearerToken);
|
||||
}
|
||||
|
||||
private function getComponentConfig() {
|
||||
return [
|
||||
'identityClass' => Identity::class,
|
||||
'enableSession' => false,
|
||||
'loginUrl' => null,
|
||||
'secret' => 'secret',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
namespace api\tests\unit\components\User;
|
||||
|
||||
use api\components\User\AuthenticationResult;
|
||||
use common\models\Account;
|
||||
use common\models\AccountSession;
|
||||
use Emarref\Jwt\Algorithm\Hs256;
|
||||
use Emarref\Jwt\Claim\Expiration;
|
||||
use Emarref\Jwt\Encryption\Factory as EncryptionFactory;
|
||||
use Emarref\Jwt\Jwt;
|
||||
use Emarref\Jwt\Token;
|
||||
use api\tests\unit\TestCase;
|
||||
|
||||
class JwtAuthenticationResultTest extends TestCase {
|
||||
|
||||
public function testGetAccount() {
|
||||
$account = new Account();
|
||||
$account->id = 123;
|
||||
$model = new AuthenticationResult($account, '', null);
|
||||
$this->assertEquals($account, $model->getAccount());
|
||||
}
|
||||
|
||||
public function testGetJwt() {
|
||||
$model = new AuthenticationResult(new Account(), 'mocked jwt', null);
|
||||
$this->assertEquals('mocked jwt', $model->getJwt());
|
||||
}
|
||||
|
||||
public function testGetSession() {
|
||||
$model = new AuthenticationResult(new Account(), '', null);
|
||||
$this->assertNull($model->getSession());
|
||||
|
||||
$session = new AccountSession();
|
||||
$session->id = 321;
|
||||
$model = new AuthenticationResult(new Account(), '', $session);
|
||||
$this->assertEquals($session, $model->getSession());
|
||||
}
|
||||
|
||||
public function testGetAsResponse() {
|
||||
$jwtToken = $this->createJwtToken(time() + 3600);
|
||||
$model = new AuthenticationResult(new Account(), $jwtToken, null);
|
||||
$result = $model->getAsResponse();
|
||||
$this->assertEquals($jwtToken, $result['access_token']);
|
||||
$this->assertSame(3600, $result['expires_in']);
|
||||
|
||||
/** @noinspection SummerTimeUnsafeTimeManipulationInspection */
|
||||
$jwtToken = $this->createJwtToken(time() + 86400);
|
||||
$session = new AccountSession();
|
||||
$session->refresh_token = 'refresh token';
|
||||
$model = new AuthenticationResult(new Account(), $jwtToken, $session);
|
||||
$result = $model->getAsResponse();
|
||||
$this->assertEquals($jwtToken, $result['access_token']);
|
||||
$this->assertEquals('refresh token', $result['refresh_token']);
|
||||
$this->assertSame(86400, $result['expires_in']);
|
||||
}
|
||||
|
||||
private function createJwtToken(int $expires): string {
|
||||
$token = new Token();
|
||||
$token->addClaim(new Expiration($expires));
|
||||
|
||||
return (new Jwt())->serialize($token, EncryptionFactory::create(new Hs256('123')));
|
||||
}
|
||||
|
||||
}
|
57
api/tests/unit/filters/NginxCacheTest.php
Normal file
57
api/tests/unit/filters/NginxCacheTest.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
namespace api\tests\unit\filters;
|
||||
|
||||
use api\filters\NginxCache;
|
||||
use api\tests\unit\TestCase;
|
||||
use Yii;
|
||||
use yii\base\Action;
|
||||
use yii\web\Controller;
|
||||
use yii\web\HeaderCollection;
|
||||
use yii\web\Request;
|
||||
|
||||
class NginxCacheTest extends TestCase {
|
||||
|
||||
public function testAfterAction() {
|
||||
$this->testAfterActionInternal(3600, 3600);
|
||||
$this->testAfterActionInternal('@' . (time() + 30), '@' . (time() + 30));
|
||||
$this->testAfterActionInternal(function() {
|
||||
return 3000;
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
private function testAfterActionInternal($ruleConfig, $expected) {
|
||||
/** @var HeaderCollection|\PHPUnit_Framework_MockObject_MockObject $headers */
|
||||
$headers = $this->getMockBuilder(HeaderCollection::class)
|
||||
->setMethods(['set'])
|
||||
->getMock();
|
||||
|
||||
$headers->expects($this->once())
|
||||
->method('set')
|
||||
->with('X-Accel-Expires', $expected);
|
||||
|
||||
/** @var Request|\PHPUnit_Framework_MockObject_MockObject $request */
|
||||
$request = $this->getMockBuilder(Request::class)
|
||||
->setMethods(['getHeaders'])
|
||||
->getMock();
|
||||
|
||||
$request->expects($this->any())
|
||||
->method('getHeaders')
|
||||
->willReturn($headers);
|
||||
|
||||
Yii::$app->set('response', $request);
|
||||
|
||||
/** @var Controller|\PHPUnit_Framework_MockObject_MockObject $controller */
|
||||
$controller = $this->getMockBuilder(Controller::class)
|
||||
->setConstructorArgs(['mock', Yii::$app])
|
||||
->getMock();
|
||||
|
||||
$component = new NginxCache([
|
||||
'rules' => [
|
||||
'index' => $ruleConfig,
|
||||
],
|
||||
]);
|
||||
|
||||
$component->afterAction(new Action('index', $controller), '');
|
||||
}
|
||||
|
||||
}
|
49
api/tests/unit/models/FeedbackFormTest.php
Normal file
49
api/tests/unit/models/FeedbackFormTest.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
namespace codeception\api\unit\models;
|
||||
|
||||
use api\models\FeedbackForm;
|
||||
use common\models\Account;
|
||||
use api\tests\unit\TestCase;
|
||||
use yii\swiftmailer\Message;
|
||||
|
||||
class FeedbackFormTest extends TestCase {
|
||||
|
||||
public function testSendMessage() {
|
||||
$model = new FeedbackForm([
|
||||
'subject' => 'Тема обращения',
|
||||
'email' => 'erickskrauch@ely.by',
|
||||
'message' => 'Привет мир!',
|
||||
]);
|
||||
$this->assertTrue($model->sendMessage());
|
||||
$this->tester->seeEmailIsSent(1, 'message file exists');
|
||||
}
|
||||
|
||||
public function testSendMessageWithEmail() {
|
||||
/** @var FeedbackForm|\PHPUnit_Framework_MockObject_MockObject $model */
|
||||
$model = $this->getMockBuilder(FeedbackForm::class)
|
||||
->setMethods(['getAccount'])
|
||||
->setConstructorArgs([[
|
||||
'subject' => 'Тема обращения',
|
||||
'email' => 'erickskrauch@ely.by',
|
||||
'message' => 'Привет мир!',
|
||||
]])
|
||||
->getMock();
|
||||
|
||||
$model
|
||||
->expects($this->any())
|
||||
->method('getAccount')
|
||||
->will($this->returnValue(new Account([
|
||||
'id' => '123',
|
||||
'username' => 'Erick',
|
||||
'email' => 'find-this@email.net',
|
||||
'created_at' => time() - 86400,
|
||||
])));
|
||||
$this->assertTrue($model->sendMessage());
|
||||
/** @var Message $message */
|
||||
$message = $this->tester->grabLastSentEmail();
|
||||
$this->assertInstanceOf(Message::class, $message);
|
||||
$data = (string)$message;
|
||||
$this->assertContains('find-this@email.net', $data);
|
||||
}
|
||||
|
||||
}
|
66
api/tests/unit/models/JwtIdentityTest.php
Normal file
66
api/tests/unit/models/JwtIdentityTest.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
namespace codeception\api\unit\models;
|
||||
|
||||
use api\components\User\IdentityInterface;
|
||||
use api\components\User\Jwt;
|
||||
use api\components\User\JwtIdentity;
|
||||
use Codeception\Specify;
|
||||
use Emarref\Jwt\Claim;
|
||||
use Emarref\Jwt\Encryption\Factory as EncryptionFactory;
|
||||
use Emarref\Jwt\Token;
|
||||
use api\tests\unit\TestCase;
|
||||
use common\tests\_support\ProtectedCaller;
|
||||
use common\tests\fixtures\AccountFixture;
|
||||
use Yii;
|
||||
|
||||
class JwtIdentityTest extends TestCase {
|
||||
use Specify;
|
||||
use ProtectedCaller;
|
||||
|
||||
public function _fixtures(): array {
|
||||
return [
|
||||
'accounts' => AccountFixture::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function testFindIdentityByAccessToken() {
|
||||
$token = $this->generateToken();
|
||||
$identity = JwtIdentity::findIdentityByAccessToken($token);
|
||||
$this->assertInstanceOf(IdentityInterface::class, $identity);
|
||||
$this->assertEquals($token, $identity->getId());
|
||||
$this->assertEquals($this->tester->grabFixture('accounts', 'admin')['id'], $identity->getAccount()->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \yii\web\UnauthorizedHttpException
|
||||
* @expectedExceptionMessage Token expired
|
||||
*/
|
||||
public function testFindIdentityByAccessTokenWithExpiredToken() {
|
||||
$token = new Token();
|
||||
$token->addClaim(new Claim\IssuedAt(1464593193));
|
||||
$token->addClaim(new Claim\Expiration(1464596793));
|
||||
$token->addClaim(new Claim\Subject('ely|' . $this->tester->grabFixture('accounts', 'admin')['id']));
|
||||
$expiredToken = (new Jwt())->serialize($token, EncryptionFactory::create(Yii::$app->user->getAlgorithm()));
|
||||
|
||||
JwtIdentity::findIdentityByAccessToken($expiredToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \yii\web\UnauthorizedHttpException
|
||||
* @expectedExceptionMessage Incorrect token
|
||||
*/
|
||||
public function testFindIdentityByAccessTokenWithEmptyToken() {
|
||||
JwtIdentity::findIdentityByAccessToken('');
|
||||
}
|
||||
|
||||
protected function generateToken() {
|
||||
/** @var \api\components\User\Component $component */
|
||||
$component = Yii::$app->user;
|
||||
/** @var \common\models\Account $account */
|
||||
$account = $this->tester->grabFixture('accounts', 'admin');
|
||||
$token = $this->callProtected($component, 'createToken', $account);
|
||||
|
||||
return $this->callProtected($component, 'serializeToken', $token);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
namespace api\tests\_support\models\authentication;
|
||||
|
||||
use api\components\User\AuthenticationResult;
|
||||
use api\models\authentication\ConfirmEmailForm;
|
||||
use common\models\Account;
|
||||
use common\models\AccountSession;
|
||||
use common\models\EmailActivation;
|
||||
use api\tests\unit\TestCase;
|
||||
use common\tests\fixtures\EmailActivationFixture;
|
||||
|
||||
class ConfirmEmailFormTest extends TestCase {
|
||||
|
||||
public function _fixtures() {
|
||||
return [
|
||||
'emailActivations' => EmailActivationFixture::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function testConfirm() {
|
||||
$fixture = $this->tester->grabFixture('emailActivations', 'freshRegistrationConfirmation');
|
||||
$model = $this->createModel($fixture['key']);
|
||||
$result = $model->confirm();
|
||||
$this->assertInstanceOf(AuthenticationResult::class, $result);
|
||||
$this->assertInstanceOf(AccountSession::class, $result->getSession(), 'session was generated');
|
||||
$activationExists = EmailActivation::find()->andWhere(['key' => $fixture['key']])->exists();
|
||||
$this->assertFalse($activationExists, 'email activation key is not exist');
|
||||
/** @var Account $account */
|
||||
$account = Account::findOne($fixture['account_id']);
|
||||
$this->assertEquals(Account::STATUS_ACTIVE, $account->status, 'user status changed to active');
|
||||
}
|
||||
|
||||
private function createModel($key) {
|
||||
return new ConfirmEmailForm([
|
||||
'key' => $key,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
134
api/tests/unit/models/authentication/ForgotPasswordFormTest.php
Normal file
134
api/tests/unit/models/authentication/ForgotPasswordFormTest.php
Normal file
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
namespace codeception\api\unit\models\authentication;
|
||||
|
||||
use api\components\ReCaptcha\Validator as ReCaptchaValidator;
|
||||
use api\models\authentication\ForgotPasswordForm;
|
||||
use Codeception\Specify;
|
||||
use common\models\Account;
|
||||
use common\models\EmailActivation;
|
||||
use common\tasks\SendPasswordRecoveryEmail;
|
||||
use GuzzleHttp\ClientInterface;
|
||||
use api\tests\unit\TestCase;
|
||||
use common\tests\fixtures\AccountFixture;
|
||||
use common\tests\fixtures\EmailActivationFixture;
|
||||
use Yii;
|
||||
|
||||
class ForgotPasswordFormTest extends TestCase {
|
||||
use Specify;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
Yii::$container->set(ReCaptchaValidator::class, new class(mock(ClientInterface::class)) extends ReCaptchaValidator {
|
||||
public function validateValue($value) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function _fixtures() {
|
||||
return [
|
||||
'accounts' => AccountFixture::class,
|
||||
'emailActivations' => EmailActivationFixture::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function testValidateLogin() {
|
||||
$model = new ForgotPasswordForm(['login' => 'unexist']);
|
||||
$model->validateLogin('login');
|
||||
$this->assertEquals(['error.login_not_exist'], $model->getErrors('login'), 'error.login_not_exist if login is invalid');
|
||||
|
||||
$model = new ForgotPasswordForm(['login' => $this->tester->grabFixture('accounts', 'admin')['username']]);
|
||||
$model->validateLogin('login');
|
||||
$this->assertEmpty($model->getErrors('login'), 'empty errors if login is exists');
|
||||
}
|
||||
|
||||
public function testValidateActivity() {
|
||||
$model = new ForgotPasswordForm([
|
||||
'login' => $this->tester->grabFixture('accounts', 'not-activated-account')['username'],
|
||||
]);
|
||||
$model->validateActivity('login');
|
||||
$this->assertEquals(['error.account_not_activated'], $model->getErrors('login'), 'expected error if account is not confirmed');
|
||||
|
||||
$model = new ForgotPasswordForm([
|
||||
'login' => $this->tester->grabFixture('accounts', 'admin')['username'],
|
||||
]);
|
||||
$model->validateLogin('login');
|
||||
$this->assertEmpty($model->getErrors('login'), 'empty errors if login is exists');
|
||||
}
|
||||
|
||||
public function testValidateFrequency() {
|
||||
$model = $this->createModel([
|
||||
'login' => $this->tester->grabFixture('accounts', 'admin')['username'],
|
||||
'key' => $this->tester->grabFixture('emailActivations', 'freshPasswordRecovery')['key'],
|
||||
]);
|
||||
$model->validateFrequency('login');
|
||||
$this->assertEquals(['error.recently_sent_message'], $model->getErrors('login'), 'error.account_not_activated if recently was message');
|
||||
|
||||
$model = $this->createModel([
|
||||
'login' => $this->tester->grabFixture('accounts', 'admin')['username'],
|
||||
'key' => $this->tester->grabFixture('emailActivations', 'oldPasswordRecovery')['key'],
|
||||
]);
|
||||
$model->validateFrequency('login');
|
||||
$this->assertEmpty($model->getErrors('login'), 'empty errors if email was sent a long time ago');
|
||||
|
||||
$model = $this->createModel([
|
||||
'login' => $this->tester->grabFixture('accounts', 'admin')['username'],
|
||||
'key' => 'invalid-key',
|
||||
]);
|
||||
$model->validateFrequency('login');
|
||||
$this->assertEmpty($model->getErrors('login'), 'empty errors if previous confirmation model not founded');
|
||||
}
|
||||
|
||||
public function testForgotPassword() {
|
||||
/** @var Account $account */
|
||||
$account = $this->tester->grabFixture('accounts', 'admin');
|
||||
$model = new ForgotPasswordForm(['login' => $account->username]);
|
||||
$this->assertTrue($model->forgotPassword(), 'form should be successfully processed');
|
||||
$activation = $model->getEmailActivation();
|
||||
$this->assertInstanceOf(EmailActivation::class, $activation, 'getEmailActivation should return valid object instance');
|
||||
|
||||
$this->assertTaskCreated($this->tester->grabLastQueuedJob(), $account, $activation);
|
||||
}
|
||||
|
||||
public function testForgotPasswordResend() {
|
||||
/** @var Account $account */
|
||||
$account = $this->tester->grabFixture('accounts', 'account-with-expired-forgot-password-message');
|
||||
$model = new ForgotPasswordForm(['login' => $account->username]);
|
||||
$callTime = time();
|
||||
$this->assertTrue($model->forgotPassword(), 'form should be successfully processed');
|
||||
$emailActivation = $model->getEmailActivation();
|
||||
$this->assertInstanceOf(EmailActivation::class, $emailActivation);
|
||||
$this->assertGreaterThanOrEqual($callTime, $emailActivation->created_at);
|
||||
|
||||
$this->assertTaskCreated($this->tester->grabLastQueuedJob(), $account, $emailActivation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SendPasswordRecoveryEmail $job
|
||||
* @param Account $account
|
||||
* @param EmailActivation $activation
|
||||
*/
|
||||
private function assertTaskCreated($job, Account $account, EmailActivation $activation) {
|
||||
$this->assertInstanceOf(SendPasswordRecoveryEmail::class, $job);
|
||||
$this->assertSame($account->username, $job->username);
|
||||
$this->assertSame($account->email, $job->email);
|
||||
$this->assertSame($account->lang, $job->locale);
|
||||
$this->assertSame($activation->key, $job->code);
|
||||
$this->assertSame('http://localhost/recover-password/' . $activation->key, $job->link);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
* @return ForgotPasswordForm
|
||||
*/
|
||||
private function createModel(array $params = []) {
|
||||
return new class($params) extends ForgotPasswordForm {
|
||||
public $key;
|
||||
|
||||
public function getEmailActivation() {
|
||||
return EmailActivation::findOne($this->key);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
174
api/tests/unit/models/authentication/LoginFormTest.php
Normal file
174
api/tests/unit/models/authentication/LoginFormTest.php
Normal file
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
namespace api\tests\_support\models\authentication;
|
||||
|
||||
use api\components\User\AuthenticationResult;
|
||||
use api\models\authentication\LoginForm;
|
||||
use Codeception\Specify;
|
||||
use common\models\Account;
|
||||
use OTPHP\TOTP;
|
||||
use api\tests\unit\TestCase;
|
||||
use common\tests\fixtures\AccountFixture;
|
||||
|
||||
class LoginFormTest extends TestCase {
|
||||
use Specify;
|
||||
|
||||
private $originalRemoteAddr;
|
||||
|
||||
public function setUp() {
|
||||
$this->originalRemoteAddr = $_SERVER['REMOTE_ADDR'] ?? null;
|
||||
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function tearDown() {
|
||||
parent::tearDown();
|
||||
$_SERVER['REMOTE_ADDR'] = $this->originalRemoteAddr;
|
||||
}
|
||||
|
||||
public function _fixtures() {
|
||||
return [
|
||||
'accounts' => AccountFixture::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function testValidateLogin() {
|
||||
$this->specify('error.login_not_exist if login not exists', function() {
|
||||
$model = $this->createModel([
|
||||
'login' => 'mr-test',
|
||||
'account' => null,
|
||||
]);
|
||||
$model->validateLogin('login');
|
||||
$this->assertEquals(['error.login_not_exist'], $model->getErrors('login'));
|
||||
});
|
||||
|
||||
$this->specify('no errors if login exists', function() {
|
||||
$model = $this->createModel([
|
||||
'login' => 'mr-test',
|
||||
'account' => new Account(),
|
||||
]);
|
||||
$model->validateLogin('login');
|
||||
$this->assertEmpty($model->getErrors('login'));
|
||||
});
|
||||
}
|
||||
|
||||
public function testValidatePassword() {
|
||||
$this->specify('error.password_incorrect if password invalid', function() {
|
||||
$model = $this->createModel([
|
||||
'password' => '87654321',
|
||||
'account' => new Account(['password' => '12345678']),
|
||||
]);
|
||||
$model->validatePassword('password');
|
||||
$this->assertEquals(['error.password_incorrect'], $model->getErrors('password'));
|
||||
});
|
||||
|
||||
$this->specify('no errors if password valid', function() {
|
||||
$model = $this->createModel([
|
||||
'password' => '12345678',
|
||||
'account' => new Account(['password' => '12345678']),
|
||||
]);
|
||||
$model->validatePassword('password');
|
||||
$this->assertEmpty($model->getErrors('password'));
|
||||
});
|
||||
}
|
||||
|
||||
public function testValidateTotp() {
|
||||
$account = new Account(['password' => '12345678']);
|
||||
$account->password = '12345678';
|
||||
$account->is_otp_enabled = true;
|
||||
$account->otp_secret = 'AAAA';
|
||||
|
||||
$this->specify('error.totp_incorrect if totp invalid', function() use ($account) {
|
||||
$model = $this->createModel([
|
||||
'password' => '12345678',
|
||||
'totp' => '321123',
|
||||
'account' => $account,
|
||||
]);
|
||||
$model->validateTotp('totp');
|
||||
$this->assertEquals(['error.totp_incorrect'], $model->getErrors('totp'));
|
||||
});
|
||||
|
||||
$totp = TOTP::create($account->otp_secret);
|
||||
$this->specify('no errors if password valid', function() use ($account, $totp) {
|
||||
$model = $this->createModel([
|
||||
'password' => '12345678',
|
||||
'totp' => $totp->now(),
|
||||
'account' => $account,
|
||||
]);
|
||||
$model->validateTotp('totp');
|
||||
$this->assertEmpty($model->getErrors('totp'));
|
||||
});
|
||||
}
|
||||
|
||||
public function testValidateActivity() {
|
||||
$this->specify('error.account_not_activated if account in not activated state', function() {
|
||||
$model = $this->createModel([
|
||||
'account' => new Account(['status' => Account::STATUS_REGISTERED]),
|
||||
]);
|
||||
$model->validateActivity('login');
|
||||
$this->assertEquals(['error.account_not_activated'], $model->getErrors('login'));
|
||||
});
|
||||
|
||||
$this->specify('error.account_banned if account has banned status', function() {
|
||||
$model = $this->createModel([
|
||||
'account' => new Account(['status' => Account::STATUS_BANNED]),
|
||||
]);
|
||||
$model->validateActivity('login');
|
||||
$this->assertEquals(['error.account_banned'], $model->getErrors('login'));
|
||||
});
|
||||
|
||||
$this->specify('no errors if account active', function() {
|
||||
$model = $this->createModel([
|
||||
'account' => new Account(['status' => Account::STATUS_ACTIVE]),
|
||||
]);
|
||||
$model->validateActivity('login');
|
||||
$this->assertEmpty($model->getErrors('login'));
|
||||
});
|
||||
}
|
||||
|
||||
public function testLogin() {
|
||||
$model = $this->createModel([
|
||||
'login' => 'erickskrauch',
|
||||
'password' => '12345678',
|
||||
'account' => new Account([
|
||||
'username' => 'erickskrauch',
|
||||
'password' => '12345678',
|
||||
'status' => Account::STATUS_ACTIVE,
|
||||
]),
|
||||
]);
|
||||
$this->assertInstanceOf(AuthenticationResult::class, $model->login(), 'model should login user');
|
||||
$this->assertEmpty($model->getErrors(), 'error message should not be set');
|
||||
}
|
||||
|
||||
public function testLoginWithRehashing() {
|
||||
$model = new LoginForm([
|
||||
'login' => $this->tester->grabFixture('accounts', 'user-with-old-password-type')['username'],
|
||||
'password' => '12345678',
|
||||
]);
|
||||
$this->assertInstanceOf(AuthenticationResult::class, $model->login());
|
||||
$this->assertEmpty($model->getErrors());
|
||||
$this->assertEquals(
|
||||
Account::PASS_HASH_STRATEGY_YII2,
|
||||
$model->getAccount()->password_hash_strategy,
|
||||
'user, that login using account with old pass hash strategy should update it automatically'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
* @return LoginForm
|
||||
*/
|
||||
private function createModel(array $params = []) {
|
||||
return new class($params) extends LoginForm {
|
||||
private $_account;
|
||||
|
||||
public function setAccount($value) {
|
||||
$this->_account = $value;
|
||||
}
|
||||
|
||||
public function getAccount(): ?Account {
|
||||
return $this->_account;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
69
api/tests/unit/models/authentication/LogoutFormTest.php
Normal file
69
api/tests/unit/models/authentication/LogoutFormTest.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
namespace api\tests\_support\models\authentication;
|
||||
|
||||
use api\components\User\Component;
|
||||
use api\components\User\Identity;
|
||||
use api\models\authentication\LogoutForm;
|
||||
use Codeception\Specify;
|
||||
use common\models\AccountSession;
|
||||
use api\tests\unit\TestCase;
|
||||
use Yii;
|
||||
|
||||
class LogoutFormTest extends TestCase {
|
||||
use Specify;
|
||||
|
||||
public function testValidateLogout() {
|
||||
$this->specify('No actions if active session is not exists', function() {
|
||||
$userComp = $this
|
||||
->getMockBuilder(Component::class)
|
||||
->setConstructorArgs([$this->getComponentArgs()])
|
||||
->setMethods(['getActiveSession'])
|
||||
->getMock();
|
||||
$userComp
|
||||
->expects($this->any())
|
||||
->method('getActiveSession')
|
||||
->will($this->returnValue(null));
|
||||
|
||||
Yii::$app->set('user', $userComp);
|
||||
|
||||
$model = new LogoutForm();
|
||||
expect($model->logout())->true();
|
||||
});
|
||||
|
||||
$this->specify('if active session is presented, then delete should be called', function() {
|
||||
$session = $this
|
||||
->getMockBuilder(AccountSession::class)
|
||||
->setMethods(['delete'])
|
||||
->getMock();
|
||||
$session
|
||||
->expects($this->once())
|
||||
->method('delete')
|
||||
->willReturn(true);
|
||||
|
||||
$userComp = $this
|
||||
->getMockBuilder(Component::class)
|
||||
->setConstructorArgs([$this->getComponentArgs()])
|
||||
->setMethods(['getActiveSession'])
|
||||
->getMock();
|
||||
$userComp
|
||||
->expects($this->any())
|
||||
->method('getActiveSession')
|
||||
->will($this->returnValue($session));
|
||||
|
||||
Yii::$app->set('user', $userComp);
|
||||
|
||||
$model = new LogoutForm();
|
||||
$model->logout();
|
||||
});
|
||||
}
|
||||
|
||||
private function getComponentArgs() {
|
||||
return [
|
||||
'identityClass' => Identity::class,
|
||||
'enableSession' => false,
|
||||
'loginUrl' => null,
|
||||
'secret' => 'secret',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
namespace api\tests\_support\models\authentication;
|
||||
|
||||
use api\components\User\AuthenticationResult;
|
||||
use api\models\authentication\RecoverPasswordForm;
|
||||
use Codeception\Specify;
|
||||
use common\models\Account;
|
||||
use common\models\EmailActivation;
|
||||
use api\tests\unit\TestCase;
|
||||
use common\tests\fixtures\EmailActivationFixture;
|
||||
|
||||
class RecoverPasswordFormTest extends TestCase {
|
||||
use Specify;
|
||||
|
||||
public function _fixtures() {
|
||||
return [
|
||||
'emailActivations' => EmailActivationFixture::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function testRecoverPassword() {
|
||||
$fixture = $this->tester->grabFixture('emailActivations', 'freshPasswordRecovery');
|
||||
$model = new RecoverPasswordForm([
|
||||
'key' => $fixture['key'],
|
||||
'newPassword' => '12345678',
|
||||
'newRePassword' => '12345678',
|
||||
]);
|
||||
$result = $model->recoverPassword();
|
||||
$this->assertInstanceOf(AuthenticationResult::class, $result);
|
||||
$this->assertNull($result->getSession(), 'session was not generated');
|
||||
$this->assertFalse(EmailActivation::find()->andWhere(['key' => $fixture['key']])->exists());
|
||||
/** @var Account $account */
|
||||
$account = Account::findOne($fixture['account_id']);
|
||||
$this->assertTrue($account->validatePassword('12345678'));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
namespace codeception\api\unit\models\authentication;
|
||||
|
||||
use api\components\User\AuthenticationResult;
|
||||
use api\models\authentication\RefreshTokenForm;
|
||||
use Codeception\Specify;
|
||||
use common\models\AccountSession;
|
||||
use api\tests\unit\TestCase;
|
||||
use common\tests\fixtures\AccountSessionFixture;
|
||||
|
||||
class RefreshTokenFormTest extends TestCase {
|
||||
use Specify;
|
||||
|
||||
public function _fixtures() {
|
||||
return [
|
||||
'sessions' => AccountSessionFixture::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function testValidateRefreshToken() {
|
||||
$this->specify('error.refresh_token_not_exist if passed token not exists', function() {
|
||||
/** @var RefreshTokenForm $model */
|
||||
$model = new class extends RefreshTokenForm {
|
||||
public function getSession() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
$model->validateRefreshToken();
|
||||
$this->assertEquals(['error.refresh_token_not_exist'], $model->getErrors('refresh_token'));
|
||||
});
|
||||
|
||||
$this->specify('no errors if token exists', function() {
|
||||
/** @var RefreshTokenForm $model */
|
||||
$model = new class extends RefreshTokenForm {
|
||||
public function getSession() {
|
||||
return new AccountSession();
|
||||
}
|
||||
};
|
||||
$model->validateRefreshToken();
|
||||
$this->assertEmpty($model->getErrors('refresh_token'));
|
||||
});
|
||||
}
|
||||
|
||||
public function testRenew() {
|
||||
$model = new RefreshTokenForm();
|
||||
$model->refresh_token = $this->tester->grabFixture('sessions', 'admin')['refresh_token'];
|
||||
$this->assertInstanceOf(AuthenticationResult::class, $model->renew());
|
||||
}
|
||||
|
||||
}
|
141
api/tests/unit/models/authentication/RegistrationFormTest.php
Normal file
141
api/tests/unit/models/authentication/RegistrationFormTest.php
Normal file
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
namespace api\tests\_support\models\authentication;
|
||||
|
||||
use api\components\ReCaptcha\Validator as ReCaptchaValidator;
|
||||
use api\models\authentication\RegistrationForm;
|
||||
use common\models\Account;
|
||||
use common\models\EmailActivation;
|
||||
use common\models\UsernameHistory;
|
||||
use common\tasks\SendRegistrationEmail;
|
||||
use GuzzleHttp\ClientInterface;
|
||||
use api\tests\unit\TestCase;
|
||||
use common\tests\fixtures\AccountFixture;
|
||||
use common\tests\fixtures\EmailActivationFixture;
|
||||
use common\tests\fixtures\UsernameHistoryFixture;
|
||||
use common\tests\helpers\Mock;
|
||||
use Yii;
|
||||
use yii\validators\EmailValidator;
|
||||
use yii\web\Request;
|
||||
use const common\LATEST_RULES_VERSION;
|
||||
|
||||
class RegistrationFormTest extends TestCase {
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$this->mockRequest();
|
||||
Yii::$container->set(ReCaptchaValidator::class, new class(mock(ClientInterface::class)) extends ReCaptchaValidator {
|
||||
public function validateValue($value) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function _fixtures() {
|
||||
return [
|
||||
'accounts' => AccountFixture::class,
|
||||
'emailActivations' => EmailActivationFixture::class,
|
||||
'usernameHistory' => UsernameHistoryFixture::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function testValidatePasswordAndRePasswordMatch() {
|
||||
$model = new RegistrationForm([
|
||||
'password' => 'enough-length',
|
||||
'rePassword' => 'but-mismatch',
|
||||
]);
|
||||
$this->assertFalse($model->validate(['rePassword']));
|
||||
$this->assertSame(['error.rePassword_does_not_match'], $model->getErrors('rePassword'));
|
||||
|
||||
$model = new RegistrationForm([
|
||||
'password' => 'enough-length',
|
||||
'rePassword' => 'enough-length',
|
||||
]);
|
||||
$this->assertTrue($model->validate(['rePassword']));
|
||||
$this->assertEmpty($model->getErrors('rePassword'));
|
||||
}
|
||||
|
||||
public function testSignup() {
|
||||
Mock::func(EmailValidator::class, 'checkdnsrr')->andReturnTrue();
|
||||
$model = new RegistrationForm([
|
||||
'username' => 'some_username',
|
||||
'email' => 'some_email@example.com',
|
||||
'password' => 'some_password',
|
||||
'rePassword' => 'some_password',
|
||||
'rulesAgreement' => true,
|
||||
'lang' => 'ru',
|
||||
]);
|
||||
|
||||
$account = $model->signup();
|
||||
|
||||
$this->expectSuccessRegistration($account);
|
||||
$this->assertEquals('ru', $account->lang, 'lang is set');
|
||||
}
|
||||
|
||||
public function testSignupWithDefaultLanguage() {
|
||||
Mock::func(EmailValidator::class, 'checkdnsrr')->andReturnTrue();
|
||||
$model = new RegistrationForm([
|
||||
'username' => 'some_username',
|
||||
'email' => 'some_email@example.com',
|
||||
'password' => 'some_password',
|
||||
'rePassword' => 'some_password',
|
||||
'rulesAgreement' => true,
|
||||
]);
|
||||
|
||||
$account = $model->signup();
|
||||
|
||||
$this->expectSuccessRegistration($account);
|
||||
$this->assertEquals('en', $account->lang, 'lang is set');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account|null $account
|
||||
*/
|
||||
private function expectSuccessRegistration($account) {
|
||||
$this->assertInstanceOf(Account::class, $account, 'user should be valid');
|
||||
$this->assertTrue($account->validatePassword('some_password'), 'password should be correct');
|
||||
$this->assertNotEmpty($account->uuid, 'uuid is set');
|
||||
$this->assertNotNull($account->registration_ip, 'registration_ip is set');
|
||||
$this->assertEquals(LATEST_RULES_VERSION, $account->rules_agreement_version, 'actual rules version is set');
|
||||
$this->assertTrue(Account::find()->andWhere([
|
||||
'username' => 'some_username',
|
||||
'email' => 'some_email@example.com',
|
||||
])->exists(), 'user model exists in database');
|
||||
/** @var EmailActivation $activation */
|
||||
$activation = EmailActivation::find()
|
||||
->andWhere([
|
||||
'account_id' => $account->id,
|
||||
'type' => EmailActivation::TYPE_REGISTRATION_EMAIL_CONFIRMATION,
|
||||
])
|
||||
->one();
|
||||
$this->assertInstanceOf(EmailActivation::class, $activation, 'email activation code exists in database');
|
||||
$this->assertTrue(UsernameHistory::find()->andWhere([
|
||||
'username' => $account->username,
|
||||
'account_id' => $account->id,
|
||||
'applied_in' => $account->created_at,
|
||||
])->exists(), 'username history record exists in database');
|
||||
|
||||
/** @var SendRegistrationEmail $job */
|
||||
$job = $this->tester->grabLastQueuedJob();
|
||||
$this->assertInstanceOf(SendRegistrationEmail::class, $job);
|
||||
$this->assertSame($account->username, $job->username);
|
||||
$this->assertSame($account->email, $job->email);
|
||||
$this->assertSame($account->lang, $job->locale);
|
||||
$this->assertSame($activation->key, $job->code);
|
||||
$this->assertSame('http://localhost/activation/' . $activation->key, $job->link);
|
||||
}
|
||||
|
||||
private function mockRequest($ip = '88.225.20.236') {
|
||||
$request = $this->getMockBuilder(Request::class)
|
||||
->setMethods(['getUserIP'])
|
||||
->getMock();
|
||||
|
||||
$request
|
||||
->method('getUserIP')
|
||||
->willReturn($ip);
|
||||
|
||||
Yii::$app->set('request', $request);
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
namespace api\tests\_support\models\authentication;
|
||||
|
||||
use api\components\ReCaptcha\Validator as ReCaptchaValidator;
|
||||
use api\models\authentication\RepeatAccountActivationForm;
|
||||
use Codeception\Specify;
|
||||
use common\models\EmailActivation;
|
||||
use common\tasks\SendRegistrationEmail;
|
||||
use GuzzleHttp\ClientInterface;
|
||||
use api\tests\unit\TestCase;
|
||||
use common\tests\fixtures\AccountFixture;
|
||||
use common\tests\fixtures\EmailActivationFixture;
|
||||
use Yii;
|
||||
|
||||
class RepeatAccountActivationFormTest extends TestCase {
|
||||
use Specify;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
Yii::$container->set(ReCaptchaValidator::class, new class(mock(ClientInterface::class)) extends ReCaptchaValidator {
|
||||
public function validateValue($value) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function _fixtures() {
|
||||
return [
|
||||
'accounts' => AccountFixture::class,
|
||||
'activations' => EmailActivationFixture::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function testValidateEmailForAccount() {
|
||||
$this->specify('error.email_not_found if passed valid email, but it don\'t exists in database', function() {
|
||||
$model = new RepeatAccountActivationForm(['email' => 'me-is-not@exists.net']);
|
||||
$model->validateEmailForAccount('email');
|
||||
expect($model->getErrors('email'))->equals(['error.email_not_found']);
|
||||
});
|
||||
|
||||
$this->specify('error.account_already_activated if passed valid email, but account already activated', function() {
|
||||
$fixture = $this->tester->grabFixture('accounts', 'admin');
|
||||
$model = new RepeatAccountActivationForm(['email' => $fixture['email']]);
|
||||
$model->validateEmailForAccount('email');
|
||||
expect($model->getErrors('email'))->equals(['error.account_already_activated']);
|
||||
});
|
||||
|
||||
$this->specify('no errors if passed valid email for not activated account', function() {
|
||||
$fixture = $this->tester->grabFixture('accounts', 'not-activated-account');
|
||||
$model = new RepeatAccountActivationForm(['email' => $fixture['email']]);
|
||||
$model->validateEmailForAccount('email');
|
||||
expect($model->getErrors('email'))->isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
public function testValidateExistsActivation() {
|
||||
$this->specify('error.recently_sent_message if passed email has recently sent message', function() {
|
||||
$fixture = $this->tester->grabFixture('activations', 'freshRegistrationConfirmation');
|
||||
$model = $this->createModel(['emailKey' => $fixture['key']]);
|
||||
$model->validateExistsActivation('email');
|
||||
expect($model->getErrors('email'))->equals(['error.recently_sent_message']);
|
||||
});
|
||||
|
||||
$this->specify('no errors if passed email has expired activation message', function() {
|
||||
$fixture = $this->tester->grabFixture('activations', 'oldRegistrationConfirmation');
|
||||
$model = $this->createModel(['emailKey' => $fixture['key']]);
|
||||
$model->validateExistsActivation('email');
|
||||
expect($model->getErrors('email'))->isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
public function testSendRepeatMessage() {
|
||||
$model = new RepeatAccountActivationForm();
|
||||
$this->assertFalse($model->sendRepeatMessage(), 'no magic if we don\'t pass validation');
|
||||
$this->assertEmpty($this->tester->grabQueueJobs());
|
||||
|
||||
/** @var \common\models\Account $account */
|
||||
$account = $this->tester->grabFixture('accounts', 'not-activated-account-with-expired-message');
|
||||
$model = new RepeatAccountActivationForm(['email' => $account->email]);
|
||||
$this->assertTrue($model->sendRepeatMessage());
|
||||
$activation = $model->getActivation();
|
||||
$this->assertNotNull($activation);
|
||||
/** @var SendRegistrationEmail $job */
|
||||
$job = $this->tester->grabLastQueuedJob();
|
||||
$this->assertInstanceOf(SendRegistrationEmail::class, $job);
|
||||
$this->assertSame($account->username, $job->username);
|
||||
$this->assertSame($account->email, $job->email);
|
||||
$this->assertSame($account->lang, $job->locale);
|
||||
$this->assertSame($activation->key, $job->code);
|
||||
$this->assertSame('http://localhost/activation/' . $activation->key, $job->link);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
* @return RepeatAccountActivationForm
|
||||
*/
|
||||
private function createModel(array $params = []) {
|
||||
return new class($params) extends RepeatAccountActivationForm {
|
||||
public $emailKey;
|
||||
|
||||
public function getActivation() {
|
||||
return EmailActivation::findOne($this->emailKey);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
27
api/tests/unit/models/base/ApiFormTest.php
Normal file
27
api/tests/unit/models/base/ApiFormTest.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
namespace api\tests\_support\models\base;
|
||||
|
||||
use api\models\base\ApiForm;
|
||||
use api\tests\unit\TestCase;
|
||||
|
||||
class ApiFormTest extends TestCase {
|
||||
|
||||
public function testLoad() {
|
||||
$model = new DummyApiForm();
|
||||
$this->assertTrue($model->load(['field' => 'test-data']), 'model successful load data without prefix');
|
||||
$this->assertEquals('test-data', $model->field, 'field is set as passed data');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class DummyApiForm extends ApiForm {
|
||||
|
||||
public $field;
|
||||
|
||||
public function rules() {
|
||||
return [
|
||||
['field', 'safe'],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace api\tests\unit\modules\accounts\models;
|
||||
|
||||
use api\modules\accounts\models\AcceptRulesForm;
|
||||
use common\models\Account;
|
||||
use api\tests\unit\TestCase;
|
||||
use const common\LATEST_RULES_VERSION;
|
||||
|
||||
class AcceptRulesFormTest extends TestCase {
|
||||
|
||||
public function testAgreeWithLatestRules() {
|
||||
/** @var Account|\Mockery\MockInterface $account */
|
||||
$account = mock(Account::class . '[save]');
|
||||
$account->shouldReceive('save')->andReturn(true);
|
||||
$account->rules_agreement_version = LATEST_RULES_VERSION - 1;
|
||||
|
||||
$model = new AcceptRulesForm($account);
|
||||
$this->assertTrue($model->performAction());
|
||||
$this->assertEquals(LATEST_RULES_VERSION, $account->rules_agreement_version);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
namespace api\tests\unit\modules\accounts\models;
|
||||
|
||||
use api\modules\accounts\models\ChangeEmailForm;
|
||||
use common\models\Account;
|
||||
use common\models\EmailActivation;
|
||||
use api\tests\unit\TestCase;
|
||||
use common\tests\fixtures\AccountFixture;
|
||||
use common\tests\fixtures\EmailActivationFixture;
|
||||
|
||||
class ChangeEmailFormTest extends TestCase {
|
||||
|
||||
public function _fixtures() {
|
||||
return [
|
||||
'accounts' => AccountFixture::class,
|
||||
'emailActivations' => EmailActivationFixture::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function testChangeEmail() {
|
||||
/** @var Account $account */
|
||||
$account = Account::findOne($this->getAccountId());
|
||||
$newEmailConfirmationFixture = $this->tester->grabFixture('emailActivations', 'newEmailConfirmation');
|
||||
$model = new ChangeEmailForm($account, [
|
||||
'key' => $newEmailConfirmationFixture['key'],
|
||||
]);
|
||||
$this->assertTrue($model->performAction());
|
||||
$this->assertNull(EmailActivation::findOne([
|
||||
'account_id' => $account->id,
|
||||
'type' => EmailActivation::TYPE_NEW_EMAIL_CONFIRMATION,
|
||||
]));
|
||||
/** @noinspection UnserializeExploitsInspection */
|
||||
$data = unserialize($newEmailConfirmationFixture['_data']);
|
||||
$this->assertEquals($data['newEmail'], $account->email);
|
||||
}
|
||||
|
||||
private function getAccountId() {
|
||||
return $this->tester->grabFixture('accounts', 'account-with-change-email-finish-state')['id'];
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
namespace api\tests\unit\modules\accounts\models;
|
||||
|
||||
use api\modules\accounts\models\ChangeLanguageForm;
|
||||
use common\models\Account;
|
||||
use api\tests\unit\TestCase;
|
||||
|
||||
class ChangeLanguageFormTest extends TestCase {
|
||||
|
||||
public function testApplyLanguage() {
|
||||
/** @var Account|\Mockery\MockInterface $account */
|
||||
$account = mock(Account::class . '[save]');
|
||||
$account->shouldReceive('save')->andReturn(true);
|
||||
|
||||
$model = new ChangeLanguageForm($account);
|
||||
$model->lang = 'ru';
|
||||
$this->assertTrue($model->performAction());
|
||||
$this->assertEquals('ru', $account->lang);
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user