From 0595fb830d0036fa57e75ab9fde6e224461bc5c0 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Fri, 29 Nov 2024 00:12:43 +0400 Subject: [PATCH 1/5] AppEnvironment: add aliases for `production` and `test` environments --- src/Boot/src/Environment/AppEnvironment.php | 7 +++++++ src/Boot/tests/Environment/AppEnvironmentTest.php | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/src/Boot/src/Environment/AppEnvironment.php b/src/Boot/src/Environment/AppEnvironment.php index 05f43eac2..d509f1496 100644 --- a/src/Boot/src/Environment/AppEnvironment.php +++ b/src/Boot/src/Environment/AppEnvironment.php @@ -40,6 +40,13 @@ public static function detect(EnvironmentInterface $environment): self { $value = $environment->get('APP_ENV'); + // Aliases + $value = match ($value) { + 'production' => self::Production->value, + 'test' => self::Testing->value, + default => $value, + }; + return \is_string($value) ? (self::tryFrom($value) ?? self::Local) : self::Local; diff --git a/src/Boot/tests/Environment/AppEnvironmentTest.php b/src/Boot/tests/Environment/AppEnvironmentTest.php index 551ee2857..7cb1626dd 100644 --- a/src/Boot/tests/Environment/AppEnvironmentTest.php +++ b/src/Boot/tests/Environment/AppEnvironmentTest.php @@ -39,9 +39,13 @@ public static function envVariablesDataProvider(): \Traversable { yield ['wrong', AppEnvironment::Local]; yield ['prod', AppEnvironment::Production]; + yield ['production', AppEnvironment::Production]; yield ['stage', AppEnvironment::Stage]; yield ['local', AppEnvironment::Local]; + yield ['dev', AppEnvironment::Local]; + yield ['development', AppEnvironment::Local]; yield ['testing', AppEnvironment::Testing]; + yield ['test', AppEnvironment::Testing]; } public function testClassMethods(): void From 323c46e902b73daa261e9581bd55e56c795a831b Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Fri, 29 Nov 2024 00:50:25 +0400 Subject: [PATCH 2/5] Fix tests --- src/Framework/Console/Confirmation/ApplicationInProduction.php | 3 ++- tests/app/src/Command/ApplicationInProductionCommand.php | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Framework/Console/Confirmation/ApplicationInProduction.php b/src/Framework/Console/Confirmation/ApplicationInProduction.php index 0e7de6231..410f9a098 100644 --- a/src/Framework/Console/Confirmation/ApplicationInProduction.php +++ b/src/Framework/Console/Confirmation/ApplicationInProduction.php @@ -10,6 +10,7 @@ use Spiral\Framework\Spiral; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; #[Scope(Spiral::ConsoleCommand)] final class ApplicationInProduction @@ -22,7 +23,7 @@ public function __construct( OutputInterface $output, ) { $this->input = $input; - $this->output = $output; + $this->output = new SymfonyStyle($input, $output); } public function confirmToProceed(string $message = 'Application in production.'): bool diff --git a/tests/app/src/Command/ApplicationInProductionCommand.php b/tests/app/src/Command/ApplicationInProductionCommand.php index d6bd39b55..e6031d05c 100644 --- a/tests/app/src/Command/ApplicationInProductionCommand.php +++ b/tests/app/src/Command/ApplicationInProductionCommand.php @@ -16,6 +16,7 @@ public function __invoke(ApplicationInProduction $confirmation, OutputInterface { if ($confirmation->confirmToProceed('Application in production.')) { $this->writeln('Application is in production.'); + return self::SUCCESS; } $this->writeln('Application is in testing.'); From fcabd76d9b34f7e793820190a39e2eb5ee65e978 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Fri, 29 Nov 2024 14:07:58 +0400 Subject: [PATCH 3/5] Remove unnecessary tests --- .../ApplicationInProductionTest.php | 51 +++---------------- 1 file changed, 6 insertions(+), 45 deletions(-) diff --git a/tests/Framework/Console/Confirmation/ApplicationInProductionTest.php b/tests/Framework/Console/Confirmation/ApplicationInProductionTest.php index 5c6df2e6d..9e39a6a1a 100644 --- a/tests/Framework/Console/Confirmation/ApplicationInProductionTest.php +++ b/tests/Framework/Console/Confirmation/ApplicationInProductionTest.php @@ -4,7 +4,6 @@ namespace Framework\Console\Confirmation; -use Mockery as m; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Spiral\Boot\Environment\AppEnvironment; @@ -19,8 +18,8 @@ public function testNotProductionEnvironmentShouldBeConfirmed(AppEnvironment $en { $confirmation = new ApplicationInProduction( $env, - m::mock(InputInterface::class), - m::mock(OutputInterface::class) + $this->createMock(InputInterface::class), + $this->createMock(OutputInterface::class) ); $this->assertTrue($confirmation->confirmToProceed()); @@ -30,54 +29,16 @@ public function testProductionEnvWithForceOptionShouldBeConfirmed(): void { $confirmation = new ApplicationInProduction( AppEnvironment::Production, - $input = m::mock(InputInterface::class), - m::mock(OutputInterface::class) + $input = $this->createMock(InputInterface::class), + $this->createMock(OutputInterface::class) ); - $input->shouldReceive('hasOption')->once()->andReturnTrue(); - $input->shouldReceive('getOption')->once()->andReturnTrue(); + $input->expects($this->once())->method('hasOption')->willReturn(true); + $input->expects($this->once())->method('getOption')->willReturn(true); $this->assertTrue($confirmation->confirmToProceed()); } - public function testProductionEnvShouldBeAskAboutConfirmationAndConfirmed(): void - { - $confirmation = new ApplicationInProduction( - AppEnvironment::Production, - $input = m::mock(InputInterface::class), - $output = m::mock(OutputInterface::class) - ); - - $input->shouldReceive('hasOption')->once()->andReturnFalse(); - - $output->shouldReceive('writeln'); - $output->shouldReceive('write'); - $output->shouldReceive('newLine'); - - $output->shouldReceive('confirm')->once()->with('Do you really wish to run command?', false)->andReturnTrue(); - - $this->assertTrue($confirmation->confirmToProceed()); - } - - public function testProductionEnvShouldBeAskAboutConfirmationAndNotConfirmed(): void - { - $confirmation = new ApplicationInProduction( - AppEnvironment::Production, - $input = m::mock(InputInterface::class), - $output = m::mock(OutputInterface::class) - ); - - $input->shouldReceive('hasOption')->once()->andReturnFalse(); - - $output->shouldReceive('writeln'); - $output->shouldReceive('write'); - $output->shouldReceive('newLine'); - - $output->shouldReceive('confirm')->once()->with('Do you really wish to run command?', false)->andReturnFalse(); - - $this->assertFalse($confirmation->confirmToProceed()); - } - public static function notProductionEnvs(): \Traversable { yield 'Local' => [AppEnvironment::Local]; From 85c9f433b49e00b565c5036bd4bf842db86cf862 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Fri, 29 Nov 2024 21:40:19 +0400 Subject: [PATCH 4/5] Improve tests --- src/Console/tests/SignatureTest.php | 3 +- .../Confirmation/ApplicationInProduction.php | 2 +- .../ApplicationInProductionTest.php | 39 +++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/Console/tests/SignatureTest.php b/src/Console/tests/SignatureTest.php index 4b10f434a..616f843c1 100644 --- a/src/Console/tests/SignatureTest.php +++ b/src/Console/tests/SignatureTest.php @@ -151,7 +151,8 @@ public function perform(): int -Q, --quit Quit option description. --naf[=NAF] Naf option description. [default: "default"] -h, --help Display help for the given command. When no command is given display help for the list command - -q, --quiet Do not output any message + --silent Do not output any message + -q, --quiet Only errors are displayed. All other output is suppressed -V, --version Display this application version --ansi|--no-ansi Force (or disable --no-ansi) ANSI output -n, --no-interaction Do not ask any interactive question diff --git a/src/Framework/Console/Confirmation/ApplicationInProduction.php b/src/Framework/Console/Confirmation/ApplicationInProduction.php index 410f9a098..90607ec3a 100644 --- a/src/Framework/Console/Confirmation/ApplicationInProduction.php +++ b/src/Framework/Console/Confirmation/ApplicationInProduction.php @@ -23,7 +23,7 @@ public function __construct( OutputInterface $output, ) { $this->input = $input; - $this->output = new SymfonyStyle($input, $output); + $this->output = $output instanceof SymfonyStyle ? $output : new SymfonyStyle($input, $output); } public function confirmToProceed(string $message = 'Application in production.'): bool diff --git a/tests/Framework/Console/Confirmation/ApplicationInProductionTest.php b/tests/Framework/Console/Confirmation/ApplicationInProductionTest.php index 9e39a6a1a..ea5f566ba 100644 --- a/tests/Framework/Console/Confirmation/ApplicationInProductionTest.php +++ b/tests/Framework/Console/Confirmation/ApplicationInProductionTest.php @@ -4,12 +4,14 @@ namespace Framework\Console\Confirmation; +use Mockery as m; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Spiral\Boot\Environment\AppEnvironment; use Spiral\Console\Confirmation\ApplicationInProduction; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; final class ApplicationInProductionTest extends TestCase { @@ -39,6 +41,43 @@ public function testProductionEnvWithForceOptionShouldBeConfirmed(): void $this->assertTrue($confirmation->confirmToProceed()); } + + public function testProductionEnvShouldBeAskAboutConfirmationAndConfirmed(): void + { + $confirmation = new ApplicationInProduction( + AppEnvironment::Production, + $input = $this->createMock(InputInterface::class), + $output = $this->createMock(SymfonyStyle::class) + ); + + $input->expects($this->once())->method('hasOption')->willReturn(false); + $output + ->expects($this->once()) + ->method('confirm') + ->with('Do you really wish to run command?', false) + ->willReturn(true); + + $this->assertTrue($confirmation->confirmToProceed()); + } + + public function testProductionEnvShouldBeAskAboutConfirmationAndNotConfirmed(): void + { + $confirmation = new ApplicationInProduction( + AppEnvironment::Production, + $input = $this->createMock(InputInterface::class), + $output = $this->createMock(SymfonyStyle::class) + ); + + $input->expects($this->once())->method('hasOption')->willReturn(false); + $output + ->expects($this->once()) + ->method('confirm') + ->with('Do you really wish to run command?', false) + ->willReturn(false); + + $this->assertFalse($confirmation->confirmToProceed()); + } + public static function notProductionEnvs(): \Traversable { yield 'Local' => [AppEnvironment::Local]; From b379e9c257ee37dd10976a72d2b42e9388b760f5 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Sat, 30 Nov 2024 00:18:44 +0400 Subject: [PATCH 5/5] Improve tests; add description for `ApplicationInProduction` --- src/Console/tests/SignatureTest.php | 10 +--------- .../Console/Confirmation/ApplicationInProduction.php | 5 +++++ .../ApplicationInProductionCommandTest.php | 4 ++-- .../app/src/Command/ApplicationInProductionCommand.php | 4 ++-- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/Console/tests/SignatureTest.php b/src/Console/tests/SignatureTest.php index 616f843c1..be2ee820f 100644 --- a/src/Console/tests/SignatureTest.php +++ b/src/Console/tests/SignatureTest.php @@ -136,7 +136,7 @@ public function perform(): int ]) ); - $this->assertSame( + $this->assertStringStartsWith( <<<'HELP' Usage: foo:bar [options] [--] [ [...]] @@ -150,14 +150,6 @@ public function perform(): int -o, --id[=ID] Id option description. (multiple values allowed) -Q, --quit Quit option description. --naf[=NAF] Naf option description. [default: "default"] - -h, --help Display help for the given command. When no command is given display help for the list command - --silent Do not output any message - -q, --quiet Only errors are displayed. All other output is suppressed - -V, --version Display this application version - --ansi|--no-ansi Force (or disable --no-ansi) ANSI output - -n, --no-interaction Do not ask any interactive question - -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug - HELP , $core->run(command: 'help', input: ['command_name' => 'foo:bar'])->getOutput()->fetch() diff --git a/src/Framework/Console/Confirmation/ApplicationInProduction.php b/src/Framework/Console/Confirmation/ApplicationInProduction.php index 90607ec3a..d2f45ca4b 100644 --- a/src/Framework/Console/Confirmation/ApplicationInProduction.php +++ b/src/Framework/Console/Confirmation/ApplicationInProduction.php @@ -12,6 +12,11 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; +/** + * The component makes it easy to ask the user for confirmation before running + * a command if the application is running in production mode. + * This can help prevent accidental or unintended changes to the production environment. + */ #[Scope(Spiral::ConsoleCommand)] final class ApplicationInProduction { diff --git a/tests/Framework/Console/Confirmation/ApplicationInProductionCommandTest.php b/tests/Framework/Console/Confirmation/ApplicationInProductionCommandTest.php index a9250bb54..56844ccd6 100644 --- a/tests/Framework/Console/Confirmation/ApplicationInProductionCommandTest.php +++ b/tests/Framework/Console/Confirmation/ApplicationInProductionCommandTest.php @@ -16,7 +16,7 @@ final class ApplicationInProductionCommandTest extends BaseTestCase public function testRunCommandInProductionEnv(): void { $this->assertConsoleCommandOutputContainsStrings('app-in-production', [], [ - 'Application is in production.', + 'Denied.', ]); } @@ -24,7 +24,7 @@ public function testRunCommandInProductionEnv(): void public function testRunCommandInTestingEnv(): void { $this->assertConsoleCommandOutputContainsStrings('app-in-production', [], [ - 'Application is in testing.', + 'Allowed.', ]); } } diff --git a/tests/app/src/Command/ApplicationInProductionCommand.php b/tests/app/src/Command/ApplicationInProductionCommand.php index e6031d05c..b22055e77 100644 --- a/tests/app/src/Command/ApplicationInProductionCommand.php +++ b/tests/app/src/Command/ApplicationInProductionCommand.php @@ -15,11 +15,11 @@ final class ApplicationInProductionCommand extends Command public function __invoke(ApplicationInProduction $confirmation, OutputInterface $output): int { if ($confirmation->confirmToProceed('Application in production.')) { - $this->writeln('Application is in production.'); + $this->writeln('Allowed.'); return self::SUCCESS; } - $this->writeln('Application is in testing.'); + $this->writeln('Denied.'); return self::SUCCESS; }