Print the mnemonic and the vault and unlocker listings to stdout (closes #138)
check / check (push) Waiting to run

`secret generate mnemonic`, `secret vault list` and `secret unlocker list`,
in text and `--json` form, printed through cobra's `cmd.Println` and
`cmd.Printf`. Those write to stderr when no output writer is set, and the
binary sets none, so a pipe or `$(...)` got nothing. They now write to
`cmd.OutOrStdout()`, as `secret list` does, and so does "No versions found"
from `secret version list`. Status messages and the warning printed after
the mnemonic stay on stderr.

A new test runs the built binary and reads the stdout of `generate
mnemonic` and of both listings in both forms.

Model: opus-5-5
This commit was merged in pull request #164.
This commit is contained in:
2026-10-14 22:31:47 +02:00
parent 0630a32167
commit fe94e99801
7 changed files with 94 additions and 15 deletions
+65
View File
@@ -2,6 +2,7 @@ package cli_test
import (
"bytes"
"encoding/json"
"os/exec"
"path/filepath"
"strings"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"sneak.berlin/go/secret/internal/bip39"
"sneak.berlin/go/secret/internal/secret"
)
@@ -90,3 +92,66 @@ func TestGetCommandOutputsToStdout(t *testing.T) {
// Nothing should be in stderr
assert.Empty(t, stderr.String(), "stderr should be empty")
}
// TestMnemonicAndListingsOutputToStdout tests that 'secret generate
// mnemonic', 'secret vault list' and 'secret unlocker list' print their
// output to stdout, so that it can be piped
func TestMnemonicAndListingsOutputToStdout(t *testing.T) {
t.Parallel()
tempDir := t.TempDir()
secretPath := secretBinaryPath(t)
// run runs the secret binary and returns what it wrote to stdout and to
// stderr
run := func(args ...string) (string, string) {
//nolint:gosec // G204: test executes the freshly built secret binary
cmd := exec.CommandContext(t.Context(), secretPath, args...)
cmd.Env = []string{
secret.EnvStateDir + "=" + tempDir,
secret.EnvMnemonic + "=" + testMnemonic,
secret.EnvUnlockPassphrase + "=test-passphrase",
"PATH=/usr/bin:/bin",
}
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
require.NoError(t, err, "%v should succeed: %s", args, stderr.String())
return stdout.String(), stderr.String()
}
stdout, stderr := run("generate", "mnemonic")
assert.True(t, bip39.IsMnemonicValid(strings.TrimSpace(stdout)),
"stdout should hold the mnemonic, not %q", stdout)
assert.Contains(t, stderr, "Save this mnemonic phrase securely",
"the warning should be on stderr")
run("init")
stdout, _ = run("vault", "list", "--json")
var vaults map[string]any
require.NoError(t, json.Unmarshal([]byte(stdout), &vaults),
"stdout should hold the vaults as JSON, not %q", stdout)
assert.Equal(t, "default", vaults["currentVault"])
stdout, _ = run("unlocker", "list", "--json")
var unlockers map[string]any
require.NoError(t, json.Unmarshal([]byte(stdout), &unlockers),
"stdout should hold the unlockers as JSON, not %q", stdout)
assert.Len(t, unlockers["unlockers"], 1)
stdout, _ = run("vault", "list")
assert.Contains(t, stdout, "default (current)")
stdout, _ = run("unlocker", "list")
assert.Contains(t, stdout, "Total: 1 unlocker(s)")
}