From aed2170b8ed16879b05d0d6cccff84c57f6503c4 Mon Sep 17 00:00:00 2001 From: jeremynikolic Date: Wed, 12 Jun 2024 10:48:23 +0200 Subject: [PATCH 1/2] feat: Make overriders definition more extendable Adjust the way config works around overriders so that other overriders may be defined outside the package using the manager pattern --- .gitignore | 2 + config/feature-flags.php | 52 ++++--- src/Contracts/FeatureFlagOverrider.php | 4 + src/FeatureFlagsApiManager.php | 5 +- src/FeatureFlagsOverriderManager.php | 29 ++++ src/FeatureFlagsServiceProvider.php | 41 +++-- src/Overriders/ConfigOverrider.php | 27 +++- src/Overriders/InMemoryOverrider.php | 69 +++++++++ src/Traits/InteractsWithFeatureFlags.php | 40 ++++- tests/Feature/ArrayOverriderTest.php | 100 ++++++++++++ ...rridesTest.php => ConfigOverriderTest.php} | 116 ++++++++------ tests/Feature/FeatureFlagProviderTest.php | 144 +++++++++--------- 12 files changed, 456 insertions(+), 173 deletions(-) create mode 100644 src/FeatureFlagsOverriderManager.php create mode 100644 src/Overriders/InMemoryOverrider.php create mode 100644 tests/Feature/ArrayOverriderTest.php rename tests/Feature/{OverridesTest.php => ConfigOverriderTest.php} (57%) diff --git a/.gitignore b/.gitignore index 6127d06..f5004aa 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ testbench.yaml vendor node_modules .php-cs-fixer.cache + +.phpunit.result.cache diff --git a/config/feature-flags.php b/config/feature-flags.php index 539b04a..dd1642e 100644 --- a/config/feature-flags.php +++ b/config/feature-flags.php @@ -3,7 +3,6 @@ declare(strict_types=1); use Worksome\FeatureFlags\ModelFeatureFlagConvertor; -use Worksome\FeatureFlags\Overriders\ConfigOverrider; // config for Worksome/FeatureFlags return [ @@ -17,7 +16,7 @@ /** * Overrides implementing FeatureFlagOverrider contract */ - 'overrider' => ConfigOverrider::class, + 'overrider' => 'config', 'providers' => [ 'launchdarkly' => [ @@ -36,24 +35,39 @@ ], /** - * Overrides all feature flags directly without hitting the provider. - * This is particularly useful for running things in the CI, - * e.g. Cypress tests. + * List of available overriders. + * Key is to be used to specify which overrider should be active * - * Be careful in setting a default value as said value will be applied to all flags. - * Use `null` value if needing the key to be present but act as if it was not */ - 'override-all' => env('FEATURE_FLAGS_OVERRIDE_ALL'), + 'overriders' => [ + 'config' => [ + /** + * Overrides all feature flags directly without hitting the provider. + * This is particularly useful for running things in the CI, + * e.g. Cypress tests. + * + * Be careful in setting a default value as said value will be applied to all flags. + * Use `null` value if needing the key to be present but act as if it was not + */ + 'override-all' => null, + + /** + * Override flags. If a feature flag is set inside an override, + * it will be used instead of the flag set in the provider. + * + * Usage: ['feature-flag-key' => true] + * + * Be careful in setting a default value as it will be applied. + * Use `null` value if needing the key to be present but act as if it was not + * + */ + 'overrides' => [ + // + ], + ], + 'in-memory' => [ + // + ] + ], - /** - * Override flags. If a feature flag is set inside an override, - * it will be used instead of the flag set in the provider. - * - * Usage: ['feature-flag-key' => true] - * - * Be careful in setting a default value as it will be applied. - * Use `null` value if needing the key to be present but act as if it was not - * - */ - 'overrides' => [], ]; diff --git a/src/Contracts/FeatureFlagOverrider.php b/src/Contracts/FeatureFlagOverrider.php index 48f365a..e107724 100644 --- a/src/Contracts/FeatureFlagOverrider.php +++ b/src/Contracts/FeatureFlagOverrider.php @@ -13,4 +13,8 @@ public function get(FeatureFlagEnum $key): bool; public function hasAll(): bool; public function getAll(): bool; + + public function set(FeatureFlagEnum $key, bool|null $value): static; + + public function setAll(bool|null $value): static; } diff --git a/src/FeatureFlagsApiManager.php b/src/FeatureFlagsApiManager.php index fb8b0cc..25d8397 100644 --- a/src/FeatureFlagsApiManager.php +++ b/src/FeatureFlagsApiManager.php @@ -14,9 +14,8 @@ class FeatureFlagsApiManager extends Manager public function createLaunchDarklyDriver(): LaunchDarklyApiProvider { $token = $this->config->get('feature-flags.providers.launchdarkly.access-token'); - if (! is_string($token)) { - throw new LaunchDarklyMissingAccessTokenException(); - } + + assert(is_string($token), new LaunchDarklyMissingAccessTokenException()); return new LaunchDarklyApiProvider( accessToken: $token, diff --git a/src/FeatureFlagsOverriderManager.php b/src/FeatureFlagsOverriderManager.php new file mode 100644 index 0000000..f3093eb --- /dev/null +++ b/src/FeatureFlagsOverriderManager.php @@ -0,0 +1,29 @@ +config, + ); + } + + public function createInMemoryDriver(): InMemoryOverrider + { + return new InMemoryOverrider(); + } + + public function getDefaultDriver(): string + { + return strval($this->config->get('feature-flags.overrider')); // @phpstan-ignore-line + } +} diff --git a/src/FeatureFlagsServiceProvider.php b/src/FeatureFlagsServiceProvider.php index 320eb15..f7c1ea3 100644 --- a/src/FeatureFlagsServiceProvider.php +++ b/src/FeatureFlagsServiceProvider.php @@ -44,16 +44,10 @@ public function register(): void 'feature-flags' ); - $this->app->extend(FeatureFlagsProviderContract::class, function ($provider, Container $app) { - return $app->makeWith(FeatureFlagsOverrideProvider::class, [ - 'provider' => $provider, - ]); - }); + $this->app->singleton(FeatureFlagsManager::class); + + $this->app->singleton(FeatureFlagsOverriderManager::class); - $this->app->singleton( - FeatureFlagsManager::class, - static fn (Container $container) => new FeatureFlagsManager($container) - ); $this->app->singleton(FeatureFlagsProviderContract::class, function (Container $app) { /** @var FeatureFlagsManager $manager */ @@ -62,6 +56,22 @@ public function register(): void return $manager->driver(); }); + $this->app->singleton( + FeatureFlagOverrider::class, + function (Container $app) { + /** @var FeatureFlagsOverriderManager $manager */ + $manager = $app->get(FeatureFlagsOverriderManager::class); + + return $manager->driver(); + } + ); + + $this->app->extend(FeatureFlagsProviderContract::class, function ($provider, Container $app) { + return $app->makeWith(FeatureFlagsOverrideProvider::class, [ + 'provider' => $provider, + ]); + }); + $this->app->singleton(FeatureFlagUserConvertor::class, function (Container $app) { /** @var ConfigRepository $config */ $config = $app->get('config'); @@ -80,19 +90,6 @@ function (Container $app) { return $manager->driver(); } ); - - $this->app->singleton( - FeatureFlagOverrider::class, - function (Container $app) { - /** @var ConfigRepository $config */ - $config = $app->get('config'); - - /** @var class-string $convertor */ - $convertor = $config->get('feature-flags.overrider'); - - return $app->get($convertor); - } - ); } public function provides(): array diff --git a/src/Overriders/ConfigOverrider.php b/src/Overriders/ConfigOverrider.php index 82056fd..e4da992 100644 --- a/src/Overriders/ConfigOverrider.php +++ b/src/Overriders/ConfigOverrider.php @@ -12,7 +12,8 @@ { public function __construct( private Repository $config, - ) { + ) + { } /** @@ -20,13 +21,13 @@ public function __construct( */ public function has(FeatureFlagEnum $key): bool { - return $this->config->has(sprintf('feature-flags.overrides.%s', $key->value)) - && $this->config->get(sprintf('feature-flags.overrides.%s', $key->value)) !== null; + return $this->config->has(sprintf('feature-flags.overriders.config.overrides.%s', $key->value)) + && $this->config->get(sprintf('feature-flags.overriders.config.overrides.%s', $key->value)) !== null; } public function get(FeatureFlagEnum $key): bool { - return (bool) $this->config->get(sprintf('feature-flags.overrides.%s', $key->value)); + return (bool)$this->config->get(sprintf('feature-flags.overriders.config.overrides.%s', $key->value)); } /** @@ -34,12 +35,24 @@ public function get(FeatureFlagEnum $key): bool */ public function hasAll(): bool { - return $this->config->has('feature-flags.override-all') - && $this->config->get('feature-flags.override-all') !== null; + return $this->config->has('feature-flags.overriders.config.override_all') + && $this->config->get('feature-flags.overriders.config.override_all') !== null; } public function getAll(): bool { - return (bool) $this->config->get('feature-flags.override-all'); + return (bool)$this->config->get('feature-flags.overriders.config.override_all'); + } + + public function set(FeatureFlagEnum $key, bool|null $value): static + { + $this->config->set(sprintf('feature-flags.overriders.config.overrides.%s', $key->value), $value); + return $this; + } + + public function setAll(bool|null $value): static + { + $this->config->set('feature-flags.overriders.config.override_all', $value); + return $this; } } diff --git a/src/Overriders/InMemoryOverrider.php b/src/Overriders/InMemoryOverrider.php new file mode 100644 index 0000000..344d64a --- /dev/null +++ b/src/Overriders/InMemoryOverrider.php @@ -0,0 +1,69 @@ + $overrides + */ + private array $overrides = []; + /** + * @var bool|null $overrideAll + */ + private bool|null $overrideAll = null; + + /** + * Note: a flag key with null as value is considered not present, will return false + */ + public function has(FeatureFlagEnum $key): bool + { + return Arr::has($this->overrides, $key->value) + && Arr::get($this->overrides, $key->value) !== null; + } + + public function get(FeatureFlagEnum $key): bool + { + return (bool)Arr::get($this->overrides, $key->value, false); + } + + /** + * Note: null value is considered not present, will return false + */ + public function hasAll(): bool + { + return $this->overrideAll !== null; + } + + public function getAll(): bool + { + return (bool)$this->overrideAll; + } + + public function setAll(bool|null $value = null): static + { + $this->overrideAll = $value; + return $this; + } + + public function set(FeatureFlagEnum $key, mixed $value): static + { + Arr::set($this->overrides, $key->value, $value); + return $this; + } + + public function overrides(array|null $overriders): array|self + { + if ($overriders) { + $this->overrides = $overriders; + return $this; + } + return $this->overrides; + } +} diff --git a/src/Traits/InteractsWithFeatureFlags.php b/src/Traits/InteractsWithFeatureFlags.php index d1a1ce3..206c032 100644 --- a/src/Traits/InteractsWithFeatureFlags.php +++ b/src/Traits/InteractsWithFeatureFlags.php @@ -5,12 +5,18 @@ namespace Worksome\FeatureFlags\Traits; use Worksome\FeatureFlags\Contracts\FeatureFlagEnum; +use Worksome\FeatureFlags\Contracts\FeatureFlagOverrider; +/** + * This class is intended for usage mostly in testing context + * It provides the necessary methods to interact with the current feature flag overrider. + * Therefore, easily turning flag ON and OFF + */ trait InteractsWithFeatureFlags { - public function switchFeatureFlag(FeatureFlagEnum $key, bool $onOff): void + public function switchFeatureFlag(FeatureFlagEnum $key, bool|null $onOffNull): void { - $this->app['config']->set("feature-flags.overrides.{$key->value}", $onOff); + $this->featureFlagOverrider()->set($key, $onOffNull); } public function enableFeatureFlag(FeatureFlagEnum $key): void @@ -22,4 +28,34 @@ public function disableFeatureFlag(FeatureFlagEnum $key): void { $this->switchFeatureFlag($key, false); } + + public function switchFeatureFlagAll(bool|null $onOffNull): void + { + $this->featureFlagOverrider()->setAll($onOffNull); + } + + public function enableFeatureFlagAll(): void + { + $this->switchFeatureFlagAll(true); + } + + public function disableFeatureFlagAll(): void + { + $this->switchFeatureFlagAll(false); + } + + public function clearFeatureFlag(FeatureFlagEnum $key): void + { + $this->switchFeatureFlag($key, null); + } + + public function clearFeatureFlagAll(): void + { + $this->switchFeatureFlagAll(null); + } + + protected function featureFlagOverrider(): FeatureFlagOverrider + { + return $this->app->get(FeatureFlagOverrider::class); + } } diff --git a/tests/Feature/ArrayOverriderTest.php b/tests/Feature/ArrayOverriderTest.php new file mode 100644 index 0000000..5cb5427 --- /dev/null +++ b/tests/Feature/ArrayOverriderTest.php @@ -0,0 +1,100 @@ +inMemoryOverrider = new InMemoryOverrider(); +}); + +test('has returns false if override key is not present', function () { + expect($this->inMemoryOverrider->has(TestFeatureFlag::TestFlag))->toBeFalse(); +}); + +test('has returns false if override key is present but null', function () { + $this->inMemoryOverrider->set(TestFeatureFlag::TestFlag, null); + expect($this->inMemoryOverrider->has(TestFeatureFlag::TestFlag))->toBeFalse(); +}); + +test('has returns true if override key is present with truthy value', function ($value) { + $this->inMemoryOverrider->set(TestFeatureFlag::TestFlag, $value); + expect($this->inMemoryOverrider->has(TestFeatureFlag::TestFlag))->toBeTrue(); +})->with([ + true, + 1, + 1.0, + 'test', + [1], +]); + +test('has returns true if override key is present with falsy value', function ($value) { + $this->inMemoryOverrider->set(TestFeatureFlag::TestFlag, $value); + expect($this->inMemoryOverrider->has(TestFeatureFlag::TestFlag))->toBeTrue(); +})->with([ + false, + 0, + 0.0, + '', + '0', + [[]], +]); + +test('get returns true if override key is present with truthy value', function ($value) { + $this->inMemoryOverrider->set(TestFeatureFlag::TestFlag, $value); + expect($this->inMemoryOverrider->get(TestFeatureFlag::TestFlag))->toBeTrue(); +})->with([ + true, + 1, + 1.0, + 'test', + [1], +]); + +test('get returns false if override key is present with falsy value', function ($value) { + $this->inMemoryOverrider->set(TestFeatureFlag::TestFlag, $value); + expect($this->inMemoryOverrider->get(TestFeatureFlag::TestFlag))->toBeFalse(); +})->with([ + null, + false, + 0, + 0.0, + '', + '0', + [[]], +]); + +test('get returns false if override key is not present', function () { + expect($this->inMemoryOverrider->get(TestFeatureFlag::TestFlag))->toBeFalse(); +}); + +test('getAll returns true if override key is present with truthy value', function ($value) { + $this->inMemoryOverrider->set(TestFeatureFlag::TestFlag, $value); + expect($this->inMemoryOverrider->get(TestFeatureFlag::TestFlag))->toBeTrue(); +})->with([ + true, + 1, + 1.0, + 'test', + [1], +]); + +test('getAll returns false if override key is present with falsy value', function ($value) { + $this->inMemoryOverrider->set(TestFeatureFlag::TestFlag, $value); + expect($this->inMemoryOverrider->get(TestFeatureFlag::TestFlag))->toBeFalse(); +})->with([ + null, + false, + 0, + 0.0, + '', + '0', + [[]], +]); + +test('getAll returns false if override key is not present', function () { + expect($this->inMemoryOverrider->get(TestFeatureFlag::TestFlag))->toBeFalse(); +}); diff --git a/tests/Feature/OverridesTest.php b/tests/Feature/ConfigOverriderTest.php similarity index 57% rename from tests/Feature/OverridesTest.php rename to tests/Feature/ConfigOverriderTest.php index 41b8ebf..10a4427 100644 --- a/tests/Feature/OverridesTest.php +++ b/tests/Feature/ConfigOverriderTest.php @@ -18,85 +18,105 @@ }); test('has returns false if override key is present but null', function () { - $this->configRepo->set('feature-flags.overrides.test', null); + $this->configRepo->set('feature-flags.overriders.config.overrides.test', null); expect($this->configOverrides->has(TestFeatureFlag::TestFlag))->toBeFalse(); }); test('has returns true if override key is present with truthy value', function ($value) { - $this->configRepo->set('feature-flags.overrides.test', $value); + $this->configRepo->set('feature-flags.overriders.config.overrides.test', $value); expect($this->configOverrides->has(TestFeatureFlag::TestFlag))->toBeTrue(); })->with([ - true, - 1, - 1.0, - "test", - [1], - ]); + true, + 1, + 1.0, + "test", + [1], +]); test('has returns true if override key is present with falsy value', function ($value) { - $this->configRepo->set('feature-flags.overrides.test', $value); + $this->configRepo->set('feature-flags.overriders.config.overrides.test', $value); expect($this->configOverrides->has(TestFeatureFlag::TestFlag))->toBeTrue(); })->with([ - false, - 0, - 0.0, - "", - "0", - [[]], - ]); + false, + 0, + 0.0, + "", + "0", + [[]], +]); test('get returns true if override key is present with truthy value', function ($value) { - $this->configRepo->set('feature-flags.overrides.test', $value); + $this->configRepo->set('feature-flags.overriders.config.overrides.test', $value); expect($this->configOverrides->get(TestFeatureFlag::TestFlag))->toBeTrue(); })->with([ - true, - 1, - 1.0, - 'test', - [1], - ]); + true, + 1, + 1.0, + 'test', + [1], +]); test('get returns false if override key is present with falsy value', function ($value) { - $this->configRepo->set('feature-flags.overrides.test', $value); + $this->configRepo->set('feature-flags.overriders.config.overrides.test', $value); expect($this->configOverrides->get(TestFeatureFlag::TestFlag))->toBeFalse(); })->with([ - null, - false, - 0, - 0.0, - '', - '0', - [[]], - ]); + null, + false, + 0, + 0.0, + '', + '0', + [[]], +]); test('get returns false if override key is not present', function () { expect($this->configOverrides->get(TestFeatureFlag::TestFlag))->toBeFalse(); }); test('getAll returns true if override key is present with truthy value', function ($value) { - $this->configRepo->set('feature-flags.overrides.test', $value); + $this->configRepo->set('feature-flags.overriders.config.overrides.test', $value); expect($this->configOverrides->get(TestFeatureFlag::TestFlag))->toBeTrue(); })->with([ - true, - 1, - 1.0, - 'test', - [1], - ]); + true, + 1, + 1.0, + 'test', + [1], +]); test('getAll returns false if override key is present with falsy value', function ($value) { - $this->configRepo->set('feature-flags.overrides.test', $value); + $this->configRepo->set('feature-flags.overriders.config.overrides.test', $value); expect($this->configOverrides->get(TestFeatureFlag::TestFlag))->toBeFalse(); })->with([ - null, - false, - 0, - 0.0, - '', - '0', - [[]], - ]); + null, + false, + 0, + 0.0, + '', + '0', + [[]], +]); test('getAll returns false if override key is not present', function () { expect($this->configOverrides->get(TestFeatureFlag::TestFlag))->toBeFalse(); }); + +it('sets override value for a single feature flag', function ($value) { + $overrider = $this->app->make(ConfigOverrider::class); + $overrider->set(TestFeatureFlag::TestFlag, $value); + expect($this->configRepo->get('feature-flags.overriders.config.overrides.test'))->toBe($value); +})->with([ + true, + false, + null +]); + +it('sets overriderAll value', function ($value) { + $overrider = $this->app->make(ConfigOverrider::class); + $overrider->setAll($value); + expect($this->configRepo->get('feature-flags.overriders.config.override_all'))->toBe($value); +})->with([ + true, + false, + null +]); diff --git a/tests/Feature/FeatureFlagProviderTest.php b/tests/Feature/FeatureFlagProviderTest.php index ad38490..f2912fa 100644 --- a/tests/Feature/FeatureFlagProviderTest.php +++ b/tests/Feature/FeatureFlagProviderTest.php @@ -1,72 +1,72 @@ -fakeProvider = new FakeProvider(); - $this->provider = new FeatureFlagsOverrideProvider( - $this->fakeProvider, - new ConfigOverrider($this->app->get(Repository::class)) - ); -}); - -it('should return true if flag is set to true', function () { - $this->fakeProvider->setFlag(TestFeatureFlag::TestFlag, true); - expect($this->provider->flag(TestFeatureFlag::TestFlag))->toBeTrue(); -}); - -it('should return false if flag is set to false', function () { - $this->fakeProvider->setFlag(TestFeatureFlag::TestFlag, false); - expect($this->provider->flag(TestFeatureFlag::TestFlag))->toBeFalse(); -}); - -it('should validate the blade tags working correctly', function () { - $bladeSnippet = "@feature('test-flag') This is hidden feature @endfeature"; - $expectedCode = " This is hidden feature "; - expect(Blade::compileString($bladeSnippet))->toBe($expectedCode); -}); - -it('should succesfully follow the override for a feature flag', function () { - expect(Config::get('feature-flags.overrides.amazing-feature')) - ->toBe(null) - ->and($this->provider->flag(TestFeatureFlag::AmazingFeature)) - ->toBeFalse(); - - Config::set('feature-flags.overrides.amazing-feature', true); - - expect(Config::get('feature-flags.overrides.amazing-feature')) - ->toBeTrue() - ->and($this->provider->flag(TestFeatureFlag::AmazingFeature)) - ->toBeTrue(); -}); - -it('should correctly overide all feature flags if value is set', function () { - $this->fakeProvider->setFlag(TestFeatureFlag::FlagOne, true); - $this->fakeProvider->setFlag(TestFeatureFlag::FlagTwo, false); - $this->fakeProvider->setFlag(TestFeatureFlag::FlagThree, false); - - - expect($this->provider->flag(TestFeatureFlag::FlagOne)) - ->toBeTrue() - ->and($this->provider->flag(TestFeatureFlag::FlagTwo)) - ->toBeFalse() - ->and($this->provider->flag(TestFeatureFlag::FlagThree)) - ->toBeFalse(); - - Config::set('feature-flags.override-all', true); - - expect($this->provider->flag(TestFeatureFlag::FlagOne)) - ->toBeTrue() - ->and($this->provider->flag(TestFeatureFlag::FlagTwo)) - ->toBeTrue() - ->and($this->provider->flag(TestFeatureFlag::FlagThree)) - ->toBeTrue(); -}); +fakeProvider = new FakeProvider(); + $this->overrider = new InMemoryOverrider(); + $this->provider = new FeatureFlagsOverrideProvider( + $this->fakeProvider, + $this->overrider, + ); +}); + +it('should return true if flag is set to true', function () { + $this->fakeProvider->setFlag(TestFeatureFlag::TestFlag, true); + expect($this->provider->flag(TestFeatureFlag::TestFlag))->toBeTrue(); +}); + +it('should return false if flag is set to false', function () { + $this->fakeProvider->setFlag(TestFeatureFlag::TestFlag, false); + expect($this->provider->flag(TestFeatureFlag::TestFlag))->toBeFalse(); +}); + +it('should validate the blade tags working correctly', function () { + $bladeSnippet = "@feature('test-flag') This is hidden feature @endfeature"; + $expectedCode = " This is hidden feature "; + expect(Blade::compileString($bladeSnippet))->toBe($expectedCode); +}); + +it('should successfully follow the override for a feature flag', function () { + expect($this->overrider->get(TestFeatureFlag::AmazingFeature)) + ->toBeFalse() + ->and($this->provider->flag(TestFeatureFlag::AmazingFeature)) + ->toBeFalse(); + + + $this->overrider->set(TestFeatureFlag::AmazingFeature, true); + + expect($this->overrider->get(TestFeatureFlag::AmazingFeature)) + ->toBeTrue() + ->and($this->provider->flag(TestFeatureFlag::AmazingFeature)) + ->toBeTrue(); +}); + +it('should correctly overide all feature flags if value is set', function () { + $this->fakeProvider->setFlag(TestFeatureFlag::FlagOne, true); + $this->fakeProvider->setFlag(TestFeatureFlag::FlagTwo, false); + $this->fakeProvider->setFlag(TestFeatureFlag::FlagThree, false); + + + expect($this->provider->flag(TestFeatureFlag::FlagOne)) + ->toBeTrue() + ->and($this->provider->flag(TestFeatureFlag::FlagTwo)) + ->toBeFalse() + ->and($this->provider->flag(TestFeatureFlag::FlagThree)) + ->toBeFalse(); + + $this->overrider->setAll(true); + + expect($this->provider->flag(TestFeatureFlag::FlagOne)) + ->toBeTrue() + ->and($this->provider->flag(TestFeatureFlag::FlagTwo)) + ->toBeTrue() + ->and($this->provider->flag(TestFeatureFlag::FlagThree)) + ->toBeTrue(); +}); From 9a635a0bb5615d06846b3ad4fee3ef5714ef96fa Mon Sep 17 00:00:00 2001 From: Owen Voke Date: Tue, 18 Jun 2024 14:12:08 +0100 Subject: [PATCH 2/2] style: apply fixes from ECS --- config/feature-flags.php | 4 +- src/Overriders/ConfigOverrider.php | 9 +- src/Overriders/InMemoryOverrider.php | 5 +- tests/Feature/FeatureFlagProviderTest.php | 144 +++++++++++----------- 4 files changed, 82 insertions(+), 80 deletions(-) diff --git a/config/feature-flags.php b/config/feature-flags.php index dd1642e..cea4d32 100644 --- a/config/feature-flags.php +++ b/config/feature-flags.php @@ -62,11 +62,11 @@ * */ 'overrides' => [ - // + // ... ], ], 'in-memory' => [ - // + // ... ] ], diff --git a/src/Overriders/ConfigOverrider.php b/src/Overriders/ConfigOverrider.php index e4da992..0438d5c 100644 --- a/src/Overriders/ConfigOverrider.php +++ b/src/Overriders/ConfigOverrider.php @@ -12,8 +12,7 @@ { public function __construct( private Repository $config, - ) - { + ) { } /** @@ -27,7 +26,7 @@ public function has(FeatureFlagEnum $key): bool public function get(FeatureFlagEnum $key): bool { - return (bool)$this->config->get(sprintf('feature-flags.overriders.config.overrides.%s', $key->value)); + return (bool) $this->config->get(sprintf('feature-flags.overriders.config.overrides.%s', $key->value)); } /** @@ -41,18 +40,20 @@ public function hasAll(): bool public function getAll(): bool { - return (bool)$this->config->get('feature-flags.overriders.config.override_all'); + return (bool) $this->config->get('feature-flags.overriders.config.override_all'); } public function set(FeatureFlagEnum $key, bool|null $value): static { $this->config->set(sprintf('feature-flags.overriders.config.overrides.%s', $key->value), $value); + return $this; } public function setAll(bool|null $value): static { $this->config->set('feature-flags.overriders.config.override_all', $value); + return $this; } } diff --git a/src/Overriders/InMemoryOverrider.php b/src/Overriders/InMemoryOverrider.php index 344d64a..bbe1298 100644 --- a/src/Overriders/InMemoryOverrider.php +++ b/src/Overriders/InMemoryOverrider.php @@ -14,6 +14,7 @@ class InMemoryOverrider implements FeatureFlagOverrider * @var array $overrides */ private array $overrides = []; + /** * @var bool|null $overrideAll */ @@ -30,7 +31,7 @@ public function has(FeatureFlagEnum $key): bool public function get(FeatureFlagEnum $key): bool { - return (bool)Arr::get($this->overrides, $key->value, false); + return (bool) Arr::get($this->overrides, $key->value, false); } /** @@ -43,7 +44,7 @@ public function hasAll(): bool public function getAll(): bool { - return (bool)$this->overrideAll; + return (bool) $this->overrideAll; } public function setAll(bool|null $value = null): static diff --git a/tests/Feature/FeatureFlagProviderTest.php b/tests/Feature/FeatureFlagProviderTest.php index f2912fa..169b326 100644 --- a/tests/Feature/FeatureFlagProviderTest.php +++ b/tests/Feature/FeatureFlagProviderTest.php @@ -1,72 +1,72 @@ -fakeProvider = new FakeProvider(); - $this->overrider = new InMemoryOverrider(); - $this->provider = new FeatureFlagsOverrideProvider( - $this->fakeProvider, - $this->overrider, - ); -}); - -it('should return true if flag is set to true', function () { - $this->fakeProvider->setFlag(TestFeatureFlag::TestFlag, true); - expect($this->provider->flag(TestFeatureFlag::TestFlag))->toBeTrue(); -}); - -it('should return false if flag is set to false', function () { - $this->fakeProvider->setFlag(TestFeatureFlag::TestFlag, false); - expect($this->provider->flag(TestFeatureFlag::TestFlag))->toBeFalse(); -}); - -it('should validate the blade tags working correctly', function () { - $bladeSnippet = "@feature('test-flag') This is hidden feature @endfeature"; - $expectedCode = " This is hidden feature "; - expect(Blade::compileString($bladeSnippet))->toBe($expectedCode); -}); - -it('should successfully follow the override for a feature flag', function () { - expect($this->overrider->get(TestFeatureFlag::AmazingFeature)) - ->toBeFalse() - ->and($this->provider->flag(TestFeatureFlag::AmazingFeature)) - ->toBeFalse(); - - - $this->overrider->set(TestFeatureFlag::AmazingFeature, true); - - expect($this->overrider->get(TestFeatureFlag::AmazingFeature)) - ->toBeTrue() - ->and($this->provider->flag(TestFeatureFlag::AmazingFeature)) - ->toBeTrue(); -}); - -it('should correctly overide all feature flags if value is set', function () { - $this->fakeProvider->setFlag(TestFeatureFlag::FlagOne, true); - $this->fakeProvider->setFlag(TestFeatureFlag::FlagTwo, false); - $this->fakeProvider->setFlag(TestFeatureFlag::FlagThree, false); - - - expect($this->provider->flag(TestFeatureFlag::FlagOne)) - ->toBeTrue() - ->and($this->provider->flag(TestFeatureFlag::FlagTwo)) - ->toBeFalse() - ->and($this->provider->flag(TestFeatureFlag::FlagThree)) - ->toBeFalse(); - - $this->overrider->setAll(true); - - expect($this->provider->flag(TestFeatureFlag::FlagOne)) - ->toBeTrue() - ->and($this->provider->flag(TestFeatureFlag::FlagTwo)) - ->toBeTrue() - ->and($this->provider->flag(TestFeatureFlag::FlagThree)) - ->toBeTrue(); -}); +fakeProvider = new FakeProvider(); + $this->overrider = new InMemoryOverrider(); + $this->provider = new FeatureFlagsOverrideProvider( + $this->fakeProvider, + $this->overrider, + ); +}); + +it('should return true if flag is set to true', function () { + $this->fakeProvider->setFlag(TestFeatureFlag::TestFlag, true); + expect($this->provider->flag(TestFeatureFlag::TestFlag))->toBeTrue(); +}); + +it('should return false if flag is set to false', function () { + $this->fakeProvider->setFlag(TestFeatureFlag::TestFlag, false); + expect($this->provider->flag(TestFeatureFlag::TestFlag))->toBeFalse(); +}); + +it('should validate the blade tags working correctly', function () { + $bladeSnippet = "@feature('test-flag') This is hidden feature @endfeature"; + $expectedCode = " This is hidden feature "; + expect(Blade::compileString($bladeSnippet))->toBe($expectedCode); +}); + +it('should successfully follow the override for a feature flag', function () { + expect($this->overrider->get(TestFeatureFlag::AmazingFeature)) + ->toBeFalse() + ->and($this->provider->flag(TestFeatureFlag::AmazingFeature)) + ->toBeFalse(); + + + $this->overrider->set(TestFeatureFlag::AmazingFeature, true); + + expect($this->overrider->get(TestFeatureFlag::AmazingFeature)) + ->toBeTrue() + ->and($this->provider->flag(TestFeatureFlag::AmazingFeature)) + ->toBeTrue(); +}); + +it('should correctly overide all feature flags if value is set', function () { + $this->fakeProvider->setFlag(TestFeatureFlag::FlagOne, true); + $this->fakeProvider->setFlag(TestFeatureFlag::FlagTwo, false); + $this->fakeProvider->setFlag(TestFeatureFlag::FlagThree, false); + + + expect($this->provider->flag(TestFeatureFlag::FlagOne)) + ->toBeTrue() + ->and($this->provider->flag(TestFeatureFlag::FlagTwo)) + ->toBeFalse() + ->and($this->provider->flag(TestFeatureFlag::FlagThree)) + ->toBeFalse(); + + $this->overrider->setAll(true); + + expect($this->provider->flag(TestFeatureFlag::FlagOne)) + ->toBeTrue() + ->and($this->provider->flag(TestFeatureFlag::FlagTwo)) + ->toBeTrue() + ->and($this->provider->flag(TestFeatureFlag::FlagThree)) + ->toBeTrue(); +});