-
-
Notifications
You must be signed in to change notification settings - Fork 6.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rate limiter refactor test #13994
Rate limiter refactor test #13994
Changes from 9 commits
36705f0
4509ea3
a085ce8
6cc0633
723fabe
3e3c0f1
a0bbcd2
f54492b
dd12adb
2211d0a
ca19b05
8b0d38a
cc51e94
7605737
704ff5c
767cf0f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,26 +61,38 @@ class RateLimiter extends ActionFilter | |
*/ | ||
public $response; | ||
|
||
/** | ||
* {inheritdoc} | ||
*/ | ||
public function init() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. phpdoc missing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I corrected |
||
{ | ||
if (!$this->request) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What are these initializations for? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://github.com/yiisoft/yii2/blob/master/framework/filters/RateLimiter.php#L75 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. test for |
||
$this->request = Yii::$app->getRequest(); | ||
} | ||
|
||
if (!$this->response) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. test for |
||
$this->response = Yii::$app->getResponse(); | ||
} | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
public function beforeAction($action) | ||
{ | ||
$user = $this->user ? : (Yii::$app->getUser() ? Yii::$app->getUser()->getIdentity(false) : null); | ||
if ($user instanceof RateLimitInterface) { | ||
if ($this->user === null && Yii::$app->getUser()) { | ||
$this->user = Yii::$app->getUser()->getIdentity(false); | ||
} | ||
|
||
if ($this->user instanceof RateLimitInterface) { | ||
Yii::trace('Check rate limit', __METHOD__); | ||
$this->checkRateLimit( | ||
$user, | ||
$this->request ? : Yii::$app->getRequest(), | ||
$this->response ? : Yii::$app->getResponse(), | ||
$action | ||
); | ||
} elseif ($user) { | ||
$this->checkRateLimit($this->user, $this->request, $this->response, $action); | ||
} elseif ($this->user) { | ||
Yii::info('Rate limit skipped: "user" does not implement RateLimitInterface.', __METHOD__); | ||
} else { | ||
Yii::info('Rate limit skipped: user not logged in.', __METHOD__); | ||
} | ||
|
||
return true; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
<?php | ||
|
||
namespace yiiunit\framework\filters; | ||
|
||
use Yii; | ||
use yiiunit\TestCase; | ||
use Prophecy\Argument; | ||
use yiiunit\framework\filters\stubs\RateLimit; | ||
use yii\web\User; | ||
use yii\web\Request; | ||
use yii\web\Response; | ||
use yii\log\Logger; | ||
use yii\filters\RateLimiter; | ||
|
||
/** | ||
* @group filters | ||
*/ | ||
class RateLimiterTest extends TestCase | ||
{ | ||
protected function setUp() | ||
{ | ||
parent::setUp(); | ||
|
||
/* @var $logger Logger|\Prophecy\ObjectProphecy */ | ||
$logger = $this->prophesize(Logger::className()); | ||
$logger | ||
->log(Argument::any(), Argument::any(), Argument::any()) | ||
->will(function ($parameters, $logger) { | ||
$logger->messages = $parameters; | ||
}); | ||
|
||
Yii::setLogger($logger->reveal()); | ||
|
||
$this->mockWebApplication(); | ||
} | ||
protected function tearDown() | ||
{ | ||
parent::tearDown(); | ||
Yii::setLogger(null); | ||
} | ||
|
||
public function testInitFilledRequest() | ||
{ | ||
$rateLimiter = new RateLimiter(['request' => 'Request']); | ||
|
||
$this->assertEquals('Request', $rateLimiter->request); | ||
} | ||
|
||
public function testInitNotFilledRequest() | ||
{ | ||
$rateLimiter = new RateLimiter(); | ||
|
||
$this->assertInstanceOf(Request::className(), $rateLimiter->request); | ||
} | ||
|
||
public function testInitFilledResponse() | ||
{ | ||
$rateLimiter = new RateLimiter(['response' => 'Response']); | ||
|
||
$this->assertEquals('Response', $rateLimiter->response); | ||
} | ||
|
||
public function testInitNotFilledResponse() | ||
{ | ||
$rateLimiter = new RateLimiter(); | ||
|
||
$this->assertInstanceOf(Response::className(), $rateLimiter->response); | ||
} | ||
|
||
public function testBeforeActionUserInstanceOfRateLimitInterface() | ||
{ | ||
$rateLimiter = new RateLimiter(); | ||
$rateLimit = new RateLimit(); | ||
$rateLimit->setAllowance([1, time()]) | ||
->setRateLimit([1, 1]); | ||
$rateLimiter->user = $rateLimit; | ||
|
||
$result = $rateLimiter->beforeAction('test'); | ||
|
||
$this->assertContains('Check rate limit', Yii::getLogger()->messages); | ||
$this->assertTrue($result); | ||
} | ||
|
||
public function testBeforeActionUserNotInstanceOfRateLimitInterface() | ||
{ | ||
$rateLimiter = new RateLimiter(['user' => 'User']); | ||
|
||
$result = $rateLimiter->beforeAction('test'); | ||
|
||
$this->assertContains('Rate limit skipped: "user" does not implement RateLimitInterface.', Yii::getLogger()->messages); | ||
$this->assertTrue($result); | ||
} | ||
|
||
public function testBeforeActionEmptyUser() | ||
{ | ||
$user = new User(['identityClass' => RateLimit::className()]); | ||
Yii::$app->set('user', $user); | ||
$rateLimiter = new RateLimiter(); | ||
|
||
$result = $rateLimiter->beforeAction('test'); | ||
|
||
$this->assertContains('Rate limit skipped: user not logged in.', Yii::getLogger()->messages); | ||
$this->assertTrue($result); | ||
} | ||
|
||
public function testCheckRateLimitTooManyRequests() | ||
{ | ||
/* @var $rateLimit UserIdentity|\Prophecy\ObjectProphecy */ | ||
$rateLimit = new RateLimit; | ||
$rateLimit | ||
->setRateLimit([1, 1]) | ||
->setAllowance([1, time() + 2]); | ||
$rateLimiter = new RateLimiter(); | ||
|
||
$this->setExpectedException('yii\web\TooManyRequestsHttpException'); | ||
$rateLimiter->checkRateLimit($rateLimit, Yii::$app->request, Yii::$app->response, 'testAction'); | ||
} | ||
|
||
public function testCheckRateaddRateLimitHeaders() | ||
{ | ||
/* @var $user UserIdentity|\Prophecy\ObjectProphecy */ | ||
$rateLimit = new RateLimit; | ||
$rateLimit | ||
->setRateLimit([1, 1]) | ||
->setAllowance([1, time()]); | ||
$rateLimiter = $this->getMockBuilder(RateLimiter::className()) | ||
->setMethods(['addRateLimitHeaders']) | ||
->getMock(); | ||
$rateLimiter->expects(self::at(0)) | ||
->method('addRateLimitHeaders') | ||
->willReturn(null); | ||
|
||
$rateLimiter->checkRateLimit($rateLimit, Yii::$app->request, Yii::$app->response, 'testAction'); | ||
} | ||
|
||
public function testAddRateLimitHeadersDisabledRateLimitHeaders() | ||
{ | ||
$rateLimiter = new RateLimiter(); | ||
$rateLimiter->enableRateLimitHeaders = false; | ||
$response = Yii::$app->response; | ||
|
||
$rateLimiter->addRateLimitHeaders($response, 1, 0, 0); | ||
$this->assertCount(0, $response->getHeaders()); | ||
} | ||
|
||
public function testAddRateLimitHeadersEnabledRateLimitHeaders() | ||
{ | ||
$rateLimiter = new RateLimiter(); | ||
$rateLimiter->enableRateLimitHeaders = true; | ||
$response = Yii::$app->response; | ||
|
||
$rateLimiter->addRateLimitHeaders($response, 1, 0, 0); | ||
$this->assertCount(3, $response->getHeaders()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
<?php | ||
|
||
namespace yiiunit\framework\filters\stubs; | ||
|
||
use yii\base\Object; | ||
use yii\filters\RateLimitInterface; | ||
|
||
class RateLimit extends Object implements RateLimitInterface | ||
{ | ||
private $_rateLimit; | ||
|
||
private $_allowance; | ||
|
||
public function getRateLimit($request, $action) | ||
{ | ||
return $this->_rateLimit; | ||
} | ||
|
||
public function setRateLimit($rateLimit) | ||
{ | ||
$this->_rateLimit = $rateLimit; | ||
|
||
return $this; | ||
} | ||
|
||
public function loadAllowance($request, $action) | ||
{ | ||
return $this->_allowance; | ||
} | ||
|
||
public function setAllowance($allowance) | ||
{ | ||
$this->_allowance = $allowance; | ||
|
||
return $this; | ||
} | ||
|
||
|
||
public function saveAllowance($request, $action, $allowance, $timestamp) | ||
{ | ||
return [$action, $allowance, $timestamp]; | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Enh #13994: Refactored yii\filters\RateLimiter. Added tests (vladis84)