279 lines
7.2 KiB
Go
279 lines
7.2 KiB
Go
package app
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/cli"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
func TestRootCommandDoesNotInjectPatchedHelpCommands(t *testing.T) {
|
|
t.Setenv(cli.CatalogFixtureEnv, "")
|
|
t.Setenv(cli.CacheDirEnv, t.TempDir())
|
|
|
|
response := map[string]any{
|
|
"metadata": map[string]any{"count": 3, "nextCursor": ""},
|
|
"servers": []any{
|
|
discoveryServerEntry("doc", "文档管理", nil, map[string]any{
|
|
"search_docs": map[string]any{
|
|
"cliName": "search",
|
|
"flags": map[string]any{},
|
|
},
|
|
}),
|
|
discoveryServerEntry("chat", "聊天管理", map[string]any{
|
|
"message": map[string]any{"description": "消息管理"},
|
|
}, map[string]any{
|
|
"list_messages": map[string]any{
|
|
"cliName": "list",
|
|
"group": "message",
|
|
"flags": map[string]any{},
|
|
},
|
|
}),
|
|
discoveryServerEntry("minutes", "听记管理", map[string]any{
|
|
"list": map[string]any{"description": "列表"},
|
|
}, map[string]any{
|
|
"list_minutes_mine": map[string]any{
|
|
"cliName": "mine",
|
|
"group": "list",
|
|
"flags": map[string]any{},
|
|
},
|
|
}),
|
|
},
|
|
}
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
_ = json.NewEncoder(w).Encode(response)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
SetDiscoveryBaseURL(srv.URL)
|
|
t.Cleanup(func() { SetDiscoveryBaseURL("") })
|
|
|
|
root := NewRootCommand()
|
|
for _, path := range []string{
|
|
"doc upload",
|
|
"chat message list-topic-replies",
|
|
"minutes list all",
|
|
} {
|
|
if cmd := lookupCommand(root, path); cmd != nil {
|
|
t.Fatalf("findCommand(%q) = %q, want nil", path, cmd.CommandPath())
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDynamicLeafHelpDoesNotUsePatchedExamplesOrFlagText(t *testing.T) {
|
|
t.Setenv(cli.CatalogFixtureEnv, "")
|
|
t.Setenv(cli.CacheDirEnv, t.TempDir())
|
|
|
|
response := map[string]any{
|
|
"metadata": map[string]any{"count": 1, "nextCursor": ""},
|
|
"servers": []any{
|
|
discoveryServerEntry("aiapp", "AI应用管理", nil, map[string]any{
|
|
"create_ai_app": map[string]any{
|
|
"cliName": "create",
|
|
"flags": map[string]any{
|
|
"prompt": map[string]any{
|
|
"alias": "prompt",
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
}
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
_ = json.NewEncoder(w).Encode(response)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
SetDiscoveryBaseURL(srv.URL)
|
|
t.Cleanup(func() { SetDiscoveryBaseURL("") })
|
|
|
|
root := NewRootCommand()
|
|
var out bytes.Buffer
|
|
root.SetOut(&out)
|
|
root.SetErr(&out)
|
|
root.SetArgs([]string{"aiapp", "create", "--help"})
|
|
|
|
if err := root.Execute(); err != nil {
|
|
t.Fatalf("Execute(aiapp create --help) error = %v", err)
|
|
}
|
|
|
|
got := out.String()
|
|
if strings.Contains(got, "创建一个天气查询应用") {
|
|
t.Fatalf("leaf help still contains patched example:\n%s", got)
|
|
}
|
|
if strings.Contains(got, "创建 AI 应用的 prompt(必填)") {
|
|
t.Fatalf("leaf help still contains patched flag usage:\n%s", got)
|
|
}
|
|
if !strings.Contains(got, "--prompt string") {
|
|
t.Fatalf("leaf help missing dynamic prompt flag:\n%s", got)
|
|
}
|
|
}
|
|
|
|
func TestRootHelpUsesMCPOnlySummary(t *testing.T) {
|
|
t.Setenv(cli.CatalogFixtureEnv, "")
|
|
t.Setenv(cli.CacheDirEnv, t.TempDir())
|
|
|
|
response := map[string]any{
|
|
"metadata": map[string]any{"count": 2, "nextCursor": ""},
|
|
"servers": []any{
|
|
discoveryServerEntry("aiapp", "AI应用管理", nil, map[string]any{
|
|
"create_ai_app": map[string]any{
|
|
"cliName": "create",
|
|
"flags": map[string]any{},
|
|
},
|
|
}),
|
|
discoveryServerEntry("aitable", "多维表管理", nil, map[string]any{
|
|
"list_bases": map[string]any{
|
|
"cliName": "list",
|
|
"flags": map[string]any{},
|
|
},
|
|
}),
|
|
},
|
|
}
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
_ = json.NewEncoder(w).Encode(response)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
SetDiscoveryBaseURL(srv.URL)
|
|
t.Cleanup(func() { SetDiscoveryBaseURL("") })
|
|
|
|
root := NewRootCommand()
|
|
var out bytes.Buffer
|
|
root.SetOut(&out)
|
|
root.SetErr(&out)
|
|
root.SetArgs([]string{"--help"})
|
|
|
|
if err := root.Execute(); err != nil {
|
|
t.Fatalf("Execute(--help) error = %v", err)
|
|
}
|
|
|
|
got := out.String()
|
|
for _, want := range []string{"Discovered MCP Services:", "aiapp", "AI应用管理", "aitable", "多维表管理"} {
|
|
if !strings.Contains(got, want) {
|
|
t.Fatalf("root help missing %q:\n%s", want, got)
|
|
}
|
|
}
|
|
for _, unwanted := range []string{"快速开始:", "更多信息:", "auth 认证管理", "Flags:"} {
|
|
if strings.Contains(got, unwanted) {
|
|
t.Fatalf("root help unexpectedly contains %q:\n%s", unwanted, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRootHelpCustomizationDoesNotAffectSubcommandHelp(t *testing.T) {
|
|
t.Setenv(cli.CatalogFixtureEnv, "")
|
|
t.Setenv(cli.CacheDirEnv, t.TempDir())
|
|
|
|
response := map[string]any{
|
|
"metadata": map[string]any{"count": 1, "nextCursor": ""},
|
|
"servers": []any{
|
|
discoveryServerEntry("aiapp", "AI应用管理", nil, map[string]any{
|
|
"create_ai_app": map[string]any{
|
|
"cliName": "create",
|
|
"flags": map[string]any{
|
|
"prompt": map[string]any{
|
|
"alias": "prompt",
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
}
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
_ = json.NewEncoder(w).Encode(response)
|
|
}))
|
|
defer srv.Close()
|
|
|
|
SetDiscoveryBaseURL(srv.URL)
|
|
t.Cleanup(func() { SetDiscoveryBaseURL("") })
|
|
|
|
root := NewRootCommand()
|
|
var out bytes.Buffer
|
|
root.SetOut(&out)
|
|
root.SetErr(&out)
|
|
root.SetArgs([]string{"aiapp", "--help"})
|
|
|
|
if err := root.Execute(); err != nil {
|
|
t.Fatalf("Execute(aiapp --help) error = %v", err)
|
|
}
|
|
|
|
got := out.String()
|
|
if !strings.Contains(got, "Usage:") || !strings.Contains(got, "Available Commands:") || !strings.Contains(got, "Flags:") {
|
|
t.Fatalf("subcommand help should still use cobra default sections:\n%s", got)
|
|
}
|
|
if strings.Contains(got, "Discovered MCP Services:") {
|
|
t.Fatalf("subcommand help should not render root-only MCP summary:\n%s", got)
|
|
}
|
|
}
|
|
|
|
func TestRootCommandDoesNotRegisterUpgradeCommand(t *testing.T) {
|
|
root := NewRootCommand()
|
|
if cmd := lookupCommand(root, "upgrade"); cmd != nil {
|
|
t.Fatalf("findCommand(upgrade) = %q, want nil", cmd.CommandPath())
|
|
}
|
|
}
|
|
|
|
func discoveryServerEntry(command, description string, groups, toolOverrides map[string]any) map[string]any {
|
|
cliMeta := map[string]any{
|
|
"id": command,
|
|
"command": command,
|
|
"description": description,
|
|
"toolOverrides": toolOverrides,
|
|
}
|
|
if len(groups) > 0 {
|
|
cliMeta["groups"] = groups
|
|
}
|
|
|
|
return map[string]any{
|
|
"server": map[string]any{
|
|
"name": command,
|
|
"description": description,
|
|
"remotes": []any{
|
|
map[string]any{
|
|
"type": "streamable-http",
|
|
"url": "https://mcp.dingtalk.com/" + command,
|
|
},
|
|
},
|
|
},
|
|
"_meta": map[string]any{
|
|
"com.dingtalk.mcp.registry/metadata": map[string]any{
|
|
"status": "active",
|
|
"isLatest": true,
|
|
},
|
|
"com.dingtalk.mcp.registry/cli": cliMeta,
|
|
},
|
|
}
|
|
}
|
|
|
|
func lookupCommand(root *cobra.Command, path string) *cobra.Command {
|
|
if root == nil || path == "" {
|
|
return root
|
|
}
|
|
|
|
cmd := root
|
|
for _, part := range strings.Fields(path) {
|
|
found := false
|
|
for _, child := range cmd.Commands() {
|
|
if child.Name() == part {
|
|
cmd = child
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
return nil
|
|
}
|
|
}
|
|
return cmd
|
|
}
|