cli/root_harden_test.go
Rama main · réplica de lectura del repositorio original.
package cli
import (
"bytes"
"errors"
"fmt"
"strings"
"testing"
"github.com/spf13/cobra"
"go.keploy.io/server/v3/utils"
)
// TestHardenUnknownSubcommands verifies that the tree walk makes command GROUPS
// reject an unknown verb (instead of printing help and exiting 0), while leaving
// no-verb help, valid subcommands, and groups that already have their own RunE
// untouched. This is what stops `keploy mock bogus` exiting 0 (design §P0b).
func TestHardenUnknownSubcommands(t *testing.T) {
newTree := func() (root *cobra.Command, leafRan *bool) {
ran := false
root = &cobra.Command{Use: "keploy"}
group := &cobra.Command{Use: "mock"} // a group with subcommands, no RunE
group.AddCommand(&cobra.Command{
Use: "record",
RunE: func(*cobra.Command, []string) error { ran = true; return nil },
})
root.AddCommand(group)
// A group that already owns a RunE must be left alone.
withRun := &cobra.Command{Use: "cfg", RunE: func(*cobra.Command, []string) error { return nil }}
withRun.AddCommand(&cobra.Command{Use: "sub", RunE: func(*cobra.Command, []string) error { return nil }})
root.AddCommand(withRun)
HardenUnknownSubcommands(root)
return root, &ran
}
exec := func(t *testing.T, root *cobra.Command, args ...string) error {
t.Helper()
root.SetArgs(args)
root.SetOut(&bytes.Buffer{})
root.SetErr(&bytes.Buffer{})
return root.Execute()
}
t.Run("unknown verb errors", func(t *testing.T) {
root, _ := newTree()
err := exec(t, root, "mock", "bogus")
if err == nil || !strings.HasPrefix(err.Error(), "unknown command") {
t.Fatalf("unknown verb: got err=%v, want an 'unknown command' error", err)
}
})
t.Run("no verb shows help without error", func(t *testing.T) {
root, _ := newTree()
if err := exec(t, root, "mock"); err != nil {
t.Fatalf("no verb: got err=%v, want nil (help)", err)
}
})
t.Run("valid subcommand still runs", func(t *testing.T) {
root, ran := newTree()
if err := exec(t, root, "mock", "record"); err != nil {
t.Fatalf("valid sub: got err=%v", err)
}
if !*ran {
t.Fatalf("valid sub: the leaf's RunE did not run (parent RunE shadowed it)")
}
})
t.Run("group with its own RunE is left alone", func(t *testing.T) {
root, _ := newTree()
// cfg owns a RunE, so harden must not have swapped in the reject wrapper:
// an extra arg runs cfg's own RunE (nil) rather than an unknown-command error.
if err := exec(t, root, "cfg", "whatever"); err != nil {
t.Fatalf("group with own RunE: got err=%v, want nil (its own RunE handles args)", err)
}
})
}
// An unknown verb names where a command of that name, or a near one, lives
// under the group, at any depth, whether or not flags follow it: with flags,
// cobra parsed the group's own flags first and said "unknown flag", which sent
// a coding agent looking for a flag when the command was what it had wrong
// (`keploy cloud request-changes --branch x`, which is `keploy cloud review
// request-changes`). A subcommand's own bad flag is still its parent's to say.
func TestAnUnknownVerbNamesWhereItLives(t *testing.T) {
newTree := func() *cobra.Command {
root := &cobra.Command{Use: "keploy"}
root.SetFlagErrorFunc(func(_ *cobra.Command, err error) error { return errors.New("root: " + err.Error()) })
cloud := &cobra.Command{Use: "cloud"}
cloud.PersistentFlags().String("app", "", "")
review := &cobra.Command{Use: "review"}
for _, n := range []string{"approve", "request-changes", "request"} {
leaf := &cobra.Command{Use: n, RunE: func(*cobra.Command, []string) error { return nil }}
leaf.Flags().String("branch", "", "")
review.AddCommand(leaf)
}
hidden := &cobra.Command{Use: "request-changes-old", Hidden: true, RunE: func(*cobra.Command, []string) error { return nil }}
review.AddCommand(hidden)
// A hidden group's commands are not offered.
legacy := &cobra.Command{Use: "legacy", Hidden: true}
legacy.AddCommand(&cobra.Command{Use: "request-changes", RunE: func(*cobra.Command, []string) error { return nil }})
cloud.AddCommand(review, legacy, &cobra.Command{Use: "restore-test", RunE: func(*cobra.Command, []string) error { return nil }})
root.AddCommand(cloud)
HardenUnknownSubcommands(root)
TagUsageErrors(root)
return root
}
run := func(args ...string) error {
root := newTree()
root.SetArgs(args)
root.SetOut(&bytes.Buffer{})
root.SetErr(&bytes.Buffer{})
return root.Execute()
}
want := "unknown command \"request-changes\" for \"keploy cloud\"\n\nDid you mean this?\n\tkeploy cloud review request-changes"
if err := run("cloud", "request-changes"); err == nil || err.Error() != want || !utils.IsUsageError(err) {
t.Errorf("bare verb: got %v, want the usage error %q", err, want)
}
// With flags, the refusal comes through the flag-error funcs, which tag it.
for _, args := range [][]string{
{"cloud", "request-changes", "--branch", "b1", "-m", "x"},
{"cloud", "--app", "a.b", "request-changes", "--branch", "b1"},
} {
if err := run(args...); err == nil || err.Error() != want || !errors.Is(err, utils.ErrUsage) {
t.Errorf("%v: got %v, want the tagged usage error %q", args, err, want)
}
}
// A near name, one level down.
if err := run("cloud", "aprove", "--branch", "b1"); err == nil || !strings.HasSuffix(err.Error(), "Did you mean this?\n\tkeploy cloud review approve") {
t.Errorf("near name: %v", err)
}
// Nothing close: the refusal alone.
if err := run("cloud", "zzzzzz", "--branch", "b1"); err == nil || err.Error() != `unknown command "zzzzzz" for "keploy cloud"` {
t.Errorf("nothing close: %v", err)
}
// A subcommand's own unknown flag is still its parent's to say, and so is
// a group's own bad flag with no verb before it.
if err := run("cloud", "review", "approve", "--nope"); err == nil || err.Error() != "root: unknown flag: --nope" || !errors.Is(err, utils.ErrUsage) {
t.Errorf("leaf flag: %v", err)
}
if err := run("cloud", "review", "approve", "b1", "--nope"); err == nil || err.Error() != "root: unknown flag: --nope" {
t.Errorf("leaf argument then a bad flag: %v", err)
}
if err := run("cloud", "--nope"); err == nil || err.Error() != "root: unknown flag: --nope" {
t.Errorf("group flag: %v", err)
}
// The verb that exists still runs.
if err := run("cloud", "review", "request-changes", "--branch", "b1"); err != nil {
t.Errorf("valid: %v", err)
}
}
// The hints are bounded.
func TestUnknownSubcommandHintsAreBounded(t *testing.T) {
g := &cobra.Command{Use: "g"}
for i := 0; i < maxSubcommandHints+3; i++ {
g.AddCommand(&cobra.Command{Use: fmt.Sprintf("ab%d", i), RunE: func(*cobra.Command, []string) error { return nil }})
}
if n := strings.Count(UnknownSubcommand(g, "ab").Error(), "\n\tg ab"); n != maxSubcommandHints {
t.Errorf("%d hints, want %d", n, maxSubcommandHints)
}
}
// A group's own flag-error func still says the flag errors that are not an
// unknown verb, for it and the commands under it.
func TestHardeningKeepsAGroupsOwnFlagErrorFunc(t *testing.T) {
root := &cobra.Command{Use: "keploy"}
group := &cobra.Command{Use: "ui"}
group.SetFlagErrorFunc(func(_ *cobra.Command, err error) error { return errors.New("ui: " + err.Error()) })
group.AddCommand(&cobra.Command{Use: "open", RunE: func(*cobra.Command, []string) error { return nil }})
root.AddCommand(group)
HardenUnknownSubcommands(root)
for _, args := range [][]string{{"ui", "--nope"}, {"ui", "open", "--nope"}} {
root.SetArgs(args)
root.SetOut(&bytes.Buffer{})
root.SetErr(&bytes.Buffer{})
if err := root.Execute(); err == nil || err.Error() != "ui: unknown flag: --nope" {
t.Errorf("%v: %v", args, err)
}
}
}
// An exact name is never cut by the cap, however many near names come first;
// with suggestions disabled there are none; the walk leaves each command's
// suggestion distance as it found it.
func TestUnknownSubcommandHintsPutExactNamesFirst(t *testing.T) {
root := &cobra.Command{Use: "keploy"}
for i := 0; i < maxSubcommandHints+2; i++ {
root.AddCommand(&cobra.Command{Use: fmt.Sprintf("approve%d", i), RunE: func(*cobra.Command, []string) error { return nil }})
}
deep := &cobra.Command{Use: "review"}
deep.AddCommand(&cobra.Command{Use: "approve", RunE: func(*cobra.Command, []string) error { return nil }})
root.AddCommand(deep)
err := UnknownSubcommand(root, "approve").Error()
if !strings.Contains(err, "Did you mean this?\n\tkeploy review approve\n") || strings.Count(err, "\n\tkeploy ") != maxSubcommandHints {
t.Errorf("exact name not first, or hints not capped: %q", err)
}
if root.SuggestionsMinimumDistance != 0 || deep.SuggestionsMinimumDistance != 0 {
t.Errorf("distance left changed: %d %d", root.SuggestionsMinimumDistance, deep.SuggestionsMinimumDistance)
}
deep.DisableSuggestions = true
if err := UnknownSubcommand(root, "approve").Error(); strings.Contains(err, "review approve") {
t.Errorf("a command with suggestions disabled is suggested from: %q", err)
}
root.DisableSuggestions = true
if err := UnknownSubcommand(root, "approve").Error(); err != `unknown command "approve" for "keploy"` {
t.Errorf("suggestions disabled: %q", err)
}
// The root's setting holds for a group asked directly, too.
deep.DisableSuggestions = false
if err := UnknownSubcommand(deep, "aprove").Error(); err != `unknown command "aprove" for "keploy review"` {
t.Errorf("suggestions disabled on the root, asked of a group: %q", err)
}
}
// cobra refuses an unknown top-level command before any of root's funcs run;
// main turns its refusal into one that names where the command lives.
func TestHintUnknownCommand(t *testing.T) {
root := &cobra.Command{Use: "keploy"}
contract := &cobra.Command{Use: "contract"}
contract.AddCommand(&cobra.Command{Use: "generate", RunE: func(*cobra.Command, []string) error { return nil }})
root.AddCommand(contract, &cobra.Command{Use: "record", RunE: func(*cobra.Command, []string) error { return nil }})
root.SetArgs([]string{"generate"})
root.SetOut(&bytes.Buffer{})
root.SetErr(&bytes.Buffer{})
_, raw := root.ExecuteC()
err := HintUnknownCommand(root, raw)
if err == nil || err.Error() != "unknown command \"generate\" for \"keploy\"\n\nDid you mean this?\n\tkeploy contract generate" || !utils.IsUsageError(err) {
t.Errorf("got %v (from %v)", err, raw)
}
other := errors.New("boom")
if HintUnknownCommand(root, other) != other || HintUnknownCommand(root, nil) != nil {
t.Error("an error that is not cobra's refusal is changed")
}
// cobra quotes the verb with %q; it is read back as typed.
root.SetArgs([]string{"gen\terate"})
_, raw = root.ExecuteC()
if err := HintUnknownCommand(root, raw); err == nil || !strings.HasPrefix(err.Error(), `unknown command "gen\terate" for "keploy"`) {
t.Errorf("quoted verb: %v (from %v)", err, raw)
}
sub := errors.New(`unknown command "x" for "keploy contract"`)
if HintUnknownCommand(root, sub) != sub {
t.Error("a group's refusal is rewritten as the root's")
}
}