Files
keyfunc/internal/mnemonic/mnemonic_test.go
sneak 90a7c96cd1
All checks were successful
check / check (push) Successful in 34s
Skeleton, mnemonic input, derivation, and the ssh commands (closes #1)
The tool derives ed25519 SSH keys from a BIP-39 mnemonic and prints
them, either as an authorized_keys line or as an unencrypted OpenSSH
private key. Both README test vectors are asserted in the tests.

The mnemonic is looked for in the order the README gives, and refused
when it fails its checksum or when there is nowhere left to look.

The repository standards come with it: the vendored linter
configuration and policies, the script/ entrypoints with a thin
Makefile, and a Gitea workflow. Linting happens only inside the image
built from Dockerfile.lint, which pins the linter by hash, so the root
Dockerfile runs the formatting check, the tests and the build, and
script/cibuild runs the linter before it.

Model: opus-5
2026-09-07 14:44:45 +00:00

88 lines
2.5 KiB
Go

package mnemonic_test
import (
"strings"
"testing"
"git.eeqj.de/sneak/keyfunc/internal/mnemonic"
"github.com/stretchr/testify/require"
)
// Two more mnemonics that pass the checksum, so a test can tell which
// source an answer came from.
const (
fromCommandVariable = "legal winner thank year wave sausage worth " +
"useful legal winner thank yellow"
fromVariable = "letter advice cage absurd amount doctor acoustic " +
"avoid letter advice cage above"
)
// example returns the mnemonic every BIP-39 document uses to show its
// test vectors: eleven abandons and about.
func example() string {
return strings.Repeat("abandon ", 11) + "about"
}
// broken returns a mnemonic whose last word does not match the rest.
func broken() string {
return strings.TrimSpace(strings.Repeat("abandon ", 12))
}
// prints builds a shell command that writes the given words padded
// with spaces, so the test also shows that the padding is dropped.
func prints(words string) string {
return "printf ' %s \\n' '" + words + "'"
}
func TestTheCommandOnTheCommandLineWins(t *testing.T) {
t.Setenv(mnemonic.CommandVariable, prints(fromCommandVariable))
t.Setenv(mnemonic.Variable, fromVariable)
words, err := mnemonic.Read(t.Context(), prints(example()))
require.NoError(t, err)
require.Equal(t, example(), words)
}
func TestTheCommandInTheEnvironmentComesNext(t *testing.T) {
t.Setenv(mnemonic.CommandVariable, prints(fromCommandVariable))
t.Setenv(mnemonic.Variable, fromVariable)
words, err := mnemonic.Read(t.Context(), "")
require.NoError(t, err)
require.Equal(t, fromCommandVariable, words)
}
func TestTheMnemonicInTheEnvironmentComesLast(t *testing.T) {
t.Setenv(mnemonic.CommandVariable, "")
t.Setenv(mnemonic.Variable, " "+fromVariable+" ")
words, err := mnemonic.Read(t.Context(), "")
require.NoError(t, err)
require.Equal(t, fromVariable, words)
}
func TestAFailingCommandSaysWhatWentWrong(t *testing.T) {
t.Setenv(mnemonic.CommandVariable, "")
t.Setenv(mnemonic.Variable, "")
_, err := mnemonic.Read(t.Context(), "echo nothing here >&2; exit 3")
require.Error(t, err)
require.Contains(t, err.Error(), "nothing here")
}
func TestABadChecksumIsRefused(t *testing.T) {
t.Setenv(mnemonic.CommandVariable, "")
t.Setenv(mnemonic.Variable, broken())
_, err := mnemonic.Read(t.Context(), "")
require.ErrorIs(t, err, mnemonic.ErrChecksum)
}
func TestNothingToReadAndNobodyToAsk(t *testing.T) {
t.Setenv(mnemonic.CommandVariable, "")
t.Setenv(mnemonic.Variable, "")
_, err := mnemonic.Read(t.Context(), "")
require.ErrorIs(t, err, mnemonic.ErrMissing)
}