PLTCONN-2935: More tests & mock out terminal for unit tests

This commit is contained in:
fangming-ning-sp
2023-05-18 15:12:20 -05:00
parent e3033f9991
commit aec1be40f9
15 changed files with 179 additions and 39 deletions

View File

@@ -5,7 +5,8 @@ clean:
.PHONY: mocks
mocks:
# Ref: https://github.com/golang/mock
mockgen -source=client/client.go -destination=mocks/client.go -package=mocks
mockgen -source=internal/client/client.go -destination=internal/mocks/client.go -package=mocks
mockgen -source=internal/terminal/terminal.go -destination=internal/mocks/terminal.go -package=mocks
.PHONY: test
test:

View File

@@ -7,7 +7,7 @@ import (
"github.com/spf13/cobra"
)
func NewConfigureCmd() *cobra.Command {
func NewConfigureCmd(term terminal.Terminal) *cobra.Command {
var ClientID string
var ClientSecret string
var err error
@@ -20,7 +20,7 @@ func NewConfigureCmd() *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
if ClientID == "" {
ClientID, err = terminal.PromptPassword("Personal Access Token Client ID:")
ClientID, err = term.PromptPassword("Personal Access Token Client ID:")
if err != nil {
return err
}
@@ -29,7 +29,7 @@ func NewConfigureCmd() *cobra.Command {
config.SetPatClientID(ClientID)
if ClientSecret == "" {
ClientSecret, err = terminal.PromptPassword("Personal Access Token Client Secret:")
ClientSecret, err = term.PromptPassword("Personal Access Token Client Secret:")
if err != nil {
return err
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/sailpoint-oss/sailpoint-cli/internal/client"
"github.com/sailpoint-oss/sailpoint-cli/internal/config"
"github.com/sailpoint-oss/sailpoint-cli/internal/terminal"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"gopkg.in/yaml.v2"
@@ -18,7 +19,7 @@ const (
connectorsEndpoint = "/beta/platform-connectors"
)
func NewConnCmd() *cobra.Command {
func NewConnCmd(term terminal.Terminal) *cobra.Command {
conn := &cobra.Command{
Use: "connectors",
Short: "manage connectors",
@@ -45,7 +46,7 @@ func NewConnCmd() *cobra.Command {
newConnCreateCmd(Client),
newConnCreateVersionCmd(Client),
newConnVersionsCmd(Client),
newConnInvokeCmd(Client),
newConnInvokeCmd(Client, term),
newConnValidateCmd(Client),
newConnTagCmd(Client),
newConnValidateSourcesCmd(Client),

View File

@@ -9,6 +9,7 @@ import (
connclient "github.com/sailpoint-oss/sailpoint-cli/cmd/connector/client"
"github.com/sailpoint-oss/sailpoint-cli/internal/client"
"github.com/sailpoint-oss/sailpoint-cli/internal/terminal"
"github.com/spf13/cobra"
)
@@ -23,7 +24,7 @@ const (
stdTestConnection = "std:test-connection"
)
func newConnInvokeCmd(client client.Client) *cobra.Command {
func newConnInvokeCmd(client client.Client, term terminal.Terminal) *cobra.Command {
cmd := &cobra.Command{
Use: "invoke",
Short: "Invoke Command on a connector",
@@ -42,7 +43,7 @@ func newConnInvokeCmd(client client.Client) *cobra.Command {
cmd.AddCommand(
newConnInvokeTestConnectionCmd(client),
newConnInvokeChangePasswordCmd(client),
newConnInvokeChangePasswordCmd(client, term),
newConnInvokeAccountCreateCmd(client),
newConnInvokeAccountDiscoverSchemaCmd(client),
newConnInvokeAccountListCmd(client),

View File

@@ -10,7 +10,7 @@ import (
)
// newConnInvokeChangePasswordCmd defines a command to perform change password operation
func newConnInvokeChangePasswordCmd(spClient client.Client) *cobra.Command {
func newConnInvokeChangePasswordCmd(spClient client.Client, term terminal.Terminal) *cobra.Command {
cmd := &cobra.Command{
Use: "change-password",
Short: "Invoke a change-password command",
@@ -24,7 +24,7 @@ func newConnInvokeChangePasswordCmd(spClient client.Client) *cobra.Command {
return err
}
password, err := terminal.PromptPassword("Enter Password:")
password, err := term.PromptPassword("Enter Password:")
if err != nil {
return err
}

View File

@@ -0,0 +1,92 @@
// Copyright (c) 2023, SailPoint Technologies, Inc. All rights reserved.
package connector
import (
"bytes"
"io"
"net/http"
"testing"
"github.com/golang/mock/gomock"
"github.com/sailpoint-oss/sailpoint-cli/internal/mocks"
)
func TestChangePasswordWithoutInput(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
client := mocks.NewMockClient(ctrl)
term := mocks.NewMockTerm(ctrl)
cmd := newConnInvokeChangePasswordCmd(client, term)
addRequiredFlagsFromParentCmd(cmd)
b := new(bytes.Buffer)
cmd.SetOut(b)
cmd.SetArgs([]string{"-c", "test-connector", "--config-json", "{}"})
err := cmd.Execute()
if err == nil {
t.Errorf("failed to detect error: reading account without identity")
}
}
func TestChangePasswordWithIdentityAndPassword(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
i := `{"connectorRef":"test-connector","tag":"latest","type":"std:change-password","config":{},` +
`"input":{"identity":"john.doe","key":{"simple":{"id":"john.doe"}},"password":"password"}}`
client := mocks.NewMockClient(ctrl)
client.EXPECT().
Post(gomock.Any(), gomock.Any(), "application/json", bytes.NewReader([]byte(i))).
Return(&http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewReader([]byte("{}")))}, nil)
term := mocks.NewMockTerm(ctrl)
term.EXPECT().
PromptPassword(gomock.Any()).
Return("password", nil)
cmd := newConnInvokeChangePasswordCmd(client, term)
addRequiredFlagsFromParentCmd(cmd)
b := new(bytes.Buffer)
cmd.SetOut(b)
cmd.SetArgs([]string{"john.doe", "-c", "test-connector", "--config-json", "{}"})
err := cmd.Execute()
if err != nil {
t.Errorf("command failed with err: %s", err)
}
}
func TestChangePasswordWithIdentityAndPasswordAndUniqueId(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
i := `{"connectorRef":"test-connector","tag":"latest","type":"std:change-password","config":{},` +
`"input":{"identity":"john.doe","key":{"compound":{"lookupId":"john.doe","uniqueId":"12345"}},"password":"password"}}`
client := mocks.NewMockClient(ctrl)
client.EXPECT().
Post(gomock.Any(), gomock.Any(), "application/json", bytes.NewReader([]byte(i))).
Return(&http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewReader([]byte("{}")))}, nil)
term := mocks.NewMockTerm(ctrl)
term.EXPECT().
PromptPassword(gomock.Any()).
Return("password", nil)
cmd := newConnInvokeChangePasswordCmd(client, term)
addRequiredFlagsFromParentCmd(cmd)
b := new(bytes.Buffer)
cmd.SetOut(b)
cmd.SetArgs([]string{"john.doe", "12345", "-c", "test-connector", "--config-json", "{}"})
err := cmd.Execute()
if err != nil {
t.Errorf("command failed with err: %s", err)
}
}

View File

@@ -24,7 +24,7 @@ func TestNewConnInvokeCmd_noArgs(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
cmd := newConnInvokeCmd(mocks.NewMockClient(ctrl))
cmd := newConnInvokeCmd(mocks.NewMockClient(ctrl), mocks.NewMockTerm(ctrl))
if len(cmd.Commands()) != numConnInvokeSubcommands {
t.Fatalf("expected: %d, actual: %d", len(cmd.Commands()), numConnInvokeSubcommands)
}

View File

@@ -13,6 +13,7 @@ import (
"github.com/sailpoint-oss/sailpoint-cli/cmd/spconfig"
"github.com/sailpoint-oss/sailpoint-cli/cmd/transform"
"github.com/sailpoint-oss/sailpoint-cli/cmd/va"
"github.com/sailpoint-oss/sailpoint-cli/internal/terminal"
"github.com/spf13/cobra"
)
@@ -34,13 +35,15 @@ func NewRootCmd() *cobra.Command {
},
}
t := &terminal.Term{}
root.AddCommand(
set.NewSetCommand(),
environment.NewEnvironmentCommand(),
configure.NewConfigureCmd(),
connector.NewConnCmd(),
configure.NewConfigureCmd(t),
connector.NewConnCmd(t),
transform.NewTransformCmd(),
va.NewVACmd(),
va.NewVACmd(t),
search.NewSearchCmd(),
spconfig.NewSPConfigCmd(),
report.NewReportCommand(),

View File

@@ -11,7 +11,7 @@ import (
"github.com/spf13/cobra"
)
func newCollectCmd() *cobra.Command {
func newCollectCmd(term terminal.Terminal) *cobra.Command {
var output string
var logs bool
var config bool
@@ -37,7 +37,7 @@ func newCollectCmd() *cobra.Command {
}
for credential := 0; credential < len(args); credential++ {
password, _ := terminal.PromptPassword(fmt.Sprintf("Enter Password for %v:", args[credential]))
password, _ := term.PromptPassword(fmt.Sprintf("Enter Password for %v:", args[credential]))
credentials = append(credentials, password)
}

View File

@@ -13,7 +13,7 @@ import (
"github.com/spf13/cobra"
)
func NewTroubleshootCmd() *cobra.Command {
func NewTroubleshootCmd(term terminal.Terminal) *cobra.Command {
var output string
cmd := &cobra.Command{
Use: "troubleshoot",
@@ -29,7 +29,7 @@ func NewTroubleshootCmd() *cobra.Command {
var credentials []string
for credential := 0; credential < len(args); credential++ {
password, _ := terminal.PromptPassword(fmt.Sprintf("Enter Password for %v:", args[credential]))
password, _ := term.PromptPassword(fmt.Sprintf("Enter Password for %v:", args[credential]))
credentials = append(credentials, password)
}

View File

@@ -9,7 +9,7 @@ import (
"github.com/spf13/cobra"
)
func newUpdateCmd() *cobra.Command {
func newUpdateCmd(term terminal.Terminal) *cobra.Command {
cmd := &cobra.Command{
Use: "update",
Short: "Perform Update Operations on a SailPoint Virtual Appliance",
@@ -19,7 +19,7 @@ func newUpdateCmd() *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
var credentials []string
for credential := 0; credential < len(args); credential++ {
password, _ := terminal.PromptPassword(fmt.Sprintf("Enter Password for %v:", args[credential]))
password, _ := term.PromptPassword(fmt.Sprintf("Enter Password for %v:", args[credential]))
credentials = append(credentials, password)
}
for i := 0; i < len(args); i++ {

View File

@@ -4,10 +4,11 @@ package va
import (
"fmt"
"github.com/sailpoint-oss/sailpoint-cli/internal/terminal"
"github.com/spf13/cobra"
)
func NewVACmd() *cobra.Command {
func NewVACmd(term terminal.Terminal) *cobra.Command {
cmd := &cobra.Command{
Use: "va",
Short: "Interact with SailPoint Virtual Appliances",
@@ -19,9 +20,9 @@ func NewVACmd() *cobra.Command {
}
cmd.AddCommand(
newCollectCmd(),
newCollectCmd(term),
// newTroubleshootCmd(),
newUpdateCmd(),
newUpdateCmd(term),
newParseCmd(),
)

View File

@@ -1,5 +1,5 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: client/client.go
// Source: internal/client/client.go
// Package mocks is a generated GoMock package.
package mocks
@@ -95,17 +95,3 @@ func (mr *MockClientMockRecorder) Put(ctx, url, contentType, body interface{}) *
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Put", reflect.TypeOf((*MockClient)(nil).Put), ctx, url, contentType, body)
}
// VerifyToken mocks base method.
func (m *MockClient) VerifyToken(ctx context.Context, tokenUrl, clientID, secret string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "VerifyToken", ctx, tokenUrl, clientID, secret)
ret0, _ := ret[0].(error)
return ret0
}
// VerifyToken indicates an expected call of VerifyToken.
func (mr *MockClientMockRecorder) VerifyToken(ctx, tokenUrl, clientID, secret interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VerifyToken", reflect.TypeOf((*MockClient)(nil).VerifyToken), ctx, tokenUrl, clientID, secret)
}

View File

@@ -0,0 +1,49 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: internal/terminal/terminal.go
// Package mocks is a generated GoMock package.
package mocks
import (
reflect "reflect"
gomock "github.com/golang/mock/gomock"
)
// MockTerm is a mock of Term interface.
type MockTerm struct {
ctrl *gomock.Controller
recorder *MockTermMockRecorder
}
// MockTermMockRecorder is the mock recorder for MockTerm.
type MockTermMockRecorder struct {
mock *MockTerm
}
// NewMockTerm creates a new mock instance.
func NewMockTerm(ctrl *gomock.Controller) *MockTerm {
mock := &MockTerm{ctrl: ctrl}
mock.recorder = &MockTermMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockTerm) EXPECT() *MockTermMockRecorder {
return m.recorder
}
// PromptPassword mocks base method.
func (m *MockTerm) PromptPassword(promptMsg string) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "PromptPassword", promptMsg)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// PromptPassword indicates an expected call of PromptPassword.
func (mr *MockTermMockRecorder) PromptPassword(promptMsg interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PromptPassword", reflect.TypeOf((*MockTerm)(nil).PromptPassword), promptMsg)
}

View File

@@ -11,8 +11,14 @@ import (
"golang.org/x/term"
)
type Term struct{}
type Terminal interface {
PromptPassword(promptMsg string) (string, error)
}
// PromptPassword prompts user to enter password and then returns it
func PromptPassword(promptMsg string) (string, error) {
func (c *Term) PromptPassword(promptMsg string) (string, error) {
fmt.Print(promptMsg)
bytePassword, err := term.ReadPassword(int(syscall.Stdin))
if err != nil {