From 6382ca950b9719a7b5f06aa3b82f170928f0e360 Mon Sep 17 00:00:00 2001 From: Leandro Silva Date: Mon, 15 Jul 2019 13:38:09 -0400 Subject: [PATCH] add tests --- .coveralls.yml | 2 + .phpunit.result.cache | 1 + .travis.yml | 70 +++++++++---- composer.json | 8 +- phpunit.xml | 26 ++--- test/RateLimitTest.php | 219 ++++++++++++++++++++--------------------- 6 files changed, 177 insertions(+), 149 deletions(-) create mode 100644 .coveralls.yml create mode 100644 .phpunit.result.cache diff --git a/.coveralls.yml b/.coveralls.yml new file mode 100644 index 0000000..bc71b62 --- /dev/null +++ b/.coveralls.yml @@ -0,0 +1,2 @@ +coverage_clover: clover.xml +json_path: coveralls-upload.json diff --git a/.phpunit.result.cache b/.phpunit.result.cache new file mode 100644 index 0000000..c55a0c1 --- /dev/null +++ b/.phpunit.result.cache @@ -0,0 +1 @@ +C:37:"PHPUnit\Runner\DefaultTestResultCache":937:{a:2:{s:7:"defects";a:6:{s:61:"LosMiddlewareTest\RateLimit\RateLimitTest::testNeedIpOuApiKey";i:4;s:66:"LosMiddlewareTest\RateLimit\RateLimitTest::testAddHeadersForApiKey";i:4;s:62:"LosMiddlewareTest\RateLimit\RateLimitTest::testAddHeadersForIp";i:4;s:64:"LosMiddlewareTest\RateLimit\RateLimitTest::testDecreaseRemaining";i:4;s:58:"LosMiddlewareTest\RateLimit\RateLimitTest::testGenerate429";i:3;s:52:"LosMiddlewareTest\RateLimit\RateLimitTest::testReset";i:3;}s:5:"times";a:6:{s:61:"LosMiddlewareTest\RateLimit\RateLimitTest::testNeedIpOuApiKey";d:0.039;s:66:"LosMiddlewareTest\RateLimit\RateLimitTest::testAddHeadersForApiKey";d:0.018;s:62:"LosMiddlewareTest\RateLimit\RateLimitTest::testAddHeadersForIp";d:0.017;s:64:"LosMiddlewareTest\RateLimit\RateLimitTest::testDecreaseRemaining";d:0.018;s:58:"LosMiddlewareTest\RateLimit\RateLimitTest::testGenerate429";d:0.022;s:52:"LosMiddlewareTest\RateLimit\RateLimitTest::testReset";d:0.02;}}} \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 3736088..b015576 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,31 +1,65 @@ +sudo: false + language: php cache: directories: - $HOME/.composer/cache - -before_install: - - composer self-update - + +env: + global: + - COMPOSER_ARGS="--no-interaction" + - COVERAGE_DEPS="php-coveralls/php-coveralls" + matrix: - fast_finish: true include: - - php: 5.5 - - php: 5.6 + - php: 7.1 + env: + - DEPS=lowest + - php: 7.1 + env: + - DEPS=locked + - php: 7.1 + env: + - DEPS=latest + - php: 7.2 + env: + - DEPS=lowest + - php: 7.2 env: - - EXECUTE_CS_CHECK=true - - php: 7 - - php: hhvm - allow_failures: - - php: hhvm + - DEPS=locked + - CS_CHECK=true + - TEST_COVERAGE=true + - php: 7.2 + env: + - DEPS=latest + - php: 7.3 + env: + - DEPS=lowest + - php: 7.3 + env: + - DEPS=locked + - php: 7.3 + env: + - DEPS=latest + +before_install: + - if [[ $TEST_COVERAGE != 'true' ]]; then phpenv config-rm xdebug.ini || return 0 ; fi install: - - travis_retry composer install --no-interaction --ignore-platform-reqs --prefer-source - - composer info -i + - travis_retry composer install $COMPOSER_ARGS --ignore-platform-reqs + - if [[ $LEGACY_DEPS != '' ]]; then travis_retry composer update --with-dependencies $COMPOSER_ARGS $LEGACY_DEPS ; fi + - if [[ $DEPS == 'lowest' ]]; then travis_retry composer update --prefer-lowest --prefer-stable $COMPOSER_ARGS ; fi + - if [[ $DEPS == 'latest' ]]; then travis_retry composer update $COMPOSER_ARGS ; fi + - if [[ $TEST_COVERAGE == 'true' ]]; then travis_retry composer require --dev $COMPOSER_ARGS $COVERAGE_DEPS ; fi + - stty cols 120 && composer show script: - - (phpunit -c phpunit.xml) - - if [[ $EXECUTE_CS_CHECK == 'true' ]]; then ./vendor/bin/phpcs ; fi - + - if [[ $TEST_COVERAGE == 'true' ]]; then composer test-coverage ; else composer test ; fi + - if [[ $CS_CHECK == 'true' ]]; then composer cs-check ; fi + after_script: - - php vendor/bin/coveralls -v + - if [[ $TEST_COVERAGE == 'true' ]]; then composer upload-coverage ; fi + +notifications: + email: false diff --git a/composer.json b/composer.json index 137855f..11cb718 100644 --- a/composer.json +++ b/composer.json @@ -30,6 +30,9 @@ } }, "type" : "library", + "config": { + "sort-packages": true + }, "support" : { "email" : "leandro@leandrosilva.info", "source" : "https://github.com/LansoWeb/LosRateLimit", @@ -47,6 +50,9 @@ ], "cs-check": "phpcs", "cs-fix": "phpcbf", - "phpstan": "phpstan analyse -l max -c phpstan.neon src" + "phpstan": "phpstan analyse -l max -c phpstan.neon src", + "test": "phpunit --colors=always", + "test-coverage": "phpunit --colors=always --coverage-clover clover.xml", + "upload-coverage": "php-coveralls -v" } } diff --git a/phpunit.xml b/phpunit.xml index 989cac8..c11884a 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,38 +1,30 @@ - - - - ./test + + ./test - ./src - - - - - - - + + - - - - + + + + diff --git a/test/RateLimitTest.php b/test/RateLimitTest.php index 14e7bed..d15381b 100644 --- a/test/RateLimitTest.php +++ b/test/RateLimitTest.php @@ -2,183 +2,176 @@ namespace LosMiddlewareTest\RateLimit; +use LosMiddleware\RateLimit\Exception\MissingRequirement; use LosMiddleware\RateLimit\RateLimitMiddleware; +use LosMiddleware\RateLimit\RateLimitOptions; use PHPUnit\Framework\TestCase; -use Zend\Diactoros\Response; +use Psr\Http\Server\RequestHandlerInterface; +use Psr\SimpleCache\CacheInterface; +use Zend\Diactoros\Response\JsonResponse; use Zend\Diactoros\ServerRequest; -use Zend\ServiceManager\ServiceManager; -use Zend\Session\Container; -use LosMiddleware\RateLimit\Storage\ArrayStorage; -use LosMiddleware\RateLimit\Exception\MissingRequirement; +use Zend\ProblemDetails\ProblemDetailsResponseFactory; class RateLimitTest extends TestCase { - protected $middleware; + /** @var array */ + private $cache = []; + /** @var RateLimitMiddleware */ + private $middleware; - protected function setUp() + protected function setUp() : void { - $container = new ServiceManager([]); - $container->setService('config', [ - 'los_rate_limit' => [ - 'max_requests' => 2, - 'reset_time' => 10, - 'ip_max_requests' => 2, - 'ip_reset_time' => 10, - 'api_header' => 'X-Api-Key', - 'trust_forwarded' => true, - 'prefer_forwarded' => false, - 'forwarded_headers_allowed' => [ - 'Client-Ip', - 'Forwarded', - 'Forwarded-For', - 'X-Cluster-Client-Ip', - 'X-Forwarded', - 'X-Forwarded-For', - ], - 'forwarded_ip_index' => null, + $options = new RateLimitOptions([ + 'max_requests' => 2, + 'reset_time' => 10, + 'ip_max_requests' => 2, + 'ip_reset_time' => 10, + 'api_header' => 'X-Api-Key', + 'trust_forwarded' => true, + 'prefer_forwarded' => false, + 'forwarded_headers_allowed' => [ + 'Client-Ip', + 'Forwarded', + 'Forwarded-For', + 'X-Cluster-Client-Ip', + 'X-Forwarded', + 'X-Forwarded-For', ], + 'forwarded_ip_index' => null, ]); - //$factory = new RateLimitFactory(); - //$this->middleware = $factory($container); - $config = $container->get('config'); - $rateConfig = array_key_exists('los_rate_limit', $config) ? $config['los_rate_limit'] : []; - $this->middleware = new RateLimitMiddleware(new ArrayStorage(), $rateConfig); + + $problemResponse = $this->createMock(ProblemDetailsResponseFactory::class); + $problemResponse->method('createResponseFromThrowable')->willReturn(new JsonResponse([], 429)); + + $storage = $this->createMock(CacheInterface::class); + $storage->method('get')->will($this->returnCallback([$this, 'getCache'])); + $storage->method('set')->will($this->returnCallback([$this, 'setCache'])); + + $this->middleware = new RateLimitMiddleware($storage, $problemResponse, $options); } - /** - * @covers LosMiddleware\RateLimit\RateLimit::__construct - * @covers LosMiddleware\RateLimit\RateLimit::__invoke - */ public function testNeedIpOuApiKey() { $request = new ServerRequest(); - $response = new Response(); - $outFunction = function ($request, $response) { - return $response; - }; + $handler = $this->createMock(RequestHandlerInterface::class); + $handler->method('handle')->willReturn(new JsonResponse([])); - $this->setExpectedException(MissingRequirement::class); - call_user_func($this->middleware, $request, $response, $outFunction); + $this->expectException(MissingRequirement::class); + $this->middleware->process($request, $handler); } - /** - * @covers LosMiddleware\RateLimit\RateLimit::__invoke - */ public function testAddHeadersForApiKey() { $request = new ServerRequest(); $request = $request->withHeader('X-Api-Key', '123'); - $response = new Response(); - - $outFunction = function ($request, $response) { - return $response; - }; + $response = new JsonResponse([]); - $result = call_user_func($this->middleware, $request, $response, $outFunction); + $handler = $this->createMock(RequestHandlerInterface::class); + $handler->method('handle')->willReturn(new JsonResponse([])); + $result = $this->middleware->process($request, $handler); $this->assertNotSame($response, $result); - $this->assertArrayHasKey(RateLimit::HEADER_REMAINING, $result->getHeaders()); - $this->assertArrayHasKey(RateLimit::HEADER_LIMIT, $result->getHeaders()); - $this->assertArrayHasKey(RateLimit::HEADER_RESET, $result->getHeaders()); + $this->assertArrayHasKey(RateLimitMiddleware::HEADER_REMAINING, $result->getHeaders()); + $this->assertArrayHasKey(RateLimitMiddleware::HEADER_LIMIT, $result->getHeaders()); + $this->assertArrayHasKey(RateLimitMiddleware::HEADER_RESET, $result->getHeaders()); - $this->assertEquals(2, $result->getHeader(RateLimit::HEADER_REMAINING)[0]); - $this->assertLessThanOrEqual(10, $result->getHeader(RateLimit::HEADER_RESET)[0]); - $this->assertEquals(2, $result->getHeader(RateLimit::HEADER_LIMIT)[0]); + $this->assertEquals(2, $result->getHeader(RateLimitMiddleware::HEADER_REMAINING)[0]); + $this->assertLessThanOrEqual(10, $result->getHeader(RateLimitMiddleware::HEADER_RESET)[0]); + $this->assertEquals(2, $result->getHeader(RateLimitMiddleware::HEADER_LIMIT)[0]); } - /** - * @covers LosMiddleware\RateLimit\RateLimit::__invoke - * @covers LosMiddleware\RateLimit\RateLimit::getClientIp - */ public function testAddHeadersForIp() { $request = new ServerRequest(['REMOTE_ADDR' => '127.0.0.1']); $request = $request->withHeader('X-Forwarded-For', '192.168.1.1'); - $response = new Response(); + $response = new JsonResponse([]); - $outFunction = function ($request, $response) { - return $response; - }; - - $result = call_user_func($this->middleware, $request, $response, $outFunction); + $handler = $this->createMock(RequestHandlerInterface::class); + $handler->method('handle')->willReturn(new JsonResponse([])); + $result = $this->middleware->process($request, $handler); $this->assertNotSame($response, $result); - $this->assertArrayHasKey(RateLimit::HEADER_REMAINING, $result->getHeaders()); - $this->assertArrayHasKey(RateLimit::HEADER_LIMIT, $result->getHeaders()); - $this->assertArrayHasKey(RateLimit::HEADER_RESET, $result->getHeaders()); + $this->assertArrayHasKey(RateLimitMiddleware::HEADER_REMAINING, $result->getHeaders()); + $this->assertArrayHasKey(RateLimitMiddleware::HEADER_LIMIT, $result->getHeaders()); + $this->assertArrayHasKey(RateLimitMiddleware::HEADER_RESET, $result->getHeaders()); - $this->assertEquals(2, $result->getHeader(RateLimit::HEADER_REMAINING)[0]); - $this->assertLessThanOrEqual(10, $result->getHeader(RateLimit::HEADER_RESET)[0]); - $this->assertEquals(2, $result->getHeader(RateLimit::HEADER_LIMIT)[0]); + $this->assertEquals(2, $result->getHeader(RateLimitMiddleware::HEADER_REMAINING)[0]); + $this->assertLessThanOrEqual(10, $result->getHeader(RateLimitMiddleware::HEADER_RESET)[0]); + $this->assertEquals(2, $result->getHeader(RateLimitMiddleware::HEADER_LIMIT)[0]); } - /** - * @covers LosMiddleware\RateLimit\RateLimit::__invoke - */ public function testDecreaseRemaining() { $request = new ServerRequest(); $request = $request->withHeader('X-Api-Key', '123'); - $response = new Response(); - $outFunction = function ($request, $response) { - return $response; - }; + $handler = $this->createMock(RequestHandlerInterface::class); + $handler->method('handle')->willReturn(new JsonResponse([])); + $this->middleware->process($request, $handler); + $result = $this->middleware->process($request, $handler); - $result = call_user_func($this->middleware, $request, $response, $outFunction); - $result = call_user_func($this->middleware, $request, $response, $outFunction); - - $this->assertEquals(1, $result->getHeader(RateLimit::HEADER_REMAINING)[0]); - $this->assertLessThanOrEqual(10, $result->getHeader(RateLimit::HEADER_RESET)[0]); - $this->assertEquals(2, $result->getHeader(RateLimit::HEADER_LIMIT)[0]); + $this->assertEquals(1, $result->getHeader(RateLimitMiddleware::HEADER_REMAINING)[0]); + $this->assertLessThanOrEqual(10, $result->getHeader(RateLimitMiddleware::HEADER_RESET)[0]); + $this->assertEquals(2, $result->getHeader(RateLimitMiddleware::HEADER_LIMIT)[0]); } - /** - * @covers LosMiddleware\RateLimit\RateLimit::__invoke - */ public function testGenerate429() { $request = new ServerRequest(); $request = $request->withHeader('X-Api-Key', '123'); - $response = new Response(); - $outFunction = function ($request, $response) { - return $response; - }; + $handler = $this->createMock(RequestHandlerInterface::class); + $handler->method('handle')->willReturn(new JsonResponse([])); - $result = call_user_func($this->middleware, $request, $response, $outFunction); - $result = call_user_func($this->middleware, $request, $response, $outFunction); - $result = call_user_func($this->middleware, $request, $response, $outFunction); - $result = call_user_func($this->middleware, $request, $response, $outFunction); + $this->middleware->process($request, $handler); + $this->middleware->process($request, $handler); + $this->middleware->process($request, $handler); + $result = $this->middleware->process($request, $handler); - $this->assertArrayNotHasKey(RateLimit::HEADER_REMAINING, $result->getHeaders()); - $this->assertArrayNotHasKey(RateLimit::HEADER_LIMIT, $result->getHeaders()); - $this->assertLessThanOrEqual(10, $result->getHeader(RateLimit::HEADER_RESET)[0]); + $this->assertArrayNotHasKey(RateLimitMiddleware::HEADER_REMAINING, $result->getHeaders()); + $this->assertArrayHasKey(RateLimitMiddleware::HEADER_LIMIT, $result->getHeaders()); + $this->assertLessThanOrEqual(10, $result->getHeader(RateLimitMiddleware::HEADER_RESET)[0]); $this->assertSame(429, $result->getStatusCode()); } - /** - * @covers LosMiddleware\RateLimit\RateLimit::__invoke - */ public function testReset() { - $container = new Container('LosRateLimit'); - $container->offsetSet('remaining', 0); - $container->offsetSet('created', strtotime('-20 second')); +// $container = new Container('LosRateLimit'); +// $container->offsetSet('remaining', 0); +// $container->offsetSet('created', strtotime('-20 second')); $request = new ServerRequest(); $request = $request->withHeader('X-Api-Key', '123'); - $response = new Response(); - $outFunction = function ($request, $response) { - return $response; - }; + $handler = $this->createMock(RequestHandlerInterface::class); + $handler->method('handle')->willReturn(new JsonResponse([])); + $this->middleware->process($request, $handler); + $this->middleware->process($request, $handler); + + $this->cache['123']['created'] = strtotime('-20 second'); + $result = $this->middleware->process($request, $handler); - $result = call_user_func($this->middleware, $request, $response, $outFunction); + $this->assertArrayHasKey(RateLimitMiddleware::HEADER_REMAINING, $result->getHeaders()); + $this->assertArrayHasKey(RateLimitMiddleware::HEADER_LIMIT, $result->getHeaders()); + $this->assertLessThanOrEqual(10, $result->getHeader(RateLimitMiddleware::HEADER_RESET)[0]); + $this->assertSame(200, $result->getStatusCode()); + } - $this->assertArrayHasKey(RateLimit::HEADER_REMAINING, $result->getHeaders()); - $this->assertArrayHasKey(RateLimit::HEADER_LIMIT, $result->getHeaders()); - $this->assertLessThanOrEqual(10, $result->getHeader(RateLimit::HEADER_RESET)[0]); + /** + * @param null|mixed $default + * @return null|mixed + */ + public function getCache(string $key, $default = null) + { + return $this->cache[$key] ?? $default; + } + + /** + * @param mixed $value + */ + public function setCache(string $key, $value) : void + { + $this->cache[$key] = $value; } }