diff --git a/.github/workflows/running_the_tests.yml b/.github/workflows/running_the_tests.yml index a660051..3e7f871 100644 --- a/.github/workflows/running_the_tests.yml +++ b/.github/workflows/running_the_tests.yml @@ -21,7 +21,7 @@ jobs: strategy: fail-fast: false matrix: - php-version: ['8.2', '8.3', '8.4', '8.5'] + php-version: ['8.4', '8.5'] steps: - uses: shivammathur/setup-php@v2 diff --git a/composer.json b/composer.json index aa3ce2c..6e716b4 100644 --- a/composer.json +++ b/composer.json @@ -9,17 +9,17 @@ "oauth2-keycloak" ], "require": { - "php": ">=8.2", - "stevenmaguire/oauth2-keycloak": "^5.1", - "symfony/routing": "^6.4 || ^7.2 || ^8.0", - "symfony/security-bundle": "^6.4 || ^7.2 || ^8.0", - "symfony/http-kernel": "^6.4 || ^7.2 || ^8.0", - "symfony/framework-bundle": "^6.4 || ^7.2 || ^8.0", - "symfony/serializer-pack": "^1.3" + "php": ">=8.4", + "stevenmaguire/oauth2-keycloak": "^6.1", + "symfony/routing": "^6.4 || ^7.4 || ^8.0", + "symfony/security-bundle": "^6.4 || ^7.4 || ^8.0", + "symfony/http-kernel": "^6.4 || ^7.4 || ^8.0", + "symfony/framework-bundle": "^6.4 || ^7.4 || ^8.0", + "symfony/serializer-pack": "^1.4" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.75", - "phpunit/phpunit": "^11.2", + "friendsofphp/php-cs-fixer": "^3.94", + "phpunit/phpunit": "^13.0", "mockery/mockery": "^1.6", "phpstan/phpstan": "^2.1" }, diff --git a/phpunit.xml.dist b/phpunit.xml.dist index aaead59..08fcd95 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,6 +1,6 @@ - + @@ -8,7 +8,7 @@ - + diff --git a/src/Provider/KeycloakClient.php b/src/Provider/KeycloakClient.php index 55ebf31..b2142d0 100644 --- a/src/Provider/KeycloakClient.php +++ b/src/Provider/KeycloakClient.php @@ -49,7 +49,8 @@ public function __construct( } if ('' !== $this->encryption_key) { $this->keycloakProvider->setEncryptionKey($this->encryption_key); - } elseif ('' !== $this->encryption_key_path) { + } + elseif ('' !== $this->encryption_key_path) { $this->keycloakProvider->setEncryptionKeyPath($this->encryption_key_path); } } diff --git a/src/Representation/Collection/Collection.php b/src/Representation/Collection/Collection.php index 5073ef6..36db986 100644 --- a/src/Representation/Collection/Collection.php +++ b/src/Representation/Collection/Collection.php @@ -58,9 +58,9 @@ public function add(Representation $representation): void if (!$representation instanceof $expectedClass) { throw new \InvalidArgumentException(sprintf( '%s expects items to be %s representation, %s given', - (new \ReflectionClass(static::class))->getShortName(), - (new \ReflectionClass($expectedClass))->getShortName(), - (new \ReflectionClass($representation))->getShortName() + new \ReflectionClass(static::class)->getShortName(), + new \ReflectionClass($expectedClass)->getShortName(), + new \ReflectionClass($representation)->getShortName() )); } diff --git a/src/Representation/Composites.php b/src/Representation/Composites.php index 9da7a63..6c81111 100644 --- a/src/Representation/Composites.php +++ b/src/Representation/Composites.php @@ -4,12 +4,13 @@ namespace Mainick\KeycloakClientBundle\Representation; +use Mainick\KeycloakClientBundle\Representation\Collection\RealmCollection; use Mainick\KeycloakClientBundle\Representation\Type\Map; final class Composites extends Representation { public function __construct( - public ?RealCollection $realm = null, + public ?RealmCollection $realm = null, public ?Map $client = null, public ?Map $application = null, ) { diff --git a/src/Representation/GroupRepresentation.php b/src/Representation/GroupRepresentation.php index a5b60cf..8e71a95 100644 --- a/src/Representation/GroupRepresentation.php +++ b/src/Representation/GroupRepresentation.php @@ -6,7 +6,6 @@ use Mainick\KeycloakClientBundle\Annotation\Since; use Mainick\KeycloakClientBundle\Representation\Collection\GroupCollection; -use Mainick\KeycloakClientBundle\Representation\Representation; use Mainick\KeycloakClientBundle\Representation\Type\Map; final class GroupRepresentation extends Representation @@ -15,10 +14,8 @@ public function __construct( public ?string $id = null, public ?string $name = null, public ?string $path = null, - #[Since('23.0.0')] - public ?string $parentId = null, - #[Since('23.0.0')] - public ?int $subGroupCount = null, + #[Since('23.0.0')] public ?string $parentId = null, + #[Since('23.0.0')] public ?int $subGroupCount = null, public ?GroupCollection $subGroups = null, public ?Map $attributes = null, /** @var string[]|null */ diff --git a/src/Representation/RealmRepresentation.php b/src/Representation/RealmRepresentation.php index dcae9eb..9d18ddf 100644 --- a/src/Representation/RealmRepresentation.php +++ b/src/Representation/RealmRepresentation.php @@ -58,11 +58,9 @@ public function __construct( public ?bool $realmCacheEnabled = null, public ?bool $bruteForceProtected = null, public ?bool $permanentLockout = null, - #[Since('24.0.0')] - public ?int $maxTemporaryLockouts = null, + #[Since('24.0.0')] public ?int $maxTemporaryLockouts = null, public ?int $maxFailureWaitSeconds = null, - #[Since('24.0.0')] - public ?int $minimumQuickLoginWaitSeconds = null, + #[Since('24.0.0')] public ?int $minimumQuickLoginWaitSeconds = null, public ?int $waitIncrementSeconds = null, public ?int $quickLoginCheckMilliSeconds = null, public ?int $maxDeltaTimeSeconds = null, @@ -88,8 +86,7 @@ public function __construct( public ?int $otpPolicyDigits = null, public ?int $otpPolicyLookAheadWindow = null, public ?int $otpPolicyPeriod = null, - #[Since('20.0.0')] - public ?bool $otpPolicyCodeReusable = null, + #[Since('20.0.0')] public ?bool $otpPolicyCodeReusable = null, /** @var string[]|null */ public ?array $otpSupportedApplications = null, public ?string $webAuthnPolicyRpEntityName = null, @@ -105,8 +102,7 @@ public function __construct( /** @var string[]|null */ public ?array $webAuthnPolicyAcceptableAaguids = null, /** @var string[]|null */ - #[Since('23.0.0')] - public ?array $webAuthnPolicyExtraOrigins = null, + #[Since('23.0.0')] public ?array $webAuthnPolicyExtraOrigins = null, public ?string $webAuthnPolicyPasswordlessRpEntityName = null, /** @var string[]|null */ public ?array $webAuthnPolicyPasswordlessSignatureAlgorithms = null, @@ -120,8 +116,7 @@ public function __construct( /** @var string[]|null */ public ?array $webAuthnPolicyPasswordlessAcceptableAaguids = null, /** @var string[]|null */ - #[Since('23.0.0')] - public ?array $webAuthnPolicyPasswordlessExtraOrigins = null, + #[Since('23.0.0')] public ?array $webAuthnPolicyPasswordlessExtraOrigins = null, //public ?ClientProfiles $clientProfiles = null, //public ?ClientPolicies $clientPolicies = null, public ?UserCollection $users = null, @@ -167,8 +162,7 @@ public function __construct( public ?string $resetCredentialsFlow = null, public ?string $clientAuthenticationFlow = null, public ?string $dockerAuthenticationFlow = null, - #[Since('24.0.0')] - public ?string $firstBrokerLoginFlow = null, + #[Since('24.0.0')] public ?string $firstBrokerLoginFlow = null, public ?Map $attributes = null, public ?string $keycloakVersion = null, public ?bool $userManagedAccessAllowed = null, @@ -176,30 +170,22 @@ public function __construct( // public ?bool $organizationsEnabled = null, // #[Since('25.0.0')] // public ?OrganizationCollection $organizations = null, - #[Since('25.0.0')] - public ?bool $verifiableCredentialsEnabled = null, - #[Since('25.0.0')] - public ?bool $adminPermissionsEnabled = null, - #[Since('25.0.0')] - public ?bool $social = null, - #[Since('25.0.0')] - public ?bool $updateProfileOnInitialSocialLogin = null, - /** @var string[]|null */ - #[Since('25.0.0')] - public ?array $socialProviders = null, - /** @var string[]|null */ - #[Since('25.0.0')] - public ?array $applicationScopeMappings = null, + #[Since('25.0.0')] public ?bool $verifiableCredentialsEnabled = null, + #[Since('25.0.0')] public ?bool $adminPermissionsEnabled = null, + #[Since('25.0.0')] public ?bool $social = null, + #[Since('25.0.0')] public ?bool $updateProfileOnInitialSocialLogin = null, + /** @var string[]|null */ + #[Since('25.0.0')] public ?array $socialProviders = null, + /** @var string[]|null */ + #[Since('25.0.0')] public ?array $applicationScopeMappings = null, // #[Since('25.0.0')] // public ?ApplicationRepresentation $application = null, // #[Since('25.0.0')] // public ?OAuthClientRepresentation $oauthClients = null, // #[Since('25.0.0')] // public ?ClientTemplateRepresentation $clientTemplates = null, - #[Since('25.0.0')] - public ?int $oAuth2DeviceCodeLifespan = null, - #[Since('25.0.0')] - public ?int $oAuth2DevicePollingInterval = null, + #[Since('25.0.0')] public ?int $oAuth2DeviceCodeLifespan = null, + #[Since('25.0.0')] public ?int $oAuth2DevicePollingInterval = null, ) { } } diff --git a/src/Representation/Representation.php b/src/Representation/Representation.php index 69fa5ac..d98a35b 100644 --- a/src/Representation/Representation.php +++ b/src/Representation/Representation.php @@ -12,7 +12,7 @@ abstract class Representation implements \JsonSerializable abstract public function __construct(); /** - * @param array $properties + * @param array $properties * @return static * @throws PropertyDoesNotExistException */ @@ -33,13 +33,13 @@ final public static function from(array $properties): static */ public static function fromJson(string $json): static { - return static::from((new JsonEncoder())->decode($json, JsonEncoder::FORMAT)); + return static::from(new JsonEncoder()->decode($json, JsonEncoder::FORMAT)); } final public function jsonSerialize(): array { $serializable = []; - $reflectedClass = (new \ReflectionClass($this)); + $reflectedClass = new \ReflectionClass($this); $properties = $reflectedClass->getProperties(\ReflectionProperty::IS_PUBLIC); foreach ($properties as $property) { $serializable[$property->getName()] = ($property->getValue($this) instanceof \JsonSerializable) diff --git a/src/Representation/Type/Map.php b/src/Representation/Type/Map.php index 637b97f..4adf8a7 100644 --- a/src/Representation/Type/Map.php +++ b/src/Representation/Type/Map.php @@ -9,9 +9,9 @@ /** * @template T * - * @implements \JsonSerializable + * @implements \IteratorAggregate */ -class Map extends Type implements \Countable, \IteratorAggregate +class Map extends Type implements \Countable, \IteratorAggregate, \JsonSerializable { /** * @param array $data @@ -56,6 +56,10 @@ public function get(string $key): mixed return $this->data[$key]; } + /** + * @param T $value + * @return Map + */ public function with(string $key, mixed $value): self { $clone = clone $this; @@ -64,6 +68,9 @@ public function with(string $key, mixed $value): self return $clone; } + /** + * @return Map + */ public function without(string $key): self { $clone = clone $this; diff --git a/src/Representation/UPAttribute.php b/src/Representation/UPAttribute.php index 810a979..66cbc90 100644 --- a/src/Representation/UPAttribute.php +++ b/src/Representation/UPAttribute.php @@ -4,7 +4,6 @@ namespace Mainick\KeycloakClientBundle\Representation; -use Mainick\KeycloakClientBundle\Representation\Representation; use Mainick\KeycloakClientBundle\Representation\Type\Map; final class UPAttribute extends Representation @@ -19,6 +18,6 @@ public function __construct( public ?UPAttributeSelector $selector = null, public ?string $group = null, public ?bool $multivalued = null, - ){ + ) { } } diff --git a/src/Representation/UPConfig.php b/src/Representation/UPConfig.php index 7c66089..cbff228 100644 --- a/src/Representation/UPConfig.php +++ b/src/Representation/UPConfig.php @@ -6,7 +6,6 @@ use Mainick\KeycloakClientBundle\Representation\Collection\UPAttributeCollection; use Mainick\KeycloakClientBundle\Representation\Collection\UPGroupCollection; -use Mainick\KeycloakClientBundle\Representation\Representation; final class UPConfig extends Representation { diff --git a/src/Representation/UPGroup.php b/src/Representation/UPGroup.php index bf827a8..1d22aa5 100644 --- a/src/Representation/UPGroup.php +++ b/src/Representation/UPGroup.php @@ -4,7 +4,6 @@ namespace Mainick\KeycloakClientBundle\Representation; -use Mainick\KeycloakClientBundle\Representation\Representation; use Mainick\KeycloakClientBundle\Representation\Type\Map; final class UPGroup extends Representation diff --git a/src/Representation/UserConsentRepresentation.php b/src/Representation/UserConsentRepresentation.php index 53d8357..492d9ad 100644 --- a/src/Representation/UserConsentRepresentation.php +++ b/src/Representation/UserConsentRepresentation.php @@ -4,8 +4,6 @@ namespace Mainick\KeycloakClientBundle\Representation; -use Mainick\KeycloakClientBundle\Representation\Representation; - class UserConsentRepresentation extends Representation { public function __construct( diff --git a/src/Representation/UserProfileAttributeGroupMetadata.php b/src/Representation/UserProfileAttributeGroupMetadata.php index 4f39ac0..b964194 100644 --- a/src/Representation/UserProfileAttributeGroupMetadata.php +++ b/src/Representation/UserProfileAttributeGroupMetadata.php @@ -4,7 +4,6 @@ namespace Mainick\KeycloakClientBundle\Representation; -use Mainick\KeycloakClientBundle\Representation\Representation; use Mainick\KeycloakClientBundle\Representation\Type\Map; final class UserProfileAttributeGroupMetadata extends Representation diff --git a/src/Representation/UserProfileAttributeMetadata.php b/src/Representation/UserProfileAttributeMetadata.php index a53aab7..57e53cc 100644 --- a/src/Representation/UserProfileAttributeMetadata.php +++ b/src/Representation/UserProfileAttributeMetadata.php @@ -4,7 +4,6 @@ namespace Mainick\KeycloakClientBundle\Representation; -use Mainick\KeycloakClientBundle\Representation\Representation; use Mainick\KeycloakClientBundle\Representation\Type\Map; final class UserProfileAttributeMetadata extends Representation diff --git a/src/Representation/UserProfileMetadata.php b/src/Representation/UserProfileMetadata.php index f99ce87..7e924b5 100644 --- a/src/Representation/UserProfileMetadata.php +++ b/src/Representation/UserProfileMetadata.php @@ -6,7 +6,6 @@ use Mainick\KeycloakClientBundle\Representation\Collection\UserProfileAttributeGroupMetadataCollection; use Mainick\KeycloakClientBundle\Representation\Collection\UserProfileAttributeMetadataCollection; -use Mainick\KeycloakClientBundle\Representation\Representation; final class UserProfileMetadata extends Representation { diff --git a/src/Representation/UserSessionRepresentation.php b/src/Representation/UserSessionRepresentation.php index 28ae878..2979111 100644 --- a/src/Representation/UserSessionRepresentation.php +++ b/src/Representation/UserSessionRepresentation.php @@ -4,7 +4,6 @@ namespace Mainick\KeycloakClientBundle\Representation; -use Mainick\KeycloakClientBundle\Representation\Representation; use Mainick\KeycloakClientBundle\Representation\Type\Map; final class UserSessionRepresentation extends Representation diff --git a/src/Security/Authenticator/KeycloakAuthenticator.php b/src/Security/Authenticator/KeycloakAuthenticator.php index a70a629..5655079 100644 --- a/src/Security/Authenticator/KeycloakAuthenticator.php +++ b/src/Security/Authenticator/KeycloakAuthenticator.php @@ -53,8 +53,11 @@ public function authenticate(Request $request): Passport catch (IdentityProviderException $e) { throw new AuthenticationException(sprintf('Error authenticating code grant (%s)', $e->getMessage()), previous: $e); } + catch (ClientException $e) { + throw new AuthenticationException(sprintf('Bad status code returned by openID server (%s)', $e->getResponse()->getStatusCode()), previous: $e); + } catch (\Exception $e) { - throw new AuthenticationException(sprintf('Bad status code returned by openID server (%s)', $e->getStatusCode()), previous: $e); + throw new AuthenticationException(sprintf('Unexpected error occurred (%s)', $e->getMessage()), previous: $e); } if (!$accessToken || !$accessToken->getToken()) { @@ -81,10 +84,10 @@ public function onAuthenticationSuccess(Request $request, TokenInterface $token, public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response { - $request->getSession()->getBag('flashes')->add( - 'error', - 'An authentication error occured', - ); + $errors = [ + 'error' => 'An authentication error occured', + ]; + $request->getSession()->getBag('flashes')->clear()->initialize($errors); // $message = strtr($exception->getMessageKey(), $exception->getMessageData()); return new Response('Authentication failed', Response::HTTP_FORBIDDEN); diff --git a/src/Security/EntryPoint/KeycloakAuthenticationEntryPoint.php b/src/Security/EntryPoint/KeycloakAuthenticationEntryPoint.php index c6695a4..0a3b191 100644 --- a/src/Security/EntryPoint/KeycloakAuthenticationEntryPoint.php +++ b/src/Security/EntryPoint/KeycloakAuthenticationEntryPoint.php @@ -39,10 +39,13 @@ public function start(Request $request, ?AuthenticationException $authException if ($request->hasSession()) { $request->getSession()->set(KeycloakAuthorizationCodeEnum::LOGIN_REFERRER, $request->getUri()); - $request->getSession()->getBag('flashes')->add( - 'info', - 'Please log in to access this page', - ); + $info = [ + 'info' => 'Please log in to access this page', + ]; + + $flashes = $request->getSession()->getBag('flashes'); + $flashes->clear(); + $flashes->initialize($info); } $this->keycloakClientLogger?->info('KeycloakAuthenticationEntryPoint::start', [ diff --git a/src/Security/User/KeycloakUserProvider.php b/src/Security/User/KeycloakUserProvider.php index fccaac9..872e6f6 100644 --- a/src/Security/User/KeycloakUserProvider.php +++ b/src/Security/User/KeycloakUserProvider.php @@ -14,11 +14,14 @@ use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; -class KeycloakUserProvider implements UserProviderInterface +/** + * @implements UserProviderInterface + */ +readonly class KeycloakUserProvider implements UserProviderInterface { public function __construct( - private readonly LoggerInterface $keycloakClientLogger, - private readonly IamClientInterface $iamClient + private LoggerInterface $keycloakClientLogger, + private IamClientInterface $iamClient ) { } @@ -63,7 +66,7 @@ public function supportsClass(string $class): bool return KeycloakResourceOwner::class === $class; } - public function loadUserByIdentifier($identifier): UserInterface + public function loadUserByIdentifier(mixed $identifier): UserInterface { if (!$identifier instanceof AccessTokenInterface) { throw new \LogicException('Could not load a KeycloakUser without an AccessToken.'); diff --git a/src/Serializer/AttributeNormalizer.php b/src/Serializer/AttributeNormalizer.php index c748c5e..632edbf 100644 --- a/src/Serializer/AttributeNormalizer.php +++ b/src/Serializer/AttributeNormalizer.php @@ -70,7 +70,7 @@ private function getFilteredProperties(Representation $representation): array } $filteredProperties = []; - $properties = (new \ReflectionClass($representation))->getProperties(); + $properties = new \ReflectionClass($representation)->getProperties(); foreach ($properties as $property) { $sinceAttribute = $property->getAttributes(Since::class); foreach ($sinceAttribute as $since) { diff --git a/src/Serializer/RepresentationDenormalizer.php b/src/Serializer/RepresentationDenormalizer.php index 125b878..3fd56be 100644 --- a/src/Serializer/RepresentationDenormalizer.php +++ b/src/Serializer/RepresentationDenormalizer.php @@ -7,7 +7,6 @@ use Mainick\KeycloakClientBundle\Representation\Collection\Collection; use Mainick\KeycloakClientBundle\Representation\Representation; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; -use Symfony\Component\Serializer\SerializerInterface; final readonly class RepresentationDenormalizer implements DenormalizerInterface { diff --git a/src/Service/Service.php b/src/Service/Service.php index 6dbc99e..0c30eb0 100644 --- a/src/Service/Service.php +++ b/src/Service/Service.php @@ -121,7 +121,7 @@ private function getQueryParams(?Criteria $criteria): string return '?' . http_build_query($criteria->jsonSerialize()); } - private function isSuccessful($statusCode): bool + private function isSuccessful(int $statusCode): bool { return ($statusCode >= Response::HTTP_OK && $statusCode < Response::HTTP_MULTIPLE_CHOICES) || $statusCode === Response::HTTP_NOT_MODIFIED; } diff --git a/src/Token/HS256TokenDecoder.php b/src/Token/HS256TokenDecoder.php index f1521d9..7224f75 100644 --- a/src/Token/HS256TokenDecoder.php +++ b/src/Token/HS256TokenDecoder.php @@ -20,7 +20,8 @@ public function decode(string $token, string $key): array $json = json_encode($tokenDecoded, JSON_THROW_ON_ERROR); return json_decode($json, true, 512, JSON_THROW_ON_ERROR); - } catch (\Exception $e) { + } + catch (\Exception $e) { throw new TokenDecoderException('Error decoding token', $e); } } diff --git a/src/Token/JWKSTokenDecoder.php b/src/Token/JWKSTokenDecoder.php index d6f3b6c..4ddfa02 100644 --- a/src/Token/JWKSTokenDecoder.php +++ b/src/Token/JWKSTokenDecoder.php @@ -14,6 +14,9 @@ final readonly class JWKSTokenDecoder implements TokenDecoderInterface { + /** + * @throws TokenDecoderException + */ public function __construct( private ClientInterface $httpClient, private array $options @@ -21,7 +24,7 @@ public function __construct( { foreach ($options as $allowOption => $value) { if (!\in_array($allowOption, ['base_url', 'realm', 'alg', 'http_timeout', 'http_connect_timeout', 'allowed_jwks_domains'], true)) { - throw TokenDecoderException::forInvalidConfiguration(\sprintf( + throw TokenDecoderException::forInvalidConfiguration(sprintf( "Unknown option '%s' for %s", $allowOption, self::class @@ -31,7 +34,7 @@ public function __construct( foreach (['base_url', 'realm'] as $requiredOption) { if (!\array_key_exists($requiredOption, $this->options) || $this->options[$requiredOption] === null || $this->options[$requiredOption] === '') { - throw TokenDecoderException::forInvalidConfiguration(\sprintf( + throw TokenDecoderException::forInvalidConfiguration(sprintf( "Missing or empty required option '%s' for %s", $requiredOption, self::class @@ -62,7 +65,7 @@ public function decode(string $token, string $key): array { try { $parts = explode('.', $token); - if (\count($parts) !== 3 || $parts[0] === '' || $parts[1] === '' || $parts[2] === '') { + if (count($parts) !== 3 || $parts[0] === '' || $parts[1] === '' || $parts[2] === '') { throw TokenDecoderException::forDecodingError( 'Invalid JWT format: token must consist of header.payload.signature', new \Exception('invalid token format') @@ -153,6 +156,9 @@ private function getKeyForKid(string $kid, string $algorithm): Key return $keys[$kid]; } + /** + * @throws TokenDecoderException + */ private function fetchJwks(): array { $timeout = $this->options['http_timeout'] ?? 10; @@ -179,17 +185,20 @@ private function fetchJwks(): array $data = json_decode($json, true, 512, JSON_THROW_ON_ERROR); return $data; - } catch (GuzzleException $e) { + } + catch (GuzzleException $e) { throw TokenDecoderException::forJwksError( sprintf('Failed to fetch JWKS from %s: %s', $url, $e->getMessage()), $e ); - } catch (\JsonException $e) { + } + catch (\JsonException $e) { throw TokenDecoderException::forJwksError( sprintf('Invalid JSON response from JWKS endpoint: %s', $e->getMessage()), $e ); - } catch (\Exception $e) { + } + catch (\Exception $e) { throw TokenDecoderException::forJwksError( sprintf('Unable to retrieve JWKS: %s', $e->getMessage()), $e @@ -285,7 +294,8 @@ private function validateJwksUrl(string $url): void $isAllowed = true; break; } - } elseif ($host === $allowedDomain) { + } + elseif ($host === $allowedDomain) { $isAllowed = true; break; } diff --git a/src/Token/KeycloakResourceOwner.php b/src/Token/KeycloakResourceOwner.php index ab60e78..d142323 100644 --- a/src/Token/KeycloakResourceOwner.php +++ b/src/Token/KeycloakResourceOwner.php @@ -12,6 +12,8 @@ class KeycloakResourceOwner implements ResourceOwnerInterface, UserInterface { /** * Raw response. + * + * @param array $response */ protected array $response; @@ -19,6 +21,8 @@ class KeycloakResourceOwner implements ResourceOwnerInterface, UserInterface /** * Creates new resource owner. + * + * @param array $response The raw response from the token decoder */ public function __construct(array $response = [], ?AccessTokenInterface $accessToken = null) { @@ -109,7 +113,7 @@ private function getClientRoles(?string $client_id = null): array $resource_access, static fn(array $carry, array $client): array => [ ...$carry, - ...($client['roles'] ?? []) + ...$client['roles'] ?? [] ], [] ); diff --git a/tests/EventSubscriber/TokenAuthListenerTest.php b/tests/EventSubscriber/TokenAuthListenerTest.php index b8fdd55..13092a9 100644 --- a/tests/EventSubscriber/TokenAuthListenerTest.php +++ b/tests/EventSubscriber/TokenAuthListenerTest.php @@ -11,7 +11,6 @@ use Mainick\KeycloakClientBundle\EventSubscriber\TokenAuthListener; use Mainick\KeycloakClientBundle\Interface\IamClientInterface; use Mainick\KeycloakClientBundle\Provider\KeycloakClient; -use Mockery as m; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\StreamInterface; @@ -101,7 +100,7 @@ protected function setUp(): void { parent::setUp(); $this->keycloakClient = new KeycloakClient( - $this->createMock(LoggerInterface::class), + $this->createStub(LoggerInterface::class), true, 'http://mock.url/auth', 'mock_realm', @@ -114,59 +113,54 @@ protected function setUp(): void $this->access_token = JWT::encode(json_decode($jwt_tmp, true), self::ENCRYPTION_KEY, self::ENCRYPTION_ALGORITHM); } - protected function tearDown(): void - { - m::close(); - parent::tearDown(); - } - public function testCheckValidTokenOnRequest(): void { // given // mock access token - $getAccessTokenStream = $this->createMock(StreamInterface::class); + $getAccessTokenStream = $this->createStub(StreamInterface::class); $getAccessTokenStream ->method('__toString') ->willReturn('{"access_token":"'.$this->access_token.'","expires_in":3600,"refresh_token":"mock_refresh_token","scope":"email","token_type":"bearer"}'); - $getAccessTokenResponse = m::mock(ResponseInterface::class); + + $getAccessTokenResponse = $this->createStub(ResponseInterface::class); $getAccessTokenResponse - ->allows('getBody') - ->andReturns($getAccessTokenStream); + ->method('getBody') + ->willReturn($getAccessTokenStream); $getAccessTokenResponse - ->allows('getHeader') - ->andReturns(['content-type' => 'application/json']); + ->method('getHeader') + ->willReturn(['content-type' => 'application/json']); // mock resource owner $jwt_tmp = sprintf($this->jwtTemplate, time() + 3600, time(), time()); - $getResourceOwnerStream = $this->createMock(StreamInterface::class); + $getResourceOwnerStream = $this->createStub(StreamInterface::class); $getResourceOwnerStream ->method('__toString') ->willReturn($jwt_tmp); - $getResourceOwnerResponse = m::mock(ResponseInterface::class); + $getResourceOwnerResponse = $this->createStub(ResponseInterface::class); $getResourceOwnerResponse - ->allows('getBody') - ->andReturns($getResourceOwnerStream); + ->method('getBody') + ->willReturn($getResourceOwnerStream); $getResourceOwnerResponse - ->allows('getHeader') - ->andReturns(['content-type' => 'application/json']); + ->method('getHeader') + ->willReturn(['content-type' => 'application/json']); // mock http client - $client = m::mock(ClientInterface::class); + $client = $this->createStub(ClientInterface::class); $client - ->allows('send') - ->andReturns($getAccessTokenResponse, $getResourceOwnerResponse); + ->method('send') + ->willReturn($getAccessTokenResponse, $getResourceOwnerResponse); $this->keycloakClient->setHttpClient($client); // when $token = $this->keycloakClient->authenticate('mock_user', 'mock_password'); // mock event request - $logger = $this->createMock(LoggerInterface::class); + $logger = $this->createStub(LoggerInterface::class); $tokenAuthListener = new TokenAuthListener($logger, $this->keycloakClient); $request = new Request(); $request->headers->set('X-Auth-Token', $token->getToken()); $eventRequest = new RequestEvent( - $this->createMock(HttpKernelInterface::class), + $this->createStub(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST ); @@ -182,8 +176,8 @@ public function testCheckValidTokenOnRequest(): void public function testCheckValidTokenExcludesRouteWithAttribute(): void { // given - $logger = $this->createMock(LoggerInterface::class); - $iamClient = $this->createMock(IamClientInterface::class); + $logger = $this->createStub(LoggerInterface::class); + $iamClient = $this->createStub(IamClientInterface::class); $tokenAuthListener = new TokenAuthListener($logger, $iamClient); // when @@ -197,7 +191,7 @@ public function testCheckValidTokenExcludesRouteWithAttribute(): void // Mock the Event $eventRequest = new RequestEvent( - $this->createMock(HttpKernelInterface::class), + $this->createStub(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST ); diff --git a/tests/Provider/KeycloakClientTest.php b/tests/Provider/KeycloakClientTest.php index ba05ef0..233a3b6 100644 --- a/tests/Provider/KeycloakClientTest.php +++ b/tests/Provider/KeycloakClientTest.php @@ -8,7 +8,6 @@ use GuzzleHttp\ClientInterface; use League\OAuth2\Client\Tool\QueryBuilderTrait; use Mainick\KeycloakClientBundle\Provider\KeycloakClient; -use Mockery as m; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\StreamInterface; @@ -86,7 +85,7 @@ protected function setUp(): void { parent::setUp(); $this->keycloakClient = new KeycloakClient( - $this->createMock(LoggerInterface::class), + $this->createStub(LoggerInterface::class), true, 'http://mock.url/auth', 'mock_realm', @@ -101,33 +100,27 @@ protected function setUp(): void $this->access_token = JWT::encode(json_decode($jwt_tmp, true), self::ENCRYPTION_KEY, self::ENCRYPTION_ALGORITHM); } - protected function tearDown(): void - { - m::close(); - parent::tearDown(); - } - public function testRefreshToken(): void { // given - $getAccessTokenStream = $this->createMock(StreamInterface::class); + $getAccessTokenStream = $this->createStub(StreamInterface::class); $getAccessTokenStream ->method('__toString') ->willReturn('{"access_token":"'.$this->access_token.'","expires_in":3600,"refresh_token":"mock_refresh_token","scope":"email","token_type":"bearer"}'); - $getAccessTokenResponse = m::mock(ResponseInterface::class); + $getAccessTokenResponse = $this->createStub(ResponseInterface::class); $getAccessTokenResponse - ->allows('getBody') - ->andReturns($getAccessTokenStream); + ->method('getBody') + ->willReturn($getAccessTokenStream); $getAccessTokenResponse - ->allows('getHeader') - ->andReturns(['content-type' => 'application/json']); + ->method('getHeader') + ->willReturn(['content-type' => 'application/json']); + + $client = $this->createMock(ClientInterface::class); + $client->expects($this->exactly(2)) + ->method('send') + ->willReturn($getAccessTokenResponse); - $client = m::mock(ClientInterface::class); - $client - ->expects('send') - ->times(2) - ->andReturns($getAccessTokenResponse); $this->keycloakClient->setHttpClient($client); // when @@ -142,24 +135,24 @@ public function testRefreshToken(): void public function testVerifyToken(): void { // given - $getAccessTokenStream = $this->createMock(StreamInterface::class); + $getAccessTokenStream = $this->createStub(StreamInterface::class); $getAccessTokenStream ->method('__toString') ->willReturn('{"access_token":"'.$this->access_token.'","expires_in":3600,"refresh_token":"mock_refresh_token","scope":"email","token_type":"bearer"}'); - $getAccessTokenResponse = m::mock(ResponseInterface::class); + $getAccessTokenResponse = $this->createStub(ResponseInterface::class); $getAccessTokenResponse - ->allows('getBody') - ->andReturns($getAccessTokenStream); + ->method('getBody') + ->willReturn($getAccessTokenStream); $getAccessTokenResponse - ->allows('getHeader') - ->andReturns(['content-type' => 'application/json']); + ->method('getHeader') + ->willReturn(['content-type' => 'application/json']); + + $client = $this->createMock(ClientInterface::class); + $client->expects($this->once()) + ->method('send') + ->willReturn($getAccessTokenResponse); - $client = m::mock(ClientInterface::class); - $client - ->expects('send') - ->times(1) - ->andReturns($getAccessTokenResponse); $this->keycloakClient->setHttpClient($client); // when @@ -175,37 +168,38 @@ public function testVerifyToken(): void public function testUserInfo(): void { // given - $getAccessTokenStream = $this->createMock(StreamInterface::class); + $getAccessTokenStream = $this->createStub(StreamInterface::class); $getAccessTokenStream ->method('__toString') ->willReturn('{"access_token":"'.$this->access_token.'","expires_in":3600,"refresh_token":"mock_refresh_token","scope":"email","token_type":"bearer"}'); - $getAccessTokenResponse = m::mock(ResponseInterface::class); + $getAccessTokenResponse = $this->createStub(ResponseInterface::class); $getAccessTokenResponse - ->allows('getBody') - ->andReturns($getAccessTokenStream); + ->method('getBody') + ->willReturn($getAccessTokenStream); $getAccessTokenResponse - ->allows('getHeader') - ->andReturns(['content-type' => 'application/json']); + ->method('getHeader') + ->willReturn(['content-type' => 'application/json']); $jwt_tmp = sprintf($this->jwtTemplate, time() + 3600, time(), time()); - $getResourceOwnerStream = $this->createMock(StreamInterface::class); + $getResourceOwnerStream = $this->createStub(StreamInterface::class); $getResourceOwnerStream ->method('__toString') ->willReturn($jwt_tmp); - $getResourceOwnerResponse = m::mock(ResponseInterface::class); + $getResourceOwnerResponse = $this->createStub(ResponseInterface::class); $getResourceOwnerResponse - ->allows('getBody') - ->andReturns($getResourceOwnerStream); + ->method('getBody') + ->willReturn($getResourceOwnerStream); $getResourceOwnerResponse - ->allows('getHeader') - ->andReturns(['content-type' => 'application/json']); + ->method('getHeader') + ->willReturn(['content-type' => 'application/json']); - $client = m::mock(ClientInterface::class); + $client = $this->createStub(ClientInterface::class); $client - ->allows('send') - ->andReturns($getAccessTokenResponse, $getResourceOwnerResponse); + ->method('send') + ->willReturn($getAccessTokenResponse, $getResourceOwnerResponse); + $this->keycloakClient->setHttpClient($client); // when @@ -222,24 +216,24 @@ public function testUserInfo(): void public function testAuthenticate(): void { // given - $getAccessTokenStream = $this->createMock(StreamInterface::class); + $getAccessTokenStream = $this->createStub(StreamInterface::class); $getAccessTokenStream ->method('__toString') ->willReturn('{"access_token":"'.$this->access_token.'","expires_in":3600,"refresh_token":"mock_refresh_token","scope":"email","token_type":"bearer"}'); - $getAccessTokenResponse = m::mock(ResponseInterface::class); + $getAccessTokenResponse = $this->createStub(ResponseInterface::class); $getAccessTokenResponse - ->allows('getBody') - ->andReturns($getAccessTokenStream); + ->method('getBody') + ->willReturn($getAccessTokenStream); $getAccessTokenResponse - ->allows('getHeader') - ->andReturns(['content-type' => 'application/json']); + ->method('getHeader') + ->willReturn(['content-type' => 'application/json']); + + $client = $this->createMock(ClientInterface::class); + $client->expects($this->once()) + ->method('send') + ->willReturn($getAccessTokenResponse); - $client = m::mock(ClientInterface::class); - $client - ->expects('send') - ->times(1) - ->andReturns($getAccessTokenResponse); $this->keycloakClient->setHttpClient($client); // when @@ -256,24 +250,24 @@ public function testAuthenticate(): void public function testAuthenticateByCode() { // given - $getAccessTokenStream = $this->createMock(StreamInterface::class); + $getAccessTokenStream = $this->createStub(StreamInterface::class); $getAccessTokenStream ->method('__toString') ->willReturn('{"access_token":"'.$this->access_token.'","expires_in":3600,"refresh_token":"mock_refresh_token","scope":"email","token_type":"bearer"}'); - $getAccessTokenResponse = m::mock(ResponseInterface::class); + $getAccessTokenResponse = $this->createStub(ResponseInterface::class); $getAccessTokenResponse - ->allows('getBody') - ->andReturns($getAccessTokenStream); + ->method('getBody') + ->willReturn($getAccessTokenStream); $getAccessTokenResponse - ->allows('getHeader') - ->andReturns(['content-type' => 'application/json']); + ->method('getHeader') + ->willReturn(['content-type' => 'application/json']); + + $client = $this->createMock(ClientInterface::class); + $client->expects($this->once()) + ->method('send') + ->willReturn($getAccessTokenResponse); - $client = m::mock(ClientInterface::class); - $client - ->expects('send') - ->times(1) - ->andReturns($getAccessTokenResponse); $this->keycloakClient->setHttpClient($client); // when @@ -290,24 +284,24 @@ public function testAuthenticateByCode() public function testGetRolesUser(): void { // given - $getAccessTokenStream = $this->createMock(StreamInterface::class); + $getAccessTokenStream = $this->createStub(StreamInterface::class); $getAccessTokenStream ->method('__toString') ->willReturn('{"access_token":"'.$this->access_token.'","expires_in":3600,"refresh_token":"mock_refresh_token","scope":"email","token_type":"bearer"}'); - $getAccessTokenResponse = m::mock(ResponseInterface::class); + $getAccessTokenResponse = $this->createStub(ResponseInterface::class); $getAccessTokenResponse - ->allows('getBody') - ->andReturns($getAccessTokenStream); + ->method('getBody') + ->willReturn($getAccessTokenStream); $getAccessTokenResponse - ->allows('getHeader') - ->andReturns(['content-type' => 'application/json']); + ->method('getHeader') + ->willReturn(['content-type' => 'application/json']); + + $client = $this->createMock(ClientInterface::class); + $client->expects($this->once()) + ->method('send') + ->willReturn($getAccessTokenResponse); - $client = m::mock(ClientInterface::class); - $client - ->expects('send') - ->times(1) - ->andReturns($getAccessTokenResponse); $this->keycloakClient->setHttpClient($client); // when @@ -323,24 +317,24 @@ public function testGetRolesUser(): void public function testHasRoleInUserSOnes(): void { // given - $getAccessTokenStream = $this->createMock(StreamInterface::class); + $getAccessTokenStream = $this->createStub(StreamInterface::class); $getAccessTokenStream ->method('__toString') ->willReturn('{"access_token":"'.$this->access_token.'","expires_in":3600,"refresh_token":"mock_refresh_token","scope":"email","token_type":"bearer"}'); - $getAccessTokenResponse = m::mock(ResponseInterface::class); + $getAccessTokenResponse = $this->createStub(ResponseInterface::class); $getAccessTokenResponse - ->allows('getBody') - ->andReturns($getAccessTokenStream); + ->method('getBody') + ->willReturn($getAccessTokenStream); $getAccessTokenResponse - ->allows('getHeader') - ->andReturns(['content-type' => 'application/json']); + ->method('getHeader') + ->willReturn(['content-type' => 'application/json']); + + $client = $this->createMock(ClientInterface::class); + $client->expects($this->once()) + ->method('send') + ->willReturn($getAccessTokenResponse); - $client = m::mock(ClientInterface::class); - $client - ->expects('send') - ->times(1) - ->andReturns($getAccessTokenResponse); $this->keycloakClient->setHttpClient($client); // when @@ -354,24 +348,24 @@ public function testHasRoleInUserSOnes(): void public function testHasAnyRoleInUserSOnes(): void { // given - $getAccessTokenStream = $this->createMock(StreamInterface::class); + $getAccessTokenStream = $this->createStub(StreamInterface::class); $getAccessTokenStream ->method('__toString') ->willReturn('{"access_token":"'.$this->access_token.'","expires_in":3600,"refresh_token":"mock_refresh_token","scope":"email","token_type":"bearer"}'); - $getAccessTokenResponse = m::mock(ResponseInterface::class); + $getAccessTokenResponse = $this->createStub(ResponseInterface::class); $getAccessTokenResponse - ->allows('getBody') - ->andReturns($getAccessTokenStream); + ->method('getBody') + ->willReturn($getAccessTokenStream); $getAccessTokenResponse - ->allows('getHeader') - ->andReturns(['content-type' => 'application/json']); + ->method('getHeader') + ->willReturn(['content-type' => 'application/json']); + + $client = $this->createMock(ClientInterface::class); + $client->expects($this->once()) + ->method('send') + ->willReturn($getAccessTokenResponse); - $client = m::mock(ClientInterface::class); - $client - ->expects('send') - ->times(1) - ->andReturns($getAccessTokenResponse); $this->keycloakClient->setHttpClient($client); // when @@ -385,24 +379,24 @@ public function testHasAnyRoleInUserSOnes(): void public function testHasAllRolesInUserSOnes(): void { // given - $getAccessTokenStream = $this->createMock(StreamInterface::class); + $getAccessTokenStream = $this->createStub(StreamInterface::class); $getAccessTokenStream ->method('__toString') ->willReturn('{"access_token":"'.$this->access_token.'","expires_in":3600,"refresh_token":"mock_refresh_token","scope":"email","token_type":"bearer"}'); - $getAccessTokenResponse = m::mock(ResponseInterface::class); + $getAccessTokenResponse = $this->createStub(ResponseInterface::class); $getAccessTokenResponse - ->allows('getBody') - ->andReturns($getAccessTokenStream); + ->method('getBody') + ->willReturn($getAccessTokenStream); $getAccessTokenResponse - ->allows('getHeader') - ->andReturns(['content-type' => 'application/json']); + ->method('getHeader') + ->willReturn(['content-type' => 'application/json']); + + $client = $this->createMock(ClientInterface::class); + $client->expects($this->once()) + ->method('send') + ->willReturn($getAccessTokenResponse); - $client = m::mock(ClientInterface::class); - $client - ->expects('send') - ->times(1) - ->andReturns($getAccessTokenResponse); $this->keycloakClient->setHttpClient($client); // when @@ -416,24 +410,24 @@ public function testHasAllRolesInUserSOnes(): void public function testGetGroupsUser(): void { // given - $getAccessTokenStream = $this->createMock(StreamInterface::class); + $getAccessTokenStream = $this->createStub(StreamInterface::class); $getAccessTokenStream ->method('__toString') ->willReturn('{"access_token":"'.$this->access_token.'","expires_in":3600,"refresh_token":"mock_refresh_token","scope":"email","token_type":"bearer"}'); - $getAccessTokenResponse = m::mock(ResponseInterface::class); + $getAccessTokenResponse = $this->createStub(ResponseInterface::class); $getAccessTokenResponse - ->allows('getBody') - ->andReturns($getAccessTokenStream); + ->method('getBody') + ->willReturn($getAccessTokenStream); $getAccessTokenResponse - ->allows('getHeader') - ->andReturns(['content-type' => 'application/json']); + ->method('getHeader') + ->willReturn(['content-type' => 'application/json']); + + $client = $this->createMock(ClientInterface::class); + $client->expects($this->once()) + ->method('send') + ->willReturn($getAccessTokenResponse); - $client = m::mock(ClientInterface::class); - $client - ->expects('send') - ->times(1) - ->andReturns($getAccessTokenResponse); $this->keycloakClient->setHttpClient($client); // when @@ -449,24 +443,24 @@ public function testGetGroupsUser(): void public function testHasGroupInUserSOnes(): void { // given - $getAccessTokenStream = $this->createMock(StreamInterface::class); + $getAccessTokenStream = $this->createStub(StreamInterface::class); $getAccessTokenStream ->method('__toString') ->willReturn('{"access_token":"'.$this->access_token.'","expires_in":3600,"refresh_token":"mock_refresh_token","scope":"email","token_type":"bearer"}'); - $getAccessTokenResponse = m::mock(ResponseInterface::class); + $getAccessTokenResponse = $this->createStub(ResponseInterface::class); $getAccessTokenResponse - ->allows('getBody') - ->andReturns($getAccessTokenStream); + ->method('getBody') + ->willReturn($getAccessTokenStream); $getAccessTokenResponse - ->allows('getHeader') - ->andReturns(['content-type' => 'application/json']); + ->method('getHeader') + ->willReturn(['content-type' => 'application/json']); + + $client = $this->createMock(ClientInterface::class); + $client->expects($this->once()) + ->method('send') + ->willReturn($getAccessTokenResponse); - $client = m::mock(ClientInterface::class); - $client - ->expects('send') - ->times(1) - ->andReturns($getAccessTokenResponse); $this->keycloakClient->setHttpClient($client); // when @@ -480,24 +474,24 @@ public function testHasGroupInUserSOnes(): void public function testHasAnyGroupInUserSOnes(): void { // given - $getAccessTokenStream = $this->createMock(StreamInterface::class); + $getAccessTokenStream = $this->createStub(StreamInterface::class); $getAccessTokenStream ->method('__toString') ->willReturn('{"access_token":"'.$this->access_token.'","expires_in":3600,"refresh_token":"mock_refresh_token","scope":"email","token_type":"bearer"}'); - $getAccessTokenResponse = m::mock(ResponseInterface::class); + $getAccessTokenResponse = $this->createStub(ResponseInterface::class); $getAccessTokenResponse - ->allows('getBody') - ->andReturns($getAccessTokenStream); + ->method('getBody') + ->willReturn($getAccessTokenStream); $getAccessTokenResponse - ->allows('getHeader') - ->andReturns(['content-type' => 'application/json']); + ->method('getHeader') + ->willReturn(['content-type' => 'application/json']); + + $client = $this->createMock(ClientInterface::class); + $client->expects($this->once()) + ->method('send') + ->willReturn($getAccessTokenResponse); - $client = m::mock(ClientInterface::class); - $client - ->expects('send') - ->times(1) - ->andReturns($getAccessTokenResponse); $this->keycloakClient->setHttpClient($client); // when @@ -511,24 +505,24 @@ public function testHasAnyGroupInUserSOnes(): void public function testHasAllGroupsInUserSOnes(): void { // given - $getAccessTokenStream = $this->createMock(StreamInterface::class); + $getAccessTokenStream = $this->createStub(StreamInterface::class); $getAccessTokenStream ->method('__toString') ->willReturn('{"access_token":"'.$this->access_token.'","expires_in":3600,"refresh_token":"mock_refresh_token","scope":"email","token_type":"bearer"}'); - $getAccessTokenResponse = m::mock(ResponseInterface::class); + $getAccessTokenResponse = $this->createStub(ResponseInterface::class); $getAccessTokenResponse - ->allows('getBody') - ->andReturns($getAccessTokenStream); + ->method('getBody') + ->willReturn($getAccessTokenStream); $getAccessTokenResponse - ->allows('getHeader') - ->andReturns(['content-type' => 'application/json']); + ->method('getHeader') + ->willReturn(['content-type' => 'application/json']); + + $client = $this->createMock(ClientInterface::class); + $client->expects($this->once()) + ->method('send') + ->willReturn($getAccessTokenResponse); - $client = m::mock(ClientInterface::class); - $client - ->expects('send') - ->times(1) - ->andReturns($getAccessTokenResponse); $this->keycloakClient->setHttpClient($client); // when @@ -542,24 +536,24 @@ public function testHasAllGroupsInUserSOnes(): void public function testGetScopeUser(): void { // given - $getAccessTokenStream = $this->createMock(StreamInterface::class); + $getAccessTokenStream = $this->createStub(StreamInterface::class); $getAccessTokenStream ->method('__toString') ->willReturn('{"access_token":"'.$this->access_token.'","expires_in":3600,"refresh_token":"mock_refresh_token","scope":"email","token_type":"bearer"}'); - $getAccessTokenResponse = m::mock(ResponseInterface::class); + $getAccessTokenResponse = $this->createStub(ResponseInterface::class); $getAccessTokenResponse - ->allows('getBody') - ->andReturns($getAccessTokenStream); + ->method('getBody') + ->willReturn($getAccessTokenStream); $getAccessTokenResponse - ->allows('getHeader') - ->andReturns(['content-type' => 'application/json']); + ->method('getHeader') + ->willReturn(['content-type' => 'application/json']); + + $client = $this->createMock(ClientInterface::class); + $client->expects($this->once()) + ->method('send') + ->willReturn($getAccessTokenResponse); - $client = m::mock(ClientInterface::class); - $client - ->expects('send') - ->times(1) - ->andReturns($getAccessTokenResponse); $this->keycloakClient->setHttpClient($client); // when @@ -575,24 +569,24 @@ public function testGetScopeUser(): void public function testHasScopeInUserSOnes(): void { // given - $getAccessTokenStream = $this->createMock(StreamInterface::class); + $getAccessTokenStream = $this->createStub(StreamInterface::class); $getAccessTokenStream ->method('__toString') ->willReturn('{"access_token":"'.$this->access_token.'","expires_in":3600,"refresh_token":"mock_refresh_token","scope":"email","token_type":"bearer"}'); - $getAccessTokenResponse = m::mock(ResponseInterface::class); + $getAccessTokenResponse = $this->createStub(ResponseInterface::class); $getAccessTokenResponse - ->allows('getBody') - ->andReturns($getAccessTokenStream); + ->method('getBody') + ->willReturn($getAccessTokenStream); $getAccessTokenResponse - ->allows('getHeader') - ->andReturns(['content-type' => 'application/json']); + ->method('getHeader') + ->willReturn(['content-type' => 'application/json']); + + $client = $this->createMock(ClientInterface::class); + $client->expects($this->once()) + ->method('send') + ->willReturn($getAccessTokenResponse); - $client = m::mock(ClientInterface::class); - $client - ->expects('send') - ->times(1) - ->andReturns($getAccessTokenResponse); $this->keycloakClient->setHttpClient($client); // when @@ -606,24 +600,24 @@ public function testHasScopeInUserSOnes(): void public function testHasAnyScopeInUserSOnes(): void { // given - $getAccessTokenStream = $this->createMock(StreamInterface::class); + $getAccessTokenStream = $this->createStub(StreamInterface::class); $getAccessTokenStream ->method('__toString') ->willReturn('{"access_token":"'.$this->access_token.'","expires_in":3600,"refresh_token":"mock_refresh_token","scope":"email","token_type":"bearer"}'); - $getAccessTokenResponse = m::mock(ResponseInterface::class); + $getAccessTokenResponse = $this->createStub(ResponseInterface::class); $getAccessTokenResponse - ->allows('getBody') - ->andReturns($getAccessTokenStream); + ->method('getBody') + ->willReturn($getAccessTokenStream); $getAccessTokenResponse - ->allows('getHeader') - ->andReturns(['content-type' => 'application/json']); + ->method('getHeader') + ->willReturn(['content-type' => 'application/json']); + + $client = $this->createMock(ClientInterface::class); + $client->expects($this->once()) + ->method('send') + ->willReturn($getAccessTokenResponse); - $client = m::mock(ClientInterface::class); - $client - ->expects('send') - ->times(1) - ->andReturns($getAccessTokenResponse); $this->keycloakClient->setHttpClient($client); // when @@ -637,24 +631,24 @@ public function testHasAnyScopeInUserSOnes(): void public function testHasAllScopesInUserSOnes(): void { // given - $getAccessTokenStream = $this->createMock(StreamInterface::class); + $getAccessTokenStream = $this->createStub(StreamInterface::class); $getAccessTokenStream ->method('__toString') ->willReturn('{"access_token":"'.$this->access_token.'","expires_in":3600,"refresh_token":"mock_refresh_token","scope":"email","token_type":"bearer"}'); - $getAccessTokenResponse = m::mock(ResponseInterface::class); + $getAccessTokenResponse = $this->createStub(ResponseInterface::class); $getAccessTokenResponse - ->allows('getBody') - ->andReturns($getAccessTokenStream); + ->method('getBody') + ->willReturn($getAccessTokenStream); $getAccessTokenResponse - ->allows('getHeader') - ->andReturns(['content-type' => 'application/json']); + ->method('getHeader') + ->willReturn(['content-type' => 'application/json']); + + $client = $this->createMock(ClientInterface::class); + $client->expects($this->once()) + ->method('send') + ->willReturn($getAccessTokenResponse); - $client = m::mock(ClientInterface::class); - $client - ->expects('send') - ->times(1) - ->andReturns($getAccessTokenResponse); $this->keycloakClient->setHttpClient($client); // when diff --git a/tests/Security/KeycloakAuthenticatorTest.php b/tests/Security/KeycloakAuthenticatorTest.php index 52a8359..5ba130f 100644 --- a/tests/Security/KeycloakAuthenticatorTest.php +++ b/tests/Security/KeycloakAuthenticatorTest.php @@ -11,7 +11,6 @@ use Mainick\KeycloakClientBundle\Security\Authenticator\KeycloakAuthenticator; use Mainick\KeycloakClientBundle\Security\User\KeycloakUserProvider; use Mainick\KeycloakClientBundle\Token\KeycloakResourceOwner; -use Mockery as m; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Request; @@ -100,47 +99,40 @@ protected function setUp(): void $jwt_tmp = sprintf($this->jwtTemplate, time() + 3600, time(), time()); $this->access_token = JWT::encode(json_decode($jwt_tmp, true), self::ENCRYPTION_KEY, self::ENCRYPTION_ALGORITHM); - $this->iamClient = m::mock(KeycloakClient::class); - $accessToken = m::mock(AccessTokenInterface::class); + $this->iamClient = $this->createStub(KeycloakClient::class); + $accessToken = $this->createStub(AccessTokenInterface::class); $accessToken - ->allows('getToken') - ->andReturns($this->access_token); + ->method('getToken') + ->willReturn($this->access_token); $accessToken - ->allows('getRefreshToken') - ->andReturns('mock_refresh_token'); + ->method('getRefreshToken') + ->willReturn('mock_refresh_token'); $this->iamClient - ->allows('authenticateCodeGrant') - ->with('authorization_code') - ->andReturns($accessToken); + ->method('authenticateCodeGrant') + ->willReturn($accessToken); - $this->userProvider = m::mock(KeycloakUserProvider::class); - $this->resourceOwner = m::mock(KeycloakResourceOwner::class); + $this->userProvider = $this->createStub(KeycloakUserProvider::class); + $this->resourceOwner = $this->createStub(KeycloakResourceOwner::class); $this->userProvider - ->allows('loadUserByIdentifier') - ->with($accessToken) - ->andReturns($this->resourceOwner); + ->method('loadUserByIdentifier') + ->willReturn($this->resourceOwner); $this->authenticator = new KeycloakAuthenticator( - $this->createMock(LoggerInterface::class), + $this->createStub(LoggerInterface::class), $this->iamClient, $this->userProvider ); } - protected function tearDown(): void - { - m::close(); - parent::tearDown(); - } - public function testAuthenticateSuccessfulAuthentication(): void { // given - $session = m::mock(SessionInterface::class); + $session = $this->createMock(SessionInterface::class); $session - ->allows('get') + ->expects($this->once()) + ->method('get') ->with(KeycloakAuthorizationCodeEnum::STATE_SESSION_KEY) - ->andReturns('mock_state'); + ->willReturn('mock_state'); $request = new Request(); $request->query->add([ @@ -163,11 +155,12 @@ public function testAuthenticateSuccessfulAuthentication(): void public function testAuthenticateInvalidState(): void { // given - $session = m::mock(SessionInterface::class); + $session = $this->createMock(SessionInterface::class); $session - ->allows('get') + ->expects($this->once()) + ->method('get') ->with(KeycloakAuthorizationCodeEnum::STATE_SESSION_KEY) - ->andReturns('some_state'); + ->willReturn('some_state'); $request = new Request(); $request->query->add([ @@ -185,11 +178,12 @@ public function testAuthenticateInvalidState(): void public function testAuthenticateMissingCode(): void { // given - $session = m::mock(SessionInterface::class); + $session = $this->createMock(SessionInterface::class); $session - ->allows('get') + ->expects($this->once()) + ->method('get') ->with(KeycloakAuthorizationCodeEnum::STATE_SESSION_KEY) - ->andReturns('mock_state'); + ->willReturn('mock_state'); $request = new Request(); $request->query->add([ diff --git a/tests/Service/ClientsServiceTest.php b/tests/Service/ClientsServiceTest.php index c4b4fe5..35417d8 100644 --- a/tests/Service/ClientsServiceTest.php +++ b/tests/Service/ClientsServiceTest.php @@ -18,37 +18,45 @@ use Mainick\KeycloakClientBundle\Service\ClientsService; use Mainick\KeycloakClientBundle\Service\Criteria; use Mainick\KeycloakClientBundle\Token\AccessToken; -use Mockery as m; use PHPUnit\Framework\TestCase; -use Psr\Http\Message\ResponseInterface; -use Psr\Http\Message\StreamInterface; use Psr\Log\LoggerInterface; use Stevenmaguire\OAuth2\Client\Provider\Keycloak; +use Mainick\KeycloakClientBundle\Tests\Service\Support\ExecuteCommandTestHelperTrait; class ClientsServiceTest extends TestCase { + use ExecuteCommandTestHelperTrait; + private ClientsService $clientsService; - private m\MockInterface $httpClient; - private m\MockInterface $keycloakAdminClient; - private m\MockInterface $logger; - private m\MockInterface $serializer; + private ClientInterface $httpClient; + private KeycloakAdminClient $keycloakAdminClient; + private LoggerInterface $logger; + private Serializer $serializer; private AccessToken $adminAccessToken; protected function setUp(): void { parent::setUp(); - $this->httpClient = m::mock(ClientInterface::class); - $this->keycloakAdminClient = m::mock(KeycloakAdminClient::class); - $this->logger = m::mock(LoggerInterface::class); - $this->serializer = m::mock(Serializer::class); - - $keycloakProvider = m::mock(Keycloak::class); - $keycloakProvider->shouldReceive('getHttpClient')->andReturn($this->httpClient); - - $this->keycloakAdminClient->shouldReceive('getKeycloakProvider')->andReturn($keycloakProvider); - $this->keycloakAdminClient->shouldReceive('getBaseUrl')->andReturn('http://mock.url/auth'); - $this->keycloakAdminClient->shouldReceive('getVersion')->andReturn('17.0.1'); + $this->httpClient = $this->createMock(ClientInterface::class); + $this->keycloakAdminClient = $this->createStub(KeycloakAdminClient::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->serializer = $this->createMock(Serializer::class); + + $keycloakProvider = $this->createStub(Keycloak::class); + $keycloakProvider + ->method('getHttpClient') + ->willReturn($this->httpClient); + + $this->keycloakAdminClient + ->method('getKeycloakProvider') + ->willReturn($keycloakProvider); + $this->keycloakAdminClient + ->method('getBaseUrl') + ->willReturn('http://mock.url/auth'); + $this->keycloakAdminClient + ->method('getVersion') + ->willReturn('17.0.1'); $this->adminAccessToken = new AccessToken(); $this->adminAccessToken @@ -57,7 +65,9 @@ protected function setUp(): void ->setRefreshToken('mock_refresh_token') ->setValues(['scope' => 'email']); - $this->keycloakAdminClient->shouldReceive('getAdminAccessToken')->andReturn($this->adminAccessToken); + $this->keycloakAdminClient + ->method('getAdminAccessToken') + ->willReturn($this->adminAccessToken); $this->clientsService = new ClientsService( $this->logger, @@ -66,35 +76,25 @@ protected function setUp(): void $reflection = new \ReflectionClass($this->clientsService); $serializerProperty = $reflection->getProperty('serializer'); - $serializerProperty->setAccessible(true); $serializerProperty->setValue($this->clientsService, $this->serializer); } - protected function tearDown(): void - { - m::close(); - parent::tearDown(); - } - public function testAll(): void { // given $realm = 'test-realm'; $responseBody = '[{"id":"client1","clientId":"client1"},{"id":"client2","clientId":"client2"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET','admin/realms/'.$realm.'/clients', m::on(function($options) { - return isset($options['headers']['Authorization']) && - $options['headers']['Authorization'] === 'Bearer mock_token'; + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/clients', $this->callback(static function ($options): bool { + return is_array($options) + && isset($options['headers']['Authorization']) + && 'Bearer mock_token' === $options['headers']['Authorization']; })) - ->andReturn($response); + ->willReturn($response); $clientCollection = new ClientCollection(); $client1 = new ClientRepresentation(); @@ -107,11 +107,14 @@ public function testAll(): void $clientCollection->add($client2); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, ClientCollection::class) - ->andReturn($clientCollection); + ->willReturn($clientCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger + ->expects($this->once()) + ->method('info'); // when $result = $this->clientsService->all($realm); @@ -130,19 +133,13 @@ public function testAllWithCriteria(): void $criteria = new Criteria(['briefRepresentation' => 'true']); $realm = 'test-realm'; $responseBody = '[{"id":"client1","clientId":"client1"},{"id":"client2","clientId":"client2"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/clients?briefRepresentation=true', m::type('array')) - ->andReturn($response); + ->expects($this->atLeastOnce()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/clients?briefRepresentation=true', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $clientCollection = new ClientCollection(); $client1 = new ClientRepresentation(); @@ -155,11 +152,14 @@ public function testAllWithCriteria(): void $clientCollection->add($client2); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, ClientCollection::class) - ->andReturn($clientCollection); + ->willReturn($clientCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger + ->expects($this->once()) + ->method('info'); // when $result = $this->clientsService->all($realm, $criteria); @@ -177,28 +177,27 @@ public function testGet(): void // given $realm = 'test-realm'; $responseBody = '{"id":"client1","clientId":"client1"}'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/clients/client1', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/clients/client1', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $client = new ClientRepresentation(); $client->id = 'client1'; $client->clientId = 'client1'; $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, ClientRepresentation::class) - ->andReturn($client); + ->willReturn($client); - $this->logger->shouldReceive('info')->once(); + $this->logger + ->expects($this->once()) + ->method('info'); // when $result = $this->clientsService->get($realm, 'client1'); @@ -217,21 +216,21 @@ public function testCreate(): void $client->clientId = 'client1'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(201); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(201, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('POST', 'admin/realms/'.$realm.'/clients', m::on(function($options) { - return isset($options['json']); - })) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('POST', 'admin/realms/'.$realm.'/clients', $this->callback(static fn ($options): bool => isset($options['json']))) + ->willReturn($response); - $this->logger->shouldReceive('info')->once(); + $this->serializer + ->expects($this->never()) + ->method('deserialize'); + + $this->logger + ->expects($this->once()) + ->method('info'); // when $result = $this->clientsService->create($realm, $client); @@ -250,21 +249,21 @@ public function testUpdate(): void $client->description = 'Updated description'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(204); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(204, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('PUT', 'admin/realms/'.$realm.'/clients/client1', m::on(function($options) { - return isset($options['json']); - })) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('PUT', 'admin/realms/'.$realm.'/clients/client1', $this->callback(static fn ($options): bool => isset($options['json']))) + ->willReturn($response); + + $this->serializer + ->expects($this->never()) + ->method('deserialize'); - $this->logger->shouldReceive('info')->once(); + $this->logger + ->expects($this->once()) + ->method('info'); // when $result = $this->clientsService->update($realm, 'client1', $client); @@ -278,19 +277,21 @@ public function testDelete(): void // given $realm = 'test-realm'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(204); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(204, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('DELETE', 'admin/realms/'.$realm.'/clients/client1', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('DELETE', 'admin/realms/'.$realm.'/clients/client1', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); - $this->logger->shouldReceive('info')->once(); + $this->serializer + ->expects($this->never()) + ->method('deserialize'); + + $this->logger + ->expects($this->once()) + ->method('info'); // when $result = $this->clientsService->delete($realm, 'client1'); @@ -304,17 +305,13 @@ public function testRoles(): void $realm = 'test-realm'; $clientUuid = 'client1'; $responseBody = '[{"id":"role1","name":"role1"},{"id":"role2","name":"role2"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/clients/'.$clientUuid.'/roles', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/clients/'.$clientUuid.'/roles', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $roleCollection = new RoleCollection(); $role1 = new RoleRepresentation(); @@ -327,11 +324,14 @@ public function testRoles(): void $roleCollection->add($role2); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, RoleCollection::class) - ->andReturn($roleCollection); + ->willReturn($roleCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger + ->expects($this->once()) + ->method('info'); // when $result = $this->clientsService->roles($realm, $clientUuid); @@ -351,17 +351,13 @@ public function testRole(): void $clientUuid = 'client1'; $roleName = 'role1'; $responseBody = '{"id":"role1","name":"role1","description":"Test role"}'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/clients/'.$clientUuid.'/roles/'.$roleName, m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/clients/'.$clientUuid.'/roles/'.$roleName, $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $role = new RoleRepresentation(); $role->id = 'role1'; @@ -369,11 +365,14 @@ public function testRole(): void $role->description = 'Test role'; $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, RoleRepresentation::class) - ->andReturn($role); + ->willReturn($role); - $this->logger->shouldReceive('info')->once(); + $this->logger + ->expects($this->once()) + ->method('info'); // when $result = $this->clientsService->role($realm, $clientUuid, $roleName); @@ -396,21 +395,21 @@ public function testCreateRole(): void $role->description = 'New test role'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(201); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(201, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('POST', 'admin/realms/'.$realm.'/clients/'.$clientUuid.'/roles', m::on(function($options) { - return isset($options['json']); - })) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('POST', 'admin/realms/'.$realm.'/clients/'.$clientUuid.'/roles', $this->callback(static fn ($options): bool => isset($options['json']))) + ->willReturn($response); + + $this->serializer + ->expects($this->never()) + ->method('deserialize'); - $this->logger->shouldReceive('info')->once(); + $this->logger + ->expects($this->once()) + ->method('info'); // when $result = $this->clientsService->createRole($realm, $clientUuid, $role); @@ -430,21 +429,21 @@ public function testUpdateRole(): void $role->description = 'Updated role description'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(204); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(204, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('PUT', 'admin/realms/'.$realm.'/clients/'.$clientUuid.'/roles/'.$roleName, m::on(function($options) { - return isset($options['json']); - })) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('PUT', 'admin/realms/'.$realm.'/clients/'.$clientUuid.'/roles/'.$roleName, $this->callback(static fn ($options): bool => isset($options['json']))) + ->willReturn($response); + + $this->serializer + ->expects($this->never()) + ->method('deserialize'); - $this->logger->shouldReceive('info')->once(); + $this->logger + ->expects($this->once()) + ->method('info'); // when $result = $this->clientsService->updateRole($realm, $clientUuid, $roleName, $role); @@ -461,19 +460,21 @@ public function testDeleteRole(): void $roleName = 'role1'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(204); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(204, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('DELETE', 'admin/realms/'.$realm.'/clients/'.$clientUuid.'/roles/'.$roleName, m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('DELETE', 'admin/realms/'.$realm.'/clients/'.$clientUuid.'/roles/'.$roleName, $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); - $this->logger->shouldReceive('info')->once(); + $this->serializer + ->expects($this->never()) + ->method('deserialize'); + + $this->logger + ->expects($this->once()) + ->method('info'); // when $result = $this->clientsService->deleteRole($realm, $clientUuid, $roleName); @@ -489,17 +490,13 @@ public function testGetRoleGroups(): void $clientUuid = 'client1'; $roleName = 'role1'; $responseBody = '[{"id":"group1","name":"group1"},{"id":"group2","name":"group2"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/clients/'.$clientUuid.'/roles/'.$roleName.'/groups', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/clients/'.$clientUuid.'/roles/'.$roleName.'/groups', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $groupCollection = new GroupCollection(); $group1 = new GroupRepresentation(); @@ -512,11 +509,14 @@ public function testGetRoleGroups(): void $groupCollection->add($group2); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, GroupCollection::class) - ->andReturn($groupCollection); + ->willReturn($groupCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger + ->expects($this->once()) + ->method('info'); // when $result = $this->clientsService->getRoleGroups($realm, $clientUuid, $roleName); @@ -536,17 +536,13 @@ public function testGetRoleUsers(): void $clientUuid = 'client1'; $roleName = 'role1'; $responseBody = '[{"id":"user1","username":"user1"},{"id":"user2","username":"user2"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/clients/'.$clientUuid.'/roles/'.$roleName.'/users', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/clients/'.$clientUuid.'/roles/'.$roleName.'/users', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $userCollection = new UserCollection(); $user1 = new UserRepresentation(); @@ -559,11 +555,14 @@ public function testGetRoleUsers(): void $userCollection->add($user2); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, UserCollection::class) - ->andReturn($userCollection); + ->willReturn($userCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger + ->expects($this->once()) + ->method('info'); // when $result = $this->clientsService->getRoleUsers($realm, $clientUuid, $roleName); diff --git a/tests/Service/GroupsServiceTest.php b/tests/Service/GroupsServiceTest.php index 7bdb092..2cd5db3 100644 --- a/tests/Service/GroupsServiceTest.php +++ b/tests/Service/GroupsServiceTest.php @@ -15,39 +15,38 @@ use Mainick\KeycloakClientBundle\Serializer\Serializer; use Mainick\KeycloakClientBundle\Service\Criteria; use Mainick\KeycloakClientBundle\Service\GroupsService; -use Mainick\KeycloakClientBundle\Service\HttpMethodEnum; use Mainick\KeycloakClientBundle\Token\AccessToken; -use Mockery as m; +use Mainick\KeycloakClientBundle\Tests\Service\Support\ExecuteCommandTestHelperTrait; use PHPUnit\Framework\TestCase; -use Psr\Http\Message\ResponseInterface; -use Psr\Http\Message\StreamInterface; use Psr\Log\LoggerInterface; use Stevenmaguire\OAuth2\Client\Provider\Keycloak; class GroupsServiceTest extends TestCase { + use ExecuteCommandTestHelperTrait; + private GroupsService $groupsService; - private m\MockInterface $httpClient; - private m\MockInterface $keycloakAdminClient; - private m\MockInterface $logger; - private m\MockInterface $serializer; + private ClientInterface $httpClient; + private KeycloakAdminClient $keycloakAdminClient; + private LoggerInterface $logger; + private Serializer $serializer; private AccessToken $adminAccessToken; protected function setUp(): void { parent::setUp(); - $this->httpClient = m::mock(ClientInterface::class); - $this->keycloakAdminClient = m::mock(KeycloakAdminClient::class); - $this->logger = m::mock(LoggerInterface::class); - $this->serializer = m::mock(Serializer::class); + $this->httpClient = $this->createMock(ClientInterface::class); + $this->keycloakAdminClient = $this->createStub(KeycloakAdminClient::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->serializer = $this->createMock(Serializer::class); - $keycloakProvider = m::mock(Keycloak::class); - $keycloakProvider->shouldReceive('getHttpClient')->andReturn($this->httpClient); + $keycloakProvider = $this->createStub(Keycloak::class); + $keycloakProvider->method('getHttpClient')->willReturn($this->httpClient); - $this->keycloakAdminClient->shouldReceive('getKeycloakProvider')->andReturn($keycloakProvider); - $this->keycloakAdminClient->shouldReceive('getBaseUrl')->andReturn('http://mock.url/auth'); - $this->keycloakAdminClient->shouldReceive('getVersion')->andReturn('17.0.1'); + $this->keycloakAdminClient->method('getKeycloakProvider')->willReturn($keycloakProvider); + $this->keycloakAdminClient->method('getBaseUrl')->willReturn('http://mock.url/auth'); + $this->keycloakAdminClient->method('getVersion')->willReturn('17.0.1'); $this->adminAccessToken = new AccessToken(); $this->adminAccessToken @@ -56,7 +55,7 @@ protected function setUp(): void ->setRefreshToken('mock_refresh_token') ->setValues(['scope' => 'email']); - $this->keycloakAdminClient->shouldReceive('getAdminAccessToken')->andReturn($this->adminAccessToken); + $this->keycloakAdminClient->method('getAdminAccessToken')->willReturn($this->adminAccessToken); $this->groupsService = new GroupsService( $this->logger, @@ -65,35 +64,24 @@ protected function setUp(): void $reflection = new \ReflectionClass($this->groupsService); $serializerProperty = $reflection->getProperty('serializer'); - $serializerProperty->setAccessible(true); $serializerProperty->setValue($this->groupsService, $this->serializer); } - protected function tearDown(): void - { - m::close(); - parent::tearDown(); - } - public function testAll(): void { // given $realm = 'test-realm'; $responseBody = '[{"id":"group1","name":"group1"},{"id":"group2","name":"group2"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/groups', m::on(function($options) { + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/groups', $this->callback(function($options) { return isset($options['headers']['Authorization']) && $options['headers']['Authorization'] === 'Bearer mock_token'; })) - ->andReturn($response); + ->willReturn($response); $groupCollection = new GroupCollection(); $group1 = new GroupRepresentation(); @@ -106,11 +94,12 @@ public function testAll(): void $groupCollection->add($group2); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, GroupCollection::class) - ->andReturn($groupCollection); + ->willReturn($groupCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->groupsService->all($realm); @@ -129,17 +118,13 @@ public function testAllWithCriteria(): void $criteria = new Criteria(['briefRepresentation' => 'true']); $realm = 'test-realm'; $responseBody = '[{"id":"group1","name":"group1"},{"id":"group2","name":"group2"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/groups?briefRepresentation=true', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/groups?briefRepresentation=true', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $groupCollection = new GroupCollection(); $group1 = new GroupRepresentation(); @@ -152,11 +137,12 @@ public function testAllWithCriteria(): void $groupCollection->add($group2); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, GroupCollection::class) - ->andReturn($groupCollection); + ->willReturn($groupCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->groupsService->all($realm, $criteria); @@ -174,24 +160,19 @@ public function testCount(): void // given $realm = 'test-realm'; $responseBody = '2'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/groups/count', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/groups/count', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $this->serializer - ->shouldReceive('deserialize') - ->with($responseBody, 'array') - ->andReturn(2); + ->expects($this->never()) + ->method('deserialize'); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->groupsService->count($realm); @@ -207,17 +188,13 @@ public function testChildren(): void $realm = 'test-realm'; $groupId = 'parent-group'; $responseBody = '[{"id":"child1","name":"child1"},{"id":"child2","name":"child2"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/groups/'.$groupId.'/children', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/groups/'.$groupId.'/children', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $groupCollection = new GroupCollection(); $child1 = new GroupRepresentation(); @@ -230,11 +207,12 @@ public function testChildren(): void $groupCollection->add($child2); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, GroupCollection::class) - ->andReturn($groupCollection); + ->willReturn($groupCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->groupsService->children($realm, $groupId); @@ -253,17 +231,13 @@ public function testGet(): void $realm = 'test-realm'; $groupId = 'group1'; $responseBody = '{"id":"group1","name":"group1","path":"/group1"}'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/groups/'.$groupId, m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/groups/'.$groupId, $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $group = new GroupRepresentation(); $group->id = 'group1'; @@ -271,11 +245,12 @@ public function testGet(): void $group->path = '/group1'; $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, GroupRepresentation::class) - ->andReturn($group); + ->willReturn($group); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->groupsService->get($realm, $groupId); @@ -296,21 +271,21 @@ public function testCreate(): void $group->name = 'new-group'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(201); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(201, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('POST', 'admin/realms/'.$realm.'/groups', m::on(function($options) { + ->expects($this->once()) + ->method('request') + ->with('POST', 'admin/realms/'.$realm.'/groups', $this->callback(function($options) { return isset($options['json']); })) - ->andReturn($response); + ->willReturn($response); - $this->logger->shouldReceive('info')->once(); + $this->serializer + ->expects($this->never()) + ->method('deserialize'); + + $this->logger->expects($this->once())->method('info'); // when $result = $this->groupsService->create($realm, $group); @@ -328,21 +303,21 @@ public function testCreateChild(): void $group->name = 'child-group'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(201); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(201, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('POST', 'admin/realms/'.$realm.'/groups/'.$parentGroupId.'/children', m::on(function($options) { + ->expects($this->once()) + ->method('request') + ->with('POST', 'admin/realms/'.$realm.'/groups/'.$parentGroupId.'/children', $this->callback(function($options) { return isset($options['json']); })) - ->andReturn($response); + ->willReturn($response); + + $this->serializer + ->expects($this->never()) + ->method('deserialize'); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->groupsService->createChild($realm, $parentGroupId, $group); @@ -361,21 +336,21 @@ public function testUpdate(): void $group->name = 'updated-group'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(204); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(204, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('PUT', 'admin/realms/'.$realm.'/groups/'.$groupId, m::on(function($options) { + ->expects($this->once()) + ->method('request') + ->with('PUT', 'admin/realms/'.$realm.'/groups/'.$groupId, $this->callback(function($options) { return isset($options['json']); })) - ->andReturn($response); + ->willReturn($response); + + $this->serializer + ->expects($this->never()) + ->method('deserialize'); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->groupsService->update($realm, $groupId, $group); @@ -390,19 +365,19 @@ public function testDelete(): void $realm = 'test-realm'; $groupId = 'group1'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(204); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(204, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('DELETE', 'admin/realms/'.$realm.'/groups/'.$groupId, m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('DELETE', 'admin/realms/'.$realm.'/groups/'.$groupId, $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); - $this->logger->shouldReceive('info')->once(); + $this->serializer + ->expects($this->never()) + ->method('deserialize'); + + $this->logger->expects($this->once())->method('info'); // when $result = $this->groupsService->delete($realm, $groupId); @@ -417,17 +392,13 @@ public function testUsers(): void $realm = 'test-realm'; $groupId = 'group1'; $responseBody = '[{"id":"user1","username":"user1"},{"id":"user2","username":"user2"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/groups/'.$groupId.'/members', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/groups/'.$groupId.'/members', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $userCollection = new UserCollection(); $user1 = new UserRepresentation(); @@ -440,11 +411,12 @@ public function testUsers(): void $userCollection->add($user2); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, UserCollection::class) - ->andReturn($userCollection); + ->willReturn($userCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->groupsService->users($realm, $groupId); @@ -463,17 +435,13 @@ public function testRealmRoles(): void $realm = 'test-realm'; $groupId = 'group1'; $responseBody = '[{"id":"role1","name":"role1"},{"id":"role2","name":"role2"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/groups/'.$groupId.'/role-mappings/realm', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/groups/'.$groupId.'/role-mappings/realm', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $roleCollection = new RoleCollection(); $role1 = new RoleRepresentation(); @@ -486,11 +454,12 @@ public function testRealmRoles(): void $roleCollection->add($role2); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, RoleCollection::class) - ->andReturn($roleCollection); + ->willReturn($roleCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->groupsService->realmRoles($realm, $groupId); @@ -509,17 +478,13 @@ public function testAvailableRealmRoles(): void $realm = 'test-realm'; $groupId = 'group1'; $responseBody = '[{"id":"role3","name":"role3"},{"id":"role4","name":"role4"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/groups/'.$groupId.'/role-mappings/realm/available', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/groups/'.$groupId.'/role-mappings/realm/available', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $roleCollection = new RoleCollection(); $role3 = new RoleRepresentation(); @@ -532,11 +497,12 @@ public function testAvailableRealmRoles(): void $roleCollection->add($role4); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, RoleCollection::class) - ->andReturn($roleCollection); + ->willReturn($roleCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->groupsService->availableRealmRoles($realm, $groupId); @@ -559,21 +525,21 @@ public function testAddRealmRole(): void $role->name = 'role3'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(204); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(204, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('POST', 'admin/realms/'.$realm.'/groups/'.$groupId.'/role-mappings/realm', m::on(function($options) { + ->expects($this->once()) + ->method('request') + ->with('POST', 'admin/realms/'.$realm.'/groups/'.$groupId.'/role-mappings/realm', $this->callback(function($options) { return isset($options['json']); })) - ->andReturn($response); + ->willReturn($response); + + $this->serializer + ->expects($this->never()) + ->method('deserialize'); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->groupsService->addRealmRole($realm, $groupId, $role); @@ -592,21 +558,21 @@ public function testRemoveRealmRole(): void $role->name = 'role1'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(204); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(204, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('DELETE', 'admin/realms/'.$realm.'/groups/'.$groupId.'/role-mappings/realm', m::on(function($options) { + ->expects($this->once()) + ->method('request') + ->with('DELETE', 'admin/realms/'.$realm.'/groups/'.$groupId.'/role-mappings/realm', $this->callback(function($options) { return isset($options['json']); })) - ->andReturn($response); + ->willReturn($response); - $this->logger->shouldReceive('info')->once(); + $this->serializer + ->expects($this->never()) + ->method('deserialize'); + + $this->logger->expects($this->once())->method('info'); // when $result = $this->groupsService->removeRealmRole($realm, $groupId, $role); @@ -622,17 +588,13 @@ public function testClientRoles(): void $clientUuid = 'client1'; $groupId = 'group1'; $responseBody = '[{"id":"role1","name":"role1"},{"id":"role2","name":"role2"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/groups/'.$groupId.'/role-mappings/clients/'.$clientUuid, m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/groups/'.$groupId.'/role-mappings/clients/'.$clientUuid, $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $roleCollection = new RoleCollection(); $role1 = new RoleRepresentation(); @@ -645,11 +607,12 @@ public function testClientRoles(): void $roleCollection->add($role2); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, RoleCollection::class) - ->andReturn($roleCollection); + ->willReturn($roleCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->groupsService->clientRoles($realm, $clientUuid, $groupId); @@ -669,17 +632,13 @@ public function testAvailableClientRoles(): void $clientUuid = 'client1'; $groupId = 'group1'; $responseBody = '[{"id":"role3","name":"role3"},{"id":"role4","name":"role4"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/groups/'.$groupId.'/role-mappings/clients/'.$clientUuid.'/available', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/groups/'.$groupId.'/role-mappings/clients/'.$clientUuid.'/available', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $roleCollection = new RoleCollection(); $role3 = new RoleRepresentation(); @@ -692,11 +651,12 @@ public function testAvailableClientRoles(): void $roleCollection->add($role4); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, RoleCollection::class) - ->andReturn($roleCollection); + ->willReturn($roleCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->groupsService->availableClientRoles($realm, $clientUuid, $groupId); @@ -720,21 +680,21 @@ public function testAddClientRole(): void $role->name = 'role3'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(204); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(204, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('POST', 'admin/realms/'.$realm.'/groups/'.$groupId.'/role-mappings/clients/'.$clientUuid, m::on(function($options) { + ->expects($this->once()) + ->method('request') + ->with('POST', 'admin/realms/'.$realm.'/groups/'.$groupId.'/role-mappings/clients/'.$clientUuid, $this->callback(function($options) { return isset($options['json']); })) - ->andReturn($response); + ->willReturn($response); - $this->logger->shouldReceive('info')->once(); + $this->serializer + ->expects($this->never()) + ->method('deserialize'); + + $this->logger->expects($this->once())->method('info'); // when $result = $this->groupsService->addClientRole($realm, $clientUuid, $groupId, $role); @@ -754,21 +714,21 @@ public function testRemoveClientRole(): void $role->name = 'role1'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(204); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(204, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('DELETE', 'admin/realms/'.$realm.'/groups/'.$groupId.'/role-mappings/clients/'.$clientUuid, m::on(function($options) { + ->expects($this->once()) + ->method('request') + ->with('DELETE', 'admin/realms/'.$realm.'/groups/'.$groupId.'/role-mappings/clients/'.$clientUuid, $this->callback(function($options) { return isset($options['json']); })) - ->andReturn($response); + ->willReturn($response); + + $this->serializer + ->expects($this->never()) + ->method('deserialize'); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->groupsService->removeClientRole($realm, $clientUuid, $groupId, $role); diff --git a/tests/Service/RealmsServiceTest.php b/tests/Service/RealmsServiceTest.php index 350f560..9e1d6dd 100644 --- a/tests/Service/RealmsServiceTest.php +++ b/tests/Service/RealmsServiceTest.php @@ -12,37 +12,37 @@ use Mainick\KeycloakClientBundle\Service\Criteria; use Mainick\KeycloakClientBundle\Service\RealmsService; use Mainick\KeycloakClientBundle\Token\AccessToken; -use Mockery as m; +use Mainick\KeycloakClientBundle\Tests\Service\Support\ExecuteCommandTestHelperTrait; use PHPUnit\Framework\TestCase; -use Psr\Http\Message\ResponseInterface; -use Psr\Http\Message\StreamInterface; use Psr\Log\LoggerInterface; use Stevenmaguire\OAuth2\Client\Provider\Keycloak; class RealmsServiceTest extends TestCase { + use ExecuteCommandTestHelperTrait; + private RealmsService $realmsService; - private m\MockInterface $httpClient; - private m\MockInterface $keycloakAdminClient; - private m\MockInterface $logger; - private m\MockInterface $serializer; + private ClientInterface $httpClient; + private KeycloakAdminClient $keycloakAdminClient; + private LoggerInterface $logger; + private Serializer $serializer; private AccessToken $adminAccessToken; protected function setUp(): void { parent::setUp(); - $this->httpClient = m::mock(ClientInterface::class); - $this->keycloakAdminClient = m::mock(KeycloakAdminClient::class); - $this->logger = m::mock(LoggerInterface::class); - $this->serializer = m::mock(Serializer::class); + $this->httpClient = $this->createMock(ClientInterface::class); + $this->keycloakAdminClient = $this->createStub(KeycloakAdminClient::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->serializer = $this->createMock(Serializer::class); - $keycloakProvider = m::mock(Keycloak::class); - $keycloakProvider->shouldReceive('getHttpClient')->andReturn($this->httpClient); + $keycloakProvider = $this->createStub(Keycloak::class); + $keycloakProvider->method('getHttpClient')->willReturn($this->httpClient); - $this->keycloakAdminClient->shouldReceive('getKeycloakProvider')->andReturn($keycloakProvider); - $this->keycloakAdminClient->shouldReceive('getBaseUrl')->andReturn('http://mock.url/auth'); - $this->keycloakAdminClient->shouldReceive('getVersion')->andReturn('17.0.1'); + $this->keycloakAdminClient->method('getKeycloakProvider')->willReturn($keycloakProvider); + $this->keycloakAdminClient->method('getBaseUrl')->willReturn('http://mock.url/auth'); + $this->keycloakAdminClient->method('getVersion')->willReturn('17.0.1'); $this->adminAccessToken = new AccessToken(); $this->adminAccessToken @@ -51,7 +51,7 @@ protected function setUp(): void ->setRefreshToken('mock_refresh_token') ->setValues(['scope' => 'email']); - $this->keycloakAdminClient->shouldReceive('getAdminAccessToken')->andReturn($this->adminAccessToken); + $this->keycloakAdminClient->method('getAdminAccessToken')->willReturn($this->adminAccessToken); $this->realmsService = new RealmsService( $this->logger, @@ -60,34 +60,23 @@ protected function setUp(): void $reflection = new \ReflectionClass($this->realmsService); $serializerProperty = $reflection->getProperty('serializer'); - $serializerProperty->setAccessible(true); $serializerProperty->setValue($this->realmsService, $this->serializer); } - protected function tearDown(): void - { - m::close(); - parent::tearDown(); - } - public function testAll(): void { // given $responseBody = '[{"id":"master","realm":"master"},{"id":"test","realm":"test"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms', m::on(function($options) { + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms', $this->callback(function($options) { return isset($options['headers']['Authorization']) && $options['headers']['Authorization'] === 'Bearer mock_token'; })) - ->andReturn($response); + ->willReturn($response); $realmCollection = new RealmCollection(); $realm1 = new RealmRepresentation(); @@ -100,11 +89,12 @@ public function testAll(): void $realmCollection->add($realm2); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, RealmCollection::class) - ->andReturn($realmCollection); + ->willReturn($realmCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->realmsService->all(); @@ -123,17 +113,13 @@ public function testAllWithCriteria(): void $criteria = new Criteria(['briefRepresentation' => 'true']); $responseBody = '[{"id":"master","realm":"master"},{"id":"test","realm":"test"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms?briefRepresentation=true', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms?briefRepresentation=true', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $realmCollection = new RealmCollection(); $realm1 = new RealmRepresentation(); @@ -146,11 +132,12 @@ public function testAllWithCriteria(): void $realmCollection->add($realm2); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, RealmCollection::class) - ->andReturn($realmCollection); + ->willReturn($realmCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->realmsService->all($criteria); @@ -167,27 +154,24 @@ public function testGet(): void { // given $responseBody = '{"id":"test","realm":"test"}'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/test', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/test', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $realm = new RealmRepresentation(); $realm->realm = 'test'; $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, RealmRepresentation::class) - ->andReturn($realm); + ->willReturn($realm); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->realmsService->get('test'); @@ -204,21 +188,21 @@ public function testCreate(): void $realm->realm = 'new-realm'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(201); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(201, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('POST', 'admin/realms/', m::on(function($options) { + ->expects($this->once()) + ->method('request') + ->with('POST', 'admin/realms/', $this->callback(function($options) { return isset($options['json']); })) - ->andReturn($response); + ->willReturn($response); - $this->logger->shouldReceive('info')->once(); + $this->serializer + ->expects($this->never()) + ->method('deserialize'); + + $this->logger->expects($this->once())->method('info'); // when $result = $this->realmsService->create($realm); @@ -235,21 +219,21 @@ public function testUpdate(): void $realm->displayName = 'Updated Test Realm'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(204); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(204, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('PUT', 'admin/realms/test', m::on(function($options) { + ->expects($this->once()) + ->method('request') + ->with('PUT', 'admin/realms/test', $this->callback(function($options) { return isset($options['json']); })) - ->andReturn($response); + ->willReturn($response); + + $this->serializer + ->expects($this->never()) + ->method('deserialize'); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->realmsService->update('test', $realm); @@ -262,19 +246,19 @@ public function testDelete(): void { // given $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(204); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(204, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('DELETE', 'admin/realms/test', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('DELETE', 'admin/realms/test', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); + + $this->serializer + ->expects($this->never()) + ->method('deserialize'); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->realmsService->delete('test'); diff --git a/tests/Service/RolesServiceTest.php b/tests/Service/RolesServiceTest.php index 9de6b62..699b41b 100644 --- a/tests/Service/RolesServiceTest.php +++ b/tests/Service/RolesServiceTest.php @@ -16,37 +16,37 @@ use Mainick\KeycloakClientBundle\Service\Criteria; use Mainick\KeycloakClientBundle\Service\RolesService; use Mainick\KeycloakClientBundle\Token\AccessToken; -use Mockery as m; +use Mainick\KeycloakClientBundle\Tests\Service\Support\ExecuteCommandTestHelperTrait; use PHPUnit\Framework\TestCase; -use Psr\Http\Message\ResponseInterface; -use Psr\Http\Message\StreamInterface; use Psr\Log\LoggerInterface; use Stevenmaguire\OAuth2\Client\Provider\Keycloak; class RolesServiceTest extends TestCase { + use ExecuteCommandTestHelperTrait; + private RolesService $rolesService; - private m\MockInterface $httpClient; - private m\MockInterface $keycloakAdminClient; - private m\MockInterface $logger; - private m\MockInterface $serializer; + private ClientInterface $httpClient; + private KeycloakAdminClient $keycloakAdminClient; + private LoggerInterface $logger; + private Serializer $serializer; private AccessToken $adminAccessToken; protected function setUp(): void { parent::setUp(); - $this->httpClient = m::mock(ClientInterface::class); - $this->keycloakAdminClient = m::mock(KeycloakAdminClient::class); - $this->logger = m::mock(LoggerInterface::class); - $this->serializer = m::mock(Serializer::class); + $this->httpClient = $this->createMock(ClientInterface::class); + $this->keycloakAdminClient = $this->createStub(KeycloakAdminClient::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->serializer = $this->createMock(Serializer::class); - $keycloakProvider = m::mock(Keycloak::class); - $keycloakProvider->shouldReceive('getHttpClient')->andReturn($this->httpClient); + $keycloakProvider = $this->createStub(Keycloak::class); + $keycloakProvider->method('getHttpClient')->willReturn($this->httpClient); - $this->keycloakAdminClient->shouldReceive('getKeycloakProvider')->andReturn($keycloakProvider); - $this->keycloakAdminClient->shouldReceive('getBaseUrl')->andReturn('http://mock.url/auth'); - $this->keycloakAdminClient->shouldReceive('getVersion')->andReturn('17.0.1'); + $this->keycloakAdminClient->method('getKeycloakProvider')->willReturn($keycloakProvider); + $this->keycloakAdminClient->method('getBaseUrl')->willReturn('http://mock.url/auth'); + $this->keycloakAdminClient->method('getVersion')->willReturn('17.0.1'); $this->adminAccessToken = new AccessToken(); $this->adminAccessToken @@ -55,7 +55,7 @@ protected function setUp(): void ->setRefreshToken('mock_refresh_token') ->setValues(['scope' => 'email']); - $this->keycloakAdminClient->shouldReceive('getAdminAccessToken')->andReturn($this->adminAccessToken); + $this->keycloakAdminClient->method('getAdminAccessToken')->willReturn($this->adminAccessToken); $this->rolesService = new RolesService( $this->logger, @@ -64,35 +64,25 @@ protected function setUp(): void $reflection = new \ReflectionClass($this->rolesService); $serializerProperty = $reflection->getProperty('serializer'); - $serializerProperty->setAccessible(true); $serializerProperty->setValue($this->rolesService, $this->serializer); } - protected function tearDown(): void - { - m::close(); - parent::tearDown(); - } - public function testAll(): void { // given $realm = 'test-realm'; $responseBody = '[{"id":"role1","name":"role1"},{"id":"role2","name":"role2"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/roles', m::on(function($options) { - return isset($options['headers']['Authorization']) && + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/roles', $this->callback(static function ($options): bool { + return is_array($options) && + isset($options['headers']['Authorization']) && $options['headers']['Authorization'] === 'Bearer mock_token'; })) - ->andReturn($response); + ->willReturn($response); $roleCollection = new RoleCollection(); $role1 = new RoleRepresentation(); @@ -105,11 +95,12 @@ public function testAll(): void $roleCollection->add($role2); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, RoleCollection::class) - ->andReturn($roleCollection); + ->willReturn($roleCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->rolesService->all($realm); @@ -128,17 +119,13 @@ public function testAllWithCriteria(): void $criteria = new Criteria(['briefRepresentation' => 'true']); $realm = 'test-realm'; $responseBody = '[{"id":"role1","name":"role1"},{"id":"role2","name":"role2"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/roles?briefRepresentation=true', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/roles?briefRepresentation=true', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $roleCollection = new RoleCollection(); $role1 = new RoleRepresentation(); @@ -151,11 +138,12 @@ public function testAllWithCriteria(): void $roleCollection->add($role2); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, RoleCollection::class) - ->andReturn($roleCollection); + ->willReturn($roleCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->rolesService->all($realm, $criteria); @@ -174,17 +162,13 @@ public function testGet(): void $realm = 'test-realm'; $roleName = 'role1'; $responseBody = '{"id":"role1","name":"role1","description":"Test role"}'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/roles/'.$roleName, m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/roles/'.$roleName, $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $role = new RoleRepresentation(); $role->id = 'role1'; @@ -192,11 +176,12 @@ public function testGet(): void $role->description = 'Test role'; $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, RoleRepresentation::class) - ->andReturn($role); + ->willReturn($role); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->rolesService->get($realm, $roleName); @@ -218,21 +203,21 @@ public function testCreate(): void $role->description = 'New test role'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(201); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(201, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('POST', 'admin/realms/'.$realm.'/roles', m::on(function($options) { + ->expects($this->once()) + ->method('request') + ->with('POST', 'admin/realms/'.$realm.'/roles', $this->callback(static function ($options): bool { return isset($options['json']); })) - ->andReturn($response); + ->willReturn($response); + + $this->serializer + ->expects($this->never()) + ->method('deserialize'); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->rolesService->create($realm, $role); @@ -251,21 +236,21 @@ public function testUpdate(): void $role->description = 'Updated role description'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(204); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(204, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('PUT', 'admin/realms/'.$realm.'/roles/'.$roleName, m::on(function($options) { + ->expects($this->once()) + ->method('request') + ->with('PUT', 'admin/realms/'.$realm.'/roles/'.$roleName, $this->callback(static function ($options): bool { return isset($options['json']); })) - ->andReturn($response); + ->willReturn($response); + + $this->serializer + ->expects($this->never()) + ->method('deserialize'); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->rolesService->update($realm, $roleName, $role); @@ -280,19 +265,19 @@ public function testDelete(): void $realm = 'test-realm'; $roleName = 'role1'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(204); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(204, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('DELETE', 'admin/realms/'.$realm.'/roles/'.$roleName, m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('DELETE', 'admin/realms/'.$realm.'/roles/'.$roleName, $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); + + $this->serializer + ->expects($this->never()) + ->method('deserialize'); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->rolesService->delete($realm, $roleName); @@ -307,17 +292,13 @@ public function testGroups(): void $realm = 'test-realm'; $roleName = 'role1'; $responseBody = '[{"id":"group1","name":"group1"},{"id":"group2","name":"group2"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/roles/'.$roleName.'/groups', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/roles/'.$roleName.'/groups', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $groupCollection = new GroupCollection(); $group1 = new GroupRepresentation(); @@ -330,11 +311,12 @@ public function testGroups(): void $groupCollection->add($group2); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, GroupCollection::class) - ->andReturn($groupCollection); + ->willReturn($groupCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->rolesService->groups($realm, $roleName); @@ -354,17 +336,13 @@ public function testGroupsWithCriteria(): void $roleName = 'role1'; $criteria = new Criteria(['first' => '0', 'max' => '10']); $responseBody = '[{"id":"group1","name":"group1"},{"id":"group2","name":"group2"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/roles/'.$roleName.'/groups?first=0&max=10', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/roles/'.$roleName.'/groups?first=0&max=10', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $groupCollection = new GroupCollection(); $group1 = new GroupRepresentation(); @@ -377,11 +355,12 @@ public function testGroupsWithCriteria(): void $groupCollection->add($group2); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, GroupCollection::class) - ->andReturn($groupCollection); + ->willReturn($groupCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->rolesService->groups($realm, $roleName, $criteria); @@ -400,17 +379,13 @@ public function testUsers(): void $realm = 'test-realm'; $roleName = 'role1'; $responseBody = '[{"id":"user1","username":"user1"},{"id":"user2","username":"user2"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/roles/'.$roleName.'/users', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/roles/'.$roleName.'/users', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $userCollection = new UserCollection(); $user1 = new UserRepresentation(); @@ -423,11 +398,12 @@ public function testUsers(): void $userCollection->add($user2); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, UserCollection::class) - ->andReturn($userCollection); + ->willReturn($userCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->rolesService->users($realm, $roleName); @@ -447,17 +423,13 @@ public function testUsersWithCriteria(): void $roleName = 'role1'; $criteria = new Criteria(['first' => '0', 'max' => '10']); $responseBody = '[{"id":"user1","username":"user1"},{"id":"user2","username":"user2"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/roles/'.$roleName.'/users?first=0&max=10', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/roles/'.$roleName.'/users?first=0&max=10', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $userCollection = new UserCollection(); $user1 = new UserRepresentation(); @@ -470,11 +442,12 @@ public function testUsersWithCriteria(): void $userCollection->add($user2); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, UserCollection::class) - ->andReturn($userCollection); + ->willReturn($userCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->rolesService->users($realm, $roleName, $criteria); diff --git a/tests/Service/Support/ExecuteCommandTestHelperTrait.php b/tests/Service/Support/ExecuteCommandTestHelperTrait.php new file mode 100644 index 0000000..8fe5f30 --- /dev/null +++ b/tests/Service/Support/ExecuteCommandTestHelperTrait.php @@ -0,0 +1,24 @@ +createStub(StreamInterface::class); + $stream->method('getContents')->willReturn($responseBody); + + $response = $this->createStub(ResponseInterface::class); + $response->method('getStatusCode')->willReturn($statusCode); + $response->method('getBody')->willReturn($stream); + + return $response; + } +} + diff --git a/tests/Service/UsersServiceTest.php b/tests/Service/UsersServiceTest.php index c013a6c..64ebe5d 100644 --- a/tests/Service/UsersServiceTest.php +++ b/tests/Service/UsersServiceTest.php @@ -18,40 +18,39 @@ use Mainick\KeycloakClientBundle\Representation\UserSessionRepresentation; use Mainick\KeycloakClientBundle\Serializer\Serializer; use Mainick\KeycloakClientBundle\Service\Criteria; -use Mainick\KeycloakClientBundle\Service\HttpMethodEnum; use Mainick\KeycloakClientBundle\Service\UsersService; use Mainick\KeycloakClientBundle\Token\AccessToken; -use Mockery as m; +use Mainick\KeycloakClientBundle\Tests\Service\Support\ExecuteCommandTestHelperTrait; use PHPUnit\Framework\TestCase; -use Psr\Http\Message\ResponseInterface; -use Psr\Http\Message\StreamInterface; use Psr\Log\LoggerInterface; use Stevenmaguire\OAuth2\Client\Provider\Keycloak; class UsersServiceTest extends TestCase { + use ExecuteCommandTestHelperTrait; + private UsersService $usersService; - private m\MockInterface $httpClient; - private m\MockInterface $keycloakAdminClient; - private m\MockInterface $logger; - private m\MockInterface $serializer; + private ClientInterface $httpClient; + private KeycloakAdminClient $keycloakAdminClient; + private LoggerInterface $logger; + private Serializer $serializer; private AccessToken $adminAccessToken; protected function setUp(): void { parent::setUp(); - $this->httpClient = m::mock(ClientInterface::class); - $this->keycloakAdminClient = m::mock(KeycloakAdminClient::class); - $this->logger = m::mock(LoggerInterface::class); - $this->serializer = m::mock(Serializer::class); + $this->httpClient = $this->createMock(ClientInterface::class); + $this->keycloakAdminClient = $this->createStub(KeycloakAdminClient::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->serializer = $this->createMock(Serializer::class); - $keycloakProvider = m::mock(Keycloak::class); - $keycloakProvider->shouldReceive('getHttpClient')->andReturn($this->httpClient); + $keycloakProvider = $this->createStub(Keycloak::class); + $keycloakProvider->method('getHttpClient')->willReturn($this->httpClient); - $this->keycloakAdminClient->shouldReceive('getKeycloakProvider')->andReturn($keycloakProvider); - $this->keycloakAdminClient->shouldReceive('getBaseUrl')->andReturn('http://mock.url/auth'); - $this->keycloakAdminClient->shouldReceive('getVersion')->andReturn('17.0.1'); + $this->keycloakAdminClient->method('getKeycloakProvider')->willReturn($keycloakProvider); + $this->keycloakAdminClient->method('getBaseUrl')->willReturn('http://mock.url/auth'); + $this->keycloakAdminClient->method('getVersion')->willReturn('17.0.1'); $this->adminAccessToken = new AccessToken(); $this->adminAccessToken @@ -60,7 +59,7 @@ protected function setUp(): void ->setRefreshToken('mock_refresh_token') ->setValues(['scope' => 'email']); - $this->keycloakAdminClient->shouldReceive('getAdminAccessToken')->andReturn($this->adminAccessToken); + $this->keycloakAdminClient->method('getAdminAccessToken')->willReturn($this->adminAccessToken); $this->usersService = new UsersService( $this->logger, @@ -69,35 +68,25 @@ protected function setUp(): void $reflection = new \ReflectionClass($this->usersService); $serializerProperty = $reflection->getProperty('serializer'); - $serializerProperty->setAccessible(true); $serializerProperty->setValue($this->usersService, $this->serializer); } - protected function tearDown(): void - { - m::close(); - parent::tearDown(); - } - public function testAll(): void { // given $realm = 'test-realm'; $responseBody = '[{"id":"user1","username":"user1"},{"id":"user2","username":"user2"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/users', m::on(function($options) { - return isset($options['headers']['Authorization']) && - $options['headers']['Authorization'] === 'Bearer mock_token'; + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/users', $this->callback(static function ($options): bool { + return is_array($options) + && isset($options['headers']['Authorization']) + && 'Bearer mock_token' === $options['headers']['Authorization']; })) - ->andReturn($response); + ->willReturn($response); $userCollection = new UserCollection(); $user1 = new UserRepresentation(); @@ -110,11 +99,12 @@ public function testAll(): void $userCollection->add($user2); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, UserCollection::class) - ->andReturn($userCollection); + ->willReturn($userCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->usersService->all($realm); @@ -133,17 +123,13 @@ public function testAllWithCriteria(): void $criteria = new Criteria(['briefRepresentation' => 'true']); $realm = 'test-realm'; $responseBody = '[{"id":"user1","username":"user1"},{"id":"user2","username":"user2"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/users?briefRepresentation=true', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/users?briefRepresentation=true', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $userCollection = new UserCollection(); $user1 = new UserRepresentation(); @@ -156,11 +142,12 @@ public function testAllWithCriteria(): void $userCollection->add($user2); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, UserCollection::class) - ->andReturn($userCollection); + ->willReturn($userCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->usersService->all($realm, $criteria); @@ -179,17 +166,13 @@ public function testGet(): void $realm = 'test-realm'; $userId = 'user1'; $responseBody = '{"id":"user1","username":"user1","firstName":"John","lastName":"Doe"}'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/users/'.$userId, m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/users/'.$userId, $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $user = new UserRepresentation(); $user->id = 'user1'; @@ -198,11 +181,12 @@ public function testGet(): void $user->lastName = 'Doe'; $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, UserRepresentation::class) - ->andReturn($user); + ->willReturn($user); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->usersService->get($realm, $userId); @@ -221,24 +205,19 @@ public function testCount(): void // given $realm = 'test-realm'; $responseBody = '10'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/users/count', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/users/count', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $this->serializer - ->shouldReceive('deserialize') - ->with($responseBody, 'array') - ->andReturn(10); + ->expects($this->never()) + ->method('deserialize'); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->usersService->count($realm); @@ -259,21 +238,21 @@ public function testCreate(): void $user->email = 'newuser@example.com'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(201); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(201, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('POST', 'admin/realms/'.$realm.'/users', m::on(function($options) { + ->expects($this->once()) + ->method('request') + ->with('POST', 'admin/realms/'.$realm.'/users', $this->callback(static function ($options): bool { return isset($options['json']); })) - ->andReturn($response); + ->willReturn($response); + + $this->serializer + ->expects($this->never()) + ->method('deserialize'); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->usersService->create($realm, $user); @@ -295,21 +274,21 @@ public function testUpdate(): void $user->email = 'updated@example.com'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(204); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(204, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('PUT', 'admin/realms/'.$realm.'/users/'.$userId, m::on(function($options) { + ->expects($this->once()) + ->method('request') + ->with('PUT', 'admin/realms/'.$realm.'/users/'.$userId, $this->callback(static function ($options): bool { return isset($options['json']); })) - ->andReturn($response); + ->willReturn($response); + + $this->serializer + ->expects($this->never()) + ->method('deserialize'); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->usersService->update($realm, $userId, $user); @@ -324,19 +303,19 @@ public function testDelete(): void $realm = 'test-realm'; $userId = 'user1'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(204); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(204, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('DELETE', 'admin/realms/'.$realm.'/users/'.$userId, m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('DELETE', 'admin/realms/'.$realm.'/users/'.$userId, $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); + + $this->serializer + ->expects($this->never()) + ->method('deserialize'); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->usersService->delete($realm, $userId); @@ -351,23 +330,23 @@ public function testLogout(): void $realm = 'test-realm'; $userId = 'user1'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(204); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(204, $responseBody); $this->httpClient - ->shouldReceive('request') + ->expects($this->once()) + ->method('request') ->with( 'POST', 'admin/realms/'.$realm.'/users/'.$userId.'/logout', - m::type('array') + $this->callback(static fn ($options): bool => is_array($options)) ) - ->andReturn($response); + ->willReturn($response); - $this->logger->shouldReceive('info')->once(); + $this->serializer + ->expects($this->never()) + ->method('deserialize'); + + $this->logger->expects($this->once())->method('info'); // when $result = $this->usersService->logout($realm, $userId); @@ -382,17 +361,13 @@ public function testSessions(): void $realm = 'test-realm'; $userId = 'user1'; $responseBody = '[{"id":"session1","userId":"user1"},{"id":"session2","userId":"user1"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/users/'.$userId.'/sessions', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/users/'.$userId.'/sessions', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $sessionCollection = new UserSessionCollection(); $session1 = new UserSessionRepresentation(); @@ -405,11 +380,12 @@ public function testSessions(): void $sessionCollection->add($session2); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, UserSessionCollection::class) - ->andReturn($sessionCollection); + ->willReturn($sessionCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->usersService->sessions($realm, $userId); @@ -429,17 +405,13 @@ public function testOfflineSessions(): void $userId = 'user1'; $clientId = 'client1'; $responseBody = '[{"id":"session1","userId":"user1"},{"id":"session2","userId":"user1"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/users/'.$userId.'/offline-sessions/'.$clientId, m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/users/'.$userId.'/offline-sessions/'.$clientId, $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $sessionCollection = new UserSessionCollection(); $session1 = new UserSessionRepresentation(); @@ -452,11 +424,12 @@ public function testOfflineSessions(): void $sessionCollection->add($session2); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, UserSessionCollection::class) - ->andReturn($sessionCollection); + ->willReturn($sessionCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->usersService->offlineSessions($realm, $userId, $clientId); @@ -475,17 +448,13 @@ public function testGroups(): void $realm = 'test-realm'; $userId = 'user1'; $responseBody = '[{"id":"group1","name":"group1"},{"id":"group2","name":"group2"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/users/'.$userId.'/groups', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/users/'.$userId.'/groups', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $groupCollection = new GroupCollection(); $group1 = new GroupRepresentation(); @@ -498,11 +467,12 @@ public function testGroups(): void $groupCollection->add($group2); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, GroupCollection::class) - ->andReturn($groupCollection); + ->willReturn($groupCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->usersService->groups($realm, $userId); @@ -521,24 +491,19 @@ public function testGroupsCount(): void $realm = 'test-realm'; $userId = 'user1'; $responseBody = '2'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/users/'.$userId.'/groups/count', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/users/'.$userId.'/groups/count', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $this->serializer - ->shouldReceive('deserialize') - ->with($responseBody, 'array') - ->andReturn(2); + ->expects($this->never()) + ->method('deserialize'); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->usersService->groupsCount($realm, $userId); @@ -555,19 +520,19 @@ public function testJoinGroup(): void $userId = 'user1'; $groupId = 'group1'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(204); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(204, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('PUT', 'admin/realms/'.$realm.'/users/'.$userId.'/groups/'.$groupId, m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('PUT', 'admin/realms/'.$realm.'/users/'.$userId.'/groups/'.$groupId, $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); + + $this->serializer + ->expects($this->never()) + ->method('deserialize'); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->usersService->joinGroup($realm, $userId, $groupId); @@ -583,19 +548,19 @@ public function testLeaveGroup(): void $userId = 'user1'; $groupId = 'group1'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(204); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(204, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('DELETE', 'admin/realms/'.$realm.'/users/'.$userId.'/groups/'.$groupId, m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('DELETE', 'admin/realms/'.$realm.'/users/'.$userId.'/groups/'.$groupId, $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); + + $this->serializer + ->expects($this->never()) + ->method('deserialize'); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->usersService->leaveGroup($realm, $userId, $groupId); @@ -610,17 +575,13 @@ public function testRealmRoles(): void $realm = 'test-realm'; $userId = 'user1'; $responseBody = '[{"id":"role1","name":"role1"},{"id":"role2","name":"role2"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/users/'.$userId.'/role-mappings/realm', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/users/'.$userId.'/role-mappings/realm', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $roleCollection = new RoleCollection(); $role1 = new RoleRepresentation(); @@ -633,11 +594,12 @@ public function testRealmRoles(): void $roleCollection->add($role2); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, RoleCollection::class) - ->andReturn($roleCollection); + ->willReturn($roleCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->usersService->realmRoles($realm, $userId); @@ -656,17 +618,13 @@ public function testAvailableRealmRoles(): void $realm = 'test-realm'; $userId = 'user1'; $responseBody = '[{"id":"role3","name":"role3"},{"id":"role4","name":"role4"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/users/'.$userId.'/role-mappings/realm/available', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/users/'.$userId.'/role-mappings/realm/available', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $roleCollection = new RoleCollection(); $role3 = new RoleRepresentation(); @@ -679,11 +637,12 @@ public function testAvailableRealmRoles(): void $roleCollection->add($role4); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, RoleCollection::class) - ->andReturn($roleCollection); + ->willReturn($roleCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->usersService->availableRealmRoles($realm, $userId); @@ -706,21 +665,21 @@ public function testAddRealmRole(): void $role->name = 'role3'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(204); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(204, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('POST', 'admin/realms/'.$realm.'/users/'.$userId.'/role-mappings/realm', m::on(function($options) { + ->expects($this->once()) + ->method('request') + ->with('POST', 'admin/realms/'.$realm.'/users/'.$userId.'/role-mappings/realm', $this->callback(static function ($options): bool { return isset($options['json']); })) - ->andReturn($response); + ->willReturn($response); + + $this->serializer + ->expects($this->never()) + ->method('deserialize'); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->usersService->addRealmRole($realm, $userId, $role); @@ -739,21 +698,21 @@ public function testRemoveRealmRole(): void $role->name = 'role1'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(204); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(204, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('DELETE', 'admin/realms/'.$realm.'/users/'.$userId.'/role-mappings/realm', m::on(function($options) { + ->expects($this->once()) + ->method('request') + ->with('DELETE', 'admin/realms/'.$realm.'/users/'.$userId.'/role-mappings/realm', $this->callback(static function ($options): bool { return isset($options['json']); })) - ->andReturn($response); + ->willReturn($response); + + $this->serializer + ->expects($this->never()) + ->method('deserialize'); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->usersService->removeRealmRole($realm, $userId, $role); @@ -769,17 +728,13 @@ public function testClientRoles(): void $clientUuid = 'client1'; $userId = 'user1'; $responseBody = '[{"id":"role1","name":"role1"},{"id":"role2","name":"role2"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/users/'.$userId.'/role-mappings/clients/'.$clientUuid, m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/users/'.$userId.'/role-mappings/clients/'.$clientUuid, $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $roleCollection = new RoleCollection(); $role1 = new RoleRepresentation(); @@ -792,11 +747,12 @@ public function testClientRoles(): void $roleCollection->add($role2); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, RoleCollection::class) - ->andReturn($roleCollection); + ->willReturn($roleCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->usersService->clientRoles($realm, $clientUuid, $userId); @@ -816,17 +772,13 @@ public function testAvailableClientRoles(): void $clientUuid = 'client1'; $userId = 'user1'; $responseBody = '[{"id":"role3","name":"role3"},{"id":"role4","name":"role4"}]'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/users/'.$userId.'/role-mappings/clients/'.$clientUuid.'/available', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/users/'.$userId.'/role-mappings/clients/'.$clientUuid.'/available', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $roleCollection = new RoleCollection(); $role3 = new RoleRepresentation(); @@ -839,11 +791,12 @@ public function testAvailableClientRoles(): void $roleCollection->add($role4); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, RoleCollection::class) - ->andReturn($roleCollection); + ->willReturn($roleCollection); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->usersService->availableClientRoles($realm, $clientUuid, $userId); @@ -867,21 +820,21 @@ public function testAddClientRole(): void $role->name = 'role3'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(204); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(204, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('POST', 'admin/realms/'.$realm.'/users/'.$userId.'/role-mappings/clients/'.$clientUuid, m::on(function($options) { + ->expects($this->once()) + ->method('request') + ->with('POST', 'admin/realms/'.$realm.'/users/'.$userId.'/role-mappings/clients/'.$clientUuid, $this->callback(static function ($options): bool { return isset($options['json']); })) - ->andReturn($response); + ->willReturn($response); - $this->logger->shouldReceive('info')->once(); + $this->serializer + ->expects($this->never()) + ->method('deserialize'); + + $this->logger->expects($this->once())->method('info'); // when $result = $this->usersService->addClientRole($realm, $clientUuid, $userId, $role); @@ -901,21 +854,21 @@ public function testRemoveClientRole(): void $role->name = 'role1'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(204); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(204, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('DELETE', 'admin/realms/'.$realm.'/users/'.$userId.'/role-mappings/clients/'.$clientUuid, m::on(function($options) { + ->expects($this->once()) + ->method('request') + ->with('DELETE', 'admin/realms/'.$realm.'/users/'.$userId.'/role-mappings/clients/'.$clientUuid, $this->callback(static function ($options): bool { return isset($options['json']); })) - ->andReturn($response); + ->willReturn($response); - $this->logger->shouldReceive('info')->once(); + $this->serializer + ->expects($this->never()) + ->method('deserialize'); + + $this->logger->expects($this->once())->method('info'); // when $result = $this->usersService->removeClientRole($realm, $clientUuid, $userId, $role); @@ -929,26 +882,23 @@ public function testGetProfileConfig(): void // given $realm = 'test-realm'; $responseBody = '{"attributes":[{"name":"firstName","displayName":"First name"}]}'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/users/profile', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/users/profile', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $config = new UPConfig(); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, UPConfig::class) - ->andReturn($config); + ->willReturn($config); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->usersService->getProfileConfig($realm); @@ -963,26 +913,23 @@ public function testGetProfileMetadata(): void // given $realm = 'test-realm'; $responseBody = '{"userProfileAttributeMetadata":[{"name":"firstName","displayName":"First name"}]}'; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(200); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(200, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('GET', 'admin/realms/'.$realm.'/users/profile/metadata', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('GET', 'admin/realms/'.$realm.'/users/profile/metadata', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); $metadata = new UserProfileMetadata(); $this->serializer - ->shouldReceive('deserialize') + ->expects($this->once()) + ->method('deserialize') ->with($responseBody, UserProfileMetadata::class) - ->andReturn($metadata); + ->willReturn($metadata); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->usersService->getProfileMetadata($realm); @@ -998,19 +945,19 @@ public function testResetPassword(): void $realm = 'test-realm'; $userId = 'user1'; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(204); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(204, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('PUT', 'admin/realms/'.$realm.'/users/'.$userId.'/reset-password', m::type('array')) - ->andReturn($response); + ->expects($this->once()) + ->method('request') + ->with('PUT', 'admin/realms/'.$realm.'/users/'.$userId.'/reset-password', $this->callback(static fn ($options): bool => is_array($options))) + ->willReturn($response); + + $this->serializer + ->expects($this->never()) + ->method('deserialize'); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->usersService->resetPassword($realm, $userId); @@ -1026,21 +973,21 @@ public function testSendVerifyEmail(): void $userId = 'user1'; $parameters = ['clientId' => 'client1', 'redirectUri' => 'http://example.com']; $responseBody = ''; - $stream = $this->createMock(StreamInterface::class); - $stream->method('getContents')->willReturn($responseBody); - - $response = m::mock(ResponseInterface::class); - $response->shouldReceive('getStatusCode')->andReturn(204); - $response->shouldReceive('getBody')->andReturn($stream); + $response = $this->createCommandResponse(204, $responseBody); $this->httpClient - ->shouldReceive('request') - ->with('PUT', 'admin/realms/'.$realm.'/users/'.$userId.'/send-verify-email', m::on(function($options) { + ->expects($this->once()) + ->method('request') + ->with('PUT', 'admin/realms/'.$realm.'/users/'.$userId.'/send-verify-email', $this->callback(static function ($options): bool { return isset($options['json']); })) - ->andReturn($response); + ->willReturn($response); + + $this->serializer + ->expects($this->never()) + ->method('deserialize'); - $this->logger->shouldReceive('info')->once(); + $this->logger->expects($this->once())->method('info'); // when $result = $this->usersService->sendVerifyEmail($realm, $userId, $parameters); diff --git a/tests/Token/JWKSTokenDecoderTest.php b/tests/Token/JWKSTokenDecoderTest.php index ea41a49..35ce8e9 100644 --- a/tests/Token/JWKSTokenDecoderTest.php +++ b/tests/Token/JWKSTokenDecoderTest.php @@ -14,7 +14,7 @@ class JWKSTokenDecoderTest extends TestCase { public function testConstructorValidatesBaseUrl(): void { - $httpClient = $this->createMock(ClientInterface::class); + $httpClient = $this->createStub(ClientInterface::class); $this->expectException(TokenDecoderException::class); $this->expectExceptionMessage('Invalid base_url format'); @@ -29,7 +29,7 @@ public function testConstructorValidatesBaseUrl(): void public function testConstructorRejectsHttpForNonLocalhost(): void { - $httpClient = $this->createMock(ClientInterface::class); + $httpClient = $this->createStub(ClientInterface::class); $this->expectException(TokenDecoderException::class); $this->expectExceptionMessage('HTTP is only allowed for localhost'); @@ -44,7 +44,7 @@ public function testConstructorRejectsHttpForNonLocalhost(): void public function testConstructorAcceptsHttpForLocalhost(): void { - $httpClient = $this->createMock(ClientInterface::class); + $httpClient = $this->createStub(ClientInterface::class); $decoder = new JWKSTokenDecoder( $httpClient, @@ -58,7 +58,7 @@ public function testConstructorAcceptsHttpForLocalhost(): void public function testConstructorRejectsPrivateIpRanges(): void { - $httpClient = $this->createMock(ClientInterface::class); + $httpClient = $this->createStub(ClientInterface::class); $this->expectException(TokenDecoderException::class); $this->expectExceptionMessage('is not allowed'); @@ -73,7 +73,7 @@ public function testConstructorRejectsPrivateIpRanges(): void public function testConstructorRejectsMetadataEndpoints(): void { - $httpClient = $this->createMock(ClientInterface::class); + $httpClient = $this->createStub(ClientInterface::class); $this->expectException(TokenDecoderException::class); $this->expectExceptionMessage('is not allowed'); @@ -88,7 +88,7 @@ public function testConstructorRejectsMetadataEndpoints(): void public function testConstructorAcceptsValidHttpsUrl(): void { - $httpClient = $this->createMock(ClientInterface::class); + $httpClient = $this->createStub(ClientInterface::class); $decoder = new JWKSTokenDecoder( $httpClient, [ @@ -101,7 +101,7 @@ public function testConstructorAcceptsValidHttpsUrl(): void public function testFetchJwksValidatesDomainWhitelist(): void { - $httpClient = $this->createMock(ClientInterface::class); + $httpClient = $this->createStub(ClientInterface::class); $decoder = new JWKSTokenDecoder( $httpClient, [ @@ -127,7 +127,7 @@ public function testFetchJwksValidatesDomainWhitelist(): void public function testFetchJwksAllowsBaseUrlDomainByDefault(): void { // Create a mock stream for the response body - $stream = $this->createMock(StreamInterface::class); + $stream = $this->createStub(StreamInterface::class); $stream->method('getContents')->willReturn(json_encode([ 'keys' => [ [ @@ -141,11 +141,11 @@ public function testFetchJwksAllowsBaseUrlDomainByDefault(): void ], JSON_THROW_ON_ERROR)); // Create a mock response - $response = $this->createMock(Response::class); + $response = $this->createStub(Response::class); $response->method('getBody')->willReturn($stream); // Create a mock HTTP client - $httpClient = $this->createMock(ClientInterface::class); + $httpClient = $this->createStub(ClientInterface::class); $httpClient->method('request')->willReturn($response); $decoder = new JWKSTokenDecoder( @@ -163,7 +163,7 @@ public function testFetchJwksAllowsBaseUrlDomainByDefault(): void public function testWildcardDomainMatching(): void { // Create a mock stream for the response body - $stream = $this->createMock(StreamInterface::class); + $stream = $this->createStub(StreamInterface::class); $stream->method('getContents')->willReturn(json_encode([ 'keys' => [ [ @@ -177,11 +177,11 @@ public function testWildcardDomainMatching(): void ], JSON_THROW_ON_ERROR)); // Create a mock response - $response = $this->createMock(Response::class); + $response = $this->createStub(Response::class); $response->method('getBody')->willReturn($stream); // Create a mock HTTP client - $httpClient = $this->createMock(ClientInterface::class); + $httpClient = $this->createStub(ClientInterface::class); $httpClient->method('request')->willReturn($response); $decoder = new JWKSTokenDecoder( @@ -202,7 +202,7 @@ public function testJwksUrlValidationRejectsHttpForNonLocalhost(): void // for non-localhost domains by configuring an HTTP base URL directly. // Create a mock HTTP client - $httpClient = $this->createMock(ClientInterface::class); + $httpClient = $this->createStub(ClientInterface::class); $this->expectException(TokenDecoderException::class); $this->expectExceptionMessage('HTTP is only allowed for localhost.'); @@ -265,7 +265,7 @@ public function testDecodeThrowsExceptionForMissingKid(): void $payloadEncoded = rtrim(strtr(base64_encode($payload), '+/', '-_'), '='); $token = "$headerEncoded.$payloadEncoded.fake-signature"; - $httpClient = $this->createMock(ClientInterface::class); + $httpClient = $this->createStub(ClientInterface::class); $decoder = new JWKSTokenDecoder($httpClient, [ 'base_url' => 'https://keycloak.example.com', @@ -295,7 +295,7 @@ public function testDecodeThrowsExceptionForAlgorithmMismatch(): void $payloadEncoded = rtrim(strtr(base64_encode($payload), '+/', '-_'), '='); $token = "$headerEncoded.$payloadEncoded.fake-signature"; - $httpClient = $this->createMock(ClientInterface::class); + $httpClient = $this->createStub(ClientInterface::class); // Decoder expects RS256 $decoder = new JWKSTokenDecoder($httpClient, [ @@ -340,13 +340,13 @@ public function testDecodeThrowsExceptionForKidNotFoundInJwks(): void ], ]; - $stream = $this->createMock(StreamInterface::class); + $stream = $this->createStub(StreamInterface::class); $stream->method('getContents')->willReturn(json_encode($jwksData, JSON_THROW_ON_ERROR)); - $response = $this->createMock(Response::class); + $response = $this->createStub(Response::class); $response->method('getBody')->willReturn($stream); - $httpClient = $this->createMock(ClientInterface::class); + $httpClient = $this->createStub(ClientInterface::class); $httpClient->method('request')->willReturn($response); $decoder = new JWKSTokenDecoder($httpClient, [ @@ -362,7 +362,7 @@ public function testDecodeThrowsExceptionForKidNotFoundInJwks(): void public function testValidateTokenThrowsExceptionForExpiredToken(): void { - $httpClient = $this->createMock(ClientInterface::class); + $httpClient = $this->createStub(ClientInterface::class); $decoder = new JWKSTokenDecoder($httpClient, [ 'base_url' => 'https://keycloak.example.com', @@ -383,7 +383,7 @@ public function testValidateTokenThrowsExceptionForExpiredToken(): void public function testValidateTokenThrowsExceptionForInvalidIssuer(): void { - $httpClient = $this->createMock(ClientInterface::class); + $httpClient = $this->createStub(ClientInterface::class); $decoder = new JWKSTokenDecoder($httpClient, [ 'base_url' => 'https://keycloak.example.com', @@ -404,7 +404,7 @@ public function testValidateTokenThrowsExceptionForInvalidIssuer(): void public function testValidateTokenAcceptsValidToken(): void { - $httpClient = $this->createMock(ClientInterface::class); + $httpClient = $this->createStub(ClientInterface::class); $decoder = new JWKSTokenDecoder($httpClient, [ 'base_url' => 'https://keycloak.example.com', @@ -426,7 +426,7 @@ public function testValidateTokenAcceptsValidToken(): void public function testDecodeThrowsExceptionForInvalidJwtFormat(): void { - $httpClient = $this->createMock(ClientInterface::class); + $httpClient = $this->createStub(ClientInterface::class); $decoder = new JWKSTokenDecoder($httpClient, [ 'base_url' => 'https://keycloak.example.com', @@ -460,13 +460,13 @@ public function testDecodeThrowsExceptionForEmptyJwksKeys(): void // Mock JWKS with empty keys array $jwksData = ['keys' => []]; - $stream = $this->createMock(StreamInterface::class); + $stream = $this->createStub(StreamInterface::class); $stream->method('getContents')->willReturn(json_encode($jwksData, JSON_THROW_ON_ERROR)); - $response = $this->createMock(Response::class); + $response = $this->createStub(Response::class); $response->method('getBody')->willReturn($stream); - $httpClient = $this->createMock(ClientInterface::class); + $httpClient = $this->createStub(ClientInterface::class); $httpClient->method('request')->willReturn($response); $decoder = new JWKSTokenDecoder($httpClient, [