dify/dify-agent-runtime/cmd/dify-agent-cli/main_test.go
Yunlu Wen 302d7b1e1b
feat(agent): shellctl rewritten in go (#38841)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-07-15 01:58:36 +00:00

229 lines
7.1 KiB
Go

// Tests that `--help` renders correctly for every command and subcommand in
// the cobra tree, including the hidden singular `config skill|file` aliases.
// These lock the command surface so it stays identical to the original Python
// (Typer) CLI: each help output must show the command's usage path, its
// Short description, and its expected flags/subcommands.
package main
import (
"bytes"
"strings"
"testing"
"github.com/spf13/cobra"
)
// executeHelp builds a fresh root command, runs it with the given args, and
// returns the captured stdout/stderr. A fresh tree per call avoids leaking
// parsed flag state between cases.
func executeHelp(t *testing.T, args ...string) string {
t.Helper()
root := newRootCommand()
var buf bytes.Buffer
root.SetOut(&buf)
root.SetErr(&buf)
root.SetArgs(args)
if err := root.Execute(); err != nil {
t.Fatalf("execute %v: %v", args, err)
}
return buf.String()
}
func TestCommandHelp(t *testing.T) {
cases := []struct {
name string
args []string
want []string
}{
{
name: "root",
args: []string{"--help"},
want: []string{"Usage:", "dify-agent", "config", "connect", "drive", "file"},
},
{
name: "connect",
args: []string{"connect", "--help"},
want: []string{"dify-agent connect", "Establish one Agent Stub connection", "--json"},
},
{
name: "file",
args: []string{"file", "--help"},
want: []string{"dify-agent file", "upload", "download"},
},
{
name: "file upload",
args: []string{"file", "upload", "--help"},
want: []string{"dify-agent file upload", "Upload one sandbox-local file"},
},
{
name: "file download",
args: []string{"file", "download", "--help"},
want: []string{"dify-agent file download", "Download one workflow file", "--to"},
},
{
name: "drive",
args: []string{"drive", "--help"},
want: []string{"dify-agent drive", "list", "pull", "push"},
},
{
name: "drive list",
args: []string{"drive", "list", "--help"},
want: []string{"dify-agent drive list", "List drive files", "--json"},
},
{
name: "drive pull",
args: []string{"drive", "pull", "--help"},
want: []string{"dify-agent drive pull", "Pull one or more drive", "--to", "--json"},
},
{
name: "drive push",
args: []string{"drive", "push", "--help"},
want: []string{"dify-agent drive push", "Upload one local file or directory", "--kind", "--json"},
},
{
name: "config",
args: []string{"config", "--help"},
want: []string{"dify-agent config", "manifest", "skills", "files", "env", "note"},
},
{
name: "config manifest",
args: []string{"config", "manifest", "--help"},
want: []string{"dify-agent config manifest", "Show the current visible Agent config manifest"},
},
{
name: "config skills",
args: []string{"config", "skills", "--help"},
want: []string{"dify-agent config skills", "pull", "push", "delete"},
},
{
name: "config skills pull",
args: []string{"config", "skills", "pull", "--help"},
want: []string{"dify-agent config skills pull", "Pull one or all visible config skills", "--to", "--json"},
},
{
name: "config skills push",
args: []string{"config", "skills", "push", "--help"},
want: []string{"dify-agent config skills push", "Upload one or more local skill directories"},
},
{
name: "config skills delete",
args: []string{"config", "skills", "delete", "--help"},
want: []string{"dify-agent config skills delete", "Delete one or more config skills"},
},
{
name: "config files",
args: []string{"config", "files", "--help"},
want: []string{"dify-agent config files", "pull", "push", "delete"},
},
{
name: "config files pull",
args: []string{"config", "files", "pull", "--help"},
want: []string{"dify-agent config files pull", "Pull one or all visible config files", "--to", "--json"},
},
{
name: "config files push",
args: []string{"config", "files", "push", "--help"},
want: []string{"dify-agent config files push", "Upload one or more local files"},
},
{
name: "config files delete",
args: []string{"config", "files", "delete", "--help"},
want: []string{"dify-agent config files delete", "Delete one or more config files"},
},
{
name: "config env",
args: []string{"config", "env", "--help"},
want: []string{"dify-agent config env", "push"},
},
{
name: "config env push",
args: []string{"config", "env", "push", "--help"},
want: []string{"dify-agent config env push", "Set or delete config env entries"},
},
{
name: "config note",
args: []string{"config", "note", "--help"},
want: []string{"dify-agent config note", "pull", "push"},
},
{
name: "config note pull",
args: []string{"config", "note", "pull", "--help"},
want: []string{"dify-agent config note pull", "Export the current config note", "--to"},
},
{
name: "config note push",
args: []string{"config", "note", "push", "--help"},
want: []string{"dify-agent config note push", "Replace the current config note"},
},
{
name: "config skill pull (hidden alias)",
args: []string{"config", "skill", "pull", "--help"},
want: []string{"dify-agent config skill pull", "Pull one or all visible config skills", "--to", "--json"},
},
{
name: "config file pull (hidden alias)",
args: []string{"config", "file", "pull", "--help"},
want: []string{"dify-agent config file pull", "Pull one or all visible config files", "--to", "--json"},
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
out := executeHelp(t, tc.args...)
for _, want := range tc.want {
if !strings.Contains(out, want) {
t.Errorf("help for %v missing %q\n--- help output ---\n%s", tc.args, want, out)
}
}
})
}
}
// findCommand walks the cobra tree by command name (the first word of Use),
// returning nil when any path segment is missing.
func findCommand(root *cobra.Command, path ...string) *cobra.Command {
current := root
for _, name := range path {
var next *cobra.Command
for _, child := range current.Commands() {
if child.Name() == name {
next = child
break
}
}
if next == nil {
return nil
}
current = next
}
return current
}
// TestHiddenAliasesRemainHidden verifies the singular `config skill|file`
// aliases exist (so their pull help still renders) but stay hidden from the
// parent's command listing, matching the Python CLI's hidden aliases.
func TestHiddenAliasesRemainHidden(t *testing.T) {
root := newRootCommand()
for _, alias := range [][]string{{"config", "skill"}, {"config", "file"}} {
cmd := findCommand(root, alias...)
if cmd == nil {
t.Errorf("hidden alias %v not registered", alias)
continue
}
if !cmd.Hidden {
t.Errorf("alias %v must be hidden", alias)
}
if findCommand(cmd, "pull") == nil {
t.Errorf("alias %v must expose a pull subcommand", alias)
}
}
// The hidden singular aliases must not appear in `config --help`.
configHelp := executeHelp(t, "config", "--help")
for _, unwanted := range []string{" skill ", " file "} {
if strings.Contains(configHelp, unwanted) {
t.Errorf("config help unexpectedly lists hidden alias %q\n--- help output ---\n%s", unwanted, configHelp)
}
}
}