Skip to content

Commit cc909a0

Browse files
committed
bug fixes.
1 parent 5829921 commit cc909a0

5 files changed

Lines changed: 170 additions & 5 deletions

File tree

src/Cli/Commands/Secrets/EditCommand.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public function configure(): void
4646
$this->addOption( 'env', 'e', true, 'Environment to edit (default: base secrets)' );
4747
$this->addOption( 'editor', null, true, 'Editor to use (default: vi)' );
4848
$this->addOption( 'config', 'c', true, 'Config directory path (default: config)' );
49+
$this->addOption( 'verbose', 'v', false, 'Verbose output' );
4950
}
5051

5152
/**
@@ -82,6 +83,17 @@ public function execute(): int
8283
$this->output->warning( "Key file not found at: {$keyPath}" );
8384
$this->output->info( "Generating new encryption key..." );
8485

86+
// Ensure directory exists
87+
$dir = dirname( $keyPath );
88+
if( !is_dir( $dir ) )
89+
{
90+
if( !mkdir( $dir, 0755, true ) )
91+
{
92+
$this->output->error( "Failed to create directory: {$dir}" );
93+
return 1;
94+
}
95+
}
96+
8597
$key = $this->secretManager->generateKey( $keyPath );
8698
$this->output->success( "Generated new key at: {$keyPath}" );
8799
$this->output->warning( "IMPORTANT: Add {$keyPath} to .gitignore!" );

src/Cli/Commands/Secrets/Key/GenerateCommand.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public function configure(): void
4747
$this->addOption( 'config', 'c', true, 'Config directory path (default: config)' );
4848
$this->addOption( 'force', 'f', false, 'Overwrite existing key file' );
4949
$this->addOption( 'show', 's', false, 'Display the generated key' );
50+
$this->addOption( 'verbose', 'v', false, 'Verbose output' );
5051
}
5152

5253
/**

src/Cli/Commands/Secrets/ShowCommand.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public function configure(): void
4848
$this->addOption( 'key', 'k', true, 'Show only specific key/section' );
4949
$this->addOption( 'config', 'c', true, 'Config directory path (default: config)' );
5050
$this->addOption( 'force', 'f', false, 'Skip confirmation prompt' );
51+
$this->addOption( 'verbose', 'v', false, 'Verbose output' );
5152
}
5253

5354
/**
@@ -65,7 +66,7 @@ public function execute(): int
6566
{
6667
$this->output->warning( "You are about to display production secrets!" );
6768

68-
if( !$this->output->confirm( "Are you sure you want to continue?" ) )
69+
if( !$this->confirm( "Are you sure you want to continue?" ) )
6970
{
7071
$this->output->info( "Operation cancelled." );
7172
return 0;

tests/Cli/Commands/Secrets/EditCommandTest.php

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,17 +159,66 @@ public function testExecuteWithEnvironment(): void
159159
$this->assertStringContainsString( "Secrets saved to: {$credentialsPath}", $outputContent );
160160
}
161161

162+
/**
163+
* Test that environment directory is created when missing
164+
*/
165+
public function testExecuteCreatesEnvironmentDirectory(): void
166+
{
167+
// Do NOT create the secrets directory - let the command create it
168+
169+
// Create input with options
170+
$input = new Input( [
171+
'--config=' . $this->testConfigPath,
172+
'--env=staging',
173+
'--editor=echo' // Use echo as a no-op editor
174+
] );
175+
$input->parse( $this->command );
176+
177+
// Create output
178+
$output = new Output( false );
179+
180+
$this->command->setInput( $input );
181+
$this->command->setOutput( $output );
182+
183+
// Capture output
184+
ob_start();
185+
$result = $this->command->execute();
186+
$outputContent = ob_get_clean();
187+
188+
$secretsDir = $this->testConfigPath . '/secrets';
189+
$keyPath = $secretsDir . '/staging.key';
190+
$credentialsPath = $secretsDir . '/staging.yml.enc';
191+
192+
// Execute should succeed
193+
$this->assertEquals( 0, $result );
194+
195+
// Directory should be created
196+
$this->assertDirectoryExists( $secretsDir );
197+
198+
// Key and credentials should be generated
199+
$this->assertFileExists( $keyPath );
200+
$this->assertFileExists( $credentialsPath );
201+
202+
// Check output messages
203+
$this->assertStringContainsString( 'Editing staging environment secrets...', $outputContent );
204+
$this->assertStringContainsString( "Key file not found at: {$keyPath}", $outputContent );
205+
$this->assertStringContainsString( "Generating new encryption key...", $outputContent );
206+
$this->assertStringContainsString( "Generated new key at: {$keyPath}", $outputContent );
207+
$this->assertStringContainsString( "Secrets saved to: {$credentialsPath}", $outputContent );
208+
}
209+
162210
/**
163211
* Test that error is handled gracefully
164212
*/
165213
public function testExecuteWithError(): void
166214
{
167-
// Use a path that will cause an error (non-writable)
168-
$badPath = '/root/cannot_write_here';
215+
// Create a non-writable directory
216+
$nonWritablePath = sys_get_temp_dir() . '/test_non_writable_' . uniqid();
217+
mkdir( $nonWritablePath, 0000, true ); // Create with no permissions
169218

170-
// Create input with options
219+
// Create input with options pointing to the non-writable path
171220
$input = new Input( [
172-
'--config=' . $badPath
221+
'--config=' . $nonWritablePath
173222
] );
174223
$input->parse( $this->command );
175224

@@ -189,6 +238,10 @@ public function testExecuteWithError(): void
189238

190239
// Check error message
191240
$this->assertStringContainsString( 'Error editing secrets:', $outputContent );
241+
242+
// Clean up: restore permissions and remove directory
243+
chmod( $nonWritablePath, 0755 );
244+
rmdir( $nonWritablePath );
192245
}
193246

