From f519dda85d7f3e138e6058558245721a3ac27b89 Mon Sep 17 00:00:00 2001 From: healthjyk Date: Thu, 21 Mar 2024 11:13:16 +0800 Subject: [PATCH] feat: add config cmd, including subcmds get,list,set,unset --- go.mod | 2 +- go.sum | 2 + pkg/cmd/config/cmd.go | 39 +++++++ pkg/cmd/config/cmd_test.go | 14 +++ pkg/cmd/config/get/cmd.go | 39 +++++++ pkg/cmd/config/get/cmd_test.go | 24 +++++ pkg/cmd/config/get/options.go | 42 ++++++++ pkg/cmd/config/get/options_test.go | 100 +++++++++++++++++ pkg/cmd/config/list/cmd.go | 60 +++++++++++ pkg/cmd/config/list/cmd_test.go | 72 +++++++++++++ pkg/cmd/config/set/cmd.go | 45 ++++++++ pkg/cmd/config/set/cmd_test.go | 25 +++++ pkg/cmd/config/set/options.go | 46 ++++++++ pkg/cmd/config/set/options_test.go | 112 ++++++++++++++++++++ pkg/cmd/config/unset/cmd.go | 39 +++++++ pkg/cmd/config/unset/cmd_test.go | 24 +++++ pkg/cmd/config/unset/options.go | 41 +++++++ pkg/cmd/config/unset/options_test.go | 100 +++++++++++++++++ pkg/cmd/config/util/util.go | 53 ++++++++++ pkg/cmd/config/util/util_test.go | 153 +++++++++++++++++++++++++++ pkg/cmd/workspace/util/util_test.go | 2 - 21 files changed, 1031 insertions(+), 3 deletions(-) create mode 100644 pkg/cmd/config/cmd.go create mode 100644 pkg/cmd/config/cmd_test.go create mode 100644 pkg/cmd/config/get/cmd.go create mode 100644 pkg/cmd/config/get/cmd_test.go create mode 100644 pkg/cmd/config/get/options.go create mode 100644 pkg/cmd/config/get/options_test.go create mode 100644 pkg/cmd/config/list/cmd.go create mode 100644 pkg/cmd/config/list/cmd_test.go create mode 100644 pkg/cmd/config/set/cmd.go create mode 100644 pkg/cmd/config/set/cmd_test.go create mode 100644 pkg/cmd/config/set/options.go create mode 100644 pkg/cmd/config/set/options_test.go create mode 100644 pkg/cmd/config/unset/cmd.go create mode 100644 pkg/cmd/config/unset/cmd_test.go create mode 100644 pkg/cmd/config/unset/options.go create mode 100644 pkg/cmd/config/unset/options_test.go create mode 100644 pkg/cmd/config/util/util.go create mode 100644 pkg/cmd/config/util/util_test.go diff --git a/go.mod b/go.mod index ed6c20bd..56a8989d 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ require ( github.com/evanphx/json-patch v4.12.0+incompatible github.com/fluxcd/pkg/sourceignore v0.5.0 github.com/fluxcd/pkg/tar v0.4.0 + github.com/go-git/go-git/v5 v5.11.0 github.com/go-sql-driver/mysql v1.7.0 github.com/go-test/deep v1.0.3 github.com/goccy/go-yaml v1.11.0 @@ -95,7 +96,6 @@ require ( github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/deckarep/golang-set v1.7.1 // indirect github.com/go-errors/errors v1.4.2 // indirect - github.com/go-git/go-git/v5 v5.11.0 // indirect github.com/google/btree v1.0.1 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect diff --git a/go.sum b/go.sum index e5c2c60e..bdeddd96 100644 --- a/go.sum +++ b/go.sum @@ -720,6 +720,8 @@ github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhso github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= +github.com/tweekmonster/luser v0.0.0-20161003172636-3fa38070dbd7 h1:X9dsIWPuuEJlPX//UmRKophhOKCGXc46RVIGuttks68= +github.com/tweekmonster/luser v0.0.0-20161003172636-3fa38070dbd7/go.mod h1:UxoP3EypF8JfGEjAII8jx1q8rQyDnX8qdTCs/UQBVIE= github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RVck= github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY= diff --git a/pkg/cmd/config/cmd.go b/pkg/cmd/config/cmd.go new file mode 100644 index 00000000..32971405 --- /dev/null +++ b/pkg/cmd/config/cmd.go @@ -0,0 +1,39 @@ +package config + +import ( + "github.com/spf13/cobra" + "k8s.io/kubectl/pkg/util/templates" + + "kusionstack.io/kusion/pkg/cmd/config/get" + "kusionstack.io/kusion/pkg/cmd/config/list" + "kusionstack.io/kusion/pkg/cmd/config/set" + "kusionstack.io/kusion/pkg/cmd/config/unset" + "kusionstack.io/kusion/pkg/util/i18n" +) + +func NewCmd() *cobra.Command { + var ( + short = i18n.T(`Interact with the Kusion config`) + + long = i18n.T(` + Config contains the operation of Kusion configurations.`) + ) + + cmd := &cobra.Command{ + Use: "config", + Short: short, + Long: templates.LongDesc(long), + SilenceErrors: true, + RunE: func(cmd *cobra.Command, _ []string) error { + return cmd.Help() + }, + } + + getCmd := get.NewCmd() + listCmd := list.NewCmd() + setCmd := set.NewCmd() + unsetCmd := unset.NewCmd() + cmd.AddCommand(getCmd, listCmd, setCmd, unsetCmd) + + return cmd +} diff --git a/pkg/cmd/config/cmd_test.go b/pkg/cmd/config/cmd_test.go new file mode 100644 index 00000000..c9f727d9 --- /dev/null +++ b/pkg/cmd/config/cmd_test.go @@ -0,0 +1,14 @@ +package config + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewCmd(t *testing.T) { + t.Run("successfully get config help", func(t *testing.T) { + cmd := NewCmd() + assert.NotNil(t, cmd) + }) +} diff --git a/pkg/cmd/config/get/cmd.go b/pkg/cmd/config/get/cmd.go new file mode 100644 index 00000000..f33241c1 --- /dev/null +++ b/pkg/cmd/config/get/cmd.go @@ -0,0 +1,39 @@ +package get + +import ( + "github.com/spf13/cobra" + "k8s.io/kubectl/pkg/util/templates" + + "kusionstack.io/kusion/pkg/cmd/util" + "kusionstack.io/kusion/pkg/util/i18n" +) + +func NewCmd() *cobra.Command { + var ( + short = i18n.T(`Get a config item`) + + long = i18n.T(` + This command gets the value of a specified kusion config item, where the config item must be registered.`) + + example = i18n.T(` + # Get a config item + kusion config get backends.current`) + ) + + o := NewOptions() + cmd := &cobra.Command{ + Use: "get", + Short: short, + Long: templates.LongDesc(long), + Example: templates.Examples(example), + DisableFlagsInUseLine: true, + RunE: func(cmd *cobra.Command, args []string) (err error) { + defer util.RecoverErr(&err) + util.CheckErr(o.Complete(args)) + util.CheckErr(o.Validate()) + util.CheckErr(o.Run()) + return + }, + } + return cmd +} diff --git a/pkg/cmd/config/get/cmd_test.go b/pkg/cmd/config/get/cmd_test.go new file mode 100644 index 00000000..9e1374f7 --- /dev/null +++ b/pkg/cmd/config/get/cmd_test.go @@ -0,0 +1,24 @@ +package get + +import ( + "testing" + + "github.com/bytedance/mockey" + "github.com/stretchr/testify/assert" +) + +func TestNewCmd(t *testing.T) { + t.Run("successfully get config item", func(t *testing.T) { + mockey.PatchConvey("mock cmd", t, func() { + mockey.Mock((*Options).Complete).To(func(o *Options, args []string) error { + o.Item = "backends.current" + return nil + }).Build() + mockey.Mock((*Options).Run).Return(nil).Build() + + cmd := NewCmd() + err := cmd.Execute() + assert.Nil(t, err) + }) + }) +} diff --git a/pkg/cmd/config/get/options.go b/pkg/cmd/config/get/options.go new file mode 100644 index 00000000..d3f2d3b9 --- /dev/null +++ b/pkg/cmd/config/get/options.go @@ -0,0 +1,42 @@ +package get + +import ( + "fmt" + + "kusionstack.io/kusion/pkg/cmd/config/util" + "kusionstack.io/kusion/pkg/config" +) + +type Options struct { + Item string +} + +func NewOptions() *Options { + return &Options{} +} + +func (o *Options) Complete(args []string) error { + item, err := util.GetItemFromArgs(args) + if err != nil { + return err + } + o.Item = item + return nil +} + +func (o *Options) Validate() error { + if err := util.ValidateItem(o.Item); err != nil { + return err + } + return nil +} + +func (o *Options) Run() error { + val, err := config.GetEncodedConfigItem(o.Item) + if err != nil { + return err + } + + fmt.Print(val) + return nil +} diff --git a/pkg/cmd/config/get/options_test.go b/pkg/cmd/config/get/options_test.go new file mode 100644 index 00000000..909dfd4d --- /dev/null +++ b/pkg/cmd/config/get/options_test.go @@ -0,0 +1,100 @@ +package get + +import ( + "testing" + + "github.com/bytedance/mockey" + "github.com/stretchr/testify/assert" + + "kusionstack.io/kusion/pkg/config" +) + +func TestOptions_Complete(t *testing.T) { + testcases := []struct { + name string + args []string + success bool + expectedOpts *Options + }{ + { + name: "successfully complete options", + args: []string{"backends.current"}, + success: true, + expectedOpts: &Options{ + Item: "backends.current", + }, + }, + { + name: "complete field invalid args", + args: nil, + success: false, + expectedOpts: nil, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + opts := NewOptions() + err := opts.Complete(tc.args) + assert.Equal(t, tc.success, err == nil) + if tc.success { + assert.Equal(t, tc.expectedOpts, opts) + } + }) + } +} + +func TestOptions_Validate(t *testing.T) { + testcases := []struct { + name string + opts *Options + success bool + }{ + { + name: "valid options", + opts: &Options{ + Item: "backends.current", + }, + success: true, + }, + { + name: "invalid options empty config item", + opts: &Options{}, + success: false, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + err := tc.opts.Validate() + assert.Equal(t, tc.success, err == nil) + }) + } +} + +func TestOptions_Run(t *testing.T) { + testcases := []struct { + name string + opts *Options + success bool + }{ + { + name: "successfully run", + opts: &Options{ + Item: "backends.current", + }, + success: true, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + mockey.PatchConvey("mock get config item", t, func() { + mockey.Mock(config.GetEncodedConfigItem).Return("", nil).Build() + + err := tc.opts.Run() + assert.Equal(t, tc.success, err == nil) + }) + }) + } +} diff --git a/pkg/cmd/config/list/cmd.go b/pkg/cmd/config/list/cmd.go new file mode 100644 index 00000000..d6b659ee --- /dev/null +++ b/pkg/cmd/config/list/cmd.go @@ -0,0 +1,60 @@ +package list + +import ( + "fmt" + + "github.com/spf13/cobra" + "gopkg.in/yaml.v3" + "k8s.io/kubectl/pkg/util/templates" + + configutil "kusionstack.io/kusion/pkg/cmd/config/util" + "kusionstack.io/kusion/pkg/cmd/util" + "kusionstack.io/kusion/pkg/config" + "kusionstack.io/kusion/pkg/util/i18n" +) + +func NewCmd() *cobra.Command { + var ( + short = i18n.T(`List all config items`) + + long = i18n.T(` + This command lists all the kusion config items and their values.`) + + example = i18n.T(` + # List config items + kusion config list`) + ) + + cmd := &cobra.Command{ + Use: "list", + Short: short, + Long: templates.LongDesc(long), + Example: templates.Examples(example), + DisableFlagsInUseLine: true, + RunE: func(cmd *cobra.Command, args []string) (err error) { + defer util.RecoverErr(&err) + util.CheckErr(Validate(args)) + util.CheckErr(Run()) + return + }, + } + return cmd +} + +func Validate(args []string) error { + return configutil.ValidateNoArg(args) +} + +func Run() error { + cfg, err := config.GetConfig() + if err != nil { + return err + } + + content, err := yaml.Marshal(cfg) + if err != nil { + return fmt.Errorf("yaml marshal config configuration failed: %w", err) + } + fmt.Print(string(content)) + return nil +} diff --git a/pkg/cmd/config/list/cmd_test.go b/pkg/cmd/config/list/cmd_test.go new file mode 100644 index 00000000..1f1a451a --- /dev/null +++ b/pkg/cmd/config/list/cmd_test.go @@ -0,0 +1,72 @@ +package list + +import ( + "testing" + + "github.com/bytedance/mockey" + "github.com/stretchr/testify/assert" + + v1 "kusionstack.io/kusion/pkg/apis/core/v1" + "kusionstack.io/kusion/pkg/config" +) + +func TestNewCmd(t *testing.T) { + t.Run("successfully list configs", func(t *testing.T) { + mockey.PatchConvey("mock cmd", t, func() { + mockey.Mock(Run).Return(nil).Build() + + cmd := NewCmd() + err := cmd.Execute() + assert.Nil(t, err) + }) + }) +} + +func TestValidate(t *testing.T) { + testcases := []struct { + name string + args []string + success bool + }{ + { + name: "valid args", + args: nil, + success: true, + }, + { + name: "invalid args not empty", + args: []string{"invalid"}, + success: false, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + err := Validate(tc.args) + assert.Equal(t, tc.success, err == nil) + }) + } +} + +func TestRun(t *testing.T) { + testcases := []struct { + name string + success bool + }{ + { + name: "successfully run", + success: true, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + mockey.PatchConvey("mock get config", t, func() { + mockey.Mock(config.GetConfig).Return(&v1.Config{}, nil).Build() + + err := Run() + assert.Equal(t, tc.success, err == nil) + }) + }) + } +} diff --git a/pkg/cmd/config/set/cmd.go b/pkg/cmd/config/set/cmd.go new file mode 100644 index 00000000..61b7a78b --- /dev/null +++ b/pkg/cmd/config/set/cmd.go @@ -0,0 +1,45 @@ +package set + +import ( + "github.com/spf13/cobra" + "k8s.io/kubectl/pkg/util/templates" + + "kusionstack.io/kusion/pkg/cmd/util" + "kusionstack.io/kusion/pkg/util/i18n" +) + +func NewCmd() *cobra.Command { + var ( + short = i18n.T(`Set a config item`) + + long = i18n.T(` + This command sets the value of a specified kusion config item, where the config item must be registered, and the value must be in valid type.`) + + example = i18n.T(` + # Set a config item with string type value + kusion config set backends.current mysql-pre + + # Set a config item with int type value + kusion config set backends.mysql-pre.configs.port 3306 + + # Set a config item with struct or map type value + kusion config set backends.mysql-pre.configs '{"dbName":"kusion","user":"kk","host":"127.0.0.1","port":3306}'`) + ) + + o := NewOptions() + cmd := &cobra.Command{ + Use: "set", + Short: short, + Long: templates.LongDesc(long), + Example: templates.Examples(example), + DisableFlagsInUseLine: true, + RunE: func(cmd *cobra.Command, args []string) (err error) { + defer util.RecoverErr(&err) + util.CheckErr(o.Complete(args)) + util.CheckErr(o.Validate()) + util.CheckErr(o.Run()) + return + }, + } + return cmd +} diff --git a/pkg/cmd/config/set/cmd_test.go b/pkg/cmd/config/set/cmd_test.go new file mode 100644 index 00000000..c0bdf3fa --- /dev/null +++ b/pkg/cmd/config/set/cmd_test.go @@ -0,0 +1,25 @@ +package set + +import ( + "testing" + + "github.com/bytedance/mockey" + "github.com/stretchr/testify/assert" +) + +func TestNewCmd(t *testing.T) { + t.Run("successfully set config item", func(t *testing.T) { + mockey.PatchConvey("mock cmd", t, func() { + mockey.Mock((*Options).Complete).To(func(o *Options, args []string) error { + o.Item = "backends.current" + o.Value = "mysql-pre" + return nil + }).Build() + mockey.Mock((*Options).Run).Return(nil).Build() + + cmd := NewCmd() + err := cmd.Execute() + assert.Nil(t, err) + }) + }) +} diff --git a/pkg/cmd/config/set/options.go b/pkg/cmd/config/set/options.go new file mode 100644 index 00000000..086e7af3 --- /dev/null +++ b/pkg/cmd/config/set/options.go @@ -0,0 +1,46 @@ +package set + +import ( + "fmt" + + "kusionstack.io/kusion/pkg/cmd/config/util" + "kusionstack.io/kusion/pkg/config" +) + +type Options struct { + Item string + Value string +} + +func NewOptions() *Options { + return &Options{} +} + +func (o *Options) Complete(args []string) error { + item, value, err := util.GetItemValueFromArgs(args) + if err != nil { + return err + } + o.Item = item + o.Value = value + return nil +} + +func (o *Options) Validate() error { + if err := util.ValidateItem(o.Item); err != nil { + return err + } + if err := util.ValidateValue(o.Value); err != nil { + return err + } + return nil +} + +func (o *Options) Run() error { + if err := config.SetEncodedConfigItem(o.Item, o.Value); err != nil { + return err + } + + fmt.Printf("set config item %s successfully", o.Item) + return nil +} diff --git a/pkg/cmd/config/set/options_test.go b/pkg/cmd/config/set/options_test.go new file mode 100644 index 00000000..001628b8 --- /dev/null +++ b/pkg/cmd/config/set/options_test.go @@ -0,0 +1,112 @@ +package set + +import ( + "testing" + + "github.com/bytedance/mockey" + "github.com/stretchr/testify/assert" + + "kusionstack.io/kusion/pkg/config" +) + +func TestOptions_Complete(t *testing.T) { + testcases := []struct { + name string + args []string + success bool + expectedOpts *Options + }{ + { + name: "successfully complete options", + args: []string{"backends.current", "mysql-pre"}, + success: true, + expectedOpts: &Options{ + Item: "backends.current", + Value: "mysql-pre", + }, + }, + { + name: "complete field invalid args", + args: []string{"backends.current"}, + success: false, + expectedOpts: nil, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + opts := NewOptions() + err := opts.Complete(tc.args) + assert.Equal(t, tc.success, err == nil) + if tc.success { + assert.Equal(t, tc.expectedOpts, opts) + } + }) + } +} + +func TestOptions_Validate(t *testing.T) { + testcases := []struct { + name string + opts *Options + success bool + }{ + { + name: "valid options", + opts: &Options{ + Item: "backends.current", + Value: "mysql-pre", + }, + success: true, + }, + { + name: "invalid options empty config item", + opts: &Options{ + Value: "mysql-pre", + }, + success: false, + }, + { + name: "invalid options empty config value", + opts: &Options{ + Item: "backends.current", + }, + success: false, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + err := tc.opts.Validate() + assert.Equal(t, tc.success, err == nil) + }) + } +} + +func TestOptions_Run(t *testing.T) { + testcases := []struct { + name string + opts *Options + success bool + }{ + { + name: "successfully run", + opts: &Options{ + Item: "backends.current", + Value: "mysql-pre", + }, + success: true, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + mockey.PatchConvey("mock set config item", t, func() { + mockey.Mock(config.SetEncodedConfigItem).Return(nil).Build() + + err := tc.opts.Run() + assert.Equal(t, tc.success, err == nil) + }) + }) + } +} diff --git a/pkg/cmd/config/unset/cmd.go b/pkg/cmd/config/unset/cmd.go new file mode 100644 index 00000000..6d6cfcb6 --- /dev/null +++ b/pkg/cmd/config/unset/cmd.go @@ -0,0 +1,39 @@ +package unset + +import ( + "github.com/spf13/cobra" + "k8s.io/kubectl/pkg/util/templates" + + "kusionstack.io/kusion/pkg/cmd/util" + "kusionstack.io/kusion/pkg/util/i18n" +) + +func NewCmd() *cobra.Command { + var ( + short = i18n.T(`Unset a config item`) + + long = i18n.T(` + This command unsets a specified kusion config item, where the config item must be registered.`) + + example = i18n.T(` + # Unset a config item + kusion config unset backends.mysql-pre.configs.port`) + ) + + o := NewOptions() + cmd := &cobra.Command{ + Use: "unset", + Short: short, + Long: templates.LongDesc(long), + Example: templates.Examples(example), + DisableFlagsInUseLine: true, + RunE: func(cmd *cobra.Command, args []string) (err error) { + defer util.RecoverErr(&err) + util.CheckErr(o.Complete(args)) + util.CheckErr(o.Validate()) + util.CheckErr(o.Run()) + return + }, + } + return cmd +} diff --git a/pkg/cmd/config/unset/cmd_test.go b/pkg/cmd/config/unset/cmd_test.go new file mode 100644 index 00000000..7d73c566 --- /dev/null +++ b/pkg/cmd/config/unset/cmd_test.go @@ -0,0 +1,24 @@ +package unset + +import ( + "testing" + + "github.com/bytedance/mockey" + "github.com/stretchr/testify/assert" +) + +func TestNewCmd(t *testing.T) { + t.Run("successfully unset config item", func(t *testing.T) { + mockey.PatchConvey("mock cmd", t, func() { + mockey.Mock((*Options).Complete).To(func(o *Options, args []string) error { + o.Item = "backends.mysql-pre.configs.port" + return nil + }).Build() + mockey.Mock((*Options).Run).Return(nil).Build() + + cmd := NewCmd() + err := cmd.Execute() + assert.Nil(t, err) + }) + }) +} diff --git a/pkg/cmd/config/unset/options.go b/pkg/cmd/config/unset/options.go new file mode 100644 index 00000000..00a74b02 --- /dev/null +++ b/pkg/cmd/config/unset/options.go @@ -0,0 +1,41 @@ +package unset + +import ( + "fmt" + + "kusionstack.io/kusion/pkg/cmd/config/util" + "kusionstack.io/kusion/pkg/config" +) + +type Options struct { + Item string +} + +func NewOptions() *Options { + return &Options{} +} + +func (o *Options) Complete(args []string) error { + item, err := util.GetItemFromArgs(args) + if err != nil { + return err + } + o.Item = item + return nil +} + +func (o *Options) Validate() error { + if err := util.ValidateItem(o.Item); err != nil { + return err + } + return nil +} + +func (o *Options) Run() error { + if err := config.DeleteConfigItem(o.Item); err != nil { + return err + } + + fmt.Printf("set config item %s successfully", o.Item) + return nil +} diff --git a/pkg/cmd/config/unset/options_test.go b/pkg/cmd/config/unset/options_test.go new file mode 100644 index 00000000..403fe680 --- /dev/null +++ b/pkg/cmd/config/unset/options_test.go @@ -0,0 +1,100 @@ +package unset + +import ( + "testing" + + "github.com/bytedance/mockey" + "github.com/stretchr/testify/assert" + + "kusionstack.io/kusion/pkg/config" +) + +func TestOptions_Complete(t *testing.T) { + testcases := []struct { + name string + args []string + success bool + expectedOpts *Options + }{ + { + name: "successfully complete options", + args: []string{"backends.mysql-pre.configs.port"}, + success: true, + expectedOpts: &Options{ + Item: "backends.mysql-pre.configs.port", + }, + }, + { + name: "complete field invalid args", + args: nil, + success: false, + expectedOpts: nil, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + opts := NewOptions() + err := opts.Complete(tc.args) + assert.Equal(t, tc.success, err == nil) + if tc.success { + assert.Equal(t, tc.expectedOpts, opts) + } + }) + } +} + +func TestOptions_Validate(t *testing.T) { + testcases := []struct { + name string + opts *Options + success bool + }{ + { + name: "valid options", + opts: &Options{ + Item: "backends.mysql-pre.configs.port", + }, + success: true, + }, + { + name: "invalid options empty config item", + opts: &Options{}, + success: false, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + err := tc.opts.Validate() + assert.Equal(t, tc.success, err == nil) + }) + } +} + +func TestOptions_Run(t *testing.T) { + testcases := []struct { + name string + opts *Options + success bool + }{ + { + name: "successfully run", + opts: &Options{ + Item: "backends.mysql-pre.configs.port", + }, + success: true, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + mockey.PatchConvey("mock delete config item", t, func() { + mockey.Mock(config.DeleteConfigItem).Return(nil).Build() + + err := tc.opts.Run() + assert.Equal(t, tc.success, err == nil) + }) + }) + } +} diff --git a/pkg/cmd/config/util/util.go b/pkg/cmd/config/util/util.go new file mode 100644 index 00000000..3bd00b7b --- /dev/null +++ b/pkg/cmd/config/util/util.go @@ -0,0 +1,53 @@ +package util + +import ( + "errors" +) + +var ( + ErrNotNoArgs = errors.New("no arg is accepted") + ErrNotOneArgs = errors.New("only one arg is accepted") + ErrNotTwoArgs = errors.New("only two args are accepted") + ErrEmptyItem = errors.New("empty config item name") + ErrEmptyValue = errors.New("empty config item value") +) + +// GetItemFromArgs returns config item name specified by args. +func GetItemFromArgs(args []string) (string, error) { + if len(args) != 1 { + return "", ErrNotOneArgs + } + return args[0], nil +} + +// GetItemValueFromArgs returns config item name and value specified by args. +func GetItemValueFromArgs(args []string) (string, string, error) { + if len(args) != 2 { + return "", "", ErrNotTwoArgs + } + return args[0], args[1], nil +} + +// ValidateNoArg returns true if there is no arg. +func ValidateNoArg(args []string) error { + if len(args) != 0 { + return ErrNotNoArgs + } + return nil +} + +// ValidateItem returns the config item name is valid or not. +func ValidateItem(item string) error { + if item == "" { + return ErrEmptyItem + } + return nil +} + +// ValidateValue returns the config item value is valid or not. +func ValidateValue(value string) error { + if value == "" { + return ErrEmptyValue + } + return nil +} diff --git a/pkg/cmd/config/util/util_test.go b/pkg/cmd/config/util/util_test.go new file mode 100644 index 00000000..cabbf963 --- /dev/null +++ b/pkg/cmd/config/util/util_test.go @@ -0,0 +1,153 @@ +package util + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetItemFromArgs(t *testing.T) { + testcases := []struct { + name string + success bool + args []string + expectedItem string + }{ + { + name: "successfully get item", + success: true, + args: []string{"backends.current"}, + expectedItem: "backends.current", + }, + { + name: "failed to get item invalid args", + success: false, + args: []string{}, + expectedItem: "", + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + item, err := GetItemFromArgs(tc.args) + assert.Equal(t, tc.success, err == nil) + if tc.success { + assert.Equal(t, tc.expectedItem, item) + } + }) + } +} + +func TestGetItemValueFromArgs(t *testing.T) { + testcases := []struct { + name string + success bool + args []string + expectedItem string + expectedValue string + }{ + { + name: "successfully get item and value", + success: true, + args: []string{"backends.current", "oss-prod"}, + expectedItem: "backends.current", + expectedValue: "oss-prod", + }, + { + name: "failed to get item and value invalid args", + success: false, + args: []string{"backends.current"}, + expectedItem: "", + expectedValue: "", + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + item, value, err := GetItemValueFromArgs(tc.args) + assert.Equal(t, tc.success, err == nil) + if tc.success { + assert.Equal(t, tc.expectedItem, item) + assert.Equal(t, tc.expectedValue, value) + } + }) + } +} + +func TestValidateNoArg(t *testing.T) { + testcases := []struct { + name string + success bool + args []string + }{ + { + name: "no arg", + success: true, + args: []string{}, + }, + { + name: "exist arg", + success: false, + args: []string{"backends.current"}, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + err := ValidateNoArg(tc.args) + assert.Equal(t, tc.success, err == nil) + }) + } +} + +func TestValidateItem(t *testing.T) { + testcases := []struct { + name string + success bool + item string + }{ + { + name: "valid item", + success: true, + item: "backends.current", + }, + { + name: "invalid item empty", + success: false, + item: "", + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + err := ValidateItem(tc.item) + assert.Equal(t, tc.success, err == nil) + }) + } +} + +func TestValidateValue(t *testing.T) { + testcases := []struct { + name string + success bool + value string + }{ + { + name: "valid value", + success: true, + value: "oss-prod", + }, + { + name: "invalid value empty", + success: false, + value: "", + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + err := ValidateValue(tc.value) + assert.Equal(t, tc.success, err == nil) + }) + } +} diff --git a/pkg/cmd/workspace/util/util_test.go b/pkg/cmd/workspace/util/util_test.go index baa42822..d1f07c6d 100644 --- a/pkg/cmd/workspace/util/util_test.go +++ b/pkg/cmd/workspace/util/util_test.go @@ -1,7 +1,6 @@ package util import ( - "fmt" "os" "path/filepath" "testing" @@ -44,7 +43,6 @@ func TestGetValidWorkspaceFromFile(t *testing.T) { for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { _, err := GetValidWorkspaceFromFile(tc.filePath, tc.wsName) - fmt.Println(err) assert.Equal(t, tc.success, err == nil) }) }