diff --git a/Configuration/Testing/Settings.yaml b/Configuration/Testing/Settings.yaml index 07b1b05..31d150d 100644 --- a/Configuration/Testing/Settings.yaml +++ b/Configuration/Testing/Settings.yaml @@ -1,3 +1,5 @@ DigiComp: FlowSessionLock: lockStoreConnection: "flock://%FLOW_PATH_DATA%Temporary/Testing/SessionLocks/" + readOnlyExpressions: + TestUnprotected: "method(DigiComp\\FlowSessionLock\\Tests\\Functional\\Fixtures\\Controller\\ExampleController->unprotectedByConfigurationAction())" diff --git a/Tests/Functional/Fixtures/Controller/ExampleController.php b/Tests/Functional/Fixtures/Controller/ExampleController.php new file mode 100644 index 0000000..a5ecab9 --- /dev/null +++ b/Tests/Functional/Fixtures/Controller/ExampleController.php @@ -0,0 +1,43 @@ +serverRequestFactory = $this->objectManager->get(ServerRequestFactoryInterface::class); + $route = new Route(); + $route->setName('Functional Test - SessionRequestComponent::Restricted'); + $route->setUriPattern('test/sessionlock/{@action}'); + $route->setDefaults([ + '@package' => 'DigiComp.FlowSessionLock', + '@subpackage' => 'Tests\Functional\Fixtures', + '@controller' => 'Example', + '@action' => 'protected', + '@format' => 'html', + ]); + $route->setAppendExceedingArguments(true); + $this->router->addRoute($route); + } + + public function expectedDuration(): array + { + $parallelChecker = function ($allRequests, $oneRequest) { + self::assertGreaterThan(ExampleController::CONTROLLER_TIME, $oneRequest * 1000); + self::assertLessThan(ExampleController::CONTROLLER_TIME * 4, $allRequests * 1000); + }; + return [ + [ + 'http://localhost/test/sessionlock/protected', + function ($allRequests, $oneRequest) { + self::assertGreaterThan(ExampleController::CONTROLLER_TIME, $oneRequest * 1000); + self::assertGreaterThan(ExampleController::CONTROLLER_TIME * 4, $allRequests * 1000); + } + ], + [ + 'http://localhost/test/sessionlock/unprotectedbyannotation', + $parallelChecker + ], + [ + 'http://localhost/test/sessionlock/unprotectedbyconfiguration', + $parallelChecker + ] + ]; + } + + /** + * @dataProvider expectedDuration + * @test + */ + public function itDoesNotAllowToEnterMoreThanOneWithTheSameSession(string $url, \Closure $checker): void + { + $request = $this->serverRequestFactory + ->createServerRequest('GET', new Uri($url)); + $start = microtime(true); + $response = $this->browser->sendRequest($request); + $neededForOne = microtime(true) - $start; + + $sessionCookies = array_map(static function ($cookie) { + return Cookie::createFromRawSetCookieHeader($cookie); + }, $response->getHeader('Set-Cookie')); + self::assertNotEmpty($sessionCookies); + + $cookies = array_reduce($sessionCookies, static function ($out, $cookie) { + $out[$cookie->getName()] = $cookie->getValue(); + return $out; + }, []); + $nextRequest = $this->serverRequestFactory + ->createServerRequest('GET', new Uri($url)) + ->withCookieParams($cookies); + $childs = []; + $start = microtime(true); + for ($i = 0; $i < 4; $i++) { + $child = \pcntl_fork(); + if ($child === 0) { + $this->browser->sendRequest($nextRequest); + exit(); + } + $childs[] = $child; + } + foreach ($childs as $child) { + \pcntl_waitpid($child, $status); + } + $neededForAll = microtime(true) - $start; + + $checker($neededForAll, $neededForOne); + } +} diff --git a/composer.json b/composer.json index 567cb4a..e707dfc 100644 --- a/composer.json +++ b/composer.json @@ -7,6 +7,9 @@ "php": ">=7.4", "symfony/lock": "^5.2.0" }, + "require-dev": { + "ext-pcntl": "*" + }, "autoload": { "psr-4": { "DigiComp\\FlowSessionLock\\": "Classes/"