Skip to content

Commit

Permalink
test: optimize test cases of env commands
Browse files Browse the repository at this point in the history
  • Loading branch information
hwbrzzl committed Feb 21, 2025
1 parent f902299 commit 9676e7a
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 48 deletions.
22 changes: 16 additions & 6 deletions foundation/console/env_decrypt_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"github.com/goravel/framework/contracts/console"
"github.com/goravel/framework/contracts/console/command"
"github.com/goravel/framework/errors"
"github.com/goravel/framework/support/convert"
"github.com/goravel/framework/support/file"
)

type EnvDecryptCommand struct {
Expand Down Expand Up @@ -46,39 +48,41 @@ func (r *EnvDecryptCommand) Extend() command.Extend {

// Handle Execute the console command.
func (r *EnvDecryptCommand) Handle(ctx console.Context) error {
key := ctx.Option("key")
if key == "" {
key = os.Getenv("GORAVEL_ENV_ENCRYPTION_KEY")
}
key := convert.Default(ctx.Option("key"), os.Getenv("GORAVEL_ENV_ENCRYPTION_KEY"))
if key == "" {
ctx.Error("A decryption key is required.")
return nil
}

ciphertext, err := os.ReadFile(".env.encrypted")
if err != nil {
ctx.Error("Encrypted environment file not found.")
return nil
}
if _, err = os.Stat(".env"); err == nil {

if file.Exists(".env") {
ok, _ := ctx.Confirm("Environment file already exists, are you sure to overwrite?", console.ConfirmOption{
Default: true,
Default: false,
Affirmative: "Yes",
Negative: "No",
})
if !ok {
return nil
}
}

plaintext, err := r.decrypt(ciphertext, []byte(key))
if err != nil {
ctx.Error(fmt.Sprintf("Decrypt error: %v", err))
return nil
}

err = os.WriteFile(".env", plaintext, 0644)
if err != nil {
ctx.Error(fmt.Sprintf("Writer error: %v", err))
return nil
}

ctx.Success("Encrypted environment successfully decrypted.")
return nil
}
Expand All @@ -88,18 +92,22 @@ func (r *EnvDecryptCommand) decrypt(ciphertext []byte, key []byte) ([]byte, erro
if err != nil {
return nil, err
}

iv := ciphertext[:aes.BlockSize]
ciphertext = ciphertext[aes.BlockSize:]
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}

if len(ciphertext)%aes.BlockSize != 0 {
return nil, errors.AesCiphertextInvalid
}

mode := cipher.NewCBCDecrypter(block, iv)
plaintext := make([]byte, len(ciphertext))
mode.CryptBlocks(plaintext, ciphertext)

return r.pkcs7Unpad(plaintext)
}

Expand All @@ -108,9 +116,11 @@ func (r *EnvDecryptCommand) pkcs7Unpad(data []byte) ([]byte, error) {
if length == 0 {
return nil, errors.AesCiphertextInvalid
}

padding := int(data[length-1])
if padding < 1 || length < padding {
return nil, errors.AesPaddingInvalid
}

return data[:length-padding], nil
}
85 changes: 69 additions & 16 deletions foundation/console/env_decrypt_command_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package console

import (
"os"
"reflect"
"testing"

Expand All @@ -27,11 +26,9 @@ func TestEnvDecryptCommandTestSuite(t *testing.T) {
}

func (s *EnvDecryptCommandTestSuite) SetupSuite() {
s.Nil(file.PutContent(".env.encrypted", EnvDecryptCiphertext))
}

func (s *EnvDecryptCommandTestSuite) TearDownSuite() {
s.Nil(file.Remove(".env.encrypted"))
}

func (s *EnvDecryptCommandTestSuite) TestSignature() {
Expand Down Expand Up @@ -84,30 +81,85 @@ func (s *EnvDecryptCommandTestSuite) TestHandle() {
envDecryptCommand := NewEnvDecryptCommand()
mockContext := mocksconsole.NewContext(s.T())

if env, err := os.ReadFile(".env"); err == nil {
s.Run("empty key", func() {
mockContext.EXPECT().Option("key").Return("").Once()
mockContext.EXPECT().Error("A decryption key is required.").Once()
s.Nil(envDecryptCommand.Handle(mockContext))
})

s.Run("invalid key", func() {
s.Nil(file.PutContent(".env.encrypted", EnvDecryptCiphertext))
defer func() {
s.Nil(file.Remove(".env.encrypted"))
}()

mockContext.EXPECT().Option("key").Return(EnvDecryptInvalidKey).Once()
mockContext.EXPECT().Error("Decrypt error: crypto/aes: invalid key size 4").Once()

s.Nil(envDecryptCommand.Handle(mockContext))
})

s.Run(".env.encrypted is not found", func() {
mockContext.EXPECT().Option("key").Return(EnvDecryptValidKey).Once()
mockContext.EXPECT().Error("Encrypted environment file not found.").Once()
s.Nil(envDecryptCommand.Handle(mockContext))
})

s.Run(".env exists and confirm failed", func() {
s.Nil(file.PutContent(".env.encrypted", EnvDecryptCiphertext))
s.Nil(file.PutContent(".env", EnvDecryptPlaintext))
defer func() {
s.Nil(file.Remove(".env.encrypted"))
s.Nil(file.Remove(".env"))
}()

mockContext.EXPECT().Option("key").Return(EnvDecryptValidKey).Once()
mockContext.EXPECT().Confirm("Environment file already exists, are you sure to overwrite?", console.ConfirmOption{
Default: true,
Default: false,
Affirmative: "Yes",
Negative: "No",
}).Return(true, nil).Once()
s.Require().Equal(EnvDecryptPlaintext, string(env))
}

s.Run("valid key", func() {
mockContext.EXPECT().Option("key").Return(EnvDecryptValidKey).Once()
mockContext.EXPECT().Success("Encrypted environment successfully decrypted.").Once()
}).Return(false, nil).Once()
s.Nil(envDecryptCommand.Handle(mockContext))
})

s.Run("invalid key", func() {
mockContext.EXPECT().Option("key").Return(EnvDecryptInvalidKey).Once()
s.Run("success when .env exists", func() {
s.Nil(file.PutContent(".env.encrypted", EnvDecryptCiphertext))
s.Nil(file.PutContent(".env", EnvDecryptPlaintext))
defer func() {
s.Nil(file.Remove(".env"))
s.Nil(file.Remove(".env.encrypted"))
}()

mockContext.EXPECT().Option("key").Return(EnvDecryptValidKey).Once()
mockContext.EXPECT().Confirm("Environment file already exists, are you sure to overwrite?", console.ConfirmOption{
Default: true,
Default: false,
Affirmative: "Yes",
Negative: "No",
}).Return(true, nil).Once()
mockContext.EXPECT().Error("Decrypt error: crypto/aes: invalid key size 4").Once()
mockContext.EXPECT().Success("Encrypted environment successfully decrypted.").Once()

s.Nil(envDecryptCommand.Handle(mockContext))
s.True(file.Exists(".env"))
content, err := file.GetContent(".env")
s.Nil(err)
s.Equal(EnvDecryptPlaintext, content)
})

s.Run("success when .env not exists", func() {
s.Nil(file.PutContent(".env.encrypted", EnvDecryptCiphertext))
defer func() {
s.Nil(file.Remove(".env.encrypted"))
s.Nil(file.Remove(".env"))
}()

mockContext.EXPECT().Option("key").Return(EnvDecryptValidKey).Once()
mockContext.EXPECT().Success("Encrypted environment successfully decrypted.").Once()

s.Nil(envDecryptCommand.Handle(mockContext))
s.True(file.Exists(".env"))
content, err := file.GetContent(".env")
s.Nil(err)
s.Equal(EnvDecryptPlaintext, content)
})
}

Expand All @@ -119,6 +171,7 @@ func (s *EnvDecryptCommandTestSuite) TestDecrypt() {
s.Equal(EnvDecryptPlaintext, string(decrypted))
s.Nil(err)
})

s.Run("invalid key", func() {
_, err := envDecryptCommand.decrypt([]byte(EnvDecryptCiphertext), []byte(EnvDecryptInvalidKey))
s.Error(err)
Expand Down
16 changes: 10 additions & 6 deletions foundation/console/env_encrypt_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (

"github.com/goravel/framework/contracts/console"
"github.com/goravel/framework/contracts/console/command"
"github.com/goravel/framework/support/convert"
"github.com/goravel/framework/support/file"
"github.com/goravel/framework/support/str"
)

Expand Down Expand Up @@ -47,30 +49,29 @@ func (r *EnvEncryptCommand) Extend() command.Extend {

// Handle Execute the console command.
func (r *EnvEncryptCommand) Handle(ctx console.Context) error {
key := ctx.Option("key")
if key == "" {
key = str.Random(32)
}
key := convert.Default(ctx.Option("key"), str.Random(32))
plaintext, err := os.ReadFile(".env")
if err != nil {
ctx.Error("Environment file not found.")
return nil
}
if _, err = os.Stat(".env.encrypted"); err == nil {
if file.Exists(".env.encrypted") {
ok, _ := ctx.Confirm("Encrypted environment file already exists, are you sure to overwrite?", console.ConfirmOption{
Default: true,
Default: false,
Affirmative: "Yes",
Negative: "No",
})
if !ok {
return nil
}
}

ciphertext, err := r.encrypt(plaintext, []byte(key))
if err != nil {
ctx.Error(fmt.Sprintf("Encrypt error: %v", err))
return nil
}

base64Data := base64.StdEncoding.EncodeToString(ciphertext)
err = os.WriteFile(".env.encrypted", []byte(base64Data), 0644)
if err != nil {
Expand All @@ -91,16 +92,19 @@ func (r *EnvEncryptCommand) encrypt(plaintext []byte, key []byte) ([]byte, error
if err != nil {
return nil, err
}

iv := key[:aes.BlockSize]
plaintext = r.pkcs7Pad(plaintext)
mode := cipher.NewCBCEncrypter(block, iv)
ciphertext := make([]byte, len(plaintext))
mode.CryptBlocks(ciphertext, plaintext)

return append(iv, ciphertext...), nil
}

func (r *EnvEncryptCommand) pkcs7Pad(data []byte) []byte {
padding := aes.BlockSize - len(data)%aes.BlockSize
padText := bytes.Repeat([]byte{byte(padding)}, padding)

return append(data, padText...)
}
Loading

0 comments on commit 9676e7a

Please sign in to comment.