147 lines
5.6 KiB
Go
147 lines
5.6 KiB
Go
// Copyright 2026 Alibaba Group
|
|
// Licensed under the Apache License, Version 2.0
|
|
|
|
package cmdutil
|
|
|
|
import (
|
|
"errors"
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
|
|
apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
func TestCrossPlatformCoverageCommandResolutionProjectsExactShortcutContract(t *testing.T) {
|
|
root := &cobra.Command{Use: "dws"}
|
|
chat := &cobra.Command{Use: "chat"}
|
|
root.AddCommand(chat)
|
|
|
|
resolution := NewCommandResolution(
|
|
chat,
|
|
" +chat-mesages ",
|
|
ResolutionUnknownShortcut,
|
|
[]string{"+chat-messages"},
|
|
"",
|
|
)
|
|
structured := requireTypedCommandResolution(t, resolution.Err(), ResolutionUnknownShortcut)
|
|
|
|
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"? (Run 'dws chat --help' for the full list)` {
|
|
t.Fatalf("Hint = %q", structured.Hint)
|
|
}
|
|
wantActions := []string{
|
|
"Run 'dws chat --help' for the full list",
|
|
"Run 'dws shortcut list --service chat --format json'",
|
|
}
|
|
if !slices.Equal(structured.Actions, wantActions) {
|
|
t.Fatalf("Actions = %#v, want %#v", structured.Actions, wantActions)
|
|
}
|
|
assertResolutionDetails(t, structured, "+chat-mesages", []string{"+chat-messages"})
|
|
}
|
|
|
|
func TestCrossPlatformCoverageGroupRunEClassifiesShortcutLikePreParse(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) {}})
|
|
root.AddCommand(chat)
|
|
|
|
structured := requireTypedCommandResolution(t, GroupRunE(chat, []string{"+chat-mesages"}), ResolutionUnknownShortcut)
|
|
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"? (Run 'dws chat --help' for the full list)` {
|
|
t.Fatalf("Hint = %q", structured.Hint)
|
|
}
|
|
if !slices.Equal(structured.Actions, []string{
|
|
"Run 'dws chat --help' for the full list",
|
|
"Run 'dws shortcut list --service chat --format json'",
|
|
}) {
|
|
t.Fatalf("Actions = %#v", structured.Actions)
|
|
}
|
|
assertResolutionDetails(t, structured, "+chat-mesages", []string{"+chat-messages"})
|
|
}
|
|
|
|
func TestCrossPlatformCoverageCommandResolutionBoundsOneSuggestionSetForHintAndDetails(t *testing.T) {
|
|
root := &cobra.Command{Use: "dws"}
|
|
group := &cobra.Command{Use: "demo"}
|
|
root.AddCommand(group)
|
|
|
|
resolution := NewCommandResolution(
|
|
group,
|
|
" alphx ",
|
|
ResolutionUnknownSubcommand,
|
|
[]string{" alpha ", "alpha", "beta", "", "gamma", "delta"},
|
|
"",
|
|
)
|
|
if !slices.Equal(resolution.suggestions, []string{"alpha", "beta", "gamma"}) {
|
|
t.Fatalf("Suggestions = %#v", resolution.suggestions)
|
|
}
|
|
if got, ok := resolution.Details()["suggestions"].([]string); !ok || !slices.Equal(got, resolution.suggestions) {
|
|
t.Fatalf("Details.suggestions = %#v", resolution.Details()["suggestions"])
|
|
}
|
|
structured := requireTypedCommandResolution(t, resolution.Err(), ResolutionUnknownSubcommand)
|
|
if structured.Message != `unknown subcommand "alphx" for "dws demo"` {
|
|
t.Fatalf("Message = %q", structured.Message)
|
|
}
|
|
if strings.Count(structured.Hint, `"dws demo `) != MaxCommandSuggestions || strings.Contains(structured.Hint, "delta") {
|
|
t.Fatalf("Hint = %q", structured.Hint)
|
|
}
|
|
assertResolutionDetails(t, structured, "alphx", []string{"alpha", "beta", "gamma"})
|
|
}
|
|
|
|
func TestCrossPlatformCoverageHintSubCmdUsesSameTypedResolutionFactory(t *testing.T) {
|
|
root := &cobra.Command{Use: "dws"}
|
|
parent := &cobra.Command{Use: "contact"}
|
|
hint := HintSubCmd("department", "use: dws contact dept")
|
|
root.AddCommand(parent)
|
|
parent.AddCommand(hint)
|
|
|
|
structured := requireTypedCommandResolution(t, hint.RunE(hint, nil), ResolutionUnknownSubcommand)
|
|
if structured.Hint != "use: dws contact dept (Run 'dws contact --help' for the full list)" {
|
|
t.Fatalf("Hint = %q", structured.Hint)
|
|
}
|
|
if !IsHintOnlyCommand(hint) {
|
|
t.Fatal("hint command lost hint-only identity")
|
|
}
|
|
assertResolutionDetails(t, structured, "department", []string{})
|
|
}
|
|
|
|
func TestCrossPlatformCoverageCommandResolutionDefensiveFallbacks(t *testing.T) {
|
|
resolution := NewCommandResolution(nil, "missing", ResolutionReason("unsupported"), nil, "")
|
|
structured := requireTypedCommandResolution(t, resolution.Err(), ResolutionUnknownSubcommand)
|
|
if structured.Message != `unknown subcommand "missing" for "dws"` || structured.Hint != "Run 'dws --help' for the full list" {
|
|
t.Fatalf("fallback resolution = %#v", structured)
|
|
}
|
|
assertResolutionDetails(t, structured, "missing", []string{})
|
|
if suggestions := structured.Details["suggestions"].([]string); suggestions == nil {
|
|
t.Fatal("empty suggestions must remain a machine-readable array, not null")
|
|
}
|
|
}
|
|
|
|
func requireTypedCommandResolution(t *testing.T, err error, reason ResolutionReason) *apperrors.Error {
|
|
t.Helper()
|
|
var structured *apperrors.Error
|
|
if !errors.As(err, &structured) {
|
|
t.Fatalf("error = %T %v, want *errors.Error", err, err)
|
|
}
|
|
if structured.Category != apperrors.CategoryValidation || structured.Reason != string(reason) || structured.ExitCode() != 3 {
|
|
t.Fatalf("structured error = %#v", structured)
|
|
}
|
|
return structured
|
|
}
|
|
|
|
func assertResolutionDetails(t *testing.T, structured *apperrors.Error, input string, suggestions []string) {
|
|
t.Helper()
|
|
if structured.Details["input"] != input {
|
|
t.Fatalf("Details.input = %#v, want %q", structured.Details["input"], input)
|
|
}
|
|
got, ok := structured.Details["suggestions"].([]string)
|
|
if !ok || !slices.Equal(got, suggestions) {
|
|
t.Fatalf("Details.suggestions = %#v, want %#v", structured.Details["suggestions"], suggestions)
|
|
}
|
|
}
|