185 lines
6.3 KiB
Go
185 lines
6.3 KiB
Go
// Copyright 2026 Alibaba Group
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"flag"
|
|
"go/format"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/app"
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/cli"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
func TestParamAliasGeneratorUsesDistributionOwnedCommandTree(t *testing.T) {
|
|
got := reflect.ValueOf(newParamAliasRoot).Pointer()
|
|
want := reflect.ValueOf(app.NewSchemaSourceRootCommand).Pointer()
|
|
if got != want {
|
|
t.Fatal("parameter alias generator must use the distribution-owned Schema source command tree")
|
|
}
|
|
}
|
|
|
|
func preserveParamAliasGeneratorGlobals(t *testing.T) {
|
|
t.Helper()
|
|
oldArgs, oldFlags := os.Args, flag.CommandLine
|
|
oldRoot := newParamAliasRoot
|
|
oldReduce := reduceParamAliasEntries
|
|
oldFormat := formatParamAliasSource
|
|
oldWrite := writeParamAliasFile
|
|
oldExit := exitParamAliasProcess
|
|
t.Cleanup(func() {
|
|
os.Args, flag.CommandLine = oldArgs, oldFlags
|
|
newParamAliasRoot = oldRoot
|
|
reduceParamAliasEntries = oldReduce
|
|
formatParamAliasSource = oldFormat
|
|
writeParamAliasFile = oldWrite
|
|
exitParamAliasProcess = oldExit
|
|
})
|
|
}
|
|
|
|
func repositoryRoot(t *testing.T) string {
|
|
t.Helper()
|
|
root, err := filepath.Abs(filepath.Join("..", "..", ".."))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return root
|
|
}
|
|
|
|
func TestCrossPlatformCoverageParamAliasMainWritesGeneratedFile(t *testing.T) {
|
|
preserveParamAliasGeneratorGlobals(t)
|
|
output := filepath.Join(t.TempDir(), "param_aliases_generated.go")
|
|
flag.CommandLine = flag.NewFlagSet("param-alias-success", flag.ContinueOnError)
|
|
os.Args = []string{"cmd_param_aliases", "-root", repositoryRoot(t), "-output", output}
|
|
newParamAliasRoot = func(...context.Context) *cobra.Command { return &cobra.Command{Use: "dws"} }
|
|
reduceParamAliasEntries = func(*cobra.Command) ([]cli.ParamAliasEntry, error) {
|
|
return []cli.ParamAliasEntry{{CLIPath: "demo get", Aliases: map[string]string{"uid": "user"}}}, nil
|
|
}
|
|
|
|
main()
|
|
data, err := os.ReadFile(output)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(string(data), "func loadGeneratedParamAliases() []ParamAliasEntry") ||
|
|
!strings.Contains(string(data), `"uid": "user"`) {
|
|
t.Fatalf("generated source missing runtime table:\n%s", data)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageParamAliasMainReportsFailure(t *testing.T) {
|
|
preserveParamAliasGeneratorGlobals(t)
|
|
flag.CommandLine = flag.NewFlagSet("param-alias-failure", flag.ContinueOnError)
|
|
os.Args = []string{
|
|
"cmd_param_aliases",
|
|
"-root", repositoryRoot(t),
|
|
"-output", filepath.Join(repositoryRoot(t), "internal", "cli", "param_concepts.json"),
|
|
}
|
|
exitParamAliasProcess = func(code int) { panic(code) }
|
|
defer func() {
|
|
if recovered := recover(); recovered != 1 {
|
|
t.Fatalf("main exit = %#v, want 1", recovered)
|
|
}
|
|
}()
|
|
main()
|
|
}
|
|
|
|
func TestGenerateParamAliasesFailurePaths(t *testing.T) {
|
|
preserveParamAliasGeneratorGlobals(t)
|
|
root := repositoryRoot(t)
|
|
output := filepath.Join(t.TempDir(), "param_aliases_generated.go")
|
|
newParamAliasRoot = func(...context.Context) *cobra.Command { return &cobra.Command{Use: "dws"} }
|
|
|
|
if err := generateParamAliases(root, filepath.Join(root, "internal", "cli", "param_concepts.json")); err == nil {
|
|
t.Fatal("generateParamAliases accepted an output that overlaps a reviewed input")
|
|
}
|
|
|
|
reduceParamAliasEntries = func(*cobra.Command) ([]cli.ParamAliasEntry, error) {
|
|
return nil, errors.New("reduce")
|
|
}
|
|
if err := generateParamAliases(root, output); err == nil || !strings.Contains(err.Error(), "reduce") {
|
|
t.Fatalf("reduction error = %v", err)
|
|
}
|
|
|
|
reduceParamAliasEntries = func(*cobra.Command) ([]cli.ParamAliasEntry, error) { return nil, nil }
|
|
formatParamAliasSource = func([]byte) ([]byte, error) { return nil, errors.New("format") }
|
|
if err := generateParamAliases(root, output); err == nil || !strings.Contains(err.Error(), "format generated") {
|
|
t.Fatalf("format error = %v", err)
|
|
}
|
|
|
|
formatParamAliasSource = format.Source
|
|
writeParamAliasFile = func(string, []byte, os.FileMode) error { return errors.New("write") }
|
|
if err := generateParamAliases(root, output); err == nil || !strings.Contains(err.Error(), "write generated") {
|
|
t.Fatalf("write error = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestValidateParamAliasOutputIsolation(t *testing.T) {
|
|
root := repositoryRoot(t)
|
|
if err := validateOutputIsolation(root, filepath.Join(t.TempDir(), "aliases.go")); err != nil {
|
|
t.Fatalf("temporary output rejected: %v", err)
|
|
}
|
|
if err := validateOutputIsolation(root, filepath.Join(root, "internal", "cli", "not_a_delivery_target.go")); err == nil ||
|
|
!strings.Contains(err.Error(), "not a canonical generated delivery target") {
|
|
t.Fatalf("non-canonical repository output error = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestRenderParamAliasesDeterministicShape(t *testing.T) {
|
|
preserveParamAliasGeneratorGlobals(t)
|
|
entries := []cli.ParamAliasEntry{
|
|
{
|
|
CLIPath: "demo get",
|
|
Aliases: map[string]string{"z-name": "name", "a-name": "name"},
|
|
Blocked: []string{"page", "count"},
|
|
Ambiguous: []string{"user-id"},
|
|
},
|
|
{CLIPath: "demo empty"},
|
|
}
|
|
data, err := renderParamAliases(entries)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
text := string(data)
|
|
if strings.Index(text, `"a-name": "name"`) > strings.Index(text, `"z-name": "name"`) {
|
|
t.Fatalf("alias keys are not sorted:\n%s", text)
|
|
}
|
|
for _, want := range []string{
|
|
"func loadGeneratedParamAliases() []ParamAliasEntry",
|
|
`Blocked: []string{"page", "count"}`,
|
|
`Ambiguous: []string{"user-id"}`,
|
|
`CLIPath: "demo empty"`,
|
|
} {
|
|
if !strings.Contains(text, want) {
|
|
t.Fatalf("generated source missing %q:\n%s", want, text)
|
|
}
|
|
}
|
|
|
|
empty, err := renderParamAliases(nil)
|
|
if err != nil || !strings.Contains(string(empty), "var generatedParamAliases = []ParamAliasEntry{") {
|
|
t.Fatalf("empty render = %q, error = %v", empty, err)
|
|
}
|
|
if got := renderStringSlice(nil); got != "[]string{}" {
|
|
t.Fatalf("renderStringSlice(nil) = %q", got)
|
|
}
|
|
if got := sortedKeys(nil); len(got) != 0 {
|
|
t.Fatalf("sortedKeys(nil) = %v", got)
|
|
}
|
|
if got := sortedKeys(map[string]string{"b": "2", "a": "1"}); !reflect.DeepEqual(got, []string{"a", "b"}) {
|
|
t.Fatalf("sortedKeys() = %v", got)
|
|
}
|
|
|
|
formatParamAliasSource = func([]byte) ([]byte, error) { return nil, errors.New("broken formatter") }
|
|
if _, err := renderParamAliases(entries); err == nil || !strings.Contains(err.Error(), "broken formatter") {
|
|
t.Fatalf("formatter error = %v", err)
|
|
}
|
|
}
|