razonyang /
psr-rate-limiter
| 1 | <?php |
||
| 2 | namespace RazonYang\Psr\RateLimiter\Tests; |
||
| 3 | |||
| 4 | use Nyholm\Psr7\Factory\Psr17Factory; |
||
| 5 | use PHPUnit\Framework\TestCase; |
||
| 6 | use Psr\Http\Message\ResponseFactoryInterface; |
||
| 7 | use Psr\Http\Message\ResponseInterface; |
||
| 8 | use Psr\Http\Message\ServerRequestInterface; |
||
| 9 | use Psr\Http\Server\RequestHandlerInterface; |
||
| 10 | use Psr\Log\NullLogger; |
||
| 11 | use RazonYang\Psr\RateLimiter\Middleware; |
||
| 12 | use RazonYang\TokenBucket\Manager\MemcachedManager; |
||
| 13 | use RazonYang\TokenBucket\ManagerInterface; |
||
| 14 | |||
| 15 | class MiddlewareTest extends TestCase |
||
| 16 | { |
||
| 17 | private $httpFactory; |
||
| 18 | |||
| 19 | public function setUp(): void |
||
| 20 | { |
||
| 21 | parent::setUp(); |
||
| 22 | |||
| 23 | $this->httpFactory = new Psr17Factory(); |
||
| 24 | } |
||
| 25 | |||
| 26 | public function tearDown(): void |
||
| 27 | { |
||
| 28 | $this->httpFactory = null; |
||
| 29 | |||
| 30 | parent::tearDown(); |
||
| 31 | } |
||
| 32 | |||
| 33 | private function createManager(int $capacity, float $rate): ManagerInterface |
||
| 34 | { |
||
| 35 | $memcached = new \Memcached(); |
||
| 36 | $memcached->addServer('localhost', 11211); |
||
| 37 | $manager = new MemcachedManager($capacity, $rate, new NullLogger(), $memcached); |
||
| 38 | return $manager; |
||
| 39 | } |
||
| 40 | |||
| 41 | private function createNameCallback(string $name): \Closure |
||
| 42 | { |
||
| 43 | return function (ServerRequestInterface $request) use ($name): string { |
||
|
0 ignored issues
–
show
|
|||
| 44 | return $name; |
||
| 45 | }; |
||
| 46 | } |
||
| 47 | |||
| 48 | private function createRateLimiter(int $capacity, float $rate, \Closure $nameCallback): Middleware |
||
| 49 | { |
||
| 50 | return new Middleware($this->createManager($capacity, $rate), $this->httpFactory, $nameCallback); |
||
| 51 | } |
||
| 52 | |||
| 53 | /** |
||
| 54 | * @dataProvider dataLimitPeriod |
||
| 55 | */ |
||
| 56 | public function testSetLimitPeriod(int $period): void |
||
| 57 | { |
||
| 58 | $limiter = $this->createRateLimiter(1, 1, $this->createNameCallback('')); |
||
| 59 | $limiter->setLimitPeriod($period); |
||
| 60 | $property = new \ReflectionProperty(Middleware::class, 'limitPeriod'); |
||
| 61 | $property->setAccessible(true); |
||
| 62 | $this->assertSame($period, $property->getValue($limiter)); |
||
| 63 | } |
||
| 64 | |||
| 65 | public function dataLimitPeriod(): array |
||
| 66 | { |
||
| 67 | return [ |
||
| 68 | [60], |
||
| 69 | [3600], |
||
| 70 | ]; |
||
| 71 | } |
||
| 72 | |||
| 73 | public function testGetResponseFactory(): void |
||
| 74 | { |
||
| 75 | $limiter = $this->createRateLimiter(1, 1, $this->createNameCallback('')); |
||
| 76 | $method = new \ReflectionMethod(Middleware::class, 'getResponseFactory'); |
||
| 77 | $method->setAccessible(true); |
||
| 78 | $this->assertSame($this->httpFactory, $method->invoke($limiter)); |
||
| 79 | } |
||
| 80 | |||
| 81 | public function testProcess(): void |
||
| 82 | { |
||
| 83 | $name = uniqid(); |
||
| 84 | $limiter = $this->createRateLimiter(1, 60, $this->createNameCallback($name)); |
||
| 85 | |||
| 86 | $request = $this->httpFactory->createServerRequest('GET', '/'); |
||
| 87 | |||
| 88 | $handler = $this->createHandler(); |
||
| 89 | $response = $limiter->process($request, $handler); |
||
| 90 | $this->assertTrue($handler->isHandled()); |
||
| 91 | $this->assertSame(200, $response->getStatusCode()); |
||
| 92 | $headers = ['X-Rate-Limit-Limit', 'X-Rate-Limit-Remaining', 'X-Rate-Limit-Reset']; |
||
| 93 | foreach ($headers as $header) { |
||
| 94 | $this->assertTrue($response->hasHeader($header)); |
||
| 95 | } |
||
| 96 | |||
| 97 | $handler2 = $this->createHandler(); |
||
| 98 | $response = $limiter->process($request, $handler2); |
||
| 99 | $this->assertFalse($handler2->isHandled()); |
||
| 100 | $this->assertSame(429, $response->getStatusCode()); |
||
| 101 | foreach ($headers as $header) { |
||
| 102 | $this->assertTrue($response->hasHeader($header)); |
||
| 103 | } |
||
| 104 | } |
||
| 105 | |||
| 106 | public function testProcessSkip(): void |
||
| 107 | { |
||
| 108 | $name = ''; |
||
| 109 | $limiter = $this->createRateLimiter(1, 60, $this->createNameCallback($name)); |
||
| 110 | |||
| 111 | $request = $this->httpFactory->createServerRequest('GET', 'http://localhost'); |
||
| 112 | |||
| 113 | $handler = $this->createHandler(); |
||
| 114 | $response = $limiter->process($request, $handler); |
||
| 115 | $this->assertTrue($handler->isHandled()); |
||
| 116 | $this->assertSame(200, $response->getStatusCode()); |
||
| 117 | $headers = ['X-Rate-Limit-Limit', 'X-Rate-Limit-Remaining', 'X-Rate-Limit-Reset']; |
||
| 118 | foreach ($headers as $header) { |
||
| 119 | $this->assertFalse($response->hasHeader($header)); |
||
| 120 | } |
||
| 121 | } |
||
| 122 | |||
| 123 | private function createHandler() |
||
| 124 | { |
||
| 125 | return new class($this->httpFactory) implements RequestHandlerInterface { |
||
| 126 | /** |
||
| 127 | * @var ResponseFactoryInterface $factory |
||
| 128 | */ |
||
| 129 | private $factory; |
||
| 130 | |||
| 131 | private $handled = false; |
||
| 132 | |||
| 133 | public function isHandled(): bool |
||
| 134 | { |
||
| 135 | return $this->handled === true; |
||
| 136 | } |
||
| 137 | |||
| 138 | public function __construct($factory) |
||
| 139 | { |
||
| 140 | $this->factory = $factory; |
||
| 141 | } |
||
| 142 | |||
| 143 | public function handle(ServerRequestInterface $request): ResponseInterface |
||
| 144 | { |
||
| 145 | $this->handled = true; |
||
| 146 | return $this->factory->createResponse(200); |
||
| 147 | } |
||
| 148 | }; |
||
| 149 | } |
||
| 150 | } |
||
| 151 |
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.