From 919cb741b9844d7d6bb69d73ea0c2ebaedee6bc6 Mon Sep 17 00:00:00 2001 From: nicumicle <20170987+nicumicleI@users.noreply.github.com> Date: Sun, 10 Mar 2024 09:19:09 +0100 Subject: [PATCH 1/5] #102: Fix status codes for expired tokens --- Changelog.md | 1 + simple-jwt-login/routes/api.php | 18 ++++++----- .../src/Helpers/StatusCodeHelper.php | 30 +++++++++++++++++++ 3 files changed, 41 insertions(+), 8 deletions(-) create mode 100644 simple-jwt-login/src/Helpers/StatusCodeHelper.php diff --git a/Changelog.md b/Changelog.md index 57fb677..ec93da0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ ## Unreleased - Add OAuth support for Google [#97](https://github.com/nicumicle/simple-jwt-login/issues/97) +- Fix status code for expired tokens [#102](https://github.com/nicumicle/simple-jwt-login/issues/102) ## 3.5.3 (16 November 2023) - Fix licence in composer.json diff --git a/simple-jwt-login/routes/api.php b/simple-jwt-login/routes/api.php index f45aa37..44634fb 100644 --- a/simple-jwt-login/routes/api.php +++ b/simple-jwt-login/routes/api.php @@ -2,6 +2,7 @@ use SimpleJWTLogin\Helpers\CorsHelper; use SimpleJWTLogin\Helpers\ServerHelper; +use SimpleJWTLogin\Helpers\StatusCodeHelper; use SimpleJWTLogin\Libraries\ParseRequest; use SimpleJWTLogin\Modules\SimpleJWTLoginHooks; use SimpleJWTLogin\Services\ProtectEndpointService; @@ -82,15 +83,16 @@ ->loginUser( $routeService->getUserFromJwt($jwt) ); - } catch (\Exception $e) { + } catch (\Exception $exception) { @header('Content-Type: application/json; charset=UTF-8'); + wp_send_json_error( [ - 'message' => $e->getMessage(), - 'errorCode' => $e->getCode(), + 'message' => $exception->getMessage(), + 'errorCode' => $exception->getCode(), 'type' => 'simple-jwt-login-middleware' ], - 400 + StatusCodeHelper::getStatusCodeFromExeption($exception, 400) ); return false; } @@ -178,14 +180,14 @@ $service->withSession($_SESSION); } return $service->makeAction(); - } catch (Exception $e) { + } catch (Exception $exception) { @header('Content-Type: application/json; charset=UTF-8'); wp_send_json_error( [ - 'message' => $e->getMessage(), - 'errorCode' => $e->getCode() + 'message' => $exception->getMessage(), + 'errorCode' => $exception->getCode() ], - 400 + StatusCodeHelper::getStatusCodeFromExeption($exception, 400) ); return false; diff --git a/simple-jwt-login/src/Helpers/StatusCodeHelper.php b/simple-jwt-login/src/Helpers/StatusCodeHelper.php new file mode 100644 index 0000000..0db25b2 --- /dev/null +++ b/simple-jwt-login/src/Helpers/StatusCodeHelper.php @@ -0,0 +1,30 @@ +getCode(), $unauthorizedCode)) { + return 401; + } + + return $defaultStatusCode; + } +} From 182882d9b0d3d5bc8711120e27932cfe1c096da8 Mon Sep 17 00:00:00 2001 From: nicumicle <20170987+nicumicleI@users.noreply.github.com> Date: Sun, 10 Mar 2024 09:22:27 +0100 Subject: [PATCH 2/5] Fix PHP 5.5 issue --- simple-jwt-login/src/Helpers/StatusCodeHelper.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/simple-jwt-login/src/Helpers/StatusCodeHelper.php b/simple-jwt-login/src/Helpers/StatusCodeHelper.php index 0db25b2..a6717cb 100644 --- a/simple-jwt-login/src/Helpers/StatusCodeHelper.php +++ b/simple-jwt-login/src/Helpers/StatusCodeHelper.php @@ -1,7 +1,5 @@ Date: Sun, 10 Mar 2024 10:22:10 +0100 Subject: [PATCH 3/5] Fix Feature tests phpunit annotation errors --- phpunit.xml.dist | 1 + .../AccessEndpoints/EmptyOptionsTest.php | 6 ++- .../AccessEndpoints/RevokedJWTTest.php | 10 ++-- tests/Feature/Authentication/SuccessTest.php | 14 ++---- tests/Feature/Autologin/SuccessTest.php | 2 +- tests/Feature/Autologin/ValidationTest.php | 50 ++++++++++--------- .../ActiveOnAllEndpointsTest.php | 11 ++-- .../ActiveOnSpecificEndpointsTest.php | 11 ++-- .../ProtectEndpoints/NotActiveTest.php | 4 +- tests/Feature/RegisterUsers/SuccessTest.php | 15 ++---- .../Feature/RegisterUsers/ValidationsTest.php | 24 +++++---- tests/Feature/TestBase.php | 8 +-- 12 files changed, 80 insertions(+), 76 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index f3b9032..319abca 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -4,6 +4,7 @@ bootstrap="phpunit_bootstrap.php" backupGlobals="false" colors="true" + testdox="true" processIsolation="true" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.3/phpunit.xsd" diff --git a/tests/Feature/AccessEndpoints/EmptyOptionsTest.php b/tests/Feature/AccessEndpoints/EmptyOptionsTest.php index 5a73043..617ef52 100644 --- a/tests/Feature/AccessEndpoints/EmptyOptionsTest.php +++ b/tests/Feature/AccessEndpoints/EmptyOptionsTest.php @@ -2,8 +2,10 @@ namespace SimpleJwtLoginTests\Feature\AccessEndpoints; +use PHPUnit\Framework\Attributes\TestDox; use SimpleJWTLogin\ErrorCodes; use SimpleJwtLoginTests\Feature\TestBase; +use PHPUnit\Framework\Attributes\DataProvider; class EmptyOptionsTest extends TestBase { @@ -88,9 +90,9 @@ public static function endpointsProvider() ]; } + #[DataProvider('endpointsProvider')] + #[TestDox("Access endpoints is not allowed")] /** - * @testdox Access endpoints is not allowed - * @dataProvider endpointsProvider * @return void * @throws \GuzzleHttp\Exception\GuzzleException */ diff --git a/tests/Feature/AccessEndpoints/RevokedJWTTest.php b/tests/Feature/AccessEndpoints/RevokedJWTTest.php index d297b8a..14236d8 100644 --- a/tests/Feature/AccessEndpoints/RevokedJWTTest.php +++ b/tests/Feature/AccessEndpoints/RevokedJWTTest.php @@ -2,6 +2,8 @@ namespace SimpleJwtLoginTests\Feature\AccessEndpoints; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\TestDox; use SimpleJWTLogin\ErrorCodes; use SimpleJwtLoginTests\Feature\TestBase; @@ -80,9 +82,9 @@ public static function endpointsProvider() ]; } + #[DataProvider('endpointsProvider')] + #[TestDox("Calling endpoints with revoked JWT")] /** - * @testdox Calling endpoints with revoked JWT - * @dataProvider endpointsProvider * @param string $method * @param string $endpoint * @return void @@ -96,7 +98,7 @@ public function testRevokedJWT($method, $endpoint) 'allow_redirects' => [ 'track_redirects' => true, ], - ], + ] ); // Register random user @@ -142,7 +144,7 @@ public function testRevokedJWT($method, $endpoint) 'email' => $email, 'password' => $password, ]), - ], + ] ); $contents = $response->getBody()->getContents(); diff --git a/tests/Feature/Authentication/SuccessTest.php b/tests/Feature/Authentication/SuccessTest.php index 2611fc1..d671b40 100644 --- a/tests/Feature/Authentication/SuccessTest.php +++ b/tests/Feature/Authentication/SuccessTest.php @@ -2,6 +2,7 @@ namespace SimpleJwtLoginTests\Feature\Authentication; +use PHPUnit\Framework\Attributes\TestDox; use SimpleJwtLoginTests\Feature\TestBase; class SuccessTest extends TestBase @@ -39,9 +40,7 @@ public static function setUpBeforeClass(): void ]); } - /** - * @testdox User can authenticate with email and password - */ + #[TestDox("User can authenticate with email and password")] public function testAuthenticationEmail() { // Register random user @@ -68,9 +67,8 @@ public function testAuthenticationEmail() $this->assertSame(200, $statusCode, "unable to delete the user"); } - /** - * @testdox User can refresh a valid JWT - */ + + #[TestDox("User can refresh a valid JWT")] public function testRefreshToken() { // Register random user @@ -107,9 +105,7 @@ public function testRefreshToken() $this->assertSame(200, $statusCode, "unable to delete the user"); } - /** - * @testdox User can validate a JWT - */ + #[TestDox("User can validate a JWT")] public function testValidateToken() { // Register random user diff --git a/tests/Feature/Autologin/SuccessTest.php b/tests/Feature/Autologin/SuccessTest.php index 0a5ce98..b2d4b09 100644 --- a/tests/Feature/Autologin/SuccessTest.php +++ b/tests/Feature/Autologin/SuccessTest.php @@ -48,7 +48,7 @@ public function testSuccessAutologin() 'allow_redirects' => [ 'track_redirects' => true, ], - ], + ] ); // Register random user diff --git a/tests/Feature/Autologin/ValidationTest.php b/tests/Feature/Autologin/ValidationTest.php index 23ae009..44eae8f 100644 --- a/tests/Feature/Autologin/ValidationTest.php +++ b/tests/Feature/Autologin/ValidationTest.php @@ -2,6 +2,8 @@ namespace SimpleJwtLoginTests\Feature\Autologin; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\TestDox; use SimpleJWTLogin\ErrorCodes; use SimpleJwtLoginTests\Feature\TestBase; @@ -47,61 +49,61 @@ public static function autologinValidationProvider() return [ 'empty_jwt' => [ 'jwt' => null, - 'error_message' => 'Wrong Request.', - 'error_code' => ErrorCodes::ERR_VALIDATE_LOGIN_WRONG_REQUEST, + 'errorMessage' => 'Wrong Request.', + 'errorCode' => ErrorCodes::ERR_VALIDATE_LOGIN_WRONG_REQUEST, ], 'invalid_jwt' => [ 'jwt' => "123", - 'error_message' => 'Wrong number of segments', - 'error_code' => ErrorCodes::ERR_WRONG_NUMBER_OF_SEGMENTS, + 'errorMessage' => 'Wrong number of segments', + 'errorCode' => ErrorCodes::ERR_WRONG_NUMBER_OF_SEGMENTS, ], 'invalid_jwt_values' => [ 'jwt' => "1.1.2", - 'error_message' => 'Syntax error, malformed JSON', - 'error_code' => ErrorCodes::ERR_UNKNOWN_ERROR, + 'errorMessage' => 'Syntax error, malformed JSON', + 'errorCode' => ErrorCodes::ERR_UNKNOWN_ERROR, ], ]; } + #[DataProvider('autologinValidationProvider')] + #[TestDox("Autologin Validation with JWT as Query Parameter")] /** - * @testdox Autologin Validation with JWT as Query Parameter - * @dataProvider autologinValidationProvider * @param ?string $jwt - * @param string $expectedErrorMessage - * @param int $expectedErrorCode + * @param string $errorMessage + * @param int $errorCode * @return void * @throws \GuzzleHttp\Exception\GuzzleException */ - public function testJWTInQueryParams($jwt, $expectedErrorMessage, $expectedErrorCode) + public function testJWTInQueryParams($jwt, $errorMessage, $errorCode) { $response = $this->client->get( - self::API_URL . "?rest_route=/simple-jwt-login/v1/autologin&JWT=" . $jwt, + self::API_URL . "?rest_route=/simple-jwt-login/v1/autologin&JWT=" . $jwt ); $contents = $response->getBody()->getContents(); $contentsArr = json_decode($contents, true); $expectedError = $this->generateErrorJson( - $expectedErrorMessage, - $expectedErrorCode + $errorMessage, + $errorCode ); $this->assertSame( $expectedError, - $contentsArr, + $contentsArr ); } + #[DataProvider('autologinValidationProvider')] + #[TestDox("Autologin Validation with JWT in the Header")] /** - * @testdox Autologin Validation with JWT in the Header - * @dataProvider autologinValidationProvider * @param ?string $jwt - * @param string $expectedErrorMessage - * @param int $expectedErrorCode + * @param string $errorMessage + * @param int $errorCode * @return void * @throws \GuzzleHttp\Exception\GuzzleException */ - public function testJWTInHeader($jwt, $expectedErrorMessage, $expectedErrorCode) + public function testJWTInHeader($jwt, $errorMessage, $errorCode) { $response = $this->client->get( self::API_URL . "?rest_route=/simple-jwt-login/v1/autologin", @@ -109,20 +111,20 @@ public function testJWTInHeader($jwt, $expectedErrorMessage, $expectedErrorCode) 'headers' => [ 'Authorization' => $jwt, ], - ], + ] ); $contents = $response->getBody()->getContents(); $contentsArr = json_decode($contents, true); $expectedError = $this->generateErrorJson( - $expectedErrorMessage, - $expectedErrorCode + $errorMessage, + $errorCode ); $this->assertSame( $expectedError, - $contentsArr, + $contentsArr ); } } diff --git a/tests/Feature/ProtectEndpoints/ActiveOnAllEndpointsTest.php b/tests/Feature/ProtectEndpoints/ActiveOnAllEndpointsTest.php index 3d3d7fb..65f2709 100644 --- a/tests/Feature/ProtectEndpoints/ActiveOnAllEndpointsTest.php +++ b/tests/Feature/ProtectEndpoints/ActiveOnAllEndpointsTest.php @@ -4,6 +4,7 @@ namespace SimpleJwtLoginTests\Feature\ProtectEndpoints; +use PHPUnit\Framework\Attributes\TestDox; use SimpleJWTLogin\Modules\Settings\ProtectEndpointSettings; use SimpleJwtLoginTests\Feature\TestBase; @@ -51,8 +52,8 @@ public static function setUpBeforeClass(): void ]); } + #[TestDox("WordPress endpoint can be accessed without JWT if whitelisted")] /** - * @testdox WordPress endpoint can be accessed without JWT if whitelisted * @return void */ public function testCanAccessWhitelistedEndpoint() @@ -62,8 +63,8 @@ public function testCanAccessWhitelistedEndpoint() $this->assertEquals(200, $resp->getStatusCode()); } + #[TestDox("WordPress endpoint can't be accessed without JWT if whitelisted")] /** - * @testdox WordPress endpoint can't be accessed without JWT if whitelisted * @return void */ public function testEndpointCanNotBeAccessedWithoutJWT() @@ -77,14 +78,14 @@ public function testEndpointCanNotBeAccessedWithoutJWT() $this->generateErrorJson( "You are not authorized to access this endpoint.", 403, - ['type' => 'simple-jwt-login-route-protect'], + ['type' => 'simple-jwt-login-route-protect'] ), - $contentsArr, + $contentsArr ); } + #[TestDox("WordPress endpoint can be accessed with JWT if whitelisted")] /** - * @testdox WordPress endpoint can be accessed with JWT if whitelisted * @return void */ public function testEndpointCanBeAccessedWithJWT() diff --git a/tests/Feature/ProtectEndpoints/ActiveOnSpecificEndpointsTest.php b/tests/Feature/ProtectEndpoints/ActiveOnSpecificEndpointsTest.php index 1c3b908..8987b4a 100644 --- a/tests/Feature/ProtectEndpoints/ActiveOnSpecificEndpointsTest.php +++ b/tests/Feature/ProtectEndpoints/ActiveOnSpecificEndpointsTest.php @@ -4,6 +4,7 @@ namespace SimpleJwtLoginTests\Feature\ProtectEndpoints; +use PHPUnit\Framework\Attributes\TestDox; use SimpleJWTLogin\Modules\Settings\ProtectEndpointSettings; use SimpleJwtLoginTests\Feature\TestBase; @@ -52,8 +53,8 @@ public static function setUpBeforeClass(): void ]); } + #[TestDox("WordPress endpoint can be accessed without JWT if not protected")] /** - * @testdox WordPress endpoint can be accessed without JWT if not protected * @return void */ public function testCanAccessNotProtectedEndpoint() @@ -63,8 +64,8 @@ public function testCanAccessNotProtectedEndpoint() $this->assertEquals(200, $resp->getStatusCode()); } + #[TestDox("WordPress endpoint can't be accessed without JWT if protected")] /** - * @testdox WordPress endpoint can't be accessed without JWT if protected * @return void */ public function testEndpointCanNotBeAccessedWithoutJWT() @@ -78,14 +79,14 @@ public function testEndpointCanNotBeAccessedWithoutJWT() $this->generateErrorJson( "You are not authorized to access this endpoint.", 403, - ['type' => 'simple-jwt-login-route-protect'], + ['type' => 'simple-jwt-login-route-protect'] ), - $contentsArr, + $contentsArr ); } + #[TestDox("WordPress endpoint can be accessed with JWT if whitelisted")] /** - * @testdox WordPress endpoint can be accessed with JWT if whitelisted * @return void */ public function testEndpointCanBeAccessedWithJWT() diff --git a/tests/Feature/ProtectEndpoints/NotActiveTest.php b/tests/Feature/ProtectEndpoints/NotActiveTest.php index 43eef5e..b484e63 100644 --- a/tests/Feature/ProtectEndpoints/NotActiveTest.php +++ b/tests/Feature/ProtectEndpoints/NotActiveTest.php @@ -4,6 +4,7 @@ namespace SimpleJwtLoginTests\Feature\ProtectEndpoints; +use PHPUnit\Framework\Attributes\TestDox; use SimpleJwtLoginTests\Feature\TestBase; class NotActiveTest extends TestBase @@ -48,8 +49,9 @@ public static function setUpBeforeClass(): void ]); } + + #[TestDox("WordPress endpoint can be accesses if protect endpoints is disabled")] /** - * @testdox WordPress endpoint can be accesses if protect endpoints is disabled * @return void */ public function testCanAccessEndpoint() diff --git a/tests/Feature/RegisterUsers/SuccessTest.php b/tests/Feature/RegisterUsers/SuccessTest.php index 4f9927e..bbfcf57 100644 --- a/tests/Feature/RegisterUsers/SuccessTest.php +++ b/tests/Feature/RegisterUsers/SuccessTest.php @@ -3,6 +3,7 @@ namespace SimpleJwtLoginTests\Feature\RegisterUsers; use Faker\Factory; +use PHPUnit\Framework\Attributes\TestDox; use SimpleJwtLoginTests\Feature\TestBase; class SuccessTest extends TestBase @@ -24,9 +25,7 @@ public static function setUpBeforeClass(): void ]); } - /** - * @testdox User can register with query params - */ + #[TestDox("User can register with query params")] public function testSuccessWithQueryParams() { $faker = Factory::create(); @@ -47,9 +46,7 @@ public function testSuccessWithQueryParams() $this->assertSame(true, $json['success']); } - /** - * @testdox User can register with form data - */ + #[TestDox("User can register with form data")] public function testSuccessWithFormData() { $faker = Factory::create(); @@ -73,9 +70,7 @@ public function testSuccessWithFormData() $this->assertSame(true, $json['success']); } - /** - * @testdox User can register with JSON body - */ + #[TestDox("User can register with JSON body")] public function testSuccessWithJSONBody() { $faker = Factory::create(); @@ -99,8 +94,8 @@ public function testSuccessWithJSONBody() $this->assertSame(true, $json['success']); } + #[TestDox("User can register with custom user_meta")] /** - * @testdox User can register with custom user_meta * @return void * @throws \GuzzleHttp\Exception\GuzzleException */ diff --git a/tests/Feature/RegisterUsers/ValidationsTest.php b/tests/Feature/RegisterUsers/ValidationsTest.php index c57640c..04d1c2e 100644 --- a/tests/Feature/RegisterUsers/ValidationsTest.php +++ b/tests/Feature/RegisterUsers/ValidationsTest.php @@ -2,6 +2,8 @@ namespace SimpleJwtLoginTests\Feature\RegisterUsers; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\TestDox; use SimpleJWTLogin\ErrorCodes; use SimpleJwtLoginTests\Feature\TestBase; @@ -31,7 +33,7 @@ public static function registerProvider(): array 'email' => null, 'username' => null, 'password' => null, - 'expected_error' => self::generateErrorJson( + 'expectedError' => self::generateErrorJson( 'Missing email or password.', ErrorCodes::ERR_REGISTER_MISSING_EMAIL_OR_PASSWORD ) @@ -40,7 +42,7 @@ public static function registerProvider(): array 'email' => "", 'username' => "", 'password' => "", - 'expected_error' => self::generateErrorJson( + 'expectedError' => self::generateErrorJson( 'Missing email or password.', ErrorCodes::ERR_REGISTER_MISSING_EMAIL_OR_PASSWORD ) @@ -49,7 +51,7 @@ public static function registerProvider(): array 'email' => "abc", 'username' => null, 'password' => "123", - 'expected_error' => self::generateErrorJson( + 'expectedError' => self::generateErrorJson( 'Invalid email address.', ErrorCodes::ERR_REGISTER_INVALID_EMAIL_ADDRESS ) @@ -58,7 +60,7 @@ public static function registerProvider(): array 'email' => "test@simplejwtlogin", 'username' => null, 'password' => null, - 'expected_error' => self::generateErrorJson( + 'expectedError' => self::generateErrorJson( 'Missing email or password.', ErrorCodes::ERR_REGISTER_MISSING_EMAIL_OR_PASSWORD ) @@ -67,7 +69,7 @@ public static function registerProvider(): array 'email' => null, 'username' => "admin", 'password' => null, - 'expected_error' => self::generateErrorJson( + 'expectedError' => self::generateErrorJson( 'Missing email or password.', ErrorCodes::ERR_REGISTER_MISSING_EMAIL_OR_PASSWORD ) @@ -76,9 +78,9 @@ public static function registerProvider(): array ]; } + #[DataProvider('registerProvider')] + #[TestDox("Register User Validation errors with query params")] /** - * @testdox Register User Validation errors with query params - * @dataProvider registerProvider * @return void * @throws \GuzzleHttp\Exception\GuzzleException */ @@ -100,9 +102,9 @@ public function testRegisterValidationErrors($email, $username, $password, $expe ); } + #[DataProvider('registerProvider')] + #[TestDox("Register User Validation errors with JSON body")] /** - * @testdox Register User Validation errors with JSON body - * @dataProvider registerProvider * @return void * @throws \GuzzleHttp\Exception\GuzzleException */ @@ -126,9 +128,9 @@ public function testRegisterValidationErrorsBodyJSON($email, $username, $passwor ); } + #[DataProvider('registerProvider')] + #[TestDox("Register User Validation errors with form params")] /** - * @testdox Register User Validation errors with form params - * @dataProvider registerProvider * @return void * @throws \GuzzleHttp\Exception\GuzzleException */ diff --git a/tests/Feature/TestBase.php b/tests/Feature/TestBase.php index 63f39e0..62290a4 100644 --- a/tests/Feature/TestBase.php +++ b/tests/Feature/TestBase.php @@ -12,7 +12,7 @@ class TestBase extends TestCase /** * @var Client|null */ - protected ?Client $client; + protected $client; const API_URL = 'http://localhost'; @@ -137,7 +137,7 @@ protected static function updateSimpleJWTOption($newOption) "INSERT IGNORE INTO %s (option_name, option_value) VALUES('%s', '%s');", $table, SimpleJWTLoginSettings::OPTIONS_KEY, - json_encode($newOption), + json_encode($newOption) ) ); } else { @@ -168,7 +168,7 @@ protected function updateWpOption($optionKey, $optionValue) "UPDATE %s SET option_value='%s' WHERE option_name='%s' LIMIT 1;", $table, $optionValue, - $optionKey, + $optionKey ) ); } @@ -289,7 +289,7 @@ protected function getWpOptionValue($optionKey) $resource = self::$dbCon->query( sprintf( "SELECT option_value FROM $table WHERE option_name='%s' LIMIT 1;", - $optionKey, + $optionKey ) ); while ($rows = $resource->fetch_assoc()) { From d8c999434252f0c1a556741a8e283c7130b85699 Mon Sep 17 00:00:00 2001 From: nicumicle <20170987+nicumicleI@users.noreply.github.com> Date: Sun, 10 Mar 2024 11:56:54 +0100 Subject: [PATCH 4/5] Fix unit tests --- tests/Unit/Helpers/ArrayHelperTest.php | 13 ++--- .../Helpers/Jwt/JwtKeyCertificateTest.php | 3 +- .../Helpers/Jwt/JwtKeyDecryptionKeyTest.php | 3 +- tests/Unit/Helpers/Jwt/JwtKeyFactoryTest.php | 9 ++-- tests/Unit/Helpers/Jwt/JwtKeyWpConfigTest.php | 4 +- tests/Unit/Helpers/ServerHelperTest.php | 27 +++++----- tests/Unit/Libraries/JWTTest.php | 3 +- tests/Unit/Libraries/ParseRequestTest.php | 14 +++--- tests/Unit/Modules/AuthCodeBuilderTest.php | 5 +- .../Modules/Settings/LoginSettingsTest.php | 13 ++--- .../Settings/ProtectEndpointSettingsTest.php | 10 ++-- .../Modules/Settings/RegisterSettingsTest.php | 47 ++++++++--------- .../Modules/Settings/SettingsFactoryTest.php | 3 +- .../Modules/SimpleJWTLoginSettingsTest.php | 3 +- tests/Unit/Modules/UserPropertiesTest.php | 5 +- .../Unit/Services/AuthenticateServiceTest.php | 14 +++--- tests/Unit/Services/DeleteUserServiceTest.php | 19 +++---- tests/Unit/Services/LoginServiceTest.php | 50 +++++++++---------- .../Services/ProtectEndpointServiceTest.php | 5 +- tests/Unit/Services/RedirectServiceTest.php | 15 +++--- .../Unit/Services/RefreshTokenServiceTest.php | 3 +- .../Unit/Services/RegisterUserServiceTest.php | 23 +++++---- .../Services/ResetPasswordServiceTest.php | 32 ++++++------ .../Unit/Services/RevokeTokenServiceTest.php | 3 +- .../Services/ValidateTokenServiceTest.php | 10 ++-- 25 files changed, 176 insertions(+), 160 deletions(-) diff --git a/tests/Unit/Helpers/ArrayHelperTest.php b/tests/Unit/Helpers/ArrayHelperTest.php index 35ec090..e29d469 100644 --- a/tests/Unit/Helpers/ArrayHelperTest.php +++ b/tests/Unit/Helpers/ArrayHelperTest.php @@ -2,24 +2,25 @@ namespace SimpleJwtLoginTests\Unit\Helpers; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use SimpleJWTLogin\Helpers\ArrayHelper; class ArrayHelperTest extends TestCase { + #[DataProvider('convertStringToArrayProvider')] /** - * @dataProvider convertStringToArrayProvider * @param string $string - * @param string[] $expectedResult + * @param string[] $result * @return void */ - public function testConvertStringToArray($string, $expectedResult) + public function testConvertStringToArray($string, $result) { - $result = ArrayHelper::convertStringToArray($string); + $data = ArrayHelper::convertStringToArray($string); $this->assertEquals( - $expectedResult, - $result + $result, + $data ); } diff --git a/tests/Unit/Helpers/Jwt/JwtKeyCertificateTest.php b/tests/Unit/Helpers/Jwt/JwtKeyCertificateTest.php index 5631c25..bb24329 100644 --- a/tests/Unit/Helpers/Jwt/JwtKeyCertificateTest.php +++ b/tests/Unit/Helpers/Jwt/JwtKeyCertificateTest.php @@ -2,12 +2,13 @@ namespace SimpleJwtLoginTests\Unit\Helpers\Jwt; +use PHPUnit\Framework\Attributes\DataProvider; use SimpleJWTLogin\Helpers\Jwt\JwtKeyCertificate; class JwtKeyCertificateTest extends JwtKeyBase { + #[DataProvider('settingsProvider')] /** - * @dataProvider settingsProvider * @param array $settings * @param string $expectedPublicKey * @param string $expectedPrivateKey diff --git a/tests/Unit/Helpers/Jwt/JwtKeyDecryptionKeyTest.php b/tests/Unit/Helpers/Jwt/JwtKeyDecryptionKeyTest.php index 9af0c21..5f44353 100644 --- a/tests/Unit/Helpers/Jwt/JwtKeyDecryptionKeyTest.php +++ b/tests/Unit/Helpers/Jwt/JwtKeyDecryptionKeyTest.php @@ -2,12 +2,13 @@ namespace SimpleJwtLoginTests\Unit\Helpers\Jwt; +use PHPUnit\Framework\Attributes\DataProvider; use SimpleJWTLogin\Helpers\Jwt\JwtKeyDecryptionKey; class JwtKeyDecryptionKeyTest extends JwtKeyBase { + #[DataProvider('settingProvider')] /** - * @dataProvider settingProvider * @param array $settings * @param string $expectedPublicKey * @param string $expectedPrivateKey diff --git a/tests/Unit/Helpers/Jwt/JwtKeyFactoryTest.php b/tests/Unit/Helpers/Jwt/JwtKeyFactoryTest.php index feedd8b..8e312fa 100644 --- a/tests/Unit/Helpers/Jwt/JwtKeyFactoryTest.php +++ b/tests/Unit/Helpers/Jwt/JwtKeyFactoryTest.php @@ -2,6 +2,7 @@ namespace SimpleJwtLoginTests\Unit\Helpers\Jwt; +use PHPUnit\Framework\Attributes\DataProvider; use SimpleJWTLogin\Helpers\Jwt\JwtKeyCertificate; use SimpleJWTLogin\Helpers\Jwt\JwtKeyDecryptionKey; use SimpleJWTLogin\Helpers\Jwt\JwtKeyFactory; @@ -10,14 +11,14 @@ class JwtKeyFactoryTest extends JwtKeyBase { + #[DataProvider('settingsProvider')] /** - * @dataProvider settingsProvider - * @param array $settingsArray + * @param array $settings * @param string $expected */ - public function testInstanceOfJwtKeyWpConfig($settingsArray, $expected) + public function testInstanceOfJwtKeyWpConfig($settings, $expected) { - $settingsMock = $this->getSettingsMock($settingsArray); + $settingsMock = $this->getSettingsMock($settings); $factory = JwtKeyFactory::getFactory($settingsMock); $this->assertInstanceOf($expected, $factory); } diff --git a/tests/Unit/Helpers/Jwt/JwtKeyWpConfigTest.php b/tests/Unit/Helpers/Jwt/JwtKeyWpConfigTest.php index 64887ff..09fd313 100644 --- a/tests/Unit/Helpers/Jwt/JwtKeyWpConfigTest.php +++ b/tests/Unit/Helpers/Jwt/JwtKeyWpConfigTest.php @@ -2,6 +2,7 @@ namespace SimpleJwtLoginTests\Unit\Helpers\Jwt; +use PHPUnit\Framework\Attributes\DataProvider; use SimpleJWTLogin\Helpers\Jwt\JwtKeyWpConfig; class JwtKeyWpConfigTest extends JwtKeyBase @@ -10,8 +11,9 @@ class JwtKeyWpConfigTest extends JwtKeyBase * @var boolean $preserveGlobalState */ protected $preserveGlobalState = true; + + #[DataProvider('settingsProvider')] /** - * @dataProvider settingsProvider * @param array $settings * @param string|null $expectedPublicKey * @param string|null $expectedPrivateKey diff --git a/tests/Unit/Helpers/ServerHelperTest.php b/tests/Unit/Helpers/ServerHelperTest.php index e4ab35c..48cde1c 100644 --- a/tests/Unit/Helpers/ServerHelperTest.php +++ b/tests/Unit/Helpers/ServerHelperTest.php @@ -2,13 +2,14 @@ namespace SimpleJwtLoginTests\Unit\Helpers; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use SimpleJWTLogin\Helpers\ServerHelper; class ServerHelperTest extends TestCase { + #[DataProvider('ipProvider')] /** - * @dataProvider ipProvider * @param array $server * @param mixed $expectedResult */ @@ -26,25 +27,25 @@ public static function ipProvider() return [ [ 'server' => [], - 'result' => null, + 'expectedResult' => null, ], [ 'server' => [ 'HTTP_CLIENT_IP' => '127.0.0.1' ], - 'result' => '127.0.0.1' + 'expectedResult' => '127.0.0.1' ], [ 'server' => [ 'HTTP_X_FORWARDED_FOR' => '127.0.0.1' ], - 'result' => '127.0.0.1' + 'expectedResult' => '127.0.0.1' ], [ 'server' => [ 'REMOTE_ADDR' => '127.0.0.1' ], - 'result' => '127.0.0.1' + 'expectedResult' => '127.0.0.1' ], [ 'server' => [ @@ -52,13 +53,13 @@ public static function ipProvider() 'HTTP_X_FORWARDED_FOR' => '', 'REMOTE_ADDR' => '127.0.0.1' ], - 'result' => '127.0.0.1' + 'expectedResult' => '127.0.0.1' ] ]; } + #[DataProvider('isClientInListProvider')] /** - * @dataProvider isClientInListProvider * @param mixed $list * @param bool $result */ @@ -105,8 +106,8 @@ public static function isClientInListProvider() ]; } + #[DataProvider('getHeadersProvider')] /** - * @dataProvider getHeadersProvider * @param array $server * @param array $expectedResult */ @@ -127,13 +128,13 @@ public static function getHeadersProvider() return [ [ 'server' => [], - 'result' => [] + 'expectedResult' => [] ], [ 'server' => [ 'HTTP_CUSTOM_HEADER' => 1 ], - 'result' => [ + 'expectedResult' => [ 'Custom-Header' => 1 ], ], @@ -141,16 +142,14 @@ public static function getHeadersProvider() 'server' => [ 'HTTP_Authorization' => 'Bearer 123', ], - 'result' => [ + 'expectedResult' => [ 'Authorization' => 'Bearer 123' ] ] ]; } - /** - * @dataProvider providerWildIps - */ + #[DataProvider('providerWildIps')] public function testIsClientIpInListWildCard($ipList, $expectedResult) { $serverHelper = new ServerHelper(['REMOTE_ADDR' => '127.0.0.1']); diff --git a/tests/Unit/Libraries/JWTTest.php b/tests/Unit/Libraries/JWTTest.php index efcf919..bcc3a93 100644 --- a/tests/Unit/Libraries/JWTTest.php +++ b/tests/Unit/Libraries/JWTTest.php @@ -3,6 +3,7 @@ namespace SimpleJwtLoginTests\Unit\Libraries; use Exception; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use SimpleJWTLogin\Libraries\JWT\JWT; @@ -20,8 +21,8 @@ public function testSuccessJWT() $this->assertSame($payload, (array) $decoded); } + #[DataProvider('invalidJwtProvider')] /** - * @dataProvider invalidJwtProvider * @throws Exception */ public function testWrongNumberOfSegments($jwtString) diff --git a/tests/Unit/Libraries/ParseRequestTest.php b/tests/Unit/Libraries/ParseRequestTest.php index 084ab31..e08f901 100644 --- a/tests/Unit/Libraries/ParseRequestTest.php +++ b/tests/Unit/Libraries/ParseRequestTest.php @@ -2,14 +2,14 @@ namespace SimpleJwtLoginTests\Unit\Libraries; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use SimpleJWTLogin\Libraries\ParseRequest; class ParseRequestTest extends TestCase { + #[DataProvider('contentTypeProvider')] /** - * @dataProvider contentTypeProvider - * * @param mixed $server * @param mixed $expectedResult */ @@ -27,31 +27,31 @@ public static function contentTypeProvider() return [ [ 'server' => [], - 'result' => [] + 'expectedResult' => [] ], [ 'server' => [ 'CONTENT_TYPE' => 'application/json' ], - 'result' => [], + 'expectedResult' => [], ], [ 'server' => [ 'CONTENT_TYPE' => 'multipart/form-data; boundary=something test' ], - 'result' => [], + 'expectedResult' => [], ], [ 'server' => [ 'CONTENT_TYPE' => 'text/html; charset=UTF-8' ], - 'result' => [], + 'expectedResult' => [], ], [ 'server' => [ 'CONTENT_TYPE' => 'multipart/form-data' ], - 'result' => [], + 'expectedResult' => [], ] ]; } diff --git a/tests/Unit/Modules/AuthCodeBuilderTest.php b/tests/Unit/Modules/AuthCodeBuilderTest.php index 1e8735c..902c4a5 100644 --- a/tests/Unit/Modules/AuthCodeBuilderTest.php +++ b/tests/Unit/Modules/AuthCodeBuilderTest.php @@ -2,13 +2,14 @@ namespace SimpleJwtLoginTests\Unit\Modules; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use SimpleJWTLogin\Modules\AuthCodeBuilder; class AuthCodeBuilderTest extends TestCase { + #[DataProvider('authCodeBuilderArrayProvider')] /** - * @dataProvider authCodeBuilderArrayProvider * @param array $data * @param array $expected */ @@ -63,8 +64,8 @@ public static function authCodeBuilderArrayProvider() ]; } + #[DataProvider('authCodeBuilderStringProvider')] /** - * @dataProvider authCodeBuilderStringProvider * @param array $data * @param array $expected */ diff --git a/tests/Unit/Modules/Settings/LoginSettingsTest.php b/tests/Unit/Modules/Settings/LoginSettingsTest.php index 4e7211b..f33d0dc 100644 --- a/tests/Unit/Modules/Settings/LoginSettingsTest.php +++ b/tests/Unit/Modules/Settings/LoginSettingsTest.php @@ -2,6 +2,7 @@ namespace SimpleJwtLoginTests\Unit\Modules\Settings; use Exception; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use SimpleJWTLogin\Modules\Settings\LoginSettings; use SimpleJWTLogin\Modules\WordPressDataInterface; @@ -139,8 +140,8 @@ public function testValidationInvalidRedirectURL() $loginSettings->validateSettings(); } + #[DataProvider('loginRemoveRequestParametersProvider')] /** - * @dataProvider loginRemoveRequestParametersProvider * @param array $settings * @param string $expectedResult * @return void @@ -163,7 +164,7 @@ public static function loginRemoveRequestParametersProvider() return [ 'not_set_get_default_values' => [ 'settings' => [], - 'expected_result' => implode( + 'expectedResult' => implode( ', ', [ 'rest_route', @@ -179,7 +180,7 @@ public static function loginRemoveRequestParametersProvider() 'settings' => [ 'auth_code_key' => 'auth_code', ], - 'expected_result' => implode( + 'expectedResult' => implode( ', ', [ 'rest_route', @@ -197,7 +198,7 @@ public static function loginRemoveRequestParametersProvider() 'login_remove_request_parameters' => null, 'auth_code_key' => 'auth_code', ], - 'expected_result' => implode( + 'expectedResult' => implode( ', ', [ 'rest_route', @@ -215,14 +216,14 @@ public static function loginRemoveRequestParametersProvider() 'login_remove_request_parameters' => implode(', ', ['test']), 'auth_code_key' => 'auth_code', ], - 'expected_result' => implode(', ', ['test']), + 'expectedResult' => implode(', ', ['test']), ], 'set_specific_values' => [ 'settings' => [ 'login_remove_request_parameters' => implode(', ', ['test1', 'test2']), 'auth_code_key' => 'auth_code', ], - 'expected_result' => implode(', ', ['test1', 'test2']), + 'expectedResult' => implode(', ', ['test1', 'test2']), ], ]; } diff --git a/tests/Unit/Modules/Settings/ProtectEndpointSettingsTest.php b/tests/Unit/Modules/Settings/ProtectEndpointSettingsTest.php index 207b160..69b2e99 100644 --- a/tests/Unit/Modules/Settings/ProtectEndpointSettingsTest.php +++ b/tests/Unit/Modules/Settings/ProtectEndpointSettingsTest.php @@ -3,6 +3,7 @@ namespace SimpleJwtLoginTests\Unit\Modules\Settings; use Exception; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use SimpleJWTLogin\Modules\Settings\ProtectEndpointSettings; use SimpleJWTLogin\Modules\WordPressDataInterface; @@ -207,11 +208,10 @@ public function testValidateWhenNotEnabled() $this->assertTrue($settings->validateSettings()); } + #[DataProvider('endpointsProvider')] /** * @param mixed $endpointLists * @throws Exception - * - * @dataProvider endpointsProvider */ public function testNoEndpointProvided($endpointLists) { @@ -234,17 +234,17 @@ public static function endpointsProvider() { return [ 'empty_array' => [ - 'endpoint_list' => [''] + 'endpointLists' => [''] ], 'array_with_empty_values' => [ - 'endpoint_list' => [ + 'endpointLists' => [ '', '', '', ] ], 'array_with_space' => [ - 'endpoint_list' => [ + 'endpointLists' => [ ' ', ' ', ] diff --git a/tests/Unit/Modules/Settings/RegisterSettingsTest.php b/tests/Unit/Modules/Settings/RegisterSettingsTest.php index 63abec3..f2a185d 100644 --- a/tests/Unit/Modules/Settings/RegisterSettingsTest.php +++ b/tests/Unit/Modules/Settings/RegisterSettingsTest.php @@ -2,6 +2,7 @@ namespace SimpleJwtLoginTests\Unit\Modules\Settings; use Exception; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use SimpleJWTLogin\Modules\Settings\RegisterSettings; use SimpleJWTLogin\Modules\WordPressDataInterface; @@ -104,16 +105,16 @@ public function testValidation() $registerUser->validateSettings(); } + #[DataProvider('invalidRoleProvider')] /** - * @dataProvider invalidRoleProvider - * @param string $roleName - * @param string $expectedException + * @param string $role + * @param string $exception * @throws Exception */ - public function testInvalidRole($roleName, $expectedException) + public function testInvalidRole($role, $exception) { $this->expectException(Exception::class); - $this->expectExceptionMessage($expectedException); + $this->expectExceptionMessage($exception); $this->wordPressData->method('roleExists') ->willReturn(false); @@ -122,14 +123,14 @@ public function testInvalidRole($roleName, $expectedException) ->withSettings([]) ->withPost([ 'allow_register' => '1', - 'new_user_profile' => $roleName, + 'new_user_profile' => $role, ]); $registerUser->initSettingsFromPost(); $registerUser->validateSettings(); } + #[DataProvider('passwordLengthProvider')] /** - * @dataProvider passwordLengthProvider * @param mixed $passwordLength * @param string $expectedException * @return void @@ -178,36 +179,36 @@ public static function passwordLengthProvider() { return [ 'one' => [ - 'password_length' => '1', - 'expected_exception' => 'Random password length should be at least 6 characters.', + 'passwordLength' => '1', + 'expectedException' => 'Random password length should be at least 6 characters.', ], 'negative_value' => [ - 'password_length' => '-1', - 'expected_exception' => 'Random password length should be at least 6 characters.', + 'passwordLength' => '-1', + 'expectedException' => 'Random password length should be at least 6 characters.', ], 'max_length' => [ - 'password_length' => '256', - 'expected_exception' => 'Random password length can be max 255.', + 'passwordLength' => '256', + 'expectedException' => 'Random password length can be max 255.', ], 'letters' => [ - 'password_length' => 'abc', - 'expected_exception' => 'Random password length should be an integer.', + 'passwordLength' => 'abc', + 'expectedException' => 'Random password length should be an integer.', ], 'letters_and_number' => [ - 'password_length' => 'abc123', - 'expected_exception' => 'Random password length should be an integer.', + 'passwordLength' => 'abc123', + 'expectedException' => 'Random password length should be an integer.', ], 'number_and_letters' => [ - 'password_length' => '123abc', - 'expected_exception' => 'Random password length should be an integer.', + 'passwordLength' => '123abc', + 'expectedException' => 'Random password length should be an integer.', ], 'empty_value' => [ - 'password_length' => '', - 'expected_exception' => 'Random password length should be an integer.', + 'passwordLength' => '', + 'expectedException' => 'Random password length should be an integer.', ], 'empty_space' => [ - 'password_length' => ' ', - 'expected_exception' => 'Random password length should be an integer.', + 'passwordLength' => ' ', + 'expectedException' => 'Random password length should be an integer.', ], ]; } diff --git a/tests/Unit/Modules/Settings/SettingsFactoryTest.php b/tests/Unit/Modules/Settings/SettingsFactoryTest.php index 011fb3d..fb82ad4 100644 --- a/tests/Unit/Modules/Settings/SettingsFactoryTest.php +++ b/tests/Unit/Modules/Settings/SettingsFactoryTest.php @@ -3,6 +3,7 @@ namespace SimpleJwtLoginTests\Unit\Modules\Settings; use Exception; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use SimpleJWTLogin\Modules\Settings\ApplicationsSettings; use SimpleJWTLogin\Modules\Settings\AuthCodesSettings; @@ -17,8 +18,8 @@ class SettingsFactoryTest extends TestCase { + #[DataProvider('settingsFactoryProvider')] /** - * @dataProvider settingsFactoryProvider * @param int $type * @param string $expectedInstance * diff --git a/tests/Unit/Modules/SimpleJWTLoginSettingsTest.php b/tests/Unit/Modules/SimpleJWTLoginSettingsTest.php index 334447c..89fe853 100644 --- a/tests/Unit/Modules/SimpleJWTLoginSettingsTest.php +++ b/tests/Unit/Modules/SimpleJWTLoginSettingsTest.php @@ -3,6 +3,7 @@ namespace SimpleJwtLoginTests\Unit\Modules; use Exception; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use SimpleJWTLogin\Modules\Settings\AuthCodesSettings; use SimpleJWTLogin\Modules\Settings\AuthenticationSettings; @@ -177,8 +178,8 @@ public function testCallingWithInvalidNonce() ); } + #[DataProvider('settingsProvider')] /** - * @dataProvider settingsProvider * @param mixed $settings * @throws Exception */ diff --git a/tests/Unit/Modules/UserPropertiesTest.php b/tests/Unit/Modules/UserPropertiesTest.php index ce2ed03..9a47bcc 100644 --- a/tests/Unit/Modules/UserPropertiesTest.php +++ b/tests/Unit/Modules/UserPropertiesTest.php @@ -2,13 +2,14 @@ namespace SimpleJwtLoginTests\Unit\Modules; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use SimpleJWTLogin\Modules\UserProperties; class UserPropertiesTest extends TestCase { + #[DataProvider('providerBuildMethod')] /** - * @dataProvider providerBuildMethod * @param mixed $expected * @param mixed $userProperties * @param mixed $extraParameters @@ -97,8 +98,8 @@ public static function providerBuildMethod() ]; } + #[DataProvider('providerTestGetExtraParametersFromRequest')] /** - * @dataProvider providerTestGetExtraParametersFromRequest * @param array $expected * @param array $request */ diff --git a/tests/Unit/Services/AuthenticateServiceTest.php b/tests/Unit/Services/AuthenticateServiceTest.php index 35da87d..9cab746 100644 --- a/tests/Unit/Services/AuthenticateServiceTest.php +++ b/tests/Unit/Services/AuthenticateServiceTest.php @@ -2,6 +2,7 @@ namespace SimpleJwtLoginTests\Unit\Services; use Exception; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use SimpleJWTLogin\Helpers\ServerHelper; use SimpleJWTLogin\Modules\Settings\AuthenticationSettings; @@ -25,9 +26,8 @@ public function setUp(): void ->getMock(); } + #[DataProvider('validationProvider')] /** - * @dataProvider validationProvider - * * @param array $settings * @param array $request * @param string $exceptionMessage @@ -57,21 +57,21 @@ public static function validationProvider() [ 'settings' => [], 'request' => [], - 'expectedMessage' => 'Authentication is not enabled.', + 'exceptionMessage' => 'Authentication is not enabled.', ], [ 'settings' => [ 'allow_authentication' => '0', ], 'request' => [], - 'expectedMessage' => 'Authentication is not enabled.' + 'exceptionMessage' => 'Authentication is not enabled.' ], [ 'settings' => [ 'allow_authentication' => '1', ], 'request' => [], - 'expectedMessage' => 'The email or username parameter is missing from request.' + 'exceptionMessage' => 'The email or username parameter is missing from request.' ], [ 'settings' => [ @@ -80,7 +80,7 @@ public static function validationProvider() 'request' => [ 'email' => '', ], - 'expectedMessage' => 'The password or password_hash parameter is missing from request.' + 'exceptionMessage' => 'The password or password_hash parameter is missing from request.' ], [ 'settings' => [ @@ -89,7 +89,7 @@ public static function validationProvider() 'request' => [ 'username' => '', ], - 'expectedMessage' => 'The password or password_hash parameter is missing from request.' + 'exceptionMessage' => 'The password or password_hash parameter is missing from request.' ], ]; } diff --git a/tests/Unit/Services/DeleteUserServiceTest.php b/tests/Unit/Services/DeleteUserServiceTest.php index 2d6ad59..ed7297d 100644 --- a/tests/Unit/Services/DeleteUserServiceTest.php +++ b/tests/Unit/Services/DeleteUserServiceTest.php @@ -3,6 +3,7 @@ namespace SimpleJwtLoginTests\Unit\Services; use Exception; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use SimpleJWTLogin\ErrorCodes; use SimpleJWTLogin\Helpers\ServerHelper; @@ -28,8 +29,8 @@ public function setUp(): void ->getMock(); } + #[DataProvider('validationProvider')] /** - * @dataProvider validationProvider * @param array $request * @param array $settings * @param string $exceptionMessage @@ -110,8 +111,8 @@ public function testUnableToDeleteUser() $deleteUserService->makeAction(); } + #[DataProvider('deleteByProvider')] /** - * @dataProvider deleteByProvider * @param int $deleteBy * @throws Exception */ @@ -215,14 +216,14 @@ public static function validationProvider() 'test_empty_settings_and_request' => [ 'request' => [], 'settings' => [], - 'expectedException' => 'Delete is not enabled.', + 'exceptionMessage' => 'Delete is not enabled.', ], 'test_missing_jwt_parameter' => [ 'request' => [], 'settings' => [ 'allow_delete' => true, ], - 'expectedException' => 'The `jwt` parameter is missing.', + 'exceptionMessage' => 'The `jwt` parameter is missing.', ], 'test_empty_jwt' => [ 'request' => [ @@ -231,7 +232,7 @@ public static function validationProvider() 'settings' => [ 'allow_delete' => true, ], - 'expectedException' => 'The `jwt` parameter is missing.', + 'exceptionMessage' => 'The `jwt` parameter is missing.', ], 'empty_upper_case_jwt' => [ 'request' => [ @@ -240,7 +241,7 @@ public static function validationProvider() 'settings' => [ 'allow_delete' => true, ], - 'expectedException' => 'The `jwt` parameter is missing.', + 'exceptionMessage' => 'The `jwt` parameter is missing.', ], 'test_missing_auth_code' => [ 'request' => [ @@ -250,7 +251,7 @@ public static function validationProvider() 'settings' => [ 'allow_delete' => true, ], - 'expectedException' => 'Missing AUTH KEY ( AUTH_KEY ).', + 'exceptionMessage' => 'Missing AUTH KEY ( AUTH_KEY ).', ], 'test_empty_auth_code' => [ 'request' => [ @@ -260,7 +261,7 @@ public static function validationProvider() 'settings' => [ 'allow_delete' => true, ], - 'expectedException' => 'Missing AUTH KEY ( AUTH_KEY ).', + 'exceptionMessage' => 'Missing AUTH KEY ( AUTH_KEY ).', ], 'test_ip_not_allowed' => [ 'request' => [ @@ -272,7 +273,7 @@ public static function validationProvider() 'require_delete_auth' => false, 'delete_ip' => '127.1.1.1, 127.2.2.2', ], - 'expectedException' => 'You are not allowed to delete users from this IP:', + 'exceptionMessage' => 'You are not allowed to delete users from this IP:', ], ]; diff --git a/tests/Unit/Services/LoginServiceTest.php b/tests/Unit/Services/LoginServiceTest.php index 56e6b00..78199f5 100644 --- a/tests/Unit/Services/LoginServiceTest.php +++ b/tests/Unit/Services/LoginServiceTest.php @@ -3,6 +3,7 @@ namespace SimpleJwtLoginTests\Unit\Services; use Exception; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use SimpleJWTLogin\ErrorCodes; use SimpleJWTLogin\Helpers\ServerHelper; @@ -37,8 +38,8 @@ function ($parameter) { ->willReturn('https://admin.com'); } + #[DataProvider('validationProvider')] /** - * @dataProvider validationProvider * @param array $settings * @param array $request * @param string $exceptionMessage @@ -112,7 +113,7 @@ public static function validationProvider() 'JWT' => 'test', 'AUTH_KEY' => 'test' ], - 'exception' => 'Invalid Auth Code ( AUTH_KEY ) provided.', + 'exceptionMessage' => 'Invalid Auth Code ( AUTH_KEY ) provided.', ], 'ip_not_allowed' => [ 'settings' => [ @@ -123,7 +124,7 @@ public static function validationProvider() 'request' => [ 'JWT' => 'test', ], - 'exception' => 'This IP[ 127.0.0.1 ] is not allowed to auto-login.', + 'exceptionMessage' => 'This IP[ 127.0.0.1 ] is not allowed to auto-login.', ], 'test_unable_to_find_user_in_jwt' => [ 'settings' => [ @@ -139,7 +140,7 @@ public static function validationProvider() 'HS256' ) ], - 'exception' => 'Unable to find user test property in JWT.', + 'exceptionMessage' => 'Unable to find user test property in JWT.', ], 'test_unable_to_find_user_in_jwt_nested' => [ 'settings' => [ @@ -161,7 +162,7 @@ public static function validationProvider() 'HS256' ) ], - 'exception' => 'Unable to find user properties property in JWT.', + 'exceptionMessage' => 'Unable to find user properties property in JWT.', ] ]; } @@ -241,8 +242,8 @@ public function testLoginWithRevokedJWT() $service->makeAction(); } + #[DataProvider('loginProvider')] /** - * @dataProvider loginProvider * @param array|null $request * @param array|null $session * @param array|null $cookie @@ -356,8 +357,8 @@ public static function loginProvider() ]; } + #[DataProvider('redirectOnFailProvider')] /** - * @dataProvider redirectOnFailProvider * @param array $request * @param bool $includeParams */ @@ -423,30 +424,29 @@ public static function redirectOnFailProvider() return [ 'test_empty_request_and_include_params' => [ 'request' => [], - 'include_extra_params' => true, + 'includeParams' => true, ], 'test_one_param_and_include_params' => [ 'request' => [ 'test' => '123', ], - 'include_extra_params' => true, + 'includeParams' => true, ], 'test_empty_request' => [ 'request' => [], - 'include_extra_params' => false, + 'includeParams' => false, ], 'test_one_param' => [ 'request' => [ 'test' => '123', ], - 'include_extra_params' => false, + 'includeParams' => false, ], - ]; } + #[DataProvider('issProvider')] /** - * @dataProvider issProvider * @return void * @throws Exception */ @@ -523,24 +523,24 @@ public static function issProvider() { return [ 'no_iss' => [ - 'jwt_iss' => null, - 'settings_iss' => null, - 'expected_error' => null, + 'jwtIss' => null, + 'settingsIss' => null, + 'expectedError' => null, ], 'different_iss' => [ - 'jwt_iss' => 'one', - 'settings_iss' => 'two', - 'expected_error' => 'The JWT issuer(iss) is not allowed to auto-login.', + 'jwtIss' => 'one', + 'settingsIss' => 'two', + 'expectedError' => 'The JWT issuer(iss) is not allowed to auto-login.', ], 'iss_only_in_payload' => [ - 'jwt_iss' => 'one', - 'settings_iss' => '', - 'expected_error' => null, + 'jwtIss' => 'one', + 'settingsIss' => '', + 'expectedError' => null, ], 'iss_same_as_payload' => [ - 'jwt_iss' => 'one', - 'settings_iss' => 'one', - 'expected_error' => null, + 'jwtIss' => 'one', + 'settingsIss' => 'one', + 'expectedError' => null, ], ]; } diff --git a/tests/Unit/Services/ProtectEndpointServiceTest.php b/tests/Unit/Services/ProtectEndpointServiceTest.php index 209b090..eb935cc 100644 --- a/tests/Unit/Services/ProtectEndpointServiceTest.php +++ b/tests/Unit/Services/ProtectEndpointServiceTest.php @@ -2,6 +2,7 @@ namespace SimpleJwtLoginTests\Unit\Services; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use SimpleJWTLogin\Helpers\ServerHelper; use SimpleJWTLogin\Libraries\JWT\JWT; @@ -30,14 +31,14 @@ public function setUp(): void ->willReturn('http://test.com/wp-admin/'); } + + #[DataProvider('accessProvider')] /** * @param bool $expectedResult * @param string $currentUrl * @param string $documentRoot * @param array $request * @param array $settings - * - * @dataProvider accessProvider */ public function testHasAccess($expectedResult, $currentUrl, $documentRoot, $request, $settings) { diff --git a/tests/Unit/Services/RedirectServiceTest.php b/tests/Unit/Services/RedirectServiceTest.php index 5102ae9..02587da 100644 --- a/tests/Unit/Services/RedirectServiceTest.php +++ b/tests/Unit/Services/RedirectServiceTest.php @@ -2,6 +2,7 @@ namespace SimpleJwtLoginTests\Unit\Services; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use SimpleJWTLogin\Modules\Settings\LoginSettings; use SimpleJWTLogin\Modules\SimpleJWTLoginHooks; @@ -73,8 +74,8 @@ public function testNoRedirect() $this->assertTrue($response); } + #[DataProvider('redirectCustomURLProvider')] /** - * @dataProvider redirectCustomURLProvider * @param array $extraSettings * @param string $expectedUrl * @return void @@ -158,17 +159,17 @@ public static function redirectCustomURLProvider() { return [ 'simple-redirect' => [ - 'settings' => [ + 'extraSettings' => [ 'redirect_url' => 'https://www.google.com', ], 'request' => [ 'redirectUrl' => 'http://www.test.com', 'email' => 'test@test.com', ], - 'expected_url' => 'http://www.test.com', + 'expectedUrl' => 'http://www.test.com', ], 'redirect_parameters_are_added_to_redirect_url' => [ - 'settings' => [ + 'extraSettings' => [ 'redirect_url' => 'https://www.google.com', 'login_remove_request_parameters' => 'jwt', ], @@ -176,11 +177,11 @@ public static function redirectCustomURLProvider() 'redirectUrl' => 'http://www.test.com', 'email' => 'test@test.com', ], - 'expected_url' => 'http://www.test.com?redirectUrl=' + 'expectedUrl' => 'http://www.test.com?redirectUrl=' . urlencode('http://www.test.com?email=test@test.com'), ], 'redirect_parameters_outside_redirect_url' => [ - 'settings' => [ + 'extraSettings' => [ 'redirect_url' => 'https://www.google.com', 'login_remove_request_parameters' => 'jwt, JWT,password', ], @@ -190,7 +191,7 @@ public static function redirectCustomURLProvider() 'JWT' => '123', 'password' => 'my-super-secret-password', ], - 'expected_url' => 'http://www.test.com?test=1&redirectUrl=' + 'expectedUrl' => 'http://www.test.com?test=1&redirectUrl=' . urlencode('http://www.test.com?test=1&email=test@test.com'), ] ]; diff --git a/tests/Unit/Services/RefreshTokenServiceTest.php b/tests/Unit/Services/RefreshTokenServiceTest.php index c5ceae1..a46a070 100644 --- a/tests/Unit/Services/RefreshTokenServiceTest.php +++ b/tests/Unit/Services/RefreshTokenServiceTest.php @@ -2,6 +2,7 @@ namespace SimpleJwtLoginTests\Unit\Services; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use SimpleJWTLogin\ErrorCodes; use SimpleJWTLogin\Helpers\ServerHelper; @@ -26,8 +27,8 @@ public function setUp(): void ->getMock(); } + #[DataProvider('validationProvider')] /** - * @dataProvider validationProvider * @param array $settings * @param string $exceptionMessage * @throws \Exception diff --git a/tests/Unit/Services/RegisterUserServiceTest.php b/tests/Unit/Services/RegisterUserServiceTest.php index caf8317..8a75e72 100644 --- a/tests/Unit/Services/RegisterUserServiceTest.php +++ b/tests/Unit/Services/RegisterUserServiceTest.php @@ -3,6 +3,7 @@ namespace SimpleJwtLoginTests\Unit\Services; use Exception; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use SimpleJWTLogin\ErrorCodes; use SimpleJWTLogin\Helpers\ServerHelper; @@ -33,8 +34,8 @@ function ($parameter) { ); } + #[DataProvider('validationProvider')] /** - * @dataProvider validationProvider * @param mixed $request * @param array $settings * @param string $exceptionMessage @@ -70,21 +71,21 @@ public static function validationProvider() 'test_empty_settings' => [ 'request' => [], 'settings' => [], - 'exception' => 'Register is not allowed.', + 'exceptionMessage' => 'Register is not allowed.', ], 'test_register_is_not_allowed' => [ 'request' => [], 'settings' => [ 'allow_register' => false, ], - 'exception' => 'Register is not allowed.', + 'exceptionMessage' => 'Register is not allowed.', ], 'test_default_auth_key_is_required' => [ 'request' => [], 'settings' => [ 'allow_register' => true, ], - 'exception' => 'Invalid Auth Code ( AUTH_KEY ) provided.', + 'exceptionMessage' => 'Invalid Auth Code ( AUTH_KEY ) provided.', ], 'test_with_invalid_auth_code' => [ 'request' => [ @@ -100,7 +101,7 @@ public static function validationProvider() ] ] ], - 'exception' => 'Invalid Auth Code ( AUTH_KEY ) provided.', + 'exceptionMessage' => 'Invalid Auth Code ( AUTH_KEY ) provided.', ], 'test_without_email_and_password' => [ 'request' => [ @@ -116,7 +117,7 @@ public static function validationProvider() ] ] ], - 'exception' => 'Missing email or password.', + 'exceptionMessage' => 'Missing email or password.', ], 'test_only_with_email' => [ 'request' => [ @@ -133,7 +134,7 @@ public static function validationProvider() ] ] ], - 'exception' => 'Missing email or password.', + 'exceptionMessage' => 'Missing email or password.', ], 'test_only_with_password' => [ 'request' => [ @@ -150,7 +151,7 @@ public static function validationProvider() ] ] ], - 'exception' => 'Missing email or password.', + 'exceptionMessage' => 'Missing email or password.', ], 'test_with_invalid_email' => [ 'request' => [ @@ -168,7 +169,7 @@ public static function validationProvider() ] ] ], - 'exception' => 'Invalid email address.', + 'exceptionMessage' => 'Invalid email address.', ], 'test_register_domain' => [ 'request' => [ @@ -187,7 +188,7 @@ public static function validationProvider() ], 'register_domain' => 'google.com,test.com', ], - 'exception' => 'This website does not allows users from this domain.', + 'exceptionMessage' => 'This website does not allows users from this domain.', ], 'test_register_ip' => [ 'request' => [ @@ -207,7 +208,7 @@ public static function validationProvider() 'register_ip' => '127.0.1.1', 'register_domain' => 'google.com,test.com', ], - 'exception' => 'This IP[127.0.0.1] is not allowed to register users.', + 'exceptionMessage' => 'This IP[127.0.0.1] is not allowed to register users.', ], ]; diff --git a/tests/Unit/Services/ResetPasswordServiceTest.php b/tests/Unit/Services/ResetPasswordServiceTest.php index a6a3cfb..a5cfd2a 100644 --- a/tests/Unit/Services/ResetPasswordServiceTest.php +++ b/tests/Unit/Services/ResetPasswordServiceTest.php @@ -3,6 +3,7 @@ namespace SimpleJwtLoginTests\Unit\Services; use Exception; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use SimpleJWTLogin\Helpers\ServerHelper; use SimpleJWTLogin\Libraries\JWT\JWT; @@ -27,9 +28,8 @@ public function setUp(): void ->getMock(); } + #[DataProvider('sendUserPasswordProvider')] /** - * @dataProvider sendUserPasswordProvider - * * @param mixed $settings * @param array $request * @param string $exceptionMessage @@ -57,7 +57,7 @@ public static function sendUserPasswordProvider() [ 'settings' => [], 'request' => [], - 'exception' => 'Reset Password is not allowed.' + 'exceptionMessage' => 'Reset Password is not allowed.' ], [ 'settings' => [ @@ -65,7 +65,7 @@ public static function sendUserPasswordProvider() 'reset_password_requires_auth_code' => 1, ], 'request' => [], - 'exception' => 'Invalid Auth Code ( AUTH_KEY ) provided.' + 'exceptionMessage' => 'Invalid Auth Code ( AUTH_KEY ) provided.' ], [ 'settings' => [ @@ -82,7 +82,7 @@ public static function sendUserPasswordProvider() 'request' => [ 'AUTH_KEY' => 123 ], - 'exception' => 'Missing email parameter.' + 'exceptionMessage' => 'Missing email parameter.' ], [ 'settings' => [ @@ -100,14 +100,13 @@ public static function sendUserPasswordProvider() 'AUTH_KEY' => 123, 'email' => 'userdoesnotexst@test.com' ], - 'exception' => 'Wrong user.' + 'exceptionMessage' => 'Wrong user.' ], ]; } + #[DataProvider('flowTypeProvider')] /** - * @dataProvider flowTypeProvider - * * @param int $flowType * * @throws Exception @@ -171,9 +170,8 @@ public static function flowTypeProvider() ]; } + #[DataProvider('changePasswordValidationProvider')] /** - * @dataProvider changePasswordValidationProvider - * * @param array $settings * @param array $request * @param string $exceptionMessage @@ -201,7 +199,7 @@ public static function changePasswordValidationProvider() 'empty_settings' => [ 'settings' => [], 'request' => [], - 'exception' => 'Reset Password is not allowed.' + 'exceptionMessage' => 'Reset Password is not allowed.' ], 'empty_auth_key' => [ 'settings' => [ @@ -209,7 +207,7 @@ public static function changePasswordValidationProvider() 'reset_password_requires_auth_code' => 1, ], 'request' => [], - 'exception' => 'Invalid Auth Code ( AUTH_KEY ) provided.' + 'exceptionMessage' => 'Invalid Auth Code ( AUTH_KEY ) provided.' ], 'missing_email' => [ 'settings' => [ @@ -226,7 +224,7 @@ public static function changePasswordValidationProvider() 'request' => [ 'AUTH_KEY' => 123 ], - 'exception' => 'Missing email parameter.' + 'exceptionMessage' => 'Missing email parameter.' ], 'missing_code' => [ 'settings' => [ @@ -245,7 +243,7 @@ public static function changePasswordValidationProvider() 'email' => 'email@email.com', 'new_password' => '123', ], - 'exception' => 'Missing code parameter.' + 'exceptionMessage' => 'Missing code parameter.' ], 'missing_password' => [ 'settings' => [ @@ -264,7 +262,7 @@ public static function changePasswordValidationProvider() 'email' => 'email@email.com', 'code' => '123', ], - 'exception' => 'Missing new_password parameter.' + 'exceptionMessage' => 'Missing new_password parameter.' ], 'invalid_code' => [ 'settings' => [ @@ -284,7 +282,7 @@ public static function changePasswordValidationProvider() 'code' => '123', 'new_password' => '123', ], - 'exception' => 'Invalid code provided.' + 'exceptionMessage' => 'Invalid code provided.' ], 'jwt_with_invalid_email' => [ 'settings' => [ @@ -307,7 +305,7 @@ public static function changePasswordValidationProvider() 'jwt' => JWT::encode(['email' => 'test@test.com'], 'test', 'HS256'), 'new_password' => '123', ], - 'exception' => 'This JWT can not change your password.' + 'exceptionMessage' => 'This JWT can not change your password.' ], ]; } diff --git a/tests/Unit/Services/RevokeTokenServiceTest.php b/tests/Unit/Services/RevokeTokenServiceTest.php index d17eef4..009e5ff 100644 --- a/tests/Unit/Services/RevokeTokenServiceTest.php +++ b/tests/Unit/Services/RevokeTokenServiceTest.php @@ -2,6 +2,7 @@ namespace SimpleJwtLoginTests\Unit\Services; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use SimpleJWTLogin\Helpers\ServerHelper; use SimpleJWTLogin\Libraries\JWT\JWT; @@ -27,8 +28,8 @@ public function setUp(): void ->getMock(); } + #[DataProvider('validationProvider')] /** - * @dataProvider validationProvider * @param array $settings * @param string $exceptionMessage * @throws \Exception diff --git a/tests/Unit/Services/ValidateTokenServiceTest.php b/tests/Unit/Services/ValidateTokenServiceTest.php index 4d88438..092fc08 100644 --- a/tests/Unit/Services/ValidateTokenServiceTest.php +++ b/tests/Unit/Services/ValidateTokenServiceTest.php @@ -3,6 +3,7 @@ namespace SimpleJwtLoginTests\Unit\Services; use Exception; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use SimpleJWTLogin\Libraries\JWT\JWT; use SimpleJWTLogin\Modules\Settings\LoginSettings; @@ -26,9 +27,8 @@ public function setUp(): void ->getMock(); } + #[DataProvider('validationProvider')] /** - * @dataProvider validationProvider - * * @param mixed $settings * @param string $expectedMessage * @param array $request @@ -56,7 +56,7 @@ public static function validationProvider() return [ [ 'settings' => [], - 'message' => 'Authentication is not enabled', + 'expectedMessage' => 'Authentication is not enabled', ], [ 'settings' => [ @@ -64,7 +64,7 @@ public static function validationProvider() 'request_jwt_header' => '0', 'request_jwt_url' => '1', ], - 'message' => 'The `jwt` parameter is missing.' + 'expectedMessage' => 'The `jwt` parameter is missing.' ], [ 'settings' => [ @@ -76,7 +76,7 @@ public static function validationProvider() 'url' => 'JWT' ] ], - 'message' => 'Wrong number of segments', + 'expectedMessage' => 'Wrong number of segments', 'request' => [ 'JWT' => '123' ] From 5f2b02c0e54a3f029c804105089cc5d7982e2c72 Mon Sep 17 00:00:00 2001 From: nicumicle <20170987+nicumicleI@users.noreply.github.com> Date: Sun, 10 Mar 2024 12:00:25 +0100 Subject: [PATCH 5/5] Create zip file --- download/simple-jwt-login.zip | Bin 183995 -> 184568 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/download/simple-jwt-login.zip b/download/simple-jwt-login.zip index fc1f4085585ea467d00144c10e90287c3e06ba25..fb80e11d0c45f6e1ffd391e3baab2b94d7047261 100644 GIT binary patch delta 12268 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