mirror of
https://github.com/ivuorinen/a.git
synced 2026-01-26 03:24:07 +00:00
260 lines
8.8 KiB
Go
260 lines
8.8 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/spf13/cobra"
|
|
"github.com/stretchr/testify/assert"
|
|
"gopkg.in/yaml.v2"
|
|
|
|
"github.com/ivuorinen/a/cmd"
|
|
)
|
|
|
|
func TestInitConfigPaths(t *testing.T) {
|
|
paths, err := cmd.InitConfigPaths()
|
|
assert.NoError(t, err, "initializing config paths should not produce an error")
|
|
|
|
assert.DirExists(t, paths.ConfigDir, "config directory should exist")
|
|
assert.FileExists(t, paths.ConfigFile, "config file path should exist")
|
|
assert.DirExists(t, paths.CacheDir, "cache directory should exist")
|
|
}
|
|
|
|
func TestLoadAndSaveConfig(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
cfgFile := filepath.Join(tempDir, "config.yaml")
|
|
|
|
cfg := &cmd.Config{
|
|
SSHKeyPath: "/tmp/id_rsa",
|
|
GitHubUser: "testuser",
|
|
DefaultRecipients: []string{"/tmp/key.pub"},
|
|
CacheTTLMinutes: 60,
|
|
LogFilePath: "/tmp/test.log",
|
|
}
|
|
|
|
err := cmd.SaveConfig(cfgFile, cfg)
|
|
assert.NoError(t, err, "saving config should not produce an error")
|
|
|
|
loadedCfg, err := cmd.LoadConfig(cfgFile)
|
|
assert.NoError(t, err, "loading config should not produce an error")
|
|
assert.Equal(t, cfg, loadedCfg, "loaded config should match saved config")
|
|
}
|
|
|
|
func TestDefaultLogFilePath(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
cfgFile := filepath.Join(tempDir, "config.yaml")
|
|
|
|
cfg := &cmd.Config{
|
|
SSHKeyPath: "/tmp/id_rsa",
|
|
GitHubUser: "testuser",
|
|
DefaultRecipients: []string{"/tmp/key.pub"},
|
|
CacheTTLMinutes: 60,
|
|
}
|
|
|
|
data, err := yaml.Marshal(cfg)
|
|
assert.NoError(t, err, "marshaling config should not produce an error")
|
|
assert.NoError(t, os.WriteFile(cfgFile, data, 0o600))
|
|
|
|
loadedCfg, err := cmd.LoadConfig(cfgFile)
|
|
assert.NoError(t, err, "loading config should not produce an error")
|
|
assert.NotEmpty(t, loadedCfg.LogFilePath, "default log file path should be set")
|
|
}
|
|
|
|
func TestSetupLogging(t *testing.T) {
|
|
tempLogFile := filepath.Join(t.TempDir(), "cli.log")
|
|
cfg := &cmd.Config{LogFilePath: tempLogFile}
|
|
|
|
log := logrus.New()
|
|
log.SetFormatter(&logrus.JSONFormatter{})
|
|
logFile, err := os.OpenFile(cfg.LogFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o600)
|
|
assert.NoError(t, err, "opening log file should not produce an error")
|
|
log.SetOutput(logFile)
|
|
log.SetLevel(logrus.InfoLevel)
|
|
|
|
log.Info("Test log entry")
|
|
assert.FileExists(t, tempLogFile, "log file should exist after setup")
|
|
}
|
|
|
|
func TestCmdConfig(t *testing.T) {
|
|
cfg := &cmd.Config{}
|
|
cmdObj := cmd.ConfigCmd(cfg, func(_ any) error { return nil })
|
|
assert.NotNil(t, cmdObj, "ConfigCmd should return a non-nil cobra command")
|
|
|
|
flags := cmdObj.Flags()
|
|
sshKey, _ := flags.GetString("ssh-key")
|
|
assert.Empty(t, sshKey, "default ssh-key flag should be empty")
|
|
}
|
|
|
|
func TestCmdEncryptPlaceholder(t *testing.T) {
|
|
cfg := &cmd.Config{}
|
|
log := logrus.New()
|
|
cmdObj := cmd.Encrypt(cfg, log)
|
|
assert.NotNil(t, cmdObj, "Encrypt should return a non-nil cobra command")
|
|
}
|
|
|
|
func TestCmdDecryptPlaceholder(t *testing.T) {
|
|
cfg := &cmd.Config{}
|
|
log := logrus.New()
|
|
cmdObj := cmd.Decrypt(cfg, log)
|
|
assert.NotNil(t, cmdObj, "Decrypt should return a non-nil cobra command")
|
|
}
|
|
|
|
func TestCmdCompletion(t *testing.T) {
|
|
rootCmd := &cobra.Command{Use: "a"}
|
|
cmdObj := cmd.Completion(rootCmd)
|
|
assert.NotNil(t, cmdObj, "Completion should return a non-nil cobra command")
|
|
}
|
|
|
|
// Helper to generate a temporary SSH keypair for testing
|
|
func generateSSHKeyPair(dir string) (privKey, pubKey string, err error) {
|
|
privKey = filepath.Join(dir, "id_rsa")
|
|
pubKey = privKey + ".pub"
|
|
cmd := exec.Command("ssh-keygen", "-t", "rsa", "-b", "2048", "-N", "", "-f", privKey)
|
|
if err := cmd.Run(); err != nil {
|
|
return "", "", err
|
|
}
|
|
return privKey, pubKey, nil
|
|
}
|
|
|
|
// Helper to write test results to a file
|
|
func writeTestResult(dir, name string, content []byte) {
|
|
_ = os.WriteFile(filepath.Join(dir, name), content, 0o600)
|
|
}
|
|
|
|
func TestEncryptDecrypt_Success(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
plaintext := []byte("This is a secret message for encryption test.")
|
|
|
|
// Generate SSH keypair
|
|
privKey, pubKey, err := generateSSHKeyPair(tempDir)
|
|
writeTestResult(
|
|
tempDir,
|
|
"sshkeygen_success.txt",
|
|
fmt.Appendf(nil, "priv: %s\npub: %s\nerr: %v", privKey, pubKey, err),
|
|
)
|
|
assert.NoError(t, err, "ssh-keygen should succeed")
|
|
|
|
// Write plaintext file
|
|
inputFile := filepath.Join(tempDir, "input.txt")
|
|
assert.NoError(t, os.WriteFile(inputFile, plaintext, 0o600))
|
|
|
|
// Prepare config
|
|
cfg := &cmd.Config{
|
|
DefaultRecipients: []string{pubKey},
|
|
LogFilePath: filepath.Join(tempDir, "cli.log"),
|
|
}
|
|
log := logrus.New()
|
|
|
|
// Encrypt
|
|
encryptedFile := filepath.Join(tempDir, "encrypted.txt")
|
|
encryptCmd := cmd.Encrypt(cfg, log)
|
|
err = encryptCmd.Flags().Set("input", inputFile)
|
|
assert.NoError(t, err)
|
|
err = encryptCmd.Flags().Set("output", encryptedFile)
|
|
assert.NoError(t, err)
|
|
err = encryptCmd.RunE(encryptCmd, []string{})
|
|
writeTestResult(tempDir, "encrypt_result.txt", fmt.Appendf(nil, "err: %v", err))
|
|
assert.NoError(t, err)
|
|
assert.FileExists(t, encryptedFile, "encrypted file should exist")
|
|
|
|
// Decrypt
|
|
decryptCfg := &cmd.Config{SSHKeyPath: privKey, LogFilePath: cfg.LogFilePath}
|
|
decryptedFile := filepath.Join(tempDir, "decrypted.txt")
|
|
decryptCmd := cmd.Decrypt(decryptCfg, log)
|
|
err = decryptCmd.Flags().Set("input", encryptedFile)
|
|
assert.NoError(t, err)
|
|
err = decryptCmd.Flags().Set("output", decryptedFile)
|
|
assert.NoError(t, err)
|
|
err = decryptCmd.RunE(decryptCmd, []string{})
|
|
writeTestResult(tempDir, "decrypt_result.txt", fmt.Appendf(nil, "err: %v", err))
|
|
assert.NoError(t, err)
|
|
assert.FileExists(t, decryptedFile, "decrypted file should exist")
|
|
|
|
// Compare output (decryptedFile is generated by the test and not user-controlled)
|
|
// Ensure decryptedFile exists and is in tempDir before reading (gosec G304 mitigation)
|
|
info, statErr := os.Stat(decryptedFile)
|
|
assert.NoError(t, statErr, "decrypted file should exist before reading")
|
|
assert.True(t, strings.HasPrefix(decryptedFile, tempDir), "decrypted file must be in tempDir")
|
|
assert.Equal(t, info.Mode().Perm(), os.FileMode(0o600), "decrypted file must have 0600 permissions")
|
|
|
|
// #nosec G304 -- decryptedFile is generated in tempDir and not user-controlled
|
|
decrypted, err := os.ReadFile(decryptedFile)
|
|
writeTestResult(tempDir, "decrypted.txt", decrypted)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, plaintext, decrypted, "decrypted output should match original plaintext")
|
|
}
|
|
|
|
func TestEncryptDecrypt_WrongKey(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
plaintext := []byte("Secret message for wrong key test.")
|
|
|
|
// Generate two SSH keypairs
|
|
_, pubKey1, err := generateSSHKeyPair(tempDir)
|
|
assert.NoError(t, err)
|
|
privKey2, _, err := generateSSHKeyPair(tempDir)
|
|
assert.NoError(t, err)
|
|
|
|
// Write plaintext file
|
|
inputFile := filepath.Join(tempDir, "input.txt")
|
|
assert.NoError(t, os.WriteFile(inputFile, plaintext, 0o600))
|
|
|
|
// Encrypt with pubKey1
|
|
cfg := &cmd.Config{
|
|
DefaultRecipients: []string{pubKey1},
|
|
LogFilePath: filepath.Join(tempDir, "cli.log"),
|
|
}
|
|
log := logrus.New()
|
|
encryptedFile := filepath.Join(tempDir, "encrypted.txt")
|
|
encryptCmd := cmd.Encrypt(cfg, log)
|
|
err = encryptCmd.Flags().Set("input", inputFile)
|
|
assert.NoError(t, err)
|
|
err = encryptCmd.Flags().Set("output", encryptedFile)
|
|
assert.NoError(t, err)
|
|
err = encryptCmd.RunE(encryptCmd, []string{})
|
|
writeTestResult(tempDir, "encrypt_wrongkey_result.txt", fmt.Appendf(nil, "err: %v", err))
|
|
assert.NoError(t, err)
|
|
assert.FileExists(t, encryptedFile, "encrypted file should exist")
|
|
|
|
// Try to decrypt with privKey2 (should fail)
|
|
decryptCfg := &cmd.Config{SSHKeyPath: privKey2, LogFilePath: cfg.LogFilePath}
|
|
decryptedFile := filepath.Join(tempDir, "decrypted_wrongkey.txt")
|
|
decryptCmd := cmd.Decrypt(decryptCfg, log)
|
|
err = decryptCmd.Flags().Set("input", encryptedFile)
|
|
assert.NoError(t, err)
|
|
err = decryptCmd.Flags().Set("output", decryptedFile)
|
|
assert.NoError(t, err)
|
|
err = decryptCmd.RunE(decryptCmd, []string{})
|
|
writeTestResult(tempDir, "decrypt_wrongkey_result.txt", fmt.Appendf(nil, "err: %v", err))
|
|
assert.Error(t, err, "decryption should fail with wrong key")
|
|
}
|
|
|
|
func TestEncryptDecrypt_MissingRecipient(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
plaintext := []byte("Secret message for missing recipient test.")
|
|
|
|
// Write plaintext file
|
|
inputFile := filepath.Join(tempDir, "input.txt")
|
|
assert.NoError(t, os.WriteFile(inputFile, plaintext, 0o600))
|
|
|
|
// Encrypt with no recipient
|
|
cfg := &cmd.Config{
|
|
DefaultRecipients: []string{},
|
|
LogFilePath: filepath.Join(tempDir, "cli.log"),
|
|
}
|
|
log := logrus.New()
|
|
encryptedFile := filepath.Join(tempDir, "encrypted.txt")
|
|
encryptCmd := cmd.Encrypt(cfg, log)
|
|
err := encryptCmd.Flags().Set("input", inputFile)
|
|
assert.NoError(t, err)
|
|
err = encryptCmd.Flags().Set("output", encryptedFile)
|
|
assert.NoError(t, err)
|
|
err = encryptCmd.RunE(encryptCmd, []string{})
|
|
writeTestResult(tempDir, "encrypt_missingrecipient_result.txt", fmt.Appendf(nil, "err: %v", err))
|
|
assert.Error(t, err, "encryption should fail with no recipient")
|
|
}
|