165 lines
6.3 KiB
Go
165 lines
6.3 KiB
Go
// Copyright 2026 Alibaba Group
|
|
// Licensed under the Apache License, Version 2.0
|
|
|
|
package pipeline
|
|
|
|
import (
|
|
stderrors "errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors"
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/cmdutil"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
func TestCrossPlatformCoverageValidateUnresolvedCommandClassifiesShortcutBeforeFlags(t *testing.T) {
|
|
root := &cobra.Command{Use: "dws"}
|
|
chat := &cobra.Command{Use: "chat"}
|
|
chat.AddCommand(
|
|
&cobra.Command{Use: "+chat-messages", Run: func(*cobra.Command, []string) {}},
|
|
&cobra.Command{Use: "+messages-send", Run: func(*cobra.Command, []string) {}},
|
|
&cobra.Command{Use: "chat-mesages", Run: func(*cobra.Command, []string) {}},
|
|
)
|
|
root.AddCommand(chat)
|
|
|
|
err := validateUnresolvedCommand(chat, []string{"+chat-mesages", "--keyword", "x"})
|
|
structured := requireCommandResolutionError(t, err, "unknown_shortcut")
|
|
if structured.Message != `unknown shortcut "+chat-mesages" for "dws chat"` {
|
|
t.Fatalf("Message = %q", structured.Message)
|
|
}
|
|
if structured.Hint != `Did you mean "dws chat +chat-messages"?` {
|
|
t.Fatalf("Hint = %q", structured.Hint)
|
|
}
|
|
if len(structured.Actions) != 1 || structured.Actions[0] != "Run 'dws shortcut list --service chat --format json'" {
|
|
t.Fatalf("Actions = %#v", structured.Actions)
|
|
}
|
|
if len(structured.AvailableFlags) != 0 {
|
|
t.Fatalf("AvailableFlags = %#v, want none for command error", structured.AvailableFlags)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageValidateUnresolvedCommandClassifiesOnlyExplicitContainers(t *testing.T) {
|
|
root := &cobra.Command{Use: "dws"}
|
|
dev := &cobra.Command{Use: "dev"}
|
|
app := &cobra.Command{Use: "app"}
|
|
cmdutil.MarkGroup(app)
|
|
app.AddCommand(&cobra.Command{Use: "list", Run: func(*cobra.Command, []string) {}})
|
|
dev.AddCommand(app)
|
|
root.AddCommand(dev)
|
|
|
|
err := validateUnresolvedCommand(app, []string{"search", "--keyword", "x"})
|
|
structured := requireCommandResolutionError(t, err, "unknown_subcommand")
|
|
if structured.Message != `unknown subcommand "search" for "dws dev app"` {
|
|
t.Fatalf("Message = %q", structured.Message)
|
|
}
|
|
if len(structured.Actions) != 1 || structured.Actions[0] != "Run 'dws dev app --help' to see available subcommands" {
|
|
t.Fatalf("Actions = %#v", structured.Actions)
|
|
}
|
|
|
|
positional := &cobra.Command{Use: "schema [path]", Run: func(*cobra.Command, []string) {}}
|
|
root.AddCommand(positional)
|
|
for name, test := range map[string]struct {
|
|
target *cobra.Command
|
|
remaining []string
|
|
}{
|
|
"legitimate positional": {target: positional, remaining: []string{"+chat-messages"}},
|
|
"flag remains a flag": {target: app, remaining: []string{"--keyword", "x"}},
|
|
"dash terminator": {target: app, remaining: []string{"--", "search"}},
|
|
"nil target": {target: nil, remaining: []string{"search"}},
|
|
"empty remaining": {target: app},
|
|
} {
|
|
t.Run(name, func(t *testing.T) {
|
|
if err := validateUnresolvedCommand(test.target, test.remaining); err != nil {
|
|
t.Fatalf("validateUnresolvedCommand() error = %v", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageRunPreParseArgsValidatesCommandsWithoutHandlersAndPrimesPresentation(t *testing.T) {
|
|
root := &cobra.Command{Use: "dws"}
|
|
root.PersistentFlags().String("format", "table", "")
|
|
chat := &cobra.Command{Use: "chat"}
|
|
chat.AddCommand(&cobra.Command{Use: "+chat-messages", Run: func(*cobra.Command, []string) {}})
|
|
root.AddCommand(chat)
|
|
|
|
ctx, err := RunPreParseArgs(root, nil, []string{
|
|
"--format", "json", "chat", "+chat-mesages", "--keyword", "x",
|
|
})
|
|
if ctx == nil || ctx.Command != "dws chat" {
|
|
t.Fatalf("Context = %#v", ctx)
|
|
}
|
|
requireCommandResolutionError(t, err, "unknown_shortcut")
|
|
if format, getErr := root.PersistentFlags().GetString("format"); getErr != nil || format != "json" {
|
|
t.Fatalf("format = %q, %v; want primed json presentation", format, getErr)
|
|
}
|
|
|
|
validRoot := &cobra.Command{Use: "dws"}
|
|
validChat := &cobra.Command{Use: "chat"}
|
|
validChat.AddCommand(&cobra.Command{Use: "+chat-messages", Run: func(*cobra.Command, []string) {}})
|
|
validRoot.AddCommand(validChat)
|
|
if ctx, err := RunPreParseArgs(validRoot, nil, []string{"chat", "+chat-messages"}); ctx != nil || err != nil {
|
|
t.Fatalf("valid shortcut = %#v, %v", ctx, err)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageCommandSuggestionHintIsBoundedAndFallsBack(t *testing.T) {
|
|
root := &cobra.Command{Use: "dws"}
|
|
group := &cobra.Command{Use: "demo", SuggestionsMinimumDistance: 100}
|
|
root.AddCommand(group)
|
|
for _, name := range []string{"alpha", "beta", "delta", "gamma"} {
|
|
group.AddCommand(&cobra.Command{Use: name, Run: func(*cobra.Command, []string) {}})
|
|
}
|
|
hint := commandSuggestionHint(group, "missing", "fallback")
|
|
if strings.Count(hint, `"dws demo `) != maxCommandSuggestions || strings.Contains(hint, "gamma") {
|
|
t.Fatalf("bounded hint = %q", hint)
|
|
}
|
|
|
|
empty := &cobra.Command{Use: "empty"}
|
|
root.AddCommand(empty)
|
|
if got := commandSuggestionHint(empty, "missing", "fallback"); got != "fallback" {
|
|
t.Fatalf("fallback hint = %q", got)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageCommandResolutionDefensiveAndExplicitSuggestionPaths(t *testing.T) {
|
|
root := &cobra.Command{Use: "dws"}
|
|
service := &cobra.Command{Use: "demo"}
|
|
group := &cobra.Command{Use: "nested"}
|
|
service.AddCommand(group)
|
|
root.AddCommand(service)
|
|
|
|
if isExplicitShortcutCandidate(root, "+missing") {
|
|
t.Fatal("root command was treated as a shortcut service")
|
|
}
|
|
if isExplicitShortcutCandidate(group, "+missing") {
|
|
t.Fatal("nested command was treated as a top-level shortcut service")
|
|
}
|
|
if got := commandSuggestions(nil, "missing"); got != nil {
|
|
t.Fatalf("commandSuggestions(nil) = %#v", got)
|
|
}
|
|
|
|
service.SuggestionsMinimumDistance = 1
|
|
service.AddCommand(&cobra.Command{
|
|
Use: "canonical",
|
|
SuggestFor: []string{"invented"},
|
|
Run: func(*cobra.Command, []string) {},
|
|
})
|
|
if got := commandSuggestions(service, "invented"); len(got) != 1 || got[0] != "canonical" {
|
|
t.Fatalf("explicit SuggestFor suggestions = %#v", got)
|
|
}
|
|
}
|
|
|
|
func requireCommandResolutionError(t *testing.T, err error, reason string) *apperrors.Error {
|
|
t.Helper()
|
|
var structured *apperrors.Error
|
|
if !stderrors.As(err, &structured) {
|
|
t.Fatalf("error = %T %v, want *errors.Error", err, err)
|
|
}
|
|
if structured.Category != apperrors.CategoryValidation || structured.Reason != reason || structured.ExitCode() != 3 {
|
|
t.Fatalf("structured error = %#v", structured)
|
|
}
|
|
return structured
|
|
}
|