check / check (push) Successful in 54s
REPO_POLICIES.md sets the Go module root to sneak.berlin/go/<name>. go.mod, every import of the old path and the -X path in the Makefile LDFLAGS now use sneak.berlin/go/keyfunc; make fmt moved those imports to their new place in the sort order. The old path keeps no alias. The README gives the go install line next to the build from a clone and drops this issue from its TODO list. Model: opus-5-5
141 lines
3.5 KiB
Go
141 lines
3.5 KiB
Go
package cli_test
|
|
|
|
import (
|
|
"bytes"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
bip39 "github.com/tyler-smith/go-bip39"
|
|
"golang.org/x/crypto/ssh"
|
|
"sneak.berlin/go/keyfunc/internal/childmnemonic"
|
|
"sneak.berlin/go/keyfunc/internal/cli"
|
|
"sneak.berlin/go/keyfunc/internal/derive"
|
|
"sneak.berlin/go/keyfunc/internal/mnemonic"
|
|
)
|
|
|
|
// The two lines the README says the example mnemonic produces.
|
|
const (
|
|
vectorZero = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJZOtOczrc/7CQytc" +
|
|
"uFwt7s4r8KjkZWkwjLZWBaFKD+7"
|
|
vectorOne = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOEWY8+/gmHYVC4u0Y" +
|
|
"0I4FKs+eVUulTPHfk9VtXw1tMF"
|
|
)
|
|
|
|
// The two child mnemonic lengths the tests ask for.
|
|
const (
|
|
twelve = 12
|
|
twentyFour = 24
|
|
)
|
|
|
|
// example returns the mnemonic the README gives its test vectors for:
|
|
// eleven abandons and about.
|
|
func example() string {
|
|
return strings.Repeat("abandon ", 11) + "about"
|
|
}
|
|
|
|
func TestTheReadmeTestVectors(t *testing.T) {
|
|
t.Setenv(mnemonic.Variable, example())
|
|
|
|
require.Equal(t,
|
|
vectorZero+" keyfunc/ssh/0",
|
|
strings.TrimSpace(run(t, "ssh", "pub", "-n", "0")),
|
|
)
|
|
require.Equal(t,
|
|
vectorOne+" keyfunc/ssh/1",
|
|
strings.TrimSpace(run(t, "ssh", "pub", "-n", "1")),
|
|
)
|
|
}
|
|
|
|
func TestTheCommentCanBeChosen(t *testing.T) {
|
|
t.Setenv(mnemonic.Variable, example())
|
|
|
|
line := strings.TrimSpace(run(t, "ssh", "pub", "--comment", "mine"))
|
|
require.Equal(t, vectorZero+" mine", line)
|
|
}
|
|
|
|
func TestThePrivateKeyMatchesThePublicOne(t *testing.T) {
|
|
t.Setenv(mnemonic.Variable, example())
|
|
|
|
block := run(t, "ssh", "priv", "-n", "1")
|
|
require.True(t,
|
|
strings.HasPrefix(block, "-----BEGIN OPENSSH PRIVATE KEY-----"),
|
|
)
|
|
|
|
parsed, err := ssh.ParsePrivateKey([]byte(block))
|
|
require.NoError(t, err)
|
|
|
|
back := strings.TrimSpace(
|
|
string(ssh.MarshalAuthorizedKey(parsed.PublicKey())),
|
|
)
|
|
require.Equal(t, vectorOne, back)
|
|
}
|
|
|
|
func TestAnIndexWithNoHardenedChildIsRefused(t *testing.T) {
|
|
t.Setenv(mnemonic.Variable, example())
|
|
|
|
out, err := execute(t, "ssh", "pub", "-n", "2147483648")
|
|
require.ErrorIs(t, err, derive.ErrIndexTooLarge)
|
|
require.Empty(t, out)
|
|
}
|
|
|
|
func TestTheMnemonicCommandIsUsed(t *testing.T) {
|
|
t.Setenv(mnemonic.Variable, "")
|
|
|
|
line := strings.TrimSpace(run(t,
|
|
"ssh", "pub",
|
|
"--mnemonic-command", "printf '%s\\n' '"+example()+"'",
|
|
))
|
|
require.Equal(t, vectorZero+" keyfunc/ssh/0", line)
|
|
}
|
|
|
|
func TestAChildMnemonicIsPrinted(t *testing.T) {
|
|
t.Setenv(mnemonic.Variable, example())
|
|
|
|
short := strings.Fields(run(t, "mnemonic"))
|
|
require.Len(t, short, twelve)
|
|
require.True(t, bip39.IsMnemonicValid(strings.Join(short, " ")))
|
|
|
|
long := strings.Fields(run(t, "mnemonic", "--words", "24"))
|
|
require.Len(t, long, twentyFour)
|
|
|
|
next := strings.Fields(run(t, "mnemonic", "-n", "1"))
|
|
require.NotEqual(t, short, next)
|
|
}
|
|
|
|
func TestALengthTheToolDoesNotOfferIsRefused(t *testing.T) {
|
|
t.Setenv(mnemonic.Variable, example())
|
|
|
|
out, err := execute(t, "mnemonic", "--words", "15")
|
|
require.ErrorIs(t, err, childmnemonic.ErrWordCount)
|
|
require.Empty(t, out)
|
|
}
|
|
|
|
// run executes the tool with the given arguments and returns what it
|
|
// wrote to standard output.
|
|
func run(t *testing.T, args ...string) string {
|
|
t.Helper()
|
|
|
|
out, err := execute(t, args...)
|
|
require.NoError(t, err)
|
|
|
|
return out
|
|
}
|
|
|
|
// execute runs the tool and returns both what it wrote and how it
|
|
// ended.
|
|
func execute(t *testing.T, args ...string) (string, error) {
|
|
t.Helper()
|
|
|
|
var out bytes.Buffer
|
|
|
|
root := cli.Root()
|
|
root.SetOut(&out)
|
|
root.SetErr(&out)
|
|
root.SetArgs(args)
|
|
|
|
err := root.ExecuteContext(t.Context())
|
|
|
|
return out.String(), err
|
|
}
|