check / check (push) Waiting to run
Given a subcommand they do not have, `secret vault`, `secret unlocker`, `secret version` and `secret generate` printed their help and exited 0, so a mistyped command looked like a successful one. Each now exits 1 with `unknown command "<name>" for "secret <parent>"`, and with no arguments still prints its help. Cobra checks a command's Args only when it has a RunE, so each parent gets `Args: cobra.NoArgs` and a RunE that prints its help. Args alone changes nothing. TestNoVaultImportCommand required the old answer to `secret vault import` and now requires the error. Judgement call: their help now shows a `secret <parent> [flags]` usage line, which cobra adds for any command that runs. Judgement call: `secret vault help` now fails as an unknown subcommand; `secret help vault` and `secret vault --help` still work. Model: opus-5-5
59 lines
1.9 KiB
Go
59 lines
1.9 KiB
Go
package cli_test
|
|
|
|
import (
|
|
"fmt"
|
|
"os/exec"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"sneak.berlin/go/secret/internal/secret"
|
|
)
|
|
|
|
// TestUnknownSubcommandFails is a regression test for
|
|
// https://git.eeqj.de/sneak/secret/issues/183: given a subcommand it does not
|
|
// have, `secret vault`, `secret unlocker`, `secret version` and `secret
|
|
// generate` printed their help and exited 0, so a script could not tell a
|
|
// mistyped command from a successful one. Each runs here as the built binary
|
|
// and must exit 1 naming the subcommand, as an unknown command does at the
|
|
// top level, and must still print its help and exit 0 when given nothing.
|
|
func TestUnknownSubcommandFails(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
stateDir := t.TempDir()
|
|
|
|
// run runs the built binary with args and returns what it printed and
|
|
// its exit code
|
|
run := func(args ...string) (string, int) {
|
|
//nolint:gosec // G204: test executes the freshly built secret binary
|
|
cmd := exec.CommandContext(t.Context(), secretBinaryPath(t), args...)
|
|
cmd.Env = []string{secret.EnvStateDir + "=" + stateDir}
|
|
|
|
output, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
var exitErr *exec.ExitError
|
|
require.ErrorAs(t, err, &exitErr, "secret %v did not run", args)
|
|
}
|
|
|
|
return string(output), cmd.ProcessState.ExitCode()
|
|
}
|
|
|
|
for _, call := range []string{
|
|
"vault improt", "unlocker foo", "version foo", "generate foo",
|
|
} {
|
|
parent, unknown, _ := strings.Cut(call, " ")
|
|
|
|
output, exitCode := run(parent, unknown)
|
|
assert.Equal(t, 1, exitCode, "secret %s should fail:\n%s", call, output)
|
|
assert.Contains(t, output, fmt.Sprintf(
|
|
"unknown command %q for \"secret %s\"", unknown, parent))
|
|
|
|
output, exitCode = run(parent)
|
|
assert.Equal(t, 0, exitCode,
|
|
"secret %s should succeed:\n%s", parent, output)
|
|
assert.Contains(t, output, usageHeading,
|
|
"secret %s should print its help", parent)
|
|
}
|
|
}
|