forked from sneak/secret
Replace all os.Setenv calls with t.Setenv in test functions to ensure proper test environment cleanup and better test isolation. This leaves only legitimate application code and helper functions using os.Setenv.
183 lines
5.6 KiB
Go
183 lines
5.6 KiB
Go
package secret_test
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"filippo.io/age"
|
|
"git.eeqj.de/sneak/secret/internal/secret"
|
|
"git.eeqj.de/sneak/secret/pkg/agehd"
|
|
"github.com/spf13/afero"
|
|
)
|
|
|
|
func TestPassphraseUnlockerWithRealFS(t *testing.T) {
|
|
// This test uses real filesystem
|
|
if os.Getenv("CI") == "true" {
|
|
t.Log("Running in CI environment with real filesystem")
|
|
}
|
|
|
|
// Create a temporary directory for our tests
|
|
tempDir, err := os.MkdirTemp("", "secret-passphrase-test-")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create temp dir: %v", err)
|
|
}
|
|
defer os.RemoveAll(tempDir) // Clean up after test
|
|
|
|
// Use the real filesystem
|
|
fs := afero.NewOsFs()
|
|
|
|
// Test data
|
|
testMnemonic := "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
|
testPassphrase := "test-passphrase-123"
|
|
|
|
// Create the directory structure
|
|
unlockerDir := filepath.Join(tempDir, "unlocker")
|
|
if err := os.MkdirAll(unlockerDir, secret.DirPerms); err != nil {
|
|
t.Fatalf("Failed to create unlocker directory: %v", err)
|
|
}
|
|
|
|
// Set up test metadata
|
|
metadata := secret.UnlockerMetadata{
|
|
Type: "passphrase",
|
|
CreatedAt: time.Now(),
|
|
Flags: []string{},
|
|
}
|
|
|
|
// Create passphrase unlocker
|
|
unlocker := secret.NewPassphraseUnlocker(fs, unlockerDir, metadata)
|
|
|
|
// Generate a test age identity
|
|
ageIdentity, err := age.GenerateX25519Identity()
|
|
if err != nil {
|
|
t.Fatalf("Failed to generate age identity: %v", err)
|
|
}
|
|
agePrivateKey := ageIdentity.String()
|
|
agePublicKey := ageIdentity.Recipient().String()
|
|
|
|
// Test writing public key
|
|
t.Run("WritePublicKey", func(t *testing.T) {
|
|
pubKeyPath := filepath.Join(unlockerDir, "pub.age")
|
|
if err := afero.WriteFile(fs, pubKeyPath, []byte(agePublicKey), secret.FilePerms); err != nil {
|
|
t.Fatalf("Failed to write public key: %v", err)
|
|
}
|
|
|
|
// Verify the file exists
|
|
exists, err := afero.Exists(fs, pubKeyPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to check if public key exists: %v", err)
|
|
}
|
|
if !exists {
|
|
t.Errorf("Public key file should exist at %s", pubKeyPath)
|
|
}
|
|
})
|
|
|
|
// Test encrypting private key with passphrase
|
|
t.Run("EncryptPrivateKey", func(t *testing.T) {
|
|
privKeyData := []byte(agePrivateKey)
|
|
encryptedPrivKey, err := secret.EncryptWithPassphrase(privKeyData, testPassphrase)
|
|
if err != nil {
|
|
t.Fatalf("Failed to encrypt private key: %v", err)
|
|
}
|
|
|
|
privKeyPath := filepath.Join(unlockerDir, "priv.age")
|
|
if err := afero.WriteFile(fs, privKeyPath, encryptedPrivKey, secret.FilePerms); err != nil {
|
|
t.Fatalf("Failed to write encrypted private key: %v", err)
|
|
}
|
|
|
|
// Verify the file exists
|
|
exists, err := afero.Exists(fs, privKeyPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to check if private key exists: %v", err)
|
|
}
|
|
if !exists {
|
|
t.Errorf("Encrypted private key file should exist at %s", privKeyPath)
|
|
}
|
|
})
|
|
|
|
// Test writing long-term key
|
|
t.Run("WriteLongTermKey", func(t *testing.T) {
|
|
// Derive a long-term identity from the test mnemonic
|
|
ltIdentity, err := agehd.DeriveIdentity(testMnemonic, 0)
|
|
if err != nil {
|
|
t.Fatalf("Failed to derive long-term identity: %v", err)
|
|
}
|
|
|
|
// Encrypt long-term private key to the unlocker's recipient
|
|
recipient, err := age.ParseX25519Recipient(agePublicKey)
|
|
if err != nil {
|
|
t.Fatalf("Failed to parse recipient: %v", err)
|
|
}
|
|
|
|
ltPrivKeyData := []byte(ltIdentity.String())
|
|
encryptedLtPrivKey, err := secret.EncryptToRecipient(ltPrivKeyData, recipient)
|
|
if err != nil {
|
|
t.Fatalf("Failed to encrypt long-term private key: %v", err)
|
|
}
|
|
|
|
ltPrivKeyPath := filepath.Join(unlockerDir, "longterm.age")
|
|
if err := afero.WriteFile(fs, ltPrivKeyPath, encryptedLtPrivKey, secret.FilePerms); err != nil {
|
|
t.Fatalf("Failed to write encrypted long-term private key: %v", err)
|
|
}
|
|
|
|
// Verify the file exists
|
|
exists, err := afero.Exists(fs, ltPrivKeyPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to check if long-term key exists: %v", err)
|
|
}
|
|
if !exists {
|
|
t.Errorf("Encrypted long-term key file should exist at %s", ltPrivKeyPath)
|
|
}
|
|
})
|
|
|
|
// Set test environment variable (cleaned up automatically)
|
|
t.Setenv(secret.EnvUnlockPassphrase, testPassphrase)
|
|
|
|
// Test getting identity from environment variable
|
|
t.Run("GetIdentityFromEnv", func(t *testing.T) {
|
|
identity, err := unlocker.GetIdentity()
|
|
if err != nil {
|
|
t.Fatalf("Failed to get identity from env: %v", err)
|
|
}
|
|
|
|
// Verify the identity matches what we expect
|
|
expectedPubKey := ageIdentity.Recipient().String()
|
|
actualPubKey := identity.Recipient().String()
|
|
if actualPubKey != expectedPubKey {
|
|
t.Errorf("Public key mismatch. Expected %s, got %s", expectedPubKey, actualPubKey)
|
|
}
|
|
})
|
|
|
|
// Unset the environment variable to test interactive prompt
|
|
os.Unsetenv(secret.EnvUnlockPassphrase)
|
|
|
|
// Test getting identity from prompt (this would require mocking the prompt)
|
|
// For real integration tests, we'd need to provide a way to mock the passphrase input
|
|
// Here we'll just verify the error is what we expect when no passphrase is available
|
|
t.Run("GetIdentityWithoutEnv", func(t *testing.T) {
|
|
// This should fail since we're not in an interactive terminal
|
|
_, err := unlocker.GetIdentity()
|
|
if err == nil {
|
|
t.Errorf("Should have failed to get identity without passphrase env var")
|
|
}
|
|
})
|
|
|
|
// Test removing the unlocker
|
|
t.Run("RemoveUnlocker", func(t *testing.T) {
|
|
err := unlocker.Remove()
|
|
if err != nil {
|
|
t.Fatalf("Failed to remove unlocker: %v", err)
|
|
}
|
|
|
|
// Verify the directory is gone
|
|
exists, err := afero.DirExists(fs, unlockerDir)
|
|
if err != nil {
|
|
t.Fatalf("Failed to check if unlocker directory exists: %v", err)
|
|
}
|
|
if exists {
|
|
t.Errorf("Unlocker directory should not exist after removal")
|
|
}
|
|
})
|
|
}
|