The age commands: pub, priv, encrypt and decrypt (closes #3)
All checks were successful
check / check (push) Successful in 28s
All checks were successful
check / check (push) Successful in 28s
keyfunc age derives an age identity at the generic path the way secret's agehd does, prints the recipient or the identity, and encrypts to or decrypts with it, the derived recipient always among encrypt's recipients. Two review rounds; the second passed with no findings, the clamping step now pinned by a fixed identity test. Model: opus-5 (implementation and review); fable-5-1 (landing)
This commit was merged in pull request #7.
This commit is contained in:
102
internal/cli/age_test.go
Normal file
102
internal/cli/age_test.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package cli_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.eeqj.de/sneak/keyfunc/internal/agekey"
|
||||
"git.eeqj.de/sneak/keyfunc/internal/mnemonic"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestTheAgeCommandsPrintTheKey(t *testing.T) {
|
||||
t.Setenv(mnemonic.Variable, example())
|
||||
|
||||
recipient := strings.TrimSpace(run(t, "age", "pub"))
|
||||
require.True(t, strings.HasPrefix(recipient, "age1"))
|
||||
|
||||
identity := strings.TrimSpace(run(t, "age", "priv"))
|
||||
require.True(t, strings.HasPrefix(identity, "AGE-SECRET-KEY-1"))
|
||||
}
|
||||
|
||||
func TestAFileEncryptedByTheToolIsReadBackByIt(t *testing.T) {
|
||||
t.Setenv(mnemonic.Variable, example())
|
||||
|
||||
plain := written(t, "notes.txt", "the secret\n")
|
||||
sealed := filepath.Join(t.TempDir(), "notes.age")
|
||||
|
||||
run(t, "age", "encrypt", "-o", sealed, plain)
|
||||
require.Equal(t, "the secret\n", run(t, "age", "decrypt", sealed))
|
||||
}
|
||||
|
||||
func TestTheArmoredFormIsTextThatDecrypts(t *testing.T) {
|
||||
t.Setenv(mnemonic.Variable, example())
|
||||
|
||||
plain := written(t, "notes.txt", "the secret\n")
|
||||
|
||||
armored := run(t, "age", "encrypt", "--armor", plain)
|
||||
require.True(t, strings.HasPrefix(
|
||||
armored, "-----BEGIN AGE ENCRYPTED FILE-----",
|
||||
))
|
||||
|
||||
sealed := written(t, "notes.age", armored)
|
||||
require.Equal(t, "the secret\n", run(t, "age", "decrypt", sealed))
|
||||
}
|
||||
|
||||
func TestAnotherRecipientIsAddedAndTheDerivedOneStays(t *testing.T) {
|
||||
t.Setenv(mnemonic.Variable, example())
|
||||
|
||||
theirs := strings.TrimSpace(run(t, "age", "pub", "-n", "7"))
|
||||
plain := written(t, "notes.txt", "the secret\n")
|
||||
sealed := filepath.Join(t.TempDir(), "notes.age")
|
||||
|
||||
run(t, "age", "encrypt", "--to", theirs, "-o", sealed, plain)
|
||||
|
||||
require.Equal(t, "the secret\n", run(t, "age", "decrypt", sealed))
|
||||
require.Equal(t,
|
||||
"the secret\n", run(t, "age", "decrypt", "-n", "7", sealed),
|
||||
)
|
||||
}
|
||||
|
||||
func TestAFileForAnotherKeyIsRefused(t *testing.T) {
|
||||
t.Setenv(mnemonic.Variable, example())
|
||||
|
||||
plain := written(t, "notes.txt", "the secret\n")
|
||||
sealed := filepath.Join(t.TempDir(), "notes.age")
|
||||
|
||||
run(t, "age", "encrypt", "-n", "7", "-o", sealed, plain)
|
||||
|
||||
_, err := execute(t, "age", "decrypt", sealed)
|
||||
require.ErrorIs(t, err, agekey.ErrNotRecipient)
|
||||
}
|
||||
|
||||
func TestARefusedDecryptionLeavesTheOutputFileAlone(t *testing.T) {
|
||||
t.Setenv(mnemonic.Variable, example())
|
||||
|
||||
plain := written(t, "notes.txt", "the secret\n")
|
||||
sealed := filepath.Join(t.TempDir(), "notes.age")
|
||||
existing := written(t, "notes.out", "what was already there\n")
|
||||
|
||||
run(t, "age", "encrypt", "-n", "7", "-o", sealed, plain)
|
||||
|
||||
_, err := execute(t, "age", "decrypt", "-o", existing, sealed)
|
||||
require.ErrorIs(t, err, agekey.ErrNotRecipient)
|
||||
|
||||
//nolint:gosec // the test made this path itself
|
||||
kept, err := os.ReadFile(existing)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "what was already there\n", string(kept))
|
||||
}
|
||||
|
||||
// written puts the contents in a file of that name in a directory of
|
||||
// this test's own and returns the path to it.
|
||||
func written(t *testing.T, name, contents string) string {
|
||||
t.Helper()
|
||||
|
||||
path := filepath.Join(t.TempDir(), name)
|
||||
require.NoError(t, os.WriteFile(path, []byte(contents), 0o600))
|
||||
|
||||
return path
|
||||
}
|
||||
Reference in New Issue
Block a user