fix(cli): harden parameter preparse integration

This commit is contained in:
克谨 committed 2026-07-29 10:02:28 +08:00
1 parent 7827856876
commit 195569ddfa
9 files changed
+507 -167

No files matched your search

+101 -11
View File
@@ -117,14 +117,19 @@ func TestCrossPlatformCoverageRunPreParseAppliesCorrectionsOnlyOnSuccess(t *test
func TestRunPreParseResolvesCommandPastLeadingPersistentFlags(t *testing.T) {
tests := []struct {
name string
args []string
name string
args []string
executable bool
}{
{name: "boolean long flag", args: []string{"--dry-run", "calendar", "event", "list", "--date", "2026-03-10"}},
{name: "valued long flag", args: []string{"--profile", "corp:user", "calendar", "event", "list", "--date", "2026-03-10"}},
{name: "valued shorthand", args: []string{"-f", "json", "calendar", "event", "list", "--date", "2026-03-10"}},
{name: "attached shorthand", args: []string{"-fjson", "calendar", "event", "list", "--date", "2026-03-10"}},
{name: "clustered attached shorthand", args: []string{"-vfjson", "calendar", "event", "list", "--date", "2026-03-10"}},
{name: "boolean long flag", args: []string{"--dry-run", "calendar", "event", "list", "--date", "2026-03-10"}, executable: true},
{name: "camel-case boolean long flag", args: []string{"--dryRun", "calendar", "event", "list", "--date", "2026-03-10"}},
{name: "fuzzy boolean long flag", args: []string{"--dry-rnu", "calendar", "event", "list", "--date", "2026-03-10"}},
{name: "valued long flag", args: []string{"--profile", "corp:user", "calendar", "event", "list", "--date", "2026-03-10"}, executable: true},
{name: "fuzzy valued long flag", args: []string{"--profle", "corp:user", "calendar", "event", "list", "--date", "2026-03-10"}},
{name: "sticky valued long flag", args: []string{"--timeout30", "calendar", "event", "list", "--date", "2026-03-10"}},
{name: "valued shorthand", args: []string{"-f", "json", "calendar", "event", "list", "--date", "2026-03-10"}, executable: true},
{name: "attached shorthand", args: []string{"-fjson", "calendar", "event", "list", "--date", "2026-03-10"}, executable: true},
{name: "clustered attached shorthand", args: []string{"-vfjson", "calendar", "event", "list", "--date", "2026-03-10"}, executable: true},
}
for _, test := range tests {
@@ -132,6 +137,7 @@ func TestRunPreParseResolvesCommandPastLeadingPersistentFlags(t *testing.T) {
root := &cobra.Command{Use: "dws", SilenceErrors: true, SilenceUsage: true}
root.PersistentFlags().Bool("dry-run", false, "")
root.PersistentFlags().String("profile", "", "")
root.PersistentFlags().Int("timeout", 0, "")
root.PersistentFlags().StringP("format", "f", "json", "")
root.PersistentFlags().BoolP("verbose", "v", false, "")
@@ -173,11 +179,68 @@ func TestRunPreParseResolvesCommandPastLeadingPersistentFlags(t *testing.T) {
if ctx == nil || len(ctx.Corrections) != 1 {
t.Fatalf("RunPreParseArgs() context = %#v", ctx)
}
if err := root.Execute(); err != nil {
t.Fatalf("corrected command failed: %v", err)
if test.executable {
if err := root.Execute(); err != nil {
t.Fatalf("corrected command failed: %v", err)
}
if value != "2026-03-10" {
t.Fatalf("canonical --start value = %q", value)
}
}
if value != "2026-03-10" {
t.Fatalf("canonical --start value = %q", value)
})
}
}
func TestRunPreParsePrimesPresentationFlagsForEarlyErrors(t *testing.T) {
tests := []struct {
name string
args []string
wantFormat string
wantDebug bool
wantVerbose bool
}{
{
name: "canonical flags after command",
args: []string{"child", "--name", "demo", "--format", "table", "--debug"},
wantFormat: "table",
wantDebug: true,
},
{
name: "corrected leading flags",
args: []string{"--dryRun", "--formt=pretty", "--verbos", "child", "--name", "demo"},
wantFormat: "pretty",
wantVerbose: true,
},
{
name: "clustered shorthands",
args: []string{"-vfraw", "child", "--name", "demo"},
wantFormat: "raw",
wantVerbose: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
root := &cobra.Command{Use: "dws", SilenceErrors: true, SilenceUsage: true}
root.PersistentFlags().Bool("dry-run", false, "")
root.PersistentFlags().StringP("format", "f", "json", "")
root.PersistentFlags().Bool("debug", false, "")
root.PersistentFlags().BoolP("verbose", "v", false, "")
child := &cobra.Command{Use: "child"}
child.Flags().String("name", "", "")
root.AddCommand(child)
engine := NewEngine()
engine.Register(newStub("fail", PreParse, func(*Context) error { return errors.New("early") }))
ctx, err := RunPreParseArgs(root, engine, test.args)
if err == nil || ctx == nil {
t.Fatalf("RunPreParseArgs() = %#v, %v; want early error with context", ctx, err)
}
format, _ := root.PersistentFlags().GetString("format")
debug, _ := root.PersistentFlags().GetBool("debug")
verbose, _ := root.PersistentFlags().GetBool("verbose")
if format != test.wantFormat || debug != test.wantDebug || verbose != test.wantVerbose {
t.Fatalf("presentation flags = format:%q debug:%v verbose:%v; want %q/%v/%v", format, debug, verbose, test.wantFormat, test.wantDebug, test.wantVerbose)
}
})
}
@@ -201,6 +264,12 @@ func TestCommandTraversalFlagTokenEdges(t *testing.T) {
if flag, inline, matched := persistentFlagToken(nil, "--verbose"); flag != nil || inline || matched {
t.Fatalf("nil flag set matched: %#v, %v, %v", flag, inline, matched)
}
if flag, inline, matched := (*flagTokenMatcher)(nil).matchTraversalToken(""); flag != nil || inline || matched {
t.Fatalf("nil matcher matched: %#v, %v, %v", flag, inline, matched)
}
if match := (*flagTokenMatcher)(nil).matchLongToken("--verbose"); match.recognized {
t.Fatalf("nil long matcher matched: %#v", match)
}
if flag, inline, matched := persistentFlagToken(root.PersistentFlags(), "-x"); flag != nil || inline || matched {
t.Fatalf("unknown shorthand matched: %#v, %v, %v", flag, inline, matched)
}
@@ -209,3 +278,24 @@ func TestCommandTraversalFlagTokenEdges(t *testing.T) {
t.Fatalf("boolean shorthand cluster = %#v, %v, %v", flag, inline, matched)
}
}
func TestPrimeEarlyErrorPresentationEdges(t *testing.T) {
primeEarlyErrorPresentation(nil, nil, nil)
root := &cobra.Command{Use: "root"}
root.PersistentFlags().StringP("format", "f", "json", "")
root.PersistentFlags().Bool("debug", false, "")
child := &cobra.Command{Use: "child"}
child.Flags().String("name", "", "")
root.AddCommand(child)
primeEarlyErrorPresentation(root, child, []string{
"child", "--unknown", "value", "-x", "--name", "demo",
"-f", "table", "--", "--debug",
})
format, _ := root.PersistentFlags().GetString("format")
debug, _ := root.PersistentFlags().GetBool("debug")
if format != "table" || debug {
t.Fatalf("presentation after edge argv = format:%q debug:%v", format, debug)
}
}