From 4be7fbafd47a90c16dc134cd6ab1735f815a8787 Mon Sep 17 00:00:00 2001 From: DCCooper <1866858@gmail.com> Date: Fri, 24 Jul 2020 15:51:31 +0800 Subject: [PATCH] tests: add more go tests for the project Signed-off-by: DCCooper <1866858@gmail.com> --- cmd/cli/build_test.go | 25 ++++ cmd/cli/grpc_client.go | 7 +- cmd/cli/grpc_client_test.go | 61 ++++++++- cmd/cli/login.go | 19 +-- cmd/cli/login_test.go | 245 ++++++++++++++++++++++++++++++++++++ cmd/cli/logout_test.go | 165 ++++++++++++++++++++++++ util/port_test.go | 97 ++++++++++++++ util/util_test.go | 55 ++++++++ 8 files changed, 663 insertions(+), 11 deletions(-) create mode 100644 cmd/cli/login_test.go create mode 100644 cmd/cli/logout_test.go create mode 100644 util/port_test.go diff --git a/cmd/cli/build_test.go b/cmd/cli/build_test.go index 6df3191..054eda1 100644 --- a/cmd/cli/build_test.go +++ b/cmd/cli/build_test.go @@ -15,6 +15,7 @@ package main import ( "context" + "errors" "io/ioutil" "os" "path/filepath" @@ -54,6 +55,8 @@ type mockDaemon struct { statusReq *pb.StatusRequest removeReq *pb.RemoveRequest loadReq *pb.LoadRequest + loginReq *pb.LoginRequest + logoutReq *pb.LogoutRequest } func newMockDaemon() *mockDaemon { @@ -85,6 +88,28 @@ func (f *mockDaemon) remove(_ context.Context, in *pb.RemoveRequest, opts ...grp return &mockRemoveClient{}, nil } +func (f *mockDaemon) login(_ context.Context, in *pb.LoginRequest, opts ...grpc.CallOption) (*pb.LoginResponse, error) { + f.loginReq = in + serverLen := len(f.loginReq.Server) + if serverLen == 0 || serverLen > 128 { + return &pb.LoginResponse{ + Content: "Login Failed", + }, errors.New("empty server address") + } + + return &pb.LoginResponse{Content: "Success"}, nil +} + +func (f *mockDaemon) logout(_ context.Context, in *pb.LogoutRequest, opts ...grpc.CallOption) (*pb.LogoutResponse, error) { + f.logoutReq = in + serverLen := len(f.logoutReq.Server) + if serverLen == 0 || serverLen > 128 { + return &pb.LogoutResponse{Result: "Logout Failed"}, errors.New("empty server address") + } + + return &pb.LogoutResponse{Result: "Success"}, nil +} + func TestRunBuildWithLocalDockerfile(t *testing.T) { dockerfile := ` FROM alpine:latest diff --git a/cmd/cli/grpc_client.go b/cmd/cli/grpc_client.go index 9546146..581e7f8 100644 --- a/cmd/cli/grpc_client.go +++ b/cmd/cli/grpc_client.go @@ -103,8 +103,11 @@ func getStartTimeout(timeout string) (time.Duration, error) { return defaultStartTimeout, nil } timeParse, err := time.ParseDuration(timeout) - if err != nil || timeParse < minStartTimeout || timeParse > maxStartTimeout { - return -1, errors.Wrapf(err, "invalid timeout value: %s, supported range [%s, %s]", timeout, minStartTimeout, maxStartTimeout) + if err != nil { + return -1, err + } + if timeParse < minStartTimeout || timeParse > maxStartTimeout { + return -1, errors.Errorf("invalid timeout value: %s, supported range [%s, %s]", timeout, minStartTimeout, maxStartTimeout) } return timeParse, nil } diff --git a/cmd/cli/grpc_client_test.go b/cmd/cli/grpc_client_test.go index dec5ade..7ccd6f1 100644 --- a/cmd/cli/grpc_client_test.go +++ b/cmd/cli/grpc_client_test.go @@ -16,6 +16,8 @@ package main import ( "context" "io" + "testing" + "time" types "github.com/gogo/protobuf/types" "google.golang.org/grpc" @@ -98,10 +100,10 @@ func (gcli *mockGrpcClient) Login(ctx context.Context, in *pb.LoginRequest, opts } func (gcli *mockGrpcClient) Logout(ctx context.Context, in *pb.LogoutRequest, opts ...grpc.CallOption) (*pb.LogoutResponse, error) { - if gcli.loginFunc != nil { + if gcli.logoutFunc != nil { return gcli.logoutFunc(ctx, in, opts...) } - return nil, nil + return &pb.LogoutResponse{Result: "Success Logout"}, nil } func (gcli *mockGrpcClient) Load(ctx context.Context, in *pb.LoadRequest, opts ...grpc.CallOption) (*pb.LoadResponse, error) { @@ -148,3 +150,58 @@ func (rcli *mockRemoveClient) Recv() (*pb.RemoveResponse, error) { } return resp, io.EOF } + +func TestGetStartTimeout(t *testing.T) { + type args struct { + timeout string + } + tests := []struct { + name string + args args + want time.Duration + wantErr bool + }{ + { + name: "TC1 - normal case", + args: args{timeout: "1s"}, + want: time.Second, + wantErr: false, + }, + { + name: "TC2 - normal case with empty timeout input", + args: args{timeout: ""}, + want: defaultStartTimeout, + wantErr: false, + }, + { + name: "TC3 - abnormal case with larger than max start timeout", + args: args{timeout: "21s"}, + want: -1, + wantErr: true, + }, + { + name: "TC4 - abnormal case with less than min start timeout", + args: args{timeout: "19ms"}, + want: -1, + wantErr: true, + }, + { + name: "TC5 - abnormal case with invalid timeout format", + args: args{timeout: "abc"}, + want: -1, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := getStartTimeout(tt.args.timeout) + if (err != nil) != tt.wantErr { + t.Errorf("getStartTimeout() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("getStartTimeout() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/cmd/cli/login.go b/cmd/cli/login.go index cfd90af..6dd2ab6 100644 --- a/cmd/cli/login.go +++ b/cmd/cli/login.go @@ -18,6 +18,7 @@ import ( "context" "crypto/sha256" "fmt" + "io" "os" "strings" @@ -45,6 +46,7 @@ var ( errEmptyRegistry = errors.New("empty registry found, please input one registry") errTooManyArgs = errors.New("too many arguments, login only accepts 1 argument") errLackOfFlags = errors.New("must provides --password-stdin with --username") + loginOpts loginOptions ) type loginOptions struct { @@ -55,7 +57,7 @@ type loginOptions struct { stdinPass bool } -var loginOpts loginOptions +type passReader func() ([]byte, error) // NewLoginCmd returns login command func NewLoginCmd() *cobra.Command { @@ -170,20 +172,23 @@ func getPassword(c *cobra.Command) error { } if loginOpts.stdinPass { - if err := getPassFromStdin(); err != nil { + if err := getPassFromStdin(os.Stdin); err != nil { return err } } else { - if err := getPassFromInput(); err != nil { + r := func() ([]byte, error) { + return terminal.ReadPassword(0) + } + if err := getPassFromInput(r); err != nil { return err } } return nil } -func getPassFromInput() error { +func getPassFromInput(f passReader) error { fmt.Print("Password: ") - termPass, err := terminal.ReadPassword(0) + termPass, err := f() if err != nil { return errReadPassFromTerm } @@ -196,9 +201,9 @@ func getPassFromInput() error { return nil } -func getPassFromStdin() error { +func getPassFromStdin(r io.Reader) error { var buf strings.Builder - passScanner := bufio.NewScanner(os.Stdin) + passScanner := bufio.NewScanner(r) for passScanner.Scan() { if _, err := fmt.Fprint(&buf, passScanner.Text()); err != nil { return err diff --git a/cmd/cli/login_test.go b/cmd/cli/login_test.go new file mode 100644 index 0000000..0c42569 --- /dev/null +++ b/cmd/cli/login_test.go @@ -0,0 +1,245 @@ +// Copyright (c) Huawei Technologies Co., Ltd. 2020. All rights reserved. +// isula-build licensed under the Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR +// PURPOSE. +// See the Mulan PSL v2 for more details. +// Author: Xiang Li +// Create: 2020-07-20 +// Description: This file is used for testing command login + +package main + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "strings" + "testing" + + "github.com/spf13/cobra" + "gotest.tools/assert" +) + +func TestNewLoginCmd(t *testing.T) { + loginCmd := NewLoginCmd() + loginCmd.SetArgs(strings.Split("test.org --username testuser --password-stdin", " ")) + err := loginCmd.Execute() + args := []string{"test.org"} + err = loginCommand(loginCmd, args) + if err != nil { + assert.ErrorContains(t, err, "auth info can not be empty") + } +} + +func TestGetPassFromInput(t *testing.T) { + type args struct { + f passReader + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "TC1 - normal input", + args: args{f: func() ([]byte, error) { + return []byte("aaa"), nil + }}, + wantErr: false, + }, + { + name: "TC2 - abnormal input with error", + args: args{f: func() ([]byte, error) { + return nil, errors.New("error read password") + }}, + wantErr: true, + }, + { + name: "TC3 - abnormal input with length more than 128", + args: args{func() ([]byte, error) { + return bytes.Repeat([]byte("a"), 129), nil + }}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := getPassFromInput(tt.args.f); (err != nil) != tt.wantErr { + t.Errorf("getPassFromInput() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestGetPassFromStdin(t *testing.T) { + type args struct { + r io.Reader + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "TC1 - normal input", + args: args{r: strings.NewReader("aaa")}, + wantErr: false, + }, + { + name: "TC2 - empty input", + args: args{r: strings.NewReader("")}, + wantErr: true, + }, + { + name: "TC2 - abnormal input length", + args: args{r: strings.NewReader(string(bytes.Repeat([]byte("a"), 129)))}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := getPassFromStdin(tt.args.r); (err != nil) != tt.wantErr { + t.Errorf("getPassFromStdin() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestEncryptOpts(t *testing.T) { + err := encryptOpts() + assert.NilError(t, err) +} + +func TestRunLogin(t *testing.T) { + type testcase struct { + name string + server string + errString string + wantErr bool + } + var testcases = []testcase{ + { + name: "TC1 - normal case", + server: "test.org", + wantErr: false, + }, + { + name: "TC2 - abnormal case with empty server", + server: "", + wantErr: true, + errString: "empty server address", + }, + } + for _, tc := range testcases { + ctx := context.Background() + mockD := newMockDaemon() + cli := newMockClient(&mockGrpcClient{loginFunc: mockD.login}) + + loginOpts.server = tc.server + _, err := runLogin(ctx, &cli) + assert.Equal(t, err != nil, tc.wantErr, "Failed at [%s], err: %v", tc.name, err) + if err != nil { + assert.ErrorContains(t, err, tc.errString) + } + } +} + +func TestNewLoginOptions(t *testing.T) { + type args struct { + c *cobra.Command + args []string + } + type flags struct { + username string + passStdin bool + } + tests := []struct { + name string + args args + flags flags + errString string + }{ + { + name: "TC1 - normal case", + args: args{ + c: NewLoginCmd(), + args: []string{"test.org -u testuser"}, + }, + flags: flags{ + username: "aaa", + passStdin: true, + }, + errString: "auth info can not be empty", + }, + { + name: "TC2 - abnormal case with out username flag", + args: args{ + c: NewLoginCmd(), + args: []string{"test.org"}, + }, + flags: flags{ + passStdin: true, + }, + errString: "", + }, + { + name: "TC3 - abnormal case with invalid args", + args: args{ + c: NewLoginCmd(), + args: []string{"a", "b"}, + }, + errString: "too many arguments, login only accepts 1 argument", + }, + { + name: "TC4 - abnormal case with empty args", + args: args{ + c: NewLoginCmd(), + args: []string{}, + }, + errString: "empty registry found", + }, + { + name: "TC5 - abnormal case with empty args", + args: args{ + c: NewLoginCmd(), + args: []string{"/aaaa"}, + }, + errString: "invalid registry address", + }, + { + name: "TC6 - abnormal case with long username", + args: args{ + c: NewLoginCmd(), + args: []string{"test.org"}, + }, + flags: flags{ + username: strings.Repeat("a", 129), + passStdin: true, + }, + errString: "length of input exceeded", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.flags.passStdin { + tt.args.c.Flag("password-stdin").Changed = true + tt.args.c.Flag("password-stdin").Value.Set(fmt.Sprintf("%v", tt.flags.passStdin)) + } + if tt.flags.username != "" { + tt.args.c.Flag("username").Changed = true + tt.args.c.Flag("username").Value.Set(tt.flags.username) + } + tt.args.c.ParseFlags(tt.args.args) + err := newLoginOptions(tt.args.c, tt.args.args) + if err != nil { + assert.ErrorContains(t, err, tt.errString) + } + }) + } +} diff --git a/cmd/cli/logout_test.go b/cmd/cli/logout_test.go new file mode 100644 index 0000000..3665b54 --- /dev/null +++ b/cmd/cli/logout_test.go @@ -0,0 +1,165 @@ +// Copyright (c) Huawei Technologies Co., Ltd. 2020. All rights reserved. +// isula-build licensed under the Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR +// PURPOSE. +// See the Mulan PSL v2 for more details. +// Author: Xiang Li +// Create: 2020-07-20 +// Description: This file is used for testing command logout + +package main + +import ( + "context" + "strings" + "testing" + + "github.com/spf13/cobra" + "gotest.tools/assert" +) + +func TestRunLogout(t *testing.T) { + type testcase struct { + name string + all bool + server string + errString string + wantErr bool + } + var testcases = []testcase{ + { + name: "TC1 - normal case", + all: false, + server: "test.org", + wantErr: false, + }, + { + name: "TC2 - normal case with -a flag", + all: true, + server: "test.org", + wantErr: false, + }, + { + name: "TC3 - abnormal case with empty server name", + all: false, + server: "", + wantErr: true, + }, + { + name: "TC4 - abnormal case with empty server name and -a flag", + all: true, + server: "", + wantErr: true, + }, + { + name: "TC5 - abnormal case with server name larger than 128", + all: true, + server: strings.Repeat("a", 129), + wantErr: true, + }, + } + for _, tc := range testcases { + ctx := context.Background() + mockD := newMockDaemon() + cli := newMockClient(&mockGrpcClient{logoutFunc: mockD.logout}) + + logoutOpts.all = tc.all + logoutOpts.server = tc.server + _, err := runLogout(ctx, &cli) + assert.Equal(t, err != nil, tc.wantErr, "Failed at [%s], err: %v", tc.name, err) + if err != nil { + assert.ErrorContains(t, err, tc.errString) + } + } +} + +func TestNewLogoutOptions(t *testing.T) { + type args struct { + c *cobra.Command + args []string + } + tests := []struct { + name string + args args + flag string + wantErr bool + }{ + { + name: "TC1 - normal case", + args: args{ + c: NewLogoutCmd(), + args: []string{"test.org"}, + }, + flag: "--all", + wantErr: false, + }, + { + name: "TC2 - abnormal case with empty server", + args: args{ + c: NewLogoutCmd(), + args: []string{}, + }, + wantErr: true, + }, + { + name: "TC3 - abnormal case with more than one args", + args: args{ + c: NewLogoutCmd(), + args: []string{"a", "b"}, + }, + wantErr: true, + }, + { + name: "TC4 - abnormal case with invalid server address", + args: args{ + c: NewLogoutCmd(), + args: []string{"/aaaaaa"}, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.flag != "" { + tt.args.c.Flag("all").Changed = true + } + if err := newLogoutOptions(tt.args.c, tt.args.args); (err != nil) != tt.wantErr { + t.Errorf("newLogoutOptions() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestNewLogoutCmd(t *testing.T) { + tests := []struct { + name string + args string + errString string + }{ + { + name: "TC1 - normal case", + args: "test.org", + errString: "isula_build.sock", + }, + { + name: "TC2 - abnormal case with invalid args", + args: "test.org a b", + errString: "too many arguments", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cmd := NewLogoutCmd() + cmd.SetArgs(strings.Split(tt.args, " ")) + err := cmd.Execute() + if err != nil { + + } + assert.ErrorContains(t, err, tt.errString) + }) + } +} diff --git a/util/port_test.go b/util/port_test.go new file mode 100644 index 0000000..33573a1 --- /dev/null +++ b/util/port_test.go @@ -0,0 +1,97 @@ +// Copyright (c) Huawei Technologies Co., Ltd. 2020. All rights reserved. +// iSula-Kits licensed under the Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR +// PURPOSE. +// See the Mulan PSL v2 for more details. +// Author: Xiang Li +// Create: 2020-07-20 +// Description: This file is used for testing port setting + +package util + +import ( + "testing" +) + +func TestPortSet(t *testing.T) { + type args struct { + rawPort string + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "TC1 - normal case", + args: args{rawPort: "8080"}, + want: "8080/tcp", + wantErr: false, + }, + { + name: "TC2 - normal case", + args: args{rawPort: "8080/"}, + want: "8080/tcp", + wantErr: false, + }, + { + name: "TC3 - abnormal case with invalid port format", + args: args{rawPort: "aaa"}, + want: "", + wantErr: true, + }, + { + name: "TC4 - normal case with port range", + args: args{rawPort: "3000-5000/udp"}, + want: "3000-5000/udp", + wantErr: false, + }, + { + name: "TC5 - abnormal case with invalid port range", + args: args{rawPort: "3000-500/udp"}, + want: "", + wantErr: true, + }, + { + name: "TC6 - abnormal case with invalid port range number", + args: args{rawPort: "a-b/udp"}, + want: "", + wantErr: true, + }, + { + name: "TC7 - abnormal case with invalid port range numbers", + args: args{rawPort: "3000-5000-8000/udp"}, + want: "", + wantErr: true, + }, + { + name: "TC8 - abnormal case with empty port", + args: args{rawPort: ""}, + want: "", + wantErr: true, + }, + { + name: "TC9 - abnormal case with invalid protocol", + args: args{rawPort: "80/abc"}, + want: "", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := PortSet(tt.args.rawPort) + if (err != nil) != tt.wantErr { + t.Errorf("PortSet() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("PortSet() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/util/util_test.go b/util/util_test.go index acbbce1..a0e93f7 100644 --- a/util/util_test.go +++ b/util/util_test.go @@ -233,3 +233,58 @@ func TestCopyXattrs(t *testing.T) { } } + +func TestValidateSignal(t *testing.T) { + type args struct { + sigStr string + } + tests := []struct { + name string + args args + want syscall.Signal + wantErr bool + }{ + { + name: "TC1 - normal case with integer", + args: args{sigStr: "9"}, + want: syscall.Signal(9), + wantErr: false, + }, + { + name: "TC2 - normal case with signal name", + args: args{sigStr: "SIGKILL"}, + want: syscall.Signal(9), + wantErr: false, + }, + { + name: "TC3 - abnormal case with invalid signal name", + args: args{sigStr: "aaa"}, + want: -1, + wantErr: true, + }, + { + name: "TC4 - abnormal case with invalid signal value", + args: args{sigStr: "65"}, + want: -1, + wantErr: true, + }, + { + name: "TC5 - abnormal case with invalid signal value", + args: args{sigStr: "0"}, + want: -1, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ValidateSignal(tt.args.sigStr) + if (err != nil) != tt.wantErr { + t.Errorf("ValidateSignal() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("ValidateSignal() got = %v, want %v", got, tt.want) + } + }) + } +} -- GitLab