diff --git a/providers/GoFeatureFlag/.php-cs-fixer.cache b/providers/GoFeatureFlag/.php-cs-fixer.cache new file mode 100644 index 0000000..f835355 --- /dev/null +++ b/providers/GoFeatureFlag/.php-cs-fixer.cache @@ -0,0 +1 @@ +{"php":"8.0.30","version":"3.57.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":{"default":"at_least_single_space"},"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"imports_order":["class","function","const"],"sort_algorithm":"none"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":{"only_dec_inc":true},"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"attribute_placement":"ignore","on_multiline":"ensure_fully_multiline"},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true},"hashes":{"tests\/unit\/controller\/OfrepApiTest.php":"ffc7e3ab94b4c58eb9b413fef1bef9de","tests\/unit\/GoFeatureFlagProviderTest.php":"062d381339c3bb95bd08b9fa05d1462a","tests\/TestCase.php":"efc0a05509f7101e640d29ec146c0aff","src\/util\/Validator.php":"9732fd68e5ebbac5b3139995077ce8de","src\/config\/Config.php":"eb1e5935642272c77c474adcbbcb18dd","src\/controller\/OfrepApi.php":"2c01a1ada0d53bb066b8c910d0d0de7d","src\/GoFeatureFlagProvider.php":"5dd7f706ef1705395a1f881b90744736","src\/model\/OfrepApiResponse.php":"b34e7d13b252e0c6a722dbaefcbf2862","src\/exception\/RateLimitedException.php":"09d2000c11a7ac0fd523c4d1f52d2f7c","src\/exception\/BaseGoffException.php":"4c7285b637c8ad99d1adcae77a823dd7","src\/exception\/UnknownOfrepException.php":"418a19d2eedd16d46712c524a3f49183","src\/exception\/BaseOfrepException.php":"272845dff81eba1807364a76ae2059d8","src\/exception\/UnauthorizedException.php":"07831e991ffb4fcd213c20a47de8d412","src\/exception\/InvalidConfigException.php":"9f3bf6f27ac4ba2b95e993cf0f45e636","src\/exception\/ParseException.php":"b641c7a21d0c498616bd904a1e0b0745","src\/exception\/InvalidContextException.php":"48cb0b86fa5fb2094a55fb058a5fb176","src\/exception\/FlagNotFoundException.php":"5ab85e04a92c770a86c5a4d0afb69292"}} \ No newline at end of file diff --git a/providers/GoFeatureFlag/README.md b/providers/GoFeatureFlag/README.md index 8eb5b80..5ca142c 100644 --- a/providers/GoFeatureFlag/README.md +++ b/providers/GoFeatureFlag/README.md @@ -62,7 +62,7 @@ $api->setProvider($provider); $client = $api->getClient(); $evaluationContext = new MutableEvaluationContext( "214b796a-807b-4697-b3a3-42de0ec10a37", - new Attributes(["email" => "contact@gofeatureflag.org"]) + new Attributes(["email" => 'contact@gofeatureflag.org']) ); $value = $client->getBooleanDetails('integer_key', false, $evaluationContext); diff --git a/providers/GoFeatureFlag/src/GoFeatureFlagProvider.php b/providers/GoFeatureFlag/src/GoFeatureFlagProvider.php index a0384d8..19cfb6d 100644 --- a/providers/GoFeatureFlag/src/GoFeatureFlagProvider.php +++ b/providers/GoFeatureFlag/src/GoFeatureFlagProvider.php @@ -4,6 +4,11 @@ namespace OpenFeature\Providers\GoFeatureFlag; +use OpenFeature\Providers\GoFeatureFlag\config\Config; +use OpenFeature\Providers\GoFeatureFlag\controller\OfrepApi; +use OpenFeature\Providers\GoFeatureFlag\exception\BaseOfrepException; +use OpenFeature\Providers\GoFeatureFlag\exception\InvalidConfigException; +use OpenFeature\Providers\GoFeatureFlag\util\Validator; use OpenFeature\implementation\common\Metadata; use OpenFeature\implementation\provider\AbstractProvider; use OpenFeature\implementation\provider\ResolutionDetailsBuilder; @@ -13,15 +18,16 @@ use OpenFeature\interfaces\provider\Provider; use OpenFeature\interfaces\provider\Reason; use OpenFeature\interfaces\provider\ResolutionDetails; -use OpenFeature\Providers\GoFeatureFlag\config\Config; -use OpenFeature\Providers\GoFeatureFlag\controller\OfrepApi; -use OpenFeature\Providers\GoFeatureFlag\exception\BaseOfrepException; -use OpenFeature\Providers\GoFeatureFlag\exception\InvalidConfigException; -use OpenFeature\Providers\GoFeatureFlag\util\Validator; +use Throwable; + +use function array_key_exists; +use function gettype; +use function implode; +use function is_array; class GoFeatureFlagProvider extends AbstractProvider implements Provider { - protected static string $CLIENT_NAME = 'GO Feature Flag Provider'; + protected static string $NAME = 'GO Feature Flag Provider'; private OfrepApi $ofrepApi; /** @@ -30,15 +36,15 @@ class GoFeatureFlagProvider extends AbstractProvider implements Provider public function __construct(Config $config) { Validator::validateConfig($config); - if (is_array($config->getCustomHeaders()) && !array_key_exists("Content-Type", $config->getCustomHeaders())) { - $config->getCustomHeaders()["Content-Type"] = "application/json"; + if (is_array($config->getCustomHeaders()) && !array_key_exists('Content-Type', $config->getCustomHeaders())) { + $config->getCustomHeaders()['Content-Type'] = 'application/json'; } $this->ofrepApi = new OfrepApi($config); } public function getMetadata(): Metadata { - return new Metadata(self::$CLIENT_NAME); + return new Metadata(static::$NAME); } public function resolveBooleanValue(string $flagKey, bool $defaultValue, ?EvaluationContext $context = null): ResolutionDetails @@ -49,7 +55,7 @@ public function resolveBooleanValue(string $flagKey, bool $defaultValue, ?Evalua /** * @param array $allowedClasses */ - private function evaluate(string $flagKey, mixed $defaultValue, array $allowedClasses, EvaluationContext $evaluationContext = null): ResolutionDetails + private function evaluate(string $flagKey, mixed $defaultValue, array $allowedClasses, ?EvaluationContext $evaluationContext = null): ResolutionDetails { try { Validator::validateEvaluationContext($evaluationContext); @@ -60,8 +66,9 @@ private function evaluate(string $flagKey, mixed $defaultValue, array $allowedCl if ($apiResp->isError()) { $err = new ResolutionError( $apiResp->getErrorCode() ?? ErrorCode::GENERAL(), - $apiResp->getErrorDetails() + $apiResp->getErrorDetails(), ); + return (new ResolutionDetailsBuilder()) ->withValue($defaultValue) ->withError($err) @@ -74,32 +81,37 @@ private function evaluate(string $flagKey, mixed $defaultValue, array $allowedCl ->withReason(Reason::ERROR) ->withError(new ResolutionError( ErrorCode::TYPE_MISMATCH(), - "Invalid type for $flagKey, got " . gettype($apiResp->getValue()) . " expected " . implode(", ", $allowedClasses))) + "Invalid type for $flagKey, got " . gettype($apiResp->getValue()) . ' expected ' . implode(', ', $allowedClasses), + )) ->withValue($defaultValue) ->build(); } + return (new ResolutionDetailsBuilder()) ->withValue($apiResp->getValue()) ->withReason($apiResp->getReason()) ->withVariant($apiResp->getVariant()) ->build(); - } catch (BaseOfrepException $e) { $err = new ResolutionError($e->getErrorCode(), $e->getMessage()); + return (new ResolutionDetailsBuilder()) ->withValue($defaultValue) ->withError($err) ->withReason(Reason::ERROR) ->build(); - } catch (\Exception $e) { + } catch (Throwable $e) { return (new ResolutionDetailsBuilder()) ->withValue($defaultValue) - ->withError(new ResolutionError(ErrorCode::GENERAL(), "An error occurred while evaluating the flag: " . $e->getMessage())) + ->withError(new ResolutionError(ErrorCode::GENERAL(), 'An error occurred while evaluating the flag: ' . $e->getMessage())) ->withReason(Reason::ERROR) ->build(); } } + /** + * @param array $allowedClasses + */ private function isValidType(mixed $value, array $allowedClasses): bool { foreach ($allowedClasses as $class) { @@ -107,6 +119,7 @@ private function isValidType(mixed $value, array $allowedClasses): bool return true; } } + return false; } diff --git a/providers/GoFeatureFlag/src/config/Config.php b/providers/GoFeatureFlag/src/config/Config.php index 41bb165..83e53f4 100644 --- a/providers/GoFeatureFlag/src/config/Config.php +++ b/providers/GoFeatureFlag/src/config/Config.php @@ -7,27 +7,27 @@ class Config { private string $endpoint; + /** + * @var array + */ private array $customHeaders = []; - public function __construct(string $endpoint, ?string $apiKey = '', ?array $custom_headers = []) + public function __construct(string $endpoint, ?string $apiKey = '', ?array $customHeaders = []) { $this->endpoint = $endpoint; - $this->customHeaders = $custom_headers; + $this->customHeaders = $customHeaders; if ($apiKey !== null && $apiKey !== '') { $this->customHeaders['Authorization'] = 'Bearer ' . $apiKey; } } - /** - * @return string - */ public function getEndpoint(): string { return $this->endpoint; } /** - * @return array + * @return array */ public function getCustomHeaders(): array { diff --git a/providers/GoFeatureFlag/src/controller/OfrepApi.php b/providers/GoFeatureFlag/src/controller/OfrepApi.php index ef42abd..64af750 100644 --- a/providers/GoFeatureFlag/src/controller/OfrepApi.php +++ b/providers/GoFeatureFlag/src/controller/OfrepApi.php @@ -4,10 +4,8 @@ namespace OpenFeature\Providers\GoFeatureFlag\controller; -use Exception; use GuzzleHttp\Client; use GuzzleHttp\Exception\GuzzleException; -use OpenFeature\interfaces\flags\EvaluationContext; use OpenFeature\Providers\GoFeatureFlag\config\Config; use OpenFeature\Providers\GoFeatureFlag\exception\BaseOfrepException; use OpenFeature\Providers\GoFeatureFlag\exception\FlagNotFoundException; @@ -16,7 +14,17 @@ use OpenFeature\Providers\GoFeatureFlag\exception\UnauthorizedException; use OpenFeature\Providers\GoFeatureFlag\exception\UnknownOfrepException; use OpenFeature\Providers\GoFeatureFlag\model\OfrepApiResponse; +use OpenFeature\interfaces\flags\EvaluationContext; use Psr\Http\Message\ResponseInterface; +use Throwable; + +use function array_merge; +use function is_numeric; +use function json_decode; +use function json_encode; +use function rtrim; +use function strtotime; +use function time; class OfrepApi { @@ -51,10 +59,10 @@ public function evaluate(string $flagKey, EvaluationContext $evaluationContext): } } - $base_uri = $this->options->getEndpoint(); - $evaluateApiPath = rtrim($base_uri, '/') . "/ofrep/v1/evaluate/flags/{$flagKey}"; + $baseUri = $this->options->getEndpoint(); + $evaluateApiPath = rtrim($baseUri, '/') . "/ofrep/v1/evaluate/flags/{$flagKey}"; $headers = [ - 'Content-Type' => 'application/json' + 'Content-Type' => 'application/json', ]; if ($this->options->getCustomHeaders() !== null) { @@ -63,13 +71,13 @@ public function evaluate(string $flagKey, EvaluationContext $evaluationContext): $fields = array_merge( $evaluationContext->getAttributes()->toArray(), - ['targetingKey' => $evaluationContext->getTargetingKey()] + ['targetingKey' => $evaluationContext->getTargetingKey()], ); $requestBody = json_encode(['context' => $fields]); $response = $this->client->post($evaluateApiPath, [ 'headers' => $headers, - 'body' => $requestBody + 'body' => $requestBody, ]); switch ($response->getStatusCode()) { @@ -84,13 +92,14 @@ public function evaluate(string $flagKey, EvaluationContext $evaluationContext): throw new FlagNotFoundException($flagKey, $response); case 429: $this->parseRetryLaterHeader($response); + throw new RateLimitedException($response); default: throw new UnknownOfrepException($response); } } catch (BaseOfrepException $e) { throw $e; - } catch (GuzzleException|Exception $e) { + } catch (GuzzleException | Throwable $e) { throw new UnknownOfrepException(null, $e); } } @@ -101,6 +110,7 @@ public function evaluate(string $flagKey, EvaluationContext $evaluationContext): private function parseSuccessResponse(ResponseInterface $response): OfrepApiResponse { $parsed = json_decode($response->getBody()->getContents(), true); + return OfrepApiResponse::createSuccessResponse($parsed); } @@ -110,6 +120,7 @@ private function parseSuccessResponse(ResponseInterface $response): OfrepApiResp private function parseErrorResponse(ResponseInterface $response): OfrepApiResponse { $parsed = json_decode($response->getBody()->getContents(), true); + return OfrepApiResponse::createErrorResponse($parsed); } @@ -119,7 +130,7 @@ private function parseRetryLaterHeader(ResponseInterface $response): void if ($retryAfterHeader) { if (is_numeric($retryAfterHeader)) { // Retry-After is in seconds - $this->retryAfter = time() + (int)$retryAfterHeader; + $this->retryAfter = time() + (int) $retryAfterHeader; } else { // Retry-After is in HTTP-date format $this->retryAfter = strtotime($retryAfterHeader); diff --git a/providers/GoFeatureFlag/src/exception/BaseGoffException.php b/providers/GoFeatureFlag/src/exception/BaseGoffException.php index b5124e7..cc2221f 100644 --- a/providers/GoFeatureFlag/src/exception/BaseGoffException.php +++ b/providers/GoFeatureFlag/src/exception/BaseGoffException.php @@ -4,16 +4,18 @@ namespace OpenFeature\Providers\GoFeatureFlag\exception; +use Exception; use OpenFeature\interfaces\provider\ErrorCode; use Psr\Http\Message\ResponseInterface; +use Throwable; -abstract class BaseGoffException extends \Exception +abstract class BaseGoffException extends Exception { private string $customMessage; private ?ResponseInterface $response; private ErrorCode $errorCode; - public function __construct(string $message, ErrorCode $errorCode, ?ResponseInterface $response, int $code = 0, \Exception $previous = null) + public function __construct(string $message, ErrorCode $errorCode, ?ResponseInterface $response, int $code = 0, ?Throwable $previous = null) { $this->customMessage = $message; $this->response = $response; @@ -35,4 +37,4 @@ public function getErrorCode(): ErrorCode { return $this->errorCode; } -} \ No newline at end of file +} diff --git a/providers/GoFeatureFlag/src/exception/BaseOfrepException.php b/providers/GoFeatureFlag/src/exception/BaseOfrepException.php index 0e9e132..fa33d8a 100644 --- a/providers/GoFeatureFlag/src/exception/BaseOfrepException.php +++ b/providers/GoFeatureFlag/src/exception/BaseOfrepException.php @@ -4,16 +4,18 @@ namespace OpenFeature\Providers\GoFeatureFlag\exception; +use Exception; use OpenFeature\interfaces\provider\ErrorCode; use Psr\Http\Message\ResponseInterface; +use Throwable; -abstract class BaseOfrepException extends \Exception +abstract class BaseOfrepException extends Exception { private string $customMessage; private ?ResponseInterface $response; private ErrorCode $errorCode; - public function __construct(string $message, ErrorCode $errorCode, ?ResponseInterface $response, int $code = 0, \Exception $previous = null) + public function __construct(string $message, ErrorCode $errorCode, ?ResponseInterface $response, int $code = 0, ?Throwable $previous = null) { $this->customMessage = $message; $this->response = $response; @@ -31,11 +33,8 @@ public function getResponse(): ?ResponseInterface return $this->response; } - /** - * @return ErrorCode - */ public function getErrorCode(): ErrorCode { return $this->errorCode; } -} \ No newline at end of file +} diff --git a/providers/GoFeatureFlag/src/exception/FlagNotFoundException.php b/providers/GoFeatureFlag/src/exception/FlagNotFoundException.php index 475f44f..caf3b20 100644 --- a/providers/GoFeatureFlag/src/exception/FlagNotFoundException.php +++ b/providers/GoFeatureFlag/src/exception/FlagNotFoundException.php @@ -1,5 +1,7 @@ customMessage = $message; parent::__construct($message, $code, $previous); @@ -16,4 +21,4 @@ public function getCustomMessage(): string { return $this->customMessage; } -} \ No newline at end of file +} diff --git a/providers/GoFeatureFlag/src/exception/InvalidContextException.php b/providers/GoFeatureFlag/src/exception/InvalidContextException.php index 59d1101..fbaf00b 100644 --- a/providers/GoFeatureFlag/src/exception/InvalidContextException.php +++ b/providers/GoFeatureFlag/src/exception/InvalidContextException.php @@ -1,12 +1,13 @@ $metadata + */ private function __construct( - $value, string $key, string $reason, ?string $variant, ?ErrorCode $errorCode, - ?string $errorDetails, array $metadata = []) - { + mixed $value, + string $key, + string $reason, + ?string $variant, + ?ErrorCode $errorCode, + ?string $errorDetails, + array $metadata = [], + ) { $this->value = $value; $this->key = $key; $this->reason = $reason; @@ -33,19 +43,22 @@ private function __construct( } /** + * @param array $apiData + * * @throws ParseException */ public static function createErrorResponse(array $apiData): OfrepApiResponse { Validator::validateErrorApiResponse($apiData); + return new OfrepApiResponse( null, - $apiData["key"], + $apiData['key'], Reason::ERROR, null, - OfrepApiResponse::errorCodeMapper($apiData["errorCode"]), - $apiData["errorDetails"], - [] + self::errorCodeMapper($apiData['errorCode']), + $apiData['errorDetails'], + [], ); } @@ -63,6 +76,8 @@ private static function errorCodeMapper(string $errorCode): ErrorCode } /** + * @param array $apiData + * * @throws ParseException */ public static function createSuccessResponse(array $apiData): OfrepApiResponse @@ -71,8 +86,9 @@ public static function createSuccessResponse(array $apiData): OfrepApiResponse $value = $apiData['value']; $key = $apiData['key']; $variant = $apiData['variant']; - $reason = OfrepApiResponse::reasonMapper($apiData['reason']); + $reason = self::reasonMapper($apiData['reason']); $metadata = $apiData['metadata'] ?? []; + return new OfrepApiResponse($value, $key, $reason, $variant, null, null, $metadata); } @@ -93,56 +109,38 @@ public function isError(): bool return $this->errorCode !== null; } - /** - * @return mixed - */ public function getValue(): mixed { return $this->value; } - /** - * @return string - */ public function getKey(): string { return $this->key; } - /** - * @return string - */ public function getReason(): string { return $this->reason; } - /** - * @return ?string - */ public function getVariant(): ?string { return $this->variant; } - /** - * @return ?ErrorCode - */ public function getErrorCode(): ?ErrorCode { return $this->errorCode; } - /** - * @return ?string - */ public function getErrorDetails(): ?string { return $this->errorDetails; } /** - * @return ?array + * @return ?array */ public function getMetadata(): ?array { diff --git a/providers/GoFeatureFlag/src/util/Validator.php b/providers/GoFeatureFlag/src/util/Validator.php index 9eea7ca..e00343e 100644 --- a/providers/GoFeatureFlag/src/util/Validator.php +++ b/providers/GoFeatureFlag/src/util/Validator.php @@ -4,18 +4,28 @@ namespace OpenFeature\Providers\GoFeatureFlag\util; - -use OpenFeature\interfaces\flags\EvaluationContext; use OpenFeature\Providers\GoFeatureFlag\config\Config; use OpenFeature\Providers\GoFeatureFlag\exception\InvalidConfigException; use OpenFeature\Providers\GoFeatureFlag\exception\InvalidContextException; use OpenFeature\Providers\GoFeatureFlag\exception\ParseException; +use OpenFeature\interfaces\flags\EvaluationContext; + +use function array_diff; +use function array_keys; +use function filter_var; +use function implode; +use function is_array; +use function is_string; +use function key_exists; +use function sizeof; + +use const FILTER_VALIDATE_URL; class Validator { /** - * @param Config $config - The configuration object to validate - * @return void + * @param ?Config $config - The configuration object to validate + * * @throws InvalidConfigException - if the config is invalid we return an error */ public static function validateConfig(?Config $config): void @@ -27,8 +37,8 @@ public static function validateConfig(?Config $config): void } /** - * @param string $endpoint - * @return void + * @param string $endpoint - The endpoint to validate + * * @throws InvalidConfigException */ private static function validateEndpoint(string $endpoint): void @@ -39,15 +49,17 @@ private static function validateEndpoint(string $endpoint): void } /** + * @param array $data - The data to validate + * * @throws ParseException */ public static function validateSuccessApiResponse(array $data): void { $requiredKeys = ['key', 'value', 'reason', 'variant']; $missingKeys = array_diff($requiredKeys, array_keys($data)); - if (!empty($missingKeys)) { + if (sizeof($missingKeys) !== 0) { throw new ParseException( - "missing keys in the success response: " . implode(', ', $missingKeys) + 'missing keys in the success response: ' . implode(', ', $missingKeys), ); } @@ -69,15 +81,17 @@ public static function validateSuccessApiResponse(array $data): void } /** + * @param array $data - The data to validate + * * @throws ParseException */ public static function validateErrorApiResponse(array $data): void { $requiredKeys = ['key', 'errorCode']; $missingKeys = array_diff($requiredKeys, array_keys($data)); - if (!empty($missingKeys)) { + if (!sizeof($missingKeys) !== 0) { throw new ParseException( - "missing keys in the error response: " . implode(', ', $missingKeys) + 'missing keys in the error response: ' . implode(', ', $missingKeys), ); } @@ -90,6 +104,11 @@ public static function validateErrorApiResponse(array $data): void } } + /** + * @param EvaluationContext|null $context - The evaluation context to validate + * + * @throws InvalidContextException + */ public static function validateEvaluationContext(?EvaluationContext $context): void { if ($context === null) { @@ -101,6 +120,11 @@ public static function validateEvaluationContext(?EvaluationContext $context): v } } + /** + * @param string $flagKey - The flag key to validate + * + * @throws InvalidConfigException + */ public static function validateFlagKey(string $flagKey): void { if ($flagKey === null || $flagKey === '') { diff --git a/providers/GoFeatureFlag/tests/unit/GoFeatureFlagProviderTest.php b/providers/GoFeatureFlag/tests/unit/GoFeatureFlagProviderTest.php index 4d308b9..d7ccc4a 100644 --- a/providers/GoFeatureFlag/tests/unit/GoFeatureFlagProviderTest.php +++ b/providers/GoFeatureFlag/tests/unit/GoFeatureFlagProviderTest.php @@ -6,58 +6,63 @@ use GuzzleHttp\Client; use GuzzleHttp\Psr7\Response; +use OpenFeature\OpenFeatureAPI; +use OpenFeature\Providers\GoFeatureFlag\GoFeatureFlagProvider; +use OpenFeature\Providers\GoFeatureFlag\Test\TestCase; +use OpenFeature\Providers\GoFeatureFlag\config\Config; +use OpenFeature\Providers\GoFeatureFlag\exception\InvalidConfigException; use OpenFeature\implementation\flags\Attributes; use OpenFeature\implementation\flags\MutableEvaluationContext; use OpenFeature\interfaces\flags\EvaluationContext; use OpenFeature\interfaces\provider\ErrorCode; use OpenFeature\interfaces\provider\Reason; -use OpenFeature\OpenFeatureAPI; -use OpenFeature\Providers\GoFeatureFlag\config\Config; -use OpenFeature\Providers\GoFeatureFlag\exception\InvalidConfigException; -use OpenFeature\Providers\GoFeatureFlag\GoFeatureFlagProvider; -use OpenFeature\Providers\GoFeatureFlag\Test\TestCase; +use PHPUnit\Framework\MockObject\MockObject; +use ReflectionClass; +use ReflectionException; + use function PHPUnit\Framework\assertEquals; +use function json_encode; class GoFeatureFlagProviderTest extends TestCase { private EvaluationContext $defaultEvaluationContext; - public function test_should_throw_if_invalid_endpoint(): void + public function testShouldThrowIfInvalidEndpoint(): void { $this->expectException(InvalidConfigException::class); new GoFeatureFlagProvider( - new Config('invalid') + new Config('invalid'), ); } // Configuration validation tests - public function test_should_not_throw_if_valid_endpoint(): void + public function testShouldNotThrowIfValidEndpoint(): void { $provider = new GoFeatureFlagProvider( - new Config('https://gofeatureflag.org') + new Config('https://gofeatureflag.org'), ); $this->assertInstanceOf(GoFeatureFlagProvider::class, $provider); } - public function test_should_raise_if_endpoint_is_not_http(): void + public function testShouldRaiseIfEndpointIsNotHttp(): void { $this->expectException(InvalidConfigException::class); $provider = new GoFeatureFlagProvider( - new Config('gofeatureflag.org') + new Config('gofeatureflag.org'), ); $this->assertInstanceOf(GoFeatureFlagProvider::class, $provider); } - public function test_empty_endpoint_should_throw(): void + public function testEmptyEndpointShouldThrow(): void { $this->expectException(InvalidConfigException::class); new GoFeatureFlagProvider( - new Config('') + new Config(''), ); } - public function test_metadata_name_is_defined(): void + public function testMetadataNameIsDefined(): void { $config = new Config('http://localhost:1031'); $provider = new GoFeatureFlagProvider($config); @@ -68,14 +73,14 @@ public function test_metadata_name_is_defined(): void // Metadata tests - public function test_should_return_the_value_of_the_flag_as_int(): void + public function testShouldReturnTheValueOfTheFlagAsInt(): void { $mockClient = $this->createMock(Client::class); $mockResponse = new Response(200, [], json_encode([ - "key" => "integer_key", - "value" => 42, - "reason" => "TARGETING_MATCH", - "variant" => "default" + 'key' => 'integer_key', + 'value' => 42, + 'reason' => 'TARGETING_MATCH', + 'variant' => 'default', ])); $mockClient->expects($this->once()) @@ -98,27 +103,30 @@ public function test_should_return_the_value_of_the_flag_as_int(): void assertEquals('integer_key', $got->getFlagKey()); } - private function mockHttpClient($provider, $mockClient): void + /** + * @throws ReflectionException + */ + private function mockHttpClient(GoFeatureFlagProvider $provider, MockObject $mockClient): void { - $providerReflection = new \ReflectionClass($provider); + $providerReflection = new ReflectionClass($provider); $ofrepApiProperty = $providerReflection->getProperty('ofrepApi'); $ofrepApiProperty->setAccessible(true); $ofrepApi = $ofrepApiProperty->getValue($provider); - $ofrepApiReflection = new \ReflectionClass($ofrepApi); + $ofrepApiReflection = new ReflectionClass($ofrepApi); $clientProperty = $ofrepApiReflection->getProperty('client'); $clientProperty->setAccessible(true); $clientProperty->setValue($ofrepApi, $mockClient); } - public function test_should_return_the_value_of_the_flag_as_float(): void + public function testShouldReturnTheValueOfTheFlagAsFloat(): void { $mockClient = $this->createMock(Client::class); $mockResponse = new Response(200, [], json_encode([ - "key" => "flag-key", - "value" => 42.2, - "reason" => "TARGETING_MATCH", - "variant" => "default" + 'key' => 'flag-key', + 'value' => 42.2, + 'reason' => 'TARGETING_MATCH', + 'variant' => 'default', ])); $mockClient->expects($this->once()) @@ -141,14 +149,14 @@ public function test_should_return_the_value_of_the_flag_as_float(): void assertEquals('flag-key', $got->getFlagKey()); } - public function test_should_return_the_value_of_the_flag_as_string(): void + public function testShouldReturnTheValueOfTheFlagAsString(): void { $mockClient = $this->createMock(Client::class); $mockResponse = new Response(200, [], json_encode([ - "key" => "flag-key", - "value" => "value as string", - "reason" => "TARGETING_MATCH", - "variant" => "default" + 'key' => 'flag-key', + 'value' => 'value as string', + 'reason' => 'TARGETING_MATCH', + 'variant' => 'default', ])); $mockClient->expects($this->once()) @@ -163,22 +171,22 @@ public function test_should_return_the_value_of_the_flag_as_string(): void $api = OpenFeatureAPI::getInstance(); $api->setProvider($provider); $client = $api->getClient(); - $got = $client->getStringDetails('flag-key', "default", $this->defaultEvaluationContext); - assertEquals("value as string", $got->getValue()); + $got = $client->getStringDetails('flag-key', 'default', $this->defaultEvaluationContext); + assertEquals('value as string', $got->getValue()); assertEquals(Reason::TARGETING_MATCH, $got->getReason()); assertEquals('default', $got->getVariant()); assertEquals(null, $got->getError()); assertEquals('flag-key', $got->getFlagKey()); } - public function test_should_return_the_value_of_the_flag_as_bool(): void + public function testShouldReturnTheValueOfTheFlagAsBool(): void { $mockClient = $this->createMock(Client::class); $mockResponse = new Response(200, [], json_encode([ - "key" => "flag-key", - "value" => true, - "reason" => "TARGETING_MATCH", - "variant" => "default" + 'key' => 'flag-key', + 'value' => true, + 'reason' => 'TARGETING_MATCH', + 'variant' => 'default', ])); $mockClient->expects($this->once()) @@ -201,14 +209,14 @@ public function test_should_return_the_value_of_the_flag_as_bool(): void assertEquals('flag-key', $got->getFlagKey()); } - public function test_should_return_the_value_of_the_flag_as_object(): void + public function testShouldReturnTheValueOfTheFlagAsObject(): void { $mockClient = $this->createMock(Client::class); $mockResponse = new Response(200, [], json_encode([ - "key" => "flag-key", - "value" => ["value" => "value as object"], - "reason" => "TARGETING_MATCH", - "variant" => "default" + 'key' => 'flag-key', + 'value' => ['value' => 'value as object'], + 'reason' => 'TARGETING_MATCH', + 'variant' => 'default', ])); $mockClient->expects($this->once()) @@ -223,22 +231,22 @@ public function test_should_return_the_value_of_the_flag_as_object(): void $api = OpenFeatureAPI::getInstance(); $api->setProvider($provider); $client = $api->getClient(); - $got = $client->getObjectDetails('flag-key', ["default" => true], $this->defaultEvaluationContext); - assertEquals(["value" => "value as object"], $got->getValue()); + $got = $client->getObjectDetails('flag-key', ['default' => true], $this->defaultEvaluationContext); + assertEquals(['value' => 'value as object'], $got->getValue()); assertEquals(Reason::TARGETING_MATCH, $got->getReason()); assertEquals('default', $got->getVariant()); assertEquals(null, $got->getError()); assertEquals('flag-key', $got->getFlagKey()); } - public function test_should_return_the_default_value_if_flag_is_not_the_right_type(): void + public function testShouldReturnTheDefaultValueIfFlagIsNotTheRightType(): void { $mockClient = $this->createMock(Client::class); $mockResponse = new Response(200, [], json_encode([ - "key" => "integer_key", - "value" => 42, - "reason" => "TARGETING_MATCH", - "variant" => "default" + 'key' => 'integer_key', + 'value' => 42, + 'reason' => 'TARGETING_MATCH', + 'variant' => 'default', ])); $mockClient->expects($this->once()) @@ -258,11 +266,11 @@ public function test_should_return_the_default_value_if_flag_is_not_the_right_ty assertEquals(Reason::ERROR, $got->getReason()); assertEquals(null, $got->getVariant()); assertEquals(ErrorCode::TYPE_MISMATCH(), $got->getError()->getResolutionErrorCode()); - assertEquals("Invalid type for integer_key, got integer expected boolean", $got->getError()->getResolutionErrorMessage()); + assertEquals('Invalid type for integer_key, got integer expected boolean', $got->getError()->getResolutionErrorMessage()); assertEquals('integer_key', $got->getFlagKey()); } - public function test_should_return_the_default_value_of_the_flag_if_error_send_by_the_API_http_code_403(): void + public function testShouldReturnTheDefaultValueOfTheFlagIfErrorSendByTheAPIHttpCode403(): void { $mockClient = $this->createMock(Client::class); $mockResponse = new Response(403, [], json_encode([])); @@ -284,18 +292,18 @@ public function test_should_return_the_default_value_of_the_flag_if_error_send_b assertEquals(Reason::ERROR, $got->getReason()); assertEquals(null, $got->getVariant()); assertEquals(ErrorCode::GENERAL(), $got->getError()->getResolutionErrorCode()); - assertEquals("Unauthorized access to the API", $got->getError()->getResolutionErrorMessage()); + assertEquals('Unauthorized access to the API', $got->getError()->getResolutionErrorMessage()); assertEquals('boolean_key', $got->getFlagKey()); } - public function test_should_return_the_default_value_of_the_flag_if_error_send_by_the_API__http_code_400(): void + public function testShouldReturnTheDefaultValueOfTheFlagIfErrorSendByTheAPIHttpCode400(): void { $mockClient = $this->createMock(Client::class); $mockResponse = new Response(400, [], json_encode([ - "key" => "integer_key", - "reason" => "ERROR", - "errorCode" => "INVALID_CONTEXT", - "errorDetails" => "Error Details for invalid context" + 'key' => 'integer_key', + 'reason' => 'ERROR', + 'errorCode' => 'INVALID_CONTEXT', + 'errorDetails' => 'Error Details for invalid context', ])); $mockClient->expects($this->once()) @@ -315,18 +323,18 @@ public function test_should_return_the_default_value_of_the_flag_if_error_send_b assertEquals(Reason::ERROR, $got->getReason()); assertEquals(null, $got->getVariant()); assertEquals(ErrorCode::INVALID_CONTEXT(), $got->getError()->getResolutionErrorCode()); - assertEquals("Error Details for invalid context", $got->getError()->getResolutionErrorMessage()); + assertEquals('Error Details for invalid context', $got->getError()->getResolutionErrorMessage()); assertEquals('boolean_key', $got->getFlagKey()); } - public function test_should_return_default_value_if_no_evaluation_context(): void + public function testShouldReturnDefaultValueIfNoEvaluationContext(): void { $mockClient = $this->createMock(Client::class); $mockResponse = new Response(200, [], json_encode([ - "key" => "integer_key", - "value" => 42, - "reason" => "TARGETING_MATCH", - "variant" => "default" + 'key' => 'integer_key', + 'value' => 42, + 'reason' => 'TARGETING_MATCH', + 'variant' => 'default', ])); $mockClient->method('post') @@ -345,18 +353,18 @@ public function test_should_return_default_value_if_no_evaluation_context(): voi assertEquals(Reason::ERROR, $got->getReason()); assertEquals(null, $got->getVariant()); assertEquals(ErrorCode::INVALID_CONTEXT(), $got->getError()->getResolutionErrorCode()); - assertEquals("Missing targetingKey in evaluation context", $got->getError()->getResolutionErrorMessage()); + assertEquals('Missing targetingKey in evaluation context', $got->getError()->getResolutionErrorMessage()); assertEquals('boolean_key', $got->getFlagKey()); } - public function test_should_return_default_value_if_evaluation_context_has_empty_string_targetingKey(): void + public function testShouldReturnDefaultValueIfEvaluationContextHasEmptyStringTargetingKey(): void { $mockClient = $this->createMock(Client::class); $mockResponse = new Response(200, [], json_encode([ - "key" => "integer_key", - "value" => 42, - "reason" => "TARGETING_MATCH", - "variant" => "default" + 'key' => 'integer_key', + 'value' => 42, + 'reason' => 'TARGETING_MATCH', + 'variant' => 'default', ])); $mockClient->method('post') @@ -370,23 +378,23 @@ public function test_should_return_default_value_if_evaluation_context_has_empty $api = OpenFeatureAPI::getInstance(); $api->setProvider($provider); $client = $api->getClient(); - $got = $client->getBooleanDetails('boolean_key', false, new MutableEvaluationContext("")); + $got = $client->getBooleanDetails('boolean_key', false, new MutableEvaluationContext('')); assertEquals(false, $got->getValue()); assertEquals(Reason::ERROR, $got->getReason()); assertEquals(null, $got->getVariant()); assertEquals(ErrorCode::INVALID_CONTEXT(), $got->getError()->getResolutionErrorCode()); - assertEquals("Missing targetingKey in evaluation context", $got->getError()->getResolutionErrorMessage()); + assertEquals('Missing targetingKey in evaluation context', $got->getError()->getResolutionErrorMessage()); assertEquals('boolean_key', $got->getFlagKey()); } - public function test_should_return_default_value_if_evaluation_context_has_null_targetingKey(): void + public function testShouldReturnDefaultValueIfEvaluationContextHasNullTargetingKey(): void { $mockClient = $this->createMock(Client::class); $mockResponse = new Response(200, [], json_encode([ - "key" => "integer_key", - "value" => 42, - "reason" => "TARGETING_MATCH", - "variant" => "default" + 'key' => 'integer_key', + 'value' => 42, + 'reason' => 'TARGETING_MATCH', + 'variant' => 'default', ])); $mockClient->method('post') @@ -405,18 +413,18 @@ public function test_should_return_default_value_if_evaluation_context_has_null_ assertEquals(Reason::ERROR, $got->getReason()); assertEquals(null, $got->getVariant()); assertEquals(ErrorCode::INVALID_CONTEXT(), $got->getError()->getResolutionErrorCode()); - assertEquals("Missing targetingKey in evaluation context", $got->getError()->getResolutionErrorMessage()); + assertEquals('Missing targetingKey in evaluation context', $got->getError()->getResolutionErrorMessage()); assertEquals('boolean_key', $got->getFlagKey()); } - public function test_should_return_default_value_if_flag_key_empty_string(): void + public function testShouldReturnDefaultValueIfFlagKeyEmptyString(): void { $mockClient = $this->createMock(Client::class); $mockResponse = new Response(200, [], json_encode([ - "key" => "integer_key", - "value" => 42, - "reason" => "TARGETING_MATCH", - "variant" => "default" + 'key' => 'integer_key', + 'value' => 42, + 'reason' => 'TARGETING_MATCH', + 'variant' => 'default', ])); $mockClient->method('post') @@ -435,11 +443,11 @@ public function test_should_return_default_value_if_flag_key_empty_string(): voi assertEquals(Reason::ERROR, $got->getReason()); assertEquals(null, $got->getVariant()); assertEquals(ErrorCode::GENERAL(), $got->getError()->getResolutionErrorCode()); - assertEquals("An error occurred while evaluating the flag: Flag key is null or empty", $got->getError()->getResolutionErrorMessage()); + assertEquals('An error occurred while evaluating the flag: Flag key is null or empty', $got->getError()->getResolutionErrorMessage()); assertEquals('', $got->getFlagKey()); } - public function test_return_an_error_API_response_if_500(): void + public function testReturnAnErrorAPIResponseIf500(): void { $mockClient = $this->createMock(Client::class); $mockResponse = new Response(500, [], json_encode([])); @@ -462,27 +470,13 @@ public function test_return_an_error_API_response_if_500(): void assertEquals(Reason::ERROR, $got->getReason()); assertEquals(null, $got->getVariant()); assertEquals(ErrorCode::GENERAL(), $got->getError()->getResolutionErrorCode()); - assertEquals("Unknown error occurred", $got->getError()->getResolutionErrorMessage()); + assertEquals('Unknown error occurred', $got->getError()->getResolutionErrorMessage()); assertEquals('boolean_flag', $got->getFlagKey()); } protected function setUp(): void { parent::setUp(); - $this->defaultEvaluationContext = new MutableEvaluationContext("214b796a-807b-4697-b3a3-42de0ec10a37", new Attributes(["email" => "contact@gofeatureflag.org"])); + $this->defaultEvaluationContext = new MutableEvaluationContext('214b796a-807b-4697-b3a3-42de0ec10a37', new Attributes(['email' => 'contact@gofeatureflag.org'])); } - - private function mockClient($provider, $mockClient): void - { - $providerReflection = new \ReflectionClass($provider); - $ofrepApiProperty = $providerReflection->getProperty('ofrepApi'); - $ofrepApiProperty->setAccessible(true); - $ofrepApi = $ofrepApiProperty->getValue($provider); - - $ofrepApiReflection = new \ReflectionClass($ofrepApi); - $clientProperty = $ofrepApiReflection->getProperty('client'); - $clientProperty->setAccessible(true); - $clientProperty->setValue($ofrepApi, $mockClient); - } - } diff --git a/providers/GoFeatureFlag/tests/unit/controller/OfrepApiTest.php b/providers/GoFeatureFlag/tests/unit/controller/OfrepApiTest.php index 4445aac..ec70314 100644 --- a/providers/GoFeatureFlag/tests/unit/controller/OfrepApiTest.php +++ b/providers/GoFeatureFlag/tests/unit/controller/OfrepApiTest.php @@ -4,13 +4,9 @@ namespace OpenFeature\Providers\GoFeatureFlag\Test\unit\controller; - use GuzzleHttp\Client; use GuzzleHttp\Psr7\Response; -use OpenFeature\implementation\flags\MutableEvaluationContext; -use OpenFeature\interfaces\flags\EvaluationContext; -use OpenFeature\interfaces\provider\ErrorCode; -use OpenFeature\interfaces\provider\Reason; +use OpenFeature\Providers\GoFeatureFlag\Test\TestCase; use OpenFeature\Providers\GoFeatureFlag\config\Config; use OpenFeature\Providers\GoFeatureFlag\controller\OfrepApi; use OpenFeature\Providers\GoFeatureFlag\exception\FlagNotFoundException; @@ -19,13 +15,23 @@ use OpenFeature\Providers\GoFeatureFlag\exception\UnauthorizedException; use OpenFeature\Providers\GoFeatureFlag\exception\UnknownOfrepException; use OpenFeature\Providers\GoFeatureFlag\model\OfrepApiResponse; -use OpenFeature\Providers\GoFeatureFlag\Test\TestCase; +use OpenFeature\implementation\flags\MutableEvaluationContext; +use OpenFeature\interfaces\flags\EvaluationContext; +use OpenFeature\interfaces\provider\ErrorCode; +use OpenFeature\interfaces\provider\Reason; +use ReflectionClass; + +use function gmdate; +use function json_encode; +use function sizeof; +use function time; +use function usleep; class OfrepApiTest extends TestCase { private EvaluationContext $defaultEvaluationContext; - public function test_should_raise_an_error_if_rate_limited() + public function testShouldRaiseAnErrorIfRateLimited() { $this->expectException(RateLimitedException::class); $mockClient = $this->createMock(Client::class); @@ -33,7 +39,7 @@ public function test_should_raise_an_error_if_rate_limited() $mockClient->method('post')->willReturn($mockResponse); $api = new OfrepApi(new Config('https://gofeatureflag.org')); - $reflection = new \ReflectionClass($api); + $reflection = new ReflectionClass($api); $property = $reflection->getProperty('client'); $property->setAccessible(true); $property->setValue($api, $mockClient); @@ -41,7 +47,7 @@ public function test_should_raise_an_error_if_rate_limited() $api->evaluate('flagKey', $this->defaultEvaluationContext); } - public function test_should_raise_an_error_if_not_authorized_401() + public function shouldRaiseAnErrorIfNotAuthorized401() { $this->expectException(UnauthorizedException::class); $mockClient = $this->createMock(Client::class); @@ -49,7 +55,7 @@ public function test_should_raise_an_error_if_not_authorized_401() $mockClient->method('post')->willReturn($mockResponse); $api = new OfrepApi(new Config('https://gofeatureflag.org')); - $reflection = new \ReflectionClass($api); + $reflection = new ReflectionClass($api); $property = $reflection->getProperty('client'); $property->setAccessible(true); $property->setValue($api, $mockClient); @@ -57,7 +63,7 @@ public function test_should_raise_an_error_if_not_authorized_401() $api->evaluate('flagKey', $this->defaultEvaluationContext); } - public function test_should_raise_an_error_if_not_authorized_403() + public function shouldRaiseAnErrorIfNotAuthorized403() { $this->expectException(UnauthorizedException::class); $mockClient = $this->createMock(Client::class); @@ -65,7 +71,7 @@ public function test_should_raise_an_error_if_not_authorized_403() $mockClient->method('post')->willReturn($mockResponse); $api = new OfrepApi(new Config('https://gofeatureflag.org')); - $reflection = new \ReflectionClass($api); + $reflection = new ReflectionClass($api); $property = $reflection->getProperty('client'); $property->setAccessible(true); $property->setValue($api, $mockClient); @@ -73,7 +79,7 @@ public function test_should_raise_an_error_if_not_authorized_403() $api->evaluate('flagKey', $this->defaultEvaluationContext); } - public function test_should_raise_an_error_if_flag_not_found_404() + public function shouldRaiseAnErrorIfFlagNotFound404() { $this->expectException(FlagNotFoundException::class); $mockClient = $this->createMock(Client::class); @@ -81,7 +87,7 @@ public function test_should_raise_an_error_if_flag_not_found_404() $mockClient->method('post')->willReturn($mockResponse); $api = new OfrepApi(new Config('https://gofeatureflag.org')); - $reflection = new \ReflectionClass($api); + $reflection = new ReflectionClass($api); $property = $reflection->getProperty('client'); $property->setAccessible(true); $property->setValue($api, $mockClient); @@ -89,7 +95,7 @@ public function test_should_raise_an_error_if_flag_not_found_404() $api->evaluate('flagKey', $this->defaultEvaluationContext); } - public function test_should_raise_an_error_if_unknown_http_code_500() + public function shouldRaiseAnErrorIfUnknownHttpCode500() { $this->expectException(UnknownOfrepException::class); $mockClient = $this->createMock(Client::class); @@ -97,7 +103,7 @@ public function test_should_raise_an_error_if_unknown_http_code_500() $mockClient->method('post')->willReturn($mockResponse); $api = new OfrepApi(new Config('https://gofeatureflag.org')); - $reflection = new \ReflectionClass($api); + $reflection = new ReflectionClass($api); $property = $reflection->getProperty('client'); $property->setAccessible(true); $property->setValue($api, $mockClient); @@ -105,69 +111,69 @@ public function test_should_raise_an_error_if_unknown_http_code_500() $api->evaluate('flagKey', $this->defaultEvaluationContext); } - public function test_should_return_an_error_response_if_400() + public function shouldReturnAnErrorResponseIf400() { $mockClient = $this->createMock(Client::class); $mockResponse = new Response(400, [], json_encode([ - "key" => "flagKey", - "errorCode" => "TYPE_MISMATCH", - "errorDetails" => "The flag value is not of the expected type" + 'key' => 'flagKey', + 'errorCode' => 'TYPE_MISMATCH', + 'errorDetails' => 'The flag value is not of the expected type', ])); $mockClient->method('post')->willReturn($mockResponse); $api = new OfrepApi(new Config('https://gofeatureflag.org')); - $reflection = new \ReflectionClass($api); + $reflection = new ReflectionClass($api); $property = $reflection->getProperty('client'); $property->setAccessible(true); $property->setValue($api, $mockClient); $got = $api->evaluate('flagKey', $this->defaultEvaluationContext); $this->assertInstanceOf(OfrepApiResponse::class, $got); - $this->assertEquals("flagKey", $got->getKey()); + $this->assertEquals('flagKey', $got->getKey()); $this->assertEquals(Reason::ERROR, $got->getReason()); $this->assertEquals(ErrorCode::TYPE_MISMATCH(), $got->getErrorCode()); - $this->assertEquals("The flag value is not of the expected type", $got->getErrorDetails()); + $this->assertEquals('The flag value is not of the expected type', $got->getErrorDetails()); } - public function test_should_return_a_valid_response_if_200() + public function shouldReturnAValidResponseIf200() { $mockClient = $this->createMock(Client::class); $mockResponse = new Response(200, [], json_encode([ - "key" => "flagKey", - "value" => true, - "reason" => Reason::TARGETING_MATCH, - "variant" => "default" + 'key' => 'flagKey', + 'value' => true, + 'reason' => Reason::TARGETING_MATCH, + 'variant' => 'default', ])); $mockClient->method('post')->willReturn($mockResponse); $api = new OfrepApi(new Config('https://gofeatureflag.org')); - $reflection = new \ReflectionClass($api); + $reflection = new ReflectionClass($api); $property = $reflection->getProperty('client'); $property->setAccessible(true); $property->setValue($api, $mockClient); $got = $api->evaluate('flagKey', $this->defaultEvaluationContext); $this->assertInstanceOf(OfrepApiResponse::class, $got); - $this->assertEquals("flagKey", $got->getKey()); + $this->assertEquals('flagKey', $got->getKey()); $this->assertEquals(Reason::TARGETING_MATCH, $got->getReason()); $this->assertNull($got->getErrorDetails()); $this->assertNull($got->getErrorCode()); $this->assertEquals(true, $got->getValue()); } - public function test_should_raise_an_error_if_200_and_json_does_not_contains_the_required_keys_missing_value() + public function shouldRaiseAnErrorIf200AndJsonDoesNotContainTheRequiredKeysMissingValue() { $this->expectException(ParseException::class); $mockClient = $this->createMock(Client::class); $mockResponse = new Response(200, [], json_encode([ - "key" => "flagKey", - "reason" => Reason::TARGETING_MATCH, - "variant" => "default" + 'key' => 'flagKey', + 'reason' => Reason::TARGETING_MATCH, + 'variant' => 'default', ])); $mockClient->method('post')->willReturn($mockResponse); $api = new OfrepApi(new Config('https://gofeatureflag.org')); - $reflection = new \ReflectionClass($api); + $reflection = new ReflectionClass($api); $property = $reflection->getProperty('client'); $property->setAccessible(true); $property->setValue($api, $mockClient); @@ -175,19 +181,19 @@ public function test_should_raise_an_error_if_200_and_json_does_not_contains_the $api->evaluate('flagKey', $this->defaultEvaluationContext); } - public function test_should_raise_an_error_if_200_and_json_does_not_contains_the_required_keys_missing_key() + public function shouldRaiseAnErrorIf200AndJsonDoesNotContainTheRequiredKeysMissingKey() { $this->expectException(ParseException::class); $mockClient = $this->createMock(Client::class); $mockResponse = new Response(200, [], json_encode([ - "value" => true, - "reason" => Reason::TARGETING_MATCH, - "variant" => "default" + 'value' => true, + 'reason' => Reason::TARGETING_MATCH, + 'variant' => 'default', ])); $mockClient->method('post')->willReturn($mockResponse); $api = new OfrepApi(new Config('https://gofeatureflag.org')); - $reflection = new \ReflectionClass($api); + $reflection = new ReflectionClass($api); $property = $reflection->getProperty('client'); $property->setAccessible(true); $property->setValue($api, $mockClient); @@ -195,19 +201,19 @@ public function test_should_raise_an_error_if_200_and_json_does_not_contains_the $api->evaluate('flagKey', $this->defaultEvaluationContext); } - public function test_should_raise_an_error_if_200_and_json_does_not_contains_the_required_keys_missing_reason() + public function shouldRaiseAnErrorIf200AndJsonDoesNotContainTheRequiredKeysMissingReason() { $this->expectException(ParseException::class); $mockClient = $this->createMock(Client::class); $mockResponse = new Response(200, [], json_encode([ - "key" => "flagKey", - "value" => true, - "variant" => "default" + 'key' => 'flagKey', + 'value' => true, + 'variant' => 'default', ])); $mockClient->method('post')->willReturn($mockResponse); $api = new OfrepApi(new Config('https://gofeatureflag.org')); - $reflection = new \ReflectionClass($api); + $reflection = new ReflectionClass($api); $property = $reflection->getProperty('client'); $property->setAccessible(true); $property->setValue($api, $mockClient); @@ -215,19 +221,19 @@ public function test_should_raise_an_error_if_200_and_json_does_not_contains_the $api->evaluate('flagKey', $this->defaultEvaluationContext); } - public function test_should_raise_an_error_if_200_and_json_does_not_contains_the_required_keys_missing_variant() + public function shouldRaiseAnErrorIf200AndJsonDoesNotContainTheRequiredKeysMissingVariant() { $this->expectException(ParseException::class); $mockClient = $this->createMock(Client::class); $mockResponse = new Response(200, [], json_encode([ - "key" => "flagKey", - "value" => true, - "reason" => Reason::TARGETING_MATCH, + 'key' => 'flagKey', + 'value' => true, + 'reason' => Reason::TARGETING_MATCH, ])); $mockClient->method('post')->willReturn($mockResponse); $api = new OfrepApi(new Config('https://gofeatureflag.org')); - $reflection = new \ReflectionClass($api); + $reflection = new ReflectionClass($api); $property = $reflection->getProperty('client'); $property->setAccessible(true); $property->setValue($api, $mockClient); @@ -235,18 +241,18 @@ public function test_should_raise_an_error_if_200_and_json_does_not_contains_the $api->evaluate('flagKey', $this->defaultEvaluationContext); } - public function test_should_raise_an_error_if_400_and_json_does_not_contains_the_required_keys_missing_key() + public function shouldRaiseAnErrorIf400AndJsonDoesNotContainTheRequiredKeysMissingKey() { $this->expectException(ParseException::class); $mockClient = $this->createMock(Client::class); $mockResponse = new Response(400, [], json_encode([ - "errorCode" => "TYPE_MISMATCH", - "errorDetails" => "The flag value is not of the expected type" + 'errorCode' => 'TYPE_MISMATCH', + 'errorDetails' => 'The flag value is not of the expected type', ])); $mockClient->method('post')->willReturn($mockResponse); $api = new OfrepApi(new Config('https://gofeatureflag.org')); - $reflection = new \ReflectionClass($api); + $reflection = new ReflectionClass($api); $property = $reflection->getProperty('client'); $property->setAccessible(true); $property->setValue($api, $mockClient); @@ -254,18 +260,18 @@ public function test_should_raise_an_error_if_400_and_json_does_not_contains_the $api->evaluate('flagKey', $this->defaultEvaluationContext); } - public function test_should_raise_an_error_if_400_and_json_does_not_contains_the_required_keys_missing_error_code() + public function shouldRaiseAnErrorIf400AndJsonDoesNotContainTheRequiredKeysMissingErrorCode() { $this->expectException(ParseException::class); $mockClient = $this->createMock(Client::class); $mockResponse = new Response(400, [], json_encode([ - "key" => "flagKey", - "errorDetails" => "The flag value is not of the expected type" + 'key' => 'flagKey', + 'errorDetails' => 'The flag value is not of the expected type', ])); $mockClient->method('post')->willReturn($mockResponse); $api = new OfrepApi(new Config('https://gofeatureflag.org')); - $reflection = new \ReflectionClass($api); + $reflection = new ReflectionClass($api); $property = $reflection->getProperty('client'); $property->setAccessible(true); $property->setValue($api, $mockClient); @@ -273,21 +279,21 @@ public function test_should_raise_an_error_if_400_and_json_does_not_contains_the $api->evaluate('flagKey', $this->defaultEvaluationContext); } - public function test_should_not_be_able_to_call_the_API_again_if_rate_limited_with_retry_after_int() + public function shouldNotBeAbleToCallTheApiAgainIfRateLimitedWithRetryAfterInt() { $mockClient = $this->createMock(Client::class); - $mockResponse = new Response(429, ["Retry-After" => "1"], json_encode([ - "key" => "flagKey", - "value" => true, - "reason" => Reason::TARGETING_MATCH, - "variant" => "default" + $mockResponse = new Response(429, ['Retry-After' => '1'], json_encode([ + 'key' => 'flagKey', + 'value' => true, + 'reason' => Reason::TARGETING_MATCH, + 'variant' => 'default', ])); $mockClient->expects($this->exactly(1)) ->method('post') ->willReturn($mockResponse); $api = new OfrepApi(new Config('https://gofeatureflag.org')); - $reflection = new \ReflectionClass($api); + $reflection = new ReflectionClass($api); $property = $reflection->getProperty('client'); $property->setAccessible(true); $property->setValue($api, $mockClient); @@ -305,21 +311,20 @@ public function test_should_not_be_able_to_call_the_API_again_if_rate_limited_wi } } - public function test_should_be_able_to_call_the_API_again_if_we_wait_after_the_retry_after_as_int() + public function shouldBeAbleToCallTheApiAgainIfWeWaitAfterTheRetryAfterAsInt() { $mockClient = $this->createMock(Client::class); - $mockResponseRateLimited = new Response(429, ["Retry-After" => "1"], json_encode([])); + $mockResponseRateLimited = new Response(429, ['Retry-After' => '1'], json_encode([])); $mockResponseSuccess = new Response(200, [], json_encode([ - "key" => "flagKey", - "value" => true, - "reason" => Reason::TARGETING_MATCH, - "variant" => "default" + 'key' => 'flagKey', + 'value' => true, + 'reason' => Reason::TARGETING_MATCH, + 'variant' => 'default', ])); $mockClient->method('post')->will($this->onConsecutiveCalls($mockResponseRateLimited, $mockResponseSuccess)); - $api = new OfrepApi(new Config('https://gofeatureflag.org')); - $reflection = new \ReflectionClass($api); + $reflection = new ReflectionClass($api); $property = $reflection->getProperty('client'); $property->setAccessible(true); $property->setValue($api, $mockClient); @@ -337,21 +342,21 @@ public function test_should_be_able_to_call_the_API_again_if_we_wait_after_the_r $this->assertInstanceOf(OfrepApiResponse::class, $got); } - public function test_should_not_be_able_to_call_the_API_again_if_rate_limited_with_retry_after_date() + public function shouldNotBeAbleToCallTheApiAgainIfRateLimitedWithRetryAfterDate() { $mockClient = $this->createMock(Client::class); - $mockResponse = new Response(429, ["Retry-After" => gmdate('D, d M Y H:i:s \G\M\T', time() + 1)], json_encode([ - "key" => "flagKey", - "value" => true, - "reason" => Reason::TARGETING_MATCH, - "variant" => "default" + $mockResponse = new Response(429, ['Retry-After' => gmdate('D, d M Y H:i:s \G\M\T', time() + 1)], json_encode([ + 'key' => 'flagKey', + 'value' => true, + 'reason' => Reason::TARGETING_MATCH, + 'variant' => 'default', ])); $mockClient->expects($this->exactly(1)) ->method('post') ->willReturn($mockResponse); $api = new OfrepApi(new Config('https://gofeatureflag.org')); - $reflection = new \ReflectionClass($api); + $reflection = new ReflectionClass($api); $property = $reflection->getProperty('client'); $property->setAccessible(true); $property->setValue($api, $mockClient); @@ -369,21 +374,20 @@ public function test_should_not_be_able_to_call_the_API_again_if_rate_limited_wi } } - public function test_should_be_able_to_call_the_API_again_if_we_wait_after_the_retry_after_as_date() + public function shouldBeAbleToCallTheApiAgainIfWeWaitAfterTheRetryAfterAsDate() { $mockClient = $this->createMock(Client::class); - $mockResponseRateLimited = new Response(429, ["Retry-After" => gmdate('D, d M Y H:i:s \G\M\T', time() + 1)], json_encode([])); + $mockResponseRateLimited = new Response(429, ['Retry-After' => gmdate('D, d M Y H:i:s \G\M\T', time() + 1)], json_encode([])); $mockResponseSuccess = new Response(200, [], json_encode([ - "key" => "flagKey", - "value" => true, - "reason" => Reason::TARGETING_MATCH, - "variant" => "default" + 'key' => 'flagKey', + 'value' => true, + 'reason' => Reason::TARGETING_MATCH, + 'variant' => 'default', ])); $mockClient->method('post')->will($this->onConsecutiveCalls($mockResponseRateLimited, $mockResponseSuccess)); - $api = new OfrepApi(new Config('https://gofeatureflag.org')); - $reflection = new \ReflectionClass($api); + $reflection = new ReflectionClass($api); $property = $reflection->getProperty('client'); $property->setAccessible(true); $property->setValue($api, $mockClient); @@ -401,14 +405,14 @@ public function test_should_be_able_to_call_the_API_again_if_we_wait_after_the_r $this->assertInstanceOf(OfrepApiResponse::class, $got); } - public function test_should_have_autorization_header_if_api_key_in_config() + public function shouldHaveAuthorizationHeaderIfApiKeyInConfig() { $mockClient = $this->createMock(Client::class); $mockResponse = new Response(200, [], json_encode([ - "key" => "flagKey", - "value" => true, - "reason" => Reason::TARGETING_MATCH, - "variant" => "default" + 'key' => 'flagKey', + 'value' => true, + 'reason' => Reason::TARGETING_MATCH, + 'variant' => 'default', ])); $mockClient->expects($this->once()) @@ -419,12 +423,12 @@ public function test_should_have_autorization_header_if_api_key_in_config() $this->assertArrayHasKey('headers', $options); $this->assertArrayHasKey('Authorization', $options['headers']); $this->assertEquals('Bearer your-secure-api-key', $options['headers']['Authorization']); + return $mockResponse; }); - - $api = new OfrepApi(new Config('https://gofeatureflag.org', apiKey: "your-secure-api-key")); - $reflection = new \ReflectionClass($api); + $api = new OfrepApi(new Config('https://gofeatureflag.org', apiKey: 'your-secure-api-key')); + $reflection = new ReflectionClass($api); $property = $reflection->getProperty('client'); $property->setAccessible(true); $property->setValue($api, $mockClient); @@ -435,6 +439,6 @@ public function test_should_have_autorization_header_if_api_key_in_config() protected function setUp(): void { parent::setUp(); - $this->defaultEvaluationContext = new MutableEvaluationContext("214b796a-807b-4697-b3a3-42de0ec10a37"); + $this->defaultEvaluationContext = new MutableEvaluationContext('214b796a-807b-4697-b3a3-42de0ec10a37'); } -} \ No newline at end of file +}