From 2361dbd8bc17146a636346aa2483288fa42a29a8 Mon Sep 17 00:00:00 2001 From: Serhii Andriichuk Date: Sun, 11 Sep 2022 11:49:40 +0300 Subject: [PATCH] Add system env reader --- src/Application/Command/AddCommand.php | 2 +- src/Environment/Reader/EnvReaderFactory.php | 11 ++- src/Environment/Reader/EnvReaderType.php | 14 ++++ src/Environment/Reader/SystemEnvReader.php | 16 ++++ src/Generator/SpecGenerator.php | 10 +-- .../JoseGonzalezDotEnvFileReaderTest.php | 5 ++ .../Reader/SymfonyDotEnvFileReaderTest.php | 5 ++ .../Reader/VlucasPhpDotEnvFileReaderTest.php | 6 ++ .../Generator/SpecGeneratorTest.php | 73 ++++++++++++++----- tests/fixtures/common/.env | 25 +++++++ tests/fixtures/common/keepenv.yaml | 26 +++++++ .../{case_2 => generator/laravel_preset}/.env | 0 .../laravel_preset/keepenv.yaml} | 0 .../symfony_preset/.env} | 0 .../symfony_preset/keepenv.yaml} | 0 15 files changed, 162 insertions(+), 31 deletions(-) create mode 100644 src/Environment/Reader/EnvReaderType.php create mode 100644 src/Environment/Reader/SystemEnvReader.php rename tests/fixtures/{case_2 => generator/laravel_preset}/.env (100%) rename tests/fixtures/{case_2/keepenv_laravel.yaml => generator/laravel_preset/keepenv.yaml} (100%) rename tests/fixtures/{case_2/.env.symfony => generator/symfony_preset/.env} (100%) rename tests/fixtures/{case_2/keepenv_symfony.yaml => generator/symfony_preset/keepenv.yaml} (100%) diff --git a/src/Application/Command/AddCommand.php b/src/Application/Command/AddCommand.php index 346da13..5c99c10 100644 --- a/src/Application/Command/AddCommand.php +++ b/src/Application/Command/AddCommand.php @@ -23,7 +23,7 @@ /** * @author Serhii Andriichuk */ -class AddCommand extends Command +final class AddCommand extends Command { protected static $defaultName = 'add'; diff --git a/src/Environment/Reader/EnvReaderFactory.php b/src/Environment/Reader/EnvReaderFactory.php index 96f3039..08a9656 100644 --- a/src/Environment/Reader/EnvReaderFactory.php +++ b/src/Environment/Reader/EnvReaderFactory.php @@ -14,16 +14,19 @@ class EnvReaderFactory public function make(string $type): EnvReaderInterface { switch ($type) { - case 'auto': + case EnvReaderType::AUTO: return $this->baseOnAvailability(); - case 'vlucas/phpdotenv': + case EnvReaderType::SYSTEM: + return new SystemEnvReader(); + + case EnvReaderType::VLUCAS: return new VlucasPhpDotEnvFileReader(); - case 'symfony/dotenv': + case EnvReaderType::SYMFONY: return new SymfonyDotEnvFileReader(); - case 'josegonzalez/dotenv': + case EnvReaderType::JOSEGONZALEZ: return new JoseGonzalezDotEnvFileReader(); default: diff --git a/src/Environment/Reader/EnvReaderType.php b/src/Environment/Reader/EnvReaderType.php new file mode 100644 index 0000000..84424af --- /dev/null +++ b/src/Environment/Reader/EnvReaderType.php @@ -0,0 +1,14 @@ + + */ +class SystemEnvReader implements EnvReaderInterface +{ + public function read(string ...$paths): array + { + return $_ENV; + } +} diff --git a/src/Generator/SpecGenerator.php b/src/Generator/SpecGenerator.php index c5a7912..17ebcc0 100644 --- a/src/Generator/SpecGenerator.php +++ b/src/Generator/SpecGenerator.php @@ -68,13 +68,9 @@ public function generate( continue; } - $required = trim((string) $value) !== ''; - $rules = []; - - if ($required) { - $rules['required'] = true; - } - + $rules = [ + 'required' => trim((string) $value) !== '' + ]; $rules = array_merge($rules, $this->guessType($key, $value)); $envSpec->add( diff --git a/tests/Functional/Environment/Reader/JoseGonzalezDotEnvFileReaderTest.php b/tests/Functional/Environment/Reader/JoseGonzalezDotEnvFileReaderTest.php index 0d1d7e1..a6a8770 100644 --- a/tests/Functional/Environment/Reader/JoseGonzalezDotEnvFileReaderTest.php +++ b/tests/Functional/Environment/Reader/JoseGonzalezDotEnvFileReaderTest.php @@ -50,9 +50,14 @@ public function testReaderCanProvideVariablesFromFile(): void 'APP_NAME' => 'KeepEnv', 'APP_ENV' => 'production', 'APP_DEBUG' => true, + 'PAYMENT_FEATURE_ENABLED' => 'false', 'APP_KEY' => null, 'REDIS_PORT' => 6379, + 'REDIS_PASSWORD' => null, + 'PROFILING_LIMIT' => 2.5, 'MAIL_FROM_ADDRESS' => 'test@test.com', + 'DEBUG_IP' => '127.0.0.1', + 'MAINTENANCE' => 'on', ], $variables, ); diff --git a/tests/Functional/Environment/Reader/SymfonyDotEnvFileReaderTest.php b/tests/Functional/Environment/Reader/SymfonyDotEnvFileReaderTest.php index 48e0eb3..8290660 100644 --- a/tests/Functional/Environment/Reader/SymfonyDotEnvFileReaderTest.php +++ b/tests/Functional/Environment/Reader/SymfonyDotEnvFileReaderTest.php @@ -53,6 +53,11 @@ public function testReaderCanProvideVariablesFromFile(): void 'APP_KEY' => '', 'REDIS_PORT' => '6379', 'MAIL_FROM_ADDRESS' => 'test@test.com', + 'PAYMENT_FEATURE_ENABLED' => 'false', + 'REDIS_PASSWORD' => 'null', + 'PROFILING_LIMIT' => '2.50', + 'DEBUG_IP' => '127.0.0.1', + 'MAINTENANCE' => 'on', ], $variables, ); diff --git a/tests/Functional/Environment/Reader/VlucasPhpDotEnvFileReaderTest.php b/tests/Functional/Environment/Reader/VlucasPhpDotEnvFileReaderTest.php index 8e0599f..e5b2ed6 100644 --- a/tests/Functional/Environment/Reader/VlucasPhpDotEnvFileReaderTest.php +++ b/tests/Functional/Environment/Reader/VlucasPhpDotEnvFileReaderTest.php @@ -32,6 +32,7 @@ protected function setUp(): void $this->reader = new VlucasPhpDotEnvFileReader(); + // Check that environment variables cannot affect the file reading $_ENV['APP_ENV'] = 'dev'; $_ENV['APP_RANDOM_KEY'] = 'test_123'; } @@ -53,6 +54,11 @@ public function testReaderCanProvideVariablesFromFile(): void 'APP_KEY' => '', 'REDIS_PORT' => '6379', 'MAIL_FROM_ADDRESS' => 'test@test.com', + 'PAYMENT_FEATURE_ENABLED' => 'false', + 'REDIS_PASSWORD' => 'null', + 'PROFILING_LIMIT' => '2.50', + 'DEBUG_IP' => '127.0.0.1', + 'MAINTENANCE' => 'on', ], $variables, ); diff --git a/tests/Functional/Generator/SpecGeneratorTest.php b/tests/Functional/Generator/SpecGeneratorTest.php index d38ada2..6d9f438 100644 --- a/tests/Functional/Generator/SpecGeneratorTest.php +++ b/tests/Functional/Generator/SpecGeneratorTest.php @@ -5,6 +5,7 @@ namespace Andriichuk\KeepEnv\Tests\Functional\Generator; use Andriichuk\KeepEnv\Environment\Reader\EnvReaderFactory; +use Andriichuk\KeepEnv\Environment\Reader\EnvReaderType; use Andriichuk\KeepEnv\Generator\Exceptions\SpecGeneratorException; use Andriichuk\KeepEnv\Generator\Presets\PresetFactory; use Andriichuk\KeepEnv\Generator\SpecGenerator; @@ -26,7 +27,12 @@ protected function setUp(): void $this->rootFolder = vfsStream::setup('src'); } - public function testEnvironmentSpecificationGeneration(): void + protected function tearDown(): void + { + $_ENV = []; + } + + public function testGeneratorCanAutomaticallyDetectReaderBasedOnAvailablePackage(): void { $this->rootFolder->addChild( (new vfsStreamFile('.env')) @@ -38,7 +44,7 @@ public function testEnvironmentSpecificationGeneration(): void ); $specGenerator = new SpecGenerator( - (new EnvReaderFactory())->make('auto'), + (new EnvReaderFactory())->make(EnvReaderType::AUTO), (new SpecWriterFactory())->basedOnResource('vfs://src/keepenv.yaml'), new PresetFactory(), ); @@ -51,24 +57,24 @@ public function testEnvironmentSpecificationGeneration(): void ); $this->assertFileEquals( - dirname(__DIR__, 2) . '/fixtures/case_1/keepenv.yaml', + dirname(__DIR__, 2) . '/fixtures/common/keepenv.yaml', $this->rootFolder->getChild('keepenv.yaml')->url(), ); } - public function testEnvironmentSpecificationWithLaravelPresetGeneration(): void + public function testGeneratorCanCreateSpecUsingLaravelPreset(): void { $this->rootFolder->addChild( (new vfsStreamFile('.env')) ->setContent( file_get_contents( - dirname(__DIR__, 2) . '/fixtures/case_2/.env', + dirname(__DIR__, 2) . '/fixtures/generator/laravel_preset/.env', ), ), ); $specGenerator = new SpecGenerator( - (new EnvReaderFactory())->make('auto'), + (new EnvReaderFactory())->make(EnvReaderType::AUTO), (new SpecWriterFactory())->basedOnResource('vfs://src/keepenv_laravel.yaml'), new PresetFactory(), ); @@ -76,45 +82,74 @@ public function testEnvironmentSpecificationWithLaravelPresetGeneration(): void $specGenerator->generate( 'common', [dirname($this->rootFolder->getChild('.env')->url())], - 'vfs://src/keepenv_laravel.yaml', + 'vfs://src/keepenv.yaml', static fn () => true, 'laravel' ); $this->assertFileEquals( - dirname(__DIR__, 2) . '/fixtures/case_2/keepenv_laravel.yaml', - $this->rootFolder->getChild('keepenv_laravel.yaml')->url(), + dirname(__DIR__, 2) . '/fixtures/generator/laravel_preset/keepenv.yaml', + $this->rootFolder->getChild('keepenv.yaml')->url(), ); } - public function testEnvironmentSpecificationWithSymfonyPresetGeneration(): void + public function testGeneratorCanCreateSpecUsingSymfonyPreset(): void { $this->rootFolder->addChild( - (new vfsStreamFile('.env.symfony')) + (new vfsStreamFile('.env')) ->setContent( file_get_contents( - dirname(__DIR__, 2) . '/fixtures/case_2/.env.symfony', + dirname(__DIR__, 2) . '/fixtures/generator/symfony_preset/.env', ), ), ); $specGenerator = new SpecGenerator( - (new EnvReaderFactory())->make('symfony/dotenv'), - (new SpecWriterFactory())->basedOnResource('vfs://src/keepenv_symfony.yaml'), + (new EnvReaderFactory())->make(EnvReaderType::SYMFONY), + (new SpecWriterFactory())->basedOnResource('vfs://src/keepenv.yaml'), new PresetFactory(), ); $specGenerator->generate( 'common', - [$this->rootFolder->getChild('.env.symfony')->url()], - 'vfs://src/keepenv_symfony.yaml', + [$this->rootFolder->getChild('.env')->url()], + 'vfs://src/keepenv.yaml', static fn () => true, 'symfony' ); $this->assertFileEquals( - dirname(__DIR__, 2) . '/fixtures/case_2/keepenv_symfony.yaml', - $this->rootFolder->getChild('keepenv_symfony.yaml')->url(), + dirname(__DIR__, 2) . '/fixtures/generator/symfony_preset/keepenv.yaml', + $this->rootFolder->getChild('keepenv.yaml')->url(), + ); + } + + public function testGeneratorCanCreateSpecUsingSystemVariables(): void + { + $_ENV['APP_NAME'] = 'KeepEnv'; + $_ENV['APP_ENV'] = 'production'; + $_ENV['APP_DEBUG'] = true; + $_ENV['APP_KEY'] = ''; + $_ENV['REDIS_PORT'] = 6379; + $_ENV['REDIS_PASSWORD'] = null; + $_ENV['MAIL_FROM_ADDRESS'] = 'test@test.com'; + + $specGenerator = new SpecGenerator( + (new EnvReaderFactory())->make(EnvReaderType::SYSTEM), + (new SpecWriterFactory())->basedOnResource('vfs://src/keepenv.yaml'), + new PresetFactory(), + ); + + $specGenerator->generate( + 'common', + [], + 'vfs://src/keepenv.yaml', + static fn (): bool => true, + ); + + $this->assertFileEquals( + dirname(__DIR__, 2) . '/fixtures/common/keepenv.yaml', + $this->rootFolder->getChild('keepenv.yaml')->url(), ); } @@ -126,7 +161,7 @@ public function testGeneratorThrowsExceptionOnAlreadyExistingSpecFile(): void $this->rootFolder->addChild((new vfsStreamFile('keepenv.yaml'))->setContent('')); $specGenerator = new SpecGenerator( - (new EnvReaderFactory())->make('auto'), + (new EnvReaderFactory())->make(EnvReaderType::AUTO), (new SpecWriterFactory())->basedOnResource('vfs://src/keepenv.yaml'), new PresetFactory(), ); diff --git a/tests/fixtures/common/.env b/tests/fixtures/common/.env index 6aa2d73..427f35a 100644 --- a/tests/fixtures/common/.env +++ b/tests/fixtures/common/.env @@ -1,7 +1,32 @@ +# String value APP_NAME="KeepEnv" + +# String value without quotes APP_ENV=production + +# Boolean APP_DEBUG=true + +# Boolean with quotes +PAYMENT_FEATURE_ENABLED="false" + +# Empty value APP_KEY= +# Numberic integer REDIS_PORT=6379 + +# Nullable +REDIS_PASSWORD=null + +# Numberic decimal +PROFILING_LIMIT=2.50 + +# Email address MAIL_FROM_ADDRESS=test@test.com + +# IP address +DEBUG_IP=127.0.0.1 + +# Boolean with quotes +export MAINTENANCE="on" diff --git a/tests/fixtures/common/keepenv.yaml b/tests/fixtures/common/keepenv.yaml index 230841a..5637ecc 100644 --- a/tests/fixtures/common/keepenv.yaml +++ b/tests/fixtures/common/keepenv.yaml @@ -17,14 +17,40 @@ environments: rules: required: true boolean: true + PAYMENT_FEATURE_ENABLED: + description: 'Payment feature enabled.' + rules: + required: true + boolean: true APP_KEY: description: 'Application key.' + rules: + required: false REDIS_PORT: description: 'Redis port.' rules: required: true numeric: true + REDIS_PASSWORD: + description: 'Redis password.' + rules: + required: false + PROFILING_LIMIT: + description: 'Profiling limit.' + rules: + required: true + numeric: true MAIL_FROM_ADDRESS: description: 'Mail from address.' rules: required: true + DEBUG_IP: + description: 'Debug ip.' + rules: + required: true + ip: true + MAINTENANCE: + description: Maintenance. + rules: + required: true + boolean: true diff --git a/tests/fixtures/case_2/.env b/tests/fixtures/generator/laravel_preset/.env similarity index 100% rename from tests/fixtures/case_2/.env rename to tests/fixtures/generator/laravel_preset/.env diff --git a/tests/fixtures/case_2/keepenv_laravel.yaml b/tests/fixtures/generator/laravel_preset/keepenv.yaml similarity index 100% rename from tests/fixtures/case_2/keepenv_laravel.yaml rename to tests/fixtures/generator/laravel_preset/keepenv.yaml diff --git a/tests/fixtures/case_2/.env.symfony b/tests/fixtures/generator/symfony_preset/.env similarity index 100% rename from tests/fixtures/case_2/.env.symfony rename to tests/fixtures/generator/symfony_preset/.env diff --git a/tests/fixtures/case_2/keepenv_symfony.yaml b/tests/fixtures/generator/symfony_preset/keepenv.yaml similarity index 100% rename from tests/fixtures/case_2/keepenv_symfony.yaml rename to tests/fixtures/generator/symfony_preset/keepenv.yaml