Files
x/internal/cli/cli_test.go
T
sneak 5932cc12ee Remove cbk and mnemonic commands from sutil
sutil holds only the utilities wanted on every machine; it is not a
command-line front end for the packages in pkg/. cbk and bip39wordlist
remain libraries with no CLI surface. The README states the boundary.
2026-10-31 10:16:10 -07:00

187 lines
4.6 KiB
Go

package cli_test
import (
"bytes"
"errors"
"os"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"sneak.berlin/go/x/internal/cli"
"sneak.berlin/go/x/internal/pwgen"
)
const testVersion = "v1.2.3-test"
func TestVersionCommand(t *testing.T) {
t.Parallel()
output, err := execute(t, "version")
require.NoError(t, err)
assert.Equal(t, testVersion+"\n", output)
}
func TestVersionFlag(t *testing.T) {
t.Parallel()
output, err := execute(t, "--version")
require.NoError(t, err)
assert.Contains(t, output, testVersion)
}
func TestInfoCommand(t *testing.T) {
t.Parallel()
output, err := execute(t, "info")
require.NoError(t, err)
assert.Contains(t, output, "version: "+testVersion+"\n")
assert.Contains(t, output, "go: go")
assert.Contains(t, output, "module: sneak.berlin/go/x\n")
assert.Contains(t, output, "config: (none)\n")
}
func TestConfigFileDiscoveredInConfigDir(t *testing.T) {
t.Parallel()
configDir := t.TempDir()
path := filepath.Join(configDir, ".sneak.yaml")
require.NoError(t, os.WriteFile(path, []byte("example: 1\n"), 0o600))
params := cli.Params{Version: testVersion, ConfigDir: configDir}
output, err := executeWithParams(t, params, "info")
require.NoError(t, err)
assert.Contains(t, output, "Using config file: "+path+"\n")
assert.Contains(t, output, "config: "+path+"\n")
}
func TestConfigFlagLoadsNamedFile(t *testing.T) {
t.Parallel()
path := filepath.Join(t.TempDir(), "elsewhere.yaml")
require.NoError(t, os.WriteFile(path, []byte("example: 1\n"), 0o600))
output, err := execute(t, "--config", path, "info")
require.NoError(t, err)
assert.Contains(t, output, "Using config file: "+path+"\n")
assert.Contains(t, output, "config: "+path+"\n")
}
func TestConfigFlagRejectsMissingFile(t *testing.T) {
t.Parallel()
path := filepath.Join(t.TempDir(), "missing.yaml")
_, err := execute(t, "--config", path, "version")
require.Error(t, err)
}
func TestConfigRejectsMalformedFile(t *testing.T) {
t.Parallel()
configDir := t.TempDir()
path := filepath.Join(configDir, ".sneak.yaml")
require.NoError(t, os.WriteFile(path, []byte("not: [valid\n"), 0o600))
params := cli.Params{Version: testVersion, ConfigDir: configDir}
_, err := executeWithParams(t, params, "version")
require.Error(t, err)
}
func TestGenerateRandomSeededIsDeterministic(t *testing.T) {
t.Parallel()
first, err := execute(t, "generate", "random", "--count", "4096", "seed")
require.NoError(t, err)
assert.Len(t, first, 4096)
second, err := execute(t, "generate", "random", "--count", "4096", "seed")
require.NoError(t, err)
assert.Equal(t, first, second)
other, err := execute(t, "gen", "random", "-c", "4096", "other seed")
require.NoError(t, err)
assert.NotEqual(t, first, other)
}
func TestGenerateRandomUnseeded(t *testing.T) {
t.Parallel()
first, err := execute(t, "generate", "random", "--count", "1024")
require.NoError(t, err)
assert.Len(t, first, 1024)
second, err := execute(t, "generate", "random", "--count", "1024")
require.NoError(t, err)
assert.NotEqual(t, first, second)
}
func TestGenerateRandomRejectsExtraArguments(t *testing.T) {
t.Parallel()
_, err := execute(t, "generate", "random", "one", "two")
require.Error(t, err)
}
func TestGeneratePassword(t *testing.T) {
t.Parallel()
_, err := pwgen.New(pwgen.Params{Words: nil, MinWordLength: 0, MaxWordLength: 0})
if errors.Is(err, pwgen.ErrNoUsableWords) {
t.Skip("this system has no word list")
}
require.NoError(t, err)
output, err := execute(t, "generate", "password")
require.NoError(t, err)
password := strings.TrimSuffix(output, "\n")
assert.NotContains(t, password, "\n")
assert.GreaterOrEqual(t, len(password), 3*(pwgen.DefaultMinWordLength+1)+2)
assert.True(t, strings.ContainsAny(password, "0123456789"), password)
assert.True(t, strings.ContainsAny(password, pwgen.Delimiters), password)
}
func TestUnknownCommandFails(t *testing.T) {
t.Parallel()
_, err := execute(t, "cbk")
require.Error(t, err)
}
// execute runs sutil with the given arguments, looking for config in an
// empty temporary directory, and returns everything it wrote to stdout and
// stderr.
func execute(t *testing.T, args ...string) (string, error) {
t.Helper()
params := cli.Params{Version: testVersion, ConfigDir: t.TempDir()}
return executeWithParams(t, params, args...)
}
// executeWithParams is execute with explicit Params.
func executeWithParams(
t *testing.T, params cli.Params, args ...string,
) (string, error) {
t.Helper()
root := cli.New(params)
var output bytes.Buffer
root.SetOut(&output)
root.SetErr(&output)
root.SetArgs(args)
err := root.Execute()
return output.String(), err
}