github.com/oam-dev/kubevela@v1.9.11/references/cli/def_test.go (about)

     1  /*
     2  Copyright 2021 The KubeVela Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package cli
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"fmt"
    23  	"io"
    24  	"os"
    25  	"path/filepath"
    26  	"strings"
    27  	"testing"
    28  	"time"
    29  
    30  	"github.com/spf13/cobra"
    31  	"github.com/stretchr/testify/assert"
    32  	"github.com/stretchr/testify/require"
    33  	"k8s.io/apimachinery/pkg/api/errors"
    34  	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    35  	"k8s.io/apimachinery/pkg/types"
    36  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    37  	"sigs.k8s.io/yaml"
    38  
    39  	common3 "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
    40  	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
    41  	pkgdef "github.com/oam-dev/kubevela/pkg/definition"
    42  	addonutil "github.com/oam-dev/kubevela/pkg/utils/addon"
    43  	common2 "github.com/oam-dev/kubevela/pkg/utils/common"
    44  	"github.com/oam-dev/kubevela/pkg/utils/util"
    45  )
    46  
    47  const (
    48  	// VelaTestNamespace namespace for hosting objects used during test
    49  	VelaTestNamespace = "vela-test-system"
    50  )
    51  
    52  func initArgs() common2.Args {
    53  	arg := common2.Args{}
    54  	arg.SetClient(fake.NewClientBuilder().WithScheme(common2.Scheme).Build())
    55  	return arg
    56  }
    57  
    58  func initCommand(cmd *cobra.Command) {
    59  	cmd.SilenceErrors = true
    60  	cmd.SilenceUsage = true
    61  	cmd.Flags().StringP("env", "", "", "")
    62  	cmd.SetOut(io.Discard)
    63  	cmd.SetErr(io.Discard)
    64  }
    65  
    66  func createTrait(c common2.Args, t *testing.T) string {
    67  	return createTraitWithOwnerAddon(c, "", t)
    68  }
    69  
    70  func createTraitWithOwnerAddon(c common2.Args, addonName string, t *testing.T) string {
    71  	traitName := fmt.Sprintf("my-trait-%d", time.Now().UnixNano())
    72  	createNamespacedTrait(c, traitName, VelaTestNamespace, addonName, t)
    73  	return traitName
    74  }
    75  
    76  func createNamespacedTrait(c common2.Args, name string, ns string, ownerAddon string, t *testing.T) {
    77  	traitName := fmt.Sprintf("my-trait-%d", time.Now().UnixNano())
    78  	client, err := c.GetClient()
    79  	if err != nil {
    80  		t.Fatalf("failed to get client: %v", err)
    81  	}
    82  	if err := client.Create(context.Background(), &v1beta1.TraitDefinition{
    83  		ObjectMeta: v1.ObjectMeta{
    84  			Name:      name,
    85  			Namespace: ns,
    86  			Annotations: map[string]string{
    87  				pkgdef.DescriptionKey: "My test-trait " + traitName,
    88  			},
    89  			OwnerReferences: []v1.OwnerReference{{
    90  				Name: addonutil.Addon2AppName(ownerAddon),
    91  			}},
    92  		},
    93  		Spec: v1beta1.TraitDefinitionSpec{
    94  			Schematic: &common3.Schematic{CUE: &common3.CUE{Template: "parameter: {}"}},
    95  		},
    96  	}); err != nil {
    97  		t.Fatalf("failed to create trait: %v", err)
    98  	}
    99  }
   100  
   101  func createLocalTraitAt(traitName string, localPath string, t *testing.T) string {
   102  	s := fmt.Sprintf(`// k8s metadata
   103  "%s": {
   104          type:   "trait"
   105          description: "My test-trait %s"
   106          attributes: {
   107                  appliesToWorkloads: ["webservice", "worker"]
   108                  podDisruptive: true
   109          }
   110  }
   111  
   112  // template
   113  template: {
   114          patch: {
   115                  spec: {
   116                          replicas: *1 | int
   117                  }
   118          }
   119          parameter: {
   120                  // +usage=Specify the number of workloads
   121                  replicas: *1 | int
   122          }
   123  }
   124  `, traitName, traitName)
   125  	filename := filepath.Join(localPath, traitName+".cue")
   126  	if err := os.WriteFile(filename, []byte(s), 0600); err != nil {
   127  		t.Fatalf("failed to write temp trait file %s: %v", filename, err)
   128  	}
   129  	return filename
   130  }
   131  
   132  func createLocalTrait(t *testing.T) (string, string) {
   133  	traitName := fmt.Sprintf("my-trait-%d", time.Now().UnixNano())
   134  	filename := createLocalTraitAt(traitName, os.TempDir(), t)
   135  	return traitName, filename
   136  }
   137  
   138  func createLocalTraits(t *testing.T) string {
   139  	dirname, err := os.MkdirTemp(os.TempDir(), "vela-def-test-*")
   140  	if err != nil {
   141  		t.Fatalf("failed to create temporary directory: %v", err)
   142  	}
   143  	for i := 0; i < 3; i++ {
   144  		createLocalTraitAt(fmt.Sprintf("trait-%d", i), dirname, t)
   145  	}
   146  	return dirname
   147  }
   148  
   149  func createLocalDeploymentYAML(t *testing.T) string {
   150  	s := `apiVersion: apps/v1
   151  kind: Deployment
   152  metadata:
   153    name: "main"
   154  Spec:
   155    image: "busybox"
   156  ---
   157  apiVersion: apps/v1
   158  kind: Deployment
   159  metadata:
   160    name: "secondary"
   161  Spec:
   162    image: "busybox"
   163  `
   164  	filename := filepath.Join(os.TempDir(), fmt.Sprintf("%d-deployments.yaml", time.Now().UnixNano()))
   165  	if err := os.WriteFile(filename, []byte(s), 0600); err != nil {
   166  		t.Fatalf("failed to create temp deployments file %s: %v", filename, err)
   167  	}
   168  	return filename
   169  }
   170  
   171  func removeFile(filename string, t *testing.T) {
   172  	if err := os.Remove(filename); err != nil {
   173  		t.Fatalf("failed to remove file %s: %v", filename, err)
   174  	}
   175  }
   176  
   177  func removeDir(dirname string, t *testing.T) {
   178  	if err := os.RemoveAll(dirname); err != nil {
   179  		t.Fatalf("failed to remove dir %s: %v", dirname, err)
   180  	}
   181  }
   182  
   183  func TestNewDefinitionCommandGroup(t *testing.T) {
   184  	cmd := DefinitionCommandGroup(common2.Args{}, "", util.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr})
   185  	initCommand(cmd)
   186  	cmd.SetArgs([]string{"-h"})
   187  	if err := cmd.Execute(); err != nil {
   188  		t.Fatalf("failed to execute definition command: %v", err)
   189  	}
   190  }
   191  
   192  func TestNewDefinitionInitCommand(t *testing.T) {
   193  	c := initArgs()
   194  	// test normal
   195  	cmd := NewDefinitionInitCommand(c)
   196  	initCommand(cmd)
   197  	cmd.SetArgs([]string{"my-ingress", "-t", "trait", "--desc", "test ingress"})
   198  	if err := cmd.Execute(); err != nil {
   199  		t.Fatalf("unexpected error when executing init command: %v", err)
   200  	}
   201  	// test interactive
   202  	cmd = NewDefinitionInitCommand(initArgs())
   203  	initCommand(cmd)
   204  	componentName := "my-webservice"
   205  	cmd.SetArgs([]string{componentName, "--interactive"})
   206  	templateFilename := createLocalDeploymentYAML(t)
   207  	filename := strings.Replace(templateFilename, ".yaml", ".cue", 1)
   208  	defer removeFile(templateFilename, t)
   209  	defer removeFile(filename, t)
   210  	inputs := fmt.Sprintf("comp\ncomponent\nMy webservice component.\n%s\n%s\n", templateFilename, filename)
   211  	reader := strings.NewReader(inputs)
   212  	cmd.SetIn(reader)
   213  	if err := cmd.Execute(); err != nil {
   214  		t.Fatalf("unexpeced error when executing init command interactively: %v", err)
   215  	}
   216  }
   217  
   218  func TestNewDefinitionInitCommand4Terraform(t *testing.T) {
   219  	const (
   220  		defVswitchFileName = "alibaba-vswitch.yaml"
   221  		defRedisFileName   = "tencent-redis.yaml"
   222  	)
   223  	testcases := []struct {
   224  		name   string
   225  		args   []string
   226  		output string
   227  		errMsg string
   228  		want   string
   229  	}{
   230  		{
   231  			name: "normal",
   232  			args: []string{"vswitch", "-t", "component", "--provider", "alibaba", "--desc", "xxx", "--git", "https://github.com/kubevela-contrib/terraform-modules.git", "--path", "alibaba/vswitch"},
   233  		},
   234  		{
   235  			name:   "normal from local",
   236  			args:   []string{"vswitch", "-t", "component", "--provider", "tencent", "--desc", "xxx", "--local", "test-data/redis.tf", "--output", defRedisFileName},
   237  			output: defRedisFileName,
   238  			want: `apiVersion: core.oam.dev/v1beta1
   239  kind: ComponentDefinition
   240  metadata:
   241    annotations:
   242      definition.oam.dev/description: xxx
   243    creationTimestamp: null
   244    labels:
   245      type: terraform
   246    name: tencent-vswitch
   247    namespace: vela-system
   248  spec:
   249    schematic:
   250      terraform:
   251        configuration: |
   252          terraform {
   253            required_providers {
   254              tencentcloud = {
   255                source = "tencentcloudstack/tencentcloud"
   256              }
   257            }
   258          }
   259  
   260          resource "tencentcloud_redis_instance" "main" {
   261            type_id           = 8
   262            availability_zone = var.availability_zone
   263            name              = var.instance_name
   264            password          = var.user_password
   265            mem_size          = var.mem_size
   266            port              = var.port
   267          }
   268  
   269          output "DB_IP" {
   270            value = tencentcloud_redis_instance.main.ip
   271          }
   272  
   273          output "DB_PASSWORD" {
   274            value = var.user_password
   275          }
   276  
   277          output "DB_PORT" {
   278            value = var.port
   279          }
   280  
   281          variable "availability_zone" {
   282            description = "The available zone ID of an instance to be created."
   283            type        = string
   284            default = "ap-chengdu-1"
   285          }
   286  
   287          variable "instance_name" {
   288            description = "redis instance name"
   289            type        = string
   290            default     = "sample"
   291          }
   292  
   293          variable "user_password" {
   294            description = "redis instance password"
   295            type        = string
   296            default     = "IEfewjf2342rfwfwYYfaked"
   297          }
   298  
   299          variable "mem_size" {
   300            description = "redis instance memory size"
   301            type        = number
   302            default     = 1024
   303          }
   304  
   305          variable "port" {
   306            description = "The port used to access a redis instance."
   307            type        = number
   308            default     = 6379
   309          }
   310        providerRef:
   311          name: tencent
   312          namespace: default
   313    workload:
   314      definition:
   315        apiVersion: terraform.core.oam.dev/v1beta2
   316        kind: Configuration
   317  status: {}
   318  `,
   319  		},
   320  		{
   321  			name:   "print in a file",
   322  			args:   []string{"vswitch", "-t", "component", "--provider", "alibaba", "--desc", "xxx", "--git", "https://github.com/kubevela-contrib/terraform-modules.git", "--path", "alibaba/vswitch", "--output", defVswitchFileName},
   323  			output: defVswitchFileName,
   324  			want: `apiVersion: core.oam.dev/v1beta1
   325  kind: ComponentDefinition
   326  metadata:
   327    annotations:
   328      definition.oam.dev/description: xxx
   329    creationTimestamp: null
   330    labels:
   331      type: terraform
   332    name: alibaba-vswitch
   333    namespace: vela-system
   334  spec:
   335    schematic:
   336      terraform:
   337        configuration: https://github.com/kubevela-contrib/terraform-modules.git
   338        path: alibaba/vswitch
   339        type: remote
   340    workload:
   341      definition:
   342        apiVersion: terraform.core.oam.dev/v1beta2
   343        kind: Configuration
   344  status: {}`,
   345  		},
   346  		{
   347  			name:   "not supported component",
   348  			args:   []string{"vswitch", "-t", "trait", "--provider", "alibaba"},
   349  			errMsg: "provider is only valid when the type of the definition is component",
   350  		},
   351  		{
   352  			name:   "not supported cloud provider",
   353  			args:   []string{"vswitch", "-t", "component", "--provider", "xxx"},
   354  			errMsg: "Provider `xxx` is not supported.",
   355  		},
   356  		{
   357  			name:   "git is not right",
   358  			args:   []string{"vswitch", "-t", "component", "--provider", "alibaba", "--desc", "test", "--git", "xxx"},
   359  			errMsg: "invalid git url",
   360  		},
   361  		{
   362  			name:   "git and local could be set at the same time",
   363  			args:   []string{"vswitch", "-t", "component", "--provider", "alibaba", "--desc", "test", "--git", "xxx", "--local", "yyy"},
   364  			errMsg: "only one of --git and --local can be set",
   365  		},
   366  		{
   367  			name:   "local file doesn't exist",
   368  			args:   []string{"vswitch", "-t", "component", "--provider", "tencent", "--desc", "xxx", "--local", "test-data/redis2.tf"},
   369  			errMsg: "failed to read Terraform configuration from file",
   370  		},
   371  	}
   372  
   373  	for _, tc := range testcases {
   374  		t.Run(tc.name, func(t *testing.T) {
   375  			c := initArgs()
   376  			cmd := NewDefinitionInitCommand(c)
   377  			initCommand(cmd)
   378  			cmd.SetArgs(tc.args)
   379  			err := cmd.Execute()
   380  			if err != nil && !strings.Contains(err.Error(), tc.errMsg) {
   381  				t.Fatalf("unexpected error when executing init command: %v", err)
   382  			} else if tc.want != "" {
   383  				data, err := os.ReadFile(tc.output)
   384  				defer os.Remove(tc.output)
   385  				assert.Nil(t, err)
   386  				if !strings.Contains(string(data), tc.want) {
   387  					t.Fatalf("unexpected output: %s", string(data))
   388  				}
   389  			}
   390  		})
   391  	}
   392  }
   393  
   394  func TestNewDefinitionGetCommand(t *testing.T) {
   395  	c := initArgs()
   396  
   397  	// normal test
   398  	cmd := NewDefinitionGetCommand(c)
   399  	initCommand(cmd)
   400  	traitName := createTrait(c, t)
   401  	cmd.SetArgs([]string{traitName, "-n" + VelaTestNamespace})
   402  	if err := cmd.Execute(); err != nil {
   403  		t.Fatalf("unexpeced error when executing get command: %v", err)
   404  	}
   405  	// test multi trait
   406  	cmd = NewDefinitionGetCommand(c)
   407  	initCommand(cmd)
   408  	createNamespacedTrait(c, traitName, "default", "", t)
   409  	cmd.SetArgs([]string{traitName})
   410  	if err := cmd.Execute(); err == nil {
   411  		t.Fatalf("expect found multiple traits error, but not found")
   412  	}
   413  	// test no trait
   414  	cmd = NewDefinitionGetCommand(c)
   415  	initCommand(cmd)
   416  	cmd.SetArgs([]string{traitName + "s"})
   417  	if err := cmd.Execute(); err == nil {
   418  		t.Fatalf("expect found no trait error, but not found")
   419  	}
   420  
   421  	// Load test DefinitionRevisions files into client
   422  	dir := filepath.Join("..", "..", "pkg", "definition", "testdata")
   423  	testFiles, err := os.ReadDir(dir)
   424  	assert.NoError(t, err, "read testdata failed")
   425  	for _, file := range testFiles {
   426  		if !strings.HasSuffix(file.Name(), ".yaml") {
   427  			continue
   428  		}
   429  		content, err := os.ReadFile(filepath.Join(dir, file.Name()))
   430  		assert.NoError(t, err)
   431  		def := &v1beta1.DefinitionRevision{}
   432  		err = yaml.Unmarshal(content, def)
   433  		assert.NoError(t, err)
   434  		client, err := c.GetClient()
   435  		assert.NoError(t, err)
   436  		err = client.Create(context.TODO(), def)
   437  		assert.NoError(t, err, "cannot create "+file.Name())
   438  	}
   439  
   440  	// test get revision list
   441  	cmd = NewDefinitionGetCommand(c)
   442  	initCommand(cmd)
   443  	cmd.SetArgs([]string{"webservice", "--revisions", "--namespace=rev-test-ns"})
   444  	err = cmd.Execute()
   445  	assert.NoError(t, err)
   446  
   447  	// test get a non-existent revision
   448  	cmd = NewDefinitionGetCommand(c)
   449  	initCommand(cmd)
   450  	cmd.SetArgs([]string{"webservice", "--revision=3"})
   451  	err = cmd.Execute()
   452  	assert.NotNil(t, err, "should have not found error")
   453  
   454  	// test get a revision
   455  	cmd = NewDefinitionGetCommand(c)
   456  	initCommand(cmd)
   457  	cmd.SetArgs([]string{"webservice", "--revision=1", "--namespace=rev-test-ns"})
   458  	err = cmd.Execute()
   459  	assert.NoError(t, err)
   460  }
   461  
   462  func TestNewDefinitionDocGenCommand(t *testing.T) {
   463  	c := initArgs()
   464  	cmd := NewDefinitionDocGenCommand(c, util.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr})
   465  	assert.NotNil(t, cmd.Execute())
   466  
   467  	cmd.SetArgs([]string{"alibaba-xxxxxxx"})
   468  	assert.NotNil(t, cmd.Execute())
   469  }
   470  
   471  func TestNewDefinitionListCommand(t *testing.T) {
   472  	c := initArgs()
   473  	// normal test
   474  	cmd := NewDefinitionListCommand(c)
   475  	initCommand(cmd)
   476  	_ = createTrait(c, t)
   477  	cmd.SetArgs([]string{})
   478  	if err := cmd.Execute(); err != nil {
   479  		t.Fatalf("unexpeced error when executing list command: %v", err)
   480  	}
   481  	// test no trait
   482  	cmd = NewDefinitionListCommand(c)
   483  	initCommand(cmd)
   484  	cmd.SetArgs([]string{"--namespace", "default"})
   485  	if err := cmd.Execute(); err != nil {
   486  		t.Fatalf("no trait found should not return error, err: %v", err)
   487  	}
   488  	// with addon filter
   489  	cmd = NewDefinitionListCommand(c)
   490  	initCommand(cmd)
   491  	cmd.SetArgs([]string{"--from", "non-existent-addon"})
   492  	if err := cmd.Execute(); err != nil {
   493  		t.Fatalf("applying addon filter should not return error, err: %v", err)
   494  	}
   495  }
   496  
   497  func TestNewDefinitionEditCommand(t *testing.T) {
   498  	c := initArgs()
   499  	// normal test
   500  	cmd := NewDefinitionEditCommand(c)
   501  	initCommand(cmd)
   502  	traitName := createTrait(c, t)
   503  	if err := os.Setenv("EDITOR", "sed -i -e 's/test-trait/TestTrait/g'"); err != nil {
   504  		t.Fatalf("failed to set editor env: %v", err)
   505  	}
   506  	cmd.SetArgs([]string{traitName, "-n", VelaTestNamespace})
   507  	if err := cmd.Execute(); err != nil {
   508  		t.Fatalf("unexpeced error when executing edit command: %v", err)
   509  	}
   510  	// test no change
   511  	cmd = NewDefinitionEditCommand(c)
   512  	initCommand(cmd)
   513  	createNamespacedTrait(c, traitName, "default", "", t)
   514  	if err := os.Setenv("EDITOR", "sed -i -e 's/test-trait-test/TestTrait/g'"); err != nil {
   515  		t.Fatalf("failed to set editor env: %v", err)
   516  	}
   517  	cmd.SetArgs([]string{traitName, "-n", "default"})
   518  	if err := cmd.Execute(); err != nil {
   519  		t.Fatalf("unexpeced error when executing edit command: %v", err)
   520  	}
   521  }
   522  
   523  func TestNewDefinitionRenderCommand(t *testing.T) {
   524  	c := initArgs()
   525  	// normal test
   526  	cmd := NewDefinitionRenderCommand(c)
   527  	initCommand(cmd)
   528  	_ = os.Setenv(HelmChartFormatEnvName, "true")
   529  	_, traitFilename := createLocalTrait(t)
   530  	defer removeFile(traitFilename, t)
   531  	cmd.SetArgs([]string{traitFilename})
   532  	if err := cmd.Execute(); err != nil {
   533  		t.Fatalf("unexpeced error when executing redner command: %v", err)
   534  	}
   535  	// directory read/write test
   536  	_ = os.Setenv(HelmChartFormatEnvName, "system")
   537  	dirname := createLocalTraits(t)
   538  	defer removeDir(dirname, t)
   539  	outputDir, err := os.MkdirTemp(os.TempDir(), "vela-def-tests-output-*")
   540  	if err != nil {
   541  		t.Fatalf("failed to create temporary output dir: %v", err)
   542  	}
   543  	defer removeDir(outputDir, t)
   544  	cmd = NewDefinitionRenderCommand(c)
   545  	initCommand(cmd)
   546  	cmd.SetArgs([]string{dirname, "-o", outputDir, "--message", "Author: KubeVela"})
   547  	if err := cmd.Execute(); err != nil {
   548  		t.Fatalf("unexpeced error when executing render command: %v", err)
   549  	}
   550  	// directory read/print test
   551  	_ = os.WriteFile(filepath.Join(dirname, "temp.json"), []byte("hello"), 0600)
   552  	_ = os.WriteFile(filepath.Join(dirname, "temp.cue"), []byte("hello"), 0600)
   553  	cmd = NewDefinitionRenderCommand(c)
   554  	initCommand(cmd)
   555  	cmd.SetArgs([]string{dirname})
   556  	if err := cmd.Execute(); err != nil {
   557  		t.Fatalf("unexpeced error when executing render command: %v", err)
   558  	}
   559  }
   560  
   561  func TestNewDefinitionApplyCommand(t *testing.T) {
   562  	c := initArgs()
   563  	ioStreams := util.IOStreams{In: os.Stdin, Out: bytes.NewBuffer(nil), ErrOut: bytes.NewBuffer(nil)}
   564  	// dry-run test
   565  	cmd := NewDefinitionApplyCommand(c, ioStreams)
   566  	initCommand(cmd)
   567  	_, traitFilename := createLocalTrait(t)
   568  	defer removeFile(traitFilename, t)
   569  	cmd.SetArgs([]string{traitFilename, "--dry-run"})
   570  	if err := cmd.Execute(); err != nil {
   571  		t.Fatalf("unexpeced error when executing apply command: %v", err)
   572  	}
   573  	// normal test and reapply
   574  	cmd = NewDefinitionApplyCommand(c, ioStreams)
   575  	initCommand(cmd)
   576  	cmd.SetArgs([]string{traitFilename})
   577  	for i := 0; i < 2; i++ {
   578  		if err := cmd.Execute(); err != nil {
   579  			t.Fatalf("unexpeced error when executing apply command: %v", err)
   580  		}
   581  	}
   582  }
   583  
   584  func TestNewDefinitionDelCommand(t *testing.T) {
   585  	c := initArgs()
   586  	cmd := NewDefinitionDelCommand(c)
   587  	initCommand(cmd)
   588  	traitName := createTrait(c, t)
   589  	reader := strings.NewReader("yes\n")
   590  	cmd.SetIn(reader)
   591  	cmd.SetArgs([]string{traitName, "-n", VelaTestNamespace})
   592  	if err := cmd.Execute(); err != nil {
   593  		t.Fatalf("unexpeced error when executing del command: %v", err)
   594  	}
   595  	obj := &v1beta1.TraitDefinition{}
   596  	client, err := c.GetClient()
   597  	if err != nil {
   598  		t.Fatalf("failed to get client: %v", err)
   599  	}
   600  	if err := client.Get(context.Background(), types.NamespacedName{
   601  		Namespace: VelaTestNamespace,
   602  		Name:      traitName,
   603  	}, obj); !errors.IsNotFound(err) {
   604  		t.Fatalf("should not found target definition %s, err: %v", traitName, err)
   605  	}
   606  	if err := cmd.Execute(); err == nil {
   607  		t.Fatalf("should encounter not found error, but no error found")
   608  	}
   609  }
   610  
   611  func TestNewDefinitionVetCommand(t *testing.T) {
   612  	c := initArgs()
   613  	cmd := NewDefinitionValidateCommand(c)
   614  	initCommand(cmd)
   615  	_, traitFilename := createLocalTrait(t)
   616  	_, traitFilename2 := createLocalTrait(t)
   617  	_, traitFilename3 := createLocalTrait(t)
   618  	defer removeFile(traitFilename, t)
   619  	cmd.SetArgs([]string{traitFilename})
   620  	if err := cmd.Execute(); err != nil {
   621  		t.Fatalf("unexpeced error when executing vet command: %v", err)
   622  	}
   623  	cmd.SetArgs([]string{traitFilename, traitFilename2, traitFilename3})
   624  	if err := cmd.Execute(); err != nil {
   625  		t.Fatalf("unexpeced error when executing vet command: %v", err)
   626  	}
   627  	bs, err := os.ReadFile(traitFilename)
   628  	if err != nil {
   629  		t.Fatalf("failed to read trait file %s: %v", traitFilename, err)
   630  	}
   631  	bs = []byte(string(bs) + "abc:{xa}")
   632  	if err = os.WriteFile(traitFilename, bs, 0600); err != nil {
   633  		t.Fatalf("failed to modify trait file %s: %v", traitFilename, err)
   634  	}
   635  	if err = cmd.Execute(); err == nil {
   636  		t.Fatalf("expect validation failed but error not found")
   637  	}
   638  	cmd.SetArgs([]string{traitFilename, traitFilename2, traitFilename3})
   639  	if err = cmd.Execute(); err == nil {
   640  		t.Fatalf("expect validation failed but error not found")
   641  	}
   642  	cmd.SetArgs([]string{"./test-data/defvet"})
   643  	if err = cmd.Execute(); err != nil {
   644  		t.Fatalf("unexpeced error when executing vet command: %v", err)
   645  	}
   646  }
   647  
   648  func TestNewDefinitionGenAPICommand(t *testing.T) {
   649  	c := initArgs()
   650  	cmd := NewDefinitionGenAPICommand(c)
   651  	initCommand(cmd)
   652  	internalDefPath := "../../vela-templates/definitions/internal/"
   653  
   654  	cmd.SetArgs([]string{"-f", internalDefPath, "-o", "../vela-sdk-gen", "--init", "--verbose"})
   655  	if err := cmd.Execute(); err != nil {
   656  		t.Fatalf("unexpeced error when executing genapi command: %v", err)
   657  	}
   658  }
   659  
   660  // re-use the provider testdata
   661  const providerTestDataPath = "../cuegen/generators/provider/testdata"
   662  
   663  func TestNewDefinitionGenCUECommand(t *testing.T) {
   664  	c := initArgs()
   665  	got := bytes.NewBuffer(nil)
   666  	cmd := NewDefinitionGenCUECommand(c, util.IOStreams{Out: got})
   667  	initCommand(cmd)
   668  
   669  	cmd.SetArgs([]string{
   670  		"-t", genTypeProvider,
   671  		"--types", "*k8s.io/apimachinery/pkg/apis/meta/v1/unstructured.Unstructured=ellipsis",
   672  		"--types", "*k8s.io/apimachinery/pkg/apis/meta/v1/unstructured.UnstructuredList=ellipsis",
   673  		filepath.Join(providerTestDataPath, "valid.go"),
   674  	})
   675  
   676  	require.NoError(t, cmd.Execute())
   677  
   678  	expected, err := os.ReadFile(filepath.Join(providerTestDataPath, "valid.cue"))
   679  	require.NoError(t, err)
   680  
   681  	assert.Equal(t, string(expected), got.String())
   682  }
   683  
   684  func TestNewDefinitionGenDocCommand(t *testing.T) {
   685  	c := initArgs()
   686  	got := bytes.NewBuffer(nil)
   687  	cmd := NewDefinitionGenDocCommand(c, util.IOStreams{Out: got})
   688  	initCommand(cmd)
   689  
   690  	cmd.SetArgs([]string{
   691  		"-t", genTypeProvider,
   692  		filepath.Join(providerTestDataPath, "valid.cue"),
   693  	})
   694  
   695  	require.NoError(t, cmd.Execute())
   696  
   697  	expected, err := os.ReadFile(filepath.Join(providerTestDataPath, "valid.md"))
   698  	require.NoError(t, err)
   699  
   700  	assert.Equal(t, string(expected), got.String())
   701  }