194247
/**

tests/Cli/Commands/Secrets/ShowCommandTest.php

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use PHPUnit\Framework\TestCase;
88
use Neuron\Cli\Console\Input;
99
use Neuron\Cli\Console\Output;
10+
use Neuron\Cli\IO\TestInputReader;
1011

1112
class ShowCommandTest extends TestCase
1213
{
@@ -135,6 +136,103 @@ public function testExecuteShowsSpecificKey(): void
135136
$this->assertStringNotContainsString( 'api:', $outputContent );
136137
}
137138

139+
/**
140+
* Test production environment confirmation prompt
141+
*/
142+
public function testExecuteProductionConfirmation(): void
143+
{
144+
// Create test secrets for production environment
145+
mkdir( $this->testConfigPath . '/secrets', 0755, true );
146+
$keyPath = $this->testConfigPath . '/secrets/production.key';
147+
$credentialsPath = $this->testConfigPath . '/secrets/production.yml.enc';
148+
149+
$key = $this->secretManager->generateKey( $keyPath );
150+
151+
$tempPlaintextPath = $this->testConfigPath . '/temp_plaintext.yml';
152+
$testData = "database:\n password: production_secret";
153+
file_put_contents( $tempPlaintextPath, $testData );
154+
$this->secretManager->encrypt( $tempPlaintextPath, $credentialsPath, $keyPath );
155+
unlink( $tempPlaintextPath );
156+
157+
// Test 1: User confirms - secrets should be shown
158+
$input = new Input( [
159+
'--config=' . $this->testConfigPath,
160+
'--env=production'
161+
] );
162+
$input->parse( $this->command );
163+
164+
$output = new Output( false );
165+
166+
// Set up test input reader to confirm
167+
$inputReader = new TestInputReader();
168+
$inputReader->addResponse( 'yes' );
169+
170+
$this->command->setInput( $input );
171+
$this->command->setOutput( $output );
172+
$this->command->setInputReader( $inputReader );
173+
174+
// Capture output
175+
ob_start();
176+
$result = $this->command->execute();
177+
$outputContent = ob_get_clean();
178+
179+
// Should succeed and show secrets
180+
$this->assertEquals( 0, $result );
181+
$this->assertStringContainsString( 'You are about to display production secrets!', $outputContent );
182+
$this->assertStringContainsString( 'production_secret', $outputContent );
183+
184+
// Test 2: User cancels - secrets should NOT be shown
185+
$input2 = new Input( [
186+
'--config=' . $this->testConfigPath,
187+
'--env=production'
188+
] );
189+
$input2->parse( $this->command );
190+
191+
$output2 = new Output( false );
192+
193+
// Set up test input reader to cancel
194+
$inputReader2 = new TestInputReader();
195+
$inputReader2->addResponse( 'no' );
196+
197+
$this->command->setInput( $input2 );
198+
$this->command->setOutput( $output2 );
199+
$this->command->setInputReader( $inputReader2 );
200+
201+
// Capture output
202+
ob_start();
203+
$result2 = $this->command->execute();
204+
$outputContent2 = ob_get_clean();
205+
206+
// Should exit gracefully without showing secrets
207+
$this->assertEquals( 0, $result2 );
208+
$this->assertStringContainsString( 'Operation cancelled.', $outputContent2 );
209+
$this->assertStringNotContainsString( 'production_secret', $outputContent2 );
210+
211+
// Test 3: Force flag should skip confirmation
212+
$input3 = new Input( [
213+
'--config=' . $this->testConfigPath,
214+
'--env=production',
215+
'--force'
216+
] );
217+
$input3->parse( $this->command );
218+
219+
$output3 = new Output( false );
220+
221+
$this->command->setInput( $input3 );
222+
$this->command->setOutput( $output3 );
223+
// No input reader needed - force skips confirmation
224+
225+
// Capture output
226+
ob_start();
227+
$result3 = $this->command->execute();
228+
$outputContent3 = ob_get_clean();
229+
230+
// Should succeed without confirmation prompt
231+
$this->assertEquals( 0, $result3 );
232+
$this->assertStringNotContainsString( 'You are about to display production secrets!', $outputContent3 );
233+
$this->assertStringContainsString( 'production_secret', $outputContent3 );
234+
}
235+
138236
/**
139237
* Test error when secrets file not found
140238
*/

0 commit comments

Comments
 (0